├── .gitattributes ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── build.cmd ├── default.nix ├── res ├── .gitignore ├── LICENSE ├── README.md ├── build.cmd ├── inc │ ├── app.h │ ├── app.inc │ ├── macros │ │ ├── datatypes.inc │ │ ├── datatypes.s │ │ ├── extern.inc │ │ ├── extern.s │ │ ├── patch.h │ │ ├── patch.inc │ │ ├── patch.s │ │ └── setsym.h │ └── patch.h ├── proxy │ ├── proxy.vcxproj │ ├── readme.txt │ └── res.rc └── src │ └── winmain.cpp ├── shell.nix └── src ├── cleanup.h ├── common.c ├── common.h ├── dump.c ├── export.c ├── genfiles.c ├── genlds.c ├── genmak.c ├── genpatch.c ├── genprj.c ├── genproxy.c ├── gensym.c ├── import.c ├── incbin.S ├── main.c ├── patch.c ├── pe.c ├── pe.h ├── pe2obj.c ├── re2obj.c ├── setdd.c ├── setsc.c ├── setts.c └── setvs.c /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | *.c text 5 | *.h text 6 | 7 | *.cmd text eol=clrf 8 | 9 | *.exe -text 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /result 2 | *~ 3 | \#*\# 4 | 5 | *.exe 6 | /petool 7 | 8 | .vs 9 | 10 | *.o 11 | *.aps 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 FunkyFr3sh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | -include config.mk 2 | 3 | TARGET ?= petool 4 | CFLAGS ?= -O2 -std=c99 -pedantic -Wall -Wextra 5 | LDFLAGS ?= -s 6 | 7 | COMMIT := $(shell git describe --match=NeVeRmAtCh --always --dirty) 8 | CFLAGS += -DGIT_COMMIT=\"$(COMMIT)\" 9 | 10 | ifeq ($(DESTDIR)$(PREFIX),) 11 | PREFIX = /usr/local 12 | endif 13 | 14 | ifneq ($(CROSS_COMPILE),) 15 | CC = $(CROSS_COMPILE)gcc 16 | AS = $(CROSS_COMPILE)as 17 | STRIP = $(CROSS_COMPILE)strip 18 | endif 19 | 20 | SRCS := $(wildcard src/*.c) src/incbin.S 21 | OBJS := $(addsuffix .o, $(basename $(SRCS))) 22 | 23 | .PHONY: clean all install 24 | all: $(TARGET) 25 | 26 | $(TARGET): $(OBJS) 27 | $(CC) $(LDFLAGS) -o $@ $^ 28 | 29 | clean: 30 | $(RM) $(TARGET) $(OBJS) || del $(TARGET) $(subst /,\\,$(OBJS)) 31 | 32 | install: $(TARGET) 33 | install $(TARGET) $(DESTDIR)$(PREFIX)/bin/ 34 | 35 | uninstall: 36 | $(RM) $(DESTDIR)$(PREFIX)/bin/$(TARGET) 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | petool 2 | ================================================================================ 3 | 4 | Tool to help rebuild and patch 32-bit Windows applications. 5 | 6 | ### Tools 7 | 8 | - `dump` - dump information about section of executable 9 | - `genlds` - generate GNU ld script for re-linking executable 10 | - `pe2obj` - convert PE executable into win32 object file 11 | - `patch` - apply a patch set from the .patch section 12 | - `setdd` - set any DataDirectory in PE header 13 | - `setvs` - set VirtualSize for a section 14 | - `setsc` - set Characteristics for a section 15 | - `setts` - set TimeDateStamp in FileHeader 16 | - `export` - export section data as raw binary 17 | - `import` - dump the import table as assembly 18 | - `re2obj` - convert the resource section into COFF object 19 | - `genmak` - generate project Makefile 20 | - `gensym` - generate full sym.cpp with all imports 21 | - `genprj` - generate full project directory (Supports drag and drop of 1 Executable) 22 | - `genpatch` - compare 2 executables and generate patch macros (Supports drag and drop of 2 Executables) 23 | - `genproxy` - generate proxy dll project directory (Supports drag and drop of 1 dll) 24 | 25 | Building 26 | -------------------------------------------------------------------------------- 27 | 28 | ### Build Requirements 29 | 30 | - GNU make 31 | - GNU cc 32 | 33 | ### Compiling 34 | 35 | Type 'make' to build the tools on Linux. On Windows you might need to adjust 36 | some environment variables to get a successful build. The developers test with 37 | GCC on Linux and Windows (via MinGW), but the code aims to be strictly 38 | C99-compliant so any C99-supporting platform should suffice. 39 | 40 | `petool` uses fixed-with numeric types wherever possible so building on both 64- 41 | and 32-bit architectures is supported. Note however that the code currently 42 | supports working with 32-bit portable executable. 43 | 44 | Setting up 45 | -------------------------------------------------------------------------------- 46 | 47 | To begin, generate a project by giving a path to an executable file as the only 48 | argument. Dragging and dropping an executable on `petool.exe` also does this 49 | on Windows and the project is generated next to the original executable in a 50 | subdirectory. 51 | 52 | You should be able to re-link the executable now by executing `make` without any 53 | modifications. 54 | 55 | Note: 56 | You can port old byte patches to your new repo by dragging and dropping 57 | the original executable and the patched one on `petool.exe`, this will generate 58 | the needed macros (Or use `genpatch`). 59 | 60 | Patching 61 | -------------------------------------------------------------------------------- 62 | 63 | Generating the patch set can be done with macros. There are macros available for 64 | C/C++ in `inc/macros/patch.h`, several macros for `NASM` in `inc/macros/*.inc` and 65 | macros for `GNU as` in `inc/macros/*.s`. Runtime patching is supported as well 66 | via `inc/patch.h` (Not recommended, unless you have no other choice). 67 | 68 | Below are some C/C++ examples for how these macros are used in practice. 69 | 70 | Note: All labels passed to the macros must be prefixed with _ and C++ functions 71 | must be defined with EXTERN_C 72 | 73 | /* example.cpp */ 74 | 75 | /* This does NOT work - undefined reference to `doMagic' */ 76 | CALL(0x410000, doMagic); 77 | void doMagic() 78 | { 79 | something(); 80 | } 81 | 82 | /* Working example - prefixed with _ and defined with EXTERN_C */ 83 | CALL(0x410000, _doMagic); 84 | EXTERN_C void doMagic() 85 | { 86 | something(); 87 | } 88 | 89 | /* Working example - prefixed with _ and using asm labels instead of EXTERN_C */ 90 | extern void doMagic() asm("_doMagic"); 91 | CALL(0x410000, _doMagic); 92 | void doMagic() 93 | { 94 | something(); 95 | } 96 | 97 | ### Call 98 | The `CALL` macro writes a CALL instruction at _from_ to _to_. It is commonly used 99 | to replace an existing function call with a call to your own function. This is 100 | the most simple and cleanest way to create a patch (No assembly required). 101 | 102 | `CALL` can be used to replace a 5byte call instruction, `CALL_NOP` can replace a 103 | 6byte instruction. 104 | 105 | Note: For functions using the watcom register calling convention you will need 106 | to pass the additional arg (C/C++ only). 107 | 108 | CALL(, ); 109 | CALL(, , ); 110 | CALL_NOP(, ); 111 | CALL_NOP(, , ); 112 | 113 | Example: 114 | 115 | /* Replace function call at 0x410000 with a call to doMagic */ 116 | CALL(0x410000, _doMagic); 117 | 118 | EXTERN_C void doMagic(int arg1, int arg2, int arg3) 119 | { 120 | /* insert your own code here */ 121 | something(); 122 | 123 | /* call the orginal function that was replaced by the patch (optional) */ 124 | original(arg1, arg2, arg3); 125 | 126 | /* Note: you will have to insert "original" into sym.cpp and app.h to be able to call it */ 127 | } 128 | 129 | /* Same as above, but for functions using the watcom register calling convention */ 130 | CALL(0x410000, _doMagic, 3); 131 | 132 | EXTERN_C void doMagic(int arg1, int arg2, int arg3) 133 | { 134 | /* insert your own code here */ 135 | something(); 136 | 137 | /* call the orginal function that was replaced by the patch (optional) */ 138 | original(arg1, arg2, arg3); 139 | 140 | /* Note: you will have to insert "original" into sym.cpp and app.h to be able to call it */ 141 | } 142 | 143 | Note: the `CALL` macro is also available for `NASM` and `GNU as` under the name `@CALL` 144 | 145 | ### Detour 146 | The `DETOUR` macro redirects all calls to an existing function to your own 147 | replacement function. It does also clear (INT3) the original function. 148 | 149 | Note: For functions using the watcom register calling convention you will need 150 | to pass the additional arg. 151 | 152 | DETOUR(, , ); 153 | DETOUR(, , , ); 154 | 155 | Example: 156 | 157 | /* 158 | Clear all bytes from the start of the function (0x410000) up to the end of the function (0x410200) 159 | AND do a long jump from 0x410000 to label doMagic 160 | */` 161 | DETOUR(0x410000, 0x410200, _doMagic); 162 | 163 | EXTERN_C void doMagic(int arg1, int arg2, int arg3) 164 | { 165 | /* insert your own code here */ 166 | something(); 167 | } 168 | 169 | /* Same as above, but for functions using the watcom register calling convention */ 170 | DETOUR(0x410000, 0x410200, _doMagic, 3); 171 | 172 | EXTERN_C void doMagic(int arg1, int arg2, int arg3) 173 | { 174 | /* insert your own code here */ 175 | something(); 176 | } 177 | 178 | You can also keep the original function intact by clearing only the first 179 | instructions (just to make enough space for the 5 byte jump to fit) and afterwards 180 | create a trampoline in sym.cpp to restore the instructions that were removed. 181 | 182 | /* Clear first two instructions AND do a long jump from 0x410000 to label doMagic */` 183 | DETOUR(0x410000, 0x410007, _doMagic); 184 | 185 | EXTERN_C void doMagic(int arg1, int arg2, int arg3) 186 | { 187 | /* insert your own code here */ 188 | something(); 189 | 190 | /* call the orginal function that was replaced by the patch (optional) */ 191 | original(arg1, arg2, arg3); 192 | } 193 | 194 | /* sym.cpp - Create trampoline to restore the two instructions removed by DETOUR */ 195 | TRAMPOLINE(0x410007, original, "sub esp, 8; mov eax, [esp+0x1C]"); 196 | 197 | /* Same as above, but for functions using the watcom register calling convention */ 198 | TRAMPOLINE(0x410007, original, "sub esp, 8; mov eax, [esp+0x1C]", 3); 199 | 200 | Note: the `DETOUR` macro is NOT available for `NASM` and `GNU as` but the same can be done via `@HOOK` instead 201 | 202 | ### Hook 203 | The `HOOK` macro writes a JMP instruction at _addr_. You can use `HOOK` in case 204 | there is no Call instruction nearby that could be hooked via the `CALL` macro. 205 | `HOOK` creates a naked function, so be sure you save and restore the values of the registers if needed. 206 | 207 | The `HOOK` macro with 2 args can do a additional `CLEAR`, make sure you use it in case the 208 | replaced instructions don't have a size of 5 bytes (leftover bytes could break your disassembler). 209 | 210 | HOOK(); 211 | HOOK(, ); 212 | 213 | Example: 214 | 215 | /* Insert a JMP at 0x410000 to your own code */ 216 | HOOK(0x410000) 217 | { 218 | /* insert original instructions that were replaced by the patch (5 byte jump) */ 219 | __asm("mov ecx, 0xFFFFFFFF"); 220 | 221 | /* save the values of the registers */ 222 | __asm("pushad"); 223 | 224 | /* insert your own code here */ 225 | something(); 226 | 227 | /* restore the values of the registers and jump back to the original location */ 228 | __asm("popad; jmp 0x410000 + 5"); 229 | } 230 | 231 | Note: the `HOOK` macro is also available for `NASM` and `GNU as` under the name `@HOOK` 232 | 233 | ### Set instruction 234 | Inserts the given instruction at the chosen address. 235 | 236 | SETINST(, ); 237 | 238 | Example: 239 | 240 | SETINST(0x410000, "mov eax, 1"); 241 | 242 | Note: the `SETINST` macro is also available for `NASM` and `GNU as` under the name `@SET` 243 | 244 | ### Jump 245 | Both short and long variants are included. No overflow 246 | checks are done so do pre-calculate which one you need. 247 | 248 | LJMP(, ); 249 | SJMP(, ); 250 | 251 | Example: 252 | 253 | /* Do a long jump from 0x410000 to label doMagic */` 254 | LJMP(0x410000, _doMagic); 255 | 256 | /* Do a short jump from 0x410000 to 0x41000F */` 257 | SJMP(0x410000, 0x41000F); 258 | 259 | Note: the `LJMP` macro is also available for `NASM` and `GNU as` under the name `@LJMP` 260 | 261 | Note: the `SJMP` macro is also available for `NASM` and `GNU as` under the name `@SJMP` 262 | 263 | ### Clear 264 | Sets all bytes between _from_ and _to_ (not inclusive) to the 8-bit argument 265 | _byte_. 266 | 267 | When you make a `LJMP` or anything else over the original code that would 268 | leave some instructions broken or a dead code block, consider clearing the area 269 | before writing the jump. It ensures when you or someone else is following the 270 | code in a disassembler or a debugger that they will not get confused by sudden 271 | long jumps which have broken instructions just after them. 272 | 273 | CLEAR(, , ); 274 | CLEAR_NOP(, ); 275 | CLEAR_INT(, ); 276 | 277 | Example: 278 | 279 | /* NOP 5 bytes starting from 0x410000 */` 280 | CLEAR(0x410000, 0x90, 0x410005); 281 | 282 | /* Does the same as the one above, NOP 5 bytes starting from 0x410000 */` 283 | CLEAR_NOP(0x410000, 0x410005); 284 | 285 | /* Same as CLEAR_NOP, just that it clears with INT3 instead of NOP */` 286 | CLEAR_INT(0x410000, 0x410005); 287 | 288 | Note: the `CLEAR` macro is also available for `NASM` and `GNU as` under the name `@CLEAR` 289 | 290 | ### Set values 291 | Change values at a given location. 292 | 293 | SETDWORD(, ); 294 | SETWORD(, ); 295 | SETBYTE(, ); 296 | SETBYTES(, ); 297 | SETFLOAT(, ); 298 | SETDOUBLE(, ); 299 | 300 | Example: 301 | 302 | /* Change dword value at 0x410000 to 250000 */` 303 | SETDWORD(0x410000, 250000); 304 | 305 | /* Change word value at 0x410000 to 30000 */` 306 | SETWORD(0x410000, 30000); 307 | 308 | /* Change byte value at 0x410000 to 150 */` 309 | SETBYTE(0x410000, 150); 310 | 311 | /* Change bytes at 0x410000 to 0x146878185001 */` 312 | SETBYTES(0x410000, "\x14\x68\x78\x1B\x50\x01"); 313 | 314 | /* Change string at 0x410000 to HelloWorld */` 315 | SETBYTES(0x410000, "HelloWorld\0"); 316 | 317 | /* Change float value at 0x410000 to 1.2 */` 318 | SETFLOAT(0x410000, 1.2); 319 | 320 | /* Change double value at 0x410000 to 1.2 */` 321 | SETDOUBLE(0x410000, 1.2); 322 | 323 | Note: These macros are NOT available for `NASM` and `GNU as` but the same can be done via `@SET` instead 324 | 325 | ### Existing symbols in original executable (sym.cpp) 326 | 327 | SETCGLOB(0x004D2A80, WinMain); 328 | 329 | When you need to refer to existing symbols inside the executable, you can export 330 | global symbols from assembly source via the `SETCGLOB` macro. Symbols can be any named 331 | memory address: function, data, uninitialized variable. As long as you define 332 | them in sym.cpp, you can use them anywhere. 333 | 334 | Note: For functions using the watcom register calling convention you will need 335 | to pass the additional arg. 336 | 337 | SETCGLOB(, , ); 338 | 339 | SETCGLOB(0x004D500, doMagic, 4); 340 | 341 | Compiling new code 342 | -------------------------------------------------------------------------------- 343 | 344 | You can use any compilable language that can produce an COFF or ELF object or 345 | whatever your build of `GNU binutils` supports. `GNU as`, `GNU cc`, `GNU g++` 346 | and `NASM` are the most compatible tools and supported by `w64devkit` out of the box. 347 | 348 | When mixing already compiled executable with new code, you need to make sure of 349 | the calling convention of your functions and compiler can be made compatible 350 | with everything else. 351 | 352 | Patch section 353 | -------------------------------------------------------------------------------- 354 | 355 | The `patch` command reads a PE image section for patch data. The format of the 356 | patch data is as follows: 357 | 358 | 359 | 360 | These binary blobs are right after each other to form a full patch set. The 361 | patch command simply looks up the file offset in the PE image file based on 362 | given absolute memory address and writes over the blob at that point. 363 | 364 | After the patch is applied, you should remove the patch section with GNU strip 365 | as it is not needed in the final product. The default project template includes 366 | this additional step. 367 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM 3 | REM patch environment config 4 | REM 5 | set PATH=C:\w64devkit\bin;C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw64\bin\ 6 | make clean 7 | make 8 | pause 9 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { stdenv }: 2 | 3 | stdenv.mkDerivation rec { 4 | name = "petool"; 5 | version = "1.1.1.1"; 6 | 7 | buildInputs = [ ]; 8 | src = ./.; 9 | preBuild = "makeFlagsArray=(REV=${version})"; 10 | 11 | installPhase = '' 12 | mkdir -p $out/bin 13 | install -m 755 petool $out/bin 14 | ''; 15 | 16 | enableParallelBuilding = true; 17 | 18 | meta = with stdenv.lib; { 19 | homepage = http://github.com/FunkyFr3sh/petool; 20 | description = "Tool to help rebuild, extend and patch 32-bit Windows applications."; 21 | # maintainers = 22 | license = map (builtins.getAttr "shortName") [ licenses.mit ]; 23 | # Should be portable C 24 | platforms = stdenv.lib.platforms.all; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /res/.gitignore: -------------------------------------------------------------------------------- 1 | # object files 2 | *.o 3 | 4 | # patched machine code 5 | *.exe 6 | 7 | # PETool on unix 8 | /petool 9 | 10 | # configuration 11 | config.mk 12 | 13 | # misc junk 14 | *~ 15 | \#*\# 16 | .DS_Store 17 | 18 | # Visual Studio 19 | .vs 20 | 21 | # debug symbols 22 | *.pdb 23 | -------------------------------------------------------------------------------- /res/LICENSE: -------------------------------------------------------------------------------- 1 | License = BSD0 (For all files inside of the /res folder) 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 7 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 8 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 9 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 10 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 11 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 12 | PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /res/README.md: -------------------------------------------------------------------------------- 1 | INSERT_APP_NAME patch 2 | ================== 3 | 4 | Patches to INSERT_APP_NAME 1.0.0.0 5 | 6 | Authors 7 | ------- 8 | - FunkyFr3sh 9 | 10 | 11 | Building on *nix 12 | ----------------- 13 | - Install [petool](https://github.com/FunkyFr3sh/petool) from github 14 | - Get mingw-w64 from your package manager 15 | - make or gmake 16 | 17 | Building on Windows 18 | ------------------- 19 | - Download [w64devkit-mini-for-patching.zip](https://github.com/FunkyFr3sh/petool/releases/latest/download/w64devkit-mini-for-patching.zip) ([.7z](https://github.com/FunkyFr3sh/petool/releases/latest/download/w64devkit-mini-for-patching.7z)) 20 | - Extract `w64devkit-mini-for-patching.zip` to `C:\` 21 | - run `build.cmd` 22 | -------------------------------------------------------------------------------- /res/build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM 3 | REM patch environment config 4 | REM 5 | set PATH=C:\w64devkit\bin 6 | make clean 7 | make 8 | pause 9 | -------------------------------------------------------------------------------- /res/inc/app.h: -------------------------------------------------------------------------------- 1 | #ifndef APP_H 2 | #define APP_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | // This header works with sym.cpp which defines the Vanilla symbols 9 | // This header will be split up as it becomes larger 10 | 11 | // ### Types ### 12 | 13 | 14 | // ### Variables ### 15 | 16 | 17 | // ### Functions ### 18 | 19 | int OriginalCRTStartup(void); 20 | 21 | 22 | #ifdef __cplusplus 23 | }; 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /res/inc/app.inc: -------------------------------------------------------------------------------- 1 | 2 | ; This header works with sym.cpp which defines the Vanilla symbols 3 | ; This header will be split up as it becomes larger 4 | 5 | ; ### definitions of common structures ### 6 | 7 | ; ### vars ### 8 | 9 | ; ### functions ### 10 | 11 | ; ### Imports ### 12 | -------------------------------------------------------------------------------- /res/inc/macros/datatypes.inc: -------------------------------------------------------------------------------- 1 | ;; LICENSE = BSD0 - https://github.com/FunkyFr3sh/petool 2 | 3 | %include "macros/extern.inc" 4 | 5 | %assign true 1 6 | %assign false 0 7 | 8 | ;; global const string -> arg1 = name, arg2 = value 9 | %macro gstring 2 10 | cglobal %1 11 | sstring %1, %2 12 | %endmacro 13 | 14 | ;; static (local) const string -> arg1 = name, arg2 = value 15 | %macro sstring 2 16 | [section .rdata] 17 | %1 db %2,0 18 | __SECT__ 19 | %endmacro 20 | 21 | ;; global string -> arg1 = name, arg2 = value, arg3 = length 22 | %macro gstring 3 23 | cglobal %1 24 | sstring %1, %2, %3 25 | %endmacro 26 | 27 | ;; static (local) string -> arg1 = name, arg2 = value, arg3 = length 28 | %macro sstring 3 29 | %strlen ___StringLength %2 30 | 31 | %if ___StringLength > 0 32 | [section .data] 33 | %1 db %2 34 | times ((%3) - (___StringLength)) db 0 35 | __SECT__ 36 | %else 37 | [section .bss] 38 | %1 resb %3 39 | __SECT__ 40 | %endif 41 | %endmacro 42 | 43 | ;; global int -> arg1 = name, arg2 = value 44 | %macro gint 2 45 | cglobal %1 46 | sint %1, {%2} 47 | %endmacro 48 | 49 | ;; static (local) int -> arg1 = name, arg2 = value 50 | %macro sint 2 51 | %ifidni %2,0 52 | [section .bss] 53 | %1 resd 1 54 | __SECT__ 55 | %else 56 | [section .data] 57 | %1 dd %2 58 | __SECT__ 59 | %endif 60 | %endmacro 61 | 62 | ;; global int array -> arg1 = name, arg2 = value, arg3 = length 63 | %macro gint 3 64 | cglobal %1 65 | sint %1, {%2}, %3 66 | %endmacro 67 | 68 | ;; static (local) int array -> arg1 = name, arg2 = value, arg3 = length 69 | %macro sint 3 70 | %ifidni %2,0 71 | [section .bss] 72 | %1 resd %3 73 | __SECT__ 74 | %else 75 | [section .data] 76 | %1 times %3 dd %2 77 | __SECT__ 78 | %endif 79 | %endmacro 80 | 81 | ;; global short -> arg1 = name, arg2 = value 82 | %macro gshort 2 83 | cglobal %1 84 | sshort %1, {%2} 85 | %endmacro 86 | 87 | ;; static (local) short -> arg1 = name, arg2 = value 88 | %macro sshort 2 89 | %ifidni %2,0 90 | [section .bss] 91 | %1 resw 1 92 | __SECT__ 93 | %else 94 | [section .data] 95 | %1 dw %2 96 | __SECT__ 97 | %endif 98 | %endmacro 99 | 100 | ;; global short array -> arg1 = name, arg2 = value, arg3 = length 101 | %macro gshort 3 102 | cglobal %1 103 | sshort %1, {%2}, %3 104 | %endmacro 105 | 106 | ;; static (local) short array -> arg1 = name, arg2 = value, arg3 = length 107 | %macro sshort 3 108 | %ifidni %2,0 109 | [section .bss] 110 | %1 resw %3 111 | __SECT__ 112 | %else 113 | [section .data] 114 | %1 times %3 dw %2 115 | __SECT__ 116 | %endif 117 | %endmacro 118 | 119 | ;; global bool -> arg1 = name, arg2 = value 120 | %macro gbool 2 121 | cglobal %1 122 | sbyte %1, {%2} 123 | %endmacro 124 | 125 | ;; static (local) bool -> arg1 = name, arg2 = value 126 | %macro sbool 2 127 | sbyte %1, {%2} 128 | %endmacro 129 | 130 | ;; global bool array -> arg1 = name, arg2 = value, arg3 = length 131 | %macro gbool 3 132 | cglobal %1 133 | sbyte %1, {%2}, %3 134 | %endmacro 135 | 136 | ;; static (local) bool array -> arg1 = name, arg2 = value, arg3 = length 137 | %macro sbool 3 138 | sbyte %1, {%2}, %3 139 | %endmacro 140 | 141 | ;; global byte -> arg1 = name, arg2 = value 142 | %macro gbyte 2 143 | cglobal %1 144 | sbyte %1, {%2} 145 | %endmacro 146 | 147 | ;; static (local) byte -> arg1 = name, arg2 = value 148 | %macro sbyte 2 149 | %ifidni %2,0 150 | [section .bss] 151 | %1 resb 1 152 | __SECT__ 153 | %else 154 | [section .data] 155 | %1 db %2 156 | __SECT__ 157 | %endif 158 | %endmacro 159 | 160 | ;; global byte array -> arg1 = name, arg2 = value, arg3 = length 161 | %macro gbyte 3 162 | cglobal %1 163 | sbyte %1, {%2}, %3 164 | %endmacro 165 | 166 | ;; static (local) byte array -> arg1 = name, arg2 = value, arg3 = length 167 | %macro sbyte 3 168 | %ifidni %2,0 169 | [section .bss] 170 | %1 resb %3 171 | __SECT__ 172 | %else 173 | [section .data] 174 | %1 times %3 db %2 175 | __SECT__ 176 | %endif 177 | %endmacro 178 | 179 | ;; global function -> arg1 = name 180 | %macro gfunction 1 181 | cglobal %1 182 | sfunction %1 183 | %endmacro 184 | 185 | ;; static (local) function -> arg1 = name 186 | %macro sfunction 1 187 | [section .text] 188 | align 16, db 0xCC 189 | %1: 190 | %endmacro 191 | 192 | ;; global file (const byte array) -> arg1 = name, arg2 = filePath 193 | %macro gfile 2 194 | cglobal %1 195 | cglobal %1Length 196 | sfile %1, %2 197 | %endmacro 198 | 199 | ;; static (local) file (const byte array) -> arg1 = name, arg2 = filePath 200 | %macro sfile 2 201 | [section .rdata] 202 | %1 incbin %2 203 | %1Length dd $-%1 204 | __SECT__ 205 | %endmacro 206 | -------------------------------------------------------------------------------- /res/inc/macros/datatypes.s: -------------------------------------------------------------------------------- 1 | # LICENSE = BSD0 - https://github.com/FunkyFr3sh/petool 2 | 3 | .include "macros/extern.s" 4 | 5 | .set true, 1 6 | .set false, 0 7 | 8 | # global const string -> arg1 = name, arg2 = value 9 | .macro gstring name:req, value:req 10 | cglobal \name 11 | sstring \name, \value 12 | .endm 13 | 14 | # static (local) const string -> arg1 = name, arg2 = value 15 | .macro sstring name:req, value:req 16 | .section .rdata 17 | \name\(): .asciz "\value" 18 | .endm 19 | 20 | # global int -> arg1 = name, arg2 = value, arg3 = array length 21 | .macro gint name:req, value:req, length=0 22 | cglobal \name 23 | sint \name, \value, \length 24 | .endm 25 | 26 | # static (local) int -> arg1 = name, arg2 = value, arg3 = array length 27 | .macro sint name:req, value:req, length=0 28 | .ifeq (\value) 29 | .section .bss 30 | .else 31 | .section .data 32 | .endif 33 | 34 | .ifeq (\length) 35 | \name\(): .long (\value) 36 | .else 37 | \name\(): .fill (\length), 4, (\value) 38 | .endif 39 | .endm 40 | 41 | # global short -> arg1 = name, arg2 = value, arg3 = array length 42 | .macro gshort name:req, value:req, length=0 43 | cglobal \name 44 | sshort \name, \value, \length 45 | .endm 46 | 47 | # static (local) short -> arg1 = name, arg2 = value, arg3 = array length 48 | .macro sshort name:req, value:req, length=0 49 | .ifeq (\value) 50 | .section .bss 51 | .else 52 | .section .data 53 | .endif 54 | 55 | .ifeq (\length) 56 | \name\(): .short (\value) 57 | .else 58 | \name\(): .fill (\length), 2, (\value) 59 | .endif 60 | .endm 61 | 62 | # global byte -> arg1 = name, arg2 = value, arg3 = array length 63 | .macro gbyte name:req, value:req, length=0 64 | cglobal \name 65 | sbyte \name, \value, \length 66 | .endm 67 | 68 | # static (local) byte -> arg1 = name, arg2 = value, arg3 = array length 69 | .macro sbyte name:req, value:req, length=0 70 | .ifeq (\value) 71 | .section .bss 72 | .else 73 | .section .data 74 | .endif 75 | 76 | .ifeq (\length) 77 | \name\(): .byte (\value) 78 | .else 79 | \name\(): .fill (\length), 1, (\value) 80 | .endif 81 | .endm 82 | 83 | # global bool -> arg1 = name, arg2 = value, arg3 = array length 84 | .macro gbool name:req, value:req, length=0 85 | cglobal \name 86 | sbyte \name, \value, \length 87 | .endm 88 | 89 | # static (local) bool -> arg1 = name, arg2 = value, arg3 = array length 90 | .macro sbool name:req, value:req, length=0 91 | sbyte \name, \value, \length 92 | .endm 93 | 94 | # global function -> arg1 = name 95 | .macro gfunction name:req 96 | cglobal \name 97 | sfunction \name 98 | .endm 99 | 100 | # static (local) function -> arg1 = name 101 | .macro sfunction name:req 102 | .section .text 103 | .align 16, 0xCC 104 | \name\(): 105 | .endm 106 | 107 | # global file (const byte array) -> arg1 = name, arg2 = filePath 108 | .macro gfile name:req, path:req 109 | cglobal \name 110 | cglobal \name\()Length 111 | sfile \name, \path 112 | .endm 113 | 114 | # static (local) file (const byte array) -> arg1 = name, arg2 = filePath 115 | .macro sfile name:req, path:req 116 | .section .rdata 117 | _incbin_start_\@: 118 | \name\(): .incbin "\path" 119 | _incbin_end_\@: 120 | \name\()Length: .long (_incbin_end_\@ - _incbin_start_\@) 121 | .endm 122 | -------------------------------------------------------------------------------- /res/inc/macros/extern.inc: -------------------------------------------------------------------------------- 1 | ;; LICENSE = BSD0 - https://github.com/FunkyFr3sh/petool 2 | 3 | %ifndef EXTERN_INC 4 | %define EXTERN_INC 5 | 6 | %macro cglobal 1 7 | global _%1 8 | _%1 equ %1 9 | %endmacro 10 | 11 | %macro cextern 1 12 | extern _%1 13 | %1 equ _%1 14 | %endmacro 15 | 16 | %endif 17 | -------------------------------------------------------------------------------- /res/inc/macros/extern.s: -------------------------------------------------------------------------------- 1 | # LICENSE = BSD0 - https://github.com/FunkyFr3sh/petool 2 | 3 | .macro cglobal name 4 | .global _\name 5 | .equ _\name, \name 6 | .endm 7 | 8 | .macro cextern name 9 | .equ \name, _\name 10 | .endm 11 | -------------------------------------------------------------------------------- /res/inc/macros/patch.h: -------------------------------------------------------------------------------- 1 | // LICENSE = BSD0 - https://github.com/FunkyFr3sh/petool 2 | 3 | #define PATCH_START(patchaddr) \ 4 | ".section .patch,\"d0\";" \ 5 | ".long " #patchaddr ";" \ 6 | ".long (9f - 8f);" \ 7 | "8:;" 8 | 9 | #define PATCH_END \ 10 | "9:;" \ 11 | ".section .text;" 12 | 13 | #define SETINST(addr, inst) \ 14 | __asm ( \ 15 | PATCH_START(addr) \ 16 | inst ";" \ 17 | PATCH_END \ 18 | ) 19 | 20 | #define CLEAR(start, value, end) \ 21 | __asm ( \ 22 | PATCH_START(start) \ 23 | ".fill (" #end ") - (" #start "), 1, " #value ";" \ 24 | PATCH_END \ 25 | ) 26 | 27 | #define SJMP(src, dst) \ 28 | __asm ( \ 29 | PATCH_START(src) \ 30 | ".byte 0xEB;" \ 31 | ".byte (" #dst ") - (" #src ") - 2;" \ 32 | PATCH_END \ 33 | ) 34 | 35 | #define LJMP(src, dst) \ 36 | __asm ( \ 37 | PATCH_START(src) \ 38 | ".byte 0xE9;" \ 39 | ".long (" #dst ") - (" #src ") - 5;" \ 40 | PATCH_END \ 41 | ) 42 | 43 | #define SETDWORD(addr, value) \ 44 | __asm ( \ 45 | PATCH_START(addr) \ 46 | ".long " #value ";" \ 47 | PATCH_END \ 48 | ) 49 | 50 | #define SETWORD(addr, value) \ 51 | __asm ( \ 52 | PATCH_START(addr) \ 53 | ".short " #value ";" \ 54 | PATCH_END \ 55 | ) 56 | 57 | #define SETBYTE(addr, value) \ 58 | __asm ( \ 59 | PATCH_START(addr) \ 60 | ".byte " #value ";" \ 61 | PATCH_END \ 62 | ) 63 | 64 | #define SETBYTES(addr, value) \ 65 | __asm ( \ 66 | PATCH_START(addr) \ 67 | ".ascii " #value ";" \ 68 | PATCH_END \ 69 | ) 70 | 71 | #define SETFLOAT(addr, value) \ 72 | __asm ( \ 73 | PATCH_START(addr) \ 74 | ".float " #value ";" \ 75 | PATCH_END \ 76 | ) 77 | 78 | #define SETDOUBLE(addr, value) \ 79 | __asm ( \ 80 | PATCH_START(addr) \ 81 | ".double " #value ";" \ 82 | PATCH_END \ 83 | ) 84 | 85 | #define HOOK_1(addr) \ 86 | __asm ( \ 87 | PATCH_START(addr) \ 88 | ".byte 0xE9;" \ 89 | ".long _dest" #addr "-" #addr " - 5;" \ 90 | PATCH_END \ 91 | ); \ 92 | EXTERN_C void __attribute__((naked)) dest##addr() 93 | 94 | #define HOOK_2(addr, end) \ 95 | __asm ( \ 96 | PATCH_START(addr) \ 97 | ".byte 0xE9;" \ 98 | ".long _dest" #addr "-" #addr " - 5;" \ 99 | ".fill (" #end ") - ((" #addr ") + 5), 1, 0xCC;" \ 100 | PATCH_END \ 101 | ); \ 102 | EXTERN_C void __attribute__((naked)) dest##addr() 103 | 104 | #define HOOK_X(x,A,B,FUNC, ...) FUNC 105 | #define HOOK(...) HOOK_X(,##__VA_ARGS__, \ 106 | HOOK_2(__VA_ARGS__), \ 107 | HOOK_1(__VA_ARGS__), \ 108 | HOOK_0(__VA_ARGS__) \ 109 | ) 110 | 111 | #define CLEAR_NOP(start, end) CLEAR(start, 0x90, end) 112 | #define CLEAR_INT(start, end) CLEAR(start, 0xCC, end) 113 | 114 | #define WATCALL_TO_CDECL(dst, arg_count) \ 115 | ".align 16, 0xCC;" \ 116 | ".if " #arg_count " == 0;" \ 117 | "1:;" \ 118 | "push ecx;" \ 119 | "push edx;" \ 120 | "call " #dst ";" \ 121 | "pop edx;" \ 122 | "pop ecx;" \ 123 | "ret;" \ 124 | ".elseif " #arg_count " == 1;" \ 125 | "1:;" \ 126 | "push ecx;" \ 127 | "push edx;" \ 128 | "push eax;" \ 129 | "call " #dst ";" \ 130 | "add esp, 4;" \ 131 | "pop edx;" \ 132 | "pop ecx;" \ 133 | "ret;" \ 134 | ".elseif " #arg_count " == 2;" \ 135 | "1:;" \ 136 | "push ecx;" \ 137 | "push edx;" \ 138 | "push eax;" \ 139 | "call " #dst ";" \ 140 | "add esp, 8;" \ 141 | "pop ecx;" \ 142 | "ret;" \ 143 | ".elseif " #arg_count " == 3;" \ 144 | "1:;" \ 145 | "push ecx;" \ 146 | "push ebx;" \ 147 | "push edx;" \ 148 | "push eax;" \ 149 | "call " #dst ";" \ 150 | "add esp, 12;" \ 151 | "pop ecx;" \ 152 | "ret;" \ 153 | ".elseif " #arg_count " == 4;" \ 154 | "1:;" \ 155 | "push ecx;" \ 156 | "push ebx;" \ 157 | "push edx;" \ 158 | "push eax;" \ 159 | "call " #dst ";" \ 160 | "add esp, 16;" \ 161 | "ret;" \ 162 | ".elseif " #arg_count " == 5;" \ 163 | "1:;" \ 164 | "push ebp;" \ 165 | "mov ebp, esp;" \ 166 | "push [ebp+8];" \ 167 | "push ecx;" \ 168 | "push ebx;" \ 169 | "push edx;" \ 170 | "push eax;" \ 171 | "call " #dst ";" \ 172 | "add esp, 20;" \ 173 | "pop ebp;" \ 174 | "ret 4;" \ 175 | ".elseif " #arg_count " == 6;" \ 176 | "1:;" \ 177 | "push ebp;" \ 178 | "mov ebp, esp;" \ 179 | "push [ebp+12];" \ 180 | "push [ebp+8];" \ 181 | "push ecx;" \ 182 | "push ebx;" \ 183 | "push edx;" \ 184 | "push eax;" \ 185 | "call " #dst ";" \ 186 | "add esp, 24;" \ 187 | "pop ebp;" \ 188 | "ret 8;" \ 189 | ".elseif " #arg_count " == 7;" \ 190 | "1:;" \ 191 | "push ebp;" \ 192 | "mov ebp, esp;" \ 193 | "push [ebp+16];" \ 194 | "push [ebp+12];" \ 195 | "push [ebp+8];" \ 196 | "push ecx;" \ 197 | "push ebx;" \ 198 | "push edx;" \ 199 | "push eax;" \ 200 | "call " #dst ";" \ 201 | "add esp, 28;" \ 202 | "pop ebp;" \ 203 | "ret 12;" \ 204 | ".elseif " #arg_count " == 8;" \ 205 | "1:;" \ 206 | "push ebp;" \ 207 | "mov ebp, esp;" \ 208 | "push [ebp+20];" \ 209 | "push [ebp+16];" \ 210 | "push [ebp+12];" \ 211 | "push [ebp+8];" \ 212 | "push ecx;" \ 213 | "push ebx;" \ 214 | "push edx;" \ 215 | "push eax;" \ 216 | "call " #dst ";" \ 217 | "add esp, 32;" \ 218 | "pop ebp;" \ 219 | "ret 16;" \ 220 | ".elseif " #arg_count " == 9;" \ 221 | "1:;" \ 222 | "push ebp;" \ 223 | "mov ebp, esp;" \ 224 | "push [ebp+24];" \ 225 | "push [ebp+20];" \ 226 | "push [ebp+16];" \ 227 | "push [ebp+12];" \ 228 | "push [ebp+8];" \ 229 | "push ecx;" \ 230 | "push ebx;" \ 231 | "push edx;" \ 232 | "push eax;" \ 233 | "call " #dst ";" \ 234 | "add esp, 36;" \ 235 | "pop ebp;" \ 236 | "ret 20;" \ 237 | ".elseif " #arg_count " == 10;" \ 238 | "1:;" \ 239 | "push ebp;" \ 240 | "mov ebp, esp;" \ 241 | "push [ebp+28];" \ 242 | "push [ebp+24];" \ 243 | "push [ebp+20];" \ 244 | "push [ebp+16];" \ 245 | "push [ebp+12];" \ 246 | "push [ebp+8];" \ 247 | "push ecx;" \ 248 | "push ebx;" \ 249 | "push edx;" \ 250 | "push eax;" \ 251 | "call " #dst ";" \ 252 | "add esp, 40;" \ 253 | "pop ebp;" \ 254 | "ret 24;" \ 255 | ".elseif " #arg_count " == 11;" \ 256 | "1:;" \ 257 | "push ebp;" \ 258 | "mov ebp, esp;" \ 259 | "push [ebp+32];" \ 260 | "push [ebp+28];" \ 261 | "push [ebp+24];" \ 262 | "push [ebp+20];" \ 263 | "push [ebp+16];" \ 264 | "push [ebp+12];" \ 265 | "push [ebp+8];" \ 266 | "push ecx;" \ 267 | "push ebx;" \ 268 | "push edx;" \ 269 | "push eax;" \ 270 | "call " #dst ";" \ 271 | "add esp, 44;" \ 272 | "pop ebp;" \ 273 | "ret 28;" \ 274 | ".elseif " #arg_count " == 12;" \ 275 | "1:;" \ 276 | "push ebp;" \ 277 | "mov ebp, esp;" \ 278 | "push [ebp+36];" \ 279 | "push [ebp+32];" \ 280 | "push [ebp+28];" \ 281 | "push [ebp+24];" \ 282 | "push [ebp+20];" \ 283 | "push [ebp+16];" \ 284 | "push [ebp+12];" \ 285 | "push [ebp+8];" \ 286 | "push ecx;" \ 287 | "push ebx;" \ 288 | "push edx;" \ 289 | "push eax;" \ 290 | "call " #dst ";" \ 291 | "add esp, 48;" \ 292 | "pop ebp;" \ 293 | "ret 32;" \ 294 | ".elseif " #arg_count " == 13;" \ 295 | "1:;" \ 296 | "push ebp;" \ 297 | "mov ebp, esp;" \ 298 | "push [ebp+40];" \ 299 | "push [ebp+36];" \ 300 | "push [ebp+32];" \ 301 | "push [ebp+28];" \ 302 | "push [ebp+24];" \ 303 | "push [ebp+20];" \ 304 | "push [ebp+16];" \ 305 | "push [ebp+12];" \ 306 | "push [ebp+8];" \ 307 | "push ecx;" \ 308 | "push ebx;" \ 309 | "push edx;" \ 310 | "push eax;" \ 311 | "call " #dst ";" \ 312 | "add esp, 52;" \ 313 | "pop ebp;" \ 314 | "ret 36;" \ 315 | ".elseif " #arg_count " == 14;" \ 316 | "1:;" \ 317 | "push ebp;" \ 318 | "mov ebp, esp;" \ 319 | "push [ebp+44];" \ 320 | "push [ebp+40];" \ 321 | "push [ebp+36];" \ 322 | "push [ebp+32];" \ 323 | "push [ebp+28];" \ 324 | "push [ebp+24];" \ 325 | "push [ebp+20];" \ 326 | "push [ebp+16];" \ 327 | "push [ebp+12];" \ 328 | "push [ebp+8];" \ 329 | "push ecx;" \ 330 | "push ebx;" \ 331 | "push edx;" \ 332 | "push eax;" \ 333 | "call " #dst ";" \ 334 | "add esp, 56;" \ 335 | "pop ebp;" \ 336 | "ret 40;" \ 337 | ".elseif " #arg_count " == 15;" \ 338 | "1:;" \ 339 | "push ebp;" \ 340 | "mov ebp, esp;" \ 341 | "push [ebp+48];" \ 342 | "push [ebp+44];" \ 343 | "push [ebp+40];" \ 344 | "push [ebp+36];" \ 345 | "push [ebp+32];" \ 346 | "push [ebp+28];" \ 347 | "push [ebp+24];" \ 348 | "push [ebp+20];" \ 349 | "push [ebp+16];" \ 350 | "push [ebp+12];" \ 351 | "push [ebp+8];" \ 352 | "push ecx;" \ 353 | "push ebx;" \ 354 | "push edx;" \ 355 | "push eax;" \ 356 | "call " #dst ";" \ 357 | "add esp, 60;" \ 358 | "pop ebp;" \ 359 | "ret 44;" \ 360 | ".else;" \ 361 | ".error \"Too many args in macro\";" \ 362 | ".endif;" 363 | 364 | #define DETOUR_3(start, end, dst) \ 365 | __asm ( \ 366 | PATCH_START(start) \ 367 | ".byte 0xE9;" \ 368 | ".long (" #dst ") - (" #start ") - 5;" \ 369 | ".fill (" #end ") - ((" #start ") + 5), 1, 0xCC;" \ 370 | PATCH_END \ 371 | ) 372 | 373 | #define DETOUR_4(start, end, dst, arg_count) \ 374 | __asm ( \ 375 | PATCH_START(start) \ 376 | ".byte 0xE9;" \ 377 | ".long 1f - (" #start ") - 5;" \ 378 | ".fill (" #end ") - ((" #start ") + 5), 1, 0xCC;" \ 379 | PATCH_END \ 380 | WATCALL_TO_CDECL(dst, arg_count) \ 381 | ) 382 | 383 | #define DETOUR_X(x,A,B,C,D,FUNC, ...) FUNC 384 | #define DETOUR(...) \ 385 | DETOUR_X(,##__VA_ARGS__, \ 386 | DETOUR_4(__VA_ARGS__), \ 387 | DETOUR_3(__VA_ARGS__), \ 388 | DETOUR_2(__VA_ARGS__), \ 389 | DETOUR_1(__VA_ARGS__), \ 390 | DETOUR_0(__VA_ARGS__) \ 391 | ) 392 | 393 | #define CALL_2(src, dst) \ 394 | __asm ( \ 395 | PATCH_START(src) \ 396 | ".byte 0xE8;" \ 397 | ".long (" #dst ") - (" #src ") - 5;" \ 398 | PATCH_END \ 399 | ) 400 | 401 | #define CALL_3(src, dst, arg_count) \ 402 | __asm ( \ 403 | PATCH_START(src) \ 404 | ".byte 0xE8;" \ 405 | ".long 1f - (" #src ") - 5;" \ 406 | PATCH_END \ 407 | WATCALL_TO_CDECL(dst, arg_count) \ 408 | ) 409 | 410 | #define CALL_X(x,A,B,C,FUNC, ...) FUNC 411 | #define CALL(...) \ 412 | CALL_X(,##__VA_ARGS__, \ 413 | CALL_3(__VA_ARGS__), \ 414 | CALL_2(__VA_ARGS__), \ 415 | CALL_1(__VA_ARGS__), \ 416 | CALL_0(__VA_ARGS__) \ 417 | ) 418 | 419 | #define CALL_NOP_2(src, dst) \ 420 | __asm ( \ 421 | PATCH_START(src) \ 422 | ".byte 0xE8;" \ 423 | ".long (" #dst ") - (" #src ") - 5;" \ 424 | ".byte 0x90;" \ 425 | PATCH_END \ 426 | ) 427 | 428 | #define CALL_NOP_3(src, dst, arg_count) \ 429 | __asm ( \ 430 | PATCH_START(src) \ 431 | ".byte 0xE8;" \ 432 | ".long 1f - (" #src ") - 5;" \ 433 | ".byte 0x90;" \ 434 | PATCH_END \ 435 | WATCALL_TO_CDECL(dst, arg_count) \ 436 | ) 437 | 438 | #define CALL_NOP_X(x,A,B,C,FUNC, ...) FUNC 439 | #define CALL_NOP(...) \ 440 | CALL_NOP_X(,##__VA_ARGS__, \ 441 | CALL_NOP_3(__VA_ARGS__), \ 442 | CALL_NOP_2(__VA_ARGS__), \ 443 | CALL_NOP_1(__VA_ARGS__), \ 444 | CALL_NOP_0(__VA_ARGS__) \ 445 | ) 446 | -------------------------------------------------------------------------------- /res/inc/macros/patch.inc: -------------------------------------------------------------------------------- 1 | ;; LICENSE = BSD0 - https://github.com/FunkyFr3sh/petool 2 | 3 | %macro @PATCH 1 4 | %ifctx __patch 5 | %error "can't nest patches" 6 | %else 7 | [section .patch] 8 | %push __patch 9 | dd (%1) ; patch addrss 10 | dd %$end - %$start ; length of the patch 11 | %$start: 12 | %endif 13 | %endmacro 14 | 15 | %macro @ENDPATCH 0 16 | %ifctx __patch 17 | %$end: 18 | %pop 19 | __SECT__ 20 | %else 21 | %error "no corresponding `@PATCH'" 22 | %endif 23 | %endmacro 24 | 25 | %macro @SET 2 26 | @PATCH (%1) 27 | %2 28 | @ENDPATCH 29 | %endmacro 30 | 31 | ;; arg1 : start address, will clear this 32 | ;; arg2 : what byte to put here. but be a byte long or shit breaks 33 | ;; arg3 : stop address, will NOT clear this. 34 | %macro @CLEAR 3 35 | @PATCH (%1) 36 | %if ((%3) - (%1)) < 0 37 | %error "stop address is less than start address in @CLEAR" (%3) 38 | %endif 39 | times ((%3) - (%1)) db (%2) ; patch data 40 | @ENDPATCH 41 | %endmacro 42 | 43 | %macro @SJMP 2 44 | @PATCH (%1) 45 | db 0xEB ; short jump 46 | db (%2) - (%1) - 2 ; relative offset 47 | @ENDPATCH 48 | %endmacro 49 | 50 | %macro @LJMP 2 51 | @PATCH (%1) 52 | db 0xE9 ; far jump 53 | dd (%2) - (%1) - 5 ; relative offset 54 | @ENDPATCH 55 | %endmacro 56 | 57 | %macro @CALL 2 58 | @PATCH (%1) 59 | db 0xE8 ; far call 60 | dd (%2) - (%1) - 5 ; relative offset 61 | @ENDPATCH 62 | %endmacro 63 | 64 | %macro @CALL_NOP 2 65 | @PATCH (%1) 66 | db 0xE8 ; far call 67 | dd (%2) - (%1) - 5 ; relative offset 68 | db 0x90 ; NOP 69 | @ENDPATCH 70 | %endmacro 71 | 72 | %macro @HOOK 1 73 | %define @HOOKEND (%1 + 5) 74 | @LJMP {(%1)}, _dest%1 75 | [section .text] 76 | align 16, db 0xCC 77 | _dest%1: 78 | %endmacro 79 | 80 | %macro @HOOK 2 81 | %define @HOOKEND %2 82 | %if (%2) - ((%1) + 5) < 0 83 | %error "end must be at least 5 bytes (the size of a long jump) after start" (%2) 84 | %endif 85 | 86 | @PATCH (%1) 87 | db 0xE9 ; far jump 88 | dd (_dest%1) - (%1) - 5 ; relative offset 89 | times ((%2) - ((%1) + 5)) db 0xCC ; clear int3 90 | @ENDPATCH 91 | 92 | [section .text] 93 | align 16, db 0xCC 94 | _dest%1: 95 | %endmacro 96 | 97 | %macro @CLEAR_NOP 2 98 | @CLEAR %1, 0x90, %2 99 | %endmacro 100 | 101 | %macro @CLEAR_INT 2 102 | @CLEAR %1, 0xCC, %2 103 | %endmacro 104 | -------------------------------------------------------------------------------- /res/inc/macros/patch.s: -------------------------------------------------------------------------------- 1 | # LICENSE = BSD0 - https://github.com/FunkyFr3sh/petool 2 | 3 | .macro @PATCH patchaddr:req 4 | .if __patch == 1 5 | .error "can't nest patches" 6 | .endif 7 | 8 | .set __patch, 1 9 | .section .patch,"d0" 10 | .long (\patchaddr) 11 | .long (9f - 8f) 12 | 8: 13 | .endm 14 | 15 | .macro @ENDPATCH 16 | .if __patch == 0 17 | .error "no corresponding `@PATCH'" 18 | .endif 19 | 20 | .ifndef __patch 21 | .error "no corresponding `@PATCH'" 22 | .endif 23 | 24 | .set __patch, 0 25 | 9: 26 | .section .text 27 | .endm 28 | 29 | .macro @SET addr:req, inst:req 30 | @PATCH (\addr) 31 | \inst 32 | @ENDPATCH 33 | .endm 34 | 35 | .macro @CLEAR start:req, value:req, end:req 36 | @PATCH (\start) 37 | .fill (\end) - (\start), 1, (\value) 38 | @ENDPATCH 39 | .endm 40 | 41 | .macro @SJMP src:req, dst:req 42 | @PATCH (\src) 43 | .byte 0xEB 44 | .byte (\dst) - (\src) - 2 45 | @ENDPATCH 46 | .endm 47 | 48 | .macro @LJMP src:req, dst:req 49 | @PATCH (\src) 50 | .byte 0xE9 51 | .long (\dst) - (\src) - 5 52 | @ENDPATCH 53 | .endm 54 | 55 | .macro @CALL src:req, dst:req 56 | @PATCH (\src) 57 | .byte 0xE8 58 | .long (\dst) - (\src) - 5 59 | @ENDPATCH 60 | .endm 61 | 62 | .macro @CALL_NOP src:req, dst:req 63 | @PATCH (\src) 64 | .byte 0xE8 65 | .long (\dst) - (\src) - 5 66 | .byte 0x90 67 | @ENDPATCH 68 | .endm 69 | 70 | .macro @HOOK addr:req, end 71 | .ifb \end 72 | .set @HOOKEND, (\addr) + 5 73 | @LJMP (\addr), _dest\addr 74 | .else 75 | .set @HOOKEND, (\end) 76 | 77 | .if (\end) - ((\addr) + 5) < 0 78 | .error "end must be at least 5 bytes (the size of a long jump) after start (\end)" 79 | .endif 80 | 81 | @PATCH (\addr) 82 | .byte 0xE9 83 | .long (_dest\addr) - (\addr) - 5 84 | .fill (\end) - ((\addr) + 5), 1, 0xCC 85 | @ENDPATCH 86 | .endif 87 | 88 | .section .text 89 | .align 16, 0xCC 90 | _dest\addr\(): 91 | .endm 92 | 93 | .macro @CLEAR_NOP start:req, end:req 94 | @CLEAR (\start), 0x90, (\end) 95 | .endm 96 | 97 | .macro @CLEAR_INT start:req, end:req 98 | @CLEAR (\start), 0xCC, (\end) 99 | .endm 100 | -------------------------------------------------------------------------------- /res/inc/macros/setsym.h: -------------------------------------------------------------------------------- 1 | // LICENSE = BSD0 - https://github.com/FunkyFr3sh/petool 2 | 3 | #define CDECL_TO_WATCALL(addr, arg_count) \ 4 | "push ebx;" \ 5 | "push ebp;" \ 6 | "mov ebp, esp;" \ 7 | ".if " #arg_count " >= 16;" \ 8 | ".error \"Too many args in macro\";" \ 9 | ".endif;" \ 10 | ".if " #arg_count " >= 15;" \ 11 | "push [ebp+68];" \ 12 | ".endif;" \ 13 | ".if " #arg_count " >= 14;" \ 14 | "push [ebp+64];" \ 15 | ".endif;" \ 16 | ".if " #arg_count " >= 13;" \ 17 | "push [ebp+60];" \ 18 | ".endif;" \ 19 | ".if " #arg_count " >= 12;" \ 20 | "push [ebp+56];" \ 21 | ".endif;" \ 22 | ".if " #arg_count " >= 11;" \ 23 | "push [ebp+52];" \ 24 | ".endif;" \ 25 | ".if " #arg_count " >= 10;" \ 26 | "push [ebp+48];" \ 27 | ".endif;" \ 28 | ".if " #arg_count " >= 9;" \ 29 | "push [ebp+44];" \ 30 | ".endif;" \ 31 | ".if " #arg_count " >= 8;" \ 32 | "push [ebp+40];" \ 33 | ".endif;" \ 34 | ".if " #arg_count " >= 7;" \ 35 | "push [ebp+36];" \ 36 | ".endif;" \ 37 | ".if " #arg_count " >= 6;" \ 38 | "push [ebp+32];" \ 39 | ".endif;" \ 40 | ".if " #arg_count " >= 5;" \ 41 | "push [ebp+28];" \ 42 | ".endif;" \ 43 | ".if " #arg_count " >= 4;" \ 44 | "mov ecx, [ebp+24];" \ 45 | ".endif;" \ 46 | ".if " #arg_count " >= 3;" \ 47 | "mov ebx, [ebp+20];" \ 48 | ".endif;" \ 49 | ".if " #arg_count " >= 2;" \ 50 | "mov edx, [ebp+16];" \ 51 | ".endif;" \ 52 | ".if " #arg_count " >= 1;" \ 53 | "mov eax, [ebp+12];" \ 54 | ".endif;" \ 55 | "call " #addr ";" \ 56 | "pop ebp;" \ 57 | "pop ebx;" \ 58 | "ret;" 59 | 60 | #define SETCGLOB_2(addr, name) \ 61 | __asm ( \ 62 | ".global _" #name ";" \ 63 | ".equ _" #name ", " #addr ";" \ 64 | ) 65 | 66 | #define SETCGLOB_3(addr, name, arg_count) \ 67 | __asm ( \ 68 | ".global _" #name ";" \ 69 | ".global " #name ";" \ 70 | ".equ " #name ", " #addr ";" \ 71 | ".section .text;" \ 72 | ".align 16, 0xCC;" \ 73 | "_" #name ":;" \ 74 | CDECL_TO_WATCALL(addr, arg_count) \ 75 | ) 76 | 77 | #define SETCGLOB_X(x,A,B,C,FUNC, ...) FUNC 78 | #define SETCGLOB(...) \ 79 | SETCGLOB_X(,##__VA_ARGS__, \ 80 | SETCGLOB_3(__VA_ARGS__), \ 81 | SETCGLOB_2(__VA_ARGS__), \ 82 | SETCGLOB_1(__VA_ARGS__), \ 83 | SETCGLOB_0(__VA_ARGS__) \ 84 | ) 85 | 86 | #define TRAMPOLINE_3(addr, name, inst) \ 87 | __asm ( \ 88 | ".global _" #name ";" \ 89 | ".section .text;" \ 90 | ".align 16, 0xCC;" \ 91 | "_" #name ":;" \ 92 | inst ";" \ 93 | "jmp " #addr ";" \ 94 | ) 95 | 96 | #define TRAMPOLINE_4(addr, name, inst, arg_count) \ 97 | __asm ( \ 98 | ".global _" #name ";" \ 99 | ".global " #name ";" \ 100 | ".section .text;" \ 101 | ".align 16, 0xCC;" \ 102 | #name ":;" \ 103 | inst ";" \ 104 | "jmp " #addr ";" \ 105 | ".align 16, 0xCC;" \ 106 | "_" #name ":;" \ 107 | CDECL_TO_WATCALL(name, arg_count) \ 108 | ) 109 | 110 | #define TRAMPOLINE_X(x,A,B,C,D,FUNC, ...) FUNC 111 | #define TRAMPOLINE(...) \ 112 | TRAMPOLINE_X(,##__VA_ARGS__, \ 113 | TRAMPOLINE_4(__VA_ARGS__), \ 114 | TRAMPOLINE_3(__VA_ARGS__), \ 115 | TRAMPOLINE_2(__VA_ARGS__), \ 116 | TRAMPOLINE_1(__VA_ARGS__), \ 117 | TRAMPOLINE_0(__VA_ARGS__) \ 118 | ) 119 | -------------------------------------------------------------------------------- /res/inc/patch.h: -------------------------------------------------------------------------------- 1 | // LICENSE = BSD0 - https://github.com/FunkyFr3sh/petool 2 | 3 | #ifndef PATCH_H 4 | #define PATCH_H 5 | 6 | #include 7 | 8 | static inline void patch_clear(char *start, char value, char *end) 9 | { 10 | DWORD op = PAGE_EXECUTE_READ; 11 | VirtualProtect(start, end - start, PAGE_EXECUTE_READWRITE, &op); 12 | memset(start, value, end - start); 13 | VirtualProtect(start, end - start, op, &op); 14 | } 15 | 16 | static inline void patch_sjmp(char *src, char *dst) 17 | { 18 | DWORD op = PAGE_EXECUTE_READ; 19 | VirtualProtect(src, 2, PAGE_EXECUTE_READWRITE, &op); 20 | src[0] = '\xEB'; 21 | src[1] = dst - src - 2; 22 | VirtualProtect(src, 2, op, &op); 23 | } 24 | 25 | static inline void patch_ljmp(char *src, char *dst) 26 | { 27 | DWORD op = PAGE_EXECUTE_READ; 28 | VirtualProtect(src, 5, PAGE_EXECUTE_READWRITE, &op); 29 | src[0] = '\xE9'; 30 | *((DWORD *)(&src[1])) = dst - src - 5; 31 | VirtualProtect(src, 5, op, &op); 32 | } 33 | 34 | static inline PROC patch_call(char *src, char *dst) 35 | { 36 | DWORD op = PAGE_EXECUTE_READ; 37 | VirtualProtect(src, 5, PAGE_EXECUTE_READWRITE, &op); 38 | src[0] = '\xE8'; 39 | DWORD org = *((DWORD *)(&src[1])); 40 | *((DWORD *)(&src[1])) = dst - src - 5; 41 | VirtualProtect(src, 5, op, &op); 42 | return (PROC)(src + 5 + org); 43 | } 44 | 45 | static inline void patch_call_nop(char *src, char *dst) 46 | { 47 | DWORD op = PAGE_EXECUTE_READ; 48 | VirtualProtect(src, 6, PAGE_EXECUTE_READWRITE, &op); 49 | src[0] = '\xE8'; 50 | *((DWORD *)(&src[1])) = dst - src - 5; 51 | src[5] = '\x90'; 52 | VirtualProtect(src, 6, op, &op); 53 | } 54 | 55 | static inline DWORD patch_setdword(DWORD *dst, DWORD value) 56 | { 57 | DWORD op = PAGE_EXECUTE_READ; 58 | VirtualProtect(dst, sizeof(DWORD), PAGE_EXECUTE_READWRITE, &op); 59 | DWORD org = *dst; 60 | *dst = value; 61 | VirtualProtect(dst, sizeof(DWORD), op, &op); 62 | return org; 63 | } 64 | 65 | static inline WORD patch_setword(WORD *dst, WORD value) 66 | { 67 | DWORD op = PAGE_EXECUTE_READ; 68 | VirtualProtect(dst, sizeof(WORD), PAGE_EXECUTE_READWRITE, &op); 69 | WORD org = *dst; 70 | *dst = value; 71 | VirtualProtect(dst, sizeof(WORD), op, &op); 72 | return org; 73 | } 74 | 75 | static inline BYTE patch_setbyte(BYTE *dst, BYTE value) 76 | { 77 | DWORD op = PAGE_EXECUTE_READ; 78 | VirtualProtect(dst, sizeof(BYTE), PAGE_EXECUTE_READWRITE, &op); 79 | BYTE org = *dst; 80 | *dst = value; 81 | VirtualProtect(dst, sizeof(BYTE), op, &op); 82 | return org; 83 | } 84 | 85 | static inline void patch_setbytes(char *dst, char *buf, size_t size) 86 | { 87 | DWORD op = PAGE_EXECUTE_READ; 88 | VirtualProtect(dst, size, PAGE_EXECUTE_READWRITE, &op); 89 | memcpy(dst, buf, size); 90 | VirtualProtect(dst, size, op, &op); 91 | } 92 | 93 | #define PATCH_SET(a,b) patch_setbytes(a,(char*)b,sizeof(b)-1) 94 | 95 | static inline float patch_setfloat(float *dst, float value) 96 | { 97 | DWORD op = PAGE_EXECUTE_READ; 98 | VirtualProtect(dst, sizeof(float), PAGE_EXECUTE_READWRITE, &op); 99 | float org = *dst; 100 | *dst = value; 101 | VirtualProtect(dst, sizeof(float), op, &op); 102 | return org; 103 | } 104 | 105 | static inline double patch_setdouble(double *dst, double value) 106 | { 107 | DWORD op = PAGE_EXECUTE_READ; 108 | VirtualProtect(dst, sizeof(double), PAGE_EXECUTE_READWRITE, &op); 109 | double org = *dst; 110 | *dst = value; 111 | VirtualProtect(dst, sizeof(double), op, &op); 112 | return org; 113 | } 114 | 115 | static inline void patch_clear_nop(char *start, char *end) 116 | { 117 | patch_clear(start, '\x90', end); 118 | } 119 | 120 | static inline void patch_clear_int(char *start, char *end) 121 | { 122 | patch_clear(start, '\xCC', end); 123 | } 124 | 125 | static inline void patch_detour(char *start, char *end, char *dst) 126 | { 127 | patch_clear(start + 5, '\xCC', end); 128 | patch_ljmp(start, dst); 129 | } 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /res/proxy/proxy.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 16.0 15 | {CC80C420-C375-4E62-B360-55597920149F} 16 | Win32Proj 17 | $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) 18 | 19 | 20 | 21 | DynamicLibrary 22 | true 23 | $(DefaultPlatformToolset) 24 | MultiByte 25 | 26 | 27 | DynamicLibrary 28 | false 29 | $(DefaultPlatformToolset) 30 | true 31 | MultiByte 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | true 47 | .dll 48 | 49 | 50 | false 51 | .dll 52 | 53 | 54 | 55 | NotUsing 56 | Level3 57 | Disabled 58 | false 59 | WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 60 | false 61 | 62 | 63 | MultiThreadedDebug 64 | 65 | 66 | Windows 67 | true 68 | false 69 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 70 | exports.def 71 | 72 | 73 | 74 | 75 | NotUsing 76 | Level3 77 | MaxSpeed 78 | true 79 | true 80 | false 81 | WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 82 | false 83 | 84 | 85 | MultiThreaded 86 | 87 | 88 | Windows 89 | true 90 | true 91 | true 92 | false 93 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 94 | exports.def 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /res/proxy/readme.txt: -------------------------------------------------------------------------------- 1 | ## system ## 2 | Proxy for dlls located in the windows system directory (C:\Windows\System32) 3 | 4 | ## local ## 5 | Proxy for dlls located in the game directory (e.g. SMACKW32.DLL, BINKW32.DLL, ...) 6 | 7 | Note: 8 | For local dlls you must append a "x" to the filename of the original dll: "SMACKW32.DLL" -> "SMACKW32x.DLL" 9 | Place both, your proxy "SMACKW32.DLL" and the renamed original dll "SMACKW32x.DLL" into the game directory 10 | -------------------------------------------------------------------------------- /res/proxy/res.rc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1 VERSIONINFO 4 | FILEVERSION 1, 0, 0, 0 5 | PRODUCTVERSION 1, 0, 0, 0 6 | { 7 | BLOCK "StringFileInfo" 8 | { 9 | BLOCK "040904B0" 10 | { 11 | VALUE "CompanyName", "github.com/FunkyFr3sh" 12 | VALUE "FileDescription", "petool genproxy" 13 | VALUE "FileVersion", "1.0.0.0" 14 | VALUE "LegalCopyright", "Copyright (c) 2024" 15 | VALUE "ProductName", "petool" 16 | VALUE "ProductVersion", "1.0.0.0" 17 | VALUE "Comments", "https://github.com/FunkyFr3sh/petool" 18 | } 19 | } 20 | 21 | BLOCK "VarFileInfo" 22 | { 23 | VALUE "Translation", 0x0409, 0x04B0 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /res/src/winmain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "macros/patch.h" 3 | #include "app.h" 4 | 5 | 6 | int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PSTR cmdline, int cmdshow) 7 | { 8 | return OriginalCRTStartup(); 9 | } 10 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | (import {}).callPackage ./. {} 2 | -------------------------------------------------------------------------------- /src/cleanup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define FAIL_IF_SILENT(COND) \ 4 | if (COND) { \ 5 | ret = EXIT_FAILURE; \ 6 | goto cleanup; \ 7 | } 8 | 9 | #define FAIL_IF(COND, ...) \ 10 | if (COND) { \ 11 | fprintf(stderr, __VA_ARGS__); \ 12 | ret = EXIT_FAILURE; \ 13 | goto cleanup; \ 14 | } 15 | 16 | #define FAIL_IF_PERROR(COND, MSG) \ 17 | if (COND) { \ 18 | perror(MSG); \ 19 | ret = EXIT_FAILURE; \ 20 | goto cleanup; \ 21 | } 22 | -------------------------------------------------------------------------------- /src/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "cleanup.h" 10 | #include "common.h" 11 | 12 | 13 | // Caller cleans up fh and image, length and fh_out are optional 14 | int open_and_read(FILE **fh_out, int8_t **image, uint32_t *length, 15 | const char* executable, const char *fopen_attr) 16 | { 17 | int ret = EXIT_SUCCESS; 18 | FILE* fh = NULL; 19 | 20 | fh = fopen(executable, fopen_attr ? fopen_attr : "rb"); 21 | if (!fh) { 22 | // Somehow this does fail a lot on windows - retry after 1 second does fix it 23 | sleep(1); 24 | fh = fopen(executable, fopen_attr ? fopen_attr : "rb"); 25 | } 26 | if (fh_out) *fh_out = fh; 27 | 28 | FAIL_IF_PERROR(!fh, "Could not open executable"); 29 | 30 | FAIL_IF_PERROR(fseek(fh, 0L, SEEK_END), 31 | "Need seekable file for executable, not stream"); 32 | 33 | uint32_t len = ftell(fh); 34 | rewind(fh); 35 | 36 | *image = malloc(len); 37 | FAIL_IF(!*image, "Failed to allocate memory to read executable with\n"); 38 | 39 | FAIL_IF_PERROR(fread(*image, len, 1, fh) != 1, "Error reading executable"); 40 | 41 | if (length) *length = len; 42 | 43 | cleanup: 44 | if (!fh_out) 45 | { 46 | if (fh) fclose(fh); 47 | } 48 | 49 | return ret; 50 | } 51 | 52 | bool file_exists(const char *path) 53 | { 54 | FILE *fh = fopen(path, "r"); 55 | 56 | if (fh) 57 | { 58 | fclose(fh); 59 | return true; 60 | } 61 | 62 | return false; 63 | } 64 | 65 | const char *file_basename(const char *path) 66 | { 67 | static char basebuf[4096]; 68 | size_t i; 69 | 70 | if (path == NULL) 71 | return NULL; 72 | 73 | for (i = strlen(path); i > 0; i--) 74 | { 75 | if (path[i] == '/' || path[i] == '\\') 76 | { 77 | i++; 78 | break; 79 | } 80 | } 81 | 82 | strncpy(basebuf, path + i, sizeof basebuf - 1); 83 | basebuf[sizeof basebuf - 1] = '\0'; 84 | 85 | char* p = strchr(basebuf, ' '); 86 | if (p) 87 | { 88 | do 89 | { 90 | *p = '_'; 91 | } while ((p = strchr(p, ' '))); 92 | } 93 | 94 | return basebuf; 95 | } 96 | 97 | const char* file_basename_no_ext(const char* path) 98 | { 99 | static char basebuf[4096]; 100 | size_t i; 101 | 102 | if (path == NULL) 103 | return NULL; 104 | 105 | for (i = strlen(path); i > 0; i--) 106 | { 107 | if (path[i] == '/' || path[i] == '\\') 108 | { 109 | i++; 110 | break; 111 | } 112 | } 113 | 114 | strncpy(basebuf, path + i, sizeof basebuf - 1); 115 | basebuf[sizeof basebuf - 1] = '\0'; 116 | 117 | char* p = strchr(basebuf, ' '); 118 | if (p) 119 | { 120 | do 121 | { 122 | *p = '_'; 123 | } while ((p = strchr(p, ' '))); 124 | } 125 | 126 | char* x = strrchr(basebuf, '.'); 127 | if (x) 128 | { 129 | *x = '\0'; 130 | } 131 | 132 | return basebuf; 133 | } 134 | 135 | const char* file_escaped_basename(const char* path) 136 | { 137 | static char basebuf[4096]; 138 | size_t i; 139 | 140 | if (path == NULL) 141 | return NULL; 142 | 143 | for (i = strlen(path); i > 0; i--) 144 | { 145 | if (path[i] == '/' || path[i] == '\\') 146 | { 147 | i++; 148 | break; 149 | } 150 | } 151 | 152 | strncpy(basebuf, path + i, sizeof basebuf - 1); 153 | basebuf[sizeof basebuf - 1] = '\0'; 154 | 155 | char* p = strchr(basebuf, ' '); 156 | if (p) 157 | { 158 | do 159 | { 160 | memmove(p + 1, p, strlen(p) + 1); 161 | 162 | *p++ = '\\'; 163 | } while ((p = strchr(++p, ' '))); 164 | } 165 | 166 | return basebuf; 167 | } 168 | 169 | int file_copy(const char* from, const char *to) 170 | { 171 | int ret = EXIT_SUCCESS; 172 | FILE *from_fh = NULL, *to_fh = NULL; 173 | static char buf[4096]; 174 | size_t numread; 175 | 176 | from_fh = fopen(from, "rb"); 177 | FAIL_IF_PERROR(!from_fh, "Could not open input file"); 178 | 179 | FAIL_IF(file_exists(to), "Output file already exists.\n"); 180 | 181 | to_fh = fopen(to, "wb"); 182 | FAIL_IF_PERROR(!to_fh, "Could not open output file"); 183 | 184 | while ((numread = fread(buf, 1, sizeof buf, from_fh)) > 0) 185 | { 186 | FAIL_IF_PERROR(fwrite(buf, 1, numread, to_fh) != numread, "Output file truncated"); 187 | } 188 | 189 | cleanup: 190 | if (from_fh) fclose(from_fh); 191 | if (to_fh) fclose(to_fh); 192 | return ret; 193 | } 194 | 195 | int extract_resource(const char* src, char* file_path) 196 | { 197 | int ret = EXIT_SUCCESS; 198 | FILE* fh = NULL; 199 | 200 | FAIL_IF(file_exists(file_path), "Output file already exists.\n"); 201 | 202 | fh = fopen(file_path, "wb"); 203 | FAIL_IF_PERROR(!fh, "Could not open output file"); 204 | 205 | fputs(src, fh); 206 | 207 | if (ferror(fh)) 208 | { 209 | ret = EXIT_FAILURE; 210 | } 211 | 212 | fclose(fh); 213 | 214 | cleanup: 215 | return ret; 216 | } 217 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int open_and_read(FILE**, int8_t**, uint32_t *, const char*, const char*); 5 | bool file_exists(const char *path); 6 | const char *file_basename(const char *path); 7 | const char* file_basename_no_ext(const char* path); 8 | const char* file_escaped_basename(const char* path); 9 | int file_copy(const char* from, const char *to); 10 | int extract_resource(const char* src, char* file_path); 11 | -------------------------------------------------------------------------------- /src/dump.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "pe.h" 29 | #include "cleanup.h" 30 | #include "common.h" 31 | 32 | int dump(int argc, char **argv) 33 | { 34 | // decleration before more meaningful initialization for cleanup 35 | int ret = EXIT_SUCCESS; 36 | int8_t *image = NULL; 37 | uint32_t length; 38 | 39 | FAIL_IF(argc < 2, "usage: petool dump \n"); 40 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 41 | 42 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 43 | PIMAGE_NT_HEADERS nt_hdr = (void *)(image + dos_hdr->e_lfanew); 44 | 45 | if (dos_hdr->e_magic == IMAGE_DOS_SIGNATURE) 46 | { 47 | if (nt_hdr->Signature != IMAGE_NT_SIGNATURE) 48 | { 49 | uint32_t exe_start = dos_hdr->e_cparhdr * 16L; 50 | uint32_t exe_end = dos_hdr->e_cp * 512L - (dos_hdr->e_cblp ? 512L - dos_hdr->e_cblp : 0); 51 | 52 | printf("DOS Header:\n"); 53 | printf(" e_magic: %04X\n", dos_hdr->e_magic); 54 | printf(" e_cblp: %04X\n", dos_hdr->e_cblp); 55 | printf(" e_cp: %04X\n", dos_hdr->e_cp); 56 | printf(" e_crlc: %04X\n", dos_hdr->e_crlc); 57 | printf(" e_cparhdr: %04X\n", dos_hdr->e_cparhdr); 58 | printf(" e_minalloc: %04X\n", dos_hdr->e_minalloc); 59 | printf(" e_maxalloc: %04X\n", dos_hdr->e_maxalloc); 60 | printf(" e_ss: %04X\n", dos_hdr->e_ss); 61 | printf(" e_sp: %04X\n", dos_hdr->e_sp); 62 | printf(" e_csum: %04X\n", dos_hdr->e_csum); 63 | printf(" e_ip: %04X\n", dos_hdr->e_ip); 64 | printf(" e_cs: %04X\n", dos_hdr->e_cs); 65 | printf(" e_lfarlc: %04X\n", dos_hdr->e_lfarlc); 66 | printf(" e_ovno: %04X\n", dos_hdr->e_ovno); 67 | 68 | printf("\nEXE data is from offset %04X (%d) to %04X (%d).\n", exe_start, exe_start, exe_end, exe_end); 69 | goto cleanup; 70 | } 71 | } 72 | else 73 | { 74 | // a hack for raw COFF object files 75 | nt_hdr = (void *)(image - 4); 76 | } 77 | 78 | FAIL_IF(nt_hdr->FileHeader.Machine != IMAGE_FILE_MACHINE_I386, "Machine type is not i386.\n"); 79 | 80 | printf(" section start end length vaddr vsize flags align\n"); 81 | printf("-------------------------------------------------------------------------\n"); 82 | 83 | for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 84 | { 85 | const PIMAGE_SECTION_HEADER cur_sct = IMAGE_FIRST_SECTION(nt_hdr) + i; 86 | 87 | uint32_t align = (cur_sct->Characteristics & IMAGE_SCN_ALIGN_MASK) 88 | ? (uint32_t)(1 << (((cur_sct->Characteristics & IMAGE_SCN_ALIGN_MASK) >> 20) - 1)) 89 | : nt_hdr->OptionalHeader.SectionAlignment; 90 | 91 | printf( 92 | "%8.8s %8"PRIX32"h %8"PRIX32"h %9"PRIu32" %8"PRIX32"h %9"PRIu32" %c%c%c%c%c%c %6"PRIX32"h\n", 93 | cur_sct->Name, 94 | cur_sct->PointerToRawData, 95 | cur_sct->PointerToRawData ? cur_sct->PointerToRawData + cur_sct->SizeOfRawData : 0, 96 | cur_sct->SizeOfRawData, 97 | cur_sct->VirtualAddress + nt_hdr->OptionalHeader.ImageBase, 98 | cur_sct->Misc.VirtualSize, 99 | cur_sct->Characteristics & IMAGE_SCN_MEM_READ ? 'r' : '-', 100 | cur_sct->Characteristics & IMAGE_SCN_MEM_WRITE ? 'w' : '-', 101 | cur_sct->Characteristics & IMAGE_SCN_MEM_EXECUTE ? 'x' : '-', 102 | cur_sct->Characteristics & IMAGE_SCN_CNT_CODE ? 'c' : '-', 103 | cur_sct->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA ? 'i' : '-', 104 | cur_sct->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ? 'u' : '-', 105 | align 106 | ); 107 | } 108 | 109 | printf("\n"); 110 | 111 | char dirs[][40] = { 112 | "Export Directory", 113 | "Import Directory", 114 | "Resource Directory", 115 | "Exception Directory", 116 | "Security Directory", 117 | "Base Relocation Table", 118 | "Debug Directory", 119 | "Architecture Specific Data", 120 | "RVA of GP", 121 | "TLS Directory", 122 | "Load Configuration Directory", 123 | "Bound Import Directory in headers", 124 | "Import Address Table", 125 | "Delay Load Import Descriptors", 126 | "COM Runtime descriptor" 127 | }; 128 | 129 | printf("DataDirectory vaddr size section\n"); 130 | printf("-------------------------------------------------------------------------\n"); 131 | 132 | for (uint32_t i = 0; i < nt_hdr->OptionalHeader.NumberOfRvaAndSizes; i++) 133 | { 134 | if (!nt_hdr->OptionalHeader.DataDirectory[i].VirtualAddress) 135 | continue; 136 | 137 | char section[12] = { 0 }; 138 | 139 | for (int x = 0; x < nt_hdr->FileHeader.NumberOfSections; x++) 140 | { 141 | const PIMAGE_SECTION_HEADER cur_sct = IMAGE_FIRST_SECTION(nt_hdr) + x; 142 | 143 | if (nt_hdr->OptionalHeader.DataDirectory[i].VirtualAddress >= cur_sct->VirtualAddress && 144 | nt_hdr->OptionalHeader.DataDirectory[i].VirtualAddress < cur_sct->VirtualAddress + cur_sct->SizeOfRawData) 145 | { 146 | memcpy(section, (void*)cur_sct->Name, 8); 147 | break; 148 | } 149 | } 150 | 151 | printf( 152 | "%-43s %8"PRIX32"h %9"PRIu32" %8.8s\n", 153 | dirs[i], 154 | nt_hdr->OptionalHeader.DataDirectory[i].VirtualAddress + nt_hdr->OptionalHeader.ImageBase, 155 | nt_hdr->OptionalHeader.DataDirectory[i].Size, 156 | section); 157 | } 158 | 159 | printf("\n"); 160 | 161 | cleanup: 162 | if (image) free(image); 163 | return ret; 164 | } 165 | -------------------------------------------------------------------------------- /src/export.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "pe.h" 27 | #include "cleanup.h" 28 | #include "common.h" 29 | 30 | int export(int argc, char **argv) 31 | { 32 | // decleration before more meaningful initialization for cleanup 33 | int ret = EXIT_SUCCESS; 34 | int8_t *image = NULL; 35 | uint32_t length; 36 | 37 | FAIL_IF(argc < 2, "usage: petool export [section]\n"); 38 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 39 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 40 | 41 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 42 | PIMAGE_NT_HEADERS nt_hdr = (void *)(image + dos_hdr->e_lfanew); 43 | 44 | char *section = argc > 2 ? (char *)argv[2] : ".data"; 45 | int8_t *data = NULL; 46 | int32_t data_len = 0; 47 | 48 | for (int32_t i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 49 | { 50 | PIMAGE_SECTION_HEADER sct_hdr = IMAGE_FIRST_SECTION(nt_hdr) + i; 51 | 52 | if (strncmp(section, (char *)sct_hdr->Name, 8) == 0) 53 | { 54 | data = image + sct_hdr->PointerToRawData; 55 | data_len = sct_hdr->SizeOfRawData; 56 | break; 57 | } 58 | 59 | sct_hdr++; 60 | } 61 | 62 | FAIL_IF(data == NULL, "No '%s' section in given PE image.\n", section); 63 | 64 | fwrite(data, data_len, 1, stdout); 65 | 66 | cleanup: 67 | if (image) free(image); 68 | return ret; 69 | } 70 | -------------------------------------------------------------------------------- /src/genfiles.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifdef __linux__ 18 | #include 19 | #elif defined(__FreeBSD__ ) 20 | #include 21 | #elif !defined(_WIN32) 22 | #include 23 | #endif 24 | 25 | #ifdef _WIN32 26 | #include 27 | #include 28 | #else 29 | #include 30 | #include 31 | #define MAX_PATH PATH_MAX 32 | #define _mkdir(a) mkdir(a, 0777) 33 | #endif 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include "common.h" 40 | #include "cleanup.h" 41 | 42 | extern const char res_gitignore[]; 43 | extern const char res_build_cmd[]; 44 | extern const char res_readme_md[]; 45 | extern const char res_src_winmain_c[]; 46 | extern const char res_inc_app_h[]; 47 | extern const char res_inc_app_inc[]; 48 | extern const char res_inc_patch_h[]; 49 | extern const char res_inc_macros_datatypes_inc[]; 50 | extern const char res_inc_macros_datatypes_s[]; 51 | extern const char res_inc_macros_extern_inc[]; 52 | extern const char res_inc_macros_extern_s[]; 53 | extern const char res_inc_macros_patch_h[]; 54 | extern const char res_inc_macros_patch_inc[]; 55 | extern const char res_inc_macros_setsym_h[]; 56 | extern const char res_inc_macros_patch_s[]; 57 | 58 | int genfiles(char *dir) 59 | { 60 | int ret = EXIT_SUCCESS; 61 | 62 | static char buf[MAX_PATH]; 63 | 64 | snprintf(buf, sizeof buf, "%s/.gitignore", dir); 65 | printf("Generating %s...\n", buf); 66 | FAIL_IF(extract_resource(res_gitignore, buf) != EXIT_SUCCESS, "Failed to create .gitignore\n"); 67 | 68 | snprintf(buf, sizeof buf, "%s/build.cmd", dir); 69 | printf("Generating %s...\n", buf); 70 | FAIL_IF(extract_resource(res_build_cmd, buf) != EXIT_SUCCESS, "Failed to create build.cmd\n"); 71 | 72 | snprintf(buf, sizeof buf, "%s/README.md", dir); 73 | printf("Generating %s...\n", buf); 74 | FAIL_IF(extract_resource(res_readme_md, buf) != EXIT_SUCCESS, "Failed to create README.md\n"); 75 | 76 | snprintf(buf, sizeof buf, "%s/src", dir); 77 | FAIL_IF_PERROR(_mkdir(buf) == -1, "Failed to create src subdirectory"); 78 | 79 | snprintf(buf, sizeof buf, "%s/src/winmain.cpp", dir); 80 | printf("Generating %s...\n", buf); 81 | FAIL_IF(extract_resource(res_src_winmain_c, buf) != EXIT_SUCCESS, "Failed to create winmain.cpp\n"); 82 | 83 | snprintf(buf, sizeof buf, "%s/inc", dir); 84 | FAIL_IF_PERROR(_mkdir(buf) == -1, "Failed to create inc subdirectory"); 85 | 86 | snprintf(buf, sizeof buf, "%s/inc/app.h", dir); 87 | printf("Generating %s...\n", buf); 88 | FAIL_IF(extract_resource(res_inc_app_h, buf) != EXIT_SUCCESS, "Failed to create app.h\n"); 89 | 90 | snprintf(buf, sizeof buf, "%s/inc/app.inc", dir); 91 | printf("Generating %s...\n", buf); 92 | FAIL_IF(extract_resource(res_inc_app_inc, buf) != EXIT_SUCCESS, "Failed to create app.inc\n"); 93 | 94 | snprintf(buf, sizeof buf, "%s/inc/patch.h", dir); 95 | printf("Generating %s...\n", buf); 96 | FAIL_IF(extract_resource(res_inc_patch_h, buf) != EXIT_SUCCESS, "Failed to create patch.h\n"); 97 | 98 | snprintf(buf, sizeof buf, "%s/inc/macros", dir); 99 | FAIL_IF_PERROR(_mkdir(buf) == -1, "Failed to create macros subdirectory"); 100 | 101 | snprintf(buf, sizeof buf, "%s/inc/macros/datatypes.inc", dir); 102 | printf("Generating %s...\n", buf); 103 | FAIL_IF(extract_resource(res_inc_macros_datatypes_inc, buf) != EXIT_SUCCESS, "Failed to create datatypes.inc\n"); 104 | 105 | snprintf(buf, sizeof buf, "%s/inc/macros/datatypes.s", dir); 106 | printf("Generating %s...\n", buf); 107 | FAIL_IF(extract_resource(res_inc_macros_datatypes_s, buf) != EXIT_SUCCESS, "Failed to create datatypes.s\n"); 108 | 109 | snprintf(buf, sizeof buf, "%s/inc/macros/extern.inc", dir); 110 | printf("Generating %s...\n", buf); 111 | FAIL_IF(extract_resource(res_inc_macros_extern_inc, buf) != EXIT_SUCCESS, "Failed to create extern.inc\n"); 112 | 113 | snprintf(buf, sizeof buf, "%s/inc/macros/extern.s", dir); 114 | printf("Generating %s...\n", buf); 115 | FAIL_IF(extract_resource(res_inc_macros_extern_s, buf) != EXIT_SUCCESS, "Failed to create extern.s\n"); 116 | 117 | snprintf(buf, sizeof buf, "%s/inc/macros/patch.h", dir); 118 | printf("Generating %s...\n", buf); 119 | FAIL_IF(extract_resource(res_inc_macros_patch_h, buf) != EXIT_SUCCESS, "Failed to create patch.h\n"); 120 | 121 | snprintf(buf, sizeof buf, "%s/inc/macros/patch.inc", dir); 122 | printf("Generating %s...\n", buf); 123 | FAIL_IF(extract_resource(res_inc_macros_patch_inc, buf) != EXIT_SUCCESS, "Failed to create patch.inc\n"); 124 | 125 | snprintf(buf, sizeof buf, "%s/inc/macros/setsym.h", dir); 126 | printf("Generating %s...\n", buf); 127 | FAIL_IF(extract_resource(res_inc_macros_setsym_h, buf) != EXIT_SUCCESS, "Failed to create setsym.h\n"); 128 | 129 | snprintf(buf, sizeof buf, "%s/inc/macros/patch.s", dir); 130 | printf("Generating %s...\n", buf); 131 | FAIL_IF(extract_resource(res_inc_macros_patch_s, buf) != EXIT_SUCCESS, "Failed to create patch.s\n"); 132 | 133 | cleanup: 134 | return ret; 135 | } 136 | -------------------------------------------------------------------------------- /src/genmak.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "pe.h" 25 | #include "cleanup.h" 26 | #include "common.h" 27 | 28 | int genmak(int argc, char **argv) 29 | { 30 | int ret = EXIT_SUCCESS; 31 | int8_t *image = NULL; 32 | FILE *ofh = argc > 2 ? NULL : stdout; 33 | uint32_t length; 34 | 35 | FAIL_IF(argc < 2, "usage: petool genmak [ofile]\n"); 36 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 37 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 38 | 39 | if (argc > 2) 40 | { 41 | FAIL_IF(file_exists(argv[2]), "%s: output file already exists.\n", argv[2]); 42 | ofh = fopen(argv[2], "w"); 43 | FAIL_IF_PERROR(ofh == NULL, "%s"); 44 | } 45 | 46 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 47 | PIMAGE_NT_HEADERS nt_hdr = (void *)(image + dos_hdr->e_lfanew); 48 | 49 | fprintf(ofh, "-include config.mk\n\n"); 50 | fprintf(ofh, "INPUT = %s.dat\n", file_basename_no_ext(argv[1])); 51 | fprintf(ofh, "OUTPUT = %s\n", file_escaped_basename(argv[1])); 52 | fprintf(ofh, "LDS = %s.lds\n", file_basename_no_ext(argv[1])); 53 | 54 | fprintf(ofh, "\n"); 55 | 56 | fprintf(ofh, "LDFLAGS ?="); 57 | 58 | if (nt_hdr->OptionalHeader.SectionAlignment != 0x1000) 59 | fprintf(ofh, " -Wl,--section-alignment=0x%"PRIX32"", nt_hdr->OptionalHeader.SectionAlignment); 60 | 61 | if (nt_hdr->OptionalHeader.FileAlignment != 0x200) 62 | fprintf(ofh, " -Wl,--file-alignment=0x%"PRIX32"", nt_hdr->OptionalHeader.FileAlignment); 63 | 64 | if (nt_hdr->OptionalHeader.ImageBase != 0x00400000) 65 | fprintf(ofh, " -Wl,--image-base=0x%08"PRIX32"", nt_hdr->OptionalHeader.ImageBase); 66 | 67 | if (nt_hdr->OptionalHeader.Subsystem == 2) 68 | fprintf(ofh, " -Wl,--subsystem=windows"); 69 | 70 | if (!(nt_hdr->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) 71 | fprintf(ofh, " -Wl,--disable-nxcompat"); 72 | 73 | fprintf(ofh, " -Wl,--disable-reloc-section -Wl,--enable-stdcall-fixup -static"); 74 | 75 | fprintf(ofh, "\n"); 76 | 77 | fprintf(ofh, "ASFLAGS ?= -Iinc\n"); 78 | fprintf(ofh, "NFLAGS ?= -Iinc -f elf\n"); 79 | fprintf(ofh, "CFLAGS ?= -Iinc -O2 -march=pentium4 -Wall -masm=intel\n"); 80 | fprintf(ofh, "CXXFLAGS ?= -Iinc -O2 -march=pentium4 -Wall -masm=intel\n"); 81 | 82 | fprintf(ofh, "\n"); 83 | 84 | fprintf(ofh, "LIBS = -lgdi32\n"); 85 | 86 | fprintf(ofh, "\n"); 87 | 88 | fprintf(ofh, "OBJS ="); 89 | 90 | if (strcmp(argv[0], "genmak") != 0) 91 | { 92 | fprintf(ofh, " \\\n sym.o"); 93 | } 94 | 95 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 2 && nt_hdr->OptionalHeader.DataDirectory[2].VirtualAddress) 96 | { 97 | fprintf(ofh, " \\\n rsrc.o"); 98 | } 99 | 100 | if (strcmp(argv[0], "genmak") != 0) 101 | { 102 | fprintf(ofh, " \\\n src/winmain.o"); 103 | } 104 | 105 | fprintf(ofh, "\n\n"); 106 | 107 | fprintf(ofh, "CC = i686-w64-mingw32-gcc\n"); 108 | fprintf(ofh, "CXX = i686-w64-mingw32-g++\n"); 109 | fprintf(ofh, "AS = i686-w64-mingw32-as\n"); 110 | fprintf(ofh, "STRIP ?= i686-w64-mingw32-strip\n"); 111 | fprintf(ofh, "WINDRES ?= i686-w64-mingw32-windres\n"); 112 | fprintf(ofh, "PETOOL ?= petool\n"); 113 | fprintf(ofh, "NASM ?= nasm\n"); 114 | 115 | fprintf(ofh, "\n"); 116 | 117 | /* Make sure the DataDirectory VA is within a section (Heroes of Might and Magic 3 / LoadConfig) */ 118 | uint32_t code_start = UINT32_MAX; 119 | 120 | for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 121 | { 122 | const PIMAGE_SECTION_HEADER cur_sct = IMAGE_FIRST_SECTION(nt_hdr) + i; 123 | 124 | if (cur_sct->VirtualAddress && cur_sct->VirtualAddress < code_start) 125 | { 126 | code_start = cur_sct->VirtualAddress; 127 | } 128 | } 129 | 130 | if (code_start == UINT32_MAX) 131 | { 132 | code_start = nt_hdr->OptionalHeader.SectionAlignment; 133 | } 134 | 135 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 0 && 136 | nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress && 137 | nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress >= code_start) 138 | { 139 | fprintf( 140 | ofh, 141 | "EXPORTDIR = 0 0x%"PRIX32" %"PRIu32"\n", 142 | nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress, 143 | nt_hdr->OptionalHeader.DataDirectory[0].Size); 144 | } 145 | 146 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 3 && 147 | nt_hdr->OptionalHeader.DataDirectory[3].VirtualAddress && 148 | nt_hdr->OptionalHeader.DataDirectory[3].VirtualAddress >= code_start) 149 | { 150 | fprintf( 151 | ofh, 152 | "EXCEPTDIR = 3 0x%"PRIX32" %"PRIu32"\n", 153 | nt_hdr->OptionalHeader.DataDirectory[3].VirtualAddress, 154 | nt_hdr->OptionalHeader.DataDirectory[3].Size); 155 | } 156 | 157 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 7 && 158 | nt_hdr->OptionalHeader.DataDirectory[7].VirtualAddress && 159 | nt_hdr->OptionalHeader.DataDirectory[7].VirtualAddress >= code_start) 160 | { 161 | fprintf( 162 | ofh, 163 | "ARCHITECTUR = 7 0x%"PRIX32" %"PRIu32"\n", 164 | nt_hdr->OptionalHeader.DataDirectory[7].VirtualAddress, 165 | nt_hdr->OptionalHeader.DataDirectory[7].Size); 166 | } 167 | 168 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 8 && 169 | nt_hdr->OptionalHeader.DataDirectory[8].VirtualAddress && 170 | nt_hdr->OptionalHeader.DataDirectory[8].VirtualAddress >= code_start) 171 | { 172 | fprintf( 173 | ofh, 174 | "GLOBALPTR = 8 0x%"PRIX32" %"PRIu32"\n", 175 | nt_hdr->OptionalHeader.DataDirectory[8].VirtualAddress, 176 | nt_hdr->OptionalHeader.DataDirectory[8].Size); 177 | } 178 | 179 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 9 && 180 | nt_hdr->OptionalHeader.DataDirectory[9].VirtualAddress && 181 | nt_hdr->OptionalHeader.DataDirectory[9].VirtualAddress >= code_start) 182 | { 183 | fprintf( 184 | ofh, 185 | "TLSDIR = 9 0x%"PRIX32" %"PRIu32"\n", 186 | nt_hdr->OptionalHeader.DataDirectory[9].VirtualAddress, 187 | nt_hdr->OptionalHeader.DataDirectory[9].Size); 188 | } 189 | 190 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 10 && 191 | nt_hdr->OptionalHeader.DataDirectory[10].VirtualAddress && 192 | nt_hdr->OptionalHeader.DataDirectory[10].VirtualAddress >= code_start) 193 | { 194 | fprintf( 195 | ofh, 196 | "LOADCFGDIR = 10 0x%"PRIX32" %"PRIu32"\n", 197 | nt_hdr->OptionalHeader.DataDirectory[10].VirtualAddress, 198 | nt_hdr->OptionalHeader.DataDirectory[10].Size); 199 | } 200 | 201 | uint32_t iat_size = 0; 202 | 203 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 12 && 204 | nt_hdr->OptionalHeader.DataDirectory[12].VirtualAddress && 205 | nt_hdr->OptionalHeader.DataDirectory[12].Size && 206 | nt_hdr->OptionalHeader.DataDirectory[12].VirtualAddress >= code_start) 207 | { 208 | iat_size = nt_hdr->OptionalHeader.DataDirectory[12].Size; 209 | 210 | fprintf(ofh, "IAT = 12 0x%"PRIX32" %"PRIu32"\n", nt_hdr->OptionalHeader.DataDirectory[12].VirtualAddress, iat_size); 211 | } 212 | else if ( 213 | nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 1 && 214 | nt_hdr->OptionalHeader.DataDirectory[1].VirtualAddress && 215 | nt_hdr->OptionalHeader.DataDirectory[1].Size) 216 | { 217 | /* IAT must be set or PE loader will fail to initialize the imports when they're in a read-only section */ 218 | uint32_t offset = rva_to_offset(nt_hdr->OptionalHeader.DataDirectory[1].VirtualAddress, nt_hdr); 219 | IMAGE_IMPORT_DESCRIPTOR* i = (void*)(image + offset); 220 | 221 | uint32_t iat_start = UINT32_MAX; 222 | uint32_t iat_end = 0; 223 | 224 | while (i->FirstThunk) 225 | { 226 | iat_start = i->FirstThunk < iat_start ? i->FirstThunk : iat_start; 227 | iat_end = i->FirstThunk > iat_end ? i->FirstThunk : iat_end; 228 | i++; 229 | } 230 | 231 | if (iat_end) 232 | { 233 | PIMAGE_THUNK_DATA32 ft = 234 | (PIMAGE_THUNK_DATA32)(image + rva_to_offset(iat_end, nt_hdr)); 235 | 236 | while (ft->u1.Function) 237 | { 238 | ft++; 239 | iat_end += sizeof(IMAGE_THUNK_DATA32); 240 | } 241 | 242 | if (iat_start >= code_start) 243 | { 244 | iat_size = iat_end - iat_start + 4; 245 | 246 | fprintf(ofh, "IAT = 12 0x%"PRIX32" %"PRIu32"\n", iat_start, iat_size); 247 | } 248 | } 249 | } 250 | 251 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 13 && 252 | nt_hdr->OptionalHeader.DataDirectory[13].VirtualAddress && 253 | nt_hdr->OptionalHeader.DataDirectory[13].VirtualAddress >= code_start) 254 | { 255 | fprintf( 256 | ofh, 257 | "DIMPORTDESC = 13 0x%"PRIX32" %"PRIu32"\n", 258 | nt_hdr->OptionalHeader.DataDirectory[13].VirtualAddress, 259 | nt_hdr->OptionalHeader.DataDirectory[13].Size); 260 | } 261 | 262 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 14 && 263 | nt_hdr->OptionalHeader.DataDirectory[14].VirtualAddress && 264 | nt_hdr->OptionalHeader.DataDirectory[14].VirtualAddress >= code_start) 265 | { 266 | fprintf( 267 | ofh, 268 | "COMRUNDESC = 14 0x%"PRIX32" %"PRIu32"\n", 269 | nt_hdr->OptionalHeader.DataDirectory[14].VirtualAddress, 270 | nt_hdr->OptionalHeader.DataDirectory[14].Size); 271 | } 272 | 273 | fprintf(ofh, "\n"); 274 | 275 | fprintf(ofh, "all: $(OUTPUT)\n\n"); 276 | 277 | fprintf(ofh, "%%.o: %%.asm\n"); 278 | fprintf(ofh, " $(NASM) $(NFLAGS) -o $@ $<\n\n"); 279 | 280 | fprintf(ofh, "%%.o: %%.rc\n"); 281 | fprintf(ofh, " $(WINDRES) $(WFLAGS) $< $@\n\n"); 282 | 283 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 2 && nt_hdr->OptionalHeader.DataDirectory[2].VirtualAddress) 284 | { 285 | fprintf(ofh, "rsrc.o: $(INPUT)\n"); 286 | fprintf(ofh, " $(PETOOL) re2obj $< $@\n\n"); 287 | } 288 | 289 | fprintf(ofh, "$(OUTPUT): $(OBJS)\n"); 290 | fprintf(ofh, " $(CXX) $(LDFLAGS) -T $(LDS) -o \"$@\" $^ $(LIBS)\n"); 291 | 292 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 0 && 293 | nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress && 294 | nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress >= code_start) 295 | { 296 | fprintf(ofh, " $(PETOOL) setdd \"$@\" $(EXPORTDIR) || ($(RM) \"$@\" && exit 1)\n"); 297 | } 298 | 299 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 3 && 300 | nt_hdr->OptionalHeader.DataDirectory[3].VirtualAddress && 301 | nt_hdr->OptionalHeader.DataDirectory[3].VirtualAddress >= code_start) 302 | { 303 | fprintf(ofh, " $(PETOOL) setdd \"$@\" $(EXCEPTDIR) || ($(RM) \"$@\" && exit 1)\n"); 304 | } 305 | 306 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 7 && 307 | nt_hdr->OptionalHeader.DataDirectory[7].VirtualAddress && 308 | nt_hdr->OptionalHeader.DataDirectory[7].VirtualAddress >= code_start) 309 | { 310 | fprintf(ofh, " $(PETOOL) setdd \"$@\" $(ARCHITECTUR) || ($(RM) \"$@\" && exit 1)\n"); 311 | } 312 | 313 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 8 && 314 | nt_hdr->OptionalHeader.DataDirectory[8].VirtualAddress && 315 | nt_hdr->OptionalHeader.DataDirectory[8].VirtualAddress >= code_start) 316 | { 317 | fprintf(ofh, " $(PETOOL) setdd \"$@\" $(GLOBALPTR) || ($(RM) \"$@\" && exit 1)\n"); 318 | } 319 | 320 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 9 && 321 | nt_hdr->OptionalHeader.DataDirectory[9].VirtualAddress && 322 | nt_hdr->OptionalHeader.DataDirectory[9].VirtualAddress >= code_start) 323 | { 324 | fprintf(ofh, " $(PETOOL) setdd \"$@\" $(TLSDIR) || ($(RM) \"$@\" && exit 1)\n"); 325 | } 326 | 327 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 10 && 328 | nt_hdr->OptionalHeader.DataDirectory[10].VirtualAddress && 329 | nt_hdr->OptionalHeader.DataDirectory[10].VirtualAddress >= code_start) 330 | { 331 | fprintf(ofh, " $(PETOOL) setdd \"$@\" $(LOADCFGDIR) || ($(RM) \"$@\" && exit 1)\n"); 332 | } 333 | 334 | if (iat_size) 335 | { 336 | fprintf(ofh, " $(PETOOL) setdd \"$@\" $(IAT) || ($(RM) \"$@\" && exit 1)\n"); 337 | } 338 | 339 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 13 && 340 | nt_hdr->OptionalHeader.DataDirectory[13].VirtualAddress && 341 | nt_hdr->OptionalHeader.DataDirectory[13].VirtualAddress >= code_start) 342 | { 343 | fprintf(ofh, " $(PETOOL) setdd \"$@\" $(DIMPORTDESC) || ($(RM) \"$@\" && exit 1)\n"); 344 | } 345 | 346 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 14 && 347 | nt_hdr->OptionalHeader.DataDirectory[14].VirtualAddress && 348 | nt_hdr->OptionalHeader.DataDirectory[14].VirtualAddress >= code_start) 349 | { 350 | fprintf(ofh, " $(PETOOL) setdd \"$@\" $(COMRUNDESC) || ($(RM) \"$@\" && exit 1)\n"); 351 | } 352 | 353 | fprintf(ofh, " $(PETOOL) setsc \"$@\" .p_text 0x60000020 || ($(RM) \"$@\" && exit 1)\n"); 354 | fprintf(ofh, " $(PETOOL) patch \"$@\" || ($(RM) \"$@\" && exit 1)\n"); 355 | fprintf(ofh, " $(STRIP) -R .patch \"$@\" || ($(RM) \"$@\" && exit 1)\n"); 356 | fprintf(ofh, "# $(PETOOL) dump \"$(INPUT)\"\n"); 357 | fprintf(ofh, "# $(PETOOL) dump \"$@\"\n"); 358 | 359 | fprintf(ofh, "\n"); 360 | 361 | fprintf(ofh, "clean:\n"); 362 | fprintf(ofh, " $(RM) $(OUTPUT) $(OBJS)\n"); 363 | 364 | cleanup: 365 | if (argc > 2) 366 | { 367 | if (ofh) fclose(ofh); 368 | } 369 | 370 | if (image) free(image); 371 | return ret; 372 | } 373 | -------------------------------------------------------------------------------- /src/genpatch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #ifdef __linux__ 27 | #include 28 | #elif defined(__FreeBSD__ ) 29 | #include 30 | #elif !defined(_WIN32) 31 | #include 32 | #endif 33 | 34 | #ifdef _WIN32 35 | #include 36 | #define MAX_PATH 260 /* including windows.h would conflict with pe.h */ 37 | #else 38 | #include 39 | #include 40 | #define MAX_PATH PATH_MAX 41 | #define _mkdir(a) mkdir(a, 0777) 42 | #endif 43 | 44 | #include "pe.h" 45 | #include "cleanup.h" 46 | #include "common.h" 47 | 48 | int genpatch(int argc, char** argv) 49 | { 50 | // decleration before more meaningful initialization for cleanup 51 | int ret = EXIT_SUCCESS; 52 | uint8_t* image1 = NULL; 53 | uint8_t* image2 = NULL; 54 | FILE* ofh1 = NULL; 55 | FILE* ofh2 = NULL; 56 | uint32_t length1; 57 | uint32_t length2; 58 | 59 | FAIL_IF(argc < 3, "usage: petool genpatch [ofile1] [ofile2]\n"); 60 | FAIL_IF_SILENT(open_and_read(NULL, (int8_t**)&image1, &length1, argv[1], NULL)); 61 | FAIL_IF(!is_supported_pe_image((int8_t*)image1, length1), "File1 is not a valid i386 Portable Executable (PE) image.\n"); 62 | 63 | PIMAGE_DOS_HEADER dos_hdr1 = (void*)image1; 64 | PIMAGE_NT_HEADERS nt_hdr1 = (void*)(image1 + dos_hdr1->e_lfanew); 65 | 66 | FAIL_IF_SILENT(open_and_read(NULL, (int8_t**)&image2, &length2, argv[2], NULL)); 67 | FAIL_IF(!is_supported_pe_image((int8_t*)image2, length2), "File2 is not a valid i386 Portable Executable (PE) image.\n"); 68 | 69 | PIMAGE_DOS_HEADER dos_hdr2 = (void*)image2; 70 | PIMAGE_NT_HEADERS nt_hdr2 = (void*)(image2 + dos_hdr2->e_lfanew); 71 | 72 | if (argc > 4) 73 | { 74 | ofh1 = fopen(argv[3], "w"); 75 | FAIL_IF_PERROR(ofh1 == NULL, "%s"); 76 | 77 | ofh2 = fopen(argv[4], "w"); 78 | FAIL_IF_PERROR(ofh2 == NULL, "%s"); 79 | } 80 | else 81 | { 82 | static char path[MAX_PATH]; 83 | FAIL_IF(snprintf(path, sizeof(path) - 1, "%s-patch.cpp", file_basename(argv[1])) < 0, "Fail - Path1 truncated\n"); 84 | 85 | ofh1 = fopen(path, "w"); 86 | FAIL_IF_PERROR(ofh1 == NULL, "%s"); 87 | 88 | FAIL_IF(snprintf(path, sizeof(path) - 1, "%s-patch.cpp", file_basename(argv[2])) < 0, "Fail - Path2 truncated\n"); 89 | 90 | ofh2 = fopen(path, "w"); 91 | FAIL_IF_PERROR(ofh2 == NULL, "%s"); 92 | } 93 | 94 | uint32_t dos_hdr_diff = 0; 95 | uint32_t file_hdr_diff = 0; 96 | uint32_t opt_hdr_diff = 0; 97 | uint32_t sct_hdr_diff = 0; 98 | uint32_t unknown_hdr_diff = 0; 99 | uint32_t unknown_diff = 0; 100 | uint32_t last_section = 0; 101 | 102 | fprintf(ofh1, "#include \"macros/patch.h\"\n\n"); 103 | fprintf(ofh1, "//%s -> %s\n\n", argv[1], argv[2]); 104 | 105 | fprintf(ofh2, "#include \"macros/patch.h\"\n\n"); 106 | fprintf(ofh2, "//%s -> %s\n\n", argv[2], argv[1]); 107 | 108 | for (uint32_t i = 0, len = 0; i <= length1 && i <= length2; i++) 109 | { 110 | uint32_t section = section_from_offset(i, nt_hdr1, NULL, 0); 111 | 112 | bool section_end_reached = len != 0 && last_section != section; 113 | 114 | if (i < length1 && i < length2 && image1[i] != image2[i] && !section_end_reached) 115 | { 116 | if (len == 0) 117 | { 118 | if (!section) 119 | { 120 | // Not within a section (headers/debuginfo/cert etc...) - ignored 121 | 122 | uint32_t file_hdr = dos_hdr1->e_lfanew + 4; 123 | uint32_t opt_hdr = file_hdr + sizeof(IMAGE_FILE_HEADER); 124 | uint32_t sct_hdr = opt_hdr + nt_hdr1->FileHeader.SizeOfOptionalHeader; 125 | uint32_t sct_hdr_size = nt_hdr1->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); 126 | 127 | if (i < sizeof(IMAGE_DOS_HEADER)) 128 | { 129 | dos_hdr_diff++; 130 | } 131 | else if (i >= file_hdr && i < opt_hdr) 132 | { 133 | file_hdr_diff++; 134 | } 135 | else if (i >= opt_hdr && i < sct_hdr) 136 | { 137 | opt_hdr_diff++; 138 | } 139 | else if (i >= sct_hdr && i < sct_hdr + sct_hdr_size) 140 | { 141 | sct_hdr_diff++; 142 | } 143 | else if (i < nt_hdr1->OptionalHeader.SizeOfHeaders) 144 | { 145 | unknown_hdr_diff++; 146 | } 147 | else 148 | { 149 | unknown_diff++; 150 | } 151 | 152 | continue; 153 | } 154 | 155 | last_section = section; 156 | } 157 | 158 | len++; 159 | } 160 | else if (len != 0) 161 | { 162 | char section_name[12] = { 0 }; 163 | section_from_offset(i - len, nt_hdr1, section_name, sizeof(section_name)); 164 | 165 | fprintf(ofh1, "SETBYTES(0x%08X, \"", nt_hdr1->OptionalHeader.ImageBase + offset_to_rva(i - len, nt_hdr1)); 166 | for (uint32_t x = 0; x < len; x++) 167 | { 168 | fprintf(ofh1, "\\x%02X", image2[i - (len - x)]); 169 | } 170 | fprintf(ofh1, "\"); //%08X (%s)\n", i - len, section_name); 171 | 172 | fprintf(ofh2, "SETBYTES(0x%08X, \"", nt_hdr2->OptionalHeader.ImageBase + offset_to_rva(i - len, nt_hdr2)); 173 | for (uint32_t x = 0; x < len; x++) 174 | { 175 | fprintf(ofh2, "\\x%02X", image1[i - (len - x)]); 176 | } 177 | fprintf(ofh2, "\"); //%08X (%s)\n", i - len, section_name); 178 | 179 | len = 0; 180 | 181 | if (section_end_reached) 182 | i--; 183 | } 184 | } 185 | 186 | if (length1 != length2) 187 | { 188 | char s[] = "\n//WARNING: file1 size does not match file2 size\n"; 189 | fprintf(ofh1, "%s", s); 190 | fprintf(ofh2, "%s", s); 191 | } 192 | 193 | if (dos_hdr_diff > 0) 194 | { 195 | char s[] = "\n//WARNING: %u bytes changed in IMAGE_DOS_HEADER\n"; 196 | fprintf(ofh1, s, dos_hdr_diff); 197 | fprintf(ofh2, s, dos_hdr_diff); 198 | } 199 | 200 | if (file_hdr_diff > 0) 201 | { 202 | char s[] = "\n//WARNING: %u bytes changed in IMAGE_FILE_HEADER\n"; 203 | fprintf(ofh1, s, file_hdr_diff); 204 | fprintf(ofh2, s, file_hdr_diff); 205 | } 206 | 207 | if (opt_hdr_diff > 0) 208 | { 209 | char s[] = "\n//WARNING: %u bytes changed in IMAGE_OPTIONAL_HEADER\n"; 210 | fprintf(ofh1, s, opt_hdr_diff); 211 | fprintf(ofh2, s, opt_hdr_diff); 212 | } 213 | 214 | if (sct_hdr_diff > 0) 215 | { 216 | char s[] = "\n//WARNING: %u bytes changed in IMAGE_SECTION_HEADER[]\n"; 217 | fprintf(ofh1, s, sct_hdr_diff); 218 | fprintf(ofh2, s, sct_hdr_diff); 219 | } 220 | 221 | if (unknown_hdr_diff > 0) 222 | { 223 | char s[] = "\n//WARNING: %u unknown bytes changed in region OptionalHeader.SizeOfHeaders\n"; 224 | fprintf(ofh1, s, unknown_hdr_diff); 225 | fprintf(ofh2, s, unknown_hdr_diff); 226 | } 227 | 228 | if (unknown_diff > 0) 229 | { 230 | char s[] = "\n//WARNING: %u unknown bytes changed (data not within any section or headers)\n"; 231 | fprintf(ofh1, s, unknown_diff); 232 | fprintf(ofh2, s, unknown_diff); 233 | } 234 | 235 | cleanup: 236 | if (image1) free(image1); 237 | if (image2) free(image2); 238 | if (ofh1) fclose(ofh1); 239 | if (ofh2) fclose(ofh2); 240 | return ret; 241 | } 242 | -------------------------------------------------------------------------------- /src/genprj.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #ifdef __linux__ 24 | #include 25 | #elif defined(__FreeBSD__ ) 26 | #include 27 | #elif !defined(_WIN32) 28 | #include 29 | #endif 30 | 31 | #ifdef _WIN32 32 | #include 33 | #define MAX_PATH 260 /* including windows.h would conflict with pe.h */ 34 | #else 35 | #include 36 | #include 37 | #define MAX_PATH PATH_MAX 38 | #define _mkdir(a) mkdir(a, 0777) 39 | #endif 40 | 41 | #include "pe.h" 42 | #include "cleanup.h" 43 | #include "common.h" 44 | 45 | int genlds(int argc, char **argv); 46 | int genmak(int argc, char **argv); 47 | int gensym(int argc, char** argv); 48 | int genfiles(char* dir); 49 | 50 | int genprj(int argc, char **argv) 51 | { 52 | int ret = EXIT_SUCCESS; 53 | int8_t* image = NULL; 54 | uint32_t length; 55 | static char base[MAX_PATH]; 56 | static char buf[MAX_PATH]; 57 | static char dir[MAX_PATH]; 58 | char *cmd_argv[3] = { argv[0], argv[1], buf }; 59 | 60 | FAIL_IF(argc < 2, "usage: petool genprj [directory]\n"); 61 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 62 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 63 | 64 | strncpy(base, file_basename_no_ext(argv[1]), sizeof(base) - 1); 65 | 66 | if (argc > 2) 67 | { 68 | memset(dir, 0, sizeof dir); 69 | strncpy(dir, argv[2], sizeof dir - 1); 70 | } 71 | else 72 | { 73 | snprintf(dir, sizeof dir, "%s", base); 74 | } 75 | 76 | printf("Input file : %s\n", argv[1]); 77 | printf("Output directory: %s\n", dir); 78 | 79 | FAIL_IF_PERROR(_mkdir(dir) == -1, "Failed to create output directory"); 80 | 81 | FAIL_IF(snprintf(buf, sizeof buf, "%s/%s.dat", dir, base) < 0, "Failed to copy game executable - Path truncated\n"); 82 | printf("Copying %s -> %s...\n", argv[1], buf); 83 | FAIL_IF(file_copy(argv[1], buf) != EXIT_SUCCESS, "Failed to copy file\n"); 84 | 85 | FAIL_IF(snprintf(buf, sizeof buf, "%s/%s.lds", dir, base) < 0, "Failed to create linker script - Path truncated\n"); 86 | printf("Generating %s...\n", buf); 87 | FAIL_IF(genlds(3, cmd_argv) != EXIT_SUCCESS, "Failed to create linker script\n"); 88 | 89 | FAIL_IF(snprintf(buf, sizeof buf, "%s/sym.cpp", dir) < 0, "Failed to create sym.cpp - Path truncated\n"); 90 | printf("Generating %s...\n", buf); 91 | FAIL_IF(gensym(3, cmd_argv) != EXIT_SUCCESS, "Failed to create sym.cpp\n"); 92 | 93 | FAIL_IF(snprintf(buf, sizeof buf, "%s/Makefile", dir) < 0, "Failed to create makefile - Path truncated\n"); 94 | printf("Generating %s...\n", buf); 95 | FAIL_IF(genmak(3, cmd_argv) != EXIT_SUCCESS, "Failed to create Makefile\n"); 96 | 97 | printf("Extracting optional files and examples...\n"); 98 | FAIL_IF(genfiles(dir) != EXIT_SUCCESS, "Failed to extract optional files and examples\n"); 99 | 100 | cleanup: 101 | if (image) free(image); 102 | return ret; 103 | } 104 | -------------------------------------------------------------------------------- /src/genproxy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #ifdef __linux__ 24 | #include 25 | #elif defined(__FreeBSD__ ) 26 | #include 27 | #elif !defined(_WIN32) 28 | #include 29 | #endif 30 | 31 | #ifdef _WIN32 32 | #include 33 | #define MAX_PATH 260 /* including windows.h would conflict with pe.h */ 34 | #else 35 | #include 36 | #include 37 | #define MAX_PATH PATH_MAX 38 | #define _mkdir(a) mkdir(a, 0777) 39 | #endif 40 | 41 | #include "pe.h" 42 | #include "cleanup.h" 43 | #include "common.h" 44 | 45 | int genproxy_def(int argc, char** argv, bool forward); 46 | int genproxy_exports(int argc, char** argv); 47 | int genproxy_dllmain(int argc, char** argv, bool forward); 48 | int genproxy_make(int argc, char** argv, bool forward); 49 | 50 | extern const char res_proxy_readme_txt[]; 51 | extern const char res_proxy_dllmain_cpp[]; 52 | extern const char res_proxy_res_rc[]; 53 | extern const char res_proxy_vcxproj[]; 54 | extern const char res_build_cmd[]; 55 | extern const char res_inc_patch_h[]; 56 | 57 | int genproxy(int argc, char **argv) 58 | { 59 | int ret = EXIT_SUCCESS; 60 | int8_t* image = NULL; 61 | uint32_t length; 62 | static char base[MAX_PATH]; 63 | static char buf[MAX_PATH]; 64 | static char dir[MAX_PATH]; 65 | static char subdir[MAX_PATH]; 66 | char *cmd_argv[3] = { argv[0], argv[1], buf }; 67 | 68 | FAIL_IF(argc < 2, "usage: petool genproxy [directory]\n"); 69 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 70 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 71 | 72 | PIMAGE_DOS_HEADER dos_hdr = (void*)image; 73 | PIMAGE_NT_HEADERS nt_hdr = (void*)(image + dos_hdr->e_lfanew); 74 | 75 | FAIL_IF(nt_hdr->OptionalHeader.NumberOfRvaAndSizes < 1, "Not enough DataDirectories.\n"); 76 | FAIL_IF(!nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress, "No export directory in dll\n"); 77 | 78 | strncpy(base, file_basename_no_ext(argv[1]), sizeof(base) - 1); 79 | 80 | if (argc > 2) 81 | { 82 | memset(dir, 0, sizeof dir); 83 | strncpy(dir, argv[2], sizeof dir - 1); 84 | } 85 | else 86 | { 87 | FAIL_IF(snprintf(dir, sizeof dir, "%s-proxy", base) < 0, "Failed to create output directory - Path truncated\n") 88 | } 89 | 90 | printf("Input file : %s\n", argv[1]); 91 | printf("Output directory: %s\n", dir); 92 | 93 | FAIL_IF_PERROR(_mkdir(dir) == -1, "Failed to create output directory"); 94 | 95 | FAIL_IF(snprintf(buf, sizeof buf, "%s/readme.txt", dir) < 0, "Failed to create readme.txt - Path truncated\n"); 96 | printf("Generating %s...\n", buf); 97 | FAIL_IF(extract_resource(res_proxy_readme_txt, buf) != EXIT_SUCCESS, "Failed to create readme.txt\n"); 98 | 99 | 100 | FAIL_IF(snprintf(subdir, sizeof subdir, "%s/system", dir) < 0, "Failed to create output subdirectory - Path truncated\n"); 101 | FAIL_IF_PERROR(_mkdir(subdir) == -1, "Failed to create output subdirectory"); 102 | 103 | FAIL_IF(snprintf(buf, sizeof buf, "%s/exports.def", subdir) < 0, "Failed to create exports.def - Path truncated\n"); 104 | printf("Generating %s...\n", buf); 105 | FAIL_IF(genproxy_def(3, cmd_argv, false) != EXIT_SUCCESS, "Failed to create exports.def\n"); 106 | 107 | FAIL_IF(snprintf(buf, sizeof buf, "%s/dllmain.cpp", subdir) < 0, "Failed to create dllmain.cpp - Path truncated\n"); 108 | printf("Generating %s...\n", buf); 109 | FAIL_IF(genproxy_dllmain(3, cmd_argv, false) != EXIT_SUCCESS, "Failed to create dllmain.cpp\n"); 110 | 111 | FAIL_IF(snprintf(buf, sizeof buf, "%s/exports.cpp", subdir) < 0, "Failed to create exports.cpp - Path truncated\n"); 112 | printf("Generating %s...\n", buf); 113 | FAIL_IF(genproxy_exports(3, cmd_argv) != EXIT_SUCCESS, "Failed to create exports.cpp\n"); 114 | 115 | FAIL_IF(snprintf(buf, sizeof buf, "%s/Makefile", subdir) < 0, "Failed to create makefile - Path truncated\n"); 116 | printf("Generating %s...\n", buf); 117 | FAIL_IF(genproxy_make(3, cmd_argv, false) != EXIT_SUCCESS, "Failed to create Makefile\n"); 118 | 119 | FAIL_IF(snprintf(buf, sizeof buf, "%s/build.cmd", subdir) < 0, "Failed to create build.cmd - Path truncated\n"); 120 | printf("Generating %s...\n", buf); 121 | FAIL_IF(extract_resource(res_build_cmd, buf) != EXIT_SUCCESS, "Failed to create build.cmd\n"); 122 | 123 | FAIL_IF(snprintf(buf, sizeof buf, "%s/patch.h", subdir) < 0, "Failed to create patch.h - Path truncated\n"); 124 | printf("Generating %s...\n", buf); 125 | FAIL_IF(extract_resource(res_inc_patch_h, buf) != EXIT_SUCCESS, "Failed to create patch.h\n"); 126 | 127 | FAIL_IF(snprintf(buf, sizeof buf, "%s/%s.vcxproj", subdir, base) < 0, "Failed to create .vcxproj - Path truncated\n"); 128 | printf("Generating %s...\n", buf); 129 | FAIL_IF(extract_resource(res_proxy_vcxproj, buf) != EXIT_SUCCESS, "Failed to create .vcxproj\n"); 130 | 131 | FAIL_IF(snprintf(buf, sizeof buf, "%s/res.rc", subdir) < 0, "Failed to create res.rc - Path truncated\n"); 132 | printf("Generating %s...\n", buf); 133 | FAIL_IF(extract_resource(res_proxy_res_rc, buf) != EXIT_SUCCESS, "Failed to create res.rc\n"); 134 | 135 | 136 | FAIL_IF(snprintf(subdir, sizeof subdir, "%s/local", dir) < 0, "Failed to create output subdirectory - Path truncated\n"); 137 | FAIL_IF_PERROR(_mkdir(subdir) == -1, "Failed to create output subdirectory"); 138 | 139 | FAIL_IF(snprintf(buf, sizeof buf, "%s/exports.def", subdir) < 0, "Failed to create exports.def - Path truncated\n"); 140 | printf("Generating %s...\n", buf); 141 | FAIL_IF(genproxy_def(3, cmd_argv, true) != EXIT_SUCCESS, "Failed to create exports.def\n"); 142 | 143 | FAIL_IF(snprintf(buf, sizeof buf, "%s/dllmain.cpp", subdir) < 0, "Failed to create dllmain.cpp - Path truncated\n"); 144 | printf("Generating %s...\n", buf); 145 | FAIL_IF(genproxy_dllmain(3, cmd_argv, true) != EXIT_SUCCESS, "Failed to create dllmain.cpp\n"); 146 | 147 | FAIL_IF(snprintf(buf, sizeof buf, "%s/Makefile", subdir) < 0, "Failed to create makefile - Path truncated\n"); 148 | printf("Generating %s...\n", buf); 149 | FAIL_IF(genproxy_make(3, cmd_argv, true) != EXIT_SUCCESS, "Failed to create Makefile\n"); 150 | 151 | FAIL_IF(snprintf(buf, sizeof buf, "%s/build.cmd", subdir) < 0, "Failed to create build.cmd - Path truncated\n"); 152 | printf("Generating %s...\n", buf); 153 | FAIL_IF(extract_resource(res_build_cmd, buf) != EXIT_SUCCESS, "Failed to create build.cmd\n"); 154 | 155 | FAIL_IF(snprintf(buf, sizeof buf, "%s/patch.h", subdir) < 0, "Failed to create patch.h - Path truncated\n"); 156 | printf("Generating %s...\n", buf); 157 | FAIL_IF(extract_resource(res_inc_patch_h, buf) != EXIT_SUCCESS, "Failed to create patch.h\n"); 158 | 159 | FAIL_IF(snprintf(buf, sizeof buf, "%s/%s.vcxproj", subdir, base) < 0, "Failed to create .vcxproj - Path truncated\n"); 160 | printf("Generating %s...\n", buf); 161 | FAIL_IF(extract_resource(res_proxy_vcxproj, buf) != EXIT_SUCCESS, "Failed to create .vcxproj\n"); 162 | 163 | FAIL_IF(snprintf(buf, sizeof buf, "%s/res.rc", subdir) < 0, "Failed to create res.rc - Path truncated\n"); 164 | printf("Generating %s...\n", buf); 165 | FAIL_IF(extract_resource(res_proxy_res_rc, buf) != EXIT_SUCCESS, "Failed to create res.rc\n"); 166 | 167 | FAIL_IF(snprintf(buf, sizeof buf, "%s/%sx.dll", subdir, base) < 0, "Failed to copy original dll - Path truncated\n"); 168 | printf("Copying %s -> %s...\n", argv[1], buf); 169 | FAIL_IF(file_copy(argv[1], buf) != EXIT_SUCCESS, "Failed to copy original dll\n"); 170 | 171 | cleanup: 172 | if (image) free(image); 173 | return ret; 174 | } 175 | 176 | int genproxy_def(int argc, char** argv, bool forward) 177 | { 178 | // decleration before more meaningful initialization for cleanup 179 | int ret = EXIT_SUCCESS; 180 | int8_t* image = NULL; 181 | FILE* ofh = NULL; 182 | uint32_t length; 183 | static char base[MAX_PATH]; 184 | 185 | FAIL_IF(argc < 3, "usage: genproxy_exports [ofile]\n"); 186 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 187 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 188 | 189 | FAIL_IF(file_exists(argv[2]), "%s: output file already exists.\n", argv[2]); 190 | ofh = fopen(argv[2], "w"); 191 | FAIL_IF_PERROR(ofh == NULL, "%s"); 192 | 193 | PIMAGE_DOS_HEADER dos_hdr = (void*)image; 194 | PIMAGE_NT_HEADERS nt_hdr = (void*)(image + dos_hdr->e_lfanew); 195 | 196 | FAIL_IF(nt_hdr->OptionalHeader.NumberOfRvaAndSizes < 1, "Not enough DataDirectories.\n"); 197 | FAIL_IF(!nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress, "No export directory in dll\n"); 198 | 199 | uint32_t offset = rva_to_offset(nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress, nt_hdr); 200 | IMAGE_EXPORT_DIRECTORY* export_dir = (void*)(image + offset); 201 | 202 | fprintf(ofh, "LIBRARY %s\n", file_basename(argv[1])); 203 | fprintf(ofh, "\n"); 204 | fprintf(ofh, "EXPORTS\n"); 205 | 206 | strncpy(base, file_basename_no_ext(argv[1]), sizeof(base) - 1); 207 | 208 | uint32_t* names = (uint32_t*)(image + rva_to_offset(export_dir->AddressOfNames, nt_hdr)); 209 | uint16_t* ordinals = (uint16_t*)(image + rva_to_offset(export_dir->AddressOfNameOrdinals, nt_hdr)); 210 | 211 | for (uint32_t i = 0; i < export_dir->NumberOfFunctions; i++) 212 | { 213 | char* name = NULL; 214 | 215 | for (uint32_t x = 0; x < export_dir->NumberOfNames; x++) 216 | { 217 | if (ordinals[x] == i) 218 | { 219 | name = (char*)(image + rva_to_offset(names[x], nt_hdr)); 220 | break; 221 | } 222 | } 223 | 224 | if (name) 225 | { 226 | bool is_private = 227 | strcmp(name, "DllCanUnloadNow") == 0 || 228 | strcmp(name, "DllGetClassObject") == 0 || 229 | strcmp(name, "DllGetClassFactoryFromClassString") == 0 || 230 | strcmp(name, "DllGetDocumentation") == 0 || 231 | strcmp(name, "DllInitialize") == 0 || 232 | strcmp(name, "DllInstall") == 0 || 233 | strcmp(name, "DllRegisterServer") == 0 || 234 | strcmp(name, "DllRegisterServerEx") == 0 || 235 | strcmp(name, "DllRegisterServerExW") == 0 || 236 | strcmp(name, "DllUnload") == 0 || 237 | strcmp(name, "DllUnregisterServer") == 0 || 238 | strcmp(name, "RasCustomDeleteEntryNotify") == 0 || 239 | strcmp(name, "RasCustomDial") == 0 || 240 | strcmp(name, "RasCustomDialDlg") == 0 || 241 | strcmp(name, "RasCustomEntryDlg") == 0; 242 | 243 | if (is_private) 244 | { 245 | if (forward) 246 | { 247 | fprintf(ofh, " %-40s = %sx.%-40s PRIVATE\n", name, base, name); 248 | } 249 | else 250 | { 251 | fprintf(ofh, " %-40s = __export_%-3u PRIVATE\n", name, i); 252 | } 253 | } 254 | else 255 | { 256 | if (forward) 257 | { 258 | fprintf(ofh, " %-40s = %sx.%-40s @%u\n", name, base, name, export_dir->Base + i); 259 | } 260 | else 261 | { 262 | fprintf(ofh, " %-40s = __export_%-3u @%u\n", name, i, export_dir->Base + i); 263 | } 264 | } 265 | } 266 | else 267 | { 268 | if (forward) 269 | { 270 | // forwaring to ordinal is not supported by GNU ld right now 271 | //fprintf(ofh, " %-40s = %sx.#%-40u @%u NONAME\n", name, base, export_dir->Base + i, export_dir->Base + i); 272 | } 273 | else 274 | { 275 | fprintf(ofh, " __export_%-31u = __export_%-3u @%u NONAME\n", i, i, export_dir->Base + i); 276 | } 277 | } 278 | } 279 | 280 | cleanup: 281 | if (image) free(image); 282 | if (ofh) fclose(ofh); 283 | return ret; 284 | } 285 | 286 | int genproxy_exports(int argc, char** argv) 287 | { 288 | // decleration before more meaningful initialization for cleanup 289 | int ret = EXIT_SUCCESS; 290 | int8_t* image = NULL; 291 | FILE* ofh = NULL; 292 | uint32_t length; 293 | 294 | FAIL_IF(argc < 3, "usage: genproxy_exports [ofile]\n"); 295 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 296 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 297 | 298 | FAIL_IF(file_exists(argv[2]), "%s: output file already exists.\n", argv[2]); 299 | ofh = fopen(argv[2], "w"); 300 | FAIL_IF_PERROR(ofh == NULL, "%s"); 301 | 302 | PIMAGE_DOS_HEADER dos_hdr = (void*)image; 303 | PIMAGE_NT_HEADERS nt_hdr = (void*)(image + dos_hdr->e_lfanew); 304 | 305 | FAIL_IF(nt_hdr->OptionalHeader.NumberOfRvaAndSizes < 1, "Not enough DataDirectories.\n"); 306 | FAIL_IF(!nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress, "No export directory in dll\n"); 307 | 308 | uint32_t offset = rva_to_offset(nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress, nt_hdr); 309 | IMAGE_EXPORT_DIRECTORY* export_dir = (void*)(image + offset); 310 | 311 | fprintf(ofh, "// petool genproxy - https://github.com/FunkyFr3sh/petool\n"); 312 | fprintf(ofh, "\n"); 313 | fprintf(ofh, "#include \n"); 314 | fprintf(ofh, "\n"); 315 | fprintf(ofh, "FARPROC g_exports[%u];\n", export_dir->NumberOfFunctions); 316 | fprintf(ofh, "static INIT_ONCE g_exports_init_once;\n"); 317 | fprintf(ofh, "\n"); 318 | fprintf(ofh, "static void exports_init()\n"); 319 | fprintf(ofh, "{\n"); 320 | fprintf(ofh, " WCHAR path[MAX_PATH];\n"); 321 | fprintf(ofh, " if (!GetSystemDirectoryW(path, _countof(path)))\n"); 322 | fprintf(ofh, " return;\n"); 323 | fprintf(ofh, "\n"); 324 | fprintf(ofh, " if (wcscat_s(path, L\"\\\\%s\") != 0)\n", file_basename(argv[1])); 325 | fprintf(ofh, " return;\n"); 326 | fprintf(ofh, "\n"); 327 | fprintf(ofh, " HMODULE dll = LoadLibraryW(path);\n"); 328 | fprintf(ofh, " if (!dll)\n"); 329 | fprintf(ofh, " return;\n"); 330 | fprintf(ofh, "\n"); 331 | 332 | uint32_t* names = (uint32_t*)(image + rva_to_offset(export_dir->AddressOfNames, nt_hdr)); 333 | uint16_t* ordinals = (uint16_t*)(image + rva_to_offset(export_dir->AddressOfNameOrdinals, nt_hdr)); 334 | uint32_t base = export_dir->Base; 335 | 336 | for (uint32_t i = 0; i < export_dir->NumberOfFunctions; i++) 337 | { 338 | char* name = NULL; 339 | 340 | for (uint32_t x = 0; x < export_dir->NumberOfNames; x++) 341 | { 342 | if (ordinals[x] == i) 343 | { 344 | name = (char*)(image + rva_to_offset(names[x], nt_hdr)); 345 | break; 346 | } 347 | } 348 | 349 | int align = snprintf(0, 0, "%u", i); 350 | align = 3 - (align > 3 ? 3 : align); 351 | 352 | if (name) 353 | { 354 | fprintf(ofh, " g_exports[%u]%*s = GetProcAddress(dll, \"%s\");\n", i, align, "", name); 355 | } 356 | else 357 | { 358 | fprintf(ofh, " g_exports[%u]%*s = GetProcAddress(dll, MAKEINTRESOURCEA(%u));\n", i, align, "", base + i); 359 | } 360 | } 361 | 362 | fprintf(ofh, "}\n"); 363 | fprintf(ofh, "\n"); 364 | fprintf(ofh, "#if defined(_MSC_VER)\n"); 365 | fprintf(ofh, "#define ASM_JMP(a) __asm { jmp g_exports[a*4] }\n"); 366 | fprintf(ofh, "#define NAKED __declspec(naked)\n"); 367 | fprintf(ofh, "#define NOINLINE __declspec(noinline)\n"); 368 | fprintf(ofh, "#else\n"); 369 | fprintf(ofh, "#define ASM_JMP(a) __asm(\"jmp _g_exports[\" #a \"*4]\")\n"); 370 | fprintf(ofh, "#define NAKED __attribute__((naked))\n"); 371 | fprintf(ofh, "#define NOINLINE __attribute__((noinline))\n"); 372 | fprintf(ofh, "#endif\n"); 373 | fprintf(ofh, "\n"); 374 | fprintf(ofh, "static NOINLINE void exports_init_once()\n"); 375 | fprintf(ofh, "{\n"); 376 | fprintf(ofh, " BOOL pending;\n"); 377 | fprintf(ofh, " if (InitOnceBeginInitialize(&g_exports_init_once, 0, &pending, NULL) && pending)\n"); 378 | fprintf(ofh, " {\n"); 379 | fprintf(ofh, " exports_init();\n"); 380 | fprintf(ofh, " InitOnceComplete(&g_exports_init_once, 0, NULL);\n"); 381 | fprintf(ofh, " }\n"); 382 | fprintf(ofh, "}\n"); 383 | fprintf(ofh, "\n"); 384 | fprintf(ofh, "#define CREATE_EXPORT(a) \\\n"); 385 | fprintf(ofh, " EXTERN_C NAKED void __export_##a() { \\\n"); 386 | fprintf(ofh, " exports_init_once(); \\\n"); 387 | fprintf(ofh, " ASM_JMP(a); \\\n"); 388 | fprintf(ofh, " }\n"); 389 | fprintf(ofh, "\n"); 390 | 391 | for (uint32_t i = 0; i < export_dir->NumberOfFunctions; i++) 392 | { 393 | fprintf(ofh, "CREATE_EXPORT(%u)\n", i); 394 | } 395 | 396 | cleanup: 397 | if (image) free(image); 398 | if (ofh) fclose(ofh); 399 | return ret; 400 | } 401 | 402 | int genproxy_dllmain(int argc, char** argv, bool forward) 403 | { 404 | // decleration before more meaningful initialization for cleanup 405 | int ret = EXIT_SUCCESS; 406 | int8_t* image = NULL; 407 | FILE* ofh = NULL; 408 | uint32_t length; 409 | static char base[MAX_PATH]; 410 | 411 | FAIL_IF(argc < 3, "usage: genproxy_dllmain [ofile]\n"); 412 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 413 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 414 | 415 | FAIL_IF(file_exists(argv[2]), "%s: output file already exists.\n", argv[2]); 416 | ofh = fopen(argv[2], "w"); 417 | FAIL_IF_PERROR(ofh == NULL, "%s"); 418 | 419 | PIMAGE_DOS_HEADER dos_hdr = (void*)image; 420 | PIMAGE_NT_HEADERS nt_hdr = (void*)(image + dos_hdr->e_lfanew); 421 | 422 | FAIL_IF(nt_hdr->OptionalHeader.NumberOfRvaAndSizes < 1, "Not enough DataDirectories.\n"); 423 | FAIL_IF(!nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress, "No export directory in dll\n"); 424 | 425 | uint32_t offset = rva_to_offset(nt_hdr->OptionalHeader.DataDirectory[0].VirtualAddress, nt_hdr); 426 | IMAGE_EXPORT_DIRECTORY* export_dir = (void*)(image + offset); 427 | 428 | fprintf(ofh, "#include \n"); 429 | fprintf(ofh, "#include \"patch.h\"\n"); 430 | fprintf(ofh, "\n"); 431 | fprintf(ofh, "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)\n"); 432 | fprintf(ofh, "{\n"); 433 | fprintf(ofh, " switch(fdwReason)\n"); 434 | fprintf(ofh, " {\n"); 435 | fprintf(ofh, " case DLL_PROCESS_ATTACH:\n"); 436 | fprintf(ofh, " break;\n"); 437 | fprintf(ofh, " case DLL_PROCESS_DETACH:\n"); 438 | fprintf(ofh, " break;\n"); 439 | fprintf(ofh, " }\n"); 440 | fprintf(ofh, " return TRUE;\n"); 441 | fprintf(ofh, "}\n"); 442 | 443 | if (!forward) 444 | { 445 | goto cleanup; 446 | } 447 | 448 | fprintf(ofh, "\n"); 449 | fprintf(ofh, "// petool genproxy - https://github.com/FunkyFr3sh/petool\n"); 450 | fprintf(ofh, "#if defined(_MSC_VER)\n"); 451 | 452 | strncpy(base, file_basename_no_ext(argv[1]), sizeof(base) - 1); 453 | 454 | uint32_t* names = (uint32_t*)(image + rva_to_offset(export_dir->AddressOfNames, nt_hdr)); 455 | uint16_t* ordinals = (uint16_t*)(image + rva_to_offset(export_dir->AddressOfNameOrdinals, nt_hdr)); 456 | 457 | for (uint32_t i = 0; i < export_dir->NumberOfFunctions; i++) 458 | { 459 | char* name = NULL; 460 | 461 | for (uint32_t x = 0; x < export_dir->NumberOfNames; x++) 462 | { 463 | if (ordinals[x] == i) 464 | { 465 | name = (char*)(image + rva_to_offset(names[x], nt_hdr)); 466 | break; 467 | } 468 | } 469 | 470 | if (name) 471 | { 472 | bool is_private = 473 | strcmp(name, "DllCanUnloadNow") == 0 || 474 | strcmp(name, "DllGetClassObject") == 0 || 475 | strcmp(name, "DllGetClassFactoryFromClassString") == 0 || 476 | strcmp(name, "DllGetDocumentation") == 0 || 477 | strcmp(name, "DllInitialize") == 0 || 478 | strcmp(name, "DllInstall") == 0 || 479 | strcmp(name, "DllRegisterServer") == 0 || 480 | strcmp(name, "DllRegisterServerEx") == 0 || 481 | strcmp(name, "DllRegisterServerExW") == 0 || 482 | strcmp(name, "DllUnload") == 0 || 483 | strcmp(name, "DllUnregisterServer") == 0 || 484 | strcmp(name, "RasCustomDeleteEntryNotify") == 0 || 485 | strcmp(name, "RasCustomDial") == 0 || 486 | strcmp(name, "RasCustomDialDlg") == 0 || 487 | strcmp(name, "RasCustomEntryDlg") == 0; 488 | 489 | if (is_private) 490 | { 491 | fprintf(ofh, "#pragma comment(linker, \"/export:%s=%sx.%s,PRIVATE\")\n", name, base, name); 492 | } 493 | else 494 | { 495 | fprintf(ofh, "#pragma comment(linker, \"/export:%s=%sx.%s,@%u\")\n", name, base, name, export_dir->Base + i); 496 | } 497 | } 498 | else 499 | { 500 | uint32_t ord = export_dir->Base + i; 501 | fprintf(ofh, "#pragma comment(linker, \"/export:__export_%u=%sx.#%u,@%u,NONAME\")\n", i, base, ord, ord); 502 | } 503 | } 504 | 505 | fprintf(ofh, "#endif\n"); 506 | 507 | cleanup: 508 | if (image) free(image); 509 | if (ofh) fclose(ofh); 510 | return ret; 511 | } 512 | 513 | int genproxy_make(int argc, char** argv, bool forward) 514 | { 515 | // decleration before more meaningful initialization for cleanup 516 | int ret = EXIT_SUCCESS; 517 | FILE* ofh = NULL; 518 | 519 | FAIL_IF(argc < 3, "usage: genproxy_make [ofile]\n"); 520 | 521 | FAIL_IF(file_exists(argv[2]), "%s: output file already exists.\n", argv[2]); 522 | ofh = fopen(argv[2], "w"); 523 | FAIL_IF_PERROR(ofh == NULL, "%s"); 524 | 525 | fprintf(ofh, "-include config.mk\n"); 526 | fprintf(ofh, "\n"); 527 | fprintf(ofh, "TARGET ?= %s\n", file_basename(argv[1])); 528 | fprintf(ofh, "\n"); 529 | fprintf(ofh, "LDFLAGS ?= -Wl,--enable-stdcall-fixup -s -shared -static\n"); 530 | fprintf(ofh, "CFLAGS ?= -masm=intel -O2 -Wall -march=pentium4 -D _WIN32_WINNT=0x0600\n"); 531 | fprintf(ofh, "CXXFLAGS ?= -masm=intel -O2 -Wall -march=pentium4 -D _WIN32_WINNT=0x0600\n"); 532 | fprintf(ofh, "LIBS ?= \n"); 533 | fprintf(ofh, "\n"); 534 | fprintf(ofh, "CC = i686-w64-mingw32-gcc\n"); 535 | fprintf(ofh, "CXX = i686-w64-mingw32-g++\n"); 536 | fprintf(ofh, "STRIP ?= i686-w64-mingw32-strip\n"); 537 | fprintf(ofh, "WINDRES ?= i686-w64-mingw32-windres\n"); 538 | fprintf(ofh, "\n"); 539 | 540 | if (forward) 541 | { 542 | fprintf(ofh, "OBJS = dllmain.o res.o\n"); 543 | } 544 | else 545 | { 546 | fprintf(ofh, "OBJS = dllmain.o exports.o res.o\n"); 547 | } 548 | 549 | fprintf(ofh, "\n"); 550 | fprintf(ofh, ".PHONY: all clean\n"); 551 | fprintf(ofh, "all: $(TARGET)\n"); 552 | fprintf(ofh, "\n"); 553 | fprintf(ofh, "%%.o: %%.rc\n"); 554 | fprintf(ofh, " $(WINDRES) -J rc $< $@ || windres -J rc $< $@\n"); 555 | fprintf(ofh, "\n"); 556 | fprintf(ofh, "$(TARGET): $(OBJS)\n"); 557 | fprintf(ofh, " $(CXX) $(LDFLAGS) -o $@ $^ exports.def -L. $(LIBS)\n"); 558 | fprintf(ofh, "\n"); 559 | fprintf(ofh, "clean:\n"); 560 | fprintf(ofh, " $(RM) $(OBJS) $(TARGET) || del $(TARGET) $(subst /,\\\\,$(OBJS))\n"); 561 | 562 | cleanup: 563 | if (ofh) fclose(ofh); 564 | return ret; 565 | } 566 | -------------------------------------------------------------------------------- /src/gensym.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "pe.h" 27 | #include "cleanup.h" 28 | #include "common.h" 29 | 30 | int gensym(int argc, char** argv) 31 | { 32 | // decleration before more meaningful initialization for cleanup 33 | int ret = EXIT_SUCCESS; 34 | int8_t *image = NULL; 35 | FILE *ofh = argc > 2 ? NULL : stdout; 36 | uint32_t length; 37 | 38 | FAIL_IF(argc < 2, "usage: petool gensym [ofile]\n"); 39 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 40 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 41 | 42 | if (argc > 2) 43 | { 44 | FAIL_IF(file_exists(argv[2]), "%s: output file already exists.\n", argv[2]); 45 | ofh = fopen(argv[2], "w"); 46 | FAIL_IF_PERROR(ofh == NULL, "%s"); 47 | } 48 | 49 | PIMAGE_DOS_HEADER dos_hdr = (void*)image; 50 | PIMAGE_NT_HEADERS nt_hdr = (void*)(image + dos_hdr->e_lfanew); 51 | 52 | fprintf(ofh, "#include \"macros/setsym.h\"\n\n\n"); 53 | fprintf(ofh, "/* vars */\n\n\n"); 54 | fprintf(ofh, "/* functions */\n\n"); 55 | fprintf(ofh, "SETCGLOB(0x%08"PRIX32", OriginalCRTStartup);\n", (nt_hdr->OptionalHeader.ImageBase + nt_hdr->OptionalHeader.AddressOfEntryPoint)); 56 | 57 | if (strcmp(argv[0], "gensym") == 0 && 58 | nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 1 && 59 | nt_hdr->OptionalHeader.DataDirectory[1].VirtualAddress && 60 | nt_hdr->OptionalHeader.DataDirectory[1].Size) 61 | { 62 | fprintf(ofh, "/* imports */\n\n"); 63 | 64 | uint32_t offset = rva_to_offset(nt_hdr->OptionalHeader.DataDirectory[1].VirtualAddress, nt_hdr); 65 | IMAGE_IMPORT_DESCRIPTOR* i = (void*)(image + offset); 66 | 67 | while (i->FirstThunk) { 68 | char name[260] = { 0 }; 69 | 70 | if (i->Name != 0) { 71 | strncpy( 72 | name, 73 | (char*)(image + rva_to_offset(i->Name, nt_hdr)), 74 | sizeof name - 1 75 | ); 76 | 77 | fprintf(ofh, "\n/* %s */\n", name); 78 | } 79 | 80 | uint32_t thunk = i->OriginalFirstThunk ? i->OriginalFirstThunk : i->FirstThunk; 81 | 82 | PIMAGE_THUNK_DATA32 ft = 83 | (PIMAGE_THUNK_DATA32)(image + rva_to_offset(thunk, nt_hdr)); 84 | 85 | uint32_t function = nt_hdr->OptionalHeader.ImageBase + i->FirstThunk; 86 | 87 | while (ft->u1.AddressOfData) 88 | { 89 | PIMAGE_IMPORT_BY_NAME import = 90 | (PIMAGE_IMPORT_BY_NAME)(image + rva_to_offset(ft->u1.AddressOfData, nt_hdr)); 91 | 92 | if ((ft->u1.Ordinal & IMAGE_ORDINAL_FLAG32) == 0) 93 | { 94 | fprintf(ofh, "SETCGLOB(0x%08"PRIX32", _imp__%s);\n", function, (const char*)import->Name); 95 | } 96 | else 97 | { 98 | char* p = strrchr(name, '.'); 99 | if (p) 100 | { 101 | *p = '\0'; 102 | } 103 | 104 | int ordinal = (ft->u1.Ordinal & ~IMAGE_ORDINAL_FLAG32) & 0xffff; 105 | 106 | fprintf(ofh, "SETCGLOB(0x%08"PRIX32", _imp__%s_Ordinal_%d);\n", function, name, ordinal); 107 | } 108 | 109 | 110 | ft++; 111 | function += sizeof(IMAGE_THUNK_DATA32); 112 | } 113 | 114 | i++; 115 | } 116 | } 117 | 118 | cleanup: 119 | if (image) free(image); 120 | if (argc > 2) 121 | { 122 | if (ofh) fclose(ofh); 123 | } 124 | return ret; 125 | } 126 | -------------------------------------------------------------------------------- /src/import.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "pe.h" 27 | #include "cleanup.h" 28 | #include "common.h" 29 | 30 | int import(int argc, char **argv) 31 | { 32 | // decleration before more meaningful initialization for cleanup 33 | int ret = EXIT_SUCCESS; 34 | int8_t *image = NULL; 35 | FILE *ofh = argc > 3 ? NULL : stdout; 36 | uint32_t length; 37 | 38 | FAIL_IF(argc < 2, "usage: petool import [nasm] [ofile]\n"); 39 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 40 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 41 | 42 | if (argc > 3) 43 | { 44 | FAIL_IF(file_exists(argv[3]), "%s: output file already exists.\n", argv[3]); 45 | ofh = fopen(argv[3], "w"); 46 | FAIL_IF_PERROR(ofh == NULL, "%s"); 47 | } 48 | 49 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 50 | PIMAGE_NT_HEADERS nt_hdr = (void *)(image + dos_hdr->e_lfanew); 51 | 52 | FAIL_IF (nt_hdr->OptionalHeader.NumberOfRvaAndSizes < 2, "Not enough DataDirectories.\n"); 53 | FAIL_IF (!nt_hdr->OptionalHeader.DataDirectory[1].VirtualAddress, "No import directory in executable.\n"); 54 | 55 | uint32_t offset = rva_to_offset(nt_hdr->OptionalHeader.DataDirectory[1].VirtualAddress, nt_hdr); 56 | IMAGE_IMPORT_DESCRIPTOR *i = (void *)(image + offset); 57 | 58 | if (argc > 2 && toupper(argv[2][0]) == 'N') { 59 | fprintf(ofh, "; Imports for %s\n", argv[1]); 60 | fprintf(ofh, "ImageBase equ 0x%"PRIX32"\n", nt_hdr->OptionalHeader.ImageBase); 61 | fprintf(ofh, "\n"); 62 | fprintf(ofh, "section .idata\n\n"); 63 | 64 | while (1) { 65 | if (i->Name != 0) { 66 | char *name = (char *)(image + rva_to_offset(i->Name, nt_hdr)); 67 | fprintf(ofh, "; %s\n", name); 68 | } else { 69 | fprintf(ofh, "; END\n"); 70 | } 71 | 72 | fprintf(ofh, "dd 0x%-8"PRIX32" ; OriginalFirstThunk\n", i->OriginalFirstThunk); 73 | fprintf(ofh, "dd 0x%-8"PRIX32" ; TimeDateStamp\n", i->TimeDateStamp); 74 | fprintf(ofh, "dd 0x%-8"PRIX32" ; ForwarderChain\n", i->ForwarderChain); 75 | fprintf(ofh, "dd 0x%-8"PRIX32" ; Name\n", i->Name); 76 | fprintf(ofh, "dd 0x%-8"PRIX32" ; FirstThunk\n", i->FirstThunk); 77 | 78 | fprintf(ofh, "\n"); 79 | 80 | if (i->OriginalFirstThunk == 0) 81 | break; 82 | i++; 83 | } 84 | } else { 85 | fprintf(ofh, "/* Imports for %s */\n", argv[1]); 86 | fprintf(ofh, ".equ ImageBase, 0x%"PRIX32"\n", nt_hdr->OptionalHeader.ImageBase); 87 | fprintf(ofh, "\n"); 88 | fprintf(ofh, ".section .idata\n\n"); 89 | 90 | while (1) { 91 | if (i->Name != 0) { 92 | char *name = (char *)(image + rva_to_offset(i->Name, nt_hdr)); 93 | fprintf(ofh, "/* %s */\n", name); 94 | } else { 95 | fprintf(ofh, "/* END */\n"); 96 | } 97 | 98 | fprintf(ofh, ".long 0x%-8"PRIX32" /* OriginalFirstThunk */\n", i->OriginalFirstThunk); 99 | fprintf(ofh, ".long 0x%-8"PRIX32" /* TimeDateStamp */\n", i->TimeDateStamp); 100 | fprintf(ofh, ".long 0x%-8"PRIX32" /* ForwarderChain */\n", i->ForwarderChain); 101 | fprintf(ofh, ".long 0x%-8"PRIX32" /* Name */\n", i->Name); 102 | fprintf(ofh, ".long 0x%-8"PRIX32" /* FirstThunk */\n", i->FirstThunk); 103 | 104 | fprintf(ofh, "\n"); 105 | 106 | if (i->OriginalFirstThunk == 0) 107 | break; 108 | i++; 109 | } 110 | } 111 | 112 | cleanup: 113 | if (image) free(image); 114 | if (argc > 3) 115 | { 116 | if (ofh) fclose(ofh); 117 | } 118 | return ret; 119 | } 120 | -------------------------------------------------------------------------------- /src/incbin.S: -------------------------------------------------------------------------------- 1 | .macro INCBIN name:req, path:req 2 | .global \name 3 | .global _\name 4 | .data 5 | _\name\(): 6 | \name\(): 7 | .incbin "\path" 8 | .byte 0 9 | .endm 10 | 11 | INCBIN res_proxy_readme_txt, "res/proxy/readme.txt" 12 | INCBIN res_proxy_res_rc, "res/proxy/res.rc" 13 | INCBIN res_proxy_vcxproj, "res/proxy/proxy.vcxproj" 14 | INCBIN res_gitignore, "res/.gitignore" 15 | INCBIN res_build_cmd, "res/build.cmd" 16 | INCBIN res_readme_md, "res/README.md" 17 | INCBIN res_src_winmain_c, "res/src/winmain.cpp" 18 | INCBIN res_inc_app_h, "res/inc/app.h" 19 | INCBIN res_inc_app_inc, "res/inc/app.inc" 20 | INCBIN res_inc_patch_h, "res/inc/patch.h" 21 | INCBIN res_inc_macros_datatypes_inc, "res/inc/macros/datatypes.inc" 22 | INCBIN res_inc_macros_datatypes_s, "res/inc/macros/datatypes.s" 23 | INCBIN res_inc_macros_extern_inc, "res/inc/macros/extern.inc" 24 | INCBIN res_inc_macros_extern_s, "res/inc/macros/extern.s" 25 | INCBIN res_inc_macros_patch_h, "res/inc/macros/patch.h" 26 | INCBIN res_inc_macros_patch_inc, "res/inc/macros/patch.inc" 27 | INCBIN res_inc_macros_setsym_h, "res/inc/macros/setsym.h" 28 | INCBIN res_inc_macros_patch_s, "res/inc/macros/patch.s" 29 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "common.h" 22 | 23 | #if !defined(_WIN32) 24 | #define strcmpi strcasecmp 25 | #endif 26 | 27 | int dump(int argc, char **argv); 28 | int genlds(int argc, char **argv); 29 | int pe2obj(int argc, char **argv); 30 | int patch(int argc, char **argv); 31 | int setdd(int argc, char **argv); 32 | int setvs(int argc, char **argv); 33 | int setsc(int argc, char** argv); 34 | int setts(int argc, char** argv); 35 | int export(int argc, char **argv); 36 | int import(int argc, char **argv); 37 | int re2obj(int argc, char **argv); 38 | int genmak(int argc, char **argv); 39 | int gensym(int argc, char** argv); 40 | int genprj(int argc, char **argv); 41 | int genpatch(int argc, char** argv); 42 | int genproxy(int argc, char** argv); 43 | 44 | int help(char *progname) 45 | { 46 | fprintf(stderr, "petool git~%s (c) 2013 - 2024 Toni Spets, Ericson2314, FunkyFr3sh\n", GIT_COMMIT); 47 | fprintf(stderr, "https://github.com/FunkyFr3sh/petool\n\n"); 48 | fprintf(stderr, "usage: %s [args ...]\n\n", progname); 49 | fprintf(stderr, "commands:" "\n" 50 | " dump -- dump information about section of executable" "\n" 51 | " genlds -- generate GNU ld script for re-linking executable" "\n" 52 | " pe2obj -- convert PE executable into win32 object file" "\n" 53 | " patch -- apply a patch set from the .patch section" "\n" 54 | " setdd -- set any DataDirectory in PE header" "\n" 55 | " setvs -- set VirtualSize for a section" "\n" 56 | " setsc -- set Characteristics for a section" "\n" 57 | " setts -- set TimeDateStamp in FileHeader" "\n" 58 | " export -- export section data as raw binary" "\n" 59 | " import -- dump the import table as assembly" "\n" 60 | " re2obj -- convert the resource section into COFF object" "\n" 61 | " genmak -- generate project Makefile" "\n" 62 | " gensym -- generate full sym.cpp with all imports" "\n" 63 | " genprj -- generate full project directory" "\n" 64 | " genpatch -- compare 2 executables and generate patch macros" "\n" 65 | " genproxy -- generate proxy dll project directory" "\n" 66 | " help -- this information" "\n" 67 | ); 68 | 69 | return EXIT_SUCCESS; 70 | } 71 | 72 | int main(int argc, char **argv) 73 | { 74 | int ret = EXIT_SUCCESS; 75 | 76 | if (argc < 2) 77 | { 78 | help(argv[0]); 79 | fprintf(stderr, "\nNo command given:\n"); 80 | fprintf(stderr, " please give valid command name as first argument or\n"); 81 | fprintf(stderr, " drag and drop a Portable Executable (PE) image on petool\n\n"); 82 | ret = EXIT_FAILURE; 83 | } 84 | else if (strcmp(argv[1], "dump") == 0) return dump (argc - 1, argv + 1); 85 | else if (strcmp(argv[1], "genlds") == 0) return genlds (argc - 1, argv + 1); 86 | else if (strcmp(argv[1], "pe2obj") == 0) return pe2obj (argc - 1, argv + 1); 87 | else if (strcmp(argv[1], "patch") == 0) return patch (argc - 1, argv + 1); 88 | else if (strcmp(argv[1], "setdd") == 0) return setdd (argc - 1, argv + 1); 89 | else if (strcmp(argv[1], "setvs") == 0) return setvs (argc - 1, argv + 1); 90 | else if (strcmp(argv[1], "setc") == 0) return setsc (argc - 1, argv + 1); 91 | else if (strcmp(argv[1], "setsc") == 0) return setsc (argc - 1, argv + 1); 92 | else if (strcmp(argv[1], "setts") == 0) return setts (argc - 1, argv + 1); 93 | else if (strcmp(argv[1], "export") == 0) return export (argc - 1, argv + 1); 94 | else if (strcmp(argv[1], "import") == 0) return import (argc - 1, argv + 1); 95 | else if (strcmp(argv[1], "re2obj") == 0) return re2obj (argc - 1, argv + 1); 96 | else if (strcmp(argv[1], "genmak") == 0) return genmak (argc - 1, argv + 1); 97 | else if (strcmp(argv[1], "gensym") == 0) return gensym (argc - 1, argv + 1); 98 | else if (strcmp(argv[1], "genprj") == 0) return genprj (argc - 1, argv + 1); 99 | else if (strcmp(argv[1], "genpatch") == 0) return genpatch(argc - 1, argv + 1); 100 | else if (strcmp(argv[1], "genproxy") == 0) return genproxy(argc - 1, argv + 1); 101 | else if (strcmp(argv[1], "help") == 0) return help(argv[0]); 102 | else if (argc == 3 && file_exists(argv[1]) && file_exists(argv[2])) 103 | { 104 | char* cmd_argv[3] = { "genpatch", argv[1], argv[2] }; 105 | ret = genpatch(3, cmd_argv); 106 | } 107 | else if (argc == 2 && file_exists(argv[1]) && strlen(argv[1]) >= 4 && !strcmpi(argv[1] + strlen(argv[1]) - 4, ".dll")) 108 | { 109 | char* cmd_argv[2] = { "genproxy", argv[1] }; 110 | ret = genproxy(2, cmd_argv); 111 | } 112 | else 113 | { 114 | if (file_exists(argv[1])) 115 | { 116 | char *cmd_argv[2] = { "genprj", argv[1] }; 117 | ret = genprj(2, cmd_argv); 118 | } 119 | else 120 | { 121 | fprintf(stderr, "Unknown command: %s\n", argv[1]); 122 | help(argv[0]); 123 | ret = EXIT_FAILURE; 124 | } 125 | } 126 | 127 | #if defined(_WIN32) 128 | if (ret != EXIT_SUCCESS) 129 | { 130 | system("pause"); 131 | } 132 | #endif 133 | 134 | return ret; 135 | } 136 | -------------------------------------------------------------------------------- /src/patch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "pe.h" 27 | #include "cleanup.h" 28 | #include "common.h" 29 | 30 | static struct { 31 | uint32_t start; 32 | uint32_t end; 33 | } g_patch_offsets[4096]; 34 | 35 | int patch_image(int8_t *image, uint32_t address, int8_t *patch, uint32_t length) 36 | { 37 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 38 | PIMAGE_NT_HEADERS nt_hdr = (PIMAGE_NT_HEADERS)(image + dos_hdr->e_lfanew); 39 | 40 | for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 41 | { 42 | PIMAGE_SECTION_HEADER sct_hdr = IMAGE_FIRST_SECTION(nt_hdr) + i; 43 | 44 | if (sct_hdr->VirtualAddress + nt_hdr->OptionalHeader.ImageBase <= address && address < sct_hdr->VirtualAddress + nt_hdr->OptionalHeader.ImageBase + sct_hdr->SizeOfRawData) 45 | { 46 | uint32_t offset = sct_hdr->PointerToRawData + (address - (sct_hdr->VirtualAddress + nt_hdr->OptionalHeader.ImageBase)); 47 | 48 | if (sct_hdr->SizeOfRawData < length || sct_hdr->PointerToRawData + sct_hdr->SizeOfRawData < offset + length) 49 | { 50 | fprintf(stderr, "Error: Patch '%08"PRIX32"' is too long (%"PRIu32" bytes)\n", address, length); 51 | 52 | return EXIT_FAILURE; 53 | } 54 | 55 | memcpy(image + offset, patch, length); 56 | 57 | for (uint32_t i = 0; i < sizeof(g_patch_offsets) / sizeof(g_patch_offsets[0]); i++) 58 | { 59 | if (g_patch_offsets[i].start) 60 | { 61 | if ((address >= g_patch_offsets[i].start && address < g_patch_offsets[i].end) || 62 | (address < g_patch_offsets[i].start && address + length - 1 >= g_patch_offsets[i].start)) 63 | { 64 | fprintf( 65 | stderr, 66 | "Warning: Conflicting patches detected\n" 67 | " Patch 1 start = %08"PRIX32", end = %08"PRIX32", size = %"PRIu32" bytes\n" 68 | " Patch 2 start = %08"PRIX32", end = %08"PRIX32", size = %"PRIu32" bytes\n\n", 69 | address, 70 | address + length, 71 | length, 72 | g_patch_offsets[i].start, 73 | g_patch_offsets[i].end, 74 | g_patch_offsets[i].end - g_patch_offsets[i].start); 75 | } 76 | } 77 | else 78 | { 79 | g_patch_offsets[i].start = address; 80 | g_patch_offsets[i].end = address + length; 81 | break; 82 | } 83 | } 84 | 85 | //printf("PATCH %8"PRId32" bytes -> %8"PRIX32"\n", length, address); 86 | return EXIT_SUCCESS; 87 | } 88 | } 89 | 90 | fprintf(stderr, "Error: memory address %08"PRIX32" not found in image\n", address); 91 | return EXIT_FAILURE; 92 | } 93 | 94 | uint32_t get_uint32(int8_t * *p) 95 | { 96 | uint32_t ret = *(uint32_t *)*p; 97 | *p += sizeof(uint32_t); 98 | return ret; 99 | } 100 | 101 | int patch(int argc, char **argv) 102 | { 103 | // decleration before more meaningful initialization for cleanup 104 | int ret = EXIT_FAILURE; 105 | FILE *fh = NULL; 106 | int8_t *image = NULL; 107 | uint32_t length; 108 | 109 | FAIL_IF(argc < 2, "usage: petool patch [section]\n"); 110 | FAIL_IF_SILENT(open_and_read(&fh, &image, &length, argv[1], "r+b")); 111 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 112 | 113 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 114 | PIMAGE_NT_HEADERS nt_hdr = (void *)(image + dos_hdr->e_lfanew); 115 | 116 | char *section = argc > 2 ? (char *)argv[2] : ".patch"; 117 | int8_t *patch = NULL; 118 | int32_t patch_len = 0; 119 | 120 | for (int32_t i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 121 | { 122 | PIMAGE_SECTION_HEADER sct_hdr = IMAGE_FIRST_SECTION(nt_hdr) + i; 123 | 124 | if (strncmp(section, (char *)sct_hdr->Name, 8) == 0) 125 | { 126 | patch = image + sct_hdr->PointerToRawData; 127 | patch_len = sct_hdr->Misc.VirtualSize; 128 | } 129 | else 130 | { 131 | //hack for bug in binutils - make sure vsize is not less than rawsize so we don't lose any data on strip 132 | if (sct_hdr->Misc.VirtualSize && sct_hdr->SizeOfRawData > sct_hdr->Misc.VirtualSize) 133 | { 134 | sct_hdr->Misc.VirtualSize = sct_hdr->SizeOfRawData; 135 | } 136 | } 137 | } 138 | 139 | if (patch == NULL) 140 | { 141 | fprintf(stderr, "Warning: No '%s' section in given PE image.\n", section); 142 | ret = EXIT_SUCCESS; 143 | goto cleanup; 144 | } 145 | 146 | uint32_t patch_count = 0; 147 | uint32_t patch_bytes = 0; 148 | 149 | for (int8_t *p = patch; p < patch + patch_len;) 150 | { 151 | uint32_t paddress = get_uint32(&p); 152 | if (paddress == 0) 153 | { 154 | fprintf(stderr, "Warning: Trailing zero address in '%s' section.\n", section); 155 | break; 156 | } 157 | 158 | uint32_t plength = get_uint32(&p); 159 | 160 | if (plength > 0) 161 | { 162 | FAIL_IF_SILENT(patch_image(image, paddress, p, plength) == EXIT_FAILURE); 163 | 164 | patch_count++; 165 | patch_bytes += plength; 166 | 167 | p += plength; 168 | } 169 | else 170 | { 171 | fprintf( 172 | stderr, 173 | "Warning: Empty patch detected\n" 174 | " Patch start = %08"PRIX32", size = 0 bytes\n\n", 175 | paddress); 176 | } 177 | } 178 | 179 | fprintf(stderr, "Applied %"PRIu32" patches (%"PRIu32" bytes)\n", patch_count, patch_bytes); 180 | 181 | /* FIXME: implement checksum calculation */ 182 | nt_hdr->OptionalHeader.CheckSum = 0; 183 | 184 | rewind(fh); 185 | FAIL_IF_PERROR(fwrite(image, length, 1, fh) != 1, "Error writing executable"); 186 | 187 | ret = EXIT_SUCCESS; 188 | cleanup: 189 | if (image) free(image); 190 | if (fh) fclose(fh); 191 | return ret; 192 | } 193 | -------------------------------------------------------------------------------- /src/pe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "pe.h" 27 | #include "cleanup.h" 28 | #include "common.h" 29 | 30 | bool is_supported_pe_image(int8_t* image, uint32_t length) 31 | { 32 | int ret = EXIT_SUCCESS; 33 | 34 | PIMAGE_DOS_HEADER dos_hdr = (void*)image; 35 | PIMAGE_NT_HEADERS nt_hdr = (void*)(image + dos_hdr->e_lfanew); 36 | 37 | FAIL_IF(length < sizeof(IMAGE_DOS_HEADER), "File too small.\n"); 38 | FAIL_IF(dos_hdr->e_magic != IMAGE_DOS_SIGNATURE, "File DOS signature invalid.\n"); 39 | FAIL_IF(dos_hdr->e_lfanew > length - 4 || dos_hdr->e_lfanew < sizeof(IMAGE_DOS_HEADER), "NT headers not found in file.\n"); 40 | FAIL_IF(nt_hdr->Signature != IMAGE_NT_SIGNATURE, "File NT signature invalid.\n"); 41 | FAIL_IF(nt_hdr->FileHeader.Machine != IMAGE_FILE_MACHINE_I386, "File machine type is not i386.\n"); 42 | 43 | if (nt_hdr->OptionalHeader.NumberOfRvaAndSizes > 14) 44 | { 45 | FAIL_IF(nt_hdr->OptionalHeader.DataDirectory[14].VirtualAddress, "File type unsupported (.NET assembly).\n"); 46 | } 47 | 48 | cleanup: 49 | return ret == EXIT_SUCCESS; 50 | } 51 | 52 | uint32_t rva_to_offset(uint32_t address, PIMAGE_NT_HEADERS nt_hdr) 53 | { 54 | for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 55 | { 56 | PIMAGE_SECTION_HEADER sct_hdr = IMAGE_FIRST_SECTION(nt_hdr) + i; 57 | 58 | if (sct_hdr->VirtualAddress <= address && address < sct_hdr->VirtualAddress + sct_hdr->SizeOfRawData) 59 | { 60 | return sct_hdr->PointerToRawData + (address - sct_hdr->VirtualAddress); 61 | } 62 | } 63 | 64 | return address; 65 | } 66 | 67 | uint32_t offset_to_rva(uint32_t address, PIMAGE_NT_HEADERS nt_hdr) 68 | { 69 | for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 70 | { 71 | PIMAGE_SECTION_HEADER sct_hdr = IMAGE_FIRST_SECTION(nt_hdr) + i; 72 | 73 | if (address >= sct_hdr->PointerToRawData && address < sct_hdr->PointerToRawData + sct_hdr->SizeOfRawData) 74 | { 75 | return (address - sct_hdr->PointerToRawData) + sct_hdr->VirtualAddress; 76 | } 77 | } 78 | 79 | return (1 << 31); 80 | } 81 | 82 | uint32_t section_from_offset(uint32_t address, PIMAGE_NT_HEADERS nt_hdr, char* section, uint32_t size) 83 | { 84 | if (section) 85 | { 86 | if (size < 9) 87 | return 0; 88 | 89 | memset(section, 0, size); 90 | } 91 | 92 | for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 93 | { 94 | PIMAGE_SECTION_HEADER sct_hdr = IMAGE_FIRST_SECTION(nt_hdr) + i; 95 | 96 | if (address >= sct_hdr->PointerToRawData && address < sct_hdr->PointerToRawData + sct_hdr->SizeOfRawData) 97 | { 98 | if (section && address) 99 | { 100 | memcpy(section, (void*)sct_hdr->Name, 8); 101 | } 102 | 103 | return sct_hdr->PointerToRawData; 104 | } 105 | } 106 | 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /src/pe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | // based on MingW headers converted to stdint 18 | 19 | #include 20 | #include 21 | 22 | #define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */ 23 | #define IMAGE_OS2_SIGNATURE 0x454E /* NE */ 24 | #define IMAGE_OS2_SIGNATURE_LE 0x454C /* LE */ 25 | #define IMAGE_OS2_SIGNATURE_LX 0x584C /* LX */ 26 | #define IMAGE_VXD_SIGNATURE 0x454C /* LE */ 27 | #define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ 28 | 29 | #define IMAGE_SCN_CNT_CODE 0x00000020 30 | #define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 31 | #define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 32 | #define IMAGE_SCN_MEM_EXECUTE 0x20000000 33 | #define IMAGE_SCN_MEM_READ 0x40000000 34 | #define IMAGE_SCN_MEM_WRITE 0x80000000 35 | #define IMAGE_SCN_ALIGN_MASK 0x00F00000 36 | 37 | #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory 38 | #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory 39 | #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory 40 | #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory 41 | #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory 42 | #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table 43 | #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory 44 | #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data 45 | #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP 46 | #define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory 47 | #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory 48 | #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers 49 | #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table 50 | #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors 51 | #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor 52 | 53 | #define IMAGE_ORDINAL_FLAG32 0x80000000 54 | 55 | #define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 56 | 57 | #define FIELD_OFFSET(t,f) ((intptr_t)&(((t*)0)->f)) 58 | 59 | #define IMAGE_SIZEOF_SHORT_NAME 8 60 | #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 61 | #define IMAGE_FIRST_SECTION(h) ((PIMAGE_SECTION_HEADER) ((intptr_t)h+FIELD_OFFSET(IMAGE_NT_HEADERS,OptionalHeader)+((PIMAGE_NT_HEADERS)(h))->FileHeader.SizeOfOptionalHeader)) 62 | 63 | #define IMAGE_FILE_MACHINE_I386 0x014C 64 | 65 | #pragma pack(push,2) 66 | typedef struct _IMAGE_DOS_HEADER { 67 | uint16_t e_magic; 68 | uint16_t e_cblp; 69 | uint16_t e_cp; 70 | uint16_t e_crlc; 71 | uint16_t e_cparhdr; 72 | uint16_t e_minalloc; 73 | uint16_t e_maxalloc; 74 | uint16_t e_ss; 75 | uint16_t e_sp; 76 | uint16_t e_csum; 77 | uint16_t e_ip; 78 | uint16_t e_cs; 79 | uint16_t e_lfarlc; 80 | uint16_t e_ovno; 81 | uint16_t e_res[4]; 82 | uint16_t e_oemid; 83 | uint16_t e_oeminfo; 84 | uint16_t e_res2[10]; 85 | uint32_t e_lfanew; 86 | } IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER; 87 | #pragma pack(pop) 88 | 89 | #pragma pack(push,4) 90 | 91 | typedef struct _IMAGE_FILE_HEADER { 92 | uint16_t Machine; 93 | uint16_t NumberOfSections; 94 | uint32_t TimeDateStamp; 95 | uint32_t PointerToSymbolTable; 96 | uint32_t NumberOfSymbols; 97 | uint16_t SizeOfOptionalHeader; 98 | uint16_t Characteristics; 99 | } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; 100 | 101 | typedef struct _IMAGE_DATA_DIRECTORY { 102 | uint32_t VirtualAddress; 103 | uint32_t Size; 104 | } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 105 | 106 | typedef struct _IMAGE_OPTIONAL_HEADER { 107 | uint16_t Magic; 108 | uint8_t MajorLinkerVersion; 109 | uint8_t MinorLinkerVersion; 110 | uint32_t SizeOfCode; 111 | uint32_t SizeOfInitializedData; 112 | uint32_t SizeOfUninitializedData; 113 | uint32_t AddressOfEntryPoint; 114 | uint32_t BaseOfCode; 115 | uint32_t BaseOfData; 116 | uint32_t ImageBase; 117 | uint32_t SectionAlignment; 118 | uint32_t FileAlignment; 119 | uint16_t MajorOperatingSystemVersion; 120 | uint16_t MinorOperatingSystemVersion; 121 | uint16_t MajorImageVersion; 122 | uint16_t MinorImageVersion; 123 | uint16_t MajorSubsystemVersion; 124 | uint16_t MinorSubsystemVersion; 125 | uint32_t Win32VersionValue; 126 | uint32_t SizeOfImage; 127 | uint32_t SizeOfHeaders; 128 | uint32_t CheckSum; 129 | uint16_t Subsystem; 130 | uint16_t DllCharacteristics; 131 | uint32_t SizeOfStackReserve; 132 | uint32_t SizeOfStackCommit; 133 | uint32_t SizeOfHeapReserve; 134 | uint32_t SizeOfHeapCommit; 135 | uint32_t LoaderFlags; 136 | uint32_t NumberOfRvaAndSizes; 137 | IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 138 | } IMAGE_OPTIONAL_HEADER,*PIMAGE_OPTIONAL_HEADER; 139 | 140 | typedef struct _IMAGE_NT_HEADERS { 141 | uint32_t Signature; 142 | IMAGE_FILE_HEADER FileHeader; 143 | IMAGE_OPTIONAL_HEADER OptionalHeader; 144 | } IMAGE_NT_HEADERS,*PIMAGE_NT_HEADERS; 145 | 146 | typedef struct _IMAGE_SECTION_HEADER { 147 | uint8_t Name[IMAGE_SIZEOF_SHORT_NAME]; 148 | union { 149 | uint32_t PhysicalAddress; 150 | uint32_t VirtualSize; 151 | } Misc; 152 | uint32_t VirtualAddress; 153 | uint32_t SizeOfRawData; 154 | uint32_t PointerToRawData; 155 | uint32_t PointerToRelocations; 156 | uint32_t PointerToLinenumbers; 157 | uint16_t NumberOfRelocations; 158 | uint16_t NumberOfLinenumbers; 159 | uint32_t Characteristics; 160 | } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; 161 | 162 | typedef struct _IMAGE_IMPORT_DESCRIPTOR { 163 | uint32_t OriginalFirstThunk; 164 | uint32_t TimeDateStamp; 165 | uint32_t ForwarderChain; 166 | uint32_t Name; 167 | uint32_t FirstThunk; 168 | } IMAGE_IMPORT_DESCRIPTOR; 169 | 170 | typedef struct _IMAGE_EXPORT_DIRECTORY { 171 | uint32_t Characteristics; 172 | uint32_t TimeDateStamp; 173 | uint16_t MajorVersion; 174 | uint16_t MinorVersion; 175 | uint32_t Name; 176 | uint32_t Base; 177 | uint32_t NumberOfFunctions; 178 | uint32_t NumberOfNames; 179 | uint32_t AddressOfFunctions; 180 | uint32_t AddressOfNames; 181 | uint32_t AddressOfNameOrdinals; 182 | } IMAGE_EXPORT_DIRECTORY; 183 | 184 | typedef struct _IMAGE_RESOURCE_DIRECTORY { 185 | uint32_t Characteristics; 186 | uint32_t TimeDateStamp; 187 | uint16_t MajorVersion; 188 | uint16_t MinorVersion; 189 | uint16_t NumberOfNamedEntries; 190 | uint16_t NumberOfIdEntries; 191 | } IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY; 192 | 193 | typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY { 194 | uint32_t Name; 195 | uint32_t OffsetToData; 196 | } IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY; 197 | 198 | typedef struct _IMAGE_RESOURCE_DATA_ENTRY { 199 | uint32_t OffsetToData; 200 | uint32_t Size; 201 | uint32_t CodePage; 202 | uint32_t Reserved; 203 | } IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY; 204 | 205 | typedef struct _IMAGE_THUNK_DATA32 { 206 | union { 207 | uint32_t ForwarderString; 208 | uint32_t Function; 209 | uint32_t Ordinal; 210 | uint32_t AddressOfData; 211 | } u1; 212 | } IMAGE_THUNK_DATA32; 213 | typedef IMAGE_THUNK_DATA32* PIMAGE_THUNK_DATA32; 214 | 215 | typedef struct _IMAGE_IMPORT_BY_NAME { 216 | uint16_t Hint; 217 | uint8_t Name[1]; 218 | } IMAGE_IMPORT_BY_NAME, * PIMAGE_IMPORT_BY_NAME; 219 | 220 | #pragma pack(pop) 221 | 222 | bool is_supported_pe_image(int8_t* image, uint32_t length); 223 | uint32_t rva_to_offset(uint32_t address, PIMAGE_NT_HEADERS nt_hdr); 224 | uint32_t offset_to_rva(uint32_t address, PIMAGE_NT_HEADERS nt_hdr); 225 | uint32_t section_from_offset(uint32_t address, PIMAGE_NT_HEADERS nt_hdr, char* section, uint32_t size); 226 | -------------------------------------------------------------------------------- /src/pe2obj.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "pe.h" 28 | #include "cleanup.h" 29 | #include "common.h" 30 | 31 | int pe2obj(int argc, char **argv) 32 | { 33 | // decleration before more meaningful initialization for cleanup 34 | int ret = EXIT_SUCCESS; 35 | FILE *ofh = NULL; 36 | int8_t *image = NULL; 37 | uint32_t length; 38 | 39 | FAIL_IF(argc != 3, "usage: petool pe2obj \n"); 40 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 41 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 42 | 43 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 44 | PIMAGE_NT_HEADERS nt_hdr = (void *)(image + dos_hdr->e_lfanew); 45 | 46 | for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 47 | { 48 | const PIMAGE_SECTION_HEADER cur_sct = IMAGE_FIRST_SECTION(nt_hdr) + i; 49 | if (cur_sct->PointerToRawData) 50 | { 51 | cur_sct->PointerToRawData -= dos_hdr->e_lfanew + 4; 52 | } 53 | 54 | if (cur_sct->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA && !(cur_sct->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)) 55 | { 56 | cur_sct->PointerToRawData = 0; 57 | cur_sct->SizeOfRawData = 0; 58 | } 59 | } 60 | 61 | ofh = fopen(argv[2], "wb"); 62 | FAIL_IF_PERROR(!ofh, "Error opening output file"); 63 | 64 | FAIL_IF_PERROR(fwrite((char *)image + (dos_hdr->e_lfanew + 4), 65 | length - (dos_hdr->e_lfanew + 4), 66 | 1, 67 | ofh) != 1, 68 | "Failed to write object file to output file\n"); 69 | 70 | cleanup: 71 | if (image) free(image); 72 | if (ofh) fclose(ofh); 73 | return ret; 74 | } 75 | -------------------------------------------------------------------------------- /src/re2obj.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "pe.h" 27 | #include "cleanup.h" 28 | #include "common.h" 29 | 30 | #pragma pack(push,2) 31 | typedef struct { 32 | char e_name[8]; 33 | uint32_t e_value; 34 | int16_t e_scnum; 35 | uint16_t e_type; 36 | uint8_t e_sclass; 37 | uint8_t e_numaux; 38 | } t_syment; 39 | 40 | typedef struct { 41 | uint32_t VirtualAddress; 42 | uint32_t SymbolTableIndex; 43 | uint16_t Type; 44 | } t_reloc; 45 | 46 | typedef struct { 47 | uint32_t base; 48 | void *root; 49 | t_reloc relocs[1024]; 50 | uint32_t nrelocs; 51 | } re2obj_s; 52 | #pragma pack(pop) 53 | 54 | void traverse_directory(re2obj_s *state, PIMAGE_RESOURCE_DIRECTORY rdir, int level) 55 | { 56 | PIMAGE_RESOURCE_DIRECTORY_ENTRY rdir_ent = (void *)((uint8_t *)rdir + sizeof(IMAGE_RESOURCE_DIRECTORY)); 57 | 58 | for (int i = 0; i < (rdir->NumberOfNamedEntries + rdir->NumberOfIdEntries); i++) 59 | { 60 | if (level < 2) 61 | { 62 | traverse_directory(state, (void *)((uint8_t *)state->root + (rdir_ent->OffsetToData &~ (1 << 31))), level + 1); 63 | } 64 | else 65 | { 66 | PIMAGE_RESOURCE_DATA_ENTRY leaf = (void *)((uint8_t *)state->root + (rdir_ent->OffsetToData &~ (1 << 31))); 67 | 68 | leaf->OffsetToData = leaf->OffsetToData - state->base; 69 | 70 | state->relocs[state->nrelocs].VirtualAddress = (uint8_t *)&leaf->OffsetToData - (uint8_t *)state->root; 71 | state->relocs[state->nrelocs].Type = 7; 72 | state->nrelocs++; 73 | } 74 | 75 | rdir_ent++; 76 | } 77 | } 78 | 79 | int re2obj(int argc, char **argv) 80 | { 81 | // decleration before more meaningful initialization for cleanup 82 | int ret = EXIT_SUCCESS; 83 | int8_t *image = NULL; 84 | FILE *ofh = argc > 2 ? NULL : stdout; 85 | re2obj_s state; 86 | uint32_t length; 87 | 88 | memset(&state, 0, sizeof(state)); 89 | 90 | FAIL_IF(argc < 2, "usage: petool re2obj [ofile]\n"); 91 | FAIL_IF_SILENT(open_and_read(NULL, &image, &length, argv[1], NULL)); 92 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 93 | 94 | if (argc > 2) 95 | { 96 | ofh = fopen(argv[2], "wb"); 97 | FAIL_IF_PERROR(ofh == NULL, "%s"); 98 | } 99 | 100 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 101 | PIMAGE_NT_HEADERS nt_hdr = (void *)(image + dos_hdr->e_lfanew); 102 | 103 | char *section = ".rsrc"; 104 | void *data = NULL; 105 | uint32_t data_len = 0; 106 | 107 | for (int32_t i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 108 | { 109 | PIMAGE_SECTION_HEADER sct_hdr = IMAGE_FIRST_SECTION(nt_hdr) + i; 110 | 111 | if (strncmp(section, (char*)sct_hdr->Name, 8) == 0) 112 | { 113 | data = image + sct_hdr->PointerToRawData; 114 | data_len = sct_hdr->SizeOfRawData; 115 | if (sct_hdr ->Misc.VirtualSize > 0 && sct_hdr->Misc.VirtualSize < data_len) 116 | data_len = sct_hdr->Misc.VirtualSize; 117 | 118 | state.base = sct_hdr->VirtualAddress; 119 | state.root = data; 120 | 121 | traverse_directory(&state, (PIMAGE_RESOURCE_DIRECTORY)data, 0); 122 | break; 123 | } 124 | 125 | sct_hdr++; 126 | } 127 | 128 | FAIL_IF(data == NULL, "No '%s' section in given PE image.\n", section); 129 | 130 | IMAGE_FILE_HEADER FileHeader; 131 | memset(&FileHeader, 0, sizeof FileHeader); 132 | FileHeader.Machine = IMAGE_FILE_MACHINE_I386; 133 | FileHeader.NumberOfSections = 1; 134 | FileHeader.Characteristics = 0x0104; 135 | 136 | IMAGE_SECTION_HEADER SectionHeader; 137 | memset(&SectionHeader, 0, sizeof SectionHeader); 138 | strcpy((char *)SectionHeader.Name, ".rsrc"); 139 | SectionHeader.SizeOfRawData = data_len; 140 | SectionHeader.PointerToRawData = sizeof(FileHeader) + sizeof(SectionHeader); 141 | SectionHeader.PointerToRelocations = data_len + SectionHeader.PointerToRawData; 142 | SectionHeader.NumberOfRelocations = state.nrelocs; 143 | SectionHeader.Characteristics = 0xC0300040; 144 | 145 | FileHeader.PointerToSymbolTable = SectionHeader.PointerToRelocations + (sizeof(t_reloc) * state.nrelocs); 146 | FileHeader.NumberOfSymbols = 1; 147 | 148 | fwrite(&FileHeader, sizeof FileHeader, 1, ofh); 149 | fwrite(&SectionHeader, sizeof SectionHeader, 1, ofh); 150 | fwrite(data, data_len, 1, ofh); 151 | 152 | for (uint32_t i = 0; i < state.nrelocs; i++) 153 | fwrite(&state.relocs[i], sizeof(state.relocs[0]), 1, ofh); 154 | 155 | t_syment ent; 156 | strcpy(ent.e_name, ".rsrc"); 157 | ent.e_value = 0; 158 | ent.e_scnum = 1; 159 | ent.e_type = 0; 160 | ent.e_sclass = 3; 161 | ent.e_numaux = 0; 162 | 163 | fwrite(&ent, sizeof ent, 1, ofh); 164 | 165 | cleanup: 166 | if (image) free(image); 167 | if (argc > 2) 168 | { 169 | if (ofh) fclose(ofh); 170 | } 171 | return ret; 172 | } 173 | -------------------------------------------------------------------------------- /src/setdd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "pe.h" 27 | #include "cleanup.h" 28 | #include "common.h" 29 | 30 | int setdd(int argc, char **argv) 31 | { 32 | // decleration before more meaningful initialization for cleanup 33 | int ret = EXIT_SUCCESS; 34 | FILE *fh = NULL; 35 | int8_t *image = NULL; 36 | uint32_t length; 37 | 38 | FAIL_IF(argc != 5, "usage: petool setdd <#DataDirectory> \n"); 39 | FAIL_IF_SILENT(open_and_read(&fh, &image, &length, argv[1], "r+b")); 40 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 41 | 42 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 43 | PIMAGE_NT_HEADERS nt_hdr = (void *)(image + dos_hdr->e_lfanew); 44 | 45 | uint32_t dd = strtol(argv[2], NULL, 0); 46 | FAIL_IF(nt_hdr->OptionalHeader.NumberOfRvaAndSizes <= dd, "Data directory #%"PRIu32" is missing.\n", dd); 47 | 48 | nt_hdr->OptionalHeader.DataDirectory[dd].VirtualAddress = strtol(argv[3], NULL, 0); 49 | nt_hdr->OptionalHeader.DataDirectory[dd].Size = strtol(argv[4], NULL, 0); 50 | 51 | /* FIXME: implement checksum calculation */ 52 | nt_hdr->OptionalHeader.CheckSum = 0; 53 | 54 | rewind(fh); 55 | FAIL_IF_PERROR(fwrite(image, length, 1, fh) != 1, "Error writing executable"); 56 | 57 | cleanup: 58 | if (image) free(image); 59 | if (fh) fclose(fh); 60 | return ret; 61 | } 62 | -------------------------------------------------------------------------------- /src/setsc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "pe.h" 27 | #include "cleanup.h" 28 | #include "common.h" 29 | 30 | int setsc(int argc, char **argv) 31 | { 32 | // decleration before more meaningful initialization for cleanup 33 | int ret = EXIT_SUCCESS; 34 | FILE *fh = NULL; 35 | int8_t *image = NULL; 36 | uint32_t length; 37 | 38 | FAIL_IF(argc != 4, "usage: petool setsc
\n"); 39 | FAIL_IF_SILENT(open_and_read(&fh, &image, &length, argv[1], "r+b")); 40 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 41 | 42 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 43 | PIMAGE_NT_HEADERS nt_hdr = (void *)(image + dos_hdr->e_lfanew); 44 | 45 | uint32_t flags = strtoul(argv[3], NULL, 0); 46 | FAIL_IF(flags == 0, "Characteristics can't be zero.\n"); 47 | 48 | for (int32_t i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 49 | { 50 | PIMAGE_SECTION_HEADER sct_hdr = IMAGE_FIRST_SECTION(nt_hdr) + i; 51 | 52 | if (strncmp(argv[2], (char *)sct_hdr->Name, 8) == 0) 53 | { 54 | sct_hdr->Characteristics = flags; // update characteristics 55 | 56 | nt_hdr->OptionalHeader.CheckSum = 0; // FIXME: implement checksum calculation 57 | rewind(fh); // write to file 58 | FAIL_IF_PERROR(fwrite(image, length, 1, fh) != 1, "Error writing executable"); 59 | goto cleanup; // done 60 | } 61 | } 62 | 63 | fprintf(stderr, "No '%s' section in given PE image.\n", argv[2]); 64 | 65 | cleanup: 66 | if (image) free(image); 67 | if (fh) fclose(fh); 68 | return ret; 69 | } 70 | -------------------------------------------------------------------------------- /src/setts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "pe.h" 27 | #include "cleanup.h" 28 | #include "common.h" 29 | 30 | int setts(int argc, char **argv) 31 | { 32 | // decleration before more meaningful initialization for cleanup 33 | int ret = EXIT_SUCCESS; 34 | FILE *fh = NULL; 35 | int8_t *image = NULL; 36 | uint32_t length; 37 | 38 | FAIL_IF(argc != 3, "usage: petool setts \n"); 39 | FAIL_IF_SILENT(open_and_read(&fh, &image, &length, argv[1], "r+b")); 40 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 41 | 42 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 43 | PIMAGE_NT_HEADERS nt_hdr = (void *)(image + dos_hdr->e_lfanew); 44 | 45 | nt_hdr->FileHeader.TimeDateStamp = strtoul(argv[2], NULL, 0); 46 | 47 | /* FIXME: implement checksum calculation */ 48 | nt_hdr->OptionalHeader.CheckSum = 0; 49 | 50 | rewind(fh); 51 | FAIL_IF_PERROR(fwrite(image, length, 1, fh) != 1, "Error writing executable"); 52 | 53 | cleanup: 54 | if (image) free(image); 55 | if (fh) fclose(fh); 56 | return ret; 57 | } 58 | -------------------------------------------------------------------------------- /src/setvs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Toni Spets 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "pe.h" 27 | #include "cleanup.h" 28 | #include "common.h" 29 | 30 | int setvs(int argc, char **argv) 31 | { 32 | // decleration before more meaningful initialization for cleanup 33 | int ret = EXIT_SUCCESS; 34 | FILE *fh = NULL; 35 | int8_t *image = NULL; 36 | uint32_t length; 37 | 38 | FAIL_IF(argc != 4, "usage: petool setvs
\n"); 39 | FAIL_IF_SILENT(open_and_read(&fh, &image, &length, argv[1], "r+b")); 40 | FAIL_IF(!is_supported_pe_image(image, length), "File is not a valid i386 Portable Executable (PE) image.\n"); 41 | 42 | PIMAGE_DOS_HEADER dos_hdr = (void *)image; 43 | PIMAGE_NT_HEADERS nt_hdr = (void *)(image + dos_hdr->e_lfanew); 44 | 45 | uint32_t vs = strtoul(argv[3], NULL, 0); 46 | FAIL_IF(vs == 0, "VirtualSize can't be zero.\n"); 47 | 48 | for (int32_t i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) 49 | { 50 | PIMAGE_SECTION_HEADER sct_hdr = IMAGE_FIRST_SECTION(nt_hdr) + i; 51 | 52 | if (strncmp(argv[2], (char *)sct_hdr->Name, 8) == 0) 53 | { 54 | FAIL_IF(vs < sct_hdr->SizeOfRawData,"VirtualSize can't be smaller than raw size.\n"); 55 | sct_hdr->Misc.VirtualSize = vs; // update section size 56 | // update total virtual size of image 57 | nt_hdr->OptionalHeader.SizeOfImage += vs - sct_hdr->Misc.VirtualSize; 58 | nt_hdr->OptionalHeader.CheckSum = 0; // FIXME: implement checksum calculation 59 | rewind(fh); // write to file 60 | FAIL_IF_PERROR(fwrite(image, length, 1, fh) != 1, "Error writing executable"); 61 | goto cleanup; // done 62 | } 63 | } 64 | 65 | fprintf(stderr, "No '%s' section in given PE image.\n", argv[2]); 66 | 67 | cleanup: 68 | if (image) free(image); 69 | if (fh) fclose(fh); 70 | return ret; 71 | } 72 | --------------------------------------------------------------------------------