- -----=========----- -------==========================================---------- ---------===_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==---------------------------- ;-------------------------------START---------------------------make.bat tasm32 /mx /m3 /z /q tut tlink32 -x /Tpe /aa /c tut,tut,, import32.lib ;--------------------------------END----------------------------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 there 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======------- -------====================-- --====================-------- -----==========----- -