sub_401EE0
.Config error
passed as an argument to the function at 0x4036F0 at ❸.The function at 0x4036F0 takes the strings as a parameter, but also uses ECX as the this
pointer. A reference to the object used by the this
pointer is stored on the stack at var_AC
at ❹. We later see that object passed to the CxxThrowException
function at ❺, which tells us that the function at 0x4036F0 is a member function of an exception object. Based on the context in which sub_4036F0
is called, we can assume that the function is initializing an exception with the string Config error
.
It’s important to recognize the function call with an error string argument followed by a call to CxxThrowException
because similar code consisting of an error string passed to a function followed by a call to CxxThrowException
appears throughout this program. Each time we see this pattern, we can conclude that the function is initializing an exception, so we don’t need to waste time analyzing these functions.
If we continue analyzing the function at 0x403180, we realize that it reads data from the configuration file config.dat and stores it in secondObject
. We can now conclude that secondObject
is an object to store and read configuration information, and we rename it configObject
.
Now we return to sub_401EE0
to see if we can better determine how firstObject
is used. After creating the configObject
object, sub_401EE0
stores a bunch of information in firstObject
, as shown in .
Example C-222. switch
statements inside the maybe_poll
function
0040251F mov al, [esi+4] 00402522 add eax, -61h ; switch 6 cases 00402525 cmp eax, 5 00402528 ja short loc_40257D ; default 0040252A jmp ds:off_4025C8[eax*4] ; switch jump
The value used for the switch decision is stored in [esi+4]
. That value is then stored in EAX, and 0x61 is subtracted from it. If the value is not lower than five, none of the switch jumps are taken. This ensures that the value is between 0x61 and 0x66 (which represents ASCII characters a through f). 0x61 less than the value is then used as an offset into the switch table. IDA Pro has recognized and labeled the switch table.
We click off_4025C8
, which takes us to the six possible locations that we need to analyze. We’ll label these case_1
through case_6
and analyze them one at a time:
case_1
calls the delete operator and then immediately returns without actually doing anything. We’ll rename this case_doNothing
.
case_2
calls atoi
to parse a string into a number, and then calls the sleep
function before returning. We’ll rename it case_sleep
.
case_3
does some string parsing, and then calls CreateProcess
. We’ll rename it case_ExecuteCommand
.
case_4
calls CreateFile
and writes the HTTP response received from the command-and-control server to disk. We’ll rename it case_downloadFile
.
case_5
also calls CreateFile
, but it uploads the data from the file to the remote server using an HTTP POST
command. We’ll rename it case_uploadFile
.
case_6
calls GetComputerName
, GetUserName
, GetVersionEx
, and GetDefaultLCID
, which together perform a survey of the victim’s machine and send the results back to the command-and-control server.
Overall, we have a backdoor program that reads a configuration file that determines the command-and-control server, sends a beacon to the command-and-control server, and implements several different functions based on the response from the command-and-control server.