Unpacking NTKrnl Protect 0.1

Wednesday, January 24 2007 @ 05:07 AM CET

Contributed by: bpx

Level : intermediate

Author of yoda-protectors is back! This time he brings us ntkrnl protector =)

This is a powerful protector, however protection options are weakly implemented
making it relatively simple to unpack.

Note, if anyone can provide me with a file protected by more high level
VM/metamorphism/emulator options, I would be very happy :D

This tutorial comes in two parts: unpacking, and coding an imprec plugin.
Attached is my Unpackme/Imprec Plugin.

[file:20070125104459743 Download here]

For the record I am using the following tools:

- OllyDbg 1.10 fixed with re-pair.
- HideOD plugin [see thread http://www.reversing.be/forum/viewtopic.php?t=607 ]

- Lord PE
- ImpREC
- Fasm
- Frhed or other hex editor

[1 : Getting Started]

Loading at EP you'll notice it looks exactly like ASPR entrypoint =)

For those interested, how do we tell the difference?
Open exe in reshack or similar and if the "63" reference is present
it will contain something to this effect:

NTkrnl Secure Suite
Version 0.1
Metamorphism Portable Executable (PE) Packer and Protector Library
Copyright 2006-2007 Ashkbiz Danehkar
All Rights Reserved

Homepage: http://www.ntkrnl.com
E-mail: info@ntkrnl.com

Big tip off eh?

[1.1 : Getting to OEP]

First you will need to apply protection against checking of debug bits. [see plugin above]

press shift-f9 and ignore any exceptions until you stop on the following:

00330000 C3 ret

wtf? why'd we stop =P ?
just one ret in the memory range!
you'll note at the bottom it says we hit a break-on-access.
Odd, since we never set one.
This is a funny trick, Ashkbiz sets the protect on this memory range to the same as mem-bp.
This will make olly stop and pass except.
The protector then checks if ret executed or if exception occured.


Lie.. change the C3 -> CC and cause int3 exception =D

Good idea, weakly implemented.

So good, we changed ret to int3.
Now press ctrl-G and go to LoadLibraryA and set a BP

Continue pressing shift-f9 until we break in LoadLibrary
tracing out of function [ctrl-f9] we end up in internal import resolver.

Note, remember to remove breakpoint in loadlibrary when you are done with it.


0045F278    FF75 F4         push    dword ptr ss:[ebp-C]
0045F27B    E8 FA000000     call    0045F37A
0045F280    85C0            test    eax, eax


0045F2B4    52              push    edx
0045F2B5    57              push    edi
0045F2B6    E8 E4000000     call    0045F39F                         ; jmp to kernel32.GetProcAddress
0045F2BB    60              pushad
0045F2BC    8BF8            mov     edi, eax
0045F2BE    B9 04000000     mov     ecx, 4
0045F2C3    B8 60060000     mov     eax, 660


0045F315    8B45 EC         mov     eax, dword ptr ss:[ebp-14]
0045F318    8BE5            mov     esp, ebp
0045F31A    5D              pop     ebp
0045F31B    C2 0400         ret     4

Breakpoint that ret 4 on the end and trace out of the import resolver. You'll end up here:

0045F05A    8945 C4         mov     dword ptr ss:[ebp-3C], eax
0045F05D    8B45 F4         mov     eax, dword ptr ss:[ebp-C]
0045F060    0340 3C         add     eax, dword ptr ds:[eax+3C]
0045F063    8D58 28         lea     ebx, dword ptr ds:[eax+28]
0045F066    8B40 28         mov     eax, dword ptr ds:[eax+28]
0045F069    0345 F4         add     eax, dword ptr ss:[ebp-C]
0045F06C    C703 00000000   mov     dword ptr ds:[ebx], 0
0045F072    8B5D F4         mov     ebx, dword ptr ss:[ebp-C]
0045F075    035B 3C         add     ebx, dword ptr ds:[ebx+3C]
0045F078    66:F743 16 0020 test    word ptr ds:[ebx+16], 2000
0045F07E    74 10           je      short 0045F090
0045F080    6A 00           push    0
0045F082    6A 01           push    1
0045F084    FF75 F4         push    dword ptr ss:[ebp-C]
0045F087    FFD0            call    near eax

Breakpoint that "call eax" and trace into it.

Now we are in internal protector code =)
Press ctrl-b to search and enter the following bytes in hex box:

61 FF E0


jmp eax

The first result you get is crap for import redirection, and the second (ctrl-L) is the jump to the OEP.

Breakpoint oep jmp then shift-f9 some more.
Broke? good, now trace into it.

OEP @ 00401378 EB 10 jmp short 0040138A

[1.2 : Dumping and Fixing]

To dump at OEP use Lord PE with intelli-dump engine as normal dumping fails ;)

What we have to fix:

- Imports
- Resource directory
- TLS directory
- Olly can't analyze code


Open Your dump in LordPE PE editor and change the following entries:
Change Base of Code to zero and olly will analyse it later.

Fixing resources:

Open memory map in olly [alt-M] and scroll till you see

00400000 | 00001000 | Unpackme | | PE Header

Double click this line.
Scroll down untill you see the Resource Table Address / Size in the dump.
Change the Resource Directory RVA / size in dumped exe to one we see in olly and it will be 90% fixed!

RVA: 00089000
SIZE: 00009C00

Fixing TLS:

We will add our own TLS directory.

Open LordPE and go to section table.
Right click and hit add section header.
You can name it .tls =)

Set virtual size to 1000 and raw size to 200
Now in your hex editor of choice add 0x200 bytes to end of dumped exe.

Finally go to the TLS directory in the Directory Table and set it to RVA of section [000E5000].
Size is always 18.

Save and open TLS directory with the [...] button.

Set the following entries:

DataBlockStartVa: 004E5100
DataBlockEndVa: 004E5200
IndexVariableVa: 004E5300
CallBackTableVa: 004E5400

Easy enough, just increment Virtual Address of our added section by 100 each time.
Now we have fixed TLS block =)

[2 : Writing imprec plugin for imports]

All imports are redirected in the following proc:

01291314    60              pushad
01291315    E8 00000000     call    0129131A
0129131A    5D              pop     ebp
0129131B    81ED BF8F4F00   sub     ebp, 4F8FBF
01291321    FFB5 31904F00   push    dword ptr ss:[ebp+4F9031]
01291327    FFB5 35904F00   push    dword ptr ss:[ebp+4F9035]
0129132D    FFB5 39904F00   push    dword ptr ss:[ebp+4F9039]
01291333    FFB5 29904F00   push    dword ptr ss:[ebp+4F9029]
01291339    FFB5 2D904F00   push    dword ptr ss:[ebp+4F902D]
0129133F    E8 08000000     call    0129134C
01291344    894424 1C       mov     dword ptr ss:[esp+1C], eax
01291348    61              popad
01291349    FFE0            jmp     near eax
0129134B    C3              ret

Good concept, weak implementation.

lets think, that "jmp eax" is prolly where import is called ;)
So if we nop jmp eax, this proc will simply return redirected proc :D

So lets code a plugin in imprec that takes advantage of this, yea?

Source code listing in Fasm:

; NTkrnl protector 0.1 plugin tracer for impREC
; bpx^2k7
format PE GUI 4.0 DLL
entry e

include 'c:fasm32includewin32a.inc'

section '.code' code readable executable

e: ; DLL Entrypoint
    xor eax,eax
    inc eax
    ret 12


    push ebp
    mov ebp,esp

    push ebx
    push esi

    invoke MapViewOfFile, dword [ebp+4+4] , FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, 0  
    mov esi, eax                 ; We put our found proc in this map
    mov ebx, dword [ebp+16+4]

    invoke IsBadReadPtr, ebx, 4  ; Is this a valid thunk we are tracing ?
    test eax, eax
    jz ptr_ok1

    jmp end2


    cmp dword [ebx],0000E860h    ; Look for ntkrnl signature
    je ptr_ok2

    jmp end2


    mov word [ebx+35h],9090h     ; nop jmp eax
    call ebx                     ; call redirector proc [eax now contains real API!] 

    mov ebx, esi
    mov dword [ebx], eax
    invoke UnmapViewOfFile, esi ; Write changes
    invoke CloseHandle, dword [ebp+4+4]

    pop esi
    pop ebx
    mov eax,200

    ret 20

section '.idata' import data readable writeable

  library kernel,'KERNEL32.DLL'

  import kernel,

section '.edata' export data readable

  export 'nt_krnlprotect_0_1.dll',

section '.reloc' fixups data discardable

Look at the jump table entrys in olly, eg.

0047317E - FF25 C45A4800 jmp near dword ptr ds:[485AC4]

Go to the memory addr 485AC4 in dump scrolling up
We can guess Imports start at 485000 and are no longer than 2000.

Open impREC, use RVA 85000 / size 2000 / OEP RVA 1378 and hit get imports.
The bad thunks are resolved by the handy imprec plugin tracer =)

Add imports to dump and we are done :D

Note, if you have trouble unpacking check for the following:

- You are not using win98.
- Unpacking is time-sensitive, are you moving too slowly ?
- Try and remove all breakpoints on API functions.

Hope to see new version of this soon ;)

FIN - / bpx^2k7 /