dfhack/reversing/ms_ehseh.idc

803 lines
20 KiB
C

//Microsoft Visual C++ Win32 SEH/C++ EH info parser
//Version 3.0 2006.03.02 Igor Skochinsky <skochinsky@mail.ru>
#include <idc.idc>
#define __INCLUDED
#include <ms_rtti.idc>
static getEHRec()
{
auto id;
id = GetStrucIdByName("EHRegistrationNode");
if (id==-1)
{
id = AddStruc(-1,"EHRegistrationNode");
ForceDWMember(id, 0, "pNext");
ForceDWMember(id, 4, "frameHandler");
ForceDWMember(id, 8, "state");
}
return id;
}
static getEHRecCatch()
{
auto id;
id = GetStrucIdByName("EHRegistrationNodeCatch");
if (id==-1)
{
id = AddStruc(-1,"EHRegistrationNodeCatch");
ForceDWMember(id, 0, "SavedESP");
ForceDWMember(id, 4, "pNext");
ForceDWMember(id, 8, "frameHandler");
ForceDWMember(id, 12, "state");
}
return id;
}
static CommentStackEH(start, hasESP, EHCookie, GSCookie)
{
if (hasESP)
CommentStack(start,-16, "__$EHRec$", getEHRecCatch());
else
CommentStack(start,-12, "__$EHRec$", getEHRec());
if (GSCookie)
CommentStack(start,-GSCookie, "__$GSCookie$",-1);
if (EHCookie)
CommentStack(start,-EHCookie, "__$EHCookie$",-1);
}
/* from frame.obj
typedef struct _s_FuncInfo {
unsigned int magicNumber;
int maxState;
const struct _s_UnwindMapEntry * pUnwindMap;
unsigned int nTryBlocks;
const struct _s_TryBlockMapEntry * pTryBlockMap;
unsigned int nIPMapEntries;
void * pIPtoStateMap;
const struct _s_ESTypeList * pESTypeList;
} FuncInfo;
*/
//handler:
// mov eax, offset funcInfo
// jmp ___CxxFrameHandler
static ParseCxxHandler(func, handler, fixFunc)
{
auto x, start, y, z, end, i, count, t, u, i2, cnt2, a, hasESP;
auto EHCookieOffset, GSCookieOffset;
start = func;
x = handler;
y = x;
z = x;
EHCookieOffset=0; GSCookieOffset=0;
if (matchBytes(x,"8B5424088D420C"))
// 8B 54 24 08 mov edx, [esp+8]
// 8D 42 0C lea eax, [edx+0Ch]
{
//EH cookie check:
// 8B 4A xx mov ecx, [edx-XXh]
// OR
// 8B 8A xx xx xx xx mov ecx, [edx-XXh]
// 33 C8 xor ecx, eax
// E8 xx xx xx xx call __security_check_cookie
x = x+7;
if (matchBytes(x,"8B4A??33C8E8"))
{
//byte argument
EHCookieOffset = (~Byte(x+2)+1)&0xFF;
EHCookieOffset = 12 + EHCookieOffset;
x = x+10;
}
else if (matchBytes(x,"8B8A????????33C8E8"))
{
//dword argument
EHCookieOffset = (~Dword(x+2)+1);
EHCookieOffset = 12 + EHCookieOffset;
x = x+13;
}
if (matchBytes(x,"8B4A??33C8E8"))
{
// 8B 4A xx mov ecx, [edx-XXh]
// 33 C8 xor ecx, eax
// E8 xx xx xx xx call __security_check_cookie
GSCookieOffset = (~Byte(x+2)+1)&0xFF;
GSCookieOffset = 12 + GSCookieOffset;
x = x+10;
}
else if (matchBytes(x,"8B8A????????33C8E8"))
{
//dword argument
GSCookieOffset = (~Dword(x+9)+1);
GSCookieOffset = 12 + GSCookieOffset;
x = x+13;
}
//Message("EH3: EH Cookie=%02X, GSCookie=%02X\n",EHCookieOffset, GSCookieOffset);
}
if (Byte(x)==0xB8) {
x = Dword(x+1);
}
else {
Message("\"mov eax, offset FuncInfo\" not found at offset %08X!\n",x);
return;
}
if (Dword(x)-0x19930520>0xF) {
Message("Magic is not 1993052Xh!\n");
return;
}
Message(form("Detected function start at %08X\n",start));
u = x; //FuncInfo;
//parse unwind handlers
count = Dword(u+4); //maxState
i=0;
x = Dword(u+8); //pUnwindMap
while (i<count) {
t = Dword(x+4); //unwind action address
if (t<MAXADDR && t>y) y=t; //find lowest
if (t!=0 && t<z) z=t; //find highest
x = x+8;
i = i+1;
}
if (y==0) {
Message("All pointers are NULL!\n");
return;
}
if (z>y)
{
Message("Something's very wrong!\n");
return;
}
end = FindFuncEnd(y);
if (end==BADADDR) {
if (fixFunc) MakeUnkn(y, 1);
if (BADADDR == FindFuncEnd(y))
{
Message(form("Can't find function end at 0x%08X\n",y));
return;
}
}
Message(form("Handlers block: %08X-%08X\n", z, y));
if (GetFunctionFlags(start) == -1)
{
if (fixFunc)
{
MakeUnkn(start, 1);
MakeCode(start);
MakeFunction(start, BADADDR);
}
else
{
Message("There is no function defined at 0x%08X!\n", start);
return;
}
}
a = FindFuncEnd(start);
Message("Function end: %08X\n", a);
if (fixFunc) AnalyseArea(start,a);
if (1)//(z>a) && ((z-a)>0x20))
{
//the handlers block is too far from the function end, make it a separate chunk
if (fixFunc)
{
Message("Making separate handlers block\n");
Unknown(z, y-z);
MakeCode(z);
MakeFunction(z,y);
AnalyseArea(z,y);
MakeCode(y);
MakeFunction(y,BADADDR);
}
SetFunctionFlags(z, GetFunctionFlags(start) | FUNC_FRAME);
SetFunctionCmt(z, form("Unwind handlers of %08X", start), 0);
}
else if (fixFunc)
{
Message("Merging handlers block with main function.\n");
Unknown(start, y-start);
MakeCode(start);
MakeFunction(start,y);
AnalyseArea(start,y);
}
/*
typedef const struct _s_TryBlockMapEntry {
int tryLow; //00
int tryHigh; //04
int catchHigh; //08
int nCatches; //0C
const struct _s_HandlerType * pHandlerArray; //10
} TryBlockMapEntry;
typedef const struct _s_HandlerType {
unsigned int adjectives; //00
struct TypeDescriptor * pType; //04
int dispCatchObj; //08
void * addressOfHandler; //0C
}
*/
//parse catch blocks
y = 0;
z = 0x7FFFFFFF;
i=0;
count = Dword(u+12); //nTryBlocks
x = Dword(u+16); //pTryBlocksMap
Message("%d try blocks\n",count);
while (i<count) {
cnt2 = Dword(x+12); //nCatches
a = Dword(x+16); //pHandlerArray
i2 = 0;
Message(" %d catches\n",cnt2);
while (i2<cnt2)
{
t = Dword(a+12);
//Message(" t=0x%08.8X\n",t);
if (t!=BADADDR && t>y)
y=t; //find lowest
if (z>t)
z=t; //find highest
a = a+16;
i2 = i2+1;
}
x = x+20;
i = i+1;
}
hasESP = 0;
if (count>0)
{
hasESP = 1;
//Message("y=0x%08.8X, z=0x%08.8X\n",y,z);
end = FindFuncEnd(y);
if (end==BADADDR) {
if (fixFunc)
{
MakeUnkn(y, 1);
MakeCode(y);
}
if (BADADDR == FindFuncEnd(y))
{
Message(form("Can't find function end at 0x%08X\n",y));
return;
}
}
Message(form("Catch blocks: %08X-%08X\n", z, end));
y = FindFuncEnd(start);
if (y ==-1 || end > y)
{
if (fixFunc)
{
Message("Merging catch blocks with main function.\n");
Unknown(start, end-start);
MakeCode(start);
MakeFunction(start,end);
AnalyseArea(start,end);
}
else
Message("Catch blocks are not inside the function!\n");
}
}
//comment unwind handlers
i=0;
count = Dword(u+4); //maxState
x = Dword(u+8); //pUnwindMap
while (i<count) {
t = Dword(x+4); //unwind action address
if (t!=0)
MakeComm(t, form("state %d -> %d",i, Dword(x)));
x = x+8;
i = i+1;
}
Parse_FuncInfo(u, 0);
CommentStackEH(func, hasESP, EHCookieOffset, GSCookieOffset);
}
static fixCxx(s, doSEH, fixFunc) {
auto x, start;
start = s;
if ((Word(start) != 0xA164) || (Dword(start+2)!=0)) {
Message("Should start with \"move eax, large fs:0\"!\n");
return;
}
if ( !doSEH && (Byte(start-10) == 0x55) && (Dword(start-9) == 0xFF6AEC8B))
{
//(ebp frame)
//00: 55 push ebp
//01: 8B EC mov ebp, esp
//03: 6A FF push 0FFFFFFFFh
//05: 68 xx xx xx xx push loc_xxxxxxxx
//0A: 64 A1 00 00 00 00 mov eax, large fs:0
//10: 50 push eax
//11: 64 89 25 00 00 00 00 mov large fs:0, esp
start = start - 10;
x = Dword(start+6);
//Message("Match 1\n");
}
else if (!doSEH && (Word(start+9) == 0xFF6A) && (Byte(start+11)==0x68))
{
//00: 64 A1 00 00 00 00 mov eax, large fs:0
//06: xx xx xx
//09: 6A FF push 0FFFFFFFFh
//0B: 68 xx xx xx xx push loc_xxxxxxxx
//10: 50 push eax
//
x = Dword(start+12);
//Message("Match 2\n");
}
else if (!doSEH && (Word(start-7) == 0xFF6A) && (Byte(start-5)==0x68))
{
//-7: 6A FF push 0FFFFFFFFh
//-5: 68 xx xx xx xx push loc_xxxxxxxx
//00: 64 A1 00 00 00 00 mov eax, large fs:0
//06: 50 push eax
//07: 64 89 25 00 00 00 00 mov large fs:0, esp
//
x = Dword(start-4);
start = start-7;
//Message("Match 3\n");
}
else if (!doSEH && (Word(start+6) == 0xFF6A) && (Byte(start+8)==0x68))
{
//00: 64 A1 00 00 00 00 mov eax, large fs:0
//06: 6A FF push 0FFFFFFFFh
//08: 68 xx xx xx xx push loc_xxxxxxxx
//0D: 50 push eax
//0E: 64 89 25 00 00 00 00 mov large fs:0, esp
x = Dword(start+9);
//Message("Match 4\n");
}
else if (doSEH && (Byte(start-5)==0x68) && (Byte(start-10)==0x68) && (Dword(start-15)==0x6AEC8B55))
{
//-15: 55 push ebp
//-14: 8B EC mov ebp, esp
//-12: 6A F? push 0FFFFFFF?h
//-10: 68 xx xx xx xx push offset __sehtable$_func1
//-5 : 68 xx xx xx xx push offset _except_handlerx
//00 : 64 A1 00 00 00 00 mov eax, large fs:0
x = Dword(start-9);
//Message("Match 5\n");
if (Byte(start-11) == 0xFF) //-1 = SEH3
fixSEHFunc(start-15,x, 3, fixFunc);
else if (Byte(start-11) == 0xFE) //-2 = SEH4
fixSEHFunc(start-15,x, 4, fixFunc);
else
Message("Unknown SEH handler!\n");
return;
}
else {
//probably a custom handler
//Message("\"push 0FFFFFFFFh; push offset loc\" not found!\n");
return;
}
Message(form("Fixing function at 0x%08X\n",start));
ParseCxxHandler(start, x, fixFunc);
}
static doEHProlog(name,fixFunc)
{
auto i,s,a;
a=LocByName(name);
if (a==BADADDR)
return;
Message("%s = %08X\n",name,a);
i=RfirstB(a);
while(i!=BADADDR)
{
Message("- %08X - ",i);
// -5: mov eax, offset loc_XXXXXX
// 0: call __EH_prolog
if (Byte(i-5)==0xB8)
ParseCxxHandler(i-5, Dword(i-4),fixFunc);
else
{
Message(form("No mov eax, offset loc_XXXXXX at %08X!!!\n",i-5));
return;
}
if (SetFunctionFlags(i,GetFunctionFlags(i) | FUNC_FRAME))
{
MakeFrame(i,GetFrameLvarSize(i), 4, GetFrameArgsSize(i));
if (fixFunc) AnalyseArea(i, FindFuncEnd(i)+1);
Message("OK\n");
}
else
Message("Error\n");
i=RnextB(a,i);
}
}
static doEHPrologs(name, fixFunc)
{
doEHProlog("j"+name,fixFunc);
doEHProlog("j_"+name,fixFunc);
doEHProlog(name,fixFunc);
doEHProlog("_"+name,fixFunc);
}
static fixEHPrologs(fixFunc)
{
doEHPrologs("_EH_prolog",fixFunc);
doEHPrologs("_EH_prolog3",fixFunc);
doEHPrologs("_EH_prolog3_catch",fixFunc);
doEHPrologs("_EH_prolog3_GS",fixFunc);
doEHPrologs("_EH_prolog3_catch_GS",fixFunc);
}
static isInCodeSeg(a)
{
if (SegName(a)==".text")
return 1;
else
return 0;
}
//check a scopetable entry
static checkEntry(a,i,ver)
{
auto x;
x = Dword(a);
//EnclosingLevel should be negative or less than i
if (x&0x80000000)
{
if (ver==3 && x!=0xFFFFFFFF)
return 0;
if (ver==4 && x!=0xFFFFFFFE)
return 0;
}
else if (x>=i)
return 0;
x = Dword(a+4);
if ((x!=0) && !isInCodeSeg(x)) //filter should be zero or point to the code
return 0;
x = Dword(a+8);
if (!isInCodeSeg(x)) //handler should point to the code
return 0;
//check if there are xref to fields (i.e. after the end of the scopetable)
if (((ver!=3)||(i>0)) && isRef(GetFlags(a)))
return 0;
if (isRef(GetFlags(a+4)) || isRef(GetFlags(a+8)))
return 0;
return 1;
}
//check if there's a valid scopetable and calculate number of entries in it
static checkScopeTable(a, ver)
{
auto i,k;
if (ver==4)
{
k = Dword(a);
if ((k&0x80000000)==0) //first field should be negative
return 0;
if ((k!=0xFFFFFFFE) && (k&3)!=0) //GS cookie offset should be -2 or dword-aligned
return 0;
k = Dword(a+8);
if ((k&0x80000000)==0) //offset should be negative
return 0;
if ((k&3)!=0) //EH cookie offset should be dword-aligned
return 0;
a = a+16; //move to the scope entries list
}
i = 0;
while (checkEntry(a,i,ver))
{
i = i+1;
a = a+12;
}
return i;
}
/*
struct _EH4_EXCEPTION_REGISTRATION_RECORD {
void* SavedESP;
_EXCEPTION_POINTERS* ExceptionPointers;
_EXCEPTION_REGISTRATION_RECORD* Next;
enum _EXCEPTION_DISPOSITION (*Handler)(_EXCEPTION_RECORD*, void*, _CONTEXT*, void*);
DWORD EncodedScopeTable;
unsigned long TryLevel;
};
*/
static getSEHRec()
{
auto id;
id = GetStrucIdByName("SEHRegistrationNode");
if (id==-1)
{
id = AddStruc(-1,"SEHRegistrationNode");
ForceDWMember(id, 0, "SavedESP");
ForceDWMember(id, 4, "ExceptionPointers");
ForceDWMember(id, 8, "Next");
ForceDWMember(id, 12, "Handler");
ForceDWMember(id, 16, "EncodedScopeTable");
ForceDWMember(id, 20, "TryLevel");
}
return id;
}
static CommentStackSEH(start, scopetable)
{
auto x;
CommentStack(start,-24, "__$SEHRec$", getSEHRec());
if (scopetable)
{
x = Dword(scopetable);
if (x!=-2)
CommentStack(start,x, "__$GSCookie$", -1);
x = Dword(scopetable+8);
CommentStack(start,x, "__$EHCookie$", -1);
}
}
static fixSEHFunc(func, scopetable, ver, fixFunc)
{
auto k,i,t,u,x,y,z,hasESP,end;
k = checkScopeTable(scopetable, ver);
if (k==0)
{
Message("Bad scopetable\n");
return;
}
Message("function: %08X, scopetable: %08X (%d entries)\n", func, scopetable, k);
x = scopetable;
if (ver==4) x = x+16;
//parse the scopetable!
y = 0;
z = 0x7FFFFFFF;
i = 0;
hasESP = 0;
while (i<k) {
t = Dword(x+4);
if (t) {
hasESP=1;
if (t>y) y=t; //find lowest
if (z>t) z=t; //find highest
//Message("t=0x%08.8X\n",t);
//check the code just before, it could be jump to the end of try
if (Byte(t-2)==0xEB)
t = getRelJmpTarget(t-2);
else if (Byte(t-5)==0xE9)
t = getRelJmpTarget(t-5);
//Message("t=0x%08.8X\n",t);
if (t>y) y=t; //find lowest
if (z>t) z=t; //find highest
}
t = Dword(x+8);
//check the code just before, it could be jump to the end of try
if (t>y) y=t; //find lowest
if (z>t) z=t; //find highest
//Message("t=0x%08.8X\n",t);
if (Byte(t-2)==0xEB)
t = getRelJmpTarget(t-2);
else if (Byte(t-5)==0xE9)
t = getRelJmpTarget(t-5);
//Message("t=0x%08.8X\n",t);
if (t>y) y=t; //find lowest
if (z>t) z=t; //find highest
x = x+12;
i = i+1;
}
//Message("y=0x%08.8X, z=0x%08.8X\n",y,z);
if (1)
{
end = FindFuncEnd(y);
if (end==BADADDR) {
if (fixFunc)
{
MakeUnkn(y, 1);
MakeCode(y);
}
if (BADADDR == FindFuncEnd(y))
{
Message(form("Can't find function end at 0x%08X\n",y));
return;
}
}
//Message(form("Except blocks: %08X-%08X\n", z, end));
z = FindFuncEnd(func);
if (z ==-1 || end > z && fixFunc)
{
//Message("Merging except blocks with main function.\n");
Unknown(func, end-func);
MakeCode(func);
MakeFunction(func,end);
AnalyseArea(func,end);
}
}
//walk once more and fix finally entries
x = scopetable;
if (ver==4) x = x+16;
i = 0;
while (fixFunc && i<k) {
if (Dword(x+4)==0 && Dword(x+8)==y)
{
//the last handler is a finally handler
//check that it ends with a ret, call or jmp
z = FindFuncEnd(y);
if (z!=BADADDR &&
!(Byte(z-1)==0xC3 || Byte(z-5)==0xE9 || Byte(z-5)==0xE8 ||
Byte(z-2)==0xEB || Byte(z-1)==0xCC || Word(z-6)==0x15FF) )
{
//we need to add the following funclet to our function
end = FindFuncEnd(z);
if (end!=BADADDR)
{
Unknown(z, end-z);
MakeCode(z);
SetFunctionEnd(func,end);
}
}
}
x = x+12;
i = i+1;
}
//comment the table and handlers
x = scopetable;
ExtLinA(x,0,form("; SEH scopetable for %08X",func));
if (ver==4)
{
OffCmt(x,"GSCookieOffset");
OffCmt(x+4,"GSCookieXOROffset");
OffCmt(x+8,"EHCookieOffset");
OffCmt(x+12,"EHCookieXOROffset");
x = x+16;
CommentStackSEH(func,scopetable);
}
else
CommentStackSEH(func,0);
i = 0;
while (i<k) {
ForceDword(x);
SoftOff(x+4);
SoftOff(x+8);
MakeComm(x, form("try block %d, enclosed by %d",i, Dword(x)));
t = Dword(x+4); //exception filter
if (t!=0)
ExtLinA(t,0,form("; __except() filter for try block %d",i));
u = Dword(x+8);
if (t!=0)
ExtLinA(u,0,form("; __except {} handler for try block %d",i));
else
ExtLinA(u,0,form("; __finally {} handler for try block %d",i));
x = x+12;
i = i+1;
}
}
static doSEHProlog(name, ver, fixFunc)
{
auto i,s,locals,scopetable,k,l,func,a;
a=LocByName(name);
if (a==BADADDR)
return;
Message("%s = %08X\n",name,a);
i=RfirstB(a);
while(i!=BADADDR)
{
Message("- %08X - ",i);
// -10 68 xx xx xx xx push xx
// or
// -7 6A xx push xx
// -5 68 xx xx xx xx push OFFSET __sehtable$_func
// 0 e8 00 00 00 00 call __SEH_prolog
//
//
locals = -1; scopetable=0;
if (Byte(i-5)==0x68)
{
scopetable = Dword(i-4);
if (Byte(i-7)==0x6A)
{
func = i-7;
locals = Byte(func+1);
}
else if (Byte(i-10)==0x68)
{
func = i-10;
locals = Dword(func+1);
}
if (GetFunctionFlags(func)==-1 && fixFunc)
{
MakeUnkn(func, 1);
MakeCode(func);
MakeFunction(func, BADADDR);
}
if (SetFunctionFlags(func,GetFunctionFlags(func)|FUNC_FRAME))
{
MakeFrame(func, GetFrameLvarSize(func), 4, GetFrameArgsSize(func));
fixSEHFunc(func, scopetable, ver, fixFunc);
Message("OK\n");
}
else
Message("Error\n");
}
i=RnextB(a,i);
}
}
static doSEHPrologs(name, ver, fixFunc)
{
doSEHProlog("j"+name, ver, fixFunc);
doSEHProlog("j_"+name, ver, fixFunc);
doSEHProlog(name, ver, fixFunc);
doSEHProlog("_"+name, ver, fixFunc);
}
static fixSEHPrologs(fixFunc)
{
doSEHPrologs("_SEH_prolog",3, fixFunc);
doSEHPrologs("__SEH_prolog",3, fixFunc);
doSEHPrologs("_SEH_prolog4",4, fixFunc);
doSEHPrologs("__SEH_prolog4",4, fixFunc);
doSEHPrologs("_SEH_prolog4_GS",4, fixFunc);
doSEHPrologs("__SEH_prolog4_GS",4, fixFunc);
}
static findFunc(name)
{
auto a;
a = LocByName("j_"+name);
if (a==BADADDR)
a = LocByName(name);
return a;
}
static doSEH(fixFunc)
{
auto start, a;
start = 0;
while (1) {
//mov eax, large fs:0
start = FindBinary(start+1, 3, "64 A1 00 00 00 00");
if (start==BADADDR)
break;
fixCxx(start,1,fixFunc);
}
fixSEHPrologs(fixFunc);
}
static doEH(fixFunc)
{
auto start, a;
start = 0;
while (1) {
//mov eax, large fs:0
start = FindBinary(start+1, 3, "64 A1 00 00 00 00");
if (start==BADADDR)
break;
fixCxx(start,0,fixFunc);
}
fixEHPrologs(fixFunc);
}
static main(void)
{
auto seh, fixseh, eh, fixeh;
seh = AskYN(1, "Do you wish to parse all Win32 SEH handlers?");
if (seh==-1) return;
if (seh) {
fixseh = AskYN(1, "Do you wish to fix function boundaries as needed?");
if (fixseh==-1) return;
}
eh = AskYN(1, "Do you wish to parse all C++ EH handlers?");
if (eh==-1) return;
if (eh) {
fixeh = AskYN(1, "Do you wish to fix function boundaries as needed?");
if (fixeh==-1) return;
}
if (seh) doSEH(fixseh);
if (eh) doEH(fixeh);
//fixCxx(ScreenEA());
}