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

Comments: Post a Comment



<< Home

This page is powered by Blogger. Isn't yours?