Buuctf-Reverse(逆向) [RoarCTF2019]Polyre && SangFor(深育杯)-Reverse(逆向) XOR_Exercise Write up(代码片
Posted 水番正文
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Buuctf-Reverse(逆向) [RoarCTF2019]Polyre && SangFor(深育杯)-Reverse(逆向) XOR_Exercise Write up(代码片相关的知识,希望对你有一定的参考价值。
前言:看完这篇需要些耐心,可能会遇到很多报错什么,多查就能解决,题目只是个形式,每题带给我们的是其中的知识点,题目做不出来没关系,学习到平坦化控制流或是能写idapython都是自己的收获。
文章参考:
[RoarCTF2019]polyre_m0_46296905的博客-CSDN博客
[RoarCTF2019]Polyre | bypass ollvm - 暖暖草果 - 博客园
https://www.cnblogs.com/Mayflynymph/p/11718845.html#3.%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90
0x00 日常查壳
0x01 控制流平坦化
也是第一次碰到这种题 学习了一波
首先是装去平坦化的文章,例如
https://la13x.github.io/2021/04/18/angrinstall/#%E5%9C%A8virtualenv%E4%B8%AD%E5%AE%89%E8%A3%85angr
然后是学习平坦化这种技术的文章,例如
利用符号执行去除控制流平坦化 - 博客 - 腾讯安全应急响应中心
现在就可以开始做题了 我直接拿原题Polyre和今年深育杯拿这题改编的题目
两题差不多,碰这两题的时间也差不多
于是可以去github上获取下deflat.py脚本
GitHub - cq674350529/deflat: use angr to deobfuscation
workon env1
#进入虚拟环境
python deflat.py -f /home/p0z/Desktop/xor_exercise --addr 0x401170
#python delat.py -f 文件名 -addr main函数开始地址
0x02 使用IDApython去剩下的混淆
官方给出的IDApython
def patch_nop(start,end):
for i in range(start,end):
PatchByte(i, 0x90)
def next_instr(addr):
return addr+ItemSize(addr)
st = 0x0000000000401117
end = 0x0000000000402144
addr = st
while(addr<end):
next = next_instr(addr)
if "ds:dword_603054" in GetDisasm(addr):
while(True):
addr = next
next = next_instr(addr)
if "jnz" in GetDisasm(addr):
dest = GetOperandValue(addr, 0)
PatchByte(addr, 0xe9)
PatchByte(addr+5, 0x90)
offset = dest - (addr + 5)
PatchDword(addr + 1, offset)
print("patch bcf: 0x%x"%addr)
addr = next
break
else:
addr = next
两种方式都可以
于是我的IDA一直报错,我的IDA版本是7.5,应该是API更新的关系
于是查阅官方文档,修改即可
Porting from IDAPython 6.x-7.3, to 7.4
st = 0x0000000000401117
end = 0x0000000000402144
def patch_nop(start,end):
for i in range(start,end):
ida_bytes.patch_byte(i, 0x90) #说实话我没看出来哪里修改的 加不加这个模块都能去剩下的混淆
def next_instr(addr):
return addr + idc.get_item_size(addr) #get_item_size 获取指令或数据长度,这个函数的作用就是去往下一条指令
addr = st
while(addr < end):
next = next_instr(addr)
if "ds:dword_603054" in idc.GetDisasm(addr): #idc.GetDisasm(addr)得到addr的反汇编语句
while(True):
addr = next
next = next_instr(addr)
if "jnz" in idc.GetDisasm(addr):
dest = idc.get_operand_value(addr, 0) #得到操作数,即指令后的数 例如 jz 偏移地址 于是get_oprand_value获得偏移地址
ida_bytes.patch_byte(addr, 0xe9) #改addr地址的机器码为jmp
ida_bytes.patch_byte(addr + 5, 0x90) #addr+5的位置改成nop
offset = dest - (addr + 5) #调整为正确的偏移地址 也就是相对偏移地址 - 当前指令后的地址
ida_bytes.patch_dword(addr + 1, offset) #把偏移地址放到 jmp xxxx nop
print("patch bcf: 0x%x"%addr)
addr = next
break
else:
addr = next
0x03 正式分析
CRC文章:
0x04 GetFlag
部分人可能对 & 1出现疑惑
还有一个 | 0x8000000000000000
现在我逐步解答
一:& 1原因
这就是CRC一个特征 再来一遍
当我们是正数 左移一位
当我们是负数 左移一位 异或一个数
1. 当数是正数 左移一位 我们的结果数必为偶数
2. 当数是负数 左移一位 必为偶数
异或一个数0xB0004B7679FA26B3 这个数的二进制最后一位是1 于是我们左移的数最后一位是0
一个数的最后一位是1 不同为1
我们赋值异或完的值就是奇数
二:| 0x8000000000000000的原因
1. 只有当我们是负数时 当我们右移回去 最高位补0
2. 但他原来是负数所以要 | 0x8000000000000000
3. 让数回到负数
#include <stdio.h>
int main(void)
unsigned char enflag[] =
0x96, 0x62, 0x53, 0x43, 0x6D, 0xF2, 0x8F, 0xBC, 0x16, 0xEE,
0x30, 0x05, 0x78, 0x00, 0x01, 0x52, 0xEC, 0x08, 0x5F, 0x93,
0xEA, 0xB5, 0xC0, 0x4D, 0x50, 0xF4, 0x53, 0xD8, 0xAF, 0x90,
0x2B, 0x34, 0x81, 0x36, 0x2C, 0xAA, 0xBC, 0x0E, 0x25, 0x8B,
0xE4, 0x8A, 0xC6, 0xA2, 0x81, 0x9F, 0x75, 0x55
;
int i, j;
for ( i = 0; i < 6; i++ )
__int64 t = *((__int64 *)&enflag[i * 8]);
// printf("%p\\n", t);
for ( j = 0; j < 64; j++ ) //循环64次这样的操作
if ( t & 1 ) //负数时
t = ((unsigned __int64)t ^ 0xB0004B7679FA26B3) / 2;
t |= 0x8000000000000000;
else //正数时
t = (unsigned __int64)t / 2;
for ( j = 0; j < 8; j++ )
printf("%c", (char)t & 0xFF);
t >>= 8;
return 0;
GetFlag!
以上是关于Buuctf-Reverse(逆向) [RoarCTF2019]Polyre && SangFor(深育杯)-Reverse(逆向) XOR_Exercise Write up(代码片的主要内容,如果未能解决你的问题,请参考以下文章
Buuctf-Reverse(逆向) Crackme Write up
Buuctf-Reverse(逆向) [RoarCTF2019]Polyre && SangFor(深育杯)-Reverse(逆向) XOR_Exercise Write up(代码片
Buuctf-Reverse(逆向) Zer0pts2020-easy strcmp Write up