WarZone is a Remote Access Trojan (RAT) that is sold on a publicly available website, as a Malware-as-a-Service.
Starting our analysis from the function
start, we quickly spot a function
sub_405D70() which sets the value of a global variable
dword_54DB64 to 32 and then calls
HeapAlloc. After calling
sub_405D70, the program calls a function
sub_405D9D(&dword_419000, (unsigned int)&unk_41902C); where dword_419000 is a pointer to functions.
.data:00419000 dword_419000 dd 0 ; DATA XREF: start+66↑o
sub_405D9D loops through all of these functions and eventually calls them, and these functions are doing some basic initialization stuff. After that, we notice a function call
We see that this function calls a bunch of other functions. Decompiling some of these functions, we observe that the argument passed to them might be a struct.
This function is probably an initializer that initializes the member of some struct. To fix this thing, we reset the pointer type of
this and then create a new struct type. I’m setting the default type of each struct member as
_DWORD for making reversing easy. I’ll fix the types later if necessary. This would make our life as a reverse engineer easier than before. Similarly, we can define new struct types for other functions as well, wherever necessary.
After that, the program calls
GetModuleFileNameA(0, Filename, 0x104u).Here, the first argument is NULL, so the API call would retrieve the path of the executable for the current process. After that, data is read from the executable file (the current executable) and a pointer to the data is passed to the function
sub_411BF8(v10, (int)current_exe_data_ptr, 8538, &v11).
This seems to be a hash function. Googling some of these constants, we find out that this is
Murmur hash. This hash is just used to ensure that only instance of the malware runs at a time. If the hash already exists, the call to
CreateEventA returns a handle to the existing object and
ERROR_ALREADY_EXISTS. In this case, the malware performs cleanup by freeing the allocated memory, handles, etc. and then terminates. But, if the call to
CreateEventA returns a new handle, the malware starts its actual work.
if ( hObject )
The program creates a new registry key
HKCU\Software\Microsoft\Windows\CurrentVersion\Internet\Settings, sets the values
MaxConnectionsPerServer to 10 and then closes the handle to registry key. Now, let’s reverse the function
sub_405A10. It eventually calls
sub_411CA2 which is really interesting.
It stores the address of
sub_411CA2 into v0 and loops back until it reaches at the beginning of the PE file (‘\x90ZM’) is the famous MZ header present at the beginning of PE files. A pointer to the beginning is then passed as the second argument to the function
Consider this condition:
if ( *(_WORD *)a2 != 0x5A4D ) . 0x5A4D is the magic number for PE executables which is present as
e_magic in the struct
IMAGE_DOS_HEADER. Let’s change the type of
Now, it looks better!
e_lfanew is the last member of the DOS header structure, it holds an offset to the start of NT headers. This member is important to the PE loader on windows systems because it tells the loader where to look for the file header. From line 29,
v4 = (char *)pe_base + pe_base->e_lfanew; gives the address of the start of NT headers. Now, consider this condition
if ( *(_DWORD *)v4 != 0x4550 ). 0x4550 is the signature present in NT headers (defined by the struct IMAGE_NT_HEADERS). All we need to do to make it look better is to set the type of v4 to
PIMAGE_NT_HEADERS. After that, the malware checks whether the exe is suitable for i386 or AMD64 i.e 32 bit or 64 bit (using the value of Machine field in IMAGE_NT_HEADERS). Changing the types of some other local variables, by checking the values dynamically, we get the following code:
if ( Machine == 0x14C )
This initializes various structs which are probably going to be used later. Let’s move back to the caller of this function and reverse further.
Using dynamic analysis, we can find that the call to the function
sub_8233BF((char **)&lpAddress, a2, ".bss"); returns a pointer to the string
sub_9F02B9(&var_mtd_struct, &var_section_data, (LPCSTR *)var_bss_str); stores the section header for .bss at
These values match with the values calculated by PE Bear.
Moving forward, we find a function
sub_9360AA with a similar pattern to RC4, so I decided to find out the decrypted data using dynamic analysis. The decrypted data is present at
0xab270, we can use IDAPython to retrive the data present at this address.
Python>data = get_bytes(0xab270,70)
Taking some elements from this list,
d1 = [0x31,0x0,0x36,0x0,0x35,0x0,0x2e,0x0,0x32,0x0,0x32,0x0,0x2e,0x0,0x35,0x0,0x2e,0x0,0x36,0x0,0x36]
This gives us the C2
18.104.22.168. Also, [0x57,0x4] is a common method of storing the port, i.e port 0x457 which is