                                Loader iz ring0
                                
                                      S verom u Boga, deroko/ARTeam
                                                
1. Uvod
2. Potrebno znanje
3. Realizacija
4. Zakljucak


1. Uvod /------------------------------------------------------------
-------/

        Zasto uopste pisati ring0 loader? Iz zabave svakako. Prednost
koje ring0 loader ima je brzina. Takodje ring0 loader moze samo da radi
kado DebugLoader buduci da moramo nekako da signaliziramo ring0 kodu da
bas na odredjenoj adresi hocemo da nesto patchujemo. crackme koji cemo
ovde koristiti je obican dialog sa NAG screenom koga cemo da ubijemo iz
ring0. Razlog sto koristim ASPack je vise nego prost, ASPack se lako 
otpakuje, a poenta ovog teksta je ring0 loader...


2. Potrebno znanje /-------------------------------------------------
------------------/
        
        Prvo moramo znati kako uopste radi debugger ali sa stanovista
ring0 buduci da se cela logika debuggera u ring3 zasniva na EVENT-ovima
i WaitForDebugEvent i ContinueDebugEvent. Debugger koristi nekoliko 
interupta da bi uopste mogao da radi. Oni su po IA32 manualu odredjeni
kao vektori 1 i 3 pri cemu int 1 oznacava Single Step, a int 3 oznacava
BreakPoint. Takodje int 1 se generise ukoliko se dogodi neki od uslova 
postavljen u dr0/3 registrima na nacin opisan u dr7 registru (execution,
read, write, byte, word, dword) no to nije trenutno bitno za nas buduci
da ce nam za nas rad trebati samo int 3h.

        Kad god se u Debugovanom procesu desi neki Exception ring0 kod
dobija kontrolu preko nekoliko IDTove: 
:idt
Int   Type     Sel:Offset     Attributes Symbol/Owner
IDTbase=8003F400  Limit=07FF
0000  IntG32   0008:804D8BFF  DPL=0  P   _KiTrap00
0001  IntG32   0008:F03FA760  DPL=0  P   icextension!.text+62E0
0002  TaskG    0058:00000000  DPL=0  P   _KiTrap02
0003  IntG32   0008:F03F9FB0  DPL=3  P   icextension!.text+5B30
0004  IntG32   0008:804D92E0  DPL=3  P   _KiTrap04
0005  IntG32   0008:804D9441  DPL=0  P   _KiTrap05
0006  IntG32   0008:804D95BF  DPL=0  P   _KiTrap06
0007  IntG32   0008:804D9C33  DPL=0  P   _KiTrap07
0008  TaskG    0050:00000000  DPL=0  P   _KiTrap08
0009  IntG32   0008:804DA060  DPL=0  P   _KiTrap09
000A  IntG32   0008:804DA185  DPL=0  P   _KiTrap0A
000B  IntG32   0008:804DA2CA  DPL=0  P   _KiTrap0B
000C  IntG32   0008:804DA530  DPL=0  P   _KiTrap0C
000D  IntG32   0008:804DA827  DPL=0  P   _KiTrap0D
000E  IntG32   0008:804DAF25  DPL=0  P   _KiTrap0E
000F  IntG32   0008:804DB25A  DPL=0  P   _KiTrap0F
0010  IntG32   0008:804DB37F  DPL=0  P   _KiTrap10

Normalno softice u outputu maskira koje je IDTove hookovao da se 
posteni ljudi ne bi zbunili:

0000  IntG32   0008:F05B6A2E  DPL=0  P   NTice!.text+0008A6AE
0001  IntG32   0008:F03FA760  DPL=0  P   icextension!.text+62E0
0002  IntG32   0008:F060AF97  DPL=0  P   NTice!.data+9297
0003  IntG32   0008:F03F9FB0  DPL=3  P   icextension.text+5B30
0004  IntG32   0008:804D92E0  DPL=3  P   _KiTrap04
0005  IntG32   0008:804D9441  DPL=0  P   _KiTrap05
0006  IntG32   0008:F060AFA6  DPL=0  P   NTice!.data+92A6
0007  IntG32   0008:804D9C33  DPL=0  P   _KiTrap07
0008  TaskG    0050:00001178  DPL=0  P
0009  IntG32   0008:804DA060  DPL=0  P   _KiTrap09
000A  IntG32   0008:804DA185  DPL=0  P   _KiTrap0A
000B  IntG32   0008:804DA2CA  DPL=0  P   _KiTrap0B
000C  IntG32   0008:F060AFB5  DPL=0  P   NTice!.data+92B5
000D  IntG32   0008:F060AFC4  DPL=0  P   NTice!.data+92C4
000E  IntG32   0008:F060AFD3  DPL=0  P   NTice!.data+92D3
000F  IntG32   0008:804DB25A  DPL=0  P   _KiTrap0F
0010  IntG32   0008:804DB37F  DPL=0  P   _KiTrap10
        
Ako pazljivo pogledate vidite da je SoftICE hookovao poprilican
deo IDTa. Zasto? 

Prosto debugger mora da uhvati exception i da ga procesira, dok
ring3 debugger to cini preko Debug Porta i biva signaliziran
preko raznih _KiTrapXX, SoftICE exceptione hvata odmah i procesira
ih preko svojeg koda kako bi utvrdio da li je exception odigran
pod uslovima kad treba SoftICE da reaguje ili pak kad se exception
prosledjuje defaul handleru u IDTu.

E bas to cemo i mi uciniti, mi cemo hvatati exceptione iz ring0
i odlucivati da li je rec o nasim uslovima i to cemo procesirati
ili pak cemo izvrsavanje predati default handleru.

Da bismo uospte hookovali IDT moramo da znamo kako da dodjemo do njega.
Adresu IDT-a dobijamo preko instrukcije sidt na sledeci nacin:

<++>
.data
idttable                dq      ?

.code
                        sidt    fword ptr[idttable]
                        mov     eax, dword ptr[idttable+2]
                        
<++>

sidt radi prostu stvar daje nam LIMIT i VirtulenuAdresu IDT-a, u sledecem
formatu:
+---------+---------------------+
|  LIMIT  | Virtuelna Adresa    |
+---------+---------------------+
0       15 16                   47
 
Takodje ima jos jedan elgantniji nacin koji sam naso u themida protektoru,
kako bi se izbeglo koriscenje promenljivih:


push    edi
sidt    fword ptr[esp-2]
pop     edi

Buduci da sidt zahteva 6 bajtova da stavi svoje podatke, to je slucaj veoma
prost, pravimo mesto na stacku i stavljamo podatke iz sidt na [esp-2]:

Stack:
[EDI]           push    edi
                sidt    fword ptr[esp-2]

Izgleda stacka:

[EDI]   [Virtuelna Adresa]   <---- ESP pokazuje ovde
[dummy] [LIMIT]

prostim pop edi dobijamo virtuelnu adresu IDTa.

Ok posto imamo IDT adresu red je pocnemo malo da hookujemo IDT. IDT 
predstavlja tabelu gde je svaki handle predtsavljen sa po 8 bajtova, a
struktura koja opisuje svaki handlde u IDT izgleda ovako:

31               16 15    13 12       8  7   5 4        0
+----------------+---+-----+-----------+------+---------+
| Offset 31..16  | P | DPL | 0 D 1 1 1 | 0 0 0|         |
+----------------+---+-----+-----------+------+---------+

31                       16 15                          0
+--------------------------+----------------------------+
|  Segment Selector        |    Offset 15..0            |
+--------------------------+----------------------------+

Da bismo hookovali neki IDT trebamo prvo da znam koji hocemo da hookujemo:

U nasem slucaju to int 3h i to cinimo na sledeci nacin:

.data 
idttable                dq      ?

.code
                        sidt    fword ptr[idttable]
                        mov     ebx, dowrd ptr[idttable+2]
                                
                        lea     eax, [ebx+3*8]  ;gde je smesten int 3h?
                                
                        mov     cx, [eax+6]     ;uzimamo High word
                        rol     ecx, 16
                        mov     cx, [eax]       ;uzimamo Low word
                               
Posle ove male operacije, ecx register ce imati Virtuelni Adresu handlea. To
moramo sacuvati pre hookovanja jer ukoliko nisu ispunjeni nasi uslovi, onda
moramo pozvati default handle, a ne da ubijemo kernel ili program =)

Posto sacuvamo nas handle dalje cemo ubaciti nas:

                        mov     ecx, offset __mynewint3h
                        mov     [eax], cx
                        rol     ecx, 16
                        mov     [eax+6], cx 

i to je cela mudrost hookovanja idt-a.

Dalje sto moramo znati jeste kako da pisemo po memoriji gde god zelimo. Ceo
problem se ovde svodi na prosto uklanjane WriteProtectiona iz cr0 registra.

Rec je o bitu 16 doticnog registra, ukidanjem WP protekcije drajver moze bez
brige da pise gde god hoce.
                        
                        mov     eax, cr0
                        and     eax, 0FFFEFFFFh
                        mov     cr0, eax
                        
Posle ovog mozete do mile volje da piste po ring0, ali pazite da slucajno ne
probate da piste po nepostojecoj memoriji, jer u suprotnom, BSOD.
Posto zavrsimo nase pisanje po memoriji moramo povratiti nazad cr0:
                        
                        mov     eax, cr0
                        or      eax, 10000h
                        mov     cr0, eax
                        
Prosto zar ne?

Jos jedan uslov je ostao a to je da znamo kad je u pitanju nas proces, i kad
treba da procesiramo int 3h ubacen u ring3 kode?!?!

Postoje 2 nacina:
        1. da pozovemo PsGetCurrentProcessId
        2. da uzmemo cr3 register procesa i na osnovu njega da vrsimo
           identifikaciju procesa 


Disassembly od PsGetCurrentProcessId:

.text:0040E245 _PsGetCurrentProcessId@0 proc near  
.text:0040E245                 mov     eax, large fs:124h
.text:0040E24B                 mov     eax, [eax+1ECh]
.text:0040E251                 retn
.text:0040E251 _PsGetCurrentProcessId@0 endp

Pre pozivanja ovog koda mi moramo da namestimo FS register  na 30h,
jer FS register u ring0 pokazuje na KPCR strukturu:

kd> dt nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x01c SelfPcr          : Ptr32 _KPCR
   +0x020 Prcb             : Ptr32 _KPRCB
   +0x024 Irql             : UChar
   +0x028 IRR              : Uint4B
   +0x02c IrrActive        : Uint4B
   +0x030 IDR              : Uint4B
   +0x034 KdVersionBlock   : Ptr32 Void
   +0x038 IDT              : Ptr32 _KIDTENTRY
   +0x03c GDT              : Ptr32 _KGDTENTRY
   +0x040 TSS              : Ptr32 _KTSS
   +0x044 MajorVersion     : Uint2B
   +0x046 MinorVersion     : Uint2B
   +0x048 SetMember        : Uint4B
   +0x04c StallScaleFactor : Uint4B
   +0x050 DebugActive      : UChar
   +0x051 Number           : UChar
   +0x052 Spare0           : UChar
   +0x053 SecondLevelCacheAssociativity : UCha
   +0x054 VdmAlert         : Uint4B
   +0x058 KernelReserved   : [14] Uint4B
   +0x090 SecondLevelCacheSize : Uint4B
   +0x094 HalReserved      : [16] Uint4B
   +0x0d4 InterruptMode    : Uint4B
   +0x0d8 Spare1           : UChar
   +0x0dc KernelReserved2  : [17] Uint4B
   +0x120 PrcbData         : _KPRCB
kd>


Ovo je jedan nacin, mada ja vise volim da koristim drugi nacin koji se
sastoji u promeni contexta procesa, tacnije sledeci set instrukcija ce
ponovo ucitati cr3 register datog procesa i time ce izmeniti PDE/PTE na
koji nacin cemo moci da postavimo prvi int 3h u kontekst naseg procesa.
Inace cr3 pokazuje na adresu PDE (Page Directory Table) i sadrzi fizicku,
a ne virtuelnu, memoriju na kojoj se nalazi PDE. Budci da svaki process
ima svoju posebnu memoriju to dalje znaci da cr3 mozemo da koristimo kao
neku vrstu Process ID-a na winNT sistemima.

Da bi smo ovo izveli moramo da koristimo 3 DDIja iz ntoskrnl.exe:

PsLookupProcessByProcessId
KeStackAttachProcess
KeStackDetachProcess

Prototipi:

PsLookupProcessByProcessId (PID, ptr EPROCESS)
KeStackAttachProcess(PEPROCESS, PTR KAPC_STATE)
KeUnstackDetachProcess(PTR KAPC_STATE)

Budici da 10tak liija koda govore vise nego ceo DDK i MSDN prelazimo na 
kod odmah:

PsLookupProcessByProcessId ce nam vratiti PTR na EPROCESS strukturu procesa
ciji PID prosledimo ovom DDIju.
                        
.data
eprocess                dd      ?
.code:
...
                        push    offset eprocess
                        push    pid
                        call    PsLookupProcessByProcessId
                                
Ako PsLookupProcessByProcessId ne uspe onda eax != 0, u suprotnom ako je eax = 0
sve je proslo bez problema, buduci da PsLookupProcessByProcessId uvecava reference
count u object_headeru, trebalo bi da pozovemo posle ObDereferenceObject, dajuci
mu eprocess strukturu kao argument:
                        
                        push    eprocess
                        call    ObDereferenceObject

Sledece sto moramo pozvati je : KeStackAttachProcess kako bi PDE/PTE bili
reloadovani za context naseg procesa jer u suprotnom ne bi iz ring0 videli
memoriju procesa koji zelimo da crackujemo, vec bi videli memororiju naseg
loader.exe. 

KeStackAttachProcess prima 2 argumenta a to su pointer na eprocess strukturu
i pointer na vec alociranu KAPC_STRUKTURU, doduse nije nam od znacaja sadrzaj
KAP_STATE strukture ali evo ga:

kd> dt nt!_KAPC_STATE
   +0x000 ApcListHead      : [2] _LIST_ENTRY
   +0x010 Process          : Ptr32 _KPROCESS
   +0x014 KernelApcInProgress : UChar
   +0x015 KernelApcPending : UChar
   +0x016 UserApcPending   : UChar
kd>

Buduci da mi ovo strukturu necemo koristiti, mozemo slobnodno da lociramo buffer
duzine ove strukture i to da predamo KeStackAttachProcess, posto nabavimo nas 
cr3 register onda cemo pozvati KeUnstackDetachProcess ovako:

<++>
.data
apcstate                db      20h dup(0)
eprocess                dd      ?

.code
...
                        push    offset eprocess
                        push    pid
                        call    PsLookupProcessByProcessId
                        test    eax, eax
                        jnz     __error
                        
                        push    eprocess
                        call    ObDereferenceObject
                                
                        push    offset apcstate
                        push    eprocess
                        call    KeStackAttachProcess
                                
                        mov     eax, cr3
                        mov     c_cr3, eax

                        <ovde ide ujedno i kod za patchovanje>
                                                        
                        push    offset apcstate
                        call    KeUnstackDetachProcess
<++>

Jos jedan trik ovde koji je jako bitan, buduci da KeStackAttachProcess i promena
konteksta nece dovesti uvek do momentalnog relodovanja PDE/PTE onda moramo postaviti
SEH i procitati jedan bajta sa nase patch adrese, ako pratimo PTE u SoftICE videcemo
kako se on reloaduje:
                        
                        push    offset sehhandle
                        push    dword ptr fs:[0]
                        mov     dword ptr fs:[0], esp
                                                
                        mov     eax, insertint3h
                        mov     ebx, [eax]
                        mov     byte ptr[eax], 0cch
                                                                     
__safe:                 pop     dword ptr fs:[0]
                        add     esp, 4

Doduse kako bi ucnio kod sto citljivijim koristim 2 makroa za postavljanje i
uklanjanje SEHa:

                        init_ring0_seh  __safe
                        
                        mov     eax, insertint3h
                        mov     ebx, [eax]
                        mov     byte ptr[eax], 0cch

__safe:                 remove_ring0_seh

Pri cemu su init_ring0_seh i remove_ring0_seh slicni, ako ne i isti
sa makroima koje koristim u ring3 aplikacijama.

<++>
init_ring0_seh          macro   _xxx
                        local   __handle
                        call    __handle
                        mov     ecx, [esp+0ch]
                        mov     [ecx.context_eip], offset _xxx
                        push    dword ptr[esp+8]
                        pop     [ecx.context_esp]
                        xor     eax, eax
                        ret
__handle:               push    dword ptr fs:[0]
                        mov     dword ptr fs:[0], esp
                        endm
                                
remove_ring0_seh        macro
                        pop     dword ptr fs:[0]                                
                        add     esp, 4
                        endm
<++>
                        
E sad kad znamo sve sto se treba znati red je da ovo i realizujemo:

3. Realizacija aka Sprovodjenje reci u delo  /------------------------
--------------------------------------------/

Pogledajmo nas crackme:

001B:00406001  PUSHAD
001B:00406002  CALL      0040600A
001B:00406007  JMP       459D64F7
001B:0040600C  PUSH      EBP
001B:0040600D  RET
001B:0040600E  CALL      00406014
001B:00406013  JMP       00406072
001B:00406015  MOV       EBX,FFFFFFED

Ok procedura dolazenja do OEPa u ASpack stvarno nije teska tako da necu
ni da se zadrzavam na tome:

001B:004063B0  JNZ       004063BA                                 
001B:004063B2  MOV       EAX,00000001
001B:004063B7  RET       000C
001B:004063BA  PUSH      00401000               
001B:004063BF  RET                   <--- ovde cemo da postavimo nas int 3h
001B:004063C0  MOV       EAX,[EBP+00000426]
001B:004063C6  LEA       ECX,[EBP+0000043B]
001B:004063CC  PUSH      ECX


i konacno crackme:

001B:00401000  PUSH      00
001B:00401002  CALL      KERNEL32!GetModuleHandleA
001B:00401007  PUSH      00
001B:00401009  PUSH      00401022
001B:0040100E  PUSH      00
001B:00401010  PUSH      000003E7
001B:00401015  PUSH      EAX
001B:00401016  CALL      USER32!DialogBoxParamA
001B:0040101B  PUSH      00
001B:0040101D  CALL      KERNEL32!ExitProcess
001B:00401022  ENTER     0000,00
001B:00401026  PUSHAD
001B:00401027  XOR       EAX,EAX
001B:00401029  CMP       DWORD PTR [EBP+0C],00000110
001B:00401030  JZ        00401049
001B:00401032  CMP       DWORD PTR [EBP+0C],10
001B:00401036  JNZ       00401062
001B:00401038  PUSH      00
001B:0040103A  PUSH      DWORD PTR [EBP+08]
001B:0040103D  CALL      USER32!EndDialog
001B:00401042  MOV       EAX,00000001
001B:00401047  JMP       00401062
001B:00401049  PUSH      00               
001B:0040104B  PUSH      00402004 ; "nag"
001B:00401050  PUSH      00402000 ; "NAG"
001B:00401055  PUSH      DWORD PTR [EBP+08]
001B:00401058  CALL      USER32!MessageBoxA  <-- NAG
001B:0040105D  MOV       EAX,00000001
001B:00401062  MOV       [ESP+1C],EAX
001B:00401066  POPAD
001B:00401067  LEAVE
001B:00401068  RET       0010
001B:0040106B  JMP       [KERNEL32!ExitProcess]
001B:00401071  JMP       [KERNEL32!GetModuleHandleA]
001B:00401077  JMP       [USER32!DialogBoxParamA]
001B:0040107D  JMP       [USER32!MessageBoxA]
001B:00401083  JMP       [USER32!EndDialog]

Da se ne bih zadrzavao obajsnjavajuci kako cemo da ubijemo ovaj NAG, recicu samo
da cemo na adresu 0040104Ah da ubacimo 0FFh... 

Super sad imamo 2 adrese:

1. 004063BFh  gde ubacujemo nas int3h
2. 0040104Ah  gde ubacujemo 0FFh

Sad je sve prosto, za detaljniju implementaciju pogledajte source kod od loader.asm
i ring0.asm buduci da oni sadrze sve sto sam gore pomenuo, a i kod uvek govori vise
nego hiljade reci.

int3h hook:

<++>
myint3h:                initint                         ;sacuvaj registre i postavi
                                                        ;fs na 30h i es i ds na 23h
                        mov     eax, cr3
                        cmp     eax, c_cr3
                        jne     __passdown
                        
                        mov     eax, [esp.int_eip]      ;uzmimo EIP sacuvan na stacku
                        dec     eax                     ;umanjimo eip za 1 buduci da se
                                                        ;int3h exception odigrao nakon
                                                        ;izvrsavanja int3h
                        cmp     eax, insertint3h        ;da li je to adresa na koju smo
                        jne     __passdown              ;ubacili int 3h???
                        
                        mov     eax, patchme            ;sad proveravamo da li je PTE 
                        shr     eax, 22                 ;u memoriji, buduci da je ring3
                        test    dword ptr[eax*4+0C0300000h], 1 ;sastavljen od PAGE = 4K
                        jz      __passdown                     ;nema potrebe za proverom
                        mov     eax, patchme                   ;za PAGE = 4MB
                        shr     eax, 12                        ;proveravamo zatim da li je
                        test    dword ptr[eax*4+0C0000000h], 1 ;PAGE prisutan u memoriji
                        jz      __passdown                     ;ako je nas PAGE prisutan u
                                                               ;memoriji patchujemo nasu
                        mov     eax, cr0                       ;zeljenu adresu
                        and     eax, 0FFFEFFFFh
                        mov     cr0, eax
                        
                        mov     eax, patchme                   ;patchujemo nasu adresu
                        mov     byte ptr[eax], 0ffh            ;sa bajtom 0ffh iliti -1
                        
                        mov     eax, cr0
                        or      eax, 10000h
                        mov     cr0, eax
                                                
                        mov     [esp.int_eip], 401000h         ;sad samo postavljamo sacuvan
                                                               ;eip na oep i odande nastavljamo
                                                               ;izvrsavanje
                        restoreint                             ;povrati registre
                        iretd                                  ;i idemo nazad u ring3
                        
__passdown:             restoreint                             ;pa vracamo registre nazad
                        jmp     cs:[oldint3h]                  ;i skacemo na defaul handler
<++>                                                            

initint i removeint su samo 2 makroa koje koristim prilikom hookovanja interupta:

<++>
initint                 macro
                        pushad
                        push    fs
                        push    ds
                        push    es
                        mov     eax, 30h
                        mov     fs, ax          ;fs pokazuje na KPCR
                        mov     eax, 23h
                        mov     ds, ax
                        mov     es, ax
                        endm

restoreint              macro                   ;samo da vratimo registre
                        pop     es              ;natrag
                        pop     ds
                        pop     fs
                        popad
                        endm
<++>

Ako pazljivo pogledate source mog drajvera videcete da je u pitanju tasm32, pa eto
uspeo sam da namestim da mi se drajveri kompajliraju koristeci tasm32 i ms link.
Kao bonus dajem vam moj makefile za drajvere.

Jos jedna napomena, ako pokrenete loader videte da smo ubili nag, ali ako pokrenete
crackme.exe opet bez loadera program ce pasti:

001B:004063B0  JNZ       004063BA
001B:004063B2  MOV       EAX,00000001
001B:004063B7  RET       000C
001B:004063BA  PUSH      00401000
001B:004063BF  INT       3
001B:004063C0  MOV       EAX,[EBP+00000426]
001B:004063C6  LEA       ECX,[EBP+0000043B]
001B:004063CC  PUSH      ECX 

Ako pogledate adresu 004063BFh, vidite da je int 3h ostao tamo!? int 3h je ostao u
cache-u pa da bi ga uklonili kako kaze SoftICE command reference manual, moramo
ili da rekompajliramo fajl, ili da flusujemo cache ili da rucno editujemo instrukciju
koja je tamo ranije bila, ili prosto prekopiramo fajl...

Pa to je to...

4. Zakljucak /-------------------------------------------------------
------------/

Hmmm zakljucak? Ajd da vidim da neko napise brzi debug loader od ovog?
Salu na stranu, ovo moze biti korisno za ucenje i razumevanje win32.
U narednom broju phzine cu malko pisati o hookovanju DDIja i pokazati
kako se to moze lepo iskoristiti.

Greetzing: svim clanovima ARTeam, svim dobrim koderima

Fuckz: lamerima, medju kojima dominira nas giga...


                                                S verom u Boga, deroko/ARTeam