IDA плохо распознает код, если на него нет перекрестных ссылок. Наглядный пример - SEH обработчики для x64 кода.
Как известно, модель исключений в х64 изменилась, в ней используется data-driven подход, в котором роль метаданных играет содержимое секции PE файла. Данная секция содержит все необходимые для раскрутки данные, а именно, массив ф-ций RUNTIME_FUNCTION:
typedef struct _RUNTIME_FUNCTION
{
ULONG BeginAddress;
ULONG EndAddress;
ULONG UnwindData;
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
UnwindData представлена следующей структурой переменной длины:
typedef struct _UNWIND_INFO
{
UCHAR Version : 3;
UCHAR Flags : 5;
UCHAR SizeOfProlog;
UCHAR CountOfCodes;
UCHAR FrameRegister : 4;
UCHAR FrameOffset : 4;
UNWIND_CODE UnwindCode[1];
//
// The unwind codes are followed by an optional DWORD aligned field that
// contains the exception handler address or a function table entry if
// chained unwind information is specified. If an exception handler address
// is specified, then it is followed by the language specified exception
// handler data.
//
// union
// {
// struct
// {
// ULONG ExceptionHandler;
// ULONG ExceptionData[];
// };
//
// RUNTIME_FUNCTION FunctionEntry;
// };
//
} UNWIND_INFO, *PUNWIND_INFO;
Соответственно, если установлен флаг UNW_FLAG_EHANDLER, то в данной структуре присутствует SEH обработчик, сразу за которым следует структура SCOPE_TABLE:
typedef struct _SCOPE_TABLE
{
ULONG Count;
struct
{
ULONG BeginAddress;
ULONG EndAddress;
ULONG HandlerAddress;
ULONG JumpTarget;
} ScopeRecord[1];
} SCOPE_TABLE, *PSCOPE_TABLE;
Именно массив записей ScopeRecord и содержат SEH обработчики для ф-ции.
В ExceptionHandler структуры UNWIND_INFO чаще всего сидит _C_specific_handler, который извлекает все HandlerAddress из ScopeRecord,
проверяет принадлежность инструкции вызвавшей исключение на принадлежность к RUNTIME_FUNCTION (т.е. попадает ли она в диапазон BeginAddress - EndAddress), и если хендлер != 1 и попадает в диапазон - он вызывается.
Обладая данными знаниями несложно написать скрипт, который распознает все seh обработчики в файле.
Зачем это может понадобиться будет рассмотрено в следующей заметке.
До обработки скриптом:
PAGE:00000000007A1290 NtAllocateUuids endp
PAGE:00000000007A1290
PAGE:00000000007A1290 ; ---------------------------------------------------------------------------
PAGE:00000000007A1291 db 0Fh dup(90h)
PAGE:00000000007A12A0 db 8Bh, 0C0h
PAGE:00000000007A12A2 dw 5553h, 8348h, 28ECh
PAGE:00000000007A12A8 dq 25048B4865EA8B48h, 4845894800000188h, 983848458B48DB33h
PAGE:00000000007A12A8 dq 8BC3950F00000153h, 5D28C48348C38BC3h, 909090909090C35Bh
PAGE:00000000007A12A8 dq 9090909090909090h, 28EC83485553C08Bh, 25048B4865EA8B48h
PAGE:00000000007A12A8 dq 4045894800000188h, 9938404D8B48DB33h, 8BC3950F00000153h
PAGE:00000000007A12A8 dq 5D28C48348C38BC3h, 909090909090C35Bh, 9090909090909090h
PAGE:00000000007A12A8 dq 4820EC834855C08Bh, 8825048B4865EA8Bh, 3320458948000001h
PAGE:00000000007A12A8 dq 538138204D8B48C0h, 8348C0950F000001h, 90909090C35D20C4h
PAGE:00000000007A12A8 dq 2 dup(9090909090909090h)
PAGE:00000000007A1360
PAGE:00000000007A1360 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A1360
PAGE:00000000007A1360
PAGE:00000000007A1360 ; int __fastcall XIPLocatePages(PVOID Object, __int64, __int64, __int64, __int64)
После обработки скриптом:
PAGE:00000000007A1290 NtAllocateUuids endp
PAGE:00000000007A1290
PAGE:00000000007A1290 ; ---------------------------------------------------------------------------
PAGE:00000000007A1291 db 0Fh dup(90h)
PAGE:00000000007A12A0
PAGE:00000000007A12A0 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A12A0
PAGE:00000000007A12A0
PAGE:00000000007A12A0 sub_7A12A0 proc near
PAGE:00000000007A12A0 mov eax, eax
PAGE:00000000007A12A2 push rbx
PAGE:00000000007A12A3 push rbp
PAGE:00000000007A12A4 sub rsp, 28h
PAGE:00000000007A12A8 mov rbp, rdx
PAGE:00000000007A12AB mov rax, gs:188h
PAGE:00000000007A12B4 mov [rbp+48h], rax
PAGE:00000000007A12B8 xor ebx, ebx
PAGE:00000000007A12BA mov rax, [rbp+48h]
PAGE:00000000007A12BE cmp [rax+153h], bl
PAGE:00000000007A12C4 setnz bl
PAGE:00000000007A12C7 mov eax, ebx
PAGE:00000000007A12C9 mov eax, ebx
PAGE:00000000007A12CB add rsp, 28h
PAGE:00000000007A12CF pop rbp
PAGE:00000000007A12D0 pop rbx
PAGE:00000000007A12D1 retn
PAGE:00000000007A12D1 sub_7A12A0 endp
PAGE:00000000007A12D1
PAGE:00000000007A12D1 ; ---------------------------------------------------------------------------
PAGE:00000000007A12D2 align 20h
PAGE:00000000007A12E0
PAGE:00000000007A12E0 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A12E0
PAGE:00000000007A12E0
PAGE:00000000007A12E0 sub_7A12E0 proc near
PAGE:00000000007A12E0 mov eax, eax
PAGE:00000000007A12E2 push rbx
PAGE:00000000007A12E3 push rbp
PAGE:00000000007A12E4 sub rsp, 28h
PAGE:00000000007A12E8 mov rbp, rdx
PAGE:00000000007A12EB mov rax, gs:188h
PAGE:00000000007A12F4 mov [rbp+40h], rax
PAGE:00000000007A12F8 xor ebx, ebx
PAGE:00000000007A12FA mov rcx, [rbp+40h]
PAGE:00000000007A12FE cmp [rcx+153h], bl
PAGE:00000000007A1304 setnz bl
PAGE:00000000007A1307 mov eax, ebx
PAGE:00000000007A1309 mov eax, ebx
PAGE:00000000007A130B add rsp, 28h
PAGE:00000000007A130F pop rbp
PAGE:00000000007A1310 pop rbx
PAGE:00000000007A1311 retn
PAGE:00000000007A1311 sub_7A12E0 endp
PAGE:00000000007A1311
PAGE:00000000007A1311 ; ---------------------------------------------------------------------------
PAGE:00000000007A1312 align 20h
PAGE:00000000007A1320
PAGE:00000000007A1320 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A1320
PAGE:00000000007A1320
PAGE:00000000007A1320 sub_7A1320 proc near
PAGE:00000000007A1320 mov eax, eax
PAGE:00000000007A1322 push rbp
PAGE:00000000007A1323 sub rsp, 20h
PAGE:00000000007A1327 mov rbp, rdx
PAGE:00000000007A132A mov rax, gs:188h
PAGE:00000000007A1333 mov [rbp+20h], rax
PAGE:00000000007A1337 xor eax, eax
PAGE:00000000007A1339 mov rcx, [rbp+20h]
PAGE:00000000007A133D cmp [rcx+153h], al
PAGE:00000000007A1343 setnz al
PAGE:00000000007A1346 add rsp, 20h
PAGE:00000000007A134A pop rbp
PAGE:00000000007A134B retn
PAGE:00000000007A134B sub_7A1320 endp
PAGE:00000000007A134B
PAGE:00000000007A134B ; ---------------------------------------------------------------------------
PAGE:00000000007A134C db 90h
Собственно, сам скрипт:
import idaapi
import idautils
import idc
class ExceptionDetector():
def __init__( self ):
self.imageBase = self.GetModuleBase()
def GetModuleBase( self ):
defaultHeaderSize = 0x1000
for segEa in Segments():
imageBase = segEa - defaultHeaderSize
return imageBase
def GetExceptionSectionAddress( self ):
for ea in Segments():
if SegName( ea ) == ".pdata":
return ea
return 0
def GetHandlerAddressFromUnwindData( self, unwindDataAddress ):
versionFlags = Byte( unwindDataAddress )
countOfCodes = Byte( unwindDataAddress + 2 )
# only even count allowed
if countOfCodes % 2:
countOfCodes = countOfCodes + 1
# have flag UNW_FLAG_EHANDLER?
if versionFlags & 8:
handlerAddress = unwindDataAddress + 4 + countOfCodes * 2
return handlerAddress
return 0
def GetScopeHandlers( self, scopeTableAddress, unwindDataAddress ):
scopeHandlers = []
sizeOfScopeRecord = 16
scopeTableEntriesCount = Dword( scopeTableAddress )
# FIXME: how many scope entries is possible?
if scopeTableEntriesCount > 10:
return scopeHandlers
# read scope table and fill list with HandlerAddress values
for i in range( 0, scopeTableEntriesCount ):
scopeRecord = struct.unpack( "4I", idaapi.get_many_bytes( scopeTableAddress, sizeOfScopeRecord ) )
scopeHandler = scopeRecord[3]
if scopeHandler != 1:
scopeHandlers.append( self.imageBase + scopeHandler )
scopeTableAddress = scopeTableAddress + sizeOfScopeRecord
return scopeHandlers
# WARN: FindFuncEnd failed at times, so desirable run script twice for better results ( on second time IDA analyser works much better )
def MakeFunctionForHandler( self, scopeHandler ):
MakeUnkn( scopeHandler, DOUNK_SIMPLE )
MakeCode( scopeHandler )
functionStart = scopeHandler
functionEnd = FindFuncEnd( functionStart )
if functionEnd == BADADDR:
functionEnd = functionStart + 1
MakeFunction( functionStart, functionEnd )
def CreateFunctionsForHandlers( self ):
sizeOfRuntimeFunction = 12
ea = self.GetExceptionSectionAddress()
if ea == 0:
print "CreateFunctionsForHandlers: can't find exception section"
return
else:
print "-------------------------------------------"
print "exception section finded at address %x" % ea
print "-------------------------------------------"
while True:
runtimeFunction = struct.unpack( "3I", idaapi.get_many_bytes( ea, sizeOfRuntimeFunction ) )
unwindDataRel = runtimeFunction[2]
# end of exception section - zero padding bytes
if unwindDataRel == 0:
break
handlerAddress = self.GetHandlerAddressFromUnwindData( self.imageBase + unwindDataRel )
if handlerAddress:
handler = Dword( handlerAddress )
scopeTableAddress = handlerAddress + 4
scopeHandlers = self.GetScopeHandlers( scopeTableAddress, self.imageBase + unwindDataRel )
scopeHandlersLength = len( scopeHandlers )
if scopeHandlersLength > 0:
print "RuntimeFunction address = %x UnwindData = %x, ExceptionHandler = %x" % ( ea, self.imageBase + unwindDataRel, self.imageBase + handler )
for i in range( 0, scopeHandlersLength ):
self.MakeFunctionForHandler( scopeHandlers[i] )
print "Scope Handler = %x" % ( scopeHandlers[i] )
print "-----------------------------------------"
ea = ea + sizeOfRuntimeFunction
#
# entry point
#
print "Script Start...\n"
detector = ExceptionDetector()
detector.CreateFunctionsForHandlers()
print "\nScript End..."
p.s. Из-за особенности анализатора IDA, скрипт лучше всего запускать дважды.
После первого запуска IDA похоже обновляет свои внутренние данные и на второй запуск определяется практически все хендлеры.
Как известно, модель исключений в х64 изменилась, в ней используется data-driven подход, в котором роль метаданных играет содержимое секции PE файла. Данная секция содержит все необходимые для раскрутки данные, а именно, массив ф-ций RUNTIME_FUNCTION:
typedef struct _RUNTIME_FUNCTION
{
ULONG BeginAddress;
ULONG EndAddress;
ULONG UnwindData;
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
UnwindData представлена следующей структурой переменной длины:
typedef struct _UNWIND_INFO
{
UCHAR Version : 3;
UCHAR Flags : 5;
UCHAR SizeOfProlog;
UCHAR CountOfCodes;
UCHAR FrameRegister : 4;
UCHAR FrameOffset : 4;
UNWIND_CODE UnwindCode[1];
//
// The unwind codes are followed by an optional DWORD aligned field that
// contains the exception handler address or a function table entry if
// chained unwind information is specified. If an exception handler address
// is specified, then it is followed by the language specified exception
// handler data.
//
// union
// {
// struct
// {
// ULONG ExceptionHandler;
// ULONG ExceptionData[];
// };
//
// RUNTIME_FUNCTION FunctionEntry;
// };
//
} UNWIND_INFO, *PUNWIND_INFO;
Соответственно, если установлен флаг UNW_FLAG_EHANDLER, то в данной структуре присутствует SEH обработчик, сразу за которым следует структура SCOPE_TABLE:
typedef struct _SCOPE_TABLE
{
ULONG Count;
struct
{
ULONG BeginAddress;
ULONG EndAddress;
ULONG HandlerAddress;
ULONG JumpTarget;
} ScopeRecord[1];
} SCOPE_TABLE, *PSCOPE_TABLE;
Именно массив записей ScopeRecord и содержат SEH обработчики для ф-ции.
В ExceptionHandler структуры UNWIND_INFO чаще всего сидит _C_specific_handler, который извлекает все HandlerAddress из ScopeRecord,
проверяет принадлежность инструкции вызвавшей исключение на принадлежность к RUNTIME_FUNCTION (т.е. попадает ли она в диапазон BeginAddress - EndAddress), и если хендлер != 1 и попадает в диапазон - он вызывается.
Обладая данными знаниями несложно написать скрипт, который распознает все seh обработчики в файле.
Зачем это может понадобиться будет рассмотрено в следующей заметке.
До обработки скриптом:
PAGE:00000000007A1290 NtAllocateUuids endp
PAGE:00000000007A1290
PAGE:00000000007A1290 ; ---------------------------------------------------------------------------
PAGE:00000000007A1291 db 0Fh dup(90h)
PAGE:00000000007A12A0 db 8Bh, 0C0h
PAGE:00000000007A12A2 dw 5553h, 8348h, 28ECh
PAGE:00000000007A12A8 dq 25048B4865EA8B48h, 4845894800000188h, 983848458B48DB33h
PAGE:00000000007A12A8 dq 8BC3950F00000153h, 5D28C48348C38BC3h, 909090909090C35Bh
PAGE:00000000007A12A8 dq 9090909090909090h, 28EC83485553C08Bh, 25048B4865EA8B48h
PAGE:00000000007A12A8 dq 4045894800000188h, 9938404D8B48DB33h, 8BC3950F00000153h
PAGE:00000000007A12A8 dq 5D28C48348C38BC3h, 909090909090C35Bh, 9090909090909090h
PAGE:00000000007A12A8 dq 4820EC834855C08Bh, 8825048B4865EA8Bh, 3320458948000001h
PAGE:00000000007A12A8 dq 538138204D8B48C0h, 8348C0950F000001h, 90909090C35D20C4h
PAGE:00000000007A12A8 dq 2 dup(9090909090909090h)
PAGE:00000000007A1360
PAGE:00000000007A1360 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A1360
PAGE:00000000007A1360
PAGE:00000000007A1360 ; int __fastcall XIPLocatePages(PVOID Object, __int64, __int64, __int64, __int64)
После обработки скриптом:
PAGE:00000000007A1290 NtAllocateUuids endp
PAGE:00000000007A1290
PAGE:00000000007A1290 ; ---------------------------------------------------------------------------
PAGE:00000000007A1291 db 0Fh dup(90h)
PAGE:00000000007A12A0
PAGE:00000000007A12A0 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A12A0
PAGE:00000000007A12A0
PAGE:00000000007A12A0 sub_7A12A0 proc near
PAGE:00000000007A12A0 mov eax, eax
PAGE:00000000007A12A2 push rbx
PAGE:00000000007A12A3 push rbp
PAGE:00000000007A12A4 sub rsp, 28h
PAGE:00000000007A12A8 mov rbp, rdx
PAGE:00000000007A12AB mov rax, gs:188h
PAGE:00000000007A12B4 mov [rbp+48h], rax
PAGE:00000000007A12B8 xor ebx, ebx
PAGE:00000000007A12BA mov rax, [rbp+48h]
PAGE:00000000007A12BE cmp [rax+153h], bl
PAGE:00000000007A12C4 setnz bl
PAGE:00000000007A12C7 mov eax, ebx
PAGE:00000000007A12C9 mov eax, ebx
PAGE:00000000007A12CB add rsp, 28h
PAGE:00000000007A12CF pop rbp
PAGE:00000000007A12D0 pop rbx
PAGE:00000000007A12D1 retn
PAGE:00000000007A12D1 sub_7A12A0 endp
PAGE:00000000007A12D1
PAGE:00000000007A12D1 ; ---------------------------------------------------------------------------
PAGE:00000000007A12D2 align 20h
PAGE:00000000007A12E0
PAGE:00000000007A12E0 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A12E0
PAGE:00000000007A12E0
PAGE:00000000007A12E0 sub_7A12E0 proc near
PAGE:00000000007A12E0 mov eax, eax
PAGE:00000000007A12E2 push rbx
PAGE:00000000007A12E3 push rbp
PAGE:00000000007A12E4 sub rsp, 28h
PAGE:00000000007A12E8 mov rbp, rdx
PAGE:00000000007A12EB mov rax, gs:188h
PAGE:00000000007A12F4 mov [rbp+40h], rax
PAGE:00000000007A12F8 xor ebx, ebx
PAGE:00000000007A12FA mov rcx, [rbp+40h]
PAGE:00000000007A12FE cmp [rcx+153h], bl
PAGE:00000000007A1304 setnz bl
PAGE:00000000007A1307 mov eax, ebx
PAGE:00000000007A1309 mov eax, ebx
PAGE:00000000007A130B add rsp, 28h
PAGE:00000000007A130F pop rbp
PAGE:00000000007A1310 pop rbx
PAGE:00000000007A1311 retn
PAGE:00000000007A1311 sub_7A12E0 endp
PAGE:00000000007A1311
PAGE:00000000007A1311 ; ---------------------------------------------------------------------------
PAGE:00000000007A1312 align 20h
PAGE:00000000007A1320
PAGE:00000000007A1320 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A1320
PAGE:00000000007A1320
PAGE:00000000007A1320 sub_7A1320 proc near
PAGE:00000000007A1320 mov eax, eax
PAGE:00000000007A1322 push rbp
PAGE:00000000007A1323 sub rsp, 20h
PAGE:00000000007A1327 mov rbp, rdx
PAGE:00000000007A132A mov rax, gs:188h
PAGE:00000000007A1333 mov [rbp+20h], rax
PAGE:00000000007A1337 xor eax, eax
PAGE:00000000007A1339 mov rcx, [rbp+20h]
PAGE:00000000007A133D cmp [rcx+153h], al
PAGE:00000000007A1343 setnz al
PAGE:00000000007A1346 add rsp, 20h
PAGE:00000000007A134A pop rbp
PAGE:00000000007A134B retn
PAGE:00000000007A134B sub_7A1320 endp
PAGE:00000000007A134B
PAGE:00000000007A134B ; ---------------------------------------------------------------------------
PAGE:00000000007A134C db 90h
Собственно, сам скрипт:
import idaapi
import idautils
import idc
class ExceptionDetector():
def __init__( self ):
self.imageBase = self.GetModuleBase()
def GetModuleBase( self ):
defaultHeaderSize = 0x1000
for segEa in Segments():
imageBase = segEa - defaultHeaderSize
return imageBase
def GetExceptionSectionAddress( self ):
for ea in Segments():
if SegName( ea ) == ".pdata":
return ea
return 0
def GetHandlerAddressFromUnwindData( self, unwindDataAddress ):
versionFlags = Byte( unwindDataAddress )
countOfCodes = Byte( unwindDataAddress + 2 )
# only even count allowed
if countOfCodes % 2:
countOfCodes = countOfCodes + 1
# have flag UNW_FLAG_EHANDLER?
if versionFlags & 8:
handlerAddress = unwindDataAddress + 4 + countOfCodes * 2
return handlerAddress
return 0
def GetScopeHandlers( self, scopeTableAddress, unwindDataAddress ):
scopeHandlers = []
sizeOfScopeRecord = 16
scopeTableEntriesCount = Dword( scopeTableAddress )
# FIXME: how many scope entries is possible?
if scopeTableEntriesCount > 10:
return scopeHandlers
# read scope table and fill list with HandlerAddress values
for i in range( 0, scopeTableEntriesCount ):
scopeRecord = struct.unpack( "4I", idaapi.get_many_bytes( scopeTableAddress, sizeOfScopeRecord ) )
scopeHandler = scopeRecord[3]
if scopeHandler != 1:
scopeHandlers.append( self.imageBase + scopeHandler )
scopeTableAddress = scopeTableAddress + sizeOfScopeRecord
return scopeHandlers
# WARN: FindFuncEnd failed at times, so desirable run script twice for better results ( on second time IDA analyser works much better )
def MakeFunctionForHandler( self, scopeHandler ):
MakeUnkn( scopeHandler, DOUNK_SIMPLE )
MakeCode( scopeHandler )
functionStart = scopeHandler
functionEnd = FindFuncEnd( functionStart )
if functionEnd == BADADDR:
functionEnd = functionStart + 1
MakeFunction( functionStart, functionEnd )
def CreateFunctionsForHandlers( self ):
sizeOfRuntimeFunction = 12
ea = self.GetExceptionSectionAddress()
if ea == 0:
print "CreateFunctionsForHandlers: can't find exception section"
return
else:
print "-------------------------------------------"
print "exception section finded at address %x" % ea
print "-------------------------------------------"
while True:
runtimeFunction = struct.unpack( "3I", idaapi.get_many_bytes( ea, sizeOfRuntimeFunction ) )
unwindDataRel = runtimeFunction[2]
# end of exception section - zero padding bytes
if unwindDataRel == 0:
break
handlerAddress = self.GetHandlerAddressFromUnwindData( self.imageBase + unwindDataRel )
if handlerAddress:
handler = Dword( handlerAddress )
scopeTableAddress = handlerAddress + 4
scopeHandlers = self.GetScopeHandlers( scopeTableAddress, self.imageBase + unwindDataRel )
scopeHandlersLength = len( scopeHandlers )
if scopeHandlersLength > 0:
print "RuntimeFunction address = %x UnwindData = %x, ExceptionHandler = %x" % ( ea, self.imageBase + unwindDataRel, self.imageBase + handler )
for i in range( 0, scopeHandlersLength ):
self.MakeFunctionForHandler( scopeHandlers[i] )
print "Scope Handler = %x" % ( scopeHandlers[i] )
print "-----------------------------------------"
ea = ea + sizeOfRuntimeFunction
#
# entry point
#
print "Script Start...\n"
detector = ExceptionDetector()
detector.CreateFunctionsForHandlers()
print "\nScript End..."
p.s. Из-за особенности анализатора IDA, скрипт лучше всего запускать дважды.
После первого запуска IDA похоже обновляет свои внутренние данные и на второй запуск определяется практически все хендлеры.
Комментариев нет:
Отправить комментарий