papers
+HCU papers
courtesy of reverser'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 than 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 :(), reverser+ (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 than 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)

red homepage red links red anonymity +ORC red students' essays red academy database red bots wars
red antismut red tools red cocktails red javascript wars red search_forms red mail_reverser
red Is reverse engineering illegal?