VOGONS


Writing a patch in ASM *SOLVED*

Topic actions

First post, by Akuma

User metadata
Rank Member
Rank
Member

Fella's,

I have a game that requires a patch to run properly, there is a bug and I do not want to touch the original exe.
After some googling I cant find much information on how to write one in ASM.

EDIT: I want to patch the program in memory, its compressed.

How do I go about this? (I am new to this)

Last edited by Akuma on 2019-11-22, 15:58. Edited 2 times in total.

Reply 1 of 44, by VileR

User metadata
Rank l33t
Rank
l33t

You provide little information on what you're trying to do (or why a copy of the EXE won't do), but I'm inferring that you'd like to patch the game in-memory and then run it?

If so, a good start might be DOS function 4Bh/AL=1 (load but do not execute).

[ WEB ] - [ BLOG ] - [ TUBE ] - [ CODE ]

Reply 4 of 44, by Akuma

User metadata
Rank Member
Rank
Member
VileRancour wrote:

You provide little information on what you're trying to do (or why a copy of the EXE won't do), but I'm inferring that you'd like to patch the game in-memory and then run it?

If so, a good start might be DOS function 4Bh/AL=1 (load but do not execute).

My bad, I wasn't too clear about what I wanted, but yes I want to patch the game in-memory.

ph4nt0m wrote:

If you know the byte sequence to be patched, open the EXE in a hex editor of your choice, do the job and save as a copy.

Forgot to mention that the executable is compressed, the byte sequence cannot be patched. I searched all files for it, I can only find it in memory. Also I think the file loads a couple of others files too. In-memory patching was what I was looking for.

kjliew wrote:

Check out Binary diff/patch utilities http://www.daemonology.net/bsdiff/

Thank you, I will take a look.

Reply 5 of 44, by pantercat

User metadata
Rank Newbie
Rank
Newbie
Akuma wrote:

Forgot to mention that the executable is compressed, the byte sequence cannot be patched. I searched all files for it, I can only find it in memory. Also I think the file loads a couple of others files too. In-memory patching was what I was looking for.

I know what you mean. You have at least two ways to do it.
-write a TSR in ASM that captures some INT used by the executable you want to patch
-or capture INT and use the functions "Modify Allocated Memory Block" (INT 21h/4Ah) and "EXEC/Load and Execute Program" (INT 21h/4Bh).

Not long ago I wrote a couple of loaders to crack two games. The posts are in spanish, but I think the source code in ASM could be useful.

http://www.vlan7.org/2019/02/ms-dos-cracking- … n-cargador.html
http://www.vlan7.org/2019/04/haciendo-un-carg … oader-para.html

Happy reversing.

Reply 6 of 44, by Akuma

User metadata
Rank Member
Rank
Member

That is extremely helpful, thank you sir.

What I got so far writing my first ASM program, is a simple 'Hello world'.
1. I need to figure out how to patch in-memory
2. When to patch, the executable needs to decompress itself first, otherwise whats the point.
3. I would probably have to wait for the OEP, then patch it, if it doesnt decompress and loads something else too.

.MODEL TINY
.DATA
msg db "Hello, world!$",0dh ; [var] [declare bytes] "message$", no. bytes in hex
.CODE
org 100h ; required by com file
START:
mov ah, 09 ; int21h: function print text
lea dx, msg ; refer to msg to print
int 21h

mov ax, 4c00h ; return to dos
int 21h
END START

I got it to produce a nice small com file with TASM.

C:\>DEL TEST.MAP

C:\>DEL TEST.OBJ

C:\>DEL TEST.COM

C:\>TASM\BIN\TASM.EXE TEST.ASM TEST.OBJ
Turbo Assembler Version 4.1 Copyright (c) 1988, 1996 Borland International

Assembling file: TEST.ASM
Error messages: None
Warning messages: None
Passes: 1
Remaining memory: 466k


C:\>TASM\BIN\TLINK.EXE /t TEST.OBJ
Turbo Link Version 7.1.30.1. Copyright (c) 1987, 1996 Borland International

C:\>test
Hello, world!
C:\>

Reply 7 of 44, by VileR

User metadata
Rank l33t
Rank
l33t

A generic approach for in-memory patching is, if you tell DOS to load-but-do-not-execute the file, it is in memory and you get the segment address of the program's PSP; from there you can write to whatever locations you want within the program code. Of course, it's a good idea to test your planned patch first by using something like the DOSBox debugger to directly modify the routine in memory.

A compressed EXE is a whole different ballpark however. You might want to use some analysis tools to find out more (identify the packing scheme and perhaps find the original entry point). Ben Castricum's UNP may be a good place to start, along with its trace command "t".

I haven't tackled this before, but if I were to try writing such a loader, my approach might be to patch the unpacking code in-memory, so that once it's done unpacking (and is ready to jump to the OEP) it returns control to my loader. Then a second routine would patch the already-unpacked code in RAM with the required changes and make the jump.

Not sure if this is the best kind of project to tackle when at the hello-world level, but I did worse when I had less, so good luck. 😁

[ WEB ] - [ BLOG ] - [ TUBE ] - [ CODE ]

Reply 8 of 44, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

A common method with patch TSRs/loaders is to set up an interrupt handler and watch for a particular function that is called after the target program's code is decrypted and/or decompressed, then modify memory relative to the interrupt's return address that was pushed on the stack. For example, programs often call INT 21h/30h to get the DOS version as one of the first things they do, but that's not necessarily the ideal function to watch for. It's a good idea to check the memory you intend to modify to see if it contains expected values, which ensures you're patching the right thing and also not performing the patch more than once.

Reply 9 of 44, by BloodyCactus

User metadata
Rank Oldbie
Rank
Oldbie

yeah better to hook an interrupt vector, if the app was written in C you know its going to do a dos version check or anything is garunteed to do a memory realloc (0x4A). you can hook that and patch what you need to patch.

--/\-[ Stu : Bloody Cactus :: [ https://bloodycactus.com :: http://kråketær.com ]-/\--

Reply 10 of 44, by Akuma

User metadata
Rank Member
Rank
Member
VileRancour wrote:
A generic approach for in-memory patching is, if you tell DOS to load-but-do-not-execute the file, it is in memory and you get t […]
Show full quote

A generic approach for in-memory patching is, if you tell DOS to load-but-do-not-execute the file, it is in memory and you get the segment address of the program's PSP; from there you can write to whatever locations you want within the program code. Of course, it's a good idea to test your planned patch first by using something like the DOSBox debugger to directly modify the routine in memory.

A compressed EXE is a whole different ballpark however. You might want to use some analysis tools to find out more (identify the packing scheme and perhaps find the original entry point). Ben Castricum's UNP may be a good place to start, along with its trace command "t".

I haven't tackled this before, but if I were to try writing such a loader, my approach might be to patch the unpacking code in-memory, so that once it's done unpacking (and is ready to jump to the OEP) it returns control to my loader. Then a second routine would patch the already-unpacked code in RAM with the required changes and make the jump.

Not sure if this is the best kind of project to tackle when at the hello-world level, but I did worse when I had less, so good luck. 😁

Unpacking has never been a real problem for me, although there are some wonderful exceptions that still haunt me to this day.
(The INC loader from the Operation Wolf release iirc, thats a real nasty one)..ermm back to the topic at hand.

I plan on doing the following:
1. Write hello world
2. Write a patch for hello world, to: Hello patch!, that would hopefully cover the basics.
(debugging with dosbox debugger)
3. Deal with the unpacking, find the oep.
4. Write the patch, test and verify it.
5. Praise and glory!

I will take that luck sir!

ripsaw8080 wrote:

A common method with patch TSRs/loaders is to set up an interrupt handler and watch for a particular function that is called after the target program's code is decrypted and/or decompressed, then modify memory relative to the interrupt's return address that was pushed on the stack. For example, programs often call INT 21h/30h to get the DOS version as one of the first things they do, but that's not necessarily the ideal function to watch for. It's a good idea to check the memory you intend to modify to see if it contains expected values, which ensures you're patching the right thing and also not performing the patch more than once.

I read through some code today from 'pantercat', they hooked into INT10. I think it was something like mov ax,3510h or something.
This is going to be hard on my brain =). Yes, one iteration, that's a good one. Noted.

BloodyCactus wrote:

yeah better to hook an interrupt vector, if the app was written in C you know its going to do a dos version check or anything is garunteed to do a memory realloc (0x4A). you can hook that and patch what you need to patch.

Thank you, although most of this is 'hocus pocus' to me now. I'll get it to work eventually. I always do.😁

Reply 12 of 44, by BloodyCactus

User metadata
Rank Oldbie
Rank
Oldbie

NASM unless your compiling it on a 286 or something. TASM would be the fallback if you dont want to learn NASM.

--/\-[ Stu : Bloody Cactus :: [ https://bloodycactus.com :: http://kråketær.com ]-/\--

Reply 13 of 44, by Akuma

User metadata
Rank Member
Rank
Member

Hmm...its kinda working, but with a couple of issues.

1. It only works on my machine in dosbox, because its patching absolute, not relative ?
2. Its not stable, if I exit the game, the OS becomes unstable, it hangs second time around ?
3. I understand 80% of what I pieced together ?

If anyone can shed some light on this, Im all ears 😁

OVL     segment for 'code'
assume cs:OVL, ds:OVL

org 100h ; output a .COM program

START:
mov sp, 1024 ; set stack pointer (size in bytes)
mov bx, 1024/16 ; set bx=bytes/16, es=mem segment
mov ah, 4ah ; change memory allocation
int 21h ; commit

mov ax, 3521h ; get OLDINT
int 21h ; commit

mov OLDINT[0], bx ; safe OLDINT to call later
mov OLDINT[2], es ; ?? no idea ??

mov ax, 2521h ; set NEWINT
lea dx, NEWINT ; with pointer to NEWINT proc far routine
int 21h

lea bx, EXEC_INFO ; ?? no idea ??
lea dx, FILENAME ; pointer to FILENAME
mov ax, 4b00h ; load and run FILENAME
int 21h ; commit

mov ax, 4c00h ; ah=exit, al=return code
int 21h ; commit

OLDINT dw 0,0 ; original INT

NEWINT proc far
pushf ; safe flags
cmp dx, 029DAh ; compare if INT is called with X
jnz EXIT ; yes: goto CHECK, no: goto EXIT

CHECK:
push bp ; safe 'bp'
mov bp, sp ; 'bp' points to top of the stack
push es ; safe 'es'
mov es, [bp] ; copy bp to 'es'
cmp word ptr es:[0XXXX], 0XXXXh ; if data matches search query
jne RESTORE ; yes: goto PATCH , no: goto RESTORE

PATCH: ; --------------- patch -------------------
mov byte ptr es:[0XXXX], 0XXh ; change byte offset es:xxxx to xxxh
; ------------- end patch -----------------

RESTORE:
pop es ; restore 'es'
pop bp ; restore 'bp'

EXIT:
popf ; restore flags
jmp dword ptr cs:[OLDINT] ; jump to OLDINT

NEWINT endp

FILENAME db "GAME.EXE",0 ; program to execute
EXEC_INFO db 22 DUP (0) ; ?? no idea : 22x 00h for what ??
Show last 4 lines

OVL ends
end START

Is there a way to use a fixed font for the code window ?

Attachments

  • Filename
    PATCHASM.ZIP
    File size
    996 Bytes
    Downloads
    139 downloads
    File license
    Fair use/fair dealing exception

Reply 14 of 44, by VileR

User metadata
Rank l33t
Rank
l33t

At a first glance, it looks like you aren't setting up the interrupt chain correctly. Keep in mind that an "INT XXh" instruction pushes the flags onto the stack, then the far return address (dword), then jumps to the interrupt handler. The handler routine normally ends with an IRET instruction, which does the reverse (pops the CS:IP then the flags, and resumes).

So to properly call the old interrupt handler, your EXIT section should emulate the INT instruction by pushing the flags (PUSHF) and the far return address (e.g. PUSH CS, then CALL a far jump to the old handler, which pushes IP during the CALL). Then once the old handler returns (with an IRET), your NEWINT should also terminate with an IRET, since it also gets called with "INT XXh" and has to preserve the expected state. This also means there's no need for the existing PUSHF/POPF pair.

This might be some relevant reading material: https://web.archive.org/web/20080222000741/ht … abs/roehrl.html. There may be other issues, but I'd start with that.

(Edit:) also, your EXEC_INFO structure should contain a parameter block as described here: http://faydoc.tripod.com/structures/15/1590.htm - it's probably a good idea to actually set up the values. 😉 More info here: http://www.ctyme.com/intr/rb-2939.htm

[ WEB ] - [ BLOG ] - [ TUBE ] - [ CODE ]

Reply 15 of 44, by BloodyCactus

User metadata
Rank Oldbie
Rank
Oldbie

you cant do a 4c exit whilst trapping int21, you need to release your int21 hook back to its original values before you exit.
your execinfo needs to be valid with fcb/psp pointers, cli, etc

--/\-[ Stu : Bloody Cactus :: [ https://bloodycactus.com :: http://kråketær.com ]-/\--

Reply 16 of 44, by Akuma

User metadata
Rank Member
Rank
Member

I had some time on my hands, so I started coding again:
So I wrote a filepatcher 😁 (in 'nasm' as suggested)

How can I make this better ?

[bits 16]                           ;16 bit code generation
[org 0x0100] ;com-file = start address 0x100
[section .text] ;code section

start:
jmp begin

%macro printf 1
mov dx,%1 ;select message set in parameter %1
mov ah,09h ;select print function
int 21h ;commit
%endmacro

;http://spike.scu.edu.au/~barry/interrupts.html
;open,close,seek,write
%macro patch 4
mov ax,04200h ;select seek file, pos: start of file
mov bx,[handle] ;bx = file handle
mov cx,%1 ;cx = segment
mov dx,%2 ;dx = offset
int 21h ;commit
jc error ;if carry flag set jump to error
mov ah,40h ;select write file
mov bx,[handle] ;bx = file handle
mov cx,%3 ;cx = no. bytes to write
mov dx,%4 ;ds:dx -> data to write
int 21h ;commit
jc error ;if carry flag set jump to error
%endmacro

;function: open file
fopen:
mov dx,filename ;load filename into dx
mov ax,03D02h ;select open file
int 21h ;commit
jc error ;if carry flag set jump to error
mov [handle],ax ;store file handle
ret ;return to caller

; function: close file
fclose:
mov ax,03E00h ;select close file
int 21h ;commit
jc error ;if carry flag set jump to error
ret ;return to caller

error:
printf error1
jmp exit ;jump to exit

begin:
printf title ;print message
call fopen ;open file to be patched
patch p1seg,p1ofs,p1len,p1dat
patch p2seg,p2ofs,p2len,p2dat
patch p3seg,p3ofs,p3len,p3dat
call fclose ;close file
printf success ;print message

exit:
Show last 26 lines
mov         ax,04C00h               ;select return to dos
int 21h ;commit

[section .data]
filename db 'filename.exe',0 ;target filename
title db 'Patch for program: blah blah blah',0AH,0DH,'$'
error1 db 'Failure',0AH,0DH,'$'
success db 'Success',0AH,0DH,'$'
handle dw 0

;patch1
p1seg equ 00000h ;segment: most significant byte
p1ofs equ 00000h ;offset: least significant byte
p1dat db 0x90,0x90,0 ;string
p1len equ $-p1dat ;length

p2seg equ 00000h ;segment: most significant byte
p2ofs equ 00100h ;offset: least significant byte
p2dat db 0x90,0x90,0 ;string
p2len equ $-p2dat ;length

p3seg equ 00000h ;segment: most significant byte
p3ofs equ 00200h ;offset: least significant byte
p3dat db 0x90,0x90,0 ;string
p3len equ $-p3dat ;length

BTW:is there a way to use a fixed size font for the code window ?

Reply 17 of 44, by Akuma

User metadata
Rank Member
Rank
Member

The file patcher was a good stepping stone, but I'm still stuck with the loader.
Now I switched to NASM as BloodyCactus suggested, but I'm running into some conversion trouble.

So I adapted the code as best I knew how according to this: http://left404.com/2011/01/04/converting-x86- … masm-to-nasm-3/ , but I can't get it to work.

Thus I grabbed one of pantercat's loaders, tried a conversion, but dosbox screams bloody murder on execution.
error: illegal read from cs:ip (continuously), obviously I messed up somewhere but I cant figure out where as
the code compiles. My best guess would be the exit: jump but this isnt jeopardy 😁

Some help would be appreciated:

[bits 16]                           ;16 bit code generation
[org 0x0100] ;com-file = start address 0x100
[section .text] ;code section

START:
jmp INITCODE ; Codigo de inicializacion

NEWINT:
cmp ah, 1 ; Se ha llamado a INT 16h con la AH=1 ?
jnz EXIT ; NO: saltamos a la INT 16h del sistema
cmp word [0097], 0d75h ; ¿Tenemos en [0097] los opcodes 75 0D?
jnz EXIT ; NO: saltamos a la INT 16h del sistema
mov word [0097], 9090h ; escribimos los 90 90 en [0097]

EXIT:
jmp dword [cs:OLDINT] ; Saltamos a la INT 16h del sistema

INITCODE:
mov sp, SIZE ; redefinimos la pila
mov bx, SIZE/16
mov ah, 4Ah ; redimensionamos bloque memoria
int 21h

mov dx, MSG
mov ah, 9 ; Escribimos texto por pantalla
int 21h

xor ah, ah ; Esperamos pulsacion de tecla
int 16h

mov ax, 3516h ; Queremos el valor del puntero a la INT16
int 21h
mov word [OLDINT+0], bx ; Guardamos puntero a la INT 16h
mov word [OLDINT+2], es ; (necesitaremos llamarla despues)

mov ax,2516h ; Sobreescribimos en el vector INT 16h
mov dx, NEWINT ; un puntero a nuestra rutina NEWINT
int 21h

mov bx, EXEC_INFO
mov dword [bx+00h], 0
mov dword [bx+02h], 80h ; PSP
mov word [bx+04h], cs
mov word [bx+06h], 5Ch ; FCB 0
mov word [bx+08h], cs
mov word [bx+0ah], 6Ch ; FCB 1
mov word [bx+0ch], cs

mov dx, FILENAME
mov ax, 4b00h
int 21h ; cargar y ejecutar programa

push cs
pop ds ; DS = CS
mov ax, 4c00h ; terminar
int 21h

[section .data]
SIZE EQU 1024 ; este programa y su pila caben en 1 KB
FILENAME db "COLMENA.EXE",0 ; programa a ejecutar
Show last 6 lines
EXEC_INFO   times 22 db (0h)
MSG db 0dh,0ah
db "La Colmena CGA/EGA/Hercules/VGA-16 loader.",0dh,0ah
db "2019, vlan7",0dh,0ah,"$"
OLDINT dw 0,0 ; Espacio para el puntero a la INT 16h

Reply 18 of 44, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

The most obvious issue is that INT 16 is not "unhooked" before the program terminates; but that would only causes a crash after termination when the program's memory block is released, leaving the INT 16 handler code likely to be overwritten by the next program executed.

A more subtle issue is that INT 21/35 is changing the value of ES, and INT 21/4B uses ES:BX as a pointer to the child program's parameter block. So, you should have a PUSH CS / POP ES or so after the ES value has been stored in memory.

Those are the issues that I notice at a glance, not necessarily the only issues.

BTW, when the subject matter is applicable to DOS programming in general and not just for DOSBox, such topics are best posted in Milliways. 😉

Reply 19 of 44, by cyclone3d

User metadata
Rank l33t++
Rank
l33t++

What about using one of those old DOS Game cheater programs that you load up before your game. Then you load the game and press a hotkey sequence and it drops to the game cheater and lets you edit memory values and then go back to the game.

Generally used for changing lives, ammo, etc, but I would think it would work for patching as well.

I think some of them might have let you save profiles.. don't remember though. I haven't used one in years.

Yamaha modified setupds and drivers
Yamaha XG repository
YMF7x4 Guide
Aopen AW744L II SB-LINK