+HCU papers
courtesy of fravia's page of reverse engineering
THE RESURRECTION OF ASSEMBLY PROGRAMMING - 1
(see preceding essay: winasm_O.htm)
-
-----=========-----
-------==========================================----------
---------===_masta_'s Tutorial on Win32 ASM Coding Part 1===----------
-------==========================================----------
--==INTR0==--
-[Hi!]-
After the part 0 I got some mail saying that it was a good idea to do
a windows-based revival of the "old art" of assembly language
programming. We all know that DOS is about to die (not many (if any)
of us are happy about that), but unfortunately we can't change this
fact.
--==WHAT IS NEEDED?==--
1. Texteditor
2. TASM 5.0, complete with libs, etc.
3. Win32-API-Reference (Win32.HLP)
I assume you have basic knowledge of assemly, althought most of the
stuff is easy to grasp.
--==SENSE OF THIS PROJECT==--
We want to code a "generic patcher" which is also known as a
"Search-and-Destroy-Patcher".
Since many people may not know what to expect from a Generic-Patcher,
I want to explain it shortly. It is a patcher, which is not only able
to patch a certain version of a program, but future versions also, if
they are nearly the same. This is done by searching for a certain
byte-pattern (and writing a new one) and not by patching some offset,
which makes this kind more universal to use.
Since most likely the protection-scheme is not changed by the coder,
this bytes of this routine may have another offset-address in the
newer (older) version, but the bytes will be the same.
That's the trick =).
--==LET'S GO!==--
OK, first we think about the main structure of our program, then we
think about which functions we use and last but not least, we write
the program.
1. Intro - Little intro, presented in a MessageBox
2. Open File - Set file-handle. If file not exist -> MessageBox
3. Get Filesize
4. Allocate memory - Allocate memory equal to the filesize.
If error -> MessageBox
5. Read File - copy complete file into allocated memory
6. Search Bytes - Determination of the offset of the bytepattern.
If errors -> MessageBox
7. Set Filepointer
to offset
8. Overwrite File - Patch of the file. Success -> MessageBox
with new bytes
9. Close File - Cleanup!
Deallocate Mem
Quit
--==API-FUNCTIONS==--
- All messages will be presented by a messagebox, i.e. 'MessageBoxA'.
- For opening the file we will use the 'CreateFileA'-function, which
is more complex than the 'OpenFile, but also more flexible to use.
- To close we will use 'CloseHandle'.
- The filesize we get via 'GetFileSize'
- We allocate the mem with the help of 'GlobalAlloc'; set it free
again with 'GlobalFree'
- Logically we read the file with 'ReadFile' and write it with
'WriteFile'
- The Filepointer can be set with 'SetFilePointer'
- To quit we use 'ExitProcess'
--==THE BYTE SEARCH==--
This is the heart of our patcher. With the help of this little routine
the target-file is searched for a byte pattern, which will be changed
later. I will just explain it shortly, because the most you can get
out of the code.
OK, we first load the size of the file (the alloc. memory) into ECX to
set a value for the "REPNZ"-command; also the first byte of the search-
pattern is written into AL and ESI is set to the address of the
original values.
With 'REPNZ SCASB' the value of AL is compared to the value of the
memory address, which EDI points to (EDI is incremented by 1). The
'REPNZ'-command repeats the following 'SCASB' as long as either ECX=0
or the compared values are equal (FL Z=1).
If the values are equal ECX is loaded with the length of the patch,
EDI is decremented by 1, because the 'SCASB' already counted one byte
ahead.
The following 'REPZ CMPSB' repeats 'CMPSB' (compares the address of
[ESI] with the one of [EDI]) as long as either ECX=0 or the value
differs.
--==THE PATCH ITSELF==--
Now quickly some stuff about the patch routine itself.
First the offset is calculated by incrementing ECX (byte-counter) by 1
and this value we subtract from the total filesize:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(FILESIZE) - (BYTES UNTIL THE END OF FILE) = ACTUAL OFFSET
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
This value is put on the stack, as well as the file-handle to later
CALL the function 'SetFilePointer' to set the filepointer to our offset.
After that the buffer for the written bytes (bwrite), the length of the
patch, the offset of the new bytes and the file-handle is PUSHed and
the API-function 'WriteFile' is CALLed.
--==THE SOURCE==--
Maybe a bit complex, but I guess still easy to understand =) ...
;-------------------------------===START===---------------------------
; set a couple of options for the assembler
.386P
Locals
jumps
.Model Flat ,StdCall
mb_ok equ 0
hWnd equ 0
FILE_ATTRIBUTE_NORMAL equ 080h
OPEN_EXISTING equ 3
GENERIC_READ equ 80000000h
GENERIC_WRITE equ 40000000h
; --==declaration of all used API-functions==--
extrn ExitProcess : PROC ;procedure to end the program
extrn MessageBoxA : PROC ;procedure to show a MessageBox
extrn CreateFileA : PROC ; " ... to open a file
extrn ReadFile : PROC ;read a block of a file
extrn WriteFile : PROC ;write a block into a file
extrn CloseHandle : PROC ;close file
extrn GetFileSize : PROC ;get the filesize
extrn GlobalAlloc : PROC ;allocate memory
extrn GlobalFree : PROC ;set (free) memory
extrn SetFilePointer : PROC ;set the filepointer
; --==here begins our Data==--
.Data
caption db "_masta_'s essay on Win32-ASM-Coding, part 1",0
;captionstring, 0-terminated
text db "Hi, nice to CU again",13,10
db "This tut will describe you how to make",13,10
db "Win32-ASM Search and Destroy patchers",0
;introtext, 0-terminated
err_cap db "ERROR",0 ;caption for errormessage
openerr db "Error on opening File",0 ;errortext opening file
memerr db "Error on allocating memory",0 ;errortext alloc. memory
byterr db "File is here, but i can't find the original bytes",0
;error while bytesearch
readycap db "Ready",0 ;caption for 'done'
readytxt db "Ok, file is patched",0 ;text for 'done'
file db "make.old",0 ;what file we want to patch?
org_val db "Xmas'97" ;original values
new_val db "_masta_" ;new values
len equ $-new_val ;how many values (length)
;org_val and new_val must be equal
fhandle dd ? ;variable for the filehandle
fsize dd ? ;variable for the filesize
memptr dd ? ;pointer to allocated memory
bread dd ? ;number of read bytes
bwrite dd ? ;number of written bytes
;--==and here we start with our code==--
.Code
Main:
push mb_ok ;PUSH value for 'uType'
push offset caption ;PUSH pointer to caption
push offset text ;PUSH pointer to Text
push hWnd ;PUSH Masterhandle
call MessageBoxA ;CALL MessageBoxA
push 0 ;for Win95 always 0
push FILE_ATTRIBUTE_NORMAL ;standard Fileattributes
push OPEN_EXISTING ;open existing file
push 0 ;no Security-attributes
push 0 ;disable Share-Mode
push GENERIC_READ + GENERIC_WRITE ;read- and writeaccess
push offset file ;offset of the filename
Call CreateFileA ;open file
mov fhandle,eax ;save filehandle
cmp eax,0FFFFFFFFh ;if eax=FFFFFFFF then
error
jnz file_is_here
push mb_ok
push offset err_cap
push offset openerr
push hWnd
call MessageBoxA ; showerrormessage
jmp end_ ; jump to end
file_is_here: ;file is there, so go on
push 0 ;can be 0, if the filesize is less
then 4,3 GB :)
push fhandle ;PUSH filehandle
Call GetFileSize ;get the filesize
mov fsize,eax ;save the filesize
push fsize ;PUSH filesize=size of the buffer
push 0 ;0=GMEM_FIXED -> fixed memory-area
Call GlobalAlloc ;allocate as much as memory as filesize
mov memptr,eax ;save pointer to memory-area
cmp eax,0 ;if eax=0, then there were errors
jnz mem_ok
push mb_ok
push offset err_cap
push offset memerr
push hWnd
call MessageBoxA
jmp end_kill_handle ;end program, close file b4
mem_ok: ;memory is allocated -> next step
push 0 ;set to 0 in most cases
push offset bread ;pointer to number of read bytes
push fsize ;read how many bytes?,
fsize=whole file
push memptr ;save where? ->allocated memory
push fhandle ;filehandle
Call ReadFile ;read file!
read_ok:
mov edi,memptr ;set EDI to memory-area
mov ecx,fsize ;set ECX (for repnz) to filesize
mov esi,offset org_val ;set ESI to the string to find
mov al, byte ptr [esi] ;load AL with the first byte
loop_:
repnz scasb ;repeat until ECX=0 or AL equals
;the value of the byte [EDI], EDI is
;incremented by 1 every run
cmp ecx,0 ;If ECX=0, nothing is found
jz not_found
here_is_something: ;found matching byte
push ecx ;save register
push edi
push esi
dec edi ;EDI-1, cos REPNZ SCASB is one step too far
mov ecx,len ;ECX=length of the patch
repz cmpsb ;repeat until the values in the memory of
;[EDI] and [ESI] are different,
;or ecx=0
cmp ecx,0 ;If ecx=0, then the org_val is in memory
jz patch ;->jump to patcher
not_that: ;it is not yet here
pop esi ;POP ESI
pop edi
pop ecx
jmp loop_ ;search next byte
patch: ;start of the patcher
pop esi ;POP registers
pop edi
pop ecx
dec edi ;EDI-1
inc ecx ;ECX+1
mov eax,fsize
sub eax,ecx ;compute Offset
push 0 ;offset from the beginning of the file
push 0 ;is 0, if file <4,3GB
push eax ;offset
push fhandle ;filehandle
call SetFilePointer ;set FilePointer
push 0 ;normally 0
push offset bwrite ;how many bytes where written?
push len ;length of the bytes to write
push offset new_val ;offset to new values
push fhandle ;filehandle
Call WriteFile ;write block to file
push mb_ok
push offset readycap
push offset readytxt
push hwnd
call MessageBoxA ;OK, patch is done!
jmp end_kill_all ;END! Cleanup!
not_found:
push mb_ok
push offset err_cap
push offset byterr
push hWnd
call MessageBoxA ;the bytes where not in the file
end_kill_all:
push memptr ;pointer to Memoryarea
call GlobalFree ;enable (free) memory
end_kill_handle:
push fhandle ;PUSH filehandle
call CloseHandle ;CloseHandle
end_:
CALL ExitProcess ;Quit program
End Main ;end of code, JUMP-spot (main)
;-----------------------==END OF SOURCE==----------------------------
OK, done!
To assemble it, just run the included 'make.bat' ...
--==A LITTLE NOTICE==--
Until now I didn't see a reason to use include-files
And well, the INC-files coming with TASM are not very
complete, BUT if there is anybody out there possessing
complete *.incs then don't hesitate to send'em to me!
--==END==--
OK, I think this time we did something really useful, not just a MessageBox
like in my first essay, but a real every-day-tool of a cracker.
The source can be freely used naturally and maybe there are some things you
can optimize, especially concerning the search-routine (Hi Fungus ;)), but
I think for learning-purpose it is OK.
For a little challenge:
--> You could search for the first 4 bytes from the start
OK, I hope that my mailbox (masta_t@USA.NET) will explode soon
(CRITICS ARE WELCOME) and I will see ya all next time ... =)
By the way I am trying to establish an IRC-channel about these facts ...
--> #win32asm
I just hope there are enough people interested in this stuff and also in
giving their knowledge to others.
--==GREETINX==--
VucoeT (Translator and Designer(:])), scut (Idea is from your
DSP), |caligo| (bad news about you :(), fravia+ (best on the
web), +Aescalapius (nice Bytepatcher) not4you (we Ossis must
stick together ;)), fungus (something to optimze), Quest,
Silvio, TheDoctor, everyone on #LAC and #cracking4newbies
and to every cracker around the world.
--==WISE WORDS==--
-------====================-- --====================--------
------======everybody was a lamer, before they become ELITE======-------
-------====================-- --====================--------
-----==========-----
here follows tut.asm
; set a couple of options for the assembler
.386P
Locals
jumps
.Model Flat ,StdCall
mb_ok equ 0
hWnd equ 0
FILE_ATTRIBUTE_NORMAL equ 080h
OPEN_EXISTING equ 3
GENERIC_READ equ 80000000h
GENERIC_WRITE equ 40000000h
; --==declaration of all used API-functions==--
extrn ExitProcess : PROC ;procedure to end the program
extrn MessageBoxA : PROC ;procedure to show a MessageBox
extrn CreateFileA : PROC ; " ... to open a file
extrn ReadFile : PROC ;read a block of a file
extrn WriteFile : PROC ;write a block into a file
extrn CloseHandle : PROC ;close file
extrn GetFileSize : PROC ;get the filesize
extrn GlobalAlloc : PROC ;allocate memory
extrn GlobalFree : PROC ;set (free) memory
extrn SetFilePointer : PROC ;set the filepointer
; --==here begins our Data==--
.Data
caption db "_masta_'s essay on Win32-ASM-Coding, part 1",0
;captionstring, 0-terminated
text db "Hi, nice to CU again",13,10
db "This tut will describe you how to make",13,10
db "Win32-ASM Search and Destroy patchers",0
;introtext, 0-terminated
err_cap db "ERROR",0 ;caption for errormessage
openerr db "Error on opening File",0 ;errortext opening file
memerr db "Error on allocating memory",0 ;errortext alloc. memory
byterr db "File is here, but i can't find the original bytes",0
;error while bytesearch
readycap db "Ready",0 ;caption for 'done'
readytxt db "Ok, file is patched",0 ;text for 'done'
file db "make.old",0 ;what file we want to patch?
org_val db "Xmas'97" ;original values
new_val db "_masta_" ;new values
len equ $-new_val ;how many values (length)
;org_val and new_val must be equal
fhandle dd ? ;variable for the filehandle
fsize dd ? ;variable for the filesize
memptr dd ? ;pointer to allocated memory
bread dd ? ;number of read bytes
bwrite dd ? ;number of written bytes
;--==and here we start with our code==--
.Code
Main:
push mb_ok ;PUSH value for 'uType'
push offset caption ;PUSH pointer to caption
push offset text ;PUSH pointer to Text
push hWnd ;PUSH Masterhandle
call MessageBoxA ;CALL MessageBoxA
push 0 ;for Win95 always 0
push FILE_ATTRIBUTE_NORMAL ;standard Fileattributes
push OPEN_EXISTING ;open existing file
push 0 ;no Security-attributes
push 0 ;disable Share-Mode
push GENERIC_READ + GENERIC_WRITE ;read- and writeaccess
push offset file ;offset of the filename
Call CreateFileA ;open file
mov fhandle,eax ;save filehandle
cmp eax,0FFFFFFFFh ;if eax=FFFFFFFF then
error
jnz file_is_here
push mb_ok
push offset err_cap
push offset openerr
push hWnd
call MessageBoxA ; showerrormessage
jmp end_ ; jump to end
file_is_here: ;file is there, so go on
push 0 ;can be 0, if the filesize is less
then 4,3 GB :)
push fhandle ;PUSH filehandle
Call GetFileSize ;get the filesize
mov fsize,eax ;save the filesize
push fsize ;PUSH filesize=size of the buffer
push 0 ;0=GMEM_FIXED -> fixed memory-area
Call GlobalAlloc ;allocate as much as memory as filesize
mov memptr,eax ;save pointer to memory-area
cmp eax,0 ;if eax=0, then there were errors
jnz mem_ok
push mb_ok
push offset err_cap
push offset memerr
push hWnd
call MessageBoxA
jmp end_kill_handle ;end program, close file b4
mem_ok: ;memory is allocated -> next step
push 0 ;set to 0 in most cases
push offset bread ;pointer to number of read bytes
push fsize ;read how many bytes?,
fsize=whole file
push memptr ;save where? ->allocated memory
push fhandle ;filehandle
Call ReadFile ;read file!
read_ok:
mov edi,memptr ;set EDI to memory-area
mov ecx,fsize ;set ECX (for repnz) to filesize
mov esi,offset org_val ;set ESI to the string to find
mov al, byte ptr [esi] ;load AL with the first byte
loop_:
repnz scasb ;repeat until ECX=0 or AL equals
;the value of the byte [EDI], EDI is
;incremented by 1 every run
cmp ecx,0 ;If ECX=0, nothing is found
jz not_found
here_is_something: ;found matching byte
push ecx ;save register
push edi
push esi
dec edi ;EDI-1, cos REPNZ SCASB is one step too far
mov ecx,len ;ECX=length of the patch
repz cmpsb ;repeat until the values in the memory of
;[EDI] and [ESI] are different,
;or ecx=0
cmp ecx,0 ;If ecx=0, then the org_val is in memory
jz patch ;->jump to patcher
not_that: ;it is not yet here
pop esi ;POP ESI
pop edi
pop ecx
jmp loop_ ;search next byte
patch: ;start of the patcher
pop esi ;POP registers
pop edi
pop ecx
dec edi ;EDI-1
inc ecx ;ECX+1
mov eax,fsize
sub eax,ecx ;compute Offset
push 0 ;offset from the beginning of the file
push 0 ;is 0, if file <4,3GB
push eax ;offset
push fhandle ;filehandle
call SetFilePointer ;set FilePointer
push 0 ;normally 0
push offset bwrite ;how many bytes where written?
push len ;length of the bytes to write
push offset new_val ;offset to new values
push fhandle ;filehandle
Call WriteFile ;write block to file
push mb_ok
push offset readycap
push offset readytxt
push hwnd
call MessageBoxA ;OK, patch is done!
jmp end_kill_all ;END! Cleanup!
not_found:
push mb_ok
push offset err_cap
push offset byterr
push hWnd
call MessageBoxA ;the bytes where not in the file
end_kill_all:
push memptr ;pointer to Memoryarea
call GlobalFree ;enable (free) memory
end_kill_handle:
push fhandle ;PUSH filehandle
call CloseHandle ;CloseHandle
end_:
CALL ExitProcess ;Quit program
End Main ;end of code, JUMP-spot (main)
homepage
links
anonymity
+ORC
students' essays
academy database
bots wars
antismut
tools
cocktails
javascript wars
search_forms
mail_fravia
Is reverse engineering illegal?