Analysis of a Spam Bot
Binary starts fixing the API calls.
After fixing up the imports, the Reg32 path is retrieved. Default heap is accessed, and Socket / MAC addresses are retrieved. The MAC address is not used in the c2 packet but is used in exception handler and exception data is sent to c2.
Here in this subroutine, c2 is notified about the crash:
A number of initialization calls are made after this event
call
GetOSVersion
mov OSVersion,
eax
call
SetMaxUSerPort
call
SetMutex_AllocateChunks
call
AllocHugeBuffer
test
eax, eax
GetOSVersion() reads the OS version from registry SOFTWAREMicrosoftOSVersion
Configuration to the binary is set in SetConfig() function . It decodes a configuration from binary using a simple xor algorithm
An IDC script can be used to statically recover decoded data
#include <idc.idc>
static GeneratedString( Offset_)
{
auto String1 =
“”;
auto i =0
;
auto String2 =
“”;
for
(i =
0; i < 7416; i++) String1[i]
= String1[i]
+ Byte(0x13158000 + i);
for
(i =
0; i < 2855; i++) String2[i]
= String2[i]
+ Byte(0x13159CF8 + i);
//Message(“First Byte = %sn”, String1);
auto Salt1 =
“gcguIgMY2yinkfEZPE9nXLpsYZ7X3CSp8“;
//< 217
auto Salt2 = “uCiG9Lw2V4hguufVQoHNkelYsI5iHN1Q8JFUOoTYDeakWgZKo1QOg7KoZxAQRWfqKc8rU“;
auto len = Offset_;
auto buff =
“”;
auto cur_len =
0;
if
(len <
217)
{
buff = String1[(len *
34)
:(len *
34)
+ 34
];
//Message(“%s”, buff);
//memcpy(buff, String1 + (len * 34), 34);
cur_len =
34
–1;
}
else{
buff = String2[((len –
0x26)
*
70)
– 1:
((len –
0x26)
*
70)
– 1
+ 70];
//memcpy(buff, String2 + ((len – 0x26) * 70), 70);
cur_len =
70
–1;
}
i =
0;
while
(i < cur_len)
{
if
(cur_len ==
33)
{ buff[i]
= ord(buff[i])
^ ord(Salt1[i]);
i++;
}
else{
buff[i]
= ord(buff[i])
^ ord(Salt2[i]);
i++;
}
}
return buff;
}
static
main()
{
if
(strstr(GetDisasm(ScreenEA()),
“PUSH“))
{
auto psDisasm = GetDisasm(ScreenEA());
auto num =
long((psDisasm[strlen(“push “):strlen(psDisasm)]
));
auto psChunk = GeneratedString(num);
MakeComm( ScreenEA()
,
“Decoded String = “
+ psChunk );
}
}
The list is saved in following linked list format
struct typeinfo
{
LPVOID Pointer
DWORD Length;
}
It also sets a maxuserport constant to MAX_UNSIGNEDSHORT. Mutex’s are set, and a huge buffer is allocated. After the initialization, a call to a function is made which is responsible for creating few threads. The first thread is responsible for retrieving OSVersion from registry:
Another thread is responsible for making connections to some known hard coded mail servers
alt4.gmail-smtp-in.l.google.com
n1.smtp.messagingengine.com
mail7.digitalwaves.co.nz
and makes a connection to following domains on port 25 and wait till cmp Specifier_connection, bx is set which means the connection was successful, if not it will go into a timer state for 120 seconds till other thread functions are created.
There is one more dummy thread which is used to look up on thread ID of the same thread. It checks if it is zero or not
Other two threads are responsible for email parsing and spamming and Querying Root DNS servers for COM and RU domains
Connection to c2 is made in MainC2Subroutine
First 7 DWORD;s from OS version are combined in a buffer and encoded using the following algorithm and offset 223 is used to get the encoding key, and 224 is used to decode the data. Following is the C representation of that algorithm:
#include <stdio.h>
// Encoding Key 223 == eto ochen prostoarelkioiqyrut
unsigned
char
*EncodeKey =
“eto ochen prostoarelkioiqyrut“;
#define KEY_SIZE 29
void encode(unsigned
char * input,
int len )
{
unsigned
int i =
0;
unsigned
int j =
0;
unsigned
char tmp ;
unsigned
int total_len =
29;
unsigned
int index =
0;
if
(len >
29)
{
while
(1)
{
for
(i =
0; i < KEY_SIZE; i++)
{
input[i]
^= EncodeKey[i];
}
for
(j =
0; j < 14
; j++)
{
tmp = input[j];
input[j]
= input[i];
input[i]
= tmp;
// Swap bytes
i—;
}
// JA || JZ
if
((index &
1))
{
for
(i =0; i < 29; i++) input[i]
=
!input[i];
}
index = index +
29;
total_len = total_len +
29;
if
(total_len >= len)
break;
}
}
if
(total_len = total_len %
29)
{
for
(i = index ; i < total_len; i++)
{
input[i]
=
!input[i];
}
}
}
Communication synchronization
Only first 8 bytes are retrieved first which has the following structure:
DWORD len;
// Length of packet
DWORD CommandType;
// Command Type
after receiving the 8 bytes, rest of the bytes are retrieved in a loop
Following are the commands types present in the binary:
8 – Spam Email retrieval
5 – DNS Change
4 – Update Binary
7 – Download and execute from HTTP