;########################################################################################################
; NagStudio v 1.0 by Latigo
; You are free to touch,modify,use,tweak the source code as much as you like giving the due credit :)
; Thanks to Win32asm.cjb.net and Win32asmboard.cjb.net 
; Bye!
; latigo@ciudad.com.ar
; www.latigo.cjb.net
;########################################################################################################

.386
.MODEL FLAT,STDCALL
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc
include comdlg32.inc

includelib user32.lib
includelib kernel32.lib
includelib comdlg32.lib

WinMain PROTO hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdShow:SDWORD

.CONST

IDC_EXECHOOSE	EQU 1000
IDC_BMPCHOOSE   EQU 1001
IDC_BITMAP      EQU 1002
IDC_PROGRAM     EQU 1004
IDC_INSERTNAG   EQU 1005

.DATA 

	
	DlgMain			DB "IDD_DIALOG1",0
	GoodCaps		DB "=)",0	
	GoodMsg			DB "Nag Inserted!",0
	SectionName		DB ".Latigo"
;	FilterString 		DB "Exe Files (*.exe)",0,"*.exe",0,"Dll Files (*.dll)",0,"*.dll",0,"All Files (*.*)",0,"*.*",0,0 
	FilterString 		DB "All Files (*.*)",0,"*.*",0,0 

	OFNTitle		DB "Choose your file",0

	ErrorCaps		DB "Ugly Error!",0

	; Exe Messages
	NoChosenExeMSG		DB "You must choose an exe file first!",0
	CantOpenExeMSG		DB "Could not open the target exe!",0
	NotPeFileMSG		DB "Not a valid PE file Mr.!",0

	; BMP Messages
	NoBMPChosenMSG		DB "You must choose a BMP first!",0
	CantOpenBMPMSG		DB "The BMP file could not be open!",0
	NoBmpMSG		DB "The file you chose is NOT a BMP!",0


	NewSection		IMAGE_SECTION_HEADER  <>
	PeHeader		IMAGE_NT_HEADERS 	<>

.DATA?

	FileHandle		DD	?		; Filehandle for the program
	hInstance		DD	?		; instance of NagStudio
	NewFileSize		DD 	?		; Newsize of the 'nagged' exe
	hwndMappedFile  	DD	?		; Handle to memory mapped exe
	MemMapPtr		DD	?		; Pointer to memory mapped exe
	BMP_FileHandle		DD	?		; The file handle used for the BMP
	BMP_hwndMappedFile  	DD	?		; Handle to memory mapped bmp
	BMP_MemMapPtr		DD	?		; Pointer to memory mapped bmp
	PeOffset		DD 	?		; PE offset of the memory mapped exe
	NewImageSize		DD	?		; The new imagesize of the exe
	NewEntryPoint		DD	?		; The new entrypoint of the exe

	FileNameBuffer		DB 255 DUP (?)
	ofn			OPENFILENAME <?>

.CODE


Starts:

	INVOKE GetModuleHandle,0
	MOV hInstance,EAX
	INVOKE WinMain,hInstance,NULL,SW_SHOWDEFAULT
	INVOKE ExitProcess,0

WinMain PROC hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdShow:SDWORD

	MOV EAX, OFFSET DlgProc
	INVOKE DialogBoxParam, hInst, OFFSET DlgMain,NULL,EAX,NULL    
   	RET

WinMain ENDP

DlgProc	PROC USES ESI EDI EBX hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

	; por que si la OFN es local se me carnea todo?

	MOV EAX,uMsg

	CMP EAX,WM_CLOSE				; User wanna quit?
	JZ _close

	CMP EAX,WM_COMMAND
	JZ _command

	CMP EAX,WM_INITDIALOG
	JZ _init

	XOR EAX,EAX
	RET

	_close:
	INVOKE ExitProcess,0
	RET

	_init:
	PUSH hWnd
	CALL CenterWindow
	RET

	_command:
	MOV EAX, wParam
	CMP AX, IDC_EXECHOOSE
	JZ _choose_exe

	CMP AX, IDC_BMPCHOOSE
	JZ _choose_bmp

	CMP AX, IDC_INSERTNAG
	JZ _insert_nag

	RET

	_choose_bmp:
	CALL  GetOpen
	CMP EAX,FALSE
	JZ NuttinToDo
	INVOKE SetDlgItemText,hWnd,IDC_BITMAP,OFFSET FileNameBuffer
	NuttinToDo:
	RET

	_choose_exe:
	CALL  GetOpen
	CMP EAX,FALSE
	JZ TheEndMyFriend
	INVOKE SetDlgItemText,hWnd,IDC_PROGRAM,OFFSET FileNameBuffer
	TheEndMyFriend:
	RET

	_insert_nag:
	PUSH hWnd
	CALL BmpOpen
	CMP EAX,1
	JNZ BmpOpenError
	PUSH hWnd
	CALL MangleExe
	CMP EAX,1
	JNZ MangleExeError
	CALL BmpClose
	PUSH OFFSET GoodMsg 
	PUSH OFFSET GoodCaps 
	PUSH hWnd
	CALL MsgBox
	RET

	MangleExeError:
	; -1 = Target exe could not be open
	; -2 = Target is not a PE file
	PUSH EAX
	CALL BmpClose
	POP EAX
	CMP EAX,-1
	JZ CantOpenExe
	CMP EAX,-2
	JZ NotPeFile
	CMP EAX,-3
	JZ NoChosenExeFile
	RET
	
	BmpOpenError:
	CMP EAX,-2
	JZ FileNoBmp
	CMP EAX,-1
	JZ CantOpenBmp
	CMP EAX,0
	JZ NoBmpChosen
	RET

	CantOpenExe:
	PUSH OFFSET CantOpenExeMSG
	JMP Alert
	RET

	NotPeFile:
	PUSH OFFSET NotPeFileMSG
	JMP Alert
	RET

	NoChosenExeFile:
	PUSH OFFSET NoChosenExeMSG
	JMP Alert
	RET

	NoBmpChosen:
	PUSH OFFSET NoBMPChosenMSG
	JMP Alert

	CantOpenBmp:
	PUSH OFFSET CantOpenBMPMSG
	JMP Alert

	FileNoBmp:
	PUSH OFFSET NoBmpMSG

	Alert:
	PUSH OFFSET ErrorCaps
	PUSH hWnd
	CALL MsgBox
	RET

DlgProc	ENDP

MsgBox	PROC hWnd:DWORD,TitlePtr:DWORD,MsgPtr:DWORD

	INVOKE MessageBox,hWnd,MsgPtr,TitlePtr,MB_OK
	RET

MsgBox	ENDP


GetOpen	PROC

	MOV ofn.lStructSize,SIZEOF ofn 
	MOV ofn.hWndOwner,NULL
   	PUSH hInstance 
   	POP  ofn.hInstance 
   	MOV ofn.lpstrFilter, OFFSET FilterString 
   	MOV ofn.lpstrFile, OFFSET FileNameBuffer
   	MOV ofn.nMaxFile,255
   	MOV ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY 
   	MOV  ofn.lpstrTitle, OFFSET OFNTitle
	LEA EAX,ofn
   	INVOKE GetOpenFileName, EAX

	RET

GetOpen	ENDP


BmpOpen	PROC hWnd:DWORD

	; This proc:
	; 1) Opens the BMP and checks that it's a real bmp (BM signature in header)
	; 2) Gets it's length and width and stores it

	; Return codes:
	;  0) User did not chose a bitmap
	; -1) Could not open target file
	; -2) File is NOT a bmp
	
	INVOKE GetDlgItemText,hWnd,IDC_BITMAP,OFFSET FileNameBuffer,0FFH
	OR EAX,EAX
	JZ NoBitMapChosen

	INVOKE CreateFile,OFFSET FileNameBuffer,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
	CMP EAX,-1
	JZ BMPIOError
	MOV BMP_FileHandle,EAX
	INVOKE GetFileSize,BMP_FileHandle,NULL
	MOV BMPSize,EAX
	INVOKE CreateFileMapping,BMP_FileHandle,NULL,PAGE_READONLY,0,0,0	
	MOV BMP_hwndMappedFile,EAX
	INVOKE MapViewOfFile,BMP_hwndMappedFile,FILE_MAP_READ,0,0,0
	MOV BMP_MemMapPtr,EAX
	CMP WORD PTR [EAX], 4D42H ; 'BM' in hex
	JNE BadFile
	ADD EAX,SIZE BITMAPFILEHEADER
	ASSUME EAX : PTR BITMAPINFOHEADER
	MOV EDX,(BITMAPINFOHEADER PTR [EAX]).biWidth
	MOV BMPWidth,EDX
	MOV EDX,(BITMAPINFOHEADER PTR [EAX]).biHeight
	MOV BMPHeight,EDX
	XOR EAX,EAX
	INC EAX
	RET

	BadFile:
	CALL BmpClose
	MOV EAX,-2
	RET

	NoBitMapChosen:
	; EAX Contains a 0
	RET

	BMPIOError:
	; EAX Contains a -1
	RET

BmpOpen	ENDP

BmpClose	PROC

	INVOKE UnmapViewOfFile,BMP_MemMapPtr
	INVOKE CloseHandle, BMP_hwndMappedFile
	INVOKE CloseHandle, BMP_FileHandle
	RET

BmpClose	ENDP

MangleExe	PROC hWnd:DWORD

	; Return codes:
	; -1 = Target exe could not be open
	; -2 = Target is not a PE file
	; -3 = User did not choose any exe file

	LOCAL DosHeader		:	IMAGE_DOS_HEADER
	LOCAL NTHeader		:	IMAGE_NT_HEADERS
	LOCAL PE_OFFSET		:	DWORD
	LOCAL DummyBuffer[255]  :	BYTE	

	INVOKE GetDlgItemText,hWnd,IDC_PROGRAM,OFFSET FileNameBuffer,0FFH
	OR EAX,EAX
	JZ NoExeChosen

	INVOKE CreateFile,OFFSET FileNameBuffer,GENERIC_READ OR GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
	CMP EAX,-1
	JZ FileCreationError
	MOV [FileHandle],EAX

	;Lets read the Dos header
	INVOKE ReadFile,FileHandle,ADDR DosHeader,sizeof DosHeader,ADDR DummyBuffer,0
	
	MOV EAX,[DosHeader.e_lfanew]
	MOV PE_OFFSET,EAX

	INVOKE SetFilePointer,FileHandle,PE_OFFSET,0,FILE_BEGIN	

	INVOKE ReadFile,FileHandle,ADDR NTHeader,sizeof NTHeader,ADDR DummyBuffer,0
	CMP [NTHeader.Signature],'EP'
	JNZ NotPEFile

	INVOKE GetFileSize,FileHandle,NULL
	ADD EAX,ViriiCodeEnd - ViriiCodeStart 
	ADD EAX,BMPSize
	PUSH EAX
	PUSH 200H
	CALL AlignIt
	MOV NewFileSize,EAX

	INVOKE CreateFileMapping,FileHandle,NULL,PAGE_READWRITE,0,NewFileSize,0	
	MOV [hwndMappedFile],EAX

	INVOKE MapViewOfFile,hwndMappedFile,FILE_MAP_WRITE,0,0,NewFileSize
	MOV [MemMapPtr],EAX

	CALL CalculateValues
	CMP EAX,1
	JNZ DontUpdate

	CALL UpdateFile
	MOV EAX,1
	PUSH EAX

	DontUpdate:
	INVOKE FlushViewOfFile,MemMapPtr,NULL
	INVOKE UnmapViewOfFile,MemMapPtr
	INVOKE CloseHandle, hwndMappedFile
	INVOKE CloseHandle, FileHandle

	POP EAX
	RET

	FileCreationError:
	; EAX contains -1
	RET

	NotPEFile:
	INVOKE CloseHandle,FileHandle
	MOV EAX,-2
	RET

	NoExeChosen:
	MOV EAX,-3
	RET


MangleExe	ENDP

CalculateValues	PROC
		
	; This proc calculates the values for the new Section Header and 
	; keeps them in the NewSection STRUCT
	MOV ESI,[MemMapPtr]
	MOV EAX,DWORD PTR [ESI+3CH]
	ADD ESI,EAX
	MOV EAX,ESI
	MOV PeOffset,EAX

	; Keep ImageBase for later return to the host
	MOV EBX,[ESI+52]
	MOV ImageBase,EBX

	; We now make our section RWX
	MOV [NewSection.Characteristics],0E0000020H
	
	; We make a copy of the PeHeader
	PUSH ESI
	LEA EDI,PeHeader
	MOV ECX,size PeHeader
	REP MOVSB
	POP ESI

	; We now add the name to our section
	PUSH ESI
	LEA ESI,SectionName
	LEA EDI,NewSection
	MOV ECX,7
	REP MOVSB

	;Now we go till the end of the section headers.
	POP ESI
	MOV EBX,[ESI+74H]		; Ebx contains NumberOfRvasAndSizes (10h)
	SHL EBX,3				; Multiply it by 8 since all the datadirs weight 8 bytes
	XOR ECX,ECX
	MOV CX,[PeHeader.FileHeader.NumberOfSections]
	MOV EAX,28H				; Each section header weights 28h bytes
	MUL ECX					; Calculate how much all our sections weight
	ADD ESI,78H				; Position ESI at the door of the data dirs
	ADD ESI,EBX				; ADD esi the size of all the data dirs (x * 8)
	ADD ESI,EAX				; ADD esi the weight of all the exe's section headers.

	; ESI está ahora justo después de todos los section Headers
	; We now calculate the VirtualSize
	; size of code + 1000h (?) aligned to SectionAlignment
	MOV EAX,ViriiCodeEnd - ViriiCodeStart 
	ADD EAX,BMPSize
	MOV ECX,[PeHeader.OptionalHeader.SectionAlignment]
	PUSH EAX  	; Unaligned
	PUSH ECX	; AlignValue
	CALL AlignIt
	MOV [NewSection.Misc],EAX

	; We now calculate VirtualAddress
	; Last section's VirtualAddres + last section's VirtualSize aligned to SectionAlignemnt
	MOV EAX,[ESI-40+8]
	ADD EAX,[ESI-40+12]
	MOV ECX,[PeHeader.OptionalHeader.SectionAlignment]
	PUSH EAX
	PUSH ECX
	CALL AlignIt
	MOV [NewSection.VirtualAddress],EAX

	; We now calculate the SizeOfRawData
	; size of code aligned to FileAlignment

	MOV EAX,ViriiCodeEnd - ViriiCodeStart
	ADD EAX,BMPSize
	MOV ECX,[PeHeader.OptionalHeader.FileAlignment]
	PUSH EAX
	PUSH ECX
	CALL AlignIt
	MOV [NewSection.SizeOfRawData],EAX

	; We now calculate the PointerToRawData
	; Last section's ptr to raw data + last section's raw data aligned to filealignment

 	MOV EAX,[ESI-40+16]
	ADD EAX,[ESI-40+20]
	MOV ECX,[PeHeader.OptionalHeader.FileAlignment]
	PUSH EAX
	PUSH ECX
	CALL AlignIt
	MOV [NewSection.PointerToRawData],EAX		
	XOR EAX,EAX
	INC EAX
	RET

	CalculateValues	ENDP

	AlignIt	PROC AlignValue:DWORD, Unaligned:DWORD

	; This proc aligns the number passed as parameter (Unaligned) agains
	; the other parameter called AlignValue

	MOV EAX,Unaligned
	MOV ECX,AlignValue
	XOR EDX,EDX
	DIV ECX
	OR EDX,EDX				; Even division?
	JZ NoInc
	INC EAX
	NoInc:
	MUL ECX
	RET

	AlignIt		ENDP

UpdateFile	PROC

	; This proc:
	; copies the section header
	; copies the section
	; updates entrypoint
	; updates section numbers
	; updates imagesize
	; Deletes the damn BOUND_IMPORTs information in the PE header
	; Copies the BITMAP and it's size to the end of the .Latigo section
	; ESI points to the end of the section headers
	; lets keep the old EP and store the new one
	
	MOV EDI,PeOffset
	MOV EAX,DWORD PTR [EDI+28H]
	MOV OriginalEntryPoint,EAX

	MOV EAX,[NewSection.VirtualAddress]
	MOV DWORD PTR [EDI+28H],EAX

	XOR EAX,EAX
	; Delete the Bound Import info
	; 0D0 = 78H + 58H
	; 78H = Offset to the DATA_DIRS from PE 
	; 58H = 88d = 11 x 8 = 8 bytes per DATA_DIR * 11 entries till BOUND_IMPORT data dir

	MOV DWORD PTR [EDI+0D0H],EAX

	; We now copy the section header
	LEA EDI,NewSection
	MOV ECX, SIZE NewSection
	XCHG ESI,EDI
	REP MOVSB

	; We now copy the new section
	MOV EDI,[MemMapPtr]
	ADD EDI,[NewSection.PointerToRawData]
	LEA ESI,ViriiCodeStart
	MOV ECX,ViriiCodeEnd - ViriiCodeStart
	REP MOVSB

	; We now copy the BMP to the end of our code
	MOV ESI,[BMP_MemMapPtr]
	MOV ECX,BMPSize
	REP MOVSB

	; And now we zero-pad the rest of the section
	MOV EBX,ViriiCodeEnd - ViriiCodeStart
	ADD EBX,BMPSize
	MOV ECX,NewSection.SizeOfRawData
	SUB ECX,EBX
	XOR AL,AL
	REP STOSB
		
	MOV EDI,PeOffset
	; We now update the number of sections
	INC WORD PTR [EDI+06]
		
	; We now update the imagesize
 	MOV EAX,ViriiCodeEnd - ViriiCodeStart
 	ADD EAX,BMPSize
	MOV ECX,[PeHeader.OptionalHeader.SectionAlignment]
	PUSH EAX
	PUSH ECX
	CALL AlignIt
	ADD EAX,[PeHeader.OptionalHeader.SizeOfImage]
	MOV DWORD PTR [EDI+50H],EAX
	RET

	UpdateFile	ENDP

include Injected.asm

include Util.asm

END Starts