2

I've been tinkering with Fallout New Vegas, and have focused on modifying a very simple record that's hardcoded in the game engine. It's called an Imagespace Modifier, which is a very basic shader that can only have a select number of variables adjusted. It can be modified in the editor, but cannot be stopped from being applied to the screen. So even with it's values set to zero, it's still being calculated by the game.

So I decided to try and find the hardcoded function that calls the imagespace modifier. I think I've got it, but I've hit a problem. It uses a JNZ to determine whether or not to process the record, which is a two byte opcode instruction (0F 85). But JMP Near is a single opcode instruction (E9). So I can't just simply patch that word in memory and always skip past the code calling the imagespace modifier.

What would I need to do in order to fix that, and use JMP instead of JNZ? Note, the framework I'm using to modify the process at runtime uses C++, so it needs to be doable in that language.

Screenshot: https://i.stack.imgur.com/T9xw4.png

0

2 Answers 2

0

0f 85 takes a dword or rel32 or 4 bytes in 32 and 64 bit mode

and two bytes or rel16 in 16 bit mode

I assume 32 /64 bit because (hex view in your screen shot has a selection of 6 bytes highlighted )

0f 85 b5 00 00 00

to jmp with e9

change 0f 85 b5 00 00 00 to e9 b6 00 00 00 90

0:000> eb .
00007ffc`01d82dbc cc 0f 
00007ffc`01d82dbd eb 85
00007ffc`01d82dbe 00 b5
00007ffc`01d82dbf 48 00
00007ffc`01d82dc0 83 00
00007ffc`01d82dc1 c4 00
00007ffc`01d82dc2 38
0:000> u . l1
ntdll!LdrpDoDebuggerBreak+0x30:
00007ffc`01d82dbc 0f85b5000000    jne     ntdll!LdrpGetProcApphelpCheckModule+0xab (00007ffc`01d82e77)
0:000> eb .
00007ffc`01d82dbc 0f e9
00007ffc`01d82dbd 85 b6
00007ffc`01d82dbe b5 00
00007ffc`01d82dbf 00 00
00007ffc`01d82dc0 00 00
00007ffc`01d82dc1 00
0:000> u . l1
ntdll!LdrpDoDebuggerBreak+0x30:
00007ffc`01d82dbc e9b6000000      jmp     ntdll!LdrpGetProcApphelpCheckModule+0xab (00007ffc`01d82e77)
0:000>

or as igorsk edited in nop the first byte and modify the second and third byte

0:000> eb .
00007ffc`01d82dbc e9 90
00007ffc`01d82dbd b6 e9
00007ffc`01d82dbe 00 b5
00007ffc`01d82dbf 00 00
00007ffc`01d82dc0 00 00
00007ffc`01d82dc1 00 00
00007ffc`01d82dc2 38
0:000> u . l2
ntdll!LdrpDoDebuggerBreak+0x30:
00007ffc`01d82dbc 90              nop
00007ffc`01d82dbd e9b5000000      jmp     ntdll!LdrpGetProcApphelpCheckModule+0xab (00007ffc`01d82e77)
0:000>

for an unconditional jump the byte immediately to the patched instructions do not matter if the patched bytes are less.

but for other instructions which will fall through to the next instruction the instruction boundaries do matter

suppose you patched add to sub and sub is one byte less.
so after executing sub which is one byte lesser the next instruction will start executing on the bogus rogue byte.
this is not what was intended. we need to execute the byte which was the originally supposed to be executed and we cant fly in mid air.
we need to also execute the dummy rogue byte

so we change it to a one byte no operation instruction.

execute sub as well as nop and then we get to execute the actual original next instruction.

that is why the nop it may look silly here for this specific instruction but it is a good habit to patch all the bytes within instruction boundaries.

there are single byte nop as well as multibyte nops or instructions like mov eax,eax which do not alter the state but provide space fillers.

btw when you read the instruction manuals rel32 is always a counter it is counted from the start of next instruction a simple jump eb 00 at address 0x1000 jumps to 0x1002 because eb 00 is two bytes adding 2 to 0x1000 will make the next $ip 0x1002 so eb 01->0x1003 , eb 02 -> 0x1004 and so on

2
  • Now that's smart. I thought B5 00 00 00 was just a label, but it's actually the number of bytes to jump forward by. Out of curiosity, why is the last byte a NOP instruction? Is it a failsafe?
    – FiftyTifty
    Sep 4, 2019 at 21:07
  • added some more content to address your query take a look
    – blabb
    Sep 5, 2019 at 3:52
1

E9 is a so-called near jump which takes a four-byte offset (rel32) so you can’t actually fit it in two bytes. If you have a two-byte jnz (75 xx), you can instead use the short jump (EB) which takes a one-byte offset(rel8) just like jnz.

For the near jnz (0F 85 rel32) you can instead patch the first byte with a NOP (90) and replace 85 with E9 - this should give you the same destination but with unconditional jump.

References:

https://www.felixcloutier.com/x86/jmp

https://www.felixcloutier.com/x86/jcc

2
  • unixwiz.net/techtips/x86-jumps.html - JNZ Has two opcodes, and it takes one byte as the parameter. So it's 0F 85 B5 (a near jump), versus JMP which has one opcode and takes one byte to become E9 B5. Unless I'm missing something, that still leaves the issue of that left out byte.
    – FiftyTifty
    Sep 4, 2019 at 6:53
  • oh, I misread the question. For some reason I thought you have a short jump (75 rel8).
    – Igor Skochinsky
    Sep 4, 2019 at 8:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.