Goal: Enrich own understanding of the Flat Assember (FASM) code constructs to enhance malware analysis and forward-engineering skills especially with PE files. I find it to be extremely helpful to have code samples ready to use/analyze.
Source: Betamaster
Why FASM?
FASM is a 32-bit, open-source, cross-platform assembler, targeting the IA-32 and x86-64 architectures (in addition, FASMARM – in unofficial port of FASM, targets the ARM architecture). It is very lightweight, fast, and rather simple to leverage in Windows API programming challenges.
I. Simple Application: Hello World
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
format PE console entry start include 'win32a.inc' ;====================================== section '.data' data readable writeable ;====================================== hello_newline db "Hello World!" ,10,0 hello_no_newline db "Hello World! (without a new line)" ,0 ;======================================= section '.code' code readable executable ;======================================= start: ccall [printf],hello_newline ; Print 'Hello World!' and start a new line. ccall [printf],hello_no_newline ; Print 'Hello World!' without starting a new line. ccall [getchar] ; I added this line to exit the application AFTER the user pressed any key. stdcall [ExitProcess],0 ; Exit the application ;==================================== section '.idata' import data readable ;==================================== library kernel, 'kernel32.dll' ,\ msvcrt, 'msvcrt.dll' import kernel,\ ExitProcess, 'ExitProcess' import msvcrt,\ printf, 'printf' ,\ getchar, '_fgetchar |
II. Create a process
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
format PE GUI 4.0 entry start include 'win32a.inc' start: invoke CreateProcessA,txt_location,0,0,0,0,CREATE_NEW_CONSOLE,0,0,StartupInfo,ProcessInfo call [ExitProcess] ; Custom Data: Contains the location of notepad.exe, StartupInfo and ProcessInfo: section '.data' data readable writeable txt_location db 'C:\Windows\System32\notepad.exe',0 StartupInfo STARTUPINFO ProcessInfo PROCESS_INFORMATION ; Imported functions and corresponding names of DLL files: section '.idata' import data readable writeable library kernel,'KERNEL32.DLL' import kernel,\ CreateProcessA, "CreateProcessA",\ ExitProcess,'ExitProcess' |
III. Kill a process
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
format PE GUI 4.0 entry start include 'win32a.inc' ;================== code ===================== section '.code' code readable executable ;============================================= start: invoke GetCurrentProcess ; Retrieve a pseudo handle for current process invoke OpenProcessToken,eax,TOKEN_QUERY_TOKEN_ADJUST_PRIVILEGES,phToken ; Open access token associated with this process invoke LookupPrivilegeValue,0,Privilege ,pLocalId ; Retrieve the locally unique identifier (LUID) mov [PrivilegeCount],1 ; [PrivilegeCount] = 1 mov [Attributes],2 ; [Attributes] = 2 invoke AdjustTokenPrivileges,[phToken],0,PrivilegeCount ,0,0,0 ; Enable privileges on our token mov [prcs.dwSize],sizeof.PROCESSENTRY32 ; Store the required size of PROCESSENTRY32 in prcs.dwSize invoke CreateToolhelp32Snapshot, TH32CS_SNAPPROCESS, 0 ; Take a snapshot of the specified processes (get all running processes) mov [hSnapshot], eax ; Save the snapshot handle invoke Process32First,[hSnapshot],prcs ; Retrieve information about the first process encountered in our system snapshot .loop: mov edi,PrcList ; EDI = filename of process we want to kill invoke StrStrI,prcs.szExeFile, edi ; Compare the current process name with the one we want to kill cmp eax,0 ; - || - je .next ; Jump = Not equal, continue with the next process call kill ; Else : Kill the process .next: invoke Process32Next,[hSnapshot],prcs ; Retrieve the next process in our snapshot cmp eax,0 ; Check if there are still processes we didn't check jne .loop ; Jump = Continue the loop with the current process invoke ExitProcess,0 ; Else : No more processes. Exit. kill: invoke OpenProcess,PROCESS_TERMINATE,0,[prcs.th32ProcessID] ; Open the process with terminate privileges invoke TerminateProcess,eax,0 ; Terminate it (Kill process) retn ; And return (= exit as well) ;=================== data ==================== section '.data' data readable writeable ;============================================= TOKEN_QUERY_TOKEN_ADJUST_PRIVILEGES =28h TH32CS_SNAPPROCESS = 2 struct PROCESSENTRY32 dwSize dd ? cntUsage dd ? th32ProcessID dd ? th32DefaultHeapID dd ? th32ModuleID dd ? cntThreads dd ? th32ParentProcessID dd ? pcPriClassBase dd ? dwFlags dd ? szExeFile db 260 dup(?) ends PrivilegeCount dd ? pLocalId dd ? Attributes dd ? phToken dd ? hSnapshot dd ? prcs PROCESSENTRY32 PrcList db 'calc.exe' ,0 Privilege db 'SeDebugPrivilege' ,0 ;============================================= section '.idata' import data readable ;============================================= library kernel32, 'KERNEL32.DLL' ,\ advapi32, 'ADVAPI32.DLL' ,\ shell32, 'SHELL32.DLL' include 'API\kernel32.inc' include 'API\advapi32.inc' include 'API\shell32.inc' |
III. WriteProcessMemory
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
format PE GUI 4.0 entry start include 'win32a.inc' ;================== code ===================== section '.code' code readable executable ;============================================= proc start invoke FindWindow, NULL, WindowTitle ; Find the window titled 'Notepad' test eax,eax ; Test whether a window with that title was found or not jnz .ju1 ; Don't jump = The window was not found invoke MessageBox,0, message1, caption, MB_OK ; - Display an error message (Window not found) jmp .exit ; - Exit the application .ju1: ; Jumped = The window was found invoke GetWindowThreadProcessId, eax, ProcID ; Get the ProcessID via the window handle invoke OpenProcess, 0x1F0FFF, FALSE, [ProcID] ; Open the process using PROCESS_ALL_ACCESS (0x1F0FFF) and get a handle mov dword[ProcHandle],eax ; Save the handle ; VirtualAllocEx: Reserves/commits a region of memory within the virtual address space of out target process ; We should do this in order to avoid potential access violations (which might cause crashes) invoke VirtualAllocEx,dword [ProcHandle], 0, patchSize, MEM_COMMIT, PAGE_READWRITE cmp eax, 0 ; EAX == 0 : Failed to reserve the memory region jnz .cont ; EAX != 0 : Continue with further steps invoke MessageBox,0, message4, caption, MB_OK ; Display an error: VirtualAllocEx failed to reserve the memory region jmp .exit ; Exit the application .cont: invoke WriteProcessMemory, dword[ProcHandle], dword[startAddress], patchBytes, patchSize, patchResult cmp [patchResult],patchSize ; Compare the number of patched bytes with the length of our new bytes je .ju2 ; Don't jump = Failed to patch the target invoke MessageBox,0, message3, caption, MB_OK ; - Display an error message (An error occured) jmp .exit ; - Exit the application .ju2: ; Jumped = Target patched successfully. invoke MessageBox,0, message2, caption, MB_OK ; Display: The target has been patched successfully .exit: ; Jumper: Here we're going to exit our application invoke ExitProcess, 0 ; ExitProcess endp ;=================== data ==================== section '.data' data readable writeable ;============================================= WindowTitle db 'Notepad' , 0 ; Holds the window title of the target application ProcID dd ? ; Holds the process ID of the target application ProcHandle db ? ; Holds the process handle of the target application caption db 'Information' , 0 ; The caption displayed in all MessageBoxes message1 db 'Unable to find the window' , 0 ; Message: Window not found message2 db 'Patched successfully' ,0 ; Message: Target patched successfully message3 db 'Patching: An error occured' ,0 ; Message: An error occured while patching the target message4 db 'VirtualAllocEx failed' ,0 ; Message: Failed to successfully execute VirtualAllocEx startAddress dd 0x00401090 ; The memory address we're starting to write from patchBytes db 0x2F,0x66 ; These bytes will be written into the memory of our target executable patchSize = $ - patchBytes ; Holds the number of bytes we're going to write patchResult dd ? ; Holds the number of successfully written bytes ;============================================= section '.idata' import data readable ;============================================= library kernel32, 'KERNEL32.DLL' ,\ user32, 'USER32.DLL' import kernel32,\ ExitProcess, 'ExitProcess' ,\ OpenProcess, 'OpenProcess' ,\ VirtualAllocEx, "VirtualAllocEx" ,\ WriteProcessMemory, 'WriteProcessMemory' import user32,\ FindWindow, 'FindWindowA' ,\ GetWindowThreadProcessId, 'GetWindowThreadProcessId' ,\ MessageBox, 'MessageBoxA' |
IV. Copy a file
1
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
format PE GUI 4.0 entry start include 'win32ax.inc' ;================== code ===================== section '.code' code readable executable ;============================================= proc start mov [lpFileOp.wFunc],FO_COPY ; We want the shell to copy a file mov [lpFileOp.fFlags],FOF_SILENT ; .. silently mov [lpFileOp.pFrom],SzFileFrom ; The file which is going to be copied mov [lpFileOp.pTo],SzFileTo ; The name of the new file invoke SHFileOperationA,lpFileOp ; Execute the operation invoke ExitProcess,NULL ; Exit this program endp ;=================== data ==================== section '.data' data readable writeable ;============================================= FO_COPY = 2 FOF_SILENT = 4 SzFileFrom db 'source.txt' ,0 SzFileTo db 'target.txt' ,0 struct SHFILEOPSTRUCT hWnd dd ? wFunc dd ? pFrom dd MAX_PATH pTo dd MAX_PATH fFlags dw ? fAnyOperationsAborted dd ? hNameMappings dd ? lpszProgressTitle dd ? ends lpFileOp SHFILEOPSTRUCT ;============================================= section '.idata' import data readable ;============================================= library kernel32, 'KERNEL32.DLL' ,\ shell32, 'SHELL32.DLL' import kernel32,\ ExitProcess, 'ExitProcess' import shell32,\ SHFileOperationA, 'SHFileOperationA' |
V. Read a file
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
format pe console 4.0 include 'WIN32AX.INC' .data FileTitle db 'file.txt' ,0 hFile dd ? nSize dd ? lpBytesRead dd ? lpBuffer rb 8192 MessageBoxCaption db 'Output:' ,0 .code main: invoke CreateFile, FileTitle, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ; Open the file (to get its handle) ; TODO: TestApiError mov [hFile], eax ; Save the file's handle to hFile invoke GetFileSize, [hFile], 0 ; Determine the file size ; TODO: TestApiError mov [nSize], eax ; Save the file size given by EAX invoke ReadFile, [hFile], lpBuffer, [nSize], lpBytesRead, 0 ; Now read the full file ; TODO: TestApiError invoke CloseHandle, [hFile] ; Handle should be closed after the file has been read invoke MessageBox, NULL, addr lpBuffer, addr MessageBoxCaption, MB_OK ; Easy way of outputing the text invoke ExitProcess, 0 .end main |
VI. Write a file
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
format pe console 4.0 include 'WIN32AX.INC' .data buf db 'TEST' bufsize = $ - buf byteswritten dd ? .code main: invoke CreateFile, 'test.txt', GENERIC_WRITE, 0, 0, 4, FILE_ATTRIBUTE_NORMAL, 0 invoke WriteFile, eax, buf, bufsize, byteswritten, 0 invoke ExitProcess, 0 .end main |