本文中的例子下载地址
https://wwmf.lanzout.com/ij4zq18au9yd
密码:2vts
确定try的位置
首先确定try的位置
上面明显是一个SEH结构,在c++异常中,state固定在var_4的位置上,这里state初始化位-1,我们将var_4改名为state
上图为ida
的反编译图,当state赋值为0时,为try的开始,state赋值为-1时表示try块的结束
还原case1 - 4
下面我们分析每一个抛出的异常
_CxxThrowException的第一个参数为抛出异常的地址,那么这里的异常应该时一个常量3,我们可以跳转到第二个参数ThrowInfo
ThrowInfo的数据结构
上面是ThrowInfo的数据结构
可以看到这个异常时一个简单类型异常,并且为int
case 1 - 4 都是差不多的,它们可还原为下面的代码
case 1:
throw 3;
case 2:
throw 3.0f;
case 3:
throw '3';
case 4:
throw 3.0;
还原case 5 - 6
下面看case 5,v7应该保存了这个异常对象的地址,sub_401310是该对象的构造函数,我们先分析这个类的类名,
在CatchTableTypeArray中,前面的类是抛出的类(这里是CDiv0Excepction),后面的类是该类的基类(这里为CExceptionBase),所以这里抛出的类为CDiv0Excepction
现在代码可还原为
case 5:
throw CDiv0Excepction();
然后我们再分析CDiv0Excepction的构造函数
经过我们前面的分析,知道CDiv0Excepction有个基类CExceptionBase,所以sub_401360应为CExceptionBase的构造函数,现在将sub_401360重命名为CExceptionBase,看下图
这方面可异常关系不大,所以我简略过,下面查看CDiv0Excepction和CExceptionBase的虚表,最终可还原为
class CExceptionBase{
public:
virtual char * toString() = 0;
}
class CDiv0Excepction : public CExceptionBase{
public:
CDiv0Excepction(){
printf("CExcepctionDiv0()\r\n");
}
virtual char * toString(){
return "div zero excepction";
}
}
case 6 和 case5是差不多的 ,最终可还原为
class CAccessExcepction : public CExceptionBase{
public:
CAccessExcepction(){
printf("CAccessExcepction()\r\n");
}
virtual char * toString(){
return "access excepction";
}
}
case 6:
throw CAccessExcepction();
还原case 7
下面看case 7
看上面我们可以看到case 7抛出的是一个指针CAccessExcepction *
,(所有的对象指针都继承void *),所以下面可还原为
case 7:
throw new CAccessExcepction();
最终try块可还原为
class CExceptionBase{
public:
virtual char * toString() = 0;
}
class CDiv0Excepction : public CExceptionBase{
public:
CDiv0Excepction(){
printf("CExcepctionDiv0()\r\n");
}
virtual char * toString(){
return "div zero excepction";
}
}
class CAccessExcepction : public CExceptionBase{
public:
CAccessExcepction(){
printf("CAccessExcepction()\r\n");
}
virtual char * toString(){
return "access excepction";
}
}
int sub_401000(int n){
try{
switch(n){
case 1:
throw 3;
case 2:
throw 3.0f;
case 3:
throw '3';
case 4:
throw 3.0;
case 5:
throw CDiv0Excepction();
case 6:
throw CAccessExcepction();
case 7:
throw new CAccessExcepction();
}
catch...
}
还原前面4个catch
下面还原catch
我们可以看到再函数开头有明显的SEH增加节点的行为转到SEH_401000
看到这里是FuncInfo的地址,下面转到FuncInfo
FuncInfo的结构
上面为FuncInfo的结构
上面只有一个TryBlockMapEntry,代表这里只有一个try块,catch的数量为TryBlockMapEntry的第四个成员dwCatchCount,这里有7个catch块,在ida中msRttiDscr表示为HandlerType
msRttiDscr 有几个成员需要关注,pType表明了catch的类型,CatchProc表示catch后执行的函数,dispCatchObjOffset表示catch后异常对象在栈中的偏移(以EBP为基准线)
前面四个可还原为
catch(int a){
printf("catch int %d\r\n",a);
}
catch(float a){
printf("catch float %f\r\n",a);
}
catch(char a){
printf("catch char %c\r\n",a);
}
catch(double a){
printf("catch double %f\r\n",a);
}
还原剩下的catch异常
现在还原剩下的catch异常
大家看上面圈出的catch异常,可能会认为这个异常类型为CExceptionBase,但是注意看msRttiDscr 的第一个成员nFlag的值为8,这代表这是一个引用类型的异常,所以这个catch异常可还原为
catch(CExceptionBase& a){
printf("catch error %s\r\n",a.toString());
}
第6个catch异常可还原为
catch(CAccessExcepction* a){
printf("catch error %s\r\n",a->toString());
}
最后一个异常的类型为0,这个的意思是任何异常都匹配,这个catch异常可还原为
catch(...){
printf("catch ...\r\n");
}
还原程序的结尾
最后我们这个程序的结尾
我们找最后一个catch异常的catch函数(其实随便一个就可以),
我们可以看到在catch函数中,最后会返回一个地址,这个地址就是catch函数执行完,要跳转的那个地址,我们跟过去
看到上面将state设置为-1,代表try ... catch结束了,再接着跳转到loc_40122E
这就是这个函数的结尾,可还原为
printf("Test end!\r\n")
整个函数的还原
最后整理一下整个函数的还原情况
#include <iostream>
class CExceptionBase{
public:
virtual char * toString() = 0;
};
class CDiv0Excepction : public CExceptionBase{
public:
CDiv0Excepction(){
printf("CExcepctionDiv0()\r\n");
}
virtual char * toString(){
return "div zero excepction";
}
};
class CAccessExcepction : public CExceptionBase{
public:
CAccessExcepction(){
printf("CAccessExcepction()\r\n");
}
virtual char * toString(){
return "access excepction";
}
};
int sub_401000(int n){
try{
switch(n){
case 1:
throw 3;
case 2:
throw 3.0f;
case 3:
throw '3';
case 4:
throw 3.0;
case 5:
throw CDiv0Excepction();
case 6:
throw CAccessExcepction();
case 7:
throw new CAccessExcepction();
}
}
catch(int a){
printf("catch int %d\r\n",a);
}
catch(float a){
printf("catch float %f\r\n",a);
}
catch(char a){
printf("catch char %c\r\n",a);
}
catch(double a){
printf("catch double %f\r\n",a);
}
catch(CExceptionBase& a){
printf("catch error %s\r\n",a.toString());
}
catch(CAccessExcepction* a){
printf("catch error %s\r\n",a->toString());
}
catch(...){
printf("catch ...\r\n");
}
printf("Test end!\r\n");
}
int main(){
sub_401000(1);
}
标签:case,CAccessExcepction,CExceptionBase,32,c++,还原,printf,catch,throw
From: https://www.cnblogs.com/czlnb/p/17700753.html