I am trying to create an inline codecave to modify a very old dosgame (ROTK2). However, far calls cannot be repositioned - moving the machine code for the function call 1 byte earlier causes it to malfunction. What parameters I need to readjust to correct the problem?
To be more specific for the task needed, I need to create 3 bytes of space to be able to add an assembly line. Hence I need to reposition code (codecaving may not be a possible option due to lack of resources in script, I had to find the 3 bytes inline).
There are too many lines of code in the whole program, which I do not fully understand, but I am using dosbox debugger with breakpoints to pinpoint to lines relevant:
This position of the code prints out the year, month and season at the current point of time in the game. From line 4EE8, the two pushs of ax moves the cursor to the position (2,4) on the screen so that the year located at [0x44] and the month, number located at bl [0x46] which then needs to be modified to reference to the month in text somewhere else. Then the string is printed out, and the process is repeated.
...
00004EDF 0E push cs
00004EE0 E80500 call 0x4ee8
00004EE3 0E push cs
00004EE4 E85101 call 0x5038
00004EE7 CB retf
00004EE8 B80400 mov ax,0x4
00004EEB 50 push ax
00004EEC B80200 mov ax,0x2
00004EEF 50 push ax
00004EF0 9A3404EF03 call 0x3ef:0x434
00004EF5 83C404 add sp,byte +0x4
00004EF8 FF364400 push word [0x44]
00004EFC 8A1E4600 mov bl,[0x46]
00004F00 2AFF sub bh,bh
00004F02 D1E3 shl bx,1
00004F04 FFB71C36 push word [bx+0x361c]
00004F08 B8B834 mov ax,0x34b8
00004F0B 50 push ax
00004F0C 9AE806EF03 call 0x3ef:0x6e8
00004F11 83C406 add sp,byte +0x6
00004F14 B80C00 mov ax,0xc
00004F17 50 push ax
00004F18 B80500 mov ax,0x5
00004F1B 50 push ax
00004F1C 9A3404EF03 call 0x3ef:0x434
00004F21 83C404 add sp,byte +0x4
00004F24 A04600 mov al,[0x46]
00004F27 B103 mov cl,0x3
00004F29 2AE4 sub ah,ah
00004F2B F6F1 div cl
00004F2D 8AD8 mov bl,al
00004F2F 2AFF sub bh,bh
00004F31 D1E3 shl bx,1
00004F33 FFB7D634 push word [bx+0x34d6]
00004F37 B8CC34 mov ax,0x34cc
00004F3A 50 push ax
00004F3B 9AE806EF03 call 0x3ef:0x6e8
00004F40 83C404 add sp,byte +0x4
00004F43 CB retf
....
I can make the code more "concise" by doing this without breaking the code:
...
00004EDF 0E push cs
00004EE0 E80500 call 0x4ee8
00004EE3 0E push cs
00004EE4 E85101 call 0x5038
00004EE7 CB retf
00004EE8 B80400 mov ax,0x4
00004EEB 50 push ax
00004EEC D1E8 shr ax,1
00004EEE 50 push ax; just nice the previous number was double
00004EEF 90 nop
00004EF0 9A3404EF03 call 0x3ef:0x434
00004EF5 83C404 add sp,byte +0x4
00004EF8 FF364400 push word [0x44]
00004EFC 8A1E4600 mov bl,[0x46]
00004F00 2AFF sub bh,bh
00004F02 D1E3 shl bx,1
00004F04 FFB71C36 push word [bx+0x361c]
00004F08 B8B834 mov ax,0x34b8
00004F0B 50 push ax
00004F0C 9AE806EF03 call 0x3ef:0x6e8
00004F11 83C406 add sp,byte +0x6
00004F14 B80C00 mov ax,0xc
00004F17 50 push ax
00004F18 B80500 mov ax,0x5
00004F1B 50 push ax
00004F1C 9A3404EF03 call 0x3ef:0x434
00004F21 83C404 add sp,byte +0x4
00004F24 A04600 mov al,[0x46]
00004F27 B103 mov cl,0x3
00004F29 2AE4 sub ah,ah
00004F2B F6F1 div cl
00004F2D 8AD8 mov bl,al
00004F2F 2AFF sub bh,bh
00004F31 D1E3 shl bx,1
00004F33 FFB7D634 push word [bx+0x34d6]
00004F37 B8CC34 mov ax,0x34cc
00004F3A 50 push ax
00004F3B 9AE806EF03 call 0x3ef:0x6e8
00004F40 83C404 add sp,byte +0x4
00004F43 CB retf
....
But once I shift the function call one byte earlier (instead of having the nop in front of the call 0x3ef:0x434 but call 0x3ef:0x434 then the nop), everything cease to function.
with nop first: dosbox logger output
07D2:0000039F nop
EAX:00000002 EBX:00000054 ECX:00000000 EDX:000003CF ESI:00003431 EDI:00000107 EBP:0000E424 ESP:0000E416 DS:32C3 ES:A000 FS:0000 GS:0000 SS:32C3 CF:0 ZF:0 SF:0 OF:0 AF:1 PF:0 IF:1
07D2:000003A0 call 070C:0434
EAX:00000002 EBX:00000054 ECX:00000000 EDX:000003CF ESI:00003431 EDI:00000107 EBP:0000E424 ESP:0000E416 DS:32C3 ES:A000 FS:0000 GS:0000 SS:32C3 CF:0 ZF:0 SF:0 OF:0 AF:1 PF:0 IF:1
without nop:
07D2:0000039F call 20EF:0434
EAX:00000002 EBX:00000054 ECX:00000000 EDX:000003CF ESI:00003431 EDI:00000107 EBP:0000E424 ESP:0000E416 DS:32C3 ES:A000 FS:0000 GS:0000 SS:32C3 CF:0 ZF:0 SF:0 OF:0 AF:1 PF:0 IF:1
I have been reading assembly, bp, sp, ss and stuff, but I am still stuck. Hence asking the question, which is how to reposition function calls without corrupting code?
E80500
- that'scall rel16
– Peter Cordes00004EF0 9A3404EF03 call 0x3ef:0x434
. the 9A opcode is call far absolute. felixcloutier.com/x86/call – RufusVScall far
itself. But unless you put anop
after it, the address of every later instruction changes, too. ((Update: apparently they are, I missed that detail). There are near calls across it, e.g. changing what's at thecall 0x5038
target will obviously break the program. (To actually save space, you'd want to disassemble withobjconv
into NASM syntax to get labels for branch targets, then re-assemble, to get updated targets for near jumps.) – Peter Cordes