283 lines
9.9 KiB
C
283 lines
9.9 KiB
C
|
/*
|
||
|
* Overview
|
||
|
*
|
||
|
* 1) Finding kernel32.dll base address VMA
|
||
|
* 2) Finding functions in dynamic libraries.
|
||
|
*
|
||
|
* Method Selection
|
||
|
*
|
||
|
* To use the PEB technique : -D USE_KERNEL32_METHOD_PEB (default)
|
||
|
* To use the SEH technique : -D USE_KERNEL32_METHOD_SEH
|
||
|
* To use the TOPSTACK technique: -D USE_KERNEL32_METHOD_TOPSTACK
|
||
|
*
|
||
|
* To use Windows NT-based only : -D USE_WINNT_ONLY
|
||
|
* To eliminate register saves : -D DISABLE_REGISTER_SAVES
|
||
|
* To use inline ; -D USE_INLINE
|
||
|
* To resolve a singular hash : -D USE_SINGULAR_HASH
|
||
|
*
|
||
|
* Notes
|
||
|
*
|
||
|
* Each of these methods are not guaranteed to work. The PEB technique
|
||
|
* is definitely the most likely to work. The only time it would fail
|
||
|
* is if someone is intentionally trying to break shellcode that uses it
|
||
|
* or Microsoft changes something. The SEH is the second most likely,
|
||
|
* however, it will fail under scenarios where the last handler does not
|
||
|
* point into kernel32. The TOPSTACK technique can fail if under some
|
||
|
* circumstance the 0x1c offset into the stack does not contain a pointer
|
||
|
* into kernel32. I know of no scenarios where this happens, but it is,
|
||
|
* in theory, possible. Lastly, both the SEH and TOPSTACK methods will
|
||
|
* fail if the characters 'MZ' exist at the base of a 64k aligned page
|
||
|
* boundary that does not actually denote the base of the image.
|
||
|
*
|
||
|
* Optimizations
|
||
|
*
|
||
|
* These functions preserve registers as much as possible so that they
|
||
|
* can be plugged in easily. If you need to reduce the size you can
|
||
|
* remove some of the register saving instructions. In some cases this
|
||
|
* can save as many as 4 bytes.
|
||
|
*
|
||
|
* Credits
|
||
|
*
|
||
|
* 1) Dino Dai Zovi's PEB resolution and function resolution techniques
|
||
|
* 2) Skywing@freenode and vizzini@freenode for ideas and optimizations
|
||
|
* on the SEH technique
|
||
|
* 3) optyx for optimizing with me on the top stack technique
|
||
|
*
|
||
|
* This file is generally meant to be included in other files.
|
||
|
*
|
||
|
* skape
|
||
|
* mmiller@hick.org
|
||
|
*/
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
int main()
|
||
|
{
|
||
|
#endif
|
||
|
|
||
|
#ifdef USE_ASM_BLOCK
|
||
|
__asm
|
||
|
{
|
||
|
#endif
|
||
|
|
||
|
#if defined(USE_KERNEL32_METHOD_SEH)
|
||
|
|
||
|
/*
|
||
|
* find_kernel32 -- SEH
|
||
|
*
|
||
|
* size : 33 bytes
|
||
|
* method : Walk the list of SEH handlers until we find the last one.
|
||
|
* From there we walk down in 64k blocks until we hit the top of
|
||
|
* kernel32.
|
||
|
* targets : 95/98/ME/NT/2K/XP
|
||
|
* arguments: none
|
||
|
* return : eax (kernel32.dll base address)
|
||
|
* clobbers : eax
|
||
|
*/
|
||
|
find_kernel32:
|
||
|
#ifndef DISABLE_REGISTER_SAVES
|
||
|
push esi // Save esi
|
||
|
push ecx // Save ecx
|
||
|
#endif
|
||
|
xor ecx, ecx // Zero ecx
|
||
|
mov esi, fs:[ecx] // Snag our SEH entry
|
||
|
not ecx // Set ecx to 0xffffffff
|
||
|
find_kernel32_seh_loop:
|
||
|
lodsd // Load the memory in esi into eax
|
||
|
mov esi, eax // Use this eax as our next pointer for esi
|
||
|
cmp [eax], ecx // Is the next-handler set to 0xffffffff?
|
||
|
jne find_kernel32_seh_loop // Nope, keep going. Otherwise, fall through.
|
||
|
find_kernel32_seh_loop_done:
|
||
|
mov eax, [eax + 0x04] // Snag the function handler address in eax
|
||
|
|
||
|
find_kernel32_base:
|
||
|
find_kernel32_base_loop:
|
||
|
dec eax // Subtract to our next page
|
||
|
xor ax, ax // Zero the lower half
|
||
|
cmp word ptr [eax], 0x5a4d // Is this the top of kernel32?
|
||
|
jne find_kernel32_base_loop // Nope? Try again.
|
||
|
find_kernel32_base_finished:
|
||
|
#ifndef DISABLE_REGISTER_SAVES
|
||
|
pop ecx // Restore ecx
|
||
|
pop esi // Restore esi
|
||
|
#endif
|
||
|
#ifndef USE_INLINE
|
||
|
ret // Return
|
||
|
#endif
|
||
|
|
||
|
#elif defined(USE_KERNEL32_METHOD_TOPSTACK)
|
||
|
|
||
|
/*
|
||
|
* find_kernel32 -- top stack
|
||
|
*
|
||
|
* size : 25 bytes
|
||
|
* method : Extract the top of the stack from the TEB.
|
||
|
* 0x1c bytes into the stack should hold a vma
|
||
|
* that is inside kernel32.dll. Grab it and
|
||
|
* walk down in 64k chunks until we hit the top
|
||
|
* of kernel32.dll.
|
||
|
* targets : NT/2K/XP
|
||
|
* arguments: none
|
||
|
* return : eax (kernel32.dll base address)
|
||
|
* clobbers : eax
|
||
|
*/
|
||
|
find_kernel32:
|
||
|
#ifndef DISABLE_REGISTER_SAVES
|
||
|
push esi // Save esi
|
||
|
#endif
|
||
|
xor esi, esi // Zero esi
|
||
|
mov esi, fs:[esi + 0x18] // Extract TEB
|
||
|
lodsd // Grab a pointer we don't need
|
||
|
lodsd // Grab the top of the stack for this thread
|
||
|
mov eax, [eax - 0x1c] // Snag a function pointer that's 0x1c bytes into the stack
|
||
|
|
||
|
find_kernel32_base:
|
||
|
find_kernel32_base_loop:
|
||
|
dec eax // Subtract to our next page
|
||
|
xor ax, ax // Zero the lower half
|
||
|
cmp word ptr [eax], 0x5a4d // Is this the top of kernel32?
|
||
|
jne find_kernel32_base_loop // Nope? Try again.
|
||
|
find_kernel32_base_finished:
|
||
|
#ifndef DISABLE_REGISTER_SAVES
|
||
|
pop esi // Restore esi
|
||
|
#endif
|
||
|
#ifndef USE_INLINE
|
||
|
ret // Return
|
||
|
#endif
|
||
|
|
||
|
#else // Default method
|
||
|
|
||
|
/*
|
||
|
* find_kernel32 -- PEB
|
||
|
*
|
||
|
* size : 34 bytes
|
||
|
* method : Lookup the PEB and walk one node back in the loaded
|
||
|
* module list. Extract the base address from this entry.
|
||
|
* It should point to kernel32.dll.
|
||
|
* targets : 95/98/ME/NT/2K/XP
|
||
|
* arguments: none
|
||
|
* return : eax (kernel32.dll base address)
|
||
|
* clobbers : eax
|
||
|
*/
|
||
|
find_kernel32:
|
||
|
#ifndef DISABLE_REGISTER_SAVES
|
||
|
push esi // Save esi
|
||
|
#endif
|
||
|
xor eax, eax
|
||
|
mov eax, fs:[eax+0x30] // Extract the PEB
|
||
|
#ifndef USE_WINNT_ONLY
|
||
|
test eax, eax // Check for Windows 9x
|
||
|
js find_kernel32_9x // If signed short, jump to windows 9x lookup
|
||
|
#endif
|
||
|
find_kernel32_nt:
|
||
|
mov eax, [eax + 0x0c] // Extract the PROCESS_MODULE_INFO pointer from the PEB
|
||
|
mov esi, [eax + 0x1c] // Get the address of flink in the init module list
|
||
|
lodsd // Load the address of blink into eax
|
||
|
mov eax, [eax + 0x8] // Grab the module base address from the list entry
|
||
|
#ifndef USE_WINNT_ONLY
|
||
|
jmp find_kernel32_finished // Fall down to the bottom
|
||
|
find_kernel32_9x:
|
||
|
mov eax, [eax + 0x34] // Undocumented offset (0x34)
|
||
|
lea eax, [eax + 0x7c] // Load the address of eax+0x7c to keep us in signed byte range
|
||
|
mov eax, [eax + 0x3c] // Undocumented offset (0xb8)
|
||
|
find_kernel32_finished:
|
||
|
#endif
|
||
|
#ifndef DISABLE_REGISTER_SAVES
|
||
|
pop esi // Restore esi
|
||
|
#endif
|
||
|
#ifndef USE_INLINE
|
||
|
ret // Return
|
||
|
#endif
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* find_function
|
||
|
*
|
||
|
* method : Walks the export list of the given image
|
||
|
* until it finds a symbol whose hashed name
|
||
|
* matches the one that was passed in.
|
||
|
* targets : 95/98/ME/NT/2K/XP
|
||
|
* arguments: [esp + 0x24] (library base address)
|
||
|
* [esp + 0x28] (function hash)
|
||
|
* return : eax (resultant function address)
|
||
|
* clobbers : eax
|
||
|
*/
|
||
|
find_function:
|
||
|
#ifndef DISABLE_REGISTER_SAVES
|
||
|
pushad // Save all registers
|
||
|
#ifdef USE_INLINE
|
||
|
mov ebp, eax // Take the base address of kernel32 and put it in ebp
|
||
|
#else
|
||
|
mov ebp, [esp + 0x24] // Store the base address in eax
|
||
|
#endif
|
||
|
#else
|
||
|
#ifdef USE_INLINE
|
||
|
mov ebp, eax // Take the base address of kernel32 and put it in ebp
|
||
|
#else
|
||
|
mov ebp, [esp + 0x4] // Store the base address in eax if non-inline
|
||
|
#endif
|
||
|
#endif
|
||
|
mov eax, [ebp + 0x3c] // PE header VMA
|
||
|
mov edx, [ebp + eax + 0x78] // Export table relative offset
|
||
|
add edx, ebp // Export table VMA
|
||
|
mov ecx, [edx + 0x18] // Number of names
|
||
|
mov ebx, [edx + 0x20] // Names table relative offset
|
||
|
add ebx, ebp // Names table VMA
|
||
|
find_function_loop:
|
||
|
jecxz find_function_finished // Jump to the end if ecx is 0
|
||
|
dec ecx // Decrement our names counter
|
||
|
mov esi, [ebx + ecx * 4] // Store the relative offset of the name
|
||
|
add esi, ebp // Set esi to the VMA of the current name
|
||
|
compute_hash:
|
||
|
xor edi, edi // Zero edi
|
||
|
xor eax, eax // Zero eax
|
||
|
cld // Clear direction
|
||
|
compute_hash_again:
|
||
|
lodsb // Load the next byte from esi into al
|
||
|
test al, al // Test ourselves.
|
||
|
jz compute_hash_finished // If the ZF is set, we've hit the null term.
|
||
|
ror edi, 0xd // Rotate edi 13 bits to the right
|
||
|
add edi, eax // Add the new byte to the accumulator
|
||
|
jmp compute_hash_again // Next iteration
|
||
|
compute_hash_finished:
|
||
|
find_function_compare:
|
||
|
#ifdef USE_INLINE
|
||
|
#ifdef USE_SINGULAR_HASH
|
||
|
cmp edi, USE_SINGULAR_HASH // Compare it to a specific hash
|
||
|
#else
|
||
|
cmp edi, [esp + 0x8] // Compare the computed hash with the requested hash
|
||
|
#endif
|
||
|
#else
|
||
|
#ifdef USE_SINGULAR_HASH
|
||
|
cmp edi, USE_SINGULAR_HASH // Compare it to a specific hash
|
||
|
#else
|
||
|
cmp edi, [esp + 0x28] // Compare the computed hash with the requested hash
|
||
|
#endif
|
||
|
#endif
|
||
|
jnz find_function_loop // No match, try the next one.
|
||
|
mov ebx, [edx + 0x24] // Ordinals table relative offset
|
||
|
add ebx, ebp // Ordinals table VMA
|
||
|
mov cx, [ebx + 2 * ecx] // Extrapolate the function's ordinal
|
||
|
mov ebx, [edx + 0x1c] // Address table relative offset
|
||
|
add ebx, ebp // Address table VMA
|
||
|
mov eax, [ebx + 4 * ecx] // Extract the relative function offset from its ordinal
|
||
|
add eax, ebp // Function VMA
|
||
|
#ifndef DISABLE_REGISTER_SAVES
|
||
|
mov [esp + 0x1c], eax // Overwrite stack version of eax from pushad
|
||
|
find_function_finished:
|
||
|
popad // Restore all registers
|
||
|
#else
|
||
|
find_function_finished:
|
||
|
#endif
|
||
|
#ifndef USE_INLINE
|
||
|
ret // Return
|
||
|
#endif
|
||
|
|
||
|
#ifdef USE_ASM_BLOCK
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
}
|
||
|
#endif
|