 main menuhome
forums Show me new threads!
bookmarks
post article
view blogs
vault you must be level 2 to upload files to your vault
downloads you must be logged to access downloads
Rootkit Collection
A news back-end to implement RootKit news into your website is here or more advanced version here.
An XML/RSS feed that includes both NEWS and BLOGS for RootKit is here: XML/RSS.
Beta feed for replied posts here. feedback to admins not forums, we know about times being off...
|
Shield from DLL-Injection
Jan 07 2007, 14:02 (UTC+0) | Opcode0x90 writes: Okay, actually this blog should entitled "Shield from code-injection". If you want to know why, read on.
This method was discovered when I was doing some random debugging. Sudden idea came to my mind when I inject some DLL into olly-debugged process. Olly log traced that one thread was created and terminated. Then I thought since the DLL loading takes place in user-mode, why cant I prevent it from loading by hooking some function ?
So I put a bp on kernel32.LoadLibraryA() and inject DLL again. ollydbg halted. I traced the stack frame to one function in kernel32.dll. I inject some DLL again, and yet I traced to the same function.
My sense tell me that is the function I'm looking for. So I began coding and hook that function. Voila, now Winject reports DLL-injection failed. But wait, our job is not done yet.
After more debugging I found that my hook was preventing the our own thread from creating too. So I need a method to distinguish rogue thread from our own thread.
Finally, I found a method used by Piotr Bania to prevent shellcode execution. He used VirtualProtect() to determine whether a code is rouge or not. Usually shellcode is injected as a result of stack-overflow or any other memory-based leak. These memory should be writable. If any pointer is pointing to a writable memory section, we can conclude that it is altered by the shellcode.
Yet, this method has a flaw. Most packer and protector modifies PE and mark the image as writable (to decompress or decrypt the content) and doesn't bother to restore them. It would raise false alarm when we use VirtualProtect() to check the protection. So I thought of a better solution.
I used VirtualQuery() to check for memory type. Usually when we create a thread, it should point to code within the image. (marked by loader as MEM_IMAGE) Any VirtualAllocEx() allocated memory would not have that flag set.
End of story, here goes the real stuff:
; ======================================================================= ;
.386
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
.data
; code signature for kernel32.BaseThreadStartThunk()
Signature BYTE 033h, 0EDh, 053h, 050h, 06Ah, 000h, 0E9h
;
; 7C810659 > 33ED XOR EBP, EBP
; 7C81065B 53 PUSH EBX
; 7C81065C 50 PUSH EAX
; 7C81065D 6A 00 PUSH 0
; 7C81065F ^ E9 E8AFFFFF JMP 7C80B64C ; kernel32.BaseThreadStart
;
; strings
szBusted BYTE "Busted", 0
szkernel32 BYTE "kernel32.dll", 0
szLoadLibraryA BYTE "LoadLibraryA", 0
szFreeLibrary BYTE "FreeLibrary", 0
szExitProcess BYTE "ExitProcess", 0
.data?
; address
Addr_LoadLibraryA DWORD ?
Addr_FreeLibrary DWORD ?
Addr_ExitProcess DWORD ?
Addr_BaseThreadStart DWORD ?
Addr_BaseThreadStartThunk DWORD ?
; you know
hModule DWORD ?
lpflOldProtect DWORD ?
.code
SigSeek_FindCode proc uses ebx ecx edx esi edi dwStart:DWORD, dwEnd:DWORD, lpSig:DWORD, dwSize:DWORD
;
; EBX = dwSize
; EDX = dwEnd - dwSize
;
; load the arguments into register
mov ebx, dwSize
mov edx, dwEnd
sub edx, ebx
; scan for specified signature
mov ecx, ebx
mov esi, dwStart
mov edi, lpSig
.repeat
; compare the string
repe cmpsb
.if zero?
; found the signature, return the address
mov eax, ebx
sub eax, ecx
sub esi, eax
mov eax, esi
jmp @f
.endif
; restore ECX, ESI, EDI registers
mov eax, ebx
sub eax, ecx
sub esi, eax
sub edi, eax
mov ecx, ebx
; move to next byte
add esi, 1
.until (esi == edx)
; return FALSE
xor eax, eax
@@:
ret
SigSeek_FindCode endp
Does_Something_Smell_Fishy proc lpStartAddress:DWORD
LOCAL lpMemInfo : MEMORY_BASIC_INFORMATION
; is the start address pointing to some fishy address ?
mov eax, lpStartAddress
.if (eax == Addr_LoadLibraryA) || (eax == Addr_FreeLibrary) || (eax == Addr_ExitProcess)
; obviously suspicious
mov eax, TRUE
jmp @f
.endif
; do a check on memory allocation type
invoke VirtualQuery, lpStartAddress, addr lpMemInfo, sizeof lpMemInfo
.if !(lpMemInfo.lType & MEM_IMAGE)
; hey, this thread isn't pointing to any loaded image, hmmmm
mov eax, TRUE
jmp @f
.endif
; no fish is found here
xor eax, eax
@@:
ret
Does_Something_Smell_Fishy endp
Hook_BaseThreadStartThunk proc
LOCAL lParam
LOCAL lpStartAddress
;
; EAX = lpStartAddress
; EBX = lParam
;
; save the parameters
mov lpStartAddress, eax
mov lParam, ebx
; does something smell fishy ?
invoke Does_Something_Smell_Fishy, lpStartAddress
.if (eax != FALSE)
; yeah, it smells
invoke MessageBox, NULL, addr szBusted, NULL, MB_OK
; kill this rogue thread
invoke ExitThread, NULL
.else
; jump to kernel32.BaseThreadStart()
xor ebp, ebp
push lParam
push lpStartAddress
push NULL
jmp Addr_BaseThreadStart
.endif
Hook_BaseThreadStartThunk endp
start:
; this wouldn't need any comments, eh?
invoke GetModuleHandle, addr szkernel32
mov hModule, eax
;
; scan for kernel32.BaseThreadStartThunk() signature from [base address] to [base address + 20000h] because it is not exported
;
; TODO: retrieve image size from PE, instead of using fixed image size value
;
mov ebx, eax
invoke SigSeek_FindCode, ebx, addr [ebx+20000h], addr Signature, sizeof Signature
mov Addr_BaseThreadStartThunk, eax
; extract the address of kernel32.BaseThreadStart() from jmp instruction
; destination = code location + jump offset + 5
mov ebx, [eax+7]
add ebx, eax ; / code location
add ebx, 6 ;
add ebx, 5
mov Addr_BaseThreadStart, ebx
; hook kernel32.BaseThreadStartThunk() function
invoke VirtualProtect, Addr_BaseThreadStartThunk, 5, PAGE_EXECUTE_READWRITE, addr lpflOldProtect
.if (eax != NULL)
invoke GetCurrentProcess
invoke FlushInstructionCache, eax, Addr_BaseThreadStartThunk, 5
mov edi, Addr_BaseThreadStartThunk
mov eax, offset Hook_BaseThreadStartThunk
sub eax, edi
sub eax, 5
mov byte ptr [edi+0], 0E9h ; jmp short
mov dword ptr [edi+1], eax
invoke VirtualProtect, Addr_BaseThreadStartThunk, 5, lpflOldProtect, addr lpflOldProtect
.endif
; get some function address
invoke GetProcAddress, hModule, addr szLoadLibraryA
mov Addr_LoadLibraryA, eax
invoke GetProcAddress, hModule, addr szFreeLibrary
mov Addr_FreeLibrary, eax
invoke GetProcAddress, hModule, addr szExitProcess
mov Addr_ExitProcess, eax
; we will see
invoke Sleep, INFINITE
ret
end start
; ======================================================================= ;
</pre>
Yet, this method only blocks out 80% of DLL-injecting tools that uses CreateRemoteThread(). Other method such as SetWindowsHookEx(), SetThreadContext() or code caving will work. That will take different approach to deal with.
No kernel is hurt during creation of this code. :)
Special Thanks to
- Piotr Bania,
- TiMBuS (I never knew that function has a name)
|
| |
ROOTKITS, Subverting the Windows Kernel
By: Greg Hoglund and Jamie Butler
Rootkits are powerful tools to compromise computer systems without detection. Get the original and best book on the subject here.
|
active for last 5 minutes
registered users:79912
There are currently 0 registered users and 22 guests browsing the website.
Welcome our latest registered user: Pris
| Jul 31, 12:06 |
| May 09, 04:30 |
| May 08, 15:33 |
| May 04, 15:42 |
| May 02, 03:59 |
| Best Screenshots / Analog |
| the most active news users |
based on the number of news posts for last 30 days
|