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文章:

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

Buuctf-Reverse FlareOn3-Challenge Write up

Android逆向系列文章— Android基础逆向

Android逆向-Android基础逆向(2-2)