Wednesday, 23 December 2009

bot1.exe part8 - hey command

The first command the message string is compared to, after checking if the first char of the channel message is a '.',

00403985 8B 45 3C          mov     eax, [ebp+4Ch+var_10]
00403988 8D 48 01          lea     ecx, [eax+1]
0040398B 8A 00             mov     al, [eax]
0040398D 3A 05 90 70 45 00 cmp     al, byte ptr ds:a__2            ; "."
00403993 89 0F             mov     [edi], ecx
00403995 0F 85 B3 F9 FF FF jnz     loc_40334E

is 'hey':

0040399B                   loc_40399B:                          
0040399B 8B 3F             mov     edi, [edi]
0040399D 57                push    edi
0040399E 68 EC 4D 44 00    push    offset aHey                     ; "hey"
004039A3 89 7D 3C          mov     [ebp+4Ch+var_10], edi
004039A6 E8 05 E9 01 00    call    strcmp?
004039AB 85 C0             test    eax, eax
004039AD 59                pop     ecx
004039AE 59                pop     ecx
004039AF 0F 84 2C 70 00 00 jz      hey_cmd

If strcmp returns 0, the next token is compared against 'h4ckerr00lz'

0040AA1F FF 75 44          push    [ebp+4Ch+pwd_token_aux]
0040AA22 8B F8             mov     edi, eax
0040AA24 68 B0 70 45 00    push    offset aH4ckerr00lz             ; "h4ckerr00lz"
0040AA29 E8 82 78 01 00    call    strcmp?
0040AA2E 83 C4 20          add     esp, 20h
0040AA31 85 C0             test    eax, eax
0040AA33 74 45             jz      pwd_match

At pwd_match (0040AA7A) there is a call to validate_who_info which aparently checks whether the user info (unchk@foobar) matches the regex '*@*'. If it does a jump is taken to 0040AACF.
Here, the password token is compared against "h4ckerr00lz" once again, and if there is a match, then a jz is taken to 40AAFC.
Here, the nick is copied to arg_18, and a success message is sent back to the channel ('-main- Password accepted.').
arg_18 is an input/output argument. It fits the purpose of signaling if the user issuing the command has previously identified himself.
The first reference to arg_18 (inside the function that parses the irc command, and does all command flow control) is located at 403255:

00403255 8B 75 6C          mov     esi, [ebp+4Ch+arg_18]
00403258 C7 45 28 03 00 00+mov     [ebp+4Ch+var_24], 3
0040325F BF 80 00 00 00    mov     edi, 80h
00403264
00403264                   loc_403264:                             
00403264 8D 85 14 F2 FF FF lea     eax, [ebp+4Ch+var_E38]
0040326A 50                push    eax
0040326B 56                push    esi
0040326C E8 3F F0 01 00    call    strcmp?
00403271 85 C0             test    eax, eax
00403273 59                pop     ecx
00403274 59                pop     ecx
00403275 75 07             jnz     short loc_40327E
00403277 C7 45 38 01 00 00+mov     [ebp+4Ch+authenticated], 1

the variable authenticated is, in turn used as a verification, when the contents of the message start by '.' and the command is not 'hey':

004039CA 39 5D 38          cmp     [ebp+4Ch+authenticated], ebx
004039CD 75 17             jnz     short loc_4039E6
004039CF FF 75 AC          push    [ebp+4Ch+var_A0]
004039D2 68 40 4F 44 00    push    offset a332                     ; "332" - RPL_TOPIC?
004039D7 E8 D4 E8 01 00    call    strcmp?
004039DC 85 C0             test    eax, eax
004039DE 59                pop     ecx
004039DF 59                pop     ecx
004039E0 0F 85 2A 18 00 00 jnz     loc_405210       ; will lead to the ret

I haven't tried messing around with the topic message yet, so the jnz (who's path will lead to the matching of all other commands) is always taken, if the user hasn't already identified himself.

Monday, 14 December 2009

bot1.exe part7

After both threads have been run there is a call to sub_00402CF7 which I named netcode1.
This method starts by trying to resolve the URL perdesi.magicshells.org.
After resolving the URL, there is another call to sub_0040AF58:
sub_0040AF58(&var_2c, 4, 1, 0)

Let's take a closer look at sub_0040AF58.
This function takes 4 arguments. The first argument is the address of a variable. This is to be passed to other functions that may be called inside sub_0040AF58.
The second argument is an index to decide which function is to be called, from an array of functions.
The third to indicate whether sub_0040AEB0 should be called, and finally the fourth argument is a string to be compared against a list of strings.

This time, according to our arguments, sub_0040AEB0 will be called. This function checks to see if there is a running mIRC instance.
The function located at array[4] calls GetLocaleInfo with locale set to LOCALE_SYSTEM_DEFAULT and LCType set to LOCALE_SABBREVCTRYNAME. Once the locale abbreviated name is retrieved, a '|' is appended to it followed by 5 random numbers.
If mIRC was running at the time sub_0040AEB0 was called, '[M]' is the prefix of the newly created string.
Since our bot will connect to an irc server, this string, is going to be used as the nick.

netcode1 proceeds with the connection to the irc server (by connecting to perdesi.magicshells.org), and after logging in, it tries to join the channel '#r00lz#' with password 'r0lzxz'.
After spending sometime debugging sub_402FD2 (which is responsible for handling the irc protocol) I found that at address 403985 a check takes place to see if the first char of the message is a '.'. I assumed this should represent that whatever came after the '.' should be interpreted as a command.
The commands will be explored in a future post.

bot1.exe part6


The next step taken by the bot, after updating the registry, is the creation of a thread.
This thread will execute the function at address 0x41D0BB and the call to CreateThread has the creation flags set to 0 (so the thread will run immediatly upon creation).
This is the code of the newly created thread function:

0041D0BB  sub_41D0BB proc near                    
0041D0BB  push    esi
0041D0BC  xor     esi, esi
0041D0BE  loc_41D0BE:                             
0041D0BE  push    1
0041D0C0  push    esi
0041D0C1  push    esi
0041D0C2  push    esi
0041D0C3  push    esi
0041D0C4  push    esi
0041D0C5  call    thread_routine1_core
0041D0CA  add     esp, 18h
0041D0CD  push    ds:dword_45E438
0041D0D3  call    ds:Sleep
0041D0D9  jmp     short loc_41D0BE
0041D0D9  sub_41D0BB endp

As we can see, "thread_routine1_core" will be running on an infinite loop and this is the only thing sub_41D0BB will do.

After some variable initialization and preliminary checks, "thread_routine1_core" calls a function I named GrantSeDebugPrivilege with arguments ("SeDebugPrivilege",1) :

0041CD75  push    ebp
0041CD76  lea     ebp, [esp-60h]
0041CD7A  sub     esp, 554h
0041CD80  push    ebx
0041CD81  push    esi
0041CD82  push    edi
0041CD83  xor     ebx, ebx
0041CD85  push    49h
0041CD87  xor     eax, eax
0041CD89  cmp     ds:CreateToolhelp32Snapshot, ebx
0041CD8F  pop     ecx
0041CD90  lea     edi, [ebp+60h+var_12C]
0041CD96  mov     [ebp+60h+var_130], ebx
0041CD9C  rep stosd
0041CD9E  mov     ecx, 88h
0041CDA3  lea     edi, [ebp+60h+var_350]
0041CDA9  mov     [ebp+60h+var_354], ebx
0041CDAF  rep stosd
0041CDB1  jz      loc_41CF87
0041CDB7  cmp     ds:Process32First, ebx
0041CDBD  jz      loc_41CF87
0041CDC3  cmp     ds:Process32Next, ebx
0041CDC9  jz      loc_41CF87
0041CDCF  push    1
0041CDD1  push    offset aSedebugprivi_1          ; "SeDebugPrivilege"
0041CDD6  call    GrantSeDebugPrivilege

GrantSeDebugPrivilege will open a process token relative to the current process with desired access set to TOKEN_ADJUST_PRIVILEGES (Required to enable or disable the privileges in an access token.) | TOKEN_QUERY (Required to query an access token) (= 0x28):

0041CD10  lea     eax, [ebp+var_4]
0041CD13  push    eax
0041CD14  push    28h
0041CD16  call    ds:GetCurrentProcess
0041CD1C  push    eax
0041CD1D  call    ds:OpenProcessToken
0041CD23  test    eax, eax

It will then lookup the value of the privilege named "SeDebugPrivilige", and grant that privilege to the current process:

0041CD2E  push    [ebp+arg_0]
0041CD31  xor     esi, esi
0041CD33  push    esi
0041CD34  call    ds:LookupPrivilegeValueA
0041CD3A  test    eax, eax
0041CD3C  jz      short loc_41CD67
0041CD3E  cmp     [ebp+arg_4], esi
0041CD41 +mov     [ebp+var_14], 1
0041CD48  jz      short loc_41CD50
0041CD4A  or      [ebp+var_8], 2
0041CD4E  jmp     short loc_41CD54
0041CD50  loc_41CD50:                             
0041CD50  and     [ebp+var_8], 0FFFFFFFDh

0041CD54  loc_41CD54:                             
0041CD54  push    esi
0041CD55  push    esi
0041CD56  push    esi
0041CD57  lea     eax, [ebp+var_14]
0041CD5A  push    eax
0041CD5B  push    esi
0041CD5C  push    [ebp+var_4]
0041CD5F  call    ds:AdjustTokenPrivileges
0041CD65  mov     esi, eax
0041CD67
0041CD67  loc_41CD67:                             
0041CD67  push    [ebp+var_4]
0041CD6A  call    ds:CloseHandle
0041CD70  mov     eax, esi

Here we can read:
"
By setting the SeDebugPrivilege privilege on the running process, you can obtain the process handle of any running application. When obtaining the handle to a process, you can then specify the PROCESS_ALL_ACCESS flag, which will allow the calling of various Win32 APIs upon that process handle, which you normally could not do. Some of the Win32 APIs that could be successfully called include the following: ...
"
According to this, the thread's next actions will probably need access to handles from other processes, so this is the reason why SeDebugPrivilege is granted to our process.

Once GrantSeDebugPrivilege returns, thread_routine1_core will call CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0) (0 indicates current process). With these two arguments, the call to CreateToolhelp32Snapshot will enumerate all processes.

0041CDDD 53                push    ebx
0041CDDE 6A 0F             push    0Fh
0041CDE0 FF 15 B0 F6 45 00 call    ds:CreateToolhelp32Snapshot

After this, the list of all processes will be iterated. The way thread_routine1_core was called (thread_routine1_core(0,0,0,0,0,1)) makes the following jz's instructions jump:

0041CE3B  loc_41CE3B:                             
0041CE3B  xor     eax, eax
0041CE3D  cmp     [ebp+60h+arg_10], eax
0041CE40  jz      short loc_41CE9F
...
0041CE9F  loc_41CE9F:
0041CE9F  cmp     [ebp+60h+arg_C], eax
0041CEA2  jnz     loc_41CF46
0041CEA8  cmp     [ebp+60h+arg_4], eax
0041CEAB  jz      loc_41CF5B

and at 41CF5B:

0041CF5B  loc_41CF5B:
0041CF5B             
0041CF5B  lea     eax, [ebp+60h+var_130]
0041CF61  push    eax
0041CF62  push    [ebp+60h+var_8]
0041CF65  call    ds:Process32Next
0041CF6B  test    eax, eax
0041CF6D  jnz     loc_41CE3B
0041CF73  xor     ebx, ebx

when Process32Next returns 0, the snapshot handle will be closed and the procedure will terminate.
It should be noted that thread_routine1_core will be called from somewhere else in the application, and it will be further analyzed at that point.

In addition to this auxiliar thread, another thread is created. The second auxiliary thread will wait for connections on port 113 and return the string ' : USERID : UNIX : xxxx" where xxxx is a random string generated by a call to sub_40AF58.