;****************************************************************************
;
;	FILE:		AsmDump.asm
;
;	PURPOSE:	Dump binary data as asm "dd 0ABCh,..."
;
;
;	FUNCTIONS:	AsmDump
;
;	HISTORY:
;
;	CREATED:	2002-03-30 22:39 Basse
;
;	COMMENTS:	If you start writing crackmes in asm, sooner or later you
;			try to write an app with self modifying code or SMC.
;			Then this function can come handy.
;
;			One of the problem with SMC is that you first have to write
;			and debug the code to modify. When this is done you have to 
;			modify it and in some way put it back in the app. This can be done
;			by dumping memory of the SMC and paste it from a hex editor.
;			
;			This can be an rather boring task with a lot of memory dumps
;			and a lot of cut and paste in the hex editor before you get it right.
;			You have find the memory to dump, you have to find the offset
;			in the app where to paste the dumped data. A number of page faults and
;			bluescreens later you get it right (at least that's my experience).
;
;			Wouldn't it be nice to be able to include the modified code in the
;			source file before even compiling the app. Writing crypted code
;			byte byte byte is nothing I can recommend :-). Use this instead.
;
;			How to use it
;			=============
;
;			MySMC proc
;				;Your secret code here
;				ret
;			MySMC endp
;
;			If you want to crypt the proc MySMC you include asmdump.asm (or put it in lib)
;			and call it with filename, start address and length of data to dump.
;
;			invoke AsmDump ,MyDumpFile, DumpStart, DumpLen
;
;			Where MyDumpFile is the address of the file name, DumpStart is the address
;			to start dump from and DumpLen is the number of bytes to dump.
;
;			If you use conditional compilation you can keep both the proc and the crypted 
;			data in the same file.
;
;			MySMC PROTO jump:DWORD	;Proc to dump, arg see below
;
;			.const
;			;DUMP equ TRUE		;Use this when dumping
;			;USEDUMP equ TRUE	;Use this when using the dumped data
;
;			.data
;			MyDumpFile db "paht\Dump.asm",0
;			JumpTable dd offset Proc1	;Se text below
;				dd offset Proc2
;			.code
;			start:
;				ifdef DUMP	;Note conditional compilation, no dot operators.
;					invoke MyCrypt	;Crypt/modify the proc
;					invoke AsmDump ,addr MyDumpFile,offset DumpStart,offset DumpEnd-Offset DumpStart
;				endif
;				
;				ifdef USEDUMP
;					invoke MyDecrypt	;Decrypt/modify the proc
;					invoke MySMC ,addr JumpTable	;Call the proc, now it's ok again
;					invoke MyCrypt		;Optional, crypt the proc again
;				endif
;
;				invoke ExitProcess
;
;			ifdef DUMP	;Conditional compilation
;			DumpStart:	;Start dump from this label
;			MySMC proc jump:DWORD	;Start of proc
;					;Your secret code here
;				ret
;			MySMC endp	;End of proc
;			DumpEnd:	;Dump to this label
;			endif DUMP	;Conditional
;
;			ifdef USEDUMP	;Conditional
;			MySMC proc jump:DWORD
;			;This is, or could be, the crypted and dumped data
;			dd 0AF308CB5h,03216ADD2h,0E79524F9h,03E17F5C0h
;			dd 04E55ADE8h,0874C3009h,04B171AE4h,09132A565h
;			dd 09269767Ch,0EA25B6A5h,0F339B6F9h,057814C51h
;			dd 04AF8B1B0h,05EDFF20Eh,0438A05B2h,0126C586Fh
;			dd 0BB7131DEh,0BC64E7Dh,0DA71AD8h,046C6BC8Bh
;			dd 0797F4AD9h,0281148B5h,06919B4ECh,04E5F4ED0h
;			dd 0BF4AEC25h,04940AC7Eh,042F78E64h,0B94E1A41h
;			MySMC endp
;			endif		;Conditional 
;
;			If you use this approach, it's easy to debug the original
;			code. Crypt it and dump it. Paste it in the source as data definitions,
;			compile again and test if the SMC code works as planned. This only by just 
;			commenting out one of the two conditional flags, DUMP & USEDUMP.
;
;			One thing to have in mind is, if you calling other functions
;			from you SMC proc by the invoke command, the addresses
;			is probably wrong. This because the offset for the call is calculated
;			as an offset to the instruction following the call. With the
;			conditional compilation, the code size may vary, and with this
;			also offset to called procs.
;			A way to avoid this is by defining a jump table in the data section,
;			and passing this jump table to you function as an argument.
;			In the proc you just call dword ptr[jump] for a call to Proc1
;			and call dword ptr[jump+4] for proc2 and so on. 
;			
;			And last, remember to link the app with /section:.text,RWE
;			or you get an error for illegal action (or something) when
;			you try to modify code in the .text section.
;
;			Keep on cracking
;
;			Basse
;****************************************************************************
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\masm32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib


.data
AsmBuf db 0FFh dup(0)
data db ".data",13,10,0
ddFormat1 db "dd 0%Xh",13,10,0
ddFormat2 db "dd 0%Xh,0%Xh",13,10,0
ddFormat3 db "dd 0%Xh,0%Xh,0%Xh",13,10,0
ddFormat4 db "dd 0%Xh,0%Xh,0%Xh,0%Xh",13,10,0
dbFormat db "db 0%Xh",13,10,0

.code
;============================================================================
;
;	FUNCTION:	AsmDump
;
;	PURPOSE:	Dump binary data as asm file "dd 0ABCDh,..."
;
;	ARGUMENTS:	pDumpTo:DWORD filename to dump to
;			pStart:DWORD start dump from here
;			len:DWORD length of data
;
;	COMMENTS:
;
;	PROTOTYPE:	AsmDump PROTO pDumpTo:DWORD,start:DWORD,len:DWORD
;
;	RETURNS:	TRUE/FALSE for result. TRUE if all ok.
;
;	HISTORY:
;
;	CREATED:	2002-03-29 19:48 Basse
;
;============================================================================
AsmDump proc uses ebx ecx edx edi esi pDumpTo:DWORD,pStart:DWORD,len:DWORD
	LOCAL	hFile:DWORD
	LOCAL OddBytes:BYTE
	LOCAL Dwords:DWORD
	LOCAL BytesRW:DWORD

	;Create the dump file
	invoke CreateFileA ,pDumpTo,\
    	GENERIC_WRITE,\
    	0 ,\
    	NULL,\
    	CREATE_ALWAYS,\
    	FILE_ATTRIBUTE_NORMAL,\
    	NULL

    	mov hFile,eax
    	.if eax==INVALID_HANDLE_VALUE
    		mov eax,FALSE
    		jmp done
    	.endif

	mov ecx,len
	mov OddBytes,cl		;Odd bytes
	and OddBytes,3		;Mask out odd bytes 0-3
	shr ecx,2		;To dwords
	mov Dwords,ecx		;Dword counter
	mov esi,pStart

	;Write ".data" at beginning of file
	invoke WriteFile ,hFile,addr data,7,addr BytesRW,NULL

ddDump:
	;dd 1
	lodsd
	push eax
	dec Dwords
	.if Dwords==0
		jmp write1
	.endif

	;dd 2
	lodsd
	push eax
	dec Dwords
	.if Dwords==0
		jmp write2
	.endif

   	;dd 3
	lodsd
	push eax
	dec Dwords
	.if Dwords==0
		jmp write3
	.endif

	;dd 4
	lodsd
	push eax
	dec Dwords
	jmp write4

write1:
	push offset ddFormat1
	jmp write

	;Write 2 dwords
write2:
	;========================================
	; Dword must be pushed in reversed order to get
	; the data definition in the asm file right.
	; If not, you get them in wrong oder.
	;========================================
	pop ebx
	pop eax

	push ebx
	push eax

	push offset ddFormat2	;For wsprintf
	jmp write

	;Write 3 dwords
write3:
	pop ebx
	pop ecx
	pop edx

	push ebx
	push ecx
	push edx

	push offset ddFormat3	;For wsprintf
	jmp write

	;Write 4 dwords
write4:
	pop ebx
	pop ecx
	pop edx
	pop eax

	push ebx
	push ecx
	push edx
	push eax

	push offset ddFormat4	;For wsprintf

write:
	push offset AsmBuf
	call wsprintf
	.if eax==0
		invoke GetLastError	;If things go wrong
	.endif

	invoke lnstr ,addr AsmBuf
	mov ebx,eax
	invoke WriteFile ,hFile,addr AsmBuf,ebx,addr BytesRW,NULL

	.if Dwords==0			;More dwords to write?
		jmp writeOB
	.else
		jmp ddDump
	.endif

	;Write odd bytes 0-3
writeOB:
	.if OddBytes==0
		jmp done
	.endif
	lodsb
	and eax,0FFh
	push eax
	dec OddBytes
	push offset dbFormat	;For wsprinf
	push offset AsmBuf
	call wsprintf
	invoke lnstr ,addr AsmBuf
	mov ebx,eax
	invoke WriteFile ,hFile,addr AsmBuf,ebx,addr BytesRW,NULL
	jmp writeOB

done:
	invoke CloseHandle ,hFile
	ret
AsmDump endp

end

