Lazarus

Programming => General => Topic started by: BSaidus on October 21, 2020, 08:33:43 pm

Title: PE file injection
Post by: BSaidus on October 21, 2020, 08:33:43 pm
Hello,
For learning reasons I wonder if someone has a tutorials, simple examples on how to inject functions located in DLL into an already executable so the the executable starts executing functions injected then returns to normal execution.

Thanks
Title: Re: PE file injection
Post by: jamie on October 21, 2020, 11:36:48 pm
LOL  :P

You make me laugh  :-\

Build a DLL and load it at run time..
Title: Re: PE file injection
Post by: Mr.Madguy on October 22, 2020, 07:50:06 am
Hello,
For learning reasons I wonder if someone has a tutorials, simple examples on how to inject functions located in DLL into an already executable so the the executable starts executing functions injected then returns to normal execution.

Thanks
I had old project, that implemented "game club" functionality, i.e. enabling execution of applications via network, disabling it and terminating applications, when execution is disabled. Yeah, there are better ways to do it, but I used code injection to achieve this goal. Also for learning purposes. All code itself was in DLL. Only one DLL routine call was injected into EXE, i.e. very small chunk of code. I don't have this project on my computer now - it's somewhere in my archives. If I'll have time, I'll find it.

What I can say, is that due to fact, that all addresses are fixed in PE file and can't be relocated, code injection isn't always possible, as it uses free padding space, that is used for alignment purposes. If exe doesn't have one - code injection won't be possible. And, as I remember, not all applications worked properly after that injections. But majority did.
Title: Re: PE file injection
Post by: PascalDragon on October 22, 2020, 09:23:41 am
Build a DLL and load it at run time..

BSaidus means injecting code into a third application that is not controlled by them.

For learning reasons I wonder if someone has a tutorials, simple examples on how to inject functions located in DLL into an already executable so the the executable starts executing functions injected then returns to normal execution.

You could take a look at this (https://forum.lazarus.freepascal.org/index.php?topic=28456.0) thread which seems to have done it successfully.
Title: Re: PE file injection
Post by: BSaidus on October 22, 2020, 03:26:00 pm
Thank you for your responses all,

@PascalDragon
  The topic show how to inject code (dll function) to  a running process, what I want is a kind of patch
I Inject code into exe and the code will remain on it.

Thanks.
Title: Re: PE file injection
Post by: 440bx on October 22, 2020, 04:37:15 pm
For learning reasons I wonder if someone has a tutorials, simple examples on how to inject functions located in DLL into an already executable so the the executable starts executing functions injected then returns to normal execution.
I don't have a tutorial nor examples but, I can point you in the right direction.

You mentioned you want to statically (that is not at run time) inject code into a dll (it doesn't make much, if any, difference whether dll or exe.) 

Three ways to do it come to mind.

The first way and, the simplest one is, if there is enough padding space in the dll/exe code segment(s) then, you can simply add the code into the padding space.  The one problem to be aware of is that,  the code _may_ need to have additional relocation entries added into the relocation table.  This depends on how the injected code is written.

The second way is not hard but it's very "delicate" and, takes a fair amount of work.  Basically, you extend the code segment to make it as large as you need it to be to accommodate the code you want to inject.  Doing it that way means the program that is injecting the code has to re-link the executable because the injected code causes the offsets to other segments in the PE file to no longer be correct.  If the PE file does not have a relocation table, this method cannot/shouldn't be used because there is no list of all the adjustments that must be made to ensure a valid resulting executable.

The third way, likely safest way and most flexible way, is to add a code segment that follows existing code and some of the data segments, that way, the most important offsets in the PE file remain valid and only a subset of the offsets have to be recalculated (those that reference areas after the added/injected code segment.)  This makes re-linking much simpler and possible even if the PE file does not contain a relocations section.

Lastly but, extremely important, you have to have _solid_ knowledge of the PE file format.  As far as an example, search for the source of a "PE rebase" utility.  Rebasing without injecting code is very simple but, that would give you a good idea of what's involved.  Matt Pietrek published a "rebase" utility along with a few others in his book "Windows 95 system programming secrets" and he makes the original disk available for download on his website.  His is written in C, maybe someone knows of a rebase utility written in Pascal they might want to mention.

HTH.
Title: Re: PE file injection
Post by: Warfley on October 22, 2020, 05:59:17 pm
There are several different things one could do besides what 440bx already mentioned. The most simple one is to just have a program that contains the other program and first executes it's own code before executing the original program. This is how most malware operates, but you must get creative on how to avoid AVs.

Another option would be to wrap an existing DLL. Search for a library the program uses, on Windows it usually should contain a call to LoadLibrary to initialize that library. LoadLibrary takes the a filename. Patch this filename to referr to the name of your DLL. Then all you need to do in your DLL is to load the original DLL and simply provide for all exported functions a wrapper to the internally loaded original DLL.
I did something similar once (but I just renamed the old DLL in the program directory and added my DLL under the same name). This is how many Game Hacks circumvent the anticheat (by finding whitelisted DLLs and replacing them, not by patching ACs can notice that).

The last option I can think of is to write a stub program like in the first approach I mentioned, but instead of executing the code in two different processes, start the original application and then inject the DLL into the running process like in the thread posted by PascalDragon
Title: Re: PE file injection
Post by: mav on October 22, 2020, 06:11:32 pm
Ten process injection techniques: A technical survey of common and trending process injection techniques :
  https://www.elastic.co/es/blog/ten-process-injection-techniques-technical-survey-common-and-trending-process
    :) :)
Title: Re: PE file injection
Post by: mav on October 22, 2020, 06:25:49 pm
I hope it helps...
greetings!!
Title: Re: PE file injection
Post by: Mr.Madguy on October 22, 2020, 09:10:53 pm
I don't have a tutorial nor examples but, I can point you in the right direction.

You mentioned you want to statically (that is not at run time) inject code into a dll (it doesn't make much, if any, difference whether dll or exe.) 

Three ways to do it come to mind.

The first way and, the simplest one is, if there is enough padding space in the dll/exe code segment(s) then, you can simply add the code into the padding space.  The one problem to be aware of is that,  the code _may_ need to have additional relocation entries added into the relocation table.  This depends on how the injected code is written.

The second way is not hard but it's very "delicate" and, takes a fair amount of work.  Basically, you extend the code segment to make it as large as you need it to be to accommodate the code you want to inject.  Doing it that way means the program that is injecting the code has to re-link the executable because the injected code causes the offsets to other segments in the PE file to no longer be correct.  If the PE file does not have a relocation table, this method cannot/shouldn't be used because there is no list of all the adjustments that must be made to ensure a valid resulting executable.

The third way, likely safest way and most flexible way, is to add a code segment that follows existing code and some of the data segments, that way, the most important offsets in the PE file remain valid and only a subset of the offsets have to be recalculated (those that reference areas after the added/injected code segment.)  This makes re-linking much simpler and possible even if the PE file does not contain a relocations section.

Lastly but, extremely important, you have to have _solid_ knowledge of the PE file format.  As far as an example, search for the source of a "PE rebase" utility.  Rebasing without injecting code is very simple but, that would give you a good idea of what's involved.  Matt Pietrek published a "rebase" utility along with a few others in his book "Windows 95 system programming secrets" and he makes the original disk available for download on his website.  His is written in C, maybe someone knows of a rebase utility written in Pascal they might want to mention.

HTH.
Relocation works for DLLs only, as all EXEs are usually "fixed" now, i.e. they contain hardcoded addresses, that aren't mentioned anywhere, so you can't relocated them. Therefore nothing can be moved inside EXE. That's why third method still isn't guaranteed to work. You still need padding space in Section Table in order to add new section. And it isn't guaranteed to exist.
Title: Re: PE file injection
Post by: 440bx on October 22, 2020, 09:20:56 pm
Relocation works for DLLs only, as all EXEs are usually "fixed" now, i.e. they contain hardcoded addresses, that aren't mentioned anywhere, so you can't relocated them. Therefore nothing can be moved inside EXE. That's why third method still isn't guaranteed to work. You still need padding space in Section Table in order to add new section. And it isn't guaranteed to exist.
No. That is incorrect.  As long as the executable contains a relocation table, the executable can be rebased whether it is a DLL or a .exe.  Many, if not most, exes contain hard coded addresses but they are referenced in the relocation table (if they are created with a relocation table) to enable the loader to load them at an address other than the preferred load address.

If there is no relocation table then, everything is fixed and the loader has no choice but to load them at the preferred load address (which for the executable should never be a problem - which is the reason why some compilers now default to not creating a relocation table for .exes.)


Title: Re: PE file injection
Post by: Mr.Madguy on October 23, 2020, 07:48:57 am
No. That is incorrect.  As long as the executable contains a relocation table, the executable can be rebased whether it is a DLL or a .exe.  Many, if not most, exes contain hard coded addresses but they are referenced in the relocation table (if they are created with a relocation table) to enable the loader to load them at an address other than the preferred load address.

If there is no relocation table then, everything is fixed and the loader has no choice but to load them at the preferred load address (which for the executable should never be a problem - which is the reason why some compilers now default to not creating a relocation table for .exes.)
That works, if you want to inject code to your own EXE. And we talk about arbitrary EXE. Because EXE with relocations - is something form Win32s/Win95 era, because 386 support was still required back then and 386 lacked paging. Win64 is different thing. It supports IP+X relative references. But again. It's not guaranteed, that hardcoded addresses aren't used anywhere.
Title: Re: PE file injection
Post by: GetMem on October 23, 2020, 08:32:17 am
@BSaidus

The attached project(D7) worked very well in the past, I believe it still works with 32 bit exe/dll. It does exactly what you are trying to achieve: injects a dll into an exe(not into a running process). I'm only posting this, because the code is irrelevant nowadays, besides you can find it with google, if you know where to search.
As a conclusion, the code is excelent for learning purposes, but nothing more, exactly what @BSaidus is after :D.   
Title: Re: PE file injection
Post by: PascalDragon on October 23, 2020, 09:09:47 am
No. That is incorrect.  As long as the executable contains a relocation table, the executable can be rebased whether it is a DLL or a .exe.  Many, if not most, exes contain hard coded addresses but they are referenced in the relocation table (if they are created with a relocation table) to enable the loader to load them at an address other than the preferred load address.

If there is no relocation table then, everything is fixed and the loader has no choice but to load them at the preferred load address (which for the executable should never be a problem - which is the reason why some compilers now default to not creating a relocation table for .exes.)
That works, if you want to inject code to your own EXE. And we talk about arbitrary EXE. Because EXE with relocations - is something form Win32s/Win95 era, because 386 support was still required back then and 386 lacked paging. Win64 is different thing. It supports IP+X relative references. But again. It's not guaranteed, that hardcoded addresses aren't used anywhere.

Relocations still are a thing. In fact when you want to use Address Space Layout Randomization on Windows your executables must be relocatable. MSVC creates relocatable executables by default and for e.g. aarch64-win64 it is required as well (otherwise the executables refuses to launch).
Title: Re: PE file injection
Post by: 440bx on October 23, 2020, 11:49:29 am
<snip> And we talk about arbitrary EXE. Because EXE with relocations - is something form Win32s/Win95 era, because 386 support was still required back then and 386 lacked paging. Win64 is different thing. It supports IP+X relative references. But again. It's not guaranteed, that hardcoded addresses aren't used anywhere.
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. 
Title: Re: PE file injection
Post by: Mr.Madguy 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
Title: Re: PE file injection
Post by: 440bx 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.

Title: Re: PE file injection
Post by: Mr.Madguy 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.
Title: Re: PE file injection
Post by: 440bx 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. 
Title: Re: PE file injection
Post by: Mr.Madguy 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.
Title: Re: PE file injection
Post by: 440bx 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.

Title: Re: PE file injection
Post by: Mr.Madguy 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?
Title: Re: PE file injection
Post by: PascalDragon 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.
Title: Re: PE file injection
Post by: 440bx 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.
Title: Re: PE file injection
Post by: winni 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
Title: Re: PE file injection
Post by: Mr.Madguy 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.
Title: Re: PE file injection
Post by: Mr.Madguy 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.  
Title: Re: PE file injection
Post by: BSaidus 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 (http://www.delphibasics.info/home/delphibasicscounterstrikewireleases/pe-inject)
https://migeel.sk/programming/pe-inject/ (https://migeel.sk/programming/pe-inject/)

thank you.
Title: Re: PE file injection
Post by: Mr.Madguy 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.
TinyPortal © 2005-2018