12

I want to execute an EXE File that is compiled with my application as a Resource. I want to execute it directly in Memory.

I have seen this Topic :

Is it possible to embed and run exe file in a Delphi executable app?

And this Code :

http://www.coderprofile.com/networks/source-codes/138/execute-resource-directly-in-memory

I used this Code :

type
 TSections = array [0..0] of TImageSectionHeader;

...

{$IMAGEBASE $10000000}

function GetAlignedSize(Size: dword; Alignment: dword): dword;
begin
  if ((Size mod Alignment) = 0) then
    Result := Size
  else
    Result := ((Size div Alignment) + 1) * Alignment;
end;

function ImageSize(Image: pointer): dword;
var
  Alignment: dword;
  ImageNtHeaders: PImageNtHeaders;
  PSections: ^TSections;
  SectionLoop: dword;
begin
  ImageNtHeaders := pointer(dword(dword(Image)) + dword(PImageDosHeader(Image)._lfanew));
  Alignment := ImageNtHeaders.OptionalHeader.SectionAlignment;
  if ((ImageNtHeaders.OptionalHeader.SizeOfHeaders mod Alignment) = 0) then
  begin
    Result := ImageNtHeaders.OptionalHeader.SizeOfHeaders;
  end
  else
  begin
    Result := ((ImageNtHeaders.OptionalHeader.SizeOfHeaders div Alignment) + 1) * Alignment;
  end;
  PSections := pointer(pchar(@(ImageNtHeaders.OptionalHeader)) + ImageNtHeaders.FileHeader.SizeOfOptionalHeader);
  for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
  begin
    if PSections[SectionLoop].Misc.VirtualSize <> 0 then
    begin
      if ((PSections[SectionLoop].Misc.VirtualSize mod Alignment) = 0) then
      begin
        Result := Result + PSections[SectionLoop].Misc.VirtualSize;
      end
      else
      begin
        Result := Result + (((PSections[SectionLoop].Misc.VirtualSize div Alignment) + 1) * Alignment);
      end;
    end;
  end;
end;

procedure CreateProcessEx(FileMemory: pointer);
var
  BaseAddress, Bytes, HeaderSize, InjectSize,  SectionLoop, SectionSize: dword;
  Context: TContext;
  FileData: pointer;
  ImageNtHeaders: PImageNtHeaders;
  InjectMemory: pointer;
  ProcInfo: TProcessInformation;
  PSections: ^TSections;
  StartInfo: TStartupInfo;
begin
  ImageNtHeaders := pointer(dword(dword(FileMemory)) + dword(PImageDosHeader(FileMemory)._lfanew));
  InjectSize := ImageSize(FileMemory);
  GetMem(InjectMemory, InjectSize);
  try
    FileData := InjectMemory;
    HeaderSize := ImageNtHeaders.OptionalHeader.SizeOfHeaders;
    PSections := pointer(pchar(@(ImageNtHeaders.OptionalHeader)) + ImageNtHeaders.FileHeader.SizeOfOptionalHeader);
    for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
    begin
      if PSections[SectionLoop].PointerToRawData < HeaderSize then HeaderSize := PSections[SectionLoop].PointerToRawData;
    end;
    CopyMemory(FileData, FileMemory, HeaderSize);
    FileData := pointer(dword(FileData) + GetAlignedSize(ImageNtHeaders.OptionalHeader.SizeO  fHeaders, ImageNtHeaders.OptionalHeader.SectionAlignment));
    for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
    begin
      if PSections[SectionLoop].SizeOfRawData > 0 then
      begin
        SectionSize := PSections[SectionLoop].SizeOfRawData;
        if SectionSize > PSections[SectionLoop].Misc.VirtualSize then SectionSize := PSections[SectionLoop].Misc.VirtualSize;
        CopyMemory(FileData, pointer(dword(FileMemory) + PSections[SectionLoop].PointerToRawData), SectionSize);
        FileData := pointer(dword(FileData) + GetAlignedSize(PSections[SectionLoop].Misc.VirtualSize, ImageNtHeaders.OptionalHeader.SectionAlignment));
      end
      else
      begin
        if PSections[SectionLoop].Misc.VirtualSize <> 0 then FileData := pointer(dword(FileData) + GetAlignedSize(PSections[SectionLoop].Misc.VirtualSize, ImageNtHeaders.OptionalHeader.SectionAlignment));
      end;
    end;
    ZeroMemory(@StartInfo, SizeOf(StartupInfo));
    ZeroMemory(@Context, SizeOf(TContext));
    CreateProcess(nil, pchar(ParamStr(0)), nil, nil, False, CREATE_SUSPENDED, nil, nil, StartInfo, ProcInfo);
    Context.ContextFlags := CONTEXT_FULL;
    GetThreadContext(ProcInfo.hThread, Context);
    ReadProcessMemory(ProcInfo.hProcess, pointer(Context.Ebx + 8), @BaseAddress, 4, Bytes);
    VirtualAllocEx(ProcInfo.hProcess, pointer(ImageNtHeaders.OptionalHeader.ImageBase), InjectSize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(ProcInfo.hProcess, pointer(ImageNtHeaders.OptionalHeader.ImageBase), InjectMemory, InjectSize, Bytes);
    WriteProcessMemory(ProcInfo.hProcess, pointer(Context.Ebx + 8), @ImageNtHeaders.OptionalHeader.ImageBase, 4, Bytes);
    Context.Eax := ImageNtHeaders.OptionalHeader.ImageBase + ImageNtHeaders.OptionalHeader.AddressOfEntryPoint;
    SetThreadContext(ProcInfo.hThread, Context);
    ResumeThread(ProcInfo.hThread);
  finally
    FreeMemory(InjectMemory);
  end;
end;

procedure Execute;
var
  RS : TResourceStream;      
begin
   RS := TResourceStream.Create(HInstance, 'MrResource', RT_RCDATA);
  try
   CreateProcessEx(RS.Memory);
  finally
   RS.Free;
  end;
end;

but I got " Out of Memory " error in this line ( of CreateProcessEX ) :

  GetMem(InjectMemory, InjectSize);

can someone help me solve this error ? or give me some working code/solution ?

thanks before ...

Community
  • 1
  • 1
Mahmood_N
  • 521
  • 9
  • 20
  • what's the value of InjectSize? Perhaps the out of memory error is just what it seems – David Heffernan Jun 18 '11 at 11:49
  • 3
    why this question has been downvoted? – Premature Optimization Jun 18 '11 at 12:07
  • 3
    It's an interesting question but, wow, what a lot of work. Why not work *with* with operating system, not *against* it, and drop a file in the temp directory. – Ian Boyd Jun 18 '11 at 13:15
  • Did you try to compile and run the full example from your coderprofile-link? If that works then you should start from that and change it to use your executable instead. But like Ian suggests, dropping it into temp and executing it from there is much simpler and the user will not notice the difference. Unless you are doing this to try to bypass virus scanners. – Ville Krumlinde Jun 18 '11 at 14:03
  • @David Heffernan : InjectSize := ImageSize(FileMemory); , If it makes error , I think the "ImageSize" function may have problem – Mahmood_N Jun 18 '11 at 19:28
  • @Downvoter : I know who voted down !! , no Problem ... – Mahmood_N Jun 18 '11 at 19:29
  • @lan Boyd : I have an EXE file that user must not be able to access it and save/Copy it , if I drop file in a temp directory , accessing file will be easy for user ! , but if I execute it in memory , it`s not simple to dump it – Mahmood_N Jun 18 '11 at 19:30
  • what's the value of InjectSize? I can see how it is assigned but what is the value? – David Heffernan Jun 18 '11 at 19:30
  • @Ville Krumlinde : Yes , I tested it but I got same error ! – Mahmood_N Jun 18 '11 at 19:31
  • @David Heffernan : value is "1974665216" ( shown in Call Stack ) , I think InjectSize cause error , I tried this ( for testing ) : " InjectSize := 2*InjectSize; " , then no error shown , but I got an AV in second " for " loop after 8 step ( SectionLoop = 8 ) in this line : CopyMemory(FileData, pointer(dword(FileMemory) + PSections[SectionLoop].PointerToRawData), SectionSize); , I think the ImageSize function return incorrect size – Mahmood_N Jun 18 '11 at 20:10
  • @Mahmood_N I doubt that your exe really is 2GB in size. – David Heffernan Jun 18 '11 at 20:13
  • @David Heffernan : No , I Linked "calc.exe" ( windows Calculator ) to my Application as Resource ( for Testing ) – Mahmood_N Jun 18 '11 at 20:37
  • Has it occurred to you that such a complex solution relying as it does on undocumented details might not be the right solution to your problem, whatever that might be. – David Heffernan Jun 18 '11 at 21:16

3 Answers3

6

An excelent unit for what you need has already been done with support for windows 64 bit.

you can find it here: uExecFromMem by steve10120 fixed by test

here is a trivial approach written by me if you don't want to use that unit

var
eu:array of byte;
FS:TFileStream;
CONT:TContext;
imgbase,btsIO:DWORD;
IDH:PImageDosHeader;
INH:PImageNtHeaders;
ISH:PImageSectionHeader;
i:Integer;
PInfo:TProcessInformation;
SInfo:TStartupInfo;
begin
if OpenDialog1.Execute then
  begin
    FS:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead or fmShareDenyNone);
    SetLength(eu,FS.Size);
    FS.Read(eu[0],FS.Size);
    FS.Free;
    Sinfo.cb:=Sizeof(TStartupInfo);
    CreateProcess(nil,Pchar(paramstr(0)),nil,nil,FALSE,CREATE_SUSPENDED,nil,nil,SInfo,PInfo);
    IDH:=@eu[0];
    INH:=@eu[IDH^._lfanew];
    imgbase:=DWORD(VirtualAllocEx(PInfo.hProcess,Ptr(INH^.OptionalHeader.ImageBase),INH^.OptionalHeader.SizeOfImage,MEM_COMMIT or MEM_RESERVE,PAGE_EXECUTE_READWRITE));
    ShowMessage(IntToHex(imgbase,8));
    WriteProcessMemory(PInfo.hProcess,Ptr(imgbase),@eu[0],INH^.OptionalHeader.SizeOfHeaders,btsIO);
    for i:=0 to INH^.FileHeader.NumberOfSections - 1 do
      begin
          ISH:=@eu[IDH^._lfanew + Sizeof(TImageNtHeaders) + i * Sizeof(TImageSectionHeader)];
          WriteProcessMemory(PInfo.hProcess,Ptr(imgbase + ISH^.VirtualAddress),@eu[ISH^.PointerToRawData],ISH^.SizeOfRawData,btsIO);
      end;
    CONT.ContextFlags:=CONTEXT_FULL;
    GetThreadContext(PInfo.hThread,CONT);
    CONT.Eax:=imgbase + INH^.OptionalHeader.AddressOfEntryPoint;
    WriteProcessMemory(PInfo.hProcess,Ptr(CONT.Ebx+8),@imgbase,4,btsIO);
    ShowMessage('Press ok on ENTER');
    SetThreadContext(PInfo.hThread,CONT);
    ResumeThread(PInfo.hThread);
    CloseHandle(Pinfo.hThread);
    CloseHandle(PInfo.hProcess);
  end;
end;
J...
  • 30,968
  • 6
  • 66
  • 143
opc0de
  • 11,557
  • 14
  • 94
  • 187
  • Thanks , But there is no declaration for "SInfo" and "PInfo" – Mahmood_N Jun 19 '11 at 23:57
  • 2
    I added them... though if you tried a little you should figured it out btw don't forget to set imagebase to $1000000 – opc0de Jun 20 '11 at 06:55
  • @opc0de Please provide a working solution for 64 bit PE files. –  Sep 05 '13 at 11:25
  • @user2665920 you are doing something wrong, the code has been tested or de executable has something special. Did you set IMAGE BASE ? – opc0de Sep 05 '13 at 13:38
  • @opc0de Nope i didnt set it :D. So this code can be used for 32 bit and 64 bit PE files? –  Sep 05 '13 at 13:44
  • Only 32 but set the image base like this {$IMAGEBASE $1000000} http://docwiki.embarcadero.com/RADStudio/XE4/en/Image_base_address – opc0de Sep 05 '13 at 13:47
  • @user2665920 checkout the answer above for 64 bit ;) – opc0de Sep 05 '13 at 13:47
  • @opc0de No not 64 bit OS.. 64 bit PE ! :D not the same thing.. Also what you think of [LINK](http://www.ic0de.org/showthread.php?10948-Executing-x64-pe-from-memory&p=59272#post59272), would this work? –  Sep 05 '13 at 15:03
  • Tried uExecFromMem and works excellent AFTER switch off virus warning "Virtool:Win32/DelfInject.gen!BI" for the compiled version of this code. I'm afraid this can't be used in genuine programs, sad :-( – Codebeat Feb 21 '15 at 05:57
3

To get opc0de's answer working on both 32bit and 64bit platforms change the context setting as follows,

   GetThreadContext(PInfo.hThread,CONT);
   {$IFDEF WIN64}
      CONT.P6Home:=imgbase + INH^.OptionalHeader.AddressOfEntryPoint;
      WriteProcessMemory(PInfo.hProcess,Ptr(CONT.P3Home+8),@imgbase,4,btsIO);
   {$ELSE}
      CONT.Eax:=imgbase + INH^.OptionalHeader.AddressOfEntryPoint;
      WriteProcessMemory(PInfo.hProcess,Ptr(CONT.Ebx+8),@imgbase,4,btsIO);
   {$ENDIF}
   ShowMessage('Press ok on ENTER');
   SetThreadContext(PInfo.hThread,CONT);
Community
  • 1
  • 1
2

Your expected API pointer layout sounds not correct, and the returned size is not.

How did you define all the PImageNtHeaders and such TSections types? What is the record alignment? Shouldn't it need to be packed or aligned with some granularity? Perhaps you forgot some {$A..} or enumeration size when copy/paste the original code into your unit...

Difficult to guess without the whole source code.

Arnaud Bouchez
  • 42,305
  • 3
  • 71
  • 159