Wednesday, November 23, 2005
Porting PBPM from 9x to NT
This has been quite a nice project as i had to rewrite everything from scratch
and since BPM works (as we know) per thread i wanted it to handle all threads
seperated just like i had it done back then on 9x in icedump...
1. Problem there was no VMMCall _AllocateThreadDataSlot
which turned out to be quite a problem at first
later i found out that in the r0 TEB (which is located in PCR:0x124)
was a field that contains always 0 on all threads
mov eax,fs:[124h] ; -> TEB!
mov eax,[eax+4h]
or eax,eax
jnz memory_allocated
so i used that DWORD to store all snapshots...
2. after figuring that part out it also turned out that it wasn't as easy as i thought at first to hook
the necessary code deep enough.... hooking the point where the context gets restored (setthreadcontext)
was no problem at all, but finding a good point where i could hook the "snapshot" process was kinda tricky and this code changes heavily with each service pack and maybe even with security updates...
(i guess it was the main reason for me to never publish anything of it till today)
so i had 2 parts
the more "generic one" // where i hooked zwsetthreadcontext using the keservicedescriptortable
; SEH! stuff
mov ebx,keservicedescriptortable ; grab the fucking table
mov ebx,[ebx] ; pointer to real table
add ebx,020h*4h ; point to our really interesting
; context entry!
; service number 0x20
; -> setthreadcontext | SEH!
mov eax,[ebx] ; -> grab handler rva!
mov pOldCTXHandlerSEH,eax ; store the rva!
mov pOldCTXHandlerSEHP,ebx
mov [ebx],offset myXXsetcontextthreadseh ; install my handler!
and second the part where i hooked the snapshot process:
(therefore i disabled the supervisor bit for a short amount of time and written my hook directly into ntoskernel's code section... afterwards reenable the supervisor bit and exit)
mov ecx,cr0
mov edi,ecx
and ecx,0fffeffffh
mov cr0,ecx
pushad
mov esi,ntosbase
add esi,HARDC0DED
cmp word ptr [esi],0a5f3h
jne failed_at_signature
mov pOldKiUserHandlerP,esi
; save orig_byte_sequence
pushad
mov ecx,08h
lea edi,offset orig_bytes_buffer
db 0f3h, 0a4h ;repz movsb
mov al,068h
stosb ;store push
xchg esi,eax
stosd
mov al,0c3h
stosb
popad
lea eax,offset orig_bytes_buffer
mov my_fucking_destination,eax
xchg esi,edi
mov al,068h
stosb
lea eax,offset myXXkiuser
stosd
mov al,0c3h
stosb
popad
mov ecx,edi
mov cr0,ecx
failed_e:
popad
ret
failed_at_signature:
popad
mov ecx,edi
mov cr0,ecx
popad
ret
3. the hook itself
("kiuser" (snapshot) hook)
;Ä[MY HOOK]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
myXXkiuser equ $
pushfd
pushad
mov eax,fs:[124h]
mov eax,[eax+4h]
or eax,eax
jz dont_fill_the_context
lea edi,dword ptr [esi+4]
mov esi,eax
mov ecx,06h
db 0f3h,0a5h ; repz movsd
Invoke DbgPrint, OFFSET life_sux
dont_fill_the_context:
popad
popfd
db 068h ; push
my_fucking_destination dd ?
ret
myXXkiuserend equ $
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
and the way bigger zwsetcontextthread hook
(btw. i find it pretty confusing to name the ring3 part setthreadcontext and the ring0 part zwsetCONTEXTthread, must be one of a naming genius ;) or someone just noticed bit too late like "doh, we mixed it, damn!")
;Ä[MY HOOK]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
myXXsetcontextthreadseh equ $
pushfd
pushad
mov eax,[esp+028h] ; -> ctx storage buffer
cmp eax,010000000h
jae i_dont_trust_the_kernel
cmp dword ptr [eax+018h],0155h ; most of the time used!
je reset_now
cmp dword ptr [eax+018h],024FFh ; ... :> the evil one
jne i_dont_trust_the_kernel
reset_now:
; and dword ptr [eax+018h],0 ; reset -> dr7
mov eax,fs:[124h] ; -> TEB!
mov eax,[eax+4h]
or eax,eax
jnz memory_allocated
pushad
push 018h
push 4 ; NonPagedPool
iWin32 ExAllocatePool ; allocate buffer
; eax == pointer to allocated
; memory
or eax,eax
jnz proceed_
popad
jmp i_dont_trust_the_kernel
proceed_: mov dword ptr [esp+01ch],eax ; store it!
popad
memory_allocated: mov ebx,fs:[124h]
mov [ebx+4h],eax ; install buffer!
mov edi,eax
mov esi,[esp+028h] ; -> ctx storage!
add esi,04h
mov ecx,06h
mov edx,offset mdr0
grab_regs:
lodsd
push eax
push ecx
call convert
add edx,09h+4h
pop ecx
pop eax
and dword ptr [esi-4],0 ; -> kill it!
stosd ; save value in my buffer!
loop grab_regs
mov eax,fs:[124h] ; -> TEB!
push eax
mov edx,offset kteb_
call convert
pop eax
Invoke DbgPrint, OFFSET shit_happens
i_dont_trust_the_kernel:
popad
popfd
jmp dword ptr [pOldCTXHandlerSEH]
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
and since BPM works (as we know) per thread i wanted it to handle all threads
seperated just like i had it done back then on 9x in icedump...
1. Problem there was no VMMCall _AllocateThreadDataSlot
which turned out to be quite a problem at first
later i found out that in the r0 TEB (which is located in PCR:0x124)
was a field that contains always 0 on all threads
mov eax,fs:[124h] ; -> TEB!
mov eax,[eax+4h]
or eax,eax
jnz memory_allocated
so i used that DWORD to store all snapshots...
2. after figuring that part out it also turned out that it wasn't as easy as i thought at first to hook
the necessary code deep enough.... hooking the point where the context gets restored (setthreadcontext)
was no problem at all, but finding a good point where i could hook the "snapshot" process was kinda tricky and this code changes heavily with each service pack and maybe even with security updates...
(i guess it was the main reason for me to never publish anything of it till today)
so i had 2 parts
the more "generic one" // where i hooked zwsetthreadcontext using the keservicedescriptortable
; SEH! stuff
mov ebx,keservicedescriptortable ; grab the fucking table
mov ebx,[ebx] ; pointer to real table
add ebx,020h*4h ; point to our really interesting
; context entry!
; service number 0x20
; -> setthreadcontext | SEH!
mov eax,[ebx] ; -> grab handler rva!
mov pOldCTXHandlerSEH,eax ; store the rva!
mov pOldCTXHandlerSEHP,ebx
mov [ebx],offset myXXsetcontextthreadseh ; install my handler!
and second the part where i hooked the snapshot process:
(therefore i disabled the supervisor bit for a short amount of time and written my hook directly into ntoskernel's code section... afterwards reenable the supervisor bit and exit)
mov ecx,cr0
mov edi,ecx
and ecx,0fffeffffh
mov cr0,ecx
pushad
mov esi,ntosbase
add esi,HARDC0DED
cmp word ptr [esi],0a5f3h
jne failed_at_signature
mov pOldKiUserHandlerP,esi
; save orig_byte_sequence
pushad
mov ecx,08h
lea edi,offset orig_bytes_buffer
db 0f3h, 0a4h ;repz movsb
mov al,068h
stosb ;store push
xchg esi,eax
stosd
mov al,0c3h
stosb
popad
lea eax,offset orig_bytes_buffer
mov my_fucking_destination,eax
xchg esi,edi
mov al,068h
stosb
lea eax,offset myXXkiuser
stosd
mov al,0c3h
stosb
popad
mov ecx,edi
mov cr0,ecx
failed_e:
popad
ret
failed_at_signature:
popad
mov ecx,edi
mov cr0,ecx
popad
ret
3. the hook itself
("kiuser" (snapshot) hook)
;Ä[MY HOOK]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
myXXkiuser equ $
pushfd
pushad
mov eax,fs:[124h]
mov eax,[eax+4h]
or eax,eax
jz dont_fill_the_context
lea edi,dword ptr [esi+4]
mov esi,eax
mov ecx,06h
db 0f3h,0a5h ; repz movsd
Invoke DbgPrint, OFFSET life_sux
dont_fill_the_context:
popad
popfd
db 068h ; push
my_fucking_destination dd ?
ret
myXXkiuserend equ $
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
and the way bigger zwsetcontextthread hook
(btw. i find it pretty confusing to name the ring3 part setthreadcontext and the ring0 part zwsetCONTEXTthread, must be one of a naming genius ;) or someone just noticed bit too late like "doh, we mixed it, damn!")
;Ä[MY HOOK]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
myXXsetcontextthreadseh equ $
pushfd
pushad
mov eax,[esp+028h] ; -> ctx storage buffer
cmp eax,010000000h
jae i_dont_trust_the_kernel
cmp dword ptr [eax+018h],0155h ; most of the time used!
je reset_now
cmp dword ptr [eax+018h],024FFh ; ... :> the evil one
jne i_dont_trust_the_kernel
reset_now:
; and dword ptr [eax+018h],0 ; reset -> dr7
mov eax,fs:[124h] ; -> TEB!
mov eax,[eax+4h]
or eax,eax
jnz memory_allocated
pushad
push 018h
push 4 ; NonPagedPool
iWin32 ExAllocatePool ; allocate buffer
; eax == pointer to allocated
; memory
or eax,eax
jnz proceed_
popad
jmp i_dont_trust_the_kernel
proceed_: mov dword ptr [esp+01ch],eax ; store it!
popad
memory_allocated: mov ebx,fs:[124h]
mov [ebx+4h],eax ; install buffer!
mov edi,eax
mov esi,[esp+028h] ; -> ctx storage!
add esi,04h
mov ecx,06h
mov edx,offset mdr0
grab_regs:
lodsd
push eax
push ecx
call convert
add edx,09h+4h
pop ecx
pop eax
and dword ptr [esi-4],0 ; -> kill it!
stosd ; save value in my buffer!
loop grab_regs
mov eax,fs:[124h] ; -> TEB!
push eax
mov edx,offset kteb_
call convert
pop eax
Invoke DbgPrint, OFFSET shit_happens
i_dont_trust_the_kernel:
popad
popfd
jmp dword ptr [pOldCTXHandlerSEH]
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Thursday, November 03, 2005
Just another stupid method of "Anti DLL Injection..."
The IDEA behind it
The Problem normally is that you can’t avoid DLL injection into Processes, there’s a way to scan the module list via a few Apis, Module32First/Module32Next, these could easily get hooked and fooling the detection wouldn’t be too hard. To make the cracker’s life a bit harder there’s a non API based method to get the loaded MODULES (*.dll) inside your process.
The PEB aka Process Environment Block
PEB means Process Environment Block and is nothing else than a complex structure containing information needed by Windows to handle processes, there’s another structure called TEB (Thread Environment Block) which describes each running Thread (as Windows normally just administrates Threads), but we will concentrate on the PEB structure.
To get a pointer to the PEB structure all you need to do is grabbing PCR (Processor Control Register) 0x30, or using the TEB first by using PCR (Processor Control Register) 0x18 and then grab struc member +0x30 as it points also to the actual PEB (pPEB).
Processor Control Register means fs:[x].
i.e. mov reg,dword ptr fs:[30h] -> reg = pPEB
PEB STRUCT ;
dwFlags DWORD ? ;00
Unknown04 DWORD ? ;04 == -1
ImageBaseAddress DWORD ? ;08
PebLdrData DWORD ? ;0C == *PEB_LDR_DATA
ProcessParameters DWORD ? ;10 == *PROCESS_PARAMETERS
SubSystemData DWORD ? ;14 == 0
ProgramHeap DWORD ? ;18
LockingContext DWORD ? ;1C == FastPebLock
LockRoutine DWORD ? ;20 == RtlEnterCriticalSection
UnlockRoutine DWORD ? ;24 == RtlLeaveCriticalSection
DirChange DWORD ? ;28 == 1
Unknown2C DWORD ? ;2C == apfnDispatch
Unknown30 DWORD ? ;30 == 0
Unknown34 DWORD ? ;34 == 0
Unknown38 DWORD ? ;38 == 0
Unknown3C DWORD ? ;3C == 0
Unknown40 DWORD ? ;40 == TlsBitMap
Unknown44 DWORD ? ;44 == 3FH
Unknown48 DWORD ? ;48 == 0
ProgramHeap02 DWORD ? ;4C
ProgramHeap02a DWORD ? ;50
InProgramHeap02 DWORD ? ;54
AnsiCodePage DWORD ? ;58
OemCodePage DWORD ? ;5C
UnicodeCodePage DWORD ? ;60
NumberProcessors DWORD ? ;64
GlobalFlag DWORD ? ;68
Unknown6C DWORD ? ;6C == 0
CritSectTimeout DWORD ? ;70
Unknown74 DWORD ? ;74
HeapSegmentReserve DWORD ? ;78
HeapSegementCommit DWORD ? ;7C
HeapDeCommitTotalFreeTreshold DWORD ? ;80 == 10000H
HeapDeCommitFreeBlockTreshold DWORD ? ;84 == 1000H
Unknown88 DWORD ? ;88
Unknown8C DWORD ? ;8C == 386H
Unknown90 DWORD ? ;90 == RtlpProcHeapsListBuffer
Unknown94 DWORD ? ;94
Unknown98 DWORD ? ;98 == 0
Unknown9C DWORD ? ;9C == 14H
UnknownA0 DWORD ? ;A0 == LoaderLock
dwMajorVersion DWORD ? ;A4
dwMinorVersion DWORD ? ;A8
dwBuildNumber WORD ? ;AC
CSDVersion WORD ? ;AE
dwPlatformId DWORD ? ;B0
Subsystem DWORD ? ;B4
MajorSusbsytemVersion DWORD ? ;B8
MinorSusbsytemVersion DWORD ? ;BC
ProcessAffinityMask DWORD ? ;C0
UnknownC4 DWORD 044H DUP (?) ;C4
SessionId DWORD ? ;1D4
Unknown1D8 DWORD ? ;1D8
Unknown1DC DWORD ? ;1DC
Unknown1E0 DWORD ? ;1E0
Unknown1E4 DWORD ? ;1E4
PEB ENDS ;size 1E8H, NT4 size 150H
Interesting is the following field:
PebLdrData DWORD ? ;0C == *PEB_LDR_DATA
This field contains a pointer to another structure called PEB_LDR_DATA, let’s have a look at it.
PEB_LDR_DATA STRUCT ;
cbsize DWORD ? ;00 == 24H
Flags DWORD ? ;04
Unknown8 DWORD ? ;08
InLoadOrderModuleListHead DWORD ? ;0C
PreviousInLoadOrderLdrEntry DWORD ? ;10
InMemoryOrderModuleListHead DWORD ? ;14
PreviousInMemoryOrderLdrEntry DWORD ? ;18
InInitializationOrderModuleListHead DWORD ? ;1C
PreviousInInitializationOrderLdrEntry DWORD ? ;20
PEB_LDR_DATA ENDS ;size 24H
The PEB_LDR_DATA structure contains a field called InLoadOrderModuleListHead this field is what is needed, it points again to another structure called LDR_ENTRY.
LDR_ENTRY STRUCT ;
NextInLoadOrderLdrEntry DWORD ? ;00
PreviousInLoadOrderLdrEntry DWORD ? ;04
NextInMemoryOrderLdrEntry DWORD ? ;08
PreviousInMemoryOrderLdrEntry DWORD ? ;0C
NextInInitializationOrderLdrEntry DWORD ? ;10
PreviousInInitializationOrderLdrEntry DWORD ? ;14
ModuleBase DWORD ? ;18
EntryPoint DWORD ? ;1C
ModuleSize DWORD ? ;20
ModuleFileName UNICODE_STRING <> ;24
ModuleBaseName UNICODE_STRING <> ;2C
Flags DWORD ? ;34
LoadCount WORD ? ;38
TlsIndex WORD ? ;3A
LdrpHashTableEntry0 DWORD ? ;3C
LdrpHashTableEntry1 DWORD ? ;40
TimeStamp DWORD ? ;44
LDR_ENTRY ENDS ;size 48H
And here it is a pointer to the loaded ModuleFileName (ANSI VERSION). The very first Entry in this field points to ourself, means the first module is always our actual *.exe name (GetModuleFileName does the same). Using the field NextInLoadOrderLdrEntry you can walk the list of loaded Modules, it’s getting terminated by pointing to the first entry again.
;**************************************************************************
; ANTI DLL INJECTION USING PEB
;**************************************************************************
pushad
mov ecx,cs
xor cl,cl
cmp ecx,0
jnz no_9x_for_it_tiger_style
mov eax,fs:[30h] ;// ALLOC PEB BASE!
or eax,eax
je no_9x_for_it_tiger_Style
mov eax,[eax+0Ch] ;// MOD LIST
or eax,eax
je no_9x_for_it_tiger_Style
mov eax,[eax+PEB_LDR_DATA.InLoadOrderModuleListHead]
mov esi,eax
loop_all_modules:
cmp esi,[eax+PEB_LDR_DATA.NextInLoadOrderLdrEntry]
je no_9x_for_it_tiger_style
mov ecx,[eax+LDR_ENTRY.MODULEFILENAME+4]
;///////////////////////////////////////////////////////////////
;// CHECK MODULE NAME NOW!
;// SINCE WE HAVE TO DEAL WITH ANSI STRINGS GET IT IN A PROPER WAY
;// FIRST
;///////////////////////////////////////////////////////////////
push esi
push ecx
mov esi,ecx
lea edi,dword ptr [ebp+module_name]
call normalize_string
pop ecx
pop esi
cmp dword ptr [ebp+first_loop],1
jae dont_save_current_directory
mov dword ptr [ebp+first_loop],1
;///////////////////////////////////////////////////////////////
;// SAVE CURRENT DIRECTORY
;///////////////////////////////////////////////////////////////
push ecx
push esi
push eax
lea esi,dword ptr [ebp+module_name]
call strlen_x2
mov ecx,eax
pop eax
pop esi
push esi
lea esi,dword ptr [ebp+module_name]
add esi,ecx
call getdirectory
pop esi
pop ecx
mov byte ptr [edi+2],0 ;// SET TERMINATOR
pushad
lea esi,dword ptr [ebp+module_name]
call strlen_x2
mov ecx,eax
inc ecx
lea edi,dword ptr [ebp+current_dir]
repz movsb
popad
dont_save_current_directory:
;///////////////////////////////////////////////////////////////
;// GET ACTUAL DIRECTORY AND COMPARE BOTH DIRECTORIES!
;///////////////////////////////////////////////////////////////
cmp dword ptr [ebp+first_loop],2
jne set_flag_and_loop
push ecx
push esi
push eax
lea esi,dword ptr [ebp+module_name]
call strlen_x2
mov ecx,eax
pop eax
pop esi
push esi
lea esi,dword ptr [ebp+module_name]
add esi,ecx
call getdirectory
pop esi
pop ecx
mov byte ptr [edi+2],0 ;// SET TERMINATOR
pushad
lea esi,dword ptr [ebp+module_name]
call strlen_x2
mov ecx,eax
inc ecx
lea edi,dword ptr [ebp+current_dir]
repz cmpsb
or ecx,ecx
jne fine_puh
;///////////////////////////////////////////////////////////////
;// INJECTED DLL FOUND!
;// PUT YOUR CODE HERE
;///////////////////////////////////////////////////////////////
fine_puh:
popad
set_flag_and_loop:
mov dword ptr [ebp+first_loop],2
mov eax,[eax+PEB_LDR_DATA.NextInLoadOrderLdrEntry]
jmp loop_all_modules
strlen_X2:
pushad
xor ecx,ecx
count_next_char2:
lodsb
or al,al
je con_a2
inc ecx
jmp count_next_char2
con_a2: mov dword ptr [esp+1ch],ecx
popad
ret
getdirectory:
push ecx
push esi
push eax
mov edi,esi
std
mov al,'\'
repnz scasb
cld
pop eax
pop esi
pop ecx
ret
normalize_string:
pushad
normalize_string2:
lodsb
stosb
inc esi
or al,al
jne normalize_string2
popad
ret
first_loop dd ?
module_name db 0FFh dup (00h)
current_dir db 0FFh dup (00h)
no_9x_for_it_tiger_style:
mov ecx,offset no_9x_for_it_tiger_style-offset first_loop
lea edi,dword ptr [ebp+first_loop]
mov al,00
repz stosb
popad
The Problem normally is that you can’t avoid DLL injection into Processes, there’s a way to scan the module list via a few Apis, Module32First/Module32Next, these could easily get hooked and fooling the detection wouldn’t be too hard. To make the cracker’s life a bit harder there’s a non API based method to get the loaded MODULES (*.dll) inside your process.
The PEB aka Process Environment Block
PEB means Process Environment Block and is nothing else than a complex structure containing information needed by Windows to handle processes, there’s another structure called TEB (Thread Environment Block) which describes each running Thread (as Windows normally just administrates Threads), but we will concentrate on the PEB structure.
To get a pointer to the PEB structure all you need to do is grabbing PCR (Processor Control Register) 0x30, or using the TEB first by using PCR (Processor Control Register) 0x18 and then grab struc member +0x30 as it points also to the actual PEB (pPEB).
Processor Control Register means fs:[x].
i.e. mov reg,dword ptr fs:[30h] -> reg = pPEB
PEB STRUCT ;
dwFlags DWORD ? ;00
Unknown04 DWORD ? ;04 == -1
ImageBaseAddress DWORD ? ;08
PebLdrData DWORD ? ;0C == *PEB_LDR_DATA
ProcessParameters DWORD ? ;10 == *PROCESS_PARAMETERS
SubSystemData DWORD ? ;14 == 0
ProgramHeap DWORD ? ;18
LockingContext DWORD ? ;1C == FastPebLock
LockRoutine DWORD ? ;20 == RtlEnterCriticalSection
UnlockRoutine DWORD ? ;24 == RtlLeaveCriticalSection
DirChange DWORD ? ;28 == 1
Unknown2C DWORD ? ;2C == apfnDispatch
Unknown30 DWORD ? ;30 == 0
Unknown34 DWORD ? ;34 == 0
Unknown38 DWORD ? ;38 == 0
Unknown3C DWORD ? ;3C == 0
Unknown40 DWORD ? ;40 == TlsBitMap
Unknown44 DWORD ? ;44 == 3FH
Unknown48 DWORD ? ;48 == 0
ProgramHeap02 DWORD ? ;4C
ProgramHeap02a DWORD ? ;50
InProgramHeap02 DWORD ? ;54
AnsiCodePage DWORD ? ;58
OemCodePage DWORD ? ;5C
UnicodeCodePage DWORD ? ;60
NumberProcessors DWORD ? ;64
GlobalFlag DWORD ? ;68
Unknown6C DWORD ? ;6C == 0
CritSectTimeout DWORD ? ;70
Unknown74 DWORD ? ;74
HeapSegmentReserve DWORD ? ;78
HeapSegementCommit DWORD ? ;7C
HeapDeCommitTotalFreeTreshold DWORD ? ;80 == 10000H
HeapDeCommitFreeBlockTreshold DWORD ? ;84 == 1000H
Unknown88 DWORD ? ;88
Unknown8C DWORD ? ;8C == 386H
Unknown90 DWORD ? ;90 == RtlpProcHeapsListBuffer
Unknown94 DWORD ? ;94
Unknown98 DWORD ? ;98 == 0
Unknown9C DWORD ? ;9C == 14H
UnknownA0 DWORD ? ;A0 == LoaderLock
dwMajorVersion DWORD ? ;A4
dwMinorVersion DWORD ? ;A8
dwBuildNumber WORD ? ;AC
CSDVersion WORD ? ;AE
dwPlatformId DWORD ? ;B0
Subsystem DWORD ? ;B4
MajorSusbsytemVersion DWORD ? ;B8
MinorSusbsytemVersion DWORD ? ;BC
ProcessAffinityMask DWORD ? ;C0
UnknownC4 DWORD 044H DUP (?) ;C4
SessionId DWORD ? ;1D4
Unknown1D8 DWORD ? ;1D8
Unknown1DC DWORD ? ;1DC
Unknown1E0 DWORD ? ;1E0
Unknown1E4 DWORD ? ;1E4
PEB ENDS ;size 1E8H, NT4 size 150H
Interesting is the following field:
PebLdrData DWORD ? ;0C == *PEB_LDR_DATA
This field contains a pointer to another structure called PEB_LDR_DATA, let’s have a look at it.
PEB_LDR_DATA STRUCT ;
cbsize DWORD ? ;00 == 24H
Flags DWORD ? ;04
Unknown8 DWORD ? ;08
InLoadOrderModuleListHead DWORD ? ;0C
PreviousInLoadOrderLdrEntry DWORD ? ;10
InMemoryOrderModuleListHead DWORD ? ;14
PreviousInMemoryOrderLdrEntry DWORD ? ;18
InInitializationOrderModuleListHead DWORD ? ;1C
PreviousInInitializationOrderLdrEntry DWORD ? ;20
PEB_LDR_DATA ENDS ;size 24H
The PEB_LDR_DATA structure contains a field called InLoadOrderModuleListHead this field is what is needed, it points again to another structure called LDR_ENTRY.
LDR_ENTRY STRUCT ;
NextInLoadOrderLdrEntry DWORD ? ;00
PreviousInLoadOrderLdrEntry DWORD ? ;04
NextInMemoryOrderLdrEntry DWORD ? ;08
PreviousInMemoryOrderLdrEntry DWORD ? ;0C
NextInInitializationOrderLdrEntry DWORD ? ;10
PreviousInInitializationOrderLdrEntry DWORD ? ;14
ModuleBase DWORD ? ;18
EntryPoint DWORD ? ;1C
ModuleSize DWORD ? ;20
ModuleFileName UNICODE_STRING <> ;24
ModuleBaseName UNICODE_STRING <> ;2C
Flags DWORD ? ;34
LoadCount WORD ? ;38
TlsIndex WORD ? ;3A
LdrpHashTableEntry0 DWORD ? ;3C
LdrpHashTableEntry1 DWORD ? ;40
TimeStamp DWORD ? ;44
LDR_ENTRY ENDS ;size 48H
And here it is a pointer to the loaded ModuleFileName (ANSI VERSION). The very first Entry in this field points to ourself, means the first module is always our actual *.exe name (GetModuleFileName does the same). Using the field NextInLoadOrderLdrEntry you can walk the list of loaded Modules, it’s getting terminated by pointing to the first entry again.
;**************************************************************************
; ANTI DLL INJECTION USING PEB
;**************************************************************************
pushad
mov ecx,cs
xor cl,cl
cmp ecx,0
jnz no_9x_for_it_tiger_style
mov eax,fs:[30h] ;// ALLOC PEB BASE!
or eax,eax
je no_9x_for_it_tiger_Style
mov eax,[eax+0Ch] ;// MOD LIST
or eax,eax
je no_9x_for_it_tiger_Style
mov eax,[eax+PEB_LDR_DATA.InLoadOrderModuleListHead]
mov esi,eax
loop_all_modules:
cmp esi,[eax+PEB_LDR_DATA.NextInLoadOrderLdrEntry]
je no_9x_for_it_tiger_style
mov ecx,[eax+LDR_ENTRY.MODULEFILENAME+4]
;///////////////////////////////////////////////////////////////
;// CHECK MODULE NAME NOW!
;// SINCE WE HAVE TO DEAL WITH ANSI STRINGS GET IT IN A PROPER WAY
;// FIRST
;///////////////////////////////////////////////////////////////
push esi
push ecx
mov esi,ecx
lea edi,dword ptr [ebp+module_name]
call normalize_string
pop ecx
pop esi
cmp dword ptr [ebp+first_loop],1
jae dont_save_current_directory
mov dword ptr [ebp+first_loop],1
;///////////////////////////////////////////////////////////////
;// SAVE CURRENT DIRECTORY
;///////////////////////////////////////////////////////////////
push ecx
push esi
push eax
lea esi,dword ptr [ebp+module_name]
call strlen_x2
mov ecx,eax
pop eax
pop esi
push esi
lea esi,dword ptr [ebp+module_name]
add esi,ecx
call getdirectory
pop esi
pop ecx
mov byte ptr [edi+2],0 ;// SET TERMINATOR
pushad
lea esi,dword ptr [ebp+module_name]
call strlen_x2
mov ecx,eax
inc ecx
lea edi,dword ptr [ebp+current_dir]
repz movsb
popad
dont_save_current_directory:
;///////////////////////////////////////////////////////////////
;// GET ACTUAL DIRECTORY AND COMPARE BOTH DIRECTORIES!
;///////////////////////////////////////////////////////////////
cmp dword ptr [ebp+first_loop],2
jne set_flag_and_loop
push ecx
push esi
push eax
lea esi,dword ptr [ebp+module_name]
call strlen_x2
mov ecx,eax
pop eax
pop esi
push esi
lea esi,dword ptr [ebp+module_name]
add esi,ecx
call getdirectory
pop esi
pop ecx
mov byte ptr [edi+2],0 ;// SET TERMINATOR
pushad
lea esi,dword ptr [ebp+module_name]
call strlen_x2
mov ecx,eax
inc ecx
lea edi,dword ptr [ebp+current_dir]
repz cmpsb
or ecx,ecx
jne fine_puh
;///////////////////////////////////////////////////////////////
;// INJECTED DLL FOUND!
;// PUT YOUR CODE HERE
;///////////////////////////////////////////////////////////////
fine_puh:
popad
set_flag_and_loop:
mov dword ptr [ebp+first_loop],2
mov eax,[eax+PEB_LDR_DATA.NextInLoadOrderLdrEntry]
jmp loop_all_modules
strlen_X2:
pushad
xor ecx,ecx
count_next_char2:
lodsb
or al,al
je con_a2
inc ecx
jmp count_next_char2
con_a2: mov dword ptr [esp+1ch],ecx
popad
ret
getdirectory:
push ecx
push esi
push eax
mov edi,esi
std
mov al,'\'
repnz scasb
cld
pop eax
pop esi
pop ecx
ret
normalize_string:
pushad
normalize_string2:
lodsb
stosb
inc esi
or al,al
jne normalize_string2
popad
ret
first_loop dd ?
module_name db 0FFh dup (00h)
current_dir db 0FFh dup (00h)
no_9x_for_it_tiger_style:
mov ecx,offset no_9x_for_it_tiger_style-offset first_loop
lea edi,dword ptr [ebp+first_loop]
mov al,00
repz stosb
popad
w00t
ready for take off!