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.

Infector File

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:


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:

  1. Interrupt 13 "Read Sectors" function must be hooked to detect the loading of ntldr (which is loaded by Microsofts Bootsector)
  2. ntldr must be hooked to get control again in Protected Mode later
  3. in protected mode ntoskrnl must be hooked to get control in working Windows Environment
  4. 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

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

; 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
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
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

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:

Sector 60

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

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):


; 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 (
; 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

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)
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

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)
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

000000B4  33C0              xor eax,eax                     ; error, return zero
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:

  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

  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

  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!

Affected Systems

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:

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

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:

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_
|       log.txt
|       tmp
|       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

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

;     ==> Windows crashes, blue screen (restart)


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

Simulation Blue Screen

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 Article/Sinowal Source

The code was written by people connected to the Russian Business Network. The reverse engineering was done by me, Peter Kleissner. The terms of use apply for the provided code.



Special thanks to Volksbank Austria.