Today we are going to talk about the windows api, something that will allow us to do similar stuff as what we are doing with linux using syscalls.
WindowsAPI HelloWorld
The first program is always the hello wowrld and that is what we are going to do now.
So, Windows Api is what we use to interact with the windows operating system from the user space, according to wikipedia
“The Windows API, informally WinAPI, is Microsoft’s core set of application programming interfaces (APIs) available in the Microsoft Windows operating systems. The name Windows API collectively refers to several different platform implementations that are often referred to by their own names (for example, Win32 API); see the versions section. Almost all Windows programs interact with the Windows API. On the Windows NT line of operating systems, a small number (such as programs started early in the Windows startup process) use the Native API”
You may be thinking about if winapi calls are syscalls or are the same as what we have saw on the past tutorial but know that you are not alone and some answers have been provided: https://www.quora.com/Why-does-Windows-have-the-Win32-API-for-invoking-system-calls-but-on-Linux-you-would-just-directly-invoke-the-system-call
The following answer states a good point that I want to remark, when we were going through Linux syscalls, we actually did no syscall at all, write()/open() etc are userland code they do the syscall internally but we can interpret them as syscalls for educational purposes.
Let’s get to the code now:
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
int main()
{
int msgboxID = MessageBox(NULL, "Hello dear user", "Artikblue's magic msgbox", MB_ICONWARNING | MB_CANCELTRYCONTINUE);
switch (msgboxID)
{
case IDCANCEL:
printf("CANCELING");
Beep( 750, 300 );
break;
case IDTRYAGAIN:
printf("Shall we try again?");
Beep( 250, 100 );
break;
case IDCONTINUE:
printf("The show must go on");
Beep( 950, 500 );
break;
}
return 0;
}
The following code will use the Windows Api to pop a messagebox, a small dialog that will requiere the interaction of the user. Depending on the user action the program will do some stuff. That stuff will include emitting a simple “beep” using the systems audio (those params are related to the frequency). So, the key thing here is that MessageBox and Beep make use the operating systems features, that is important, those functions are not related to the specific language, not related to some calculations we want to perform, not related to data structures /algorithms those are actually ACTIONS we want the WINDOWS SYSTEM to do for us. So we use the api for dealing with operating system actions like generating a window or emitting a sound. A lot of windows malware makes use of the windows api because a malware deals with stuff like the filesystem/systems memory/registry/devices/network.
Having a nice understanding of the windows api makes it easy for malware analysis.
The information related to this MessageBox call can be found here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox
Let us first list the detected functions by using afl
[0x77953840]> afl
0x00401000 1 1 sym.__mingw_invalidParameterHandler
0x00401010 14 273 -> 266 sym.pre_c_init
0x00401130 1 73 sym.pre_cpp_init
0x00401180 44 850 -> 809 sym.__tmainCRTStartup
0x004014e0 1 34 entry0
0x00401510 1 25 sym.atexit
0x00401530 1 12 sym.__gcc_register_frame
0x00401540 1 1 sym.__gcc_deregister_frame
0x00401550 7 177 sym.main
0x00401610 3 53 sym.__do_global_dtors
0x00401650 9 102 -> 99 sym.__do_global_ctors
0x004016c0 3 31 -> 26 sym.__main
0x004016e0 1 7 sym.my_lconv_init
0x004016f0 1 3 sym._setargv
0x00401700 6 214 -> 206 sym.__security_init_cookie
0x004017e0 4 248 sym.__report_gsfailure
0x004018e0 4 47 -> 38 sym.__dyn_tls_dtor
0x00401910 11 115 -> 113 sym.__dyn_tls_init
0x00401990 1 3 sym.__tlregdtor
0x004019a0 3 65 sym.__mingw_raise_matherr
0x004019f0 1 12 -> 18 sym.__mingw_setusermatherr
0x00401a00 2 49 sym._matherr
0x00401a38 1 96 loc.00401a38
0x00401b00 1 3 sym._fpreset
0x00401b10 1 4 sym._decode_pointer
0x00401b20 1 4 sym._encode_pointer
0x00401b30 26 464 sym.__write_memory.part.0
0x00401d00 66 1120 -> 1076 sym._pei386_runtime_relocator
0x00402160 12 236 -> 232 sym.__mingw_init_ehandler
0x00402250 36 487 -> 458 sym._gnu_exception_handler
0x00402440 7 106 sym.__mingwthr_run_key_dtors.part.0
0x004024b0 5 127 sym.___w64_mingwthr_add_key_dtor
0x00402530 13 160 -> 141 sym.___w64_mingwthr_remove_key_dtor
0x004025d0 17 218 -> 205 sym.__mingw_TLScallback
0x004026b0 3 30 sym._ValidateImageBase.part.0
0x004026d0 3 18 -> 12 sym._ValidateImageBase
0x004026f0 7 68 sym._FindPESection
0x00402740 9 141 -> 139 sym._FindPESectionByName
0x004027d0 9 116 sym.__mingw_GetSectionForAddress
0x00402850 4 62 -> 59 sym.__mingw_GetSectionCount
0x00402890 10 111 sym._FindPESectionExec
0x00402900 3 55 -> 49 sym._GetPEImageBase
0x00402940 10 145 -> 142 sym._IsNonwritableInCurrentImage
0x004029e0 16 166 -> 162 sym.__mingw_enum_import_library_names
0x00402a90 3 50 fcn.00402a90
0x00402ad0 1 6 sym.vfprintf
0x00402ad8 1 6 sym.strncmp
0x00402ae0 1 6 sym.strlen
0x00402ae8 1 6 sym.signal
0x00402af0 1 6 sym.printf
0x00402af8 1 6 sym.memcpy
0x00402b00 1 6 sym.malloc
0x00402b08 1 6 sym.fwrite
0x00402b10 1 6 sym.free
0x00402b18 1 6 sym.fprintf
0x00402b20 1 6 sym.exit
0x00402b28 1 6 sym.calloc
0x00402b30 1 6 sym.abort
0x00402b38 1 6 sym._onexit
0x00402b40 1 6 sym._initterm
0x00402b48 1 6 sym._cexit
0x00402b50 1 6 sym._amsg_exit
0x00402b58 1 6 sym.__setusermatherr
0x00402b60 1 6 sym.__set_app_type
0x00402b70 1 6 sym.__getmainargs
0x00402b80 1 31 sym.__acrt_iob_func
0x00402ba0 1 8 sym.mingw_get_invalid_parameter_handler
0x00402bb0 1 11 sym.mingw_set_invalid_parameter_handler
0x00402bc0 1 11 sym.__p__acmdln
0x00402bd0 1 11 sym.__p__fmode
0x00402be0 1 6 sym.__iob_func
0x00402cc0 1 117 sym.__report_error
[0x77953840]>
Note the first difference we see here when comparing this with our previous Linux tutorial. As you can see, we see A LOT of functions here being used almost all of them not directly used, related to the windows api
It is clear that those funcs are being used by our MessageBox call
As usual in these tutorials, the program is defined inside the main function:
[0x004015a5]> pdf
/ (fcn) sym.main 177
| sym.main ();
| ; var int local_4h @ rbp-0x4
| ; CALL XREF from 0x004013c2 (sym.__tmainCRTStartup)
| 0x00401550 55 push rbp
| 0x00401551 4889e5 mov rbp, rsp
| 0x00401554 4883ec30 sub rsp, 0x30 ; '0'
| 0x00401558 e863010000 call sym.__main
| 0x0040155d 41b936000000 mov r9d, 0x36 ; '6' ; 54
| 0x00401563 4c8d05962a00. lea r8, str.Artikblue_s_magic_msgbox ; section..rdata ; 0x404000 ; "Artikblue's m
| 0x0040156a 488d15a82a00. lea rdx, str.Hello_dear_user ; 0x404019 ; "Hello dear user"
| 0x00401571 b900000000 mov ecx, 0
| 0x00401576 488b05236e00. mov rax, qword sym.imp.USER32.dll_MessageBoxA ; [0x4083a0:8]=0x77751304
| 0x0040157d ffd0 call rax
| 0x0040157f 8945fc mov dword [local_4h], eax
| 0x00401582 837dfc0a cmp dword [local_4h], 0xa ; [0xa:4]=-1 ; 10
| ,=< 0x00401586 742d je 0x4015b5
| | 0x00401588 837dfc0b cmp dword [local_4h], 0xb ; [0xb:4]=-1 ; 11
| ,==< 0x0040158c 7448 je 0x4015d6
| || 0x0040158e 837dfc02 cmp dword [local_4h], 2 ; [0x2:4]=-1 ; 2
| ,===< 0x00401592 7562 jne 0x4015f6
| ||| 0x00401594 488d0d8e2a00. lea rcx, str.CANCELING ; 0x404029 ; "CANCELING"
| ||| 0x0040159b e850150000 call sym.printf ; int printf(const char *format)
| ||| 0x004015a0 ba2c010000 mov edx, 0x12c ; rdx
| ||| 0x004015a5 b9ee020000 mov ecx, 0x2ee ; 750
| ||| 0x004015aa 488b054f6c00. mov rax, qword sym.imp.KERNEL32.dll_Beep ; [0x408200:8]=0x778478f0
| ||| 0x004015b1 ffd0 call rax
| ,====< 0x004015b3 eb41 jmp 0x4015f6
| |||| ; JMP XREF from 0x00401586 (sym.main)
| |||`-> 0x004015b5 488d0d772a00. lea rcx, str.Shall_we_try_again ; 0x404033 ; "Shall we try again?"
| ||| 0x004015bc e82f150000 call sym.printf ; int printf(const char *format)
| ||| 0x004015c1 ba64000000 mov edx, 0x64 ; 'd' ; 100
| ||| 0x004015c6 b9fa000000 mov ecx, 0xfa ; 250
| ||| 0x004015cb 488b052e6c00. mov rax, qword sym.imp.KERNEL32.dll_Beep ; [0x408200:8]=0x778478f0
| ||| 0x004015d2 ffd0 call rax
| |||,=< 0x004015d4 eb20 jmp 0x4015f6
| |||| ; JMP XREF from 0x0040158c (sym.main)
| ||`--> 0x004015d6 488d0d6a2a00. lea rcx, str.The_show_must_go_on ; 0x404047 ; "The show must go on"
| || | 0x004015dd e80e150000 call sym.printf ; int printf(const char *format)
| || | 0x004015e2 baf4010000 mov edx, 0x1f4 ; 500
| || | 0x004015e7 b9b6030000 mov ecx, 0x3b6 ; 950
| || | 0x004015ec 488b050d6c00. mov rax, qword sym.imp.KERNEL32.dll_Beep ; [0x408200:8]=0x778478f0
| || | 0x004015f3 ffd0 call rax
| || | 0x004015f5 90 nop
| || | ; JMP XREF from 0x004015d4 (sym.main)
| || | ; JMP XREF from 0x004015b3 (sym.main)
| || | ; JMP XREF from 0x00401592 (sym.main)
| ``-`-> 0x004015f6 b800000000 mov eax, 0
| 0x004015fb 4883c430 add rsp, 0x30 ; '0'
| 0x004015ff 5d pop rbp
\ 0x00401600 c3 ret
[0x004015a5]>
Again, we can break the program in interesting slices to get the picture:
| ; CALL XREF from 0x004013c2 (sym.__tmainCRTStartup)
| 0x00401550 55 push rbp
| 0x00401551 4889e5 mov rbp, rsp
| 0x00401554 4883ec30 sub rsp, 0x30 ; '0'
| 0x00401558 e863010000 call sym.__main
| 0x0040155d 41b936000000 mov r9d, 0x36 ; '6' ; 54
| 0x00401563 4c8d05962a00. lea r8, str.Artikblue_s_magic_msgbox ; section..rdata ; 0x404000 ; "Artikblue's magic msgbox"
| 0x0040156a 488d15a82a00. lea rdx, str.Hello_dear_user ; 0x404019 ; "Hello dear user"
| 0x00401571 b900000000 mov ecx, 0
| 0x00401576 488b05236e00. mov rax, qword sym.imp.USER32.dll_MessageBoxA ; [0x4083a0:8]=0x86d8 reloc.USER32.dll_MessageBoxA
| 0x0040157d ffd0 call rax
| 0x0040157f 8945fc mov dword [local_4h], eax
As we see, the program first keeps some space on the stack (ox30), then goes to main. Then the parameters are being passed to the MessageBox function and as you see the flags are being passed all together (combined) to the function so 6 is the combination of the values of those flags: 0x30 + 0x6
You can get the full list of flags for window creation here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox
Also note how the address relative to MessageBoxA is being loaded in rax then called there, why that? Probably because that address is being dynamically resolved somehow when the program loads or whatever, it does not matter for us for now.
Then the switch statement kicks in:
| 0x0040157f 8945fc mov dword [local_4h], eax
| 0x00401582 837dfc0a cmp dword [local_4h], 0xa ; [0xa:4]=-1 ; 10
| ,=< 0x00401586 742d je 0x4015b5
| | 0x00401588 837dfc0b cmp dword [local_4h], 0xb ; [0xb:4]=-1 ; 11
| ,==< 0x0040158c 7448 je 0x4015d6
| || 0x0040158e 837dfc02 cmp dword [local_4h], 2 ; [0x2:4]=-1 ; 2
The MessageBox window offers the possibilit to do three actions, and as it is a sync process, the program won’t continue until the user clicks. When returning eax will hold a value related to the user choice that will correspond to the clicked button
Codes can be found here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox
So after the click, the program will check for the user action and run the corresponding piece of code:
Then again we see this difference, printf is called directly and then the Beep function (related to the kernel32 library), the parameters related to the beep frequencies are passed through those registers and then the func is called
| ,===< 0x00401592 7562 jne 0x4015f6
| ||| 0x00401594 488d0d8e2a00. lea rcx, str.CANCELING ; 0x404029 ; "CANCELING"
| ||| 0x0040159b e850150000 call sym.printf ; int printf(const char *format)
| ||| 0x004015a0 ba2c010000 mov edx, 0x12c ; 300
| ||| 0x004015a5 b9ee020000 mov ecx, 0x2ee ; 750
| ||| 0x004015aa 488b054f6c00. mov rax, qword sym.imp.KERNEL32.dll_Beep ; [0x408200:8]=0x83b0 reloc.KERNEL32.dll_Beep
| ||| 0x004015b1 ffd0 call rax
| ,====< 0x004015b3 eb41 jmp 0x4015f6
Nothing much more to comment here, I just wanted to show how a C program can intereact with Windows operating system functionalities (somehow a little bit similar to Linux syscalls) easily.
Let’s now get into more “systems programming” - like stuff, like the winapi way to deal with files.
Write a file to disk CreateFile and WriteFile
Let’s continue with the very basics, as you see these msdn functions work relatively the same as the linux syscalls, we open a file for read/write creating it if it does not exist with CreateFile and we get a handle to the file, a file handle or file handleR is somehow similar for us as a file descriptor, by using it we can reference the file, the system will hold information internally related to the file, for example, a pointer to the current position on the file, that will be updated afer a read/write/seek call on the file.
So, following this logic, we can easily write some bytes to the file with WriteFile by using the file handle and some buffer with the bytes to write there. Note that when using Linux syscalls write/file descriptors is a MORE ADVANCED concept, as write is general it can write to the screen, to a file, to a socket, pipe whatever “because in linux everything is a file” here the thing is more like “in windows everything is an object” so eventhough the architecture is similar AT SOME POINT windows have a specific call for writing to a file, another call for sending info to the screen etc.
Whatever, here’s the code:
#include <windows.h>
#include <stdio.h>
int main(void)
{
DWORD at;
HANDLE hFile;
LPCWSTR fname = "C:\\samples\\newfile.txt";
DWORD lpdwFlags[100];
BOOL test;
hFile = CreateFile(fname, GENERIC_WRITE,FILE_SHARE_WRITE,NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL |FILE_ATTRIBUTE_ARCHIVE | SECURITY_IMPERSONATION,NULL);
if(hFile == INVALID_HANDLE_VALUE)
printf("Could not open %s file, error %d\n", fname, GetLastError());
else{
printf("File's HANDLE is OK!\n");
printf("f handle = %d \n", hFile);
char arr[20] = "SAMPLESAMPLESAMPLETEXT";
printf("%s \n", arr);
WriteFile(hFile, &arr, 5,&at,NULL);
printf("Bytes written: %d \n", at);
CloseHandle(hFile);
}
return 0;
}
So basically the program opens a file for writting then checks if everythings ok with the open and calls WriteFile to write some bytes (arr).
Let’s diisasm this:
[0x00401550]> pdf
/ (fcn) sym.main 343
| sym.main (int arg_17ch, int arg_180h, int arg_188h);
| ; var int local_40h @ rbp-0x40
| ; var int local_38h @ rbp-0x38
| ; var int local_30h_2 @ rbp-0x30
| ; arg int arg_17ch @ rbp+0x17c
| ; arg int arg_180h @ rbp+0x180
| ; arg int arg_188h @ rbp+0x188
| ; var int local_20h @ rsp+0x20
| ; var int local_28h @ rsp+0x28
| ; var int local_30h @ rsp+0x30
| ; var int local_80h @ rsp+0x80
| ; CALL XREF from 0x004013c2 (sym.__tmainCRTStartup)
| 0x00401550 55 push rbp
| 0x00401551 4881ec100200. sub rsp, 0x210
| 0x00401558 488dac248000. lea rbp, [local_80h] ; 0x80 ; 128
| 0x00401560 e8fb010000 call sym.__main
| 0x00401565 488d05942a00. lea rax, str.C:__samples___ewfile.txt ; section..rdata ; 0x404000 ; "C:\samples\newfile.txt"
| 0x0040156c 488985880100. mov qword [arg_188h], rax ; [0x188:8]=-1 ; 392
| 0x00401573 488b85880100. mov rax, qword [arg_188h] ; [0x188:8]=-1 ; 392
| 0x0040157a 48c744243000. mov qword [local_30h], 0
| 0x00401583 c7442428a000. mov dword [local_28h], 0x200a0 ; [0x200a0:4]=-1
| 0x0040158b c74424200200. mov dword [local_20h], 2
| 0x00401593 41b900000000 mov r9d, 0
| 0x00401599 41b802000000 mov r8d, 2
| 0x0040159f ba00000040 mov edx, 0x40000000
| 0x004015a4 4889c1 mov rcx, rax
| 0x004015a7 488b054e6c00. mov rax, qword sym.imp.KERNEL32.dll_CreateFileA ; [0x4081fc:8]=0x83ba reloc.KERNEL32.dll_CreateFileA
| 0x004015ae ffd0 call rax
| 0x004015b0 488985800100. mov qword [arg_180h], rax ; [0x180:8]=-1 ; 384
| 0x004015b7 4883bd800100. cmp qword [arg_180h], 0xffffffffffffffff
| ,=< 0x004015bf 7529 jne 0x4015ea
| | 0x004015c1 488b05646c00. mov rax, qword sym.imp.KERNEL32.dll_GetLastError ; [0x40822c:8]=0x8438 reloc.KERNEL32.dll_GetLastError ; "8\x84"
| | 0x004015c8 ffd0 call rax
| | 0x004015ca 89c2 mov edx, eax
| | 0x004015cc 488b85880100. mov rax, qword [arg_188h] ; [0x188:8]=-1 ; 392
| | 0x004015d3 4189d0 mov r8d, edx
| | 0x004015d6 4889c2 mov rdx, rax
| | 0x004015d9 488d0d382a00. lea rcx, str.Could_not_open__s_file__error__d ; 0x404018 ; "Could not open %s file, error %d\n"
| | 0x004015e0 e8b3150000 call sym.printf ; int printf(const char *format)
| ,==< 0x004015e5 e9af000000 jmp 0x401699
| || ; JMP XREF from 0x004015bf (sym.main)
| |`-> 0x004015ea 488d0d492a00. lea rcx, str.File_s_HANDLE_is_OK ; 0x40403a ; "File's HANDLE is OK!"
| | 0x004015f1 e89a150000 call sym.puts ; int puts(const char *s)
| | 0x004015f6 488b85800100. mov rax, qword [arg_180h] ; [0x180:8]=-1 ; 384
| | 0x004015fd 4889c2 mov rdx, rax
| | 0x00401600 488d0d482a00. lea rcx, str.f_handle____d ; 0x40404f ; "f handle = %d \n"
| | 0x00401607 e88c150000 call sym.printf ; int printf(const char *format)
| | 0x0040160c 48b853414d50. movabs rax, 0x4153454c504d4153
| | 0x00401616 488945c0 mov qword [local_40h], rax
| | 0x0040161a 48b84d504c45. movabs rax, 0x504d4153454c504d
| | 0x00401624 488945c8 mov qword [local_38h], rax
| | 0x00401628 c745d04c4554. mov dword [local_30h_2], 0x4554454c
| | 0x0040162f 488d45c0 lea rax, [local_40h]
| | 0x00401633 4889c2 mov rdx, rax
| | 0x00401636 488d0d222a00. lea rcx, str.s ; 0x40405f ; "%s \n"
| | 0x0040163d e856150000 call sym.printf ; int printf(const char *format)
| | 0x00401642 488d957c0100. lea rdx, [arg_17ch] ; 0x17c ; 380
| | 0x00401649 488d45c0 lea rax, [local_40h]
| | 0x0040164d 488b8d800100. mov rcx, qword [arg_180h] ; [0x180:8]=-1 ; 384
| | 0x00401654 48c744242000. mov qword [local_20h], 0
| | 0x0040165d 4989d1 mov r9, rdx
| | 0x00401660 41b805000000 mov r8d, 5
| | 0x00401666 4889c2 mov rdx, rax
| | 0x00401669 488b054c6c00. mov rax, qword sym.imp.KERNEL32.dll_WriteFile ; [0x4082bc:8]=0x85b0 reloc.KERNEL32.dll_WriteFile
| | 0x00401670 ffd0 call rax
| | 0x00401672 8b857c010000 mov eax, dword [arg_17ch] ; [0x17c:4]=-1 ; 380
| | 0x00401678 89c2 mov edx, eax
| | 0x0040167a 488d0de32900. lea rcx, str.Bytes_written:__d ; 0x404064 ; "Bytes written: %d \n"
| | 0x00401681 e812150000 call sym.printf ; int printf(const char *format)
| | 0x00401686 488b85800100. mov rax, qword [arg_180h] ; [0x180:8]=-1 ; 384
| | 0x0040168d 4889c1 mov rcx, rax
| | 0x00401690 488b055d6b00. mov rax, qword sym.imp.KERNEL32.dll_CloseHandle ; [0x4081f4:8]=0x83ac reloc.KERNEL32.dll_CloseHandle
| | 0x00401697 ffd0 call rax
| | ; JMP XREF from 0x004015e5 (sym.main)
| `--> 0x00401699 b800000000 mov eax, 0
| 0x0040169e 4881c4100200. add rsp, 0x210
| 0x004016a5 5d pop rbp
\ 0x004016a6 c3 ret
[0x00401550]>
Things start to get interesting here, at first, the program calles CreateFile to actually create a file on disk. CreateFile can be used for creating a new file or for opening an existing one.
Whats interesting regarding to this function is that it will return a handle to the newle created/opened file. That handle will be used for read/write/seek operations on the file.
| 0x0040158b c74424200200. mov dword [local_20h], 2
| 0x00401593 41b900000000 mov r9d, 0
| 0x00401599 41b802000000 mov r8d, 2
| 0x0040159f ba00000040 mov edx, 0x40000000
| 0x004015a4 4889c1 mov rcx, rax
| 0x004015a7 488b054e6c00. mov rax, qword sym.imp.KERNEL32.dll_CreateFileA ; [0x4081fc:8]=0x83ba reloc.KERNEL32.dll_CreateFileA
| 0x004015ae ffd0 call rax
| 0x004015b0 b 488985800100. mov qword [arg_180h], rax ; [0x180:8]=-1 ; 384
| 0x004015b7 4883bd800100. cmp qword [arg_180h], 0xffffffffffffffff
| ,=< 0x004015bf 7529 jne 0x4015ea
| | 0x004015c1 488b05646c00. mov rax, qword sym.imp.KERNEL32.dll_GetLastError ; [0x40822c:8]=0x8438 reloc.KERNEL32.dll_GetLastError ; "8\x84"
| | 0x004015c8 ffd0 call rax
So here’s the handle after the file gets opened
[0x004015b0]> dr rax
0x00000020
Then the program will check wether the file has been opened with an error or not, as CreateFile returns an int (file handle), 0xffffffffffffffff is associated with an error opening the file, so that cmp will check if the file opened correctly.
| 0x004015b0 488985800100. mov qword [arg_180h], rax ; [0x180:8]=-1 ; 384
;-- rip:
| 0x004015b7 b 4883bd800100. cmp qword [arg_180h], 0xffffffffffffffff
| ,=< 0x004015bf 7529 jne 0x4015ea
| | 0x004015c1 488b05646c00. mov rax, qword sym.imp.KERNEL32.dll_GetLastError ; [0x40822c:8]=0x77801300
| | 0x004015c8 ffd0 call rax
At this point of the program, the file “newfile.txt” will exist on the filesystem with no content, and if we try to delete it, it won’t be possible, because the file has been registered open by our program (as long as we have the file handle, the file is open and can only be accessed according to the flags you just saw on the code).
Then the program declares a simple array of chars (buffer) and calls WriteFile
| | 0x00401669 488b054c6c00. mov rax, qword sym.imp.KERNEL32.dll_WriteFile ; [0x4082bc:8]=0x77801a
| ;-- rip:
| | 0x00401670 b ffd0 call rax
| | 0x00401672 b 8b857c010000 mov eax, dword [arg_17ch] ; [0x17c:4]=-1 ; 380
| | 0x00401678 89c2 mov edx, eax
| | 0x0040167a 488d0de32900. lea rcx, str.Bytes_written:__d ; 0x404064 ; "Bytes written: %d \n"
Right before the call, the program memory looks like this:
[0x00401670]> afvd
var arg_188h = 0x0022fe48 0x0000000000404000 .@@..... (IMAGE winapi_CREATEFILE.exe | .data) section..rdata ascii R 0x6c706d61735c3a43 (C:\samples\newfi
var arg_180h = 0x0022fe40 0x0000000000000020 ....... rcx ascii
var local_40h = 0x0022fc80 0x4153454c504d4153 SAMPLESA @rdx ascii
var local_38h = 0x0022fc88 0x504d4153454c504d MPLESAMP ascii
var local_30h_2 = 0x0022fc90 0x000000004554454c LETE.... ascii
var arg_17ch = 0x0022fe3c 0x0000002000000000 .... ... @r9 ascii
[0x00401670]>
[0x00401670]> pxw @ 0x0022fc80
0x0022fc80 0x504d4153 0x4153454c 0x454c504d 0x504d4153 SAMPLESAMPLESAMP
0x0022fc90 0x4554454c 0x00000000 0x00611f50 0x00000000 LETE....P.a.....
0x0022fca0 0x77976850 0x00000000 0x00000001 0x00000000 Ph.w............
0x0022fcb0 0x00610000 0x00000000 0x14070013 0x00000000 ..a.............
As you can see, the program holds multiple references that correspond to parts of the array, that makes sense because the contents of the char array (string) have been initialised in memory for the program.
WriteFile is called with two interesting parameters here (file handle aside). Those are the byteswritten DWORD (double word ~= int) that will hold the number of bytes written to the file and 5, the actual number of bytes (characters) we want to write to file.
The byteswritten var will be updated after the writefile. After doing the write, arg_17ch in this case will be updated with 5 (if everything went ok)
[0x00401670]> dr
rax = 0x00000005
```
In this case the file won't be available (for reading) after the handle is freed byt CloseHandle. Let's see
```
| | 0x0040168d 4889c1 mov rcx, rax
| | 0x00401690 488b055d6b00. mov rax, qword sym.imp.KERNEL32.dll_CloseHandle ; [0x4081f4:8]=0x778014b0
| | 0x00401697 ffd0 call rax
| | ; JMP XREF from 0x004015e5 (sym.main)
| `--> 0x00401699 b800000000 mov eax, 0
```
CloseHandle frees the file, and thus can be normally used in other programs.
All of the details regarding to WriteFile can be found on the msdn here: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
Now that you get general idea on CreateFile and WriteFile I advice you to read this one: https://docs.microsoft.com/en-us/windows/win32/fileio/file-buffering if you want to know more about how WriteFile deals with this topic (temporal buffering and such)
#### Read and Write files ReadFile and WriteFile
As you can guess if we have a WriteFile call, we also have a ReadFile call as well. ReadFile works kind of the same, we need to pass similar variables to it such as the file handle, a buffer for the bytes to be read, the num of bytes to read and a pointer for "bytesread".
With Write/Read we can also use overlapping but I won't go deep on the topic (FOR NOW), read it here: https://docs.microsoft.com/en-us/windows/win32/devio/overlapped-operations
So, the following program opens two files, one for Read and the other for Write, then it starts "transfering" bytes from one file to the other.
```C
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define BUFFERSIZE 180
int main()
{
printf("Reading %d bytes: \n", BUFFERSIZE);
DWORD dwBytesRead = 0;
char ReadBuffer[BUFFERSIZE] = {0};
int err;
int totalBytesRead = 0;
int r = 0;
int hFile = CreateFile("C:\\samples\\newfile.txt", // file to open
GENERIC_READ, // open for read
FILE_SHARE_READ, // share for read
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
int hFile2 = CreateFile("C:\\samples\\newfile2.txt", // file to open
GENERIC_WRITE, // open for write
FILE_SHARE_WRITE, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
if (hFile == INVALID_HANDLE_VALUE || hFile2 == INVALID_HANDLE_VALUE)
{
err = GetLastError();
printf("Error reading file, error code: %d \n", err);
}
else{
int fsize = GetFileSize(hFile, NULL);
printf("File size in bytes: %d \n", fsize);
printf("File content: \n");
while(totalBytesRead < fsize){
ReadFile(hFile, ReadBuffer, BUFFERSIZE-1, &dwBytesRead, NULL);
printf("%s", ReadBuffer);
totalBytesRead += dwBytesRead;
WriteFile(hFile2, ReadBuffer, dwBytesRead,NULL,NULL);
}
}
CloseHandle(hFile);
CloseHandle(hFile2);
return 0;
}
```
Let's check it, be ready cause the following disams is LARGE
```
[0x77953840]> s sym.main
[0x00401550]> pdf
/ (fcn) sym.main 580
| sym.main (int arg_80h, int arg_84h, int arg_88h, int arg_8ch, int arg_90h, int arg_94h, int arg_98h, int arg_9ch);
| ; var int local_40h @ rbp-0x40
| ; arg int arg_80h @ rbp+0x80
| ; arg int arg_84h @ rbp+0x84
| ; arg int arg_88h @ rbp+0x88
| ; arg int arg_8ch @ rbp+0x8c
| ; arg int arg_90h @ rbp+0x90
| ; arg int arg_94h @ rbp+0x94
| ; arg int arg_98h @ rbp+0x98
| ; arg int arg_9ch @ rbp+0x9c
| ; var int local_20h @ rsp+0x20
| ; var int local_28h @ rsp+0x28
| ; var int local_30h @ rsp+0x30
| ; var int local_80h @ rsp+0x80
| ; CALL XREF from 0x004013c2 (sym.__tmainCRTStartup)
| 0x00401550 55 push rbp
| 0x00401551 57 push rdi
| 0x00401552 4881ec280100. sub rsp, 0x128
| 0x00401559 488dac248000. lea rbp, [local_80h] ; 0x80 ; 128
| 0x00401561 e8ea020000 call sym.__main
| 0x00401566 bab4000000 mov edx, 0xb4 ; 180
| 0x0040156b 488d0d8e2a00. lea rcx, str.Reading__d_bytes: ; section..rdata ; 0x404000 ; "Reading %d bytes: \n"
| 0x00401572 e811170000 call sym.printf ; int printf(const char *format)
| 0x00401577 c78580000000. mov dword [arg_80h], 0 ; [0x80:4]=-1 ; 0
| 0x00401581 c78598000000. mov dword [arg_98h], 0 ; [0x98:4]=-1 ; 0
| 0x0040158b 488d55c0 lea rdx, [local_40h]
| 0x0040158f b800000000 mov eax, 0
| 0x00401594 b916000000 mov ecx, 0x16 ; 22
| 0x00401599 4889d7 mov rdi, rdx
| 0x0040159c f348ab rep stosq qword [rdi], rax
| 0x0040159f 4889fa mov rdx, rdi
| 0x004015a2 8902 mov dword [rdx], eax
| 0x004015a4 4883c204 add rdx, 4
| 0x004015a8 c7859c000000. mov dword [arg_9ch], 0 ; [0x9c:4]=-1 ; 0
| 0x004015b2 c78594000000. mov dword [arg_94h], 0 ; [0x94:4]=-1 ; 0
| 0x004015bc 48c744243000. mov qword [local_30h], 0
| 0x004015c5 c74424288000. mov dword [local_28h], 0x80 ; [0x80:4]=-1 ; 128
| 0x004015cd c74424200300. mov dword [local_20h], 3
| 0x004015d5 41b900000000 mov r9d, 0
| 0x004015db 41b801000000 mov r8d, 1
| 0x004015e1 ba00000080 mov edx, 0x80000000
| 0x004015e6 488d0d272a00. lea rcx, str.C:__samples___ewfile.txt ; 0x404014 ; "C:\samples\newfile.txt"
| 0x004015ed 488b05186c00. mov rax, qword sym.imp.KERNEL32.dll_CreateFileA ; [0x40820c:8]=0x83da reloc.KERNEL32.dll_CreateFileA
| 0x004015f4 ffd0 call rax
| 0x004015f6 898590000000 mov dword [arg_90h], eax ; [0x90:4]=-1 ; 144
| 0x004015fc 48c744243000. mov qword [local_30h], 0
| 0x00401605 c74424288000. mov dword [local_28h], 0x80 ; [0x80:4]=-1 ; 128
| 0x0040160d c74424200300. mov dword [local_20h], 3
| 0x00401615 41b900000000 mov r9d, 0
| 0x0040161b 41b802000000 mov r8d, 2
| 0x00401621 ba00000040 mov edx, 0x40000000
| 0x00401626 488d0dfe2900. lea rcx, str.C:__samples___ewfile2.txt ; 0x40402b ; "C:\samples\newfile2.txt"
| 0x0040162d 488b05d86b00. mov rax, qword sym.imp.KERNEL32.dll_CreateFileA ; [0x40820c:8]=0x83da reloc.KERNEL32.dll_CreateFileA
| 0x00401634 ffd0 call rax
| 0x00401636 89858c000000 mov dword [arg_8ch], eax ; [0x8c:4]=-1 ; 140
| 0x0040163c 83bd90000000. cmp dword [arg_90h], 0xffffffffffffffff
| ,=< 0x00401643 7409 je 0x40164e
| | 0x00401645 83bd8c000000. cmp dword [arg_8ch], 0xffffffffffffffff
| ,==< 0x0040164c 7528 jne 0x401676
| || ; JMP XREF from 0x00401643 (sym.main)
| |`-> 0x0040164e 488b05ef6b00. mov rax, qword sym.imp.KERNEL32.dll_GetLastError ; [0x408244:8]=0x8466 reloc.KERNEL32.dll_GetLastError ; "f\x84"
| | 0x00401655 ffd0 call rax
| | 0x00401657 898584000000 mov dword [arg_84h], eax ; [0x84:4]=-1 ; 132
| | 0x0040165d 8b8584000000 mov eax, dword [arg_84h] ; [0x84:4]=-1 ; 132
| | 0x00401663 89c2 mov edx, eax
| | 0x00401665 488d0ddc2900. lea rcx, str.Error_reading_file__error_code:___d ; 0x404048 ; "Error reading file, error code: %d \n"
| | 0x0040166c e817160000 call sym.printf ; int printf(const char *format)
| |,=< 0x00401671 e9e7000000 jmp 0x40175d
| || ; JMP XREF from 0x0040164c (sym.main)
| `--> 0x00401676 8b8590000000 mov eax, dword [arg_90h] ; [0x90:4]=-1 ; 144
| | 0x0040167c 4898 cdqe
| | 0x0040167e ba00000000 mov edx, 0
| | 0x00401683 4889c1 mov rcx, rax
| | 0x00401686 488b05af6b00. mov rax, qword sym.imp.KERNEL32.dll_GetFileSize ; [0x40823c:8]=0x8458 reloc.KERNEL32.dll_GetFileSize ; "X\x84"
| | 0x0040168d ffd0 call rax
| | 0x0040168f 898588000000 mov dword [arg_88h], eax ; [0x88:4]=-1 ; 136
| | 0x00401695 8b8588000000 mov eax, dword [arg_88h] ; [0x88:4]=-1 ; 136
| | 0x0040169b 89c2 mov edx, eax
| | 0x0040169d 488d0dca2900. lea rcx, str.File_size_in_bytes:__d ; 0x40406e ; "File size in bytes: %d \n"
| | 0x004016a4 e8df150000 call sym.printf ; int printf(const char *format)
| | 0x004016a9 488d0dd72900. lea rcx, str.File_content: ; 0x404087 ; "File content: "
| | 0x004016b0 e8cb150000 call sym.puts ; int puts(const char *s)
| ,==< 0x004016b5 e991000000 jmp 0x40174b
| || ; JMP XREF from 0x00401757 (sym.main)
| .---> 0x004016ba 8b8590000000 mov eax, dword [arg_90h] ; [0x90:4]=-1 ; 144
| :|| 0x004016c0 4898 cdqe
| :|| 0x004016c2 4889c1 mov rcx, rax
| :|| 0x004016c5 488d95800000. lea rdx, [arg_80h] ; 0x80 ; 128
| :|| 0x004016cc 488d45c0 lea rax, [local_40h]
| :|| 0x004016d0 48c744242000. mov qword [local_20h], 0
| :|| 0x004016d9 4989d1 mov r9, rdx
| :|| 0x004016dc 41b8b3000000 mov r8d, 0xb3 ; 179
| :|| 0x004016e2 4889c2 mov rdx, rax
| :|| 0x004016e5 488b05906b00. mov rax, qword sym.imp.KERNEL32.dll_ReadFile ; [0x40827c:8]=0x8500 reloc.KERNEL32.dll_ReadFile
| :|| 0x004016ec ffd0 call rax
| :|| 0x004016ee 488d45c0 lea rax, [local_40h]
| :|| 0x004016f2 4889c2 mov rdx, rax
| :|| 0x004016f5 488d0d9a2900. lea rcx, [0x00404096] ; "%s"
| :|| 0x004016fc e887150000 call sym.printf ; int printf(const char *format)
| :|| 0x00401701 8b959c000000 mov edx, dword [arg_9ch] ; [0x9c:4]=-1 ; 156
| :|| 0x00401707 8b8580000000 mov eax, dword [arg_80h] ; [0x80:4]=-1 ; 128
| :|| 0x0040170d 01d0 add eax, edx
| :|| 0x0040170f 89859c000000 mov dword [arg_9ch], eax ; [0x9c:4]=-1 ; 156
| :|| 0x00401715 488d85800000. lea rax, [arg_80h] ; 0x80 ; 128
| :|| 0x0040171c 89c2 mov edx, eax
| :|| 0x0040171e 8b858c000000 mov eax, dword [arg_8ch] ; [0x8c:4]=-1 ; 140
| :|| 0x00401724 4898 cdqe
| :|| 0x00401726 4889c1 mov rcx, rax
| :|| 0x00401729 488d45c0 lea rax, [local_40h]
| :|| 0x0040172d 48c744242000. mov qword [local_20h], 0
| :|| 0x00401736 41b900000000 mov r9d, 0
| :|| 0x0040173c 4189d0 mov r8d, edx
| :|| 0x0040173f 4889c2 mov rdx, rax
| :|| 0x00401742 488b05936b00. mov rax, qword sym.imp.KERNEL32.dll_WriteFile ; [0x4082dc:8]=0x85ea reloc.KERNEL32.dll_WriteFile
| :|| 0x00401749 ffd0 call rax
| :|| ; JMP XREF from 0x004016b5 (sym.main)
| :`--> 0x0040174b 8b859c000000 mov eax, dword [arg_9ch] ; [0x9c:4]=-1 ; 156
| : | 0x00401751 3b8588000000 cmp eax, dword [arg_88h] ; [0x88:4]=-1 ; 136
| `===< 0x00401757 0f8c5dffffff jl 0x4016ba
| | ; JMP XREF from 0x00401671 (sym.main)
| `-> 0x0040175d 8b8590000000 mov eax, dword [arg_90h] ; [0x90:4]=-1 ; 144
| 0x00401763 4898 cdqe
| 0x00401765 4889c1 mov rcx, rax
| 0x00401768 488b05956a00. mov rax, qword sym.imp.KERNEL32.dll_CloseHandle ; [0x408204:8]=0x83cc reloc.KERNEL32.dll_CloseHandle
| 0x0040176f ffd0 call rax
| 0x00401771 8b858c000000 mov eax, dword [arg_8ch] ; [0x8c:4]=-1 ; 140
| 0x00401777 4898 cdqe
| 0x00401779 4889c1 mov rcx, rax
| 0x0040177c 488b05816a00. mov rax, qword sym.imp.KERNEL32.dll_CloseHandle ; [0x408204:8]=0x83cc reloc.KERNEL32.dll_CloseHandle
| 0x00401783 ffd0 call rax
| 0x00401785 b800000000 mov eax, 0
| 0x0040178a 4881c4280100. add rsp, 0x128
| 0x00401791 5f pop rdi
| 0x00401792 5d pop rbp
\ 0x00401793 c3 ret
[0x00401550]>
```
Eventhough the program looks confusing, the magic happens here:
```
| ,==< 0x004016b5 e991000000 jmp 0x40174b
| || ; JMP XREF from 0x00401757 (sym.main)
| .---> 0x004016ba 8b8590000000 mov eax, dword [arg_90h] ; [0x90:4]=-1 ; 144
| :|| 0x004016c0 4898 cdqe
| :|| 0x004016c2 4889c1 mov rcx, rax
| :|| 0x004016c5 488d95800000. lea rdx, [arg_80h] ; 0x80 ; 128
| :|| 0x004016cc 488d45c0 lea rax, [local_40h]
| :|| 0x004016d0 48c744242000. mov qword [local_20h], 0
| :|| 0x004016d9 4989d1 mov r9, rdx
| :|| 0x004016dc 41b8b3000000 mov r8d, 0xb3 ; 179
| :|| 0x004016e2 4889c2 mov rdx, rax
| :|| 0x004016e5 488b05906b00. mov rax, qword sym.imp.KERNEL32.dll_ReadFile ; [0x40827c:8]=0x8500 reloc.KERNEL32.dll_ReadFile
| :|| 0x004016ec ffd0 call rax
| :|| 0x004016ee 488d45c0 lea rax, [local_40h]
| :|| 0x004016f2 4889c2 mov rdx, rax
| :|| 0x004016f5 488d0d9a2900. lea rcx, [0x00404096] ; "%s"
| :|| 0x004016fc e887150000 call sym.printf ; int printf(const char *format)
| :|| 0x00401701 8b959c000000 mov edx, dword [arg_9ch] ; [0x9c:4]=-1 ; 156
| :|| 0x00401707 8b8580000000 mov eax, dword [arg_80h] ; [0x80:4]=-1 ; 128
| :|| 0x0040170d 01d0 add eax, edx
| :|| 0x0040170f 89859c000000 mov dword [arg_9ch], eax ; [0x9c:4]=-1 ; 156
| :|| 0x00401715 488d85800000. lea rax, [arg_80h] ; 0x80 ; 128
| :|| 0x0040171c 89c2 mov edx, eax
| :|| 0x0040171e 8b858c000000 mov eax, dword [arg_8ch] ; [0x8c:4]=-1 ; 140
| :|| 0x00401724 4898 cdqe
| :|| 0x00401726 4889c1 mov rcx, rax
| :|| 0x00401729 488d45c0 lea rax, [local_40h]
| :|| 0x0040172d 48c744242000. mov qword [local_20h], 0
| :|| 0x00401736 41b900000000 mov r9d, 0
| :|| 0x0040173c 4189d0 mov r8d, edx
| :|| 0x0040173f 4889c2 mov rdx, rax
| :|| 0x00401742 488b05936b00. mov rax, qword sym.imp.KERNEL32.dll_WriteFile ; [0x4082dc:8]=0x85ea reloc.KERNEL32.dll_WriteFile
| :|| 0x00401749 ffd0 call rax
| :|| ; JMP XREF from 0x004016b5 (sym.main)
| :`--> 0x0040174b 8b859c000000 mov eax, dword [arg_9ch] ; [0x9c:4]=-1 ; 156
| : | 0x00401751 3b8588000000 cmp eax, dword [arg_88h] ; [0x88:4]=-1 ; 136
| `===< 0x00401757 0f8c5dffffff jl 0x4016ba
| | ; JMP XREF from 0x00401671 (sym.main)
```
I strongly recommend you to always check for loops, they usually represent solid works of code that run interesting stuff.
They key of this block and the new thing here is the ReadFile call
| :|| 0x004016c2 4889c1 mov rcx, rax | :|| 0x004016c5 488d95800000. lea rdx, [arg_80h] ; 0x80 ; 128 | :|| 0x004016cc 488d45c0 lea rax, [local_40h] | :|| 0x004016d0 48c744242000. mov qword [local_20h], 0 | :|| 0x004016d9 4989d1 mov r9, rdx | :|| 0x004016dc 41b8b3000000 mov r8d, 0xb3 ; 179 | :|| 0x004016e2 4889c2 mov rdx, rax | :|| 0x004016e5 488b05906b00. mov rax, qword sym.imp.KERNEL32.dll_ReadFile ; [0x40827c:8]=0x8500 reloc.KERNEL32.dll_ReadFile | :|| 0x004016ec b ffd0 call rax | :|| 0x004016ee 488d45c0 lea rax, [local_40h] | :|| 0x004016f2 b 4889c2 mov rdx, rax | :|| 0x004016f5 488d0d9a2900. lea rcx, [0x00404096] ; “%s” | :|| 0x004016fc e887150000 call sym.printf ; int printf(const char *format)
For us the most important parameters are the pointer to the mem space that will hold the bytes read and then the num of bytes to read. On this case 0xb3 is the num of bytes to read and those will be loaded inside local_40h
As we are already open for debug, we can just figure that out by setting breakpoints before and after the call and examining the memory:
[0x004016ec]> afvd var arg_80h = 0x0022fe20 0x0000000000000000 …….. @r9 r15 var arg_98h = 0x0022fe38 0x0000000000000000 …….. r15 var local_40h = 0x0022fd60 0x0000000000000000 …….. @rdx r15 var arg_9ch = 0x0022fe3c 0x00371f8000000000 ……7. var arg_94h = 0x0022fe34 0x0000000000000000 …….. r15 var arg_90h = 0x0022fe30 0x0000000000000020 ……. rcx ascii var arg_8ch = 0x0022fe2c 0x0000002000000024 $… … ascii var arg_84h = 0x0022fe24 0x0000000500000000 …….. var arg_88h = 0x0022fe28 0x0000002400000005 ….$… [0x004016ec]> dc hit breakpoint at: 4016f2 [0x004016ec]> afvd var arg_80h = 0x0022fe20 0x0000000000000005 …….. var arg_98h = 0x0022fe38 0x0000000000000000 …….. rdx var local_40h = 0x0022fd60 0x0000004c504d4153 SAMPL… @rax ascii var arg_9ch = 0x0022fe3c 0x00371f8000000000 ……7. var arg_94h = 0x0022fe34 0x0000000000000000 …….. rdx var arg_90h = 0x0022fe30 0x0000000000000020 ……. ascii var arg_8ch = 0x0022fe2c 0x0000002000000024 $… … ascii var arg_84h = 0x0022fe24 0x0000000500000000 …….. var arg_88h = 0x0022fe28 0x0000002400000005 ….$… [0x004016ec]>
Voilà
Then the num of bytes read is added every time to a particular variable (that CMPed at the end of the loop)
| :|| 0x004016f5 488d0d9a2900. lea rcx, [0x00404096] | :|| 0x004016fc e887150000 call sym.printf | :|| 0x00401701 8b959c000000 mov edx, dword [arg_9ch] | :|| 0x00401707 8b8580000000 mov eax, dword [arg_80h] | :|| 0x0040170d 01d0 add eax, edx | :|| 0x0040170f 89859c000000 mov dword [arg_9ch], eax | :|| 0x00401715 488d85800000. lea rax, [arg_80h] | :|| 0x0040171c 89c2 mov edx, eax
That is probably the counter
We can see that the program is using a 179 byte buffer to temp save those bytes read and then to WriteFile them into the other file. If we are going quick and blind we can inspect the status of the variable like this
0x0022fe00 0x4f444e41 0x4554474d 0x41525458 0x4d4f444e ANDOMGTEXTRANDOM 0x0022fe10 0x00455447 0x00000000 0x00000000 0x00000000 GTE…………. 0x0022fe20 0x000000b3 0x00000000 0x00000861 0x00000024 ……..a…$… 0x0022fe30 0x00000020 0x00000000 0x00000000 0x00000166 ………..f… 0x0022fe40 0x002e1f80 0x00000000 0x002e1f80 0x00000000 ……………. 0x0022fe50 0x002e1f40 0x00000000 0x004013c7 0x00000000 @………@….. [0x004016ee]> dc hit breakpoint at: 4016ee [0x004016ee]> pxw @ 0x0022fd60 0x0022fd60 0x41525458 0x4d4f444e 0x58455447 0x4e415254 XTRANDOMGTEXTRAN 0x0022fd70 0x474d4f44 0x54584554 0x444e4152 0x54474d4f DOMGTEXTRANDOMGT
As we can see the "text" continues after the previous loaded text. And that's all for this one, CloseHandle is called next and the program is done, let's proceed with another example
#### CopyFile
As you saw, we've been MANUALLY copying that file by loading the contents of one file and progressively dumping them to the other one. In a similar way with what we done in the past tutorial by using SendFile (it is not exactly the same though) we can just do one call to copy the file to the other destionation without doing manual stuff, the function will do all the heavy lifting work internally.
Check that:
```C
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
printf("Copying file using 'CopyFile'");
int b = CopyFile("C:\\samples\\newfile.txt","C:\\samples\\newfile2.txt",0);
if (!b) {
int err = GetLastError();
printf("Error: %d",err);
} else {
printf("File copied\n");
}
return 0;
}
As you see, no file handles here, just the paths and we are done.
[0x00401550]> pdf
/ (fcn) sym.main 117
| sym.main ();
| ; var int local_8h @ rbp-0x8
| ; var int local_4h @ rbp-0x4
| ; CALL XREF from 0x004013c2 (sym.__tmainCRTStartup)
| 0x00401550 55 push rbp
| 0x00401551 4889e5 mov rbp, rsp
| 0x00401554 4883ec30 sub rsp, 0x30 ; '0'
| 0x00401558 e823010000 call sym.__main
| 0x0040155d 488d0d9c2a00. lea rcx, str.Copying_file_using__CopyFile ; section..rdata ; 0x404000 ; "Copying file using 'CopyFile'"
| 0x00401564 e84f150000 call sym.printf ; int printf(const char *format)
| 0x00401569 41b800000000 mov r8d, 0
| 0x0040156f 488d15a82a00. lea rdx, str.C:__samples___ewfile2.txt ; 0x40401e ; "C:\samples\newfile2.txt"
| 0x00401576 488d0db92a00. lea rcx, str.C:__samples___ewfile.txt ; 0x404036 ; "C:\samples\newfile.txt"
| 0x0040157d 488b05606c00. mov rax, qword sym.imp.KERNEL32.dll_CopyFileA ; [0x4081e4:8]=0x838c reloc.KERNEL32.dll_CopyFileA
| 0x00401584 ffd0 call rax
| 0x00401586 8945fc mov dword [local_4h], eax
| 0x00401589 837dfc00 cmp dword [local_4h], 0
| ,=< 0x0040158d 751f jne 0x4015ae
| | 0x0040158f 488b057e6c00. mov rax, qword sym.imp.KERNEL32.dll_GetLastError ; [0x408214:8]=0x8408 reloc.KERNEL32.dll_GetLastError
| | 0x00401596 ffd0 call rax
| | 0x00401598 8945f8 mov dword [local_8h], eax
| | 0x0040159b 8b45f8 mov eax, dword [local_8h]
| | 0x0040159e 89c2 mov edx, eax
| | 0x004015a0 488d0da62a00. lea rcx, str.Error:__d ; 0x40404d ; "Error: %d"
| | 0x004015a7 e80c150000 call sym.printf ; int printf(const char *format)
| ,==< 0x004015ac eb0c jmp 0x4015ba
| || ; JMP XREF from 0x0040158d (sym.main)
| |`-> 0x004015ae 488d0da22a00. lea rcx, str.File_copied ; 0x404057 ; "File copied"
| | 0x004015b5 e8f6140000 call sym.puts ; int puts(const char *s)
| | ; JMP XREF from 0x004015ac (sym.main)
| `--> 0x004015ba b800000000 mov eax, 0
| 0x004015bf 4883c430 add rsp, 0x30 ; '0'
| 0x004015c3 5d pop rbp
\ 0x004015c4 c3 ret
[0x00401550]>
Nothing worth mentioning here you just see that the CopyFileA is being called with those two filepaths. Let’s move on
Moving inside files LZSEEK
Same as we did with the fseek syscall in Linux we can also move inside a file by seeking. Everytime we read N bytes from a file or write N bytes to it the (internal) pointer to that file content moves N bytes, we manually move the pointer using seek
Let’s see this:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
printf("Hello world!\n");
char message[10] = "secret msg";
int hFile = CreateFile("C:\\samples\\cat.jpg", // file to open
GENERIC_WRITE, // open for write
FILE_SHARE_WRITE, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
LZSeek(hFile,0,2); //0 bytes from the end
WriteFile(hFile, message, 10,NULL,NULL);
CloseHandle(hFile);
return 0;
}
This program is interesting as it presents a concept that may or may not be new to you but it is widely used in ctf games or even some malware.
The program opens a JPG file, then uses LZSEEK to move at the end of the file, then writes the message there. So the program is not a text file its an image, but as you may know JPG files start with FF D8 FF E0 and end with FF D9 bytes
All of the content (bytes) that go after the end signature won’t be interpreted by a jpg visualizer, so adding content in files that way can be used to deliver hidden messages (or malware commands) the set of techniques that allow us to do that is called steganography!
After running this program, having the needed image on the right place on the filesystem, a “secret message” will be appended at the end, after the end signature. The image will open properly but the message will be there. We can actually open it with radare2 and inspect the message:
[0x00000000]> pxw
0x00000000 0xe0ffd8ff 0x464a1000 0x01004649 0x60000101 ......JFIF.....`
0x00000010 0x00006000 0x4300dbff 0x01010200 0x02010101 .`.....C........
0x00000020 0x02010101 0x02020202 0x02020304 0x04050202 ................
0x00000030 0x06040304 0x06060605 0x06060605 0x06080907 ................
0x00000040 0x06070907 0x080b0806 0x0a0a0a09 0x08060a0a ................
0x00000050 0x0a0b0c0b 0x0a0a090c 0x00dbff0a 0x02020143 ............C...
0x00000060 0x02020202 0x05030305 0x0706070a 0x0a0a0a0a ................
0x00000070 0x0a0a0a0a 0x0a0a0a0a 0x0a0a0a0a 0x0a0a0a0a ................
0x00000080 0x0a0a0a0a 0x0a0a0a0a 0x0a0a0a0a 0x0a0a0a0a ................
0x00000090 0x0a0a0a0a 0x0a0a0a0a 0x0a0a0a0a 0xc2ff0a0a ................
0x000000a0 0x02081100 0x03200358 0x02002201 0x11030111 ....X. .."......
0x000000b0 0x00c4ff01 0x0200001c 0x01010302 0x00000000 ................
0x000000c0 0x00000000 0x05040000 0x02010603 0xff080007 ................
0x000000d0 0x011a00c4 0x01010300 0x00000101 0x00000000 ................
0x000000e0 0x00000000 0x00030201 0xff060504 0x030c00da ................
0x000000f0 0x10020001 0x00001003 0x4ae1e801 0x497e596c ...........JlY~Is?
As you see, the file presents the image format, we can seek to the end with sG and move -+ bytes with s-+ num
[0x00022f74]> px
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x00022f74 ac71 8a48 5640 a2f8 693c e3d2 0a2d 0dc7 .q.HV@..i<...-..
0x00022f84 1c13 4c68 468f 0af5 841c 26a4 8445 8705 ..LhF.....&..E..
0x00022f94 6f10 0008 1a88 4aaf fb58 8c06 0a94 9f77 o.....J..X.....w
0x00022fa4 3930 dc8e 9b53 904e da85 843f 2fce 5b2e 90...S.N...?/.[.
0x00022fb4 c09a d499 d833 0fc0 8ee8 c874 1644 08f6 .....3.....t.D..
0x00022fc4 df3e c0c0 7f09 2555 4f0f a7d6 42a5 8e78 .>....%UO...B..x
0x00022fd4 383e 3de0 4605 9aae 7d4c 61f1 1a31 0430 8>=.F...}La..1.0
0x00022fe4 b0de bef3 91b6 1586 a035 38a9 2ae2 8956 .........58.*..V
0x00022ff4 d9eb 1e90 8890 8204 be79 fac7 4994 0d28 .........y..I..(
0x00023004 649e 262a 311c a58a 08a2 0ff7 789c d558 d.&*1.......x..X
0x00023014 55d0 2719 1a5d 0d32 b712 123f c39a 02ba U.'..].2...?....
0x00023024 6c9b 2b7c 9382 9bba 054b 7491 efce 3183 l.+|.....Kt...1.
0x00023034 70bc d7e1 c51c f34b 4b07 5ffb 8cab 2c19 p......KK._...,.
0x00023044 d9dc 5ff1 926d 4135 7953 edc1 ac11 0661 .._..mA5yS.....a
0x00023054 1e7e b1f1 8090 db56 ff00 cc10 18c3 2822 .~.....V......("
0x00023064 be4f f39f ffd9 7365 6372 6574 206d 7367 .O....secret msg
[0x00022f74]> s+20
[0x00022f88]> px
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x00022f88 468f 0af5 841c 26a4 8445 8705 6f10 0008 F.....&..E..o...
0x00022f98 1a88 4aaf fb58 8c06 0a94 9f77 3930 dc8e ..J..X.....w90..
0x00022fa8 9b53 904e da85 843f 2fce 5b2e c09a d499 .S.N...?/.[.....
0x00022fb8 d833 0fc0 8ee8 c874 1644 08f6 df3e c0c0 .3.....t.D...>..
0x00022fc8 7f09 2555 4f0f a7d6 42a5 8e78 383e 3de0 ..%UO...B..x8>=.
0x00022fd8 4605 9aae 7d4c 61f1 1a31 0430 b0de bef3 F...}La..1.0....
0x00022fe8 91b6 1586 a035 38a9 2ae2 8956 d9eb 1e90 .....58.*..V....
0x00022ff8 8890 8204 be79 fac7 4994 0d28 649e 262a .....y..I..(d.&*
0x00023008 311c a58a 08a2 0ff7 789c d558 55d0 2719 1.......x..XU.'.
0x00023018 1a5d 0d32 b712 123f c39a 02ba 6c9b 2b7c .].2...?....l.+|
0x00023028 9382 9bba 054b 7491 efce 3183 70bc d7e1 .....Kt...1.p...
0x00023038 c51c f34b 4b07 5ffb 8cab 2c19 d9dc 5ff1 ...KK._...,..._.
0x00023048 926d 4135 7953 edc1 ac11 0661 1e7e b1f1 .mA5yS.....a.~..
0x00023058 8090 db56 ff00 cc10 18c3 2822 be4f f39f ...V......(".O..
0x00023068 ffd9 7365 6372 6574 206d 7367 ffff ffff ..secret msg....
0x00023078 ffff ffff ffff ffff ffff ffff ffff ffff ................
And here we have our secret message!
Listing dirs, FindFirstFile and FindNextFile
All kind of operations related to the file system can be done with the Windows Api, by the way I think that the windows api is probably the best way to deal with operations on the filesystem. At the end, you’ll find a lot of calls to the windows api when reversing most kind of malware samples, as they parse the file system for file infection / information hidding / information extraction extraction etc
So winapi offers functions related to searching files on the disk, those are FindFirstFile and FindNextFile
The following program will look for each .txt file on the filesystem:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
int main()
{
printf("Hello world!\n");
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
char base_path[MAX_DIR_LEN] = "C:\\samples\\stor\\";
hFind = FindFirstFile("C:\\samples\\stor\\*.txt",&FindFileData);
do{
memset(base_path,0,MAX_DIR_LEN);
strcpy(base_path,"C:\\samples\\stor\\");
strcat(base_path,FindFileData.cFileName);
printf("Name= %s \n",base_path );
}
while(FindNextFile(hFind, &FindFileData) != 0);
printf("exit");
CloseHandle(hFind);
return 0;
}
FindFirstFile will be called for searching each .txt file on the samples\stor folder, a handler that relates to the actual search will be returned by the system and that handler will then be used to continue the search. We’ll assume that the system keeps track of all of the searches going on and everytime FindNextFile is called for one specific search the pointer moves on one file (on the new file handler returned).
And the disasm of the main function will look like this one:
[0x00401550]> pdf
/ (fcn) sym.main 312
| sym.main (int arg_b0h, int arg_1f8h);
| ; var int local_60h @ rbp-0x60
| ; var int local_58h @ rbp-0x58
| ; var int local_50h @ rbp-0x50
| ; var int local_48h @ rbp-0x48
| ; arg int arg_b0h @ rbp+0xb0
| ; arg int arg_1f8h @ rbp+0x1f8
| ; var int local_80h @ rsp+0x80
| ; CALL XREF from 0x004013c2 (sym.__tmainCRTStartup)
| 0x00401550 55 push rbp
| 0x00401551 57 push rdi
| 0x00401552 4881ec880200. sub rsp, 0x288
| 0x00401559 488dac248000. lea rbp, [local_80h] ; 0x80 ; 128
| 0x00401561 e8da010000 call sym.__main
| 0x00401566 488d0d932a00. lea rcx, str.Hello_world ; section..rdata ; 0x404000 ; "Hello world!"
| 0x0040156d e806160000 call sym.puts ; int puts(const char *s)
| 0x00401572 48b8433a5c73. movabs rax, 0x6c706d61735c3a43
| 0x0040157c 488945a0 mov qword [local_60h], rax
| 0x00401580 48b865735c73. movabs rax, 0x5c726f74735c7365
| 0x0040158a 488945a8 mov qword [local_58h], rax
| 0x0040158e 48c745b00000. mov qword [local_50h], 0
| 0x00401596 488d55b8 lea rdx, [local_48h]
| 0x0040159a b800000000 mov eax, 0
| 0x0040159f b91d000000 mov ecx, 0x1d ; 29
| 0x004015a4 4889d7 mov rdi, rdx
| 0x004015a7 f348ab rep stosq qword [rdi], rax
| 0x004015aa 4889fa mov rdx, rdi
| 0x004015ad 8902 mov dword [rdx], eax
| 0x004015af 4883c204 add rdx, 4
| 0x004015b3 488d85b00000. lea rax, [arg_b0h] ; 0xb0 ; 176
| 0x004015ba 4889c2 mov rdx, rax
| 0x004015bd 488d0d492a00. lea rcx, str.C:__samples__stor___.txt ; 0x40400d ; "C:\samples\stor\*.txt"
| 0x004015c4 488b05516c00. mov rax, qword sym.imp.KERNEL32.dll_FindFirstFileA ; [0x40821c:8]=0x840a reloc.KERNEL32.dll_FindFirstFileA ; "\n\x8
| 0x004015cb ffd0 call rax
| 0x004015cd 488985f80100. mov qword [arg_1f8h], rax ; [0x1f8:8]=-1 ; 504
| ; JMP XREF from 0x00401654 (sym.main)
| .-> 0x004015d4 488d45a0 lea rax, [local_60h]
| : 0x004015d8 41b804010000 mov r8d, 0x104 ; 260
| : 0x004015de ba00000000 mov edx, 0
| : 0x004015e3 4889c1 mov rcx, rax
| : 0x004015e6 e89d150000 call sym.memset ; void *memset(void *s, int c, size_t n)
| : 0x004015eb 488d45a0 lea rax, [local_60h]
| : 0x004015ef 48bf433a5c73. movabs rdi, 0x6c706d61735c3a43
| : 0x004015f9 488938 mov qword [rax], rdi
| : 0x004015fc 48bf65735c73. movabs rdi, 0x5c726f74735c7365
| : 0x00401606 48897808 mov qword [rax + 8], rdi
| : 0x0040160a c6401000 mov byte [rax + 0x10], 0
| : 0x0040160e 488d85b00000. lea rax, [arg_b0h] ; 0xb0 ; 176
| : 0x00401615 488d502c lea rdx, [rax + 0x2c] ; ',' ; 44
| : 0x00401619 488d45a0 lea rax, [local_60h]
| : 0x0040161d 4889c1 mov rcx, rax
| : 0x00401620 e843150000 call sym.strcat ; char*strcat(char *s1, const char *s2)
| : 0x00401625 488d45a0 lea rax, [local_60h]
| : 0x00401629 4889c2 mov rdx, rax
| : 0x0040162c 488d0df02900. lea rcx, str.Name___s ; 0x404023 ; "Name= %s \n"
| : 0x00401633 e848150000 call sym.printf ; int printf(const char *format)
| : 0x00401638 488d85b00000. lea rax, [arg_b0h] ; 0xb0 ; 176
| : 0x0040163f 488b8df80100. mov rcx, qword [arg_1f8h] ; [0x1f8:8]=-1 ; 504
| : 0x00401646 4889c2 mov rdx, rax
| : 0x00401649 488b05d46b00. mov rax, qword sym.imp.KERNEL32.dll_FindNextFileA ; [0x408224:8]=0x841c reloc.KERNEL32.dll_FindNextFileA
| : 0x00401650 ffd0 call rax
| : 0x00401652 85c0 test eax, eax
| `=< 0x00401654 0f857affffff jne 0x4015d4
| 0x0040165a 488d0dcd2900. lea rcx, str.exit ; 0x40402e ; "exit"
| 0x00401661 e81a150000 call sym.printf ; int printf(const char *format)
| 0x00401666 488b85f80100. mov rax, qword [arg_1f8h] ; [0x1f8:8]=-1 ; 504
| 0x0040166d 4889c1 mov rcx, rax
| 0x00401670 488b058d6b00. mov rax, qword sym.imp.KERNEL32.dll_CloseHandle ; [0x408204:8]=0x83cc reloc.KERNEL32.dll_CloseHandle
| ; JMP XREF from 0x00401601 (sym.main)
| 0x00401677 ffd0 call rax
| 0x00401679 b800000000 mov eax, 0
| 0x0040167e 4881c4880200. add rsp, 0x288
| 0x00401685 5f pop rdi
| 0x00401686 5d pop rbp
\ 0x00401687 c3 ret
[0x00401550]>
Let’s go step by step. As the code is a bit large and we already know about almost everything here, we’ll focus on the new stuff
So we see that FindFirstFileA is called. You may be wondering why the “A”, that A stands for ASCII, it means that the function will deal with ascii encoding, same call without the a can be found as well.
The call will return a search handle, an identifier for the ongoing search pointing to an internal structure that knows at what point on the search the progam is, then a “user” struct related to the last/actual file found will be updated.
| 0x004015bd 488d0d492a00. lea rcx, str.C:__samples__stor___.txt ; 0x40400d ; "C:\samples\stor\*.txt"
| 0x004015c4 488b05516c00. mov rax, qword sym.imp.KERNEL32.dll_FindFirstFileA ; [0x40821c:8]=0x7751c530 ; "0\xc5Qw"
| 0x004015cb ffd0 call rax
;-- rip:
| 0x004015cd b 488985f80100. mov qword [arg_1f8h], rax ; [0x1f8:8]=-1 ; 504
| ; JMP XREF from 0x00401654 (sym.main)
[0x004015cd]> dr rax
0x00255850
The struct (WIN32_FIND_DATA) for the file info is now loaded inside our arg_b0h variable
[0x004015cd]> afvd
var local_60h = 0x0022fbe0 0x6c706d61735c3a43 C:\sampl ascii
var local_58h = 0x0022fbe8 0x5c726f74735c7365 es\stor\ ascii
var local_50h = 0x0022fbf0 0x0000000000000000 ........ r15
var local_48h = 0x0022fbf8 0x0000000000000000 ........ r15
var arg_b0h = 0x0022fcf0 0xcadba01200000020 .......
var arg_1f8h = 0x0022fe38 0x000000000000002c ,....... rsi ascii
[0x004015cd]> pxw @ 0x0022fcf0
0x0022fcf0 0x00000020 0xcadba012 0x01d632af 0xcadba012 ........2......
0x0022fd00 0x01d632af 0x0bff61f4 0x01d632b3 0x00000000 .2...a...2......
0x0022fd10 0x00000005 0x00000000 0x004a1f00 0x66647367 ..........J.gsdf
0x0022fd20 0x66647367 0x78742e67 0x50000074 0x00000000 gsdfg.txt..P....
0x0022fd30 0x004a0001 0x00000000 0x00000100 0x00000000 ..J.............
0x0022fd40 0x004a2cd0 0x00000000 0x00000010 0x00000000 .,J.............
0x0022fd50 0x004a2cd8 0x00000000 0x777741df 0x00000000 .,J......Aww....
0x0022fd60 0x004a0000 0x00000000 0x50000061 0x00000000 ..J.....a..P....
0x0022fd70 0x004a0000 0x00000000 0x00000008 0x00000000 ..J.............
The WIN32_FIND_DATA struct has the following aspect
typedef struct _WIN32_FIND_DATA { // wfd
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
TCHAR cFileName[ MAX_PATH ];
TCHAR cAlternateFileName[ 14 ];
} WIN32_FIND_DATA;
And we set a breakpoint after FindFirstFile is called run the program untill that point and then map the structure as we are already used to:
[0x004015cd]> "td struct w32find {int dwFileAttributes; long long ftCreationTime; long long ftLastAccessTime; long long ftLastWriteTime; int nFileSizeHigh; int
eserved0; int dwReserved1; char* cFileName; char* cAlternateFileName; };";
[0x004015cd]> tp w32find @ 0x0022fcf0
dwFileAttributes : 0x0022fcf0 = 32
ftCreationTime : 0x0022fcf4 = (qword)0x01d632afcadba012
ftLastAccessTime : 0x0022fcfc = (qword)0x01d632afcadba012
ftLastWriteTime : 0x0022fd04 = (qword)0x01d632b30bff61f4
nFileSizeHigh : 0x0022fd0c = 0
nFileSizeLow : 0x0022fd10 = 5
dwReserved0 : 0x0022fd14 = 0
dwReserved1 : 0x0022fd18 = 4857600
cFileName : 0x0022fd1c = gsdfgsdfg.txt
cAlternateFileName : 0x0022fd2a =
[0x004015cd]>
You see, having a strong knowledge on data types and being able to do some research for internal structs related to the OS/Lang/Libs is fundamental, having this struct mapped will save a lot of time in the future, specially here where we have a loop that loads that struct with file data, file after file.
So we can easily grasp the filename and size, not bad.
Again, the magic about mapping the struct is that now we only need to set another breakpoint there, hit dc and then hit the up arrow two times and enter to get file information for each new file.
[0x004015cd]> db 0x00401652
[0x004015cd]> dc
hit breakpoint at: 401652
[0x00401652]> tp w32find @ 0x0022fcf0
dwFileAttributes : 0x0022fcf0 = 32
ftCreationTime : 0x0022fcf4 = (qword)0x01d632afc88551c7
ftLastAccessTime : 0x0022fcfc = (qword)0x01d632afc88551c7
ftLastWriteTime : 0x0022fd04 = (qword)0x01d631fb3a23cec3
nFileSizeHigh : 0x0022fd0c = 0
nFileSizeLow : 0x0022fd10 = 5
dwReserved0 : 0x0022fd14 = 7536743
dwReserved1 : 0x0022fd18 = 6684772
cFileName : 0x0022fd1c = newfile2 (2).txt
cAlternateFileName : 0x0022fd2d =
[0x00401652]>
[0x00401652]>
And there we have the data, if you are wondering about what those hex numbers related to TIME data mean look:
0x01d632afc88551c7 = 132348969618854343dec and thats a TIMESTAMP = Monday, 25 May 2020 16:16:01, you can use plenty of online tools to parse this, like this one here: https://www.epochconverter.com/ldap so we defined the struct well it makes sense yayyyy!!
It is pretty common to find timestamps like that one on many programs, internally, those are the best way to represent datetime variables.
Let’s now move on to a more complex example:
Xor Encrypting files
Let’s move on the last example:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#define MAX_DIR_LEN 260
#define BUF_SIZ 10
void cryp(char buf[]){
char k[10] = "0123456789";
for(int i = 0; i < sizeof(buf); i ++){
buf[i] ^= k[i];
}
}
void doFile(int hFile){
char buf[BUF_SIZ];
int totalBytesRead = 0;
int dwBytesRead = 0;
int fsize = GetFileSize(hFile, NULL);
printf("File size in bytes: %d \n", fsize);
printf("File content: \n");
while(totalBytesRead < fsize){
memset(buf,0,BUF_SIZ);
ReadFile(hFile, buf, BUF_SIZ-1, &dwBytesRead, NULL);
cryp(buf);
LZSeek(hFile,-dwBytesRead,1);
WriteFile(hFile, buf, dwBytesRead,NULL,NULL);
printf("%s", buf);
totalBytesRead += dwBytesRead;
}
printf("\n");
}
int main()
{
printf("Hello world!\n");
char base_path[MAX_DIR_LEN];
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
hFind = FindFirstFile("C:\\samples\\stor\\*.txt",&FindFileData);
printf("Search handler = %d \n", hFind);
do{
memset(base_path,0,MAX_DIR_LEN);
strcpy(base_path,"C:\\samples\\stor\\");
strcat(base_path,FindFileData.cFileName);
printf("Name= %s \n",base_path );
int hFile = CreateFile(base_path, // file to open
GENERIC_ALL, // open for write
FILE_SHARE_WRITE | FILE_SHARE_READ, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
printf("File handle: %d \n", hFile);
doFile(hFile);
}
while(FindNextFile(hFind, &FindFileData) != 0);
return 0;
}
So this small poorly written program makes use of Find*File to go through a specific folder, open each file for read/write and XOR encrypt its bytes block by block it reads - xors - writes and moves on repeating the process. The key point is: as ReadFile and WriteFile both do move the file pointer N bytes forward, the progam makes use of seek for going back for writting the XORed content (then write will move the pointer back to the desired position).
Why do we go block by block? Easy, because we can XOR large files with this method, you don’t want to map N GB in memory.
Also please note that this is purely a concept, this program may crash or not work very well on many systems (pointer stuff). Eventhough some malware uses encryption on the file system, for protecting itself or for attacking the system (cryptolockers) normally, they won’t use lame code like the one I presented here! as the windows api already presents nice cryptography functionalities and there are also a bunch of free properly written and tested crypto libraries out there, we’ll review those topics on the following tutorials. Said that, we can use this as a nice example.
Let’s dig in there
[0x004016d8]> pdf
/ (fcn) sym.main 339
| sym.main (int arg_100h, int arg_204h, int arg_208h);
| ; var int local_40h @ rbp-0x40
| ; arg int arg_100h @ rbp+0x100
| ; arg int arg_204h @ rbp+0x204
| ; arg int arg_208h @ rbp+0x208
| ; var int local_20h @ rsp+0x20
| ; var int local_28h @ rsp+0x28
| ; var int local_30h @ rsp+0x30
| ; var int local_80h @ rsp+0x80
| ; CALL XREF from 0x004013c2 (sym.__tmainCRTStartup)
| 0x004016d8 55 push rbp
| 0x004016d9 4881ec900200. sub rsp, 0x290
| 0x004016e0 488dac248000. lea rbp, [local_80h] ; 0x80 ; 128
| 0x004016e8 e8f3010000 call sym.__main
| 0x004016ed 488d0d372900. lea rcx, str.Hello_world ; 0x40402b ; "Hello world!"
| 0x004016f4 e81f160000 call sym.puts ; int puts(const char *s)
| 0x004016f9 488d45c0 lea rax, [local_40h]
| 0x004016fd 4889c2 mov rdx, rax
| 0x00401700 488d0d312900. lea rcx, str.C:__samples__stor___.txt ; 0x404038 ; "C:\samples\stor\*.txt"
| 0x00401707 488b05366b00. mov rax, qword sym.imp.KERNEL32.dll_FindFirstFileA ; [0x408244:8]=0x845a reloc.KERNEL32.dll_FindFirstFileA ; "Z\x84"
| 0x0040170e ffd0 call rax
| 0x00401710 488985080200. mov qword [arg_208h], rax ; [0x208:8]=-1 ; 520
| 0x00401717 488b85080200. mov rax, qword [arg_208h] ; [0x208:8]=-1 ; 520
| 0x0040171e 4889c2 mov rdx, rax
| 0x00401721 488d0d262900. lea rcx, str.Search_handler____d ; 0x40404e ; "Search handler = %d \n"
| 0x00401728 e8fb150000 call sym.printf ; int printf(const char *format)
| ; CODE XREF from 0x00401817 (sym.main)
| .-> 0x0040172d 488d85000100. lea rax, [arg_100h] ; 0x100 ; 256
| : 0x00401734 41b804010000 mov r8d, 0x104 ; 260
| : 0x0040173a ba00000000 mov edx, 0
| : 0x0040173f 4889c1 mov rcx, rax
| : 0x00401742 e8e9150000 call sym.memset ; void *memset(void *s, int c, size_t n)
| : 0x00401747 488d85000100. lea rax, [arg_100h] ; 0x100 ; 256
| : 0x0040174e 48ba433a5c73. movabs rdx, 0x6c706d61735c3a43
| : 0x00401758 488910 mov qword [rax], rdx
| : 0x0040175b 48ba65735c73. movabs rdx, 0x5c726f74735c7365
| : 0x00401765 48895008 mov qword [rax + 8], rdx
| : 0x00401769 c6401000 mov byte [rax + 0x10], 0
| : 0x0040176d 488d45c0 lea rax, [local_40h]
| : 0x00401771 488d502c lea rdx, [rax + 0x2c] ; ',' ; 44
| : 0x00401775 488d85000100. lea rax, [arg_100h] ; 0x100 ; 256
| : 0x0040177c 4889c1 mov rcx, rax
| : 0x0040177f e884150000 call sym.strcat ; char *strcat(char *s1, const char *s2)
| : 0x00401784 488d85000100. lea rax, [arg_100h] ; 0x100 ; 256
| : 0x0040178b 4889c2 mov rdx, rax
| : 0x0040178e 488d0dcf2800. lea rcx, str.Name___s ; 0x404064 ; "Name= %s \n"
| : 0x00401795 e88e150000 call sym.printf ; int printf(const char *format)
| : 0x0040179a 488d85000100. lea rax, [arg_100h] ; 0x100 ; 256
| : 0x004017a1 48c744243000. mov qword [local_30h], 0
| : 0x004017aa c74424288000. mov dword [local_28h], 0x80 ; [0x80:4]=-1 ; 128
| : 0x004017b2 c74424200300. mov dword [local_20h], 3
| : 0x004017ba 41b900000000 mov r9d, 0
| : 0x004017c0 41b803000000 mov r8d, 3
| : 0x004017c6 ba00000010 mov edx, 0x10000000
| : 0x004017cb 4889c1 mov rcx, rax
| : 0x004017ce 488b05576a00. mov rax, qword sym.imp.KERNEL32.dll_CreateFileA ; [0x40822c:8]=0x841c reloc.KERNEL32.dll_CreateFileA
| : 0x004017d5 ffd0 call rax
| : 0x004017d7 898504020000 mov dword [arg_204h], eax ; [0x204:4]=-1 ; 516
| : 0x004017dd 8b8504020000 mov eax, dword [arg_204h] ; [0x204:4]=-1 ; 516
| : 0x004017e3 89c2 mov edx, eax
| : 0x004017e5 488d0d832800. lea rcx, str.File_handle:__d ; 0x40406f ; "File handle: %d \n"
| : 0x004017ec e837150000 call sym.printf ; int printf(const char *format)
| : 0x004017f1 8b8504020000 mov eax, dword [arg_204h] ; [0x204:4]=-1 ; 516
| : 0x004017f7 89c1 mov ecx, eax
| : 0x004017f9 e8bbfdffff call sym.doFile
| : 0x004017fe 488d45c0 lea rax, [local_40h]
| : 0x00401802 488b8d080200. mov rcx, qword [arg_208h] ; [0x208:8]=-1 ; 520
| : 0x00401809 4889c2 mov rdx, rax
| : 0x0040180c 488b05396a00. mov rax, qword sym.imp.KERNEL32.dll_FindNextFileA ; [0x40824c:8]=0x846c reloc.KERNEL32.dll_FindNextFileA ; "l\x84"
| : 0x00401813 ffd0 call rax
| : 0x00401815 85c0 test eax, eax
| `=< 0x00401817 0f8510ffffff jne 0x40172d
| 0x0040181d b800000000 mov eax, 0
| 0x00401822 4881c4900200. add rsp, 0x290
| 0x00401829 5d pop rbp
\ 0x0040182a c3 ret
[0x004016d8]>
Again, we face some initializations and a do-while style loop. When you see some block of code starting by a jmp forward then a cmp then a possible jump backwards it is clear that we are facing a while/for style loop, if we have a cmp-jmp backwards at the end of the block we have a do-while. It is important to be able to recognize those structures well as if/for/while are fundamental on programming and thus on reverse engineering programs :)
So, we have the findfirstfile call, then we enter inside the do-while.
Then the do-while starts with this:
| : 0x0040173f 4889c1 mov rcx, rax
| : 0x00401742 e8e9150000 call sym.memset ; void *memset(void *s, int c, size_t n)
| : 0x00401747 488d85000100. lea rax, [arg_100h] ; 0x100 ; 256
| : 0x0040174e 48ba433a5c73. movabs rdx, 0x6c706d61735c3a43
| : 0x00401758 488910 mov qword [rax], rdx
| : 0x0040175b 48ba65735c73. movabs rdx, 0x5c726f74735c7365
| : 0x00401765 48895008 mov qword [rax + 8], rdx
| : 0x00401769 c6401000 mov byte [rax + 0x10], 0
| : 0x0040176d 488d45c0 lea rax, [local_40h]
| : 0x00401771 488d502c lea rdx, [rax + 0x2c] ; ',' ; 44
| : 0x00401775 488d85000100. lea rax, [arg_100h] ; 0x100 ; 256
| : 0x0040177c 4889c1 mov rcx, rax
| : 0x0040177f e884150000 call sym.strcat ; char *strcat(char *s1, const char *s2)
| : 0x00401784 488d85000100. lea rax, [arg_100h] ; 0x100 ; 256
| : 0x0040178b 4889c2 mov rdx, rax
| : 0x0040178e 488d0dcf2800. lea rcx, str.Name___s ; 0x404064 ; "Name= %s \n"
| : 0x00401795 e88e150000 call sym.printf ; int printf(const char *format)
Memset “deletes” the content of the char array used to hold the file_path, then manually loads the folders address back to the variable and concatenates the file name found with the base addr of the folder.
Note that at this point, var_40h will hold the struct related to the found file object, so + 0x2c is used for moving through the struct
| : 0x00401769 c6401000 mov byte [rax + 0x10], 0
: ;-- rip:
| : 0x0040176d b 488d45c0 lea rax, [local_40h]
| : 0x00401771 488d502c lea rdx, [rax + 0x2c] ; ',' ; 44
| : 0x00401775 488d85000100. lea rax, [arg_100h] ; 0x100 ; 256
| : 0x0040177c 4889c1 mov rcx, rax
| : 0x0040177f e884150000 call sym.strcat ; char *strcat(char *s1, const char *s2)
| : 0x00401784 488d85000100. lea rax, [arg_100h] ; 0x100 ; 256
[0x0040176d]> afvd
var local_40h = 0x0061fbd0 0x119893fa00000020 .......
arg arg_208h = 0x0061fe18 0x0000000000758160 `.u.....
arg arg_100h = 0x0061fd10 0x6c706d61735c3a43 C:\sampl @rax ascii
arg arg_204h = 0x0061fe14 0x0075816000000000 ....`.u.
var local_80h = 0x0061fc10 0x005c006500630069 i.c.e.\. @rbp ascii
var local_30h = 0x0061fbc0 0x0000000000000000 ........ r15
var local_28h = 0x0061fbb8 0x00007ffa97b377f3 .w......
var local_20h = 0x0061fbb0 0x8800000000593608 .6Y.....
[0x0040176d]> pxw @ 0x0061fbd0
0x0061fbd0 0x00000020 0x119893fa 0x01d63016 0x9b1838bf ........0...8..
0x0061fbe0 0x01d63323 0x9ad559d3 0x01d63323 0x00000000 #3...Y..#3......
0x0061fbf0 0x000000c7 0x00000000 0x00000000 0x2e333333 ............333.
0x0061fc00 0x00747874 0x00000000 0x0044005c 0x00760065 txt.....\.D.e.v.
SO, the prgoram ends up opening the corresponding found file and then calls the doFile function sendinf the file handler like this:
| : 0x004017f7 89c1 mov ecx, eax
: ;-- rip:
| : 0x004017f9 b e8bbfdffff call sym.doFile
| : 0x004017fe 488d45c0 lea rax, [local_40h]
| : 0x00401802 488b8d080200. mov rcx, qword [arg_208h] ; [0x208:8]=-1 ; 520
| : 0x00401809 4889c2 mov rdx, rax
| : 0x0040180c 488b05396a00. mov rax, qword sym.imp.KERNEL32.dll_FindNextFileA ; [0x40824c:8]=0x7ffa99f621a0
| : 0x00401813 ffd0 call rax
| : 0x00401815 85c0 test eax, eax
| `=< 0x00401817 0f8510ffffff jne 0x40172d
| 0x0040181d b800000000 mov eax, 0
| 0x00401822 4881c4900200. add rsp, 0x290
| 0x00401829 5d pop rbp
\ 0x0040182a c3 ret
[0x004017f9]> dr ecx
0x000000ac
[0x004017f9]>
Yes, that is the file handler, we can check that as we are debugging the program, the file handler (dec) should be on the screen at this point
[0x004017f9]> s sym.doFile
[0x004015b9]> pdf
/ (fcn) sym.doFile 287
| sym.doFile (int arg_10h);
| ; var int local_18h @ rbp-0x18
| ; var int local_12h @ rbp-0x12
| ; var int local_8h @ rbp-0x8
| ; var int local_4h @ rbp-0x4
| ; arg int arg_10h @ rbp+0x10
| ; var int local_20h @ rsp+0x20
| ; CALL XREF from 0x004017f9 (sym.main)
| 0x004015b9 55 push rbp
| 0x004015ba 4889e5 mov rbp, rsp
| 0x004015bd 4883ec50 sub rsp, 0x50 ; 'P'
| 0x004015c1 894d10 mov dword [arg_10h], ecx ; r12 ; [0x10:4]=-1
| 0x004015c4 c745fc000000. mov dword [local_4h], 0
| 0x004015cb c745e8000000. mov dword [local_18h], 0
| 0x004015d2 8b4510 mov eax, dword [arg_10h] ; r12 ; [0x10:4]=-1
| 0x004015d5 4898 cdqe
| 0x004015d7 ba00000000 mov edx, 0
| 0x004015dc 4889c1 mov rcx, rax
| 0x004015df 488b05866c00. mov rax, qword sym.imp.KERNEL32.dll_GetFileSize ; [0x40826c:8]=0x7ffa99f622b0
| 0x004015e6 ffd0 call rax
| 0x004015e8 8945f8 mov dword [local_8h], eax
| 0x004015eb 8b45f8 mov eax, dword [local_8h]
| 0x004015ee 89c2 mov edx, eax
| 0x004015f0 488d0d092a00. lea rcx, str.File_size_in_bytes:__d ; section..rdata ; 0x404000 ; "File size in bytes: %d \n"
| 0x004015f7 e82c170000 call sym.printf ; int printf(const char *format)
| 0x004015fc 488d0d162a00. lea rcx, str.File_content: ; 0x404019 ; "File content: "
| 0x00401603 e810170000 call sym.puts ; int puts(const char *s)
| ,=< 0x00401608 e9ae000000 jmp 0x4016bb
| | ; CODE XREF from 0x004016c1 (sym.doFile)
| .--> 0x0040160d 488d45ee lea rax, [local_12h]
| :| 0x00401611 41b80a000000 mov r8d, 0xa
| :| 0x00401617 ba00000000 mov edx, 0
| :| 0x0040161c 4889c1 mov rcx, rax
| :| 0x0040161f e80c170000 call sym.memset ; void *memset(void *s, int c, size_t n)
| :| 0x00401624 8b4510 mov eax, dword [arg_10h] ; r12 ; [0x10:4]=-1
| :| 0x00401627 4898 cdqe
| :| 0x00401629 4889c1 mov rcx, rax
| :| 0x0040162c 488d55e8 lea rdx, [local_18h]
| :| 0x00401630 488d45ee lea rax, [local_12h]
| :| 0x00401634 48c744242000. mov qword [local_20h], 0
| :| 0x0040163d 4989d1 mov r9, rdx
| :| 0x00401640 41b809000000 mov r8d, 9
| :| 0x00401646 4889c2 mov rdx, rax
| :| 0x00401649 488b05646c00. mov rax, qword sym.imp.KERNEL32.dll_ReadFile ; [0x4082b4:8]=0x7ffa99f62410
| :| 0x00401650 ffd0 call rax
| :| 0x00401652 488d45ee lea rax, [local_12h]
| :| 0x00401656 4889c1 mov rcx, rax
| :| 0x00401659 e8f2feffff call sym.cryp
| :| 0x0040165e 8b45e8 mov eax, dword [local_18h]
| :| 0x00401661 f7d8 neg eax
| :| 0x00401663 41b801000000 mov r8d, 1
| :| 0x00401669 89c2 mov edx, eax
| :| 0x0040166b 8b4d10 mov ecx, dword [arg_10h] ; r12 ; [0x10:4]=-1
| :| 0x0040166e e835180000 call sym.LZSeek
| :| 0x00401673 8b45e8 mov eax, dword [local_18h]
| :| 0x00401676 89c2 mov edx, eax
| :| 0x00401678 8b4510 mov eax, dword [arg_10h] ; r12 ; [0x10:4]=-1
| :| 0x0040167b 4898 cdqe
| :| 0x0040167d 4889c1 mov rcx, rax
| :| 0x00401680 488d45ee lea rax, [local_12h]
| :| 0x00401684 48c744242000. mov qword [local_20h], 0
| :| 0x0040168d 41b900000000 mov r9d, 0
| :| 0x00401693 4189d0 mov r8d, edx
| :| 0x00401696 4889c2 mov rdx, rax
| :| 0x00401699 488b05746c00. mov rax, qword sym.imp.KERNEL32.dll_WriteFile ; [0x408314:8]=0x7ffa99f62500
| :| 0x004016a0 ffd0 call rax
| :| 0x004016a2 488d45ee lea rax, [local_12h]
| :| 0x004016a6 4889c2 mov rdx, rax
| :| 0x004016a9 488d0d782900. lea rcx, [0x00404028] ; "%s"
| :| 0x004016b0 e873160000 call sym.printf ; int printf(const char *format)
| :| 0x004016b5 8b45e8 mov eax, dword [local_18h]
| :| 0x004016b8 0145fc add dword [local_4h], eax
| :| ; CODE XREF from 0x00401608 (sym.doFile)
| :`-> 0x004016bb 8b45fc mov eax, dword [local_4h]
| : 0x004016be 3b45f8 cmp eax, dword [local_8h]
| `==< 0x004016c1 0f8c46ffffff jl 0x40160d
| 0x004016c7 b90a000000 mov ecx, 0xa
| 0x004016cc e84f160000 call sym.putchar ; int putchar(int c)
| 0x004016d1 90 nop
| 0x004016d2 4883c450 add rsp, 0x50 ; 'P'
| 0x004016d6 5d pop rbp
\ 0x004016d7 c3 ret
[0x004015b9]>
So as you should already know, what kind of structure do we have here? YES a WHILE
| 0x004015df 488b05866c00. mov rax, qword sym.imp.KERNEL32.dll_GetFileSize ; [0x40826c:8]=0x7ffa99f622b0
| 0x004015e6 ffd0 call rax
| 0x004015e8 8945f8 mov dword [local_8h], eax
| :| ; CODE XREF from 0x00401608 (sym.doFile)
| :`-> 0x004016bb 8b45fc mov eax, dword [local_4h]
| : 0x004016be 3b45f8 cmp eax, dword [local_8h]
| `==< 0x004016c1 0f8c46ffffff jl 0x40160d
| 0x004016c7 b90a000000 mov ecx, 0xa
GetFileSize is called, size then stored inside local_8h and compared with local_4h we can easily guess that this loop actually goes through the file, reading N bytes chunk by chunk after it reaches the end by having read all of them.
Let’s focus now on the loop itself:
| :| 0x00401629 4889c1 mov rcx, rax
| :| 0x0040162c 488d55e8 lea rdx, [local_18h]
| :| 0x00401630 488d45ee lea rax, [local_12h]
| :| 0x00401634 48c744242000. mov qword [local_20h], 0
| :| 0x0040163d 4989d1 mov r9, rdx
| :| 0x00401640 41b809000000 mov r8d, 9
| :| 0x00401646 4889c2 mov rdx, rax
| :| 0x00401649 488b05646c00. mov rax, qword sym.imp.KERNEL32.dll_ReadFile ; [0x4082b4:8]=0x7ffa99f62410
The first thing that it does is reading from the file, how many bytes? 9 as we see, let’s inspect those buffers after the read.
[0x00401652]> afvd
arg arg_10h = 0x0061fb90 0x00000000000000ac ........
var local_4h = 0x0061fb7c 0x0061fc1000000000 ......a.
var local_18h = 0x0061fb68 0x6f4e000000000009 ......No
var local_8h = 0x0061fb78 0x00000000000000c7 ........
var local_12h = 0x0061fb6e 0x6961676120726f4e Nor agai ascii
var local_20h = 0x0061fb50 0x0000000000000000 ........ rdx
[0x00401652]> pxw @ 0x0061fb6e
0x0061fb6e 0x20726f4e 0x69616761 0x00c7006e 0x00000000 Nor again.......
0x0061fb7e 0xfc100000 0x00000061 0x17fe0000 0x00000040 ....a.......@...
Thats it, 9 bytes being read, content dumpted to 0x0061fb6e, then it goes to the cryp function.
[0x00401550]> pdf
/ (fcn) sym.cryp 105
| sym.cryp (int arg_10h);
| ; var int local_eh @ rbp-0xe
| ; var int local_6h @ rbp-0x6
| ; var int local_4h @ rbp-0x4
| ; arg int arg_10h @ rbp+0x10
| ; CALL XREF from 0x00401659 (sym.doFile)
| 0x00401550 55 push rbp
| 0x00401551 4889e5 mov rbp, rsp
| 0x00401554 4883ec10 sub rsp, 0x10
| 0x00401558 48894d10 mov qword [arg_10h], rcx ; r12 ; [0x10:8]=-1
| 0x0040155c 48b830313233. movabs rax, 0x3736353433323130
| 0x00401566 488945f2 mov qword [local_eh], rax
| 0x0040156a 66c745fa3839 mov word [local_6h], 0x3938
| 0x00401570 c745fc000000. mov dword [local_4h], 0
| ,=< 0x00401577 eb31 jmp 0x4015aa
| | ; CODE XREF from 0x004015b0 (sym.cryp)
| .--> 0x00401579 8b45fc mov eax, dword [local_4h]
| :| 0x0040157c 4898 cdqe
| :| 0x0040157e 488b5510 mov rdx, qword [arg_10h] ; r12 ; [0x10:8]=-1
| :| 0x00401582 4801d0 add rax, rdx ; '('
| :| 0x00401585 440fb600 movzx r8d, byte [rax]
| :| 0x00401589 8b45fc mov eax, dword [local_4h]
| :| 0x0040158c 4898 cdqe
| :| 0x0040158e 0fb64c05f2 movzx ecx, byte [rbp + rax - 0xe]
| :| 0x00401593 8b45fc mov eax, dword [local_4h]
| :| 0x00401596 4898 cdqe
| :| 0x00401598 488b5510 mov rdx, qword [arg_10h] ; r12 ; [0x10:8]=-1
| :| 0x0040159c 4801d0 add rax, rdx ; '('
| :| 0x0040159f 4489c2 mov edx, r8d
| :| 0x004015a2 31ca xor edx, ecx
| :| 0x004015a4 8810 mov byte [rax], dl
| :| 0x004015a6 8345fc01 add dword [local_4h], 1
| :| ; CODE XREF from 0x00401577 (sym.cryp)
| :`-> 0x004015aa 8b45fc mov eax, dword [local_4h]
| : 0x004015ad 83f807 cmp eax, 7 ; 7
| `==< 0x004015b0 76c7 jbe 0x401579
| 0x004015b2 90 nop
| 0x004015b3 4883c410 add rsp, 0x10
| 0x004015b7 5d pop rbp
\ 0x004015b8 c3 ret
[0x00401550]>
Again, another for/while style loop. We can use the common sense here, function is labeled cryp, byte array (buffer) is sent, the XOR instruction is present there. Nothing much more to see…We can guess that the buffer will be xored with something, and what about that something?
Well, we see a movabs ->
| 0x00401558 48894d10 mov qword [arg_10h], rcx ; r12 ; [0x10:8]=-1
| 0x0040155c 48b830313233. movabs rax, 0x3736353433323130
| 0x00401566 488945f2 mov qword [local_eh], rax
| 0x0040156a b 66c745fa3839 mov word [local_6h], 0x3938
| 0x00401570 c745fc000000. mov dword [local_4h], 0
| ,=< 0x00401577 eb31 jmp 0x4015aa
| | ; CODE XREF from 0x004015b0 (sym.cryp)
[0x00401550]> dc
hit breakpoint at: 40156a
[0x00401550]> afvd
arg arg_10h = 0x0061fb30 0x000000000061fb6e n.a..... (PRIVATE ) rcx R W 0x6961676120726f4e (Nor again) --> ascii
var local_eh = 0x0061fb12 0x3736353433323130 01234567 rax ascii sequence
var local_6h = 0x0061fb1a 0xfb8000000000001a ........
var local_4h = 0x0061fb1c 0x0061fb8000000000 ......a.
[0x00401550]> pxw @ 0x0061fb12
0x0061fb12 0x33323130 0x37363534 0x0000001a 0xfb800000 01234567........
0x0061fb22 0x00000061 0x165e0000 0x00000040 0xfb6e0000 a.....^.@.....n.
We can assume a 0-7 ascii char array that will be used for a byte-by-byte xor here.
[0x0040165e]> pxw @ 0x0061fb6e
0x0061fb6e 0x13405e7e 0x5e575255 0x00c7006e 0x00000000 ~^@.URW^n.......
0x0061fb7e 0xfc100000 0x00000061 0x17fe0000 0x00000040 ....a.......@...
After the call that was the result.
They key thing is that LZSEEK is being used to move backwards for writting that content.
I will stop here for this tutorial. I suggest you to review those examples by building them and maybe automating some stuff with r2pipe.
On the next tutorial we’ll talk about sockets!
Why is important to learn about the windows api? https://www.quora.com/Is-Win32-API-still-used