Recent

Author Topic: PE file injection  (Read 4352 times)

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: PE file injection
« Reply #15 on: October 23, 2020, 03:54:56 pm »
What @PascalDragon stated above is exactly right.  While x64 allows addressing using RIP relative offsets, it is still very common for items in an executable to be referenced with absolute addresses which requires a relocations table in order for the .exe to be relocatable.
Yeah. I've checked many EXE files, and all of a sudden many of them have reloc section inside. Problem is - it's not guaranteed to exist. At some point it was common practice to strip it to decrease EXE size, as it's actually useless now, when paging and virtual addressing allows image to always have preferred base address. Many files are like this one, that was actually compiled via Lazarus:
Code: [Select]
Old Executable Header

DOS File Size                                     30C013h  (3194899. )
Load Image Size                                      450h  (  1104. )
Relocation Table entry count                          0000h  (     0. )
Relocation Table address                              0040h  (    64. )
Size of header record      (in paragraphs)            0004h  (     4. )
Minimum Memory Requirement (in paragraphs)            0000h  (     0. )
Maximum Memory Requirement (in paragraphs)            FFFFh  ( 65535. )
File load checksum                                    0000h  (     0. )
Overlay Number                                        0000h  (     0. )

Initial Stack Segment  (SS:SP)   0000:00B8
Program Entry Point    (CS:IP)   0000:0000


Portable Executable (PE) File

Header base: 00000080

CPU type                 80386
Flags                    10F [ fixed executable backwards 32bit ]
DLL flags                0000 [ ]
Linker Version           3.4
Time stamp               00000000
O/S Version              4.0
User Version             1.0
Subsystem Version        4.0
Subsystem                0002 [ Windows GUI ]
Object count             00000008
Symbols offset           0030C000
Symbols count            00000000
Optional header size     00E0
Magic #                  10B
Code size                001D2CC0
Init Data size           0005FFF4
Uninit Data size         00008A74
Entry RVA                001D3C70
Image base               00400000
Code base                00001000
Data base                001D4000
Object/File align        00001000/00000200
Reserved                 00000000
Image size               00318000
Header size              00000400
Checksum                 00000000
Stack reserve/commit     01000000/00001000
Heap reserve/commit      00100000/00001000
Number interesting RVAs  00000010
Name                   RVA       Size 
------------------  --------  --------
Exports             00000000  00000000
Imports             002C2000  000000DC
Resources           002C5000  00051B6C
Exceptions          00000000  00000000
Security            00000000  00000000
Fixups              00000000  00000000
Debug               00000000  00000000
Description         00000000  00000000
TLS                 00233FA0  00000018
Callbacks           00000000  00000000
reserved            00000000  00000000
reserved            002C2830  00000754
reserved            00000000  00000000
reserved            00000000  00000000
reserved            00000000  00000000

Object table:
#   Name      VirtSize    RVA     PhysSize  Phys off  Flags   
--  --------  --------  --------  --------  --------  --------
01  .text     001D2CC0  00001000  001D2E00  00000400  60000020 [CER]
02  .data     0005FFF4  001D4000  00060000  001D3200  C0000040 [IRW]
03  .rdata    00083D1C  00234000  00083E00  00233200  40000040 [IR]
04  .bss      00008A74  002B8000  00000000  00000000  C0000080 [URW]
05  .CRT      0000000C  002C1000  00000200  002B7000  C0000040 [IRW]
06  .idata    00002F07  002C2000  00003000  002B7200  C0000040 [IRW]
07  .rsrc     00051B6C  002C5000  00051C00  002BA200  C0000040 [IRW]
08  /4        00000018  00317000  00000200  0030BE00  42000040 [IDR]

Key to section flags:
  C - contains code
  D - discardable
  E - executable
  I - contains initialized data
  R - readable
  U - contains uninitialized data
  W - writeable
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

440bx

  • Hero Member
  • *****
  • Posts: 3921
Re: PE file injection
« Reply #16 on: October 23, 2020, 04:11:07 pm »
Problem is - it's not guaranteed to exist.
true, particularly for .exe. For dlls it's quite common for the relocation table to exist, though still not guaranteed.

At some point it was common practice to strip it to decrease EXE size, as it's actually useless now, when paging and virtual addressing allows image to always have preferred base address.
it is not useless.  ASLR depends on the existence of the relocations table.  It's easier to compromise a .exe that does not support ASLR than one that does.

Many files are like this one, that was actually compiled via Lazarus:
executables (.exe) produced with Lazarus/FPC are non relocatable by default, i.e, do not include a relocations table but, IMO, that's a questionable choice because the resulting executables are more vulnerable to simple address-based exploits.  I believe it would be preferable for the default to be to produce a relocatable .exe.  Most likely that choice was made to reduce the executable's size at the expense of relocatability.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: PE file injection
« Reply #17 on: October 23, 2020, 05:00:28 pm »
true, particularly for .exe. For dlls it's quite common for the relocation table to exist, though still not guaranteed.
DLLs have to be relocatable, because most of them use same standard base address. I.e. base address for EXE is guaranteed to be free, as EXE image is first thing, that is loaded into virtual address space. But it's not true for DLLs. There are many of them and they can use the same preferred base address.
executables (.exe) produced with Lazarus/FPC are non relocatable by default, i.e, do not include a relocations table but, IMO, that's a questionable choice because the resulting executables are more vulnerable to simple address-based exploits.  I believe it would be preferable for the default to be to produce a relocatable .exe.  Most likely that choice was made to reduce the executable's size at the expense of relocatability.
Dunno. I think, that stripped relocs protect EXE against modifications and injections. Relocs are info about all pointers inside EXE. This information can be used to do bad things.
« Last Edit: October 23, 2020, 05:05:25 pm by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

440bx

  • Hero Member
  • *****
  • Posts: 3921
Re: PE file injection
« Reply #18 on: October 23, 2020, 05:56:29 pm »
DLLs have to be relocatable, because most of them use same standard base address.
Dlls _don't_ have to be relocatable.  A programmer can set the preferred address in a group of dlls in such a way that there will be no "collisions" among them, thereby obviating the need for a relocations table.  This is fairly easy to do.

The problem is that many compiler/linkers default to the same load address but, the load address can be changed by the programmer as needed to ensure there are no load collisions in a group of dlls.

I.e. base address for EXE is guaranteed to be free, as EXE image is first thing, that is loaded into virtual address space.
That is true now but, when Win32s was being used, the load address of an NT executable was too low in the address space of Win9x to be loaded at that address.  This forced NT exectables that ran on Win9x to have a relocation table (either that or rebase them to the Win9x default load address.)

Dunno. I think, that stripped relocs protect EXE against modifications and injections. Relocs are info about all pointers inside EXE. This information can be used to do bad things.
No doubt that the relocation table information can be used for nefarious purposes but, non-relocatable exes open the door to other problems.  Most virus code is carefully coded to be independent of the location in which they reside, very few viruses, if any, have relied on the presence of a relocations table to infect an exe or dll.

There are pros and cons to both relocatable and non-relocatable. 
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: PE file injection
« Reply #19 on: October 23, 2020, 07:56:30 pm »
Dlls _don't_ have to be relocatable.  A programmer can set the preferred address in a group of dlls in such a way that there will be no "collisions" among them, thereby obviating the need for a relocations table.  This is fairly easy to do.

The problem is that many compiler/linkers default to the same load address but, the load address can be changed by the programmer as needed to ensure there are no load collisions in a group of dlls.
In this case all DLL should have different base addresses and it's not possible. At the end it's loader, that decides, what base address to give to particular DLL. For example even if you'll set proper base addresses for all your DLLs, some other 3rd party DLL still can take one of the addresses, that will force your DLL to relocate. Or may be some other DLL won't be able to load at it's preferred address and loader will decide to give your DLL's preferred address to it. Nothing is guaranteed.
That is true now but, when Win32s was being used, the load address of an NT executable was too low in the address space of Win9x to be loaded at that address.  This forced NT exectables that ran on Win9x to have a relocation table (either that or rebase them to the Win9x default load address.)
No, that's because 386 lacked paging, i.e. virtual addressing support, so all applications were loaded into one single address space, that caused the same problem, as DLL problem described above.
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

440bx

  • Hero Member
  • *****
  • Posts: 3921
Re: PE file injection
« Reply #20 on: October 23, 2020, 08:30:39 pm »
In this case all DLL should have different base addresses and it's not possible.
Not only it is possible, it is very easy to accomplish limited to the dlls you have written that your program uses.  It's trivial to create a group of dlls that load at fixed addresses as long as they are _your_ dlls, IOW, not dlls written by someone else.

At the end it's loader, that decides, what base address to give to particular DLL. For example even if you'll set proper base addresses for all your DLLs, some other 3rd party DLL still can take one of the addresses, that will force your DLL to relocate. Or may be some other DLL won't be able to load at it's preferred address and loader will decide to give your DLL's preferred address to it. Nothing is guaranteed.
Once you know the address the loader decided to use to load your dll, you can use that address as your dll's prefered load address either by using a rebase program or doing a compile/link omitting the relocation table.

No, that's because 386 lacked paging, i.e. virtual addressing support, so all applications were loaded into one single address space, that caused the same problem, as DLL problem described above.
The i386 supported paging and a 32bit instruction set and memory space (4GB).  You're probably thinking about pre-i386 programs which did not use the PE format, they used the NE format where memory management was done using selectors.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: PE file injection
« Reply #21 on: October 23, 2020, 09:59:29 pm »
The i386 supported paging and a 32bit instruction set and memory space (4GB).  You're probably thinking about pre-i386 programs which did not use the PE format, they used the NE format where memory management was done using selectors.
That means I don't remember processor history. It's not processor. It were Win32s (wrapping around Win16 without paging) and early Win95, that were putting programs into one shared address space, so relocation was required.

P.S. Tested my program on Win32s. It works, but looks ugly without driver, that supports 256 colors. Does Win 3.1 even support 256 colors? It's built-in SVGA driver is broken. Where can I find working one?
« Last Edit: October 23, 2020, 10:10:21 pm by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: PE file injection
« Reply #22 on: October 23, 2020, 10:11:15 pm »
Again, relocation nowadays is required if you want to use ASLR and it's also required on both arm-win32 and aarch64-win64 as on these platforms Windows will refuse to start the binaries otherwise.

440bx

  • Hero Member
  • *****
  • Posts: 3921
Re: PE file injection
« Reply #23 on: October 23, 2020, 11:05:21 pm »
P.S. Tested my program on Win32s. It works, but looks ugly without driver, that supports 256 colors. Does Win 3.1 even support 256 colors? It's built-in SVGA driver is broken. Where can I find working one?
It's been way too long since I was involved with Win 3.1, in spite of that, I am fairly sure that it did support 256 colors.  As far as finding an SVGA driver for it, I doubt there is a video card around that makes a Win 3.1 driver available.

A possible alternative, which I have _not_ tried with Win 3.1, is to install it in a virtual machine.  VMware workstation has a "default" SVGA driver for the O/Ss it supports.  That _might_ satisfy your needs.  Note, this is just a guess based on my experience with VMware, I don't really know what it can do with Win 3.1.

HTH.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: PE file injection
« Reply #24 on: October 23, 2020, 11:35:21 pm »
Hi!

The number of colors in Win 3.1 was depending on the proprietray driver of your graphic adaptor.

But there was no default way how your graphic card had to do that.
In those times true color was rare and expensive.
There were funny things around like 15 bit (3x5 bit).

The best way to find something compatible today is indeed SVGA.

Winni

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: PE file injection
« Reply #25 on: October 24, 2020, 06:34:49 am »
Haven't found 15/16bit driver. Patched svga256.drv. It looks better, but colors are still distorted a little bit.

UPD S3 Trio driver worked for me on DOSBOX

UPD2 With QEMU things are little bit harder. It supports Cirrus Logic video adapter. Driver works well, but all of a sudden my program doesn't work.
« Last Edit: October 24, 2020, 09:23:47 am by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: PE file injection
« Reply #26 on: October 25, 2020, 08:54:48 am »
Here, I've found my old code. Of course it's very old, so it's not even formatted properly. It's also 32bit only and not guaranteed to work. It injects stub, that calls CanRunGame routine from RunGame32.dll. If it succeeds - control is passed to EXE entry point. If not - program terminates.

It requires free padding alignment space in section table in order to inject new section.

Disclimer: This programs have been made for fun and learning purposes. They're provided as is and aren't guaranteed to work.
Code: Pascal  [Select][+][-]
  1. unit ProtectMain;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs, StdCtrls, Buttons, ImageHlp, Mask, Spin;
  8.  
  9. type
  10.   TForm1 = class(TForm)
  11.     Memo1: TMemo;
  12.     BitBtn1: TBitBtn;
  13.     BitBtn2: TBitBtn;
  14.     BitBtn3: TBitBtn;
  15.     OpenDialog1: TOpenDialog;
  16.     SpinEdit1: TSpinEdit;
  17.     procedure BitBtn2Click(Sender: TObject);
  18.     procedure BitBtn3Click(Sender: TObject);
  19.     procedure FormDestroy(Sender: TObject);
  20.     procedure BitBtn1Click(Sender: TObject);
  21.   private
  22.     { Private declarations }
  23.   public
  24.     { Public declarations }
  25.     LoadedImage:TLoadedImage;
  26.     Mapped:boolean;
  27.     ImageBase:longword;
  28.     EntryRVA,EntryVA:longword;
  29.     ImportRVA,ImportATRVA:longword;
  30.     FileAlign,SectAlign:longword;
  31.     HeaderFree,HeaderOffset:longword;
  32.     ImageSize,FileSize:longword;
  33.     CodeSize,ImportSize,ImportATSize:longword;
  34.     SectionCount:longword;
  35.     procedure DoUnmap;
  36.     function DoAlign(Size,Align:longword):longword;
  37.     function DoSize(Size,Align:longword):longword;
  38.   end;
  39.  
  40.   TMyCode=packed record
  41.   {My code}
  42.   Push:byte;{+0}
  43.   DataRVA:longword;{+1}
  44.   CallCanRunGame:word;{+5}
  45.   CanRunGameVA:longword;{+7}
  46.   OrEAXEAX:word;{+11}
  47.   JneOldEP:word;{+13}
  48.   OldEntryPointRA:longword;{+15}
  49.   PushEAX:byte;{+19}
  50.   CallExitProcess:word;{+20}
  51.   ExitProcessVA:longword;{+22}
  52.   {My data}
  53.   ImportRVA:longword;{+26}
  54.   ImportSize:longword;
  55.   ImportATRVA:longword;
  56.   ImportATSize:longword;
  57.   EntryRVA:longword;
  58.   ImageSize:longword;
  59.   CheckSum:longword;
  60.   VirtCDID:longword;
  61.   {My import}
  62.   KernelILT:longword;{+54}
  63.   KernelTimeDate:longword;
  64.   KernelChain:longword;
  65.   KernelName:longword;
  66.   KernelIAT:longword;
  67.   RunGameILT:longword;{+74}
  68.   RunGameTimeDate:longword;
  69.   RunGameChain:longword;
  70.   RunGameName:longword;
  71.   RunGameIAT:longword;
  72.   NullILT:longword;{+94}
  73.   NullTimeDate:longword;
  74.   NullChain:longword;
  75.   NullName:longword;
  76.   NullIAT:longword;
  77.   ExitProcessIAT:longword;{+114}
  78.   KernelNullIAT:longword;{+118}
  79.   CanRunGameIAT:longword;{+122}
  80.   RunGameNullIAT:longword;{+126}
  81.   ExitProcessILT:longword;{+130}
  82.   KernelNullILT:longword;{+134}
  83.   CanRunGameILT:longword;{+138}
  84.   RunGameNullILT:longword;{+142}
  85.   DllKernel:array[0..13] of char;{Kernel32.dll__}{+146}
  86.   DllRunGame:array[0..13] of char;{RunGame32.dll_}{+160}
  87.   ExitProcessHint:word;{+174}
  88.   ExitProcessName:array[0..11] of char;{ExitProcess_}{+176}
  89.   CanRunGameHint:word;{+188}
  90.   CanRunGameName:array[0..11] of char;{CanRunGame__}{+190}
  91.   {+202}
  92.   end;
  93.  
  94. const
  95.  
  96. PushOffset=$68;
  97. CallPtr=$15ff;
  98. CallRel=$E8;
  99. OrEaxEax=$C009;
  100. JneRel=$850f;
  101. PushEax=$50;
  102.  
  103. {DataRVA=OurRVA+DataOffset}
  104. {DataOffset=26;}
  105.  
  106. {ExitProcessIAT=ImageBase+OurRVA+ExitProcessIATOffset}
  107. {ExitProcessIATOffset=114;}
  108.  
  109. {CanRunGameIAT=ImageBase+OurRVA+CanRunGameIATOffset}
  110. {CanRunGameIATOffset=122;}
  111.  
  112. {OldEntryPointRA=OldEntryPointRVA-(OurRVA+OldEntryPointOffset}
  113. {OldEntryPointOffset=19;}
  114.  
  115. {ExitProcessIAT=ExitProcessILT=OurRVA+ExitProcessOffset}
  116. {ExitProcessOffset=174;}
  117. {CanRunGameIAT=CanRunGameILT=OurRVA+CanRunGameOffset}
  118. {CanRunGameOffset=188;}
  119.  
  120. {KernelILT=OurRVA+KernelILTOffset}
  121. {KernelILTOffset=130;}
  122. {KernelIAT=OurRVA+KernelIATOffset}
  123. {KernelIATOffset=114;}
  124.  
  125. {RunGameILT=OurRVA+RunGameILTOffset}
  126. {RunGameILTOffset=138;}
  127. {RunGameIAT=OurRVA+RunGameIATOffset}
  128. {RunGameIATOffset=122;}
  129.  
  130. {KernelName=OurRVA+KernelNameOffset}
  131. {KernelNameOffset=146;}
  132. {RunGameName=OurRVA+RunGameNameOffset}
  133. {RunGameNameOffset=160;}
  134.  
  135. {OurImportOffset=54;
  136. OurImportSize=202-54;
  137.  
  138. OurImportATOffset=114;
  139. OurImportATSize=130-114;}
  140.  
  141. SectName:array[0..7] of char='.gameprt';
  142.  
  143. TM_OK=' Ok';
  144. TM_NOK=' !!!';
  145.  
  146. var
  147.   Form1: TForm1;
  148.   MyCode:TMyCode=(
  149.   Push:PushOffset;
  150.   CallCanRunGame:CallPtr;
  151.   OrEaxEax:OrEaxEax;
  152.   JneOldEP:JneRel;
  153.   PushEax:PushEax;
  154.   CallExitProcess:CallPtr;
  155.   DllKernel:'Kernel32.dll'+Chr(0);
  156.   DllRunGame:'RunGame32.dll'+Chr(0);
  157.   ExitProcessName:'ExitProcess'+Chr(0);
  158.   CanRunGameName:'CanRunGame'+Chr(0)+Chr(0);
  159.   );
  160.  
  161. DataOffset,
  162. ExitProcessIATOffset,
  163. CanRunGameIATOffset,
  164. OldEntryPointOffset,
  165. ExitProcessOffset,
  166. CanRunGameOffset,
  167. KernelILTOffset,
  168. KernelIATOffset,
  169. RunGameILTOffset,
  170. RunGameIATOffset,
  171. KernelNameOffset,
  172. RunGameNameOffset,
  173. OurImportOffset,
  174. OurImportSize,
  175. OurImportATOffset,
  176. OurImportATSize
  177. :longword;
  178.  
  179. implementation
  180.  
  181. {$R *.dfm}
  182.  
  183. function GetOffset(Org,Dest:pointer):longword;
  184. begin
  185. Result:=longword(Dest)-longword(Org);
  186. end;
  187.  
  188. function GetSize(Org,Dest:pointer;Size:longword):longword;
  189. begin
  190. Result:=longword(Dest)-longword(Org)+Size;
  191. end;
  192.  
  193. procedure TForm1.DoUnmap;
  194. begin
  195. UnmapAndLoad(@LoadedImage);
  196. end;
  197.  
  198. procedure TForm1.BitBtn2Click(Sender: TObject);
  199. begin
  200. Close;
  201. end;
  202.  
  203. procedure TForm1.BitBtn3Click(Sender: TObject);
  204. var Temp:string;
  205. begin
  206. if Mapped then DoUnmap;
  207. Memo1.Clear;
  208. Memo1.Lines.Add('Selecting image file...');
  209. ForceCurrentDirectory:=true;
  210. if not OpenDialog1.Execute then Exit;
  211. Memo1.Lines.Add('Trying to load image file...');
  212. if not MapAndLoad(PChar(OpenDialog1.FileName),nil,@LoadedImage,false,false) then begin
  213. Memo1.Lines.Add('**Error**:Can not load '+OpenDialog1.FileName);
  214. Exit;
  215. end;
  216. Memo1.Lines.Add('Getting information...');
  217. Memo1.Lines.Add(StringOfChar('-',120));
  218. Memo1.Lines.Add('Image file status:');
  219. Memo1.Lines.Add(StringOfChar('-',120));
  220. ImageBase:=LoadedImage.FileHeader.OptionalHeader.ImageBase;
  221. Memo1.Lines.Add('Image base'+StringOfChar(Chr(9),4)+IntToHex(ImageBase,8));
  222. EntryRVA:=LoadedImage.FileHeader.OptionalHeader.AddressOfEntryPoint;
  223. EntryVA:=ImageBase+EntryRVA;
  224. Memo1.Lines.Add('Entry RVA/VA'+StringOfChar(Chr(9),4)+
  225. IntToHex(EntryRVA,8)+'/'+IntToHex(EntryVA,8));
  226. FileAlign:=LoadedImage.FileHeader.OptionalHeader.FileAlignment;
  227. SectAlign:=LoadedImage.FileHeader.OptionalHeader.SectionAlignment;
  228. Memo1.Lines.Add('File/Section alignment'+StringOfChar(Chr(9),3)+
  229. IntToHex(FileAlign,8)+'/'+IntToHex(SectAlign,8));
  230. HeaderOffset:=GetImageUnusedHeaderBytes(@LoadedImage,HeaderFree);
  231. if HeaderFree>=IMAGE_SIZEOF_SECTION_HEADER then Temp:=TM_OK else Temp:=TM_NOK;
  232. Memo1.Lines.Add('Header free space/Required free space'
  233. +Chr(9)+Chr(9)+IntToStr(HeaderFree)+'/'
  234. +IntToStr(IMAGE_SIZEOF_SECTION_HEADER)+StringOfChar(Chr(9),2)+Temp);
  235. ImageSize:=LoadedImage.FileHeader.OptionalHeader.SizeOfImage;
  236. CodeSize:=LoadedImage.FileHeader.OptionalHeader.SizeOfCode;
  237. ImportSize:=LoadedImage.FileHeader.OptionalHeader.DataDirectory[1].Size;
  238. ImportATSize:=LoadedImage.FileHeader.OptionalHeader.DataDirectory[12].Size;
  239. FileSize:=SetFilePointer(LoadedImage.hFile,0,nil,FILE_END);
  240. Memo1.Lines.Add('Image size/File size'+StringOfChar(Chr(9),4)+
  241. IntToHex(ImageSize,8)+'/'+IntToHex(FileSize,8));
  242. Memo1.Lines.Add('Code size/Import size'+StringOfChar(Chr(9),3)+
  243. IntToHex(CodeSize,8)+'/'+IntToHex(ImportSize,8));
  244. SectionCount:=LoadedImage.FileHeader.FileHeader.NumberOfSections;
  245. Memo1.Lines.Add('Section count'+StringOfChar(Chr(9),5)+IntToStr(SectionCount));
  246. ImportRVA:=LoadedImage.FileHeader.OptionalHeader.DataDirectory[1].VirtualAddress;
  247. ImportATRVA:=LoadedImage.FileHeader.OptionalHeader.DataDirectory[12].VirtualAddress;
  248. BitBtn1.Enabled:=true;
  249. end;
  250.  
  251. procedure TForm1.FormDestroy(Sender: TObject);
  252. begin
  253. if Mapped then DoUnmap;
  254. end;
  255.  
  256. procedure TForm1.BitBtn1Click(Sender: TObject);
  257. var MyFile:THandle;
  258. MyMapping:THandle;
  259. PhysSize,VirtSize:longword;
  260. Data:pointer;
  261. OurRVA:longword;
  262. I:integer;
  263. MySection:PImageSectionHeader;
  264. CDID:longword;
  265. begin
  266. try
  267. CDID:=StrToInt(SpinEdit1.Text);
  268. except
  269. Exit;
  270. end;
  271. DataOffset:=GetOffset(@MyCode,@MyCode.ImportRVA);
  272. ExitProcessIATOffset:=GetOffset(@MyCode,@MyCode.ExitProcessIAT);
  273. CanRunGameIATOffset:=GetOffset(@MyCode,@MyCode.CanRunGameIAT);
  274. OldEntryPointOffset:=GetOffset(@MyCode,@MyCode.PushEAX);
  275. ExitProcessOffset:=GetOffset(@MyCode,@MyCode.ExitProcessHint);
  276. CanRunGameOffset:=GetOffset(@MyCode,@MyCode.CanRunGameHint);
  277. KernelILTOffset:=GetOffset(@MyCode,@MyCode.ExitProcessILT);
  278. KernelIATOffset:=GetOffset(@MyCode,@MyCode.ExitProcessIAT);
  279. RunGameILTOffset:=GetOffset(@MyCode,@MyCode.CanRunGameILT);
  280. RunGameIATOffset:=GetOffset(@MyCode,@MyCode.CanRunGameIAT);
  281. KernelNameOffset:=GetOffset(@MyCode,@MyCode.DllKernel);
  282. RunGameNameOffset:=GetOffset(@MyCode,@MyCode.DllRunGame);
  283. OurImportOffset:=GetOffset(@MyCode,@MyCode.KernelILT);
  284. OurImportATOffset:=GetOffset(@MyCode,@MyCode.ExitProcessIAT);
  285. OurImportSize:=GetSize(@MyCode.KernelILT,@MyCode.CanRunGameName,SizeOf(MyCode.CanRunGameName));
  286. OurImportATSize:=GetSize(@MyCode.ExitProcessIAT,@MyCode.RunGameNullIAT,SizeOf(MyCode.RunGameNullIAT));
  287. MyFile:=LoadedImage.hFile;
  288. PhysSize:=FileAlign*DoAlign(SizeOf(MyCode),FileAlign);
  289. VirtSize:=SectAlign*DoAlign(SizeOf(MyCode),SectAlign);
  290. FileSize:=DoSize(FileSize,FileAlign);
  291. MyCode.ImageSize:=ImageSize;
  292. ImageSize:=DoSize(ImageSize,SectAlign);
  293. OurRVA:=ImageSize;
  294. MyCode.DataRVA:=OurRVA+DataOffset;
  295. MyCode.ImportRVA:=ImportRVA;
  296. MyCode.ImportSize:=ImportSize;
  297. if ((ImportATRVA=0) or (ImportATSize=0)) then begin
  298. MySection:=ImageRVAToSection(LoadedImage.FileHeader,LoadedImage.MappedAddress,ImportRVA);
  299. MyCode.ImportATRVA:=MySection.VirtualAddress;
  300. MyCode.ImportATSize:=MySection.Misc.VirtualSize;
  301. end
  302. else begin
  303. MyCode.ImportATRVA:=ImportATRVA;
  304. MyCode.ImportATSize:=ImportATSize;
  305. end;
  306. MyCode.EntryRVA:=EntryRVA;
  307. MyCode.CheckSum:=LoadedImage.FileHeader.OptionalHeader.CheckSum;
  308. MyCode.ExitProcessVA:=ImageBase+OurRVA+ExitProcessIATOffset;
  309. MyCode.CanRunGameVA:=ImageBase+OurRVA+CanRunGameIATOffset;
  310. MyCode.OldEntryPointRA:=EntryRVA-(OurRVA+OldEntryPointOffset);
  311. MyCode.VirtCDID:=CDID;
  312. MyCode.ExitProcessIAT:=OurRVA+ExitProcessOffset;
  313. MyCode.ExitProcessILT:=MyCode.ExitProcessIAT;
  314. MyCode.CanRunGameIAT:=OurRVA+CanRunGameOffset;
  315. MyCode.CanRunGameILT:=MyCode.CanRunGameIAT;
  316. MyCode.KernelILT:=OurRVA+KernelILTOffset;
  317. MyCode.KernelIAT:=OurRVA+KernelIATOffset;
  318. MyCode.KernelName:=OurRVA+KernelNameOffset;
  319. MyCode.RunGameILT:=OurRVA+RunGameILTOffset;
  320. MyCode.RunGameIAT:=OurRVA+RunGameIATOffset;
  321. MyCode.RunGameName:=OurRVA+RunGameNameOffset;
  322. UnmapViewOfFile(LoadedImage.MappedAddress);
  323. SetFilePointer(MyFile,FileSize+PhysSize,nil,FILE_BEGIN);
  324. SetEndOfFile(MyFile);
  325. MyMapping:=CreateFileMapping(MyFile,nil,PAGE_READWRITE,0,0,nil);
  326. Data:=MapViewOfFile(MyMapping,FILE_MAP_ALL_ACCESS,0,0,0);
  327. LoadedImage.MappedAddress:=Data;
  328. Inc(longword(Data),FileSize);
  329. ZeroMemory(Data,PhysSize);
  330. CopyMemory(Data,@MyCode,SizeOf(MyCode));
  331. longword(MySection):=longword(LoadedImage.Sections)+
  332. SizeOf(TImageSectionHeader)*LoadedImage.NumberOfSections;
  333. for I:=0 to 7 do
  334. MySection.Name[I]:=byte(SectName[I]);
  335. MySection.Misc.VirtualSize:=SizeOf(MyCode);
  336. MySection.VirtualAddress:=ImageSize;
  337. MySection.SizeOfRawData:=PhysSize;
  338. MySection.PointerToRawData:=FileSize;
  339. MySection.PointerToRelocations:=0;
  340. MySection.PointerToLinenumbers:=0;
  341. MySection.NumberOfRelocations:=0;
  342. MySection.NumberOfLinenumbers:=0;
  343. MySection.Characteristics:=
  344. IMAGE_SCN_CNT_CODE or IMAGE_SCN_MEM_EXECUTE or IMAGE_SCN_MEM_READ;
  345. Inc(LoadedImage.NumberOfSections);
  346. LoadedImage.SizeOfImage:=ImageSize+VirtSize;
  347. Inc(LoadedImage.FileHeader.FileHeader.NumberOfSections);
  348. LoadedImage.FileHeader.OptionalHeader.AddressOfEntryPoint:=OurRVA;
  349. LoadedImage.FileHeader.OptionalHeader.SizeOfImage:=ImageSize+VirtSize;
  350. LoadedImage.FileHeader.OptionalHeader.DataDirectory[1].VirtualAddress:=OurRVA+OurImportOffset;
  351. LoadedImage.FileHeader.OptionalHeader.DataDirectory[1].Size:=OurImportSize;
  352. LoadedImage.FileHeader.OptionalHeader.DataDirectory[12].VirtualAddress:=OurRVA+OurImportATOffset;
  353. LoadedImage.FileHeader.OptionalHeader.DataDirectory[12].Size:=OurImportATSize;
  354. LoadedImage.FileHeader.OptionalHeader.CheckSum:=0;
  355. BitBtn1.Enabled:=false;
  356. end;
  357.  
  358. function TForm1.DoAlign(Size,Align:longword):longword;
  359. begin
  360. Result:=1;
  361. while (Size>(Result*Align)) do Inc(Result);
  362. end;
  363.  
  364. function TForm1.DoSize(Size,Align:longword):longword;
  365. begin
  366. if (Size mod Align)<>0 then
  367. Result:=Size+Align-(Size mod Align)
  368. else Result:=Size;
  369. end;
  370.  
  371. end.
  372.  
Another fun program. NFS 4 patch to make it run on WinXP.
Code: Pascal  [Select][+][-]
  1. unit PatchMain;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs, StdCtrls, ImageHlp;
  8.  
  9. type
  10.   TForm1 = class(TForm)
  11.     Memo1: TMemo;
  12.     Button1: TButton;
  13.     Button2: TButton;
  14.     OpenDialog1: TOpenDialog;
  15.     procedure Button2Click(Sender: TObject);
  16.     procedure Button1Click(Sender: TObject);
  17.     procedure FormCreate(Sender: TObject);
  18.   private
  19.     { Private declarations }
  20.   public
  21.     { Public declarations }
  22.     procedure Print(S:string);
  23.     function EightByteToString(var S:array of byte):string;
  24.     function DoSize(Size,Align:longword):longword;
  25.   end;
  26.  
  27. var
  28.   Form1: TForm1;
  29.  
  30. implementation
  31.  
  32. {$R *.dfm}
  33.  
  34. procedure TForm1.Button2Click(Sender: TObject);
  35. begin
  36. Close;
  37. end;
  38.  
  39. procedure TForm1.Button1Click(Sender: TObject);
  40. var LI:TLoadedImage;
  41. I:longword;Section:PImageSectionHeader;
  42. begin
  43. if not OpenDialog1.Execute then Exit;
  44. Print('Loading file '+OpenDialog1.FileName);
  45. if not MapAndLoad(PChar(OpenDialog1.FileName),nil,
  46. @LI,false,false) then begin
  47. Print('**Error**:Can not load '+OpenDialog1.FileName);
  48. Exit;
  49. end;
  50. Print('Changing version data...');
  51. LI.FileHeader.OptionalHeader.MajorOperatingSystemVersion:=4;
  52. LI.FileHeader.OptionalHeader.MinorOperatingSystemVersion:=0;
  53. Print('OS version changed to "4.0"');
  54. LI.FileHeader.OptionalHeader.MajorSubsystemVersion:=4;
  55. LI.FileHeader.OptionalHeader.MinorSubsystemVersion:=0;
  56. Print('Subsistem version changed to "4.0"');
  57. for I:=0 to LI.NumberOfSections-1 do begin
  58. longword(Section):=longword(LI.Sections)+I*SizeOf(TImageSectionHeader);
  59. if Section.Misc.VirtualSize<Section.SizeOfRawData then begin
  60. if Section.PointerToRawData<>0 then
  61. Section.Misc.VirtualSize:=
  62. DoSize(Section.SizeOfRawData,LI.FileHeader.OptionalHeader.SectionAlignment)
  63. else begin
  64. Section.Misc.VirtualSize:=Section.SizeOfRawData;
  65. Section.SizeOfRawData:=0;
  66. end;
  67. Print('Fixing section size for section '
  68. +EightByteToString(Section.Name)+Chr(9)+'Changed');
  69. end
  70. else begin
  71. Print('Fixing section size for section '
  72. +EightByteToString(Section.Name)+Chr(9)+'Skipped');
  73. end;
  74. end;
  75. Print('Unloading file '+OpenDialog1.FileName);
  76. if not UnmapAndLoad(@LI) then
  77. Print('**Error**:Can not apply changes to file '+OpenDialog1.FileName);
  78. Button1.Enabled:=false;
  79. end;
  80.  
  81. procedure TForm1.Print(S:string);
  82. begin
  83. Memo1.Lines.Add(S);
  84. end;
  85.  
  86. function TForm1.EightByteToString(var S:array of byte):string;
  87. var I:integer;
  88. begin
  89. I:=0;
  90. Result:='';
  91. while S[I]<>0 do begin
  92. Result:=Result+char(S[I]);
  93. Inc(I);
  94. end;
  95. end;
  96.  
  97. function TForm1.DoSize(Size,Align:longword):longword;
  98. begin
  99. if (Size mod Align)<>0 then
  100. Result:=Size+Align-(Size mod Align)
  101. else Result:=Size;
  102. end;
  103.  
  104. procedure TForm1.FormCreate(Sender: TObject);
  105. begin
  106. Print('NFS 4 High Stakes patch for Windows XP');
  107. Print(StringOfChar('_',50));
  108. Print('Usage: Just push "Load NFS..." and select file NFSHS.EXE');
  109. Print(StringOfChar('_',50));
  110. end;
  111.  
  112. end.
  113.  
« Last Edit: October 25, 2020, 09:16:25 am by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

BSaidus

  • Hero Member
  • *****
  • Posts: 538
  • lazarus 1.8.4 Win8.1 / cross FreeBSD
Re: PE file injection
« Reply #27 on: October 25, 2020, 06:33:33 pm »
Waw, thank you for all your help.
I found a couple of sites with a source code & libraries for doing this task.
You can dowload these libs ( written in delphi ) on
http://www.delphibasics.info/home/delphibasicscounterstrikewireleases/pe-inject
https://migeel.sk/programming/pe-inject/

thank you.
lazarus 1.8.4 Win8.1 / cross FreeBSD
dhukmucmur vernadh!

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: PE file injection
« Reply #28 on: October 25, 2020, 07:03:05 pm »
Of course best way to achieve this goal - is to compress host application, that will free some space inside it's sections, where our code can be injected. Image packagers do exactly this thing.

So, you can actually achieve your goal via modifying some open source image packager, like UPX. You need to add just several instructions, as main code can be placed in separate DLL, like in my example. And this code can be arbitrary.

But it was way too hard task for me back then. I didn't even have access to Internet back then.  :-[ So, as any EXE usually has 4Kb memory alignment (usually smaller file alignment to decrease image size, even 0), it usually has enough free padding space to inject something.
« Last Edit: October 25, 2020, 07:07:07 pm by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

 

TinyPortal © 2005-2018