The EXE Modifier

Nothing works the way I want it, and source code is not available. Changes therefore must be made by hacking the binaries. I grew sick of writing patches in assembler and manually adding them to existing exe/dll files, and the limitations of this method prevented me from doing most of the things I wanted. Dll injection and run-time patching, allowed surpassing these limitations but is was just a messy solution, it had performance problems and required an external dll be injected by some method. CFF Explorers Import Adder, allowed me to attach my patching dll to the host module such that the patches could be applied on load. It was still not an ideal solution.


What was required was a way to easily apply patches and also add new code and data to an existing module. Over the course of many months (intermittently) the exe modifier took shape. The EXE/DLL modifier allows the application of patches to an existing module and the adition of new code and data. It can link in compiled c/c++ modules so that no messing about with assembly is required. Full access to the system APIs are also possible, and if a function has not been imported by the host module then the import table can be rebuilt to rectify this problem. The exe modifier also updates the relocs section such that patched code and also new code and data is correctly rebased when the module is a DLL file and needs to be relocated.


The ussage of the exe modifier is very simple, changes can be made to existing modules very quickly and with little effort. The exe modifier (exe_mod.exe) takes as input on the command line:
1. input exe/dll module
2. output exe/dll module
3. optional arguments
-- COFF object files (*.o)
-- AR library files (*.a, -l[lib name])
-- patch/link descriptor (*.def)
-- option: -b, dissable binding

Library files specified by the -l[lib name] syntax are located by searching first in the directory containing exe_mod.exe and then by the list of directories specified by the config file line "lib_path=". At this time each library is searched only once for undefined symbols so the libraries must be specified in the correct order and circular reference between libraries is not supported. The default list of libraries which are always searched is specified by the config file line "def_libs=". The exe modifier discards unreferenced sections, this functionality is equivalent to the gcc argument -Wl,gc-sections, as with gcc some sections or symbols must be marked to be kept for correctness, this list of symbols is specified by the config file line "keep_list=". Further symbols may be specified for keeping in the link descriptor file (.def).


The patch/link descriptor file is where symbols and patches are defined. The location of patches and symbols are specified by address (the address when loaded at its default base). There are several types of standard argument for patches.
An argument may be of multiple types, in this case the type is auto-detected.
ADDRESS: Argument is number in decimal or hex (auto detected), negative numbers are recognized with the - prefix.
SYMBOL: Argument is a symbol name, an optional offset may follow the symbol name. (offset format like that of ADDRESS)
STRDATA: Argument is a quote enclosed string that defines an array of data, this string may be formatted either as either a c-style string literal, or a custom format unique to this software (the format is auto detected). The 'L' prefix is supported such to define a 16bit wide string. At this time ')' cannot appear in the string (or be escaped) due to lazy coding (and occurrence of ')' is taken as the end of patch). The custom format is a list of hex values, each hex value is seperated by a preceding '\' a value can be byte, word, or dword depending on its length in characters. 1-2 characters: byte, 3-4 characters: word, 5-8 characters: dword. Values are written as little endian and alignment is not required.


Patches are defined using a function style syntax where the patch type/name starts a line and then the arguments follow enclosed in parenthesis. The patch may be split across multiple lines, the patch ends when the closing parenthesis is found. A colon ending the patch is neither required or tolerated (strtok is quick and dirty but it has limitations).

Prevents a symbol or section from being discarded when unreferenced sections are deleted.

FREE(startAddress:ADDRESS, endAddress:ADDRESS)
Declares a range of memory to be free/unused. This allows the memory to be reused during linking. A function which is no longer called due to patches could be specified avoiding the waste of the memory it occupied. Any relocations found in this range will be deleted

Declares an absolute symbol, this will never be relocated

Declares a relative symbol, this is relative the the image and can be relocated.

MEMNOP(startAddress:ADDRESS, endAddress:ADDRESS)
The address range is replaced with the opcode nop (0x90). Any relocations found in this range will be deleted.

Patches the dword value found at patchAddress. if patch value is ADDRESS and the characted '@' preceeds then patchValue will be added to the value found at patchAddress. if patchValue is SYMBOL then the dword will be set correctly during linking when the address of the specified symbol is resolved.

MEMPATCH(startAddress:ADDRESS, patchData:STRDATA)
The address range starting at startAddress is replaced with the data specified by the patchData string. Any relocations found in this range will also be deleted

MEMHOOK(patchAddress:ADDRESS, patchValue:ADDRESS/SYMBOL, oldValueSymbol:SYMBOL)
Identical to MEMPATCH except that the original value found at patchAddress is saved into the symbol oldValueSymbol. The same effect could be achieved by a separate call to MEMPATCH and SYMBOL.

Replaces a function call or jump destination, if the patch address does not refer to an existing call or jump then one will be made there. Indirect calls and jumps as used to call imports are correctly replaced with the corresponding relative version and a nop is inserted to correct for the difference in instruction length. Moves into registers are also correctly detected and patched.

CALLHOOK(patchAddress:ADDRESS, oldCallSymbol:SYMBOL)
Identical to CALLPATCH except that the original destination saved into oldCallSymbol and the new destination is oldCallSybol with "_hook" appended to the symbol. __stdcall is correctly handled, int this case "_hook" is inserted preceding the final '@'. The same effect could be achieved by a separate call to CALLPATCH and SYMBOL.

IMPORTHOOK(DllName;ImportName, hookSymbol:SYMBOL) All calls to the specified imported function are replaced with the destination specified by hookSymbol. The relocation data is used to locate references to the corresponding import address table entry. It is obvious that this will only work if relocation data is present in the pe file. Whilst unlikely, it is very possible for this method to patch incorrect locations, there is no easy way for this software to detect incorrect locations, so use of this function must be accompanied by a quantity of faith.


This software requires an install of gcc MinGW to be useful. It uses the import libraries from MinGW for linking and to compile the examples requires a c++ compiler. It is probably impossible to build the exe_modifier using a stock version of MinGW. Altering the compiler flags and maybe modification of the code will be required. I have at various times modified the header files of my install of MinGW when I find things I don't like. I also have a custom build of the linker. To facilitate easy install the script setup.vbs can be used. When run it will prompt for the install path of MinGW (defaulting to C:\MinGW). The config file exe_mod.cfg will be created with the correct library paths. After that it will prompt for a path to create the environment setup batch files which are required to build the examples. The default location for these files is C:\Windows\System32 this directory is chosen because it is always in the search path. If you do not like this you can select a different directory, "." could be selected such that the batch files are created in the exe_mod directory. I consider writing a few files into C:\Windows\System32 to be much less sinful then changing %PATH%


Exe Modifier