Традиционные способы поиска неэкспортируемых символов основываются на интеллектуальном или полуинтеллектуальном поиске с использованием дизассемблера длин или полноценного дизассемблера, вплоть до построения графа базовых блоков для кода, что приводит к проблеме отделения кода от данных ( не решаемой в общем случае статическим анализом ).
А если отказаться от построения графа кода, то к примеру поиск ф-ции в ntdll.dll может быть полным кошмаром, изза его размазанности по всему модулю и дроблению на chunks.
Пример подобной ф-ции - LdrpCallInitRoutine(из нее вызываются точки входа длл).
Практически все пути к ней от экспортируемых ф-ций идут внутри chunks(плюс они довольно сильно меняются в разных ОС), и простой дизассемблер тут не поможет, нужно строить граф кода.
Однако, как ни странно, на помощь приходит самый дубовый и примитивный подход - поиск по сигнатуре. А все потому, что она изза своих особенностей реализована не в С коде, а в ассемблере(win2k\private\ntos\dll\i386\ldrthunk.asm):
;++
;
; VOID
; LdrpCallInitRoutine(
; IN PDLL_INIT_ROUTINE InitRoutine,
; IN PVOID DllHandle,
; IN ULONG Reason,
; IN PCONTEXT Context OPTIONAL
; )
;
; Routine Description:
;
; This function calls an x86 DLL init routine. It is robust against DLLs that don't preserve EBX or fail to clean up enough stack.
; The only register that the DLL init routine cannot trash is ESI.
;
; Arguments:
;
; InitRoutine - Address of init routine to call
; DllHandle - Handle of DLL to call
; Reason - one of the DLL_PROCESS_... or DLL_THREAD... values
; Context - context pointer or NULL
;
; Return Value:
;
; FALSE if the init routine fails, TRUE for success.
;
;--
cPublicProc _LdrpCallInitRoutine , 4
InitRoutine equ [ebp + 8]
DllHandle equ [ebp + 12]
Reason equ [ebp + 16]
Context equ [ebp + 20]
stdENDP _LdrpCallInitRoutine
push ebp
mov ebp, esp
push esi ; save esi across the call
push edi ; save edi across the call
push ebx ; save ebx on the stack across the call
mov esi,esp ; save the stack pointer in esi across the call
push Context
push Reason
push DllHandle
call InitRoutine
mov esp,esi ; restore the stack pointer in case callee forgot to clean up
pop ebx ; restore ebx
pop edi ; restore edi
pop esi ; restore esi
pop ebp
stdRET _LdrpCallInitRoutine
_TEXT ends
end
Asm компилятор как известно не является оптимизирующим, поэтому на всех ОС семейства windows ф-ция LdrpCallInitRoutine будет выглядеть одинаково и её легко можно найти по сигнатуре.
Конечно это скорее исключение, чем правило, но тем не менее, лично мне это сэкономило массу времени.
А если отказаться от построения графа кода, то к примеру поиск ф-ции в ntdll.dll может быть полным кошмаром, изза его размазанности по всему модулю и дроблению на chunks.
Пример подобной ф-ции - LdrpCallInitRoutine(из нее вызываются точки входа длл).
Практически все пути к ней от экспортируемых ф-ций идут внутри chunks(плюс они довольно сильно меняются в разных ОС), и простой дизассемблер тут не поможет, нужно строить граф кода.
Однако, как ни странно, на помощь приходит самый дубовый и примитивный подход - поиск по сигнатуре. А все потому, что она изза своих особенностей реализована не в С коде, а в ассемблере(win2k\private\ntos\dll\i386\ldrthunk.asm):
;++
;
; VOID
; LdrpCallInitRoutine(
; IN PDLL_INIT_ROUTINE InitRoutine,
; IN PVOID DllHandle,
; IN ULONG Reason,
; IN PCONTEXT Context OPTIONAL
; )
;
; Routine Description:
;
; This function calls an x86 DLL init routine. It is robust against DLLs that don't preserve EBX or fail to clean up enough stack.
; The only register that the DLL init routine cannot trash is ESI.
;
; Arguments:
;
; InitRoutine - Address of init routine to call
; DllHandle - Handle of DLL to call
; Reason - one of the DLL_PROCESS_... or DLL_THREAD... values
; Context - context pointer or NULL
;
; Return Value:
;
; FALSE if the init routine fails, TRUE for success.
;
;--
cPublicProc _LdrpCallInitRoutine , 4
InitRoutine equ [ebp + 8]
DllHandle equ [ebp + 12]
Reason equ [ebp + 16]
Context equ [ebp + 20]
stdENDP _LdrpCallInitRoutine
push ebp
mov ebp, esp
push esi ; save esi across the call
push edi ; save edi across the call
push ebx ; save ebx on the stack across the call
mov esi,esp ; save the stack pointer in esi across the call
push Context
push Reason
push DllHandle
call InitRoutine
mov esp,esi ; restore the stack pointer in case callee forgot to clean up
pop ebx ; restore ebx
pop edi ; restore edi
pop esi ; restore esi
pop ebp
stdRET _LdrpCallInitRoutine
_TEXT ends
end
Asm компилятор как известно не является оптимизирующим, поэтому на всех ОС семейства windows ф-ция LdrpCallInitRoutine будет выглядеть одинаково и её легко можно найти по сигнатуре.
Конечно это скорее исключение, чем правило, но тем не менее, лично мне это сэкономило массу времени.
Комментариев нет:
Отправить комментарий