papers
+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)

redhomepage red links red anonymity +ORC redstudents' essays redacademy database redbots wars
redantismut redtools redcocktails redjavascript wars redsearch_forms redmail_fravia
redIs reverse engineering illegal?