SEH问题处理
前言
感谢leyi提供的南区美照
经过了激烈的期末月斗争,我的电分还是挂了(目前还不清楚有没有补考)。
然后从暑假开始复健逆向。
第一天我就写了两个题目,一个是hex的,开010拉到底部就结束战斗了,另外一个就是今天讲的SEH。
你需要知道的
- SEH是什么
SEH(Structured Exception Handling,结构化异常处理)是 Windows 操作系统提供的一种底层异常处理机制。它与 C++ 的try/catch不同,SEH是操作系统层面的机制,而try/catch等 C++ 异常处理在 Windows 上通常也是基于SEH实现的。
异常发生后,大概顺序是:内核->调试器->VEH->SEH
在线程初始化的时候,会自动在栈中安装一个SEH结构体,作为默认异常处理,他的next就是0xFFFFFFFFF,而这个异常程序大家应该都很熟悉,就是windows程序崩溃时那个弹窗,打印出来出错函数地址。
如果程序中使用了try、excpt、assert来处理异常信息,那么编译器就会在栈中压入一个SEH结构体,同时插入链表中。
当出现异常的时候,操作系统会先中断程序,然后从TIB中取出第一个SEH结构体(也就是最近的SEH结构),使用其中的handler处理这个异常。
如果这个异常处理函数处理不了这个异常,那么就顺着next往上找别的异常处理函数,直到找到一个可以处理这个异常的函数或者到底部,也就是弹出错误窗口然后杀死线程。
通常处理完异常后,需要执行展开(Unwind)操作,该操作先通知目标结点前的各异常处理函数释放资源,然后将之前的SEH链全部删除。该操作通常由各高级语言Rtl模块来完成,Win32汇编操作时既可以不展开,也可以手工展开,还可以使用RtlUnwind函数展开。
题目内容
西电的强壮逆向人 - 西电 CTF 终端这个题
查壳发现没有什么问题,然后就可以拖进IDA里面看代码了
乍一看,并没有什么难的,然后就去打开byte_140005010、dword_140005000、unk_140005110、sub_140001020看一下内容。然后我就直接去搓解密脚本了。
剩下部分数据处理好就可以输出解密脚本了。
但能解出来吗,很显然不行
那问题出在哪呢
puts("Can you read my assembly in exception?");
以及题目提示
云之君在track神的电脑里找到了他珍藏的flag,于是急匆匆拿出了文件想要逆出flag。 可是按照IDA反编译的伪C代码解密出来的全是乱码,这是怎么回事呢? 幸好万能的track神告诉云之君,IDA也会骗人,这个伪C代码是假的,想要拿到flag只能分析汇编代码! 悲剧的是,云之君是一个只会F5的家伙,一点汇编也看不懂呜呜呜,这可怎么办呢
然后就需要我们去看汇编
可以直接看到有SEH的字样,拉到v5 = (127 * v5 + 102) % 255;这句话的汇编代码时,发现里面大有文章。
这里快速复习一下汇编代码
imul |
有符号整数乘法 |
|---|---|
add |
加法 |
cdq |
将 eax 符号扩展为 edx:eax(用于除法) |
idiv |
有符号整数除法(商在 eax,余数在 edx) |
sar |
算术右移(带符号位填充,相当于除以 2 的幂) |
xor |
按位异或 |
mov |
数据传输 |
jmp |
无条件跳转 |
1 | mov eax, [rsp+0D8h+var_B4] ; eax = var_B4 (更新后的值) |
这个时候,就可以试着先把v5(int v5; // [rsp+24h] [rbp-B4h])的值带进去变换了。
然后就会发现一个问题,右移7位会导致出现 1 / 0 的情况,这显然是会报错的。
这个时候就需要SEH出动了,也就是_except部分
1 | loc_1400011E9: |
然后我就发现问题了,dword_140005000是会随着v5的更新而更新的。
理解这个之后,解密就好写了。由于XOR的对称性,其实跟我第一轮的密码相比并没有什么太大的区别,只需要加上校验判断以及如果v5右移后的处理(也就是更新dword_140005000)即可。
我的解密脚本
1 |
|
也就是说,不要过于依赖F5出来的东西了,去跟踪汇编代码看看哪里隐藏起来了。







