前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Radare2和Frida结合实现破解

Radare2和Frida结合实现破解

原创
作者头像
无情剑客
修改2020-12-28 11:47:07
8360
修改2020-12-28 11:47:07
举报
文章被收录于专栏:Android逆向

黑客攻击-软件破解(2) 中通过Radare2的静态分析实现了破解。本文使用frida和radare2进行动态分析来对crackerMe系列中后面的例子进行破解。

crackme0x04

流程图

首先通过agf查看流程图。

代码语言:javascript
复制
[0x080484fb]> agf
[0x08048484]>  # sym.check (char *s);
                                                   ┌─────────────────────────────────────┐
                                                   │  0x8048484                          │
                                                   │   ; CALL XREF from main @ 0x8048559 │
                                                   │ 133: sym.check (char *s);           │
                                                   │ ; var char *var_dh @ ebp-0xd        │
                                                   │ ; var uint32_t var_ch @ ebp-0xc     │
                                                   │ ; var uint32_t var_8h @ ebp-0x8     │
                                                   │ ; var int32_t var_4h @ ebp-0x4      │
                                                   │ ; arg char *s @ ebp+0x8             │
                                                   │ ; var char *format @ esp+0x4        │
                                                   │ ; var int32_t var_sp_8h @ esp+0x8   │
                                                   │ push ebp                            │
                                                   │ mov ebp, esp                        │
                                                   │ sub esp, 0x28                       │
                                                   │ mov dword [var_8h], 0               │
                                                   │ mov dword [var_ch], 0               │
                                                   └─────────────────────────────────────┘
                                                       v
                                                       │
                                                       │
┌────────────────────────────────────────────────────────┐
│                                                      │ │
│                                                ┌────────────────────────────────────────┐
│                                                │  0x8048498                             │
│                                                │ ; CODE XREF from sym.check @ 0x80484f9 │
│                                                │ mov eax, dword [s]                     │
│                                                │ ; const char *s                        │
│                                                │ mov dword [esp], eax                   │
│                                                │ ; size_t strlen(const char *s)         │
│                                                │ call sym.imp.strlen;[oa]               │
│                                                │ cmp dword [var_ch], eax                │
│                                                │ jae 0x80484fb                          │
│                                                └────────────────────────────────────────┘
│                                                        f t
│                                                        │ │
│                                                        │ └───────────────────┐
│                ┌───────────────────────────────────────┘                     │
│                │                                                             │
│            ┌────────────────────────────────────────────────────────┐    ┌────────────────────────────────────────────┐
│            │  0x80484a8                                             │    │  0x80484fb                                 │
│            │ mov eax, dword [var_ch]                                │    │ ; const char *format                       │
│            │ add eax, dword [s]                                     │    │ ; CODE XREF from sym.check @ 0x80484a6     │
│            │ movzx eax, byte [eax]                                  │    │ ; [0x8048649:4]=0x73736150                 │
│            │ mov byte [var_dh], al                                  │    │ ; "Password Incorrect!\n"                  │
│            │ lea eax, [var_4h]                                      │    │ mov dword [esp], str.Password_Incorrect    │
│            │ ;   ...                                                │    │ ; int printf(const char *format)           │
│            │ mov dword [var_sp_8h], eax                             │    │ call sym.imp.printf;[oc]                   │
│            │ ; const char *format                                   │    │ leave                                      │
│            │ ; [0x8048638:4]=0x50006425                             │    │ ret                                        │
│            │ mov dword [format], 0x8048638                          │    └────────────────────────────────────────────┘
│            │ lea eax, [var_dh]                                      │
│            │ ; const char *s                                        │
│            │ mov dword [esp], eax                                   │
│            │ ; int sscanf(const char *s, const char *format,   ...) │
│            │ call sym.imp.sscanf;[ob]                               │
│            │ mov edx, dword [var_4h]                                │
│            │ lea eax, [var_8h]                                      │
│            │ add dword [eax], edx                                   │
│            │ cmp dword [var_8h], 0xf                                │
│            │ jne 0x80484f4                                          │
│            └────────────────────────────────────────────────────────┘
│                    f t
│                    │ │
│                    │ └───────────────────────┐
│    ┌───────────────┘                         │
│    │                                         │
│┌─────────────────────────────────────┐   ┌────────────────────────────────────────┐
││  0x80484dc                          │   │  0x80484f4                             │
││ ; const char *format                │   │ ; CODE XREF from sym.check @ 0x80484da │
││ ; [0x804863b:4]=0x73736150          │   │ lea eax, [var_ch]                      │
││ ; "Password OK!\n"                  │   │ inc dword [eax]                        │
││ mov dword [esp], str.Password_OK    │   │ jmp 0x8048498                          │
││ ; int printf(const char *format)    │   └────────────────────────────────────────┘
││ call sym.imp.printf;[oc]            │       v
││ ; int status                        │       │
││ mov dword [esp], 0                  │       │
││ ; void exit(int status)             │       │
││ call sym.imp.exit;[od]              │       │
│└─────────────────────────────────────┘       │
│                                              │
│                                              │
└──────────────────────────────────────────────┘

指令和函数

sscanf() 函数的声明。

代码语言:javascript
复制
int sscanf(const char *str, const char *format, ...)

作用是从字符串读取格式化输入。

cmp指令: 该指令与SUB指令一样执行减法的操作,但它并不保存运算结果,只是根据结果设置相关的条件标志位(SF、ZF、CF、OF)。CMP指令后往往跟着条件转移指令,实现根据比较的结果产生不同的程序分支的功能。 inc是增量指令。 inc指令: 该指令对操作数oprd加1(增量),它是一个单操作数指令。操作数可以是寄存器或存储器。由于增量指令主要用于对计数器和地址指针的调整,所以它不影响进位标志CF,对其他状态标志位的影响与add指令一样。

伪代码

通过pdg查看以下decompiler的代码

代码语言:javascript
复制
[0x080484fb]> pdg

// WARNING: [r2ghidra] Detected overlap for variable var_dh

void sym.check(char *s)
{
    uint32_t uVar1;
    char var_dh;
    uint32_t var_ch;
    uint32_t var_8h;
    int32_t var_4h;

    var_8h = 0;
    var_ch = 0;
    while( true ) {
        uVar1 = sym.imp.strlen(s);
        if (uVar1 <= var_ch) break;
        var_dh = s[var_ch];
        sym.imp.sscanf(&var_dh, 0x8048638, &var_4h);
        var_8h = var_8h + var_4h;
        if (var_8h == 0xf) {
            sym.imp.printf("Password OK!\n");
            sym.imp.exit(0);
        }
        var_ch = var_ch + 1;
    }
    sym.imp.printf("Password Incorrect!\n");
    return;
}

逻辑分析

通过流程图可知核心算法在中间部分,并且可以看出存在循环。

第一次循环:

1.把var_ch内存中的值赋值给eax mov eax, dword [var_ch]2.把arg_8h(check函数参数,也就是输入的字符串)中的值和eax相加 add eax, dword [arg_8h]3.将eax取byte扩充(相当于取低8位) movzx eax, byte [eax] mov byte [var_dh], al4.将var_4h赋值给eax,注意这里是lea指令lea eax, [var_4h]5.将eax的值赋给var_sp_8h指向的内存(为sscanf传递参数,从右向左入栈) mov dword [var_sp_8h], eax6.将0x8048638("%d")赋给var_sp_4h指向的内存 mov dword [var_sp_4h], 0x80486387.eax入栈(eax和var_dh的值是一样的) lea eax, [var_dh] mov dword [esp], eax8.调用用sscanf函数 call sym.imp.sscanf9.将var_4h指向的值赋给edx mov edx, dword [var_4h]10.将var_8h的值赋给eax lea eax, [var_8h]11.edx和eax指向的值相加 add dword [eax], edx12.比较var_8h内存中的值和0xf cmp dword [var_8h], 0xf13.如果相等,则出现成功的提示,如果不相等,则跳转到0x80484f4 cmp dword [var_8h], 0xf jne 0x80484f4 假设不相等,开始第二轮的循环。

通过上面的分析,可知check函数的功能:对字符串中的每个字符取整(类似atoi),然后对每个字符相加,和0xf进行比较,只有相等的情况,才能Pass.

破解

下面通过多种方式来破解这个程序。

输入正确的值

知道逻辑,很容易破解这个程序。输入12345(和是0xf),Pass.

frida修改逻辑

修改参数

修改参数为96(和是0xf),则无论输入什么都可以使程序Pass. 32位的函数参数传递方式在strcpy为何不安全 中有所介绍。

代码语言:javascript
复制
"use strict"

var targetModule = Process.findModuleByName("crackme0x04");
var symbols = targetModule.enumerateSymbols();
var targetFun = null;
for(var i = 0; i < symbols.length; i++) {
    targetFun =  ptr(symbols[i].address);
    if (symbols[i].name == "check" && symbols[i].type == "function"){
        Interceptor.attach(targetFun,{
            onEnter:function(args){
                this.tmp = ptr(args[0]);
                this.tmp.writeByteArray([0x39,0x36]);
                console.log("success");
                console.log(hexdump(this.tmp, {
                    offset: 0,
                    length: 64,
                    header: true,
                    ansi: true
                }));
               Thread.sleep(6);//注意这里,让线程睡眠6秒,否则console不能输出
            }
        });
        break;      
    }
}

通过r2来获取96对应的asci码是0x39 0x36

运行frida脚本,这样无论输入什么的都可以Pass.

Thread.sleep(delay): suspend execution of the current thread for delay seconds specified as a number. 由于Ansi相关的函数只能用在Windows平台中,所以这里才使用byte数组。

Note that writeAnsiString() is only available (and relevant) on Windows.

修改指令

修改跳转指令。使得程序无论如何都可以成功运行。

1.如果相等,则出现成功的提示,如果不相等,则跳转到0x80484f4 cmp dword [var_8h], 0xf jne 0x80484f4

代码实现如下:

代码语言:javascript
复制
"use strict"

var targetModule = Process.findModuleByName("crackme0x04");
var symbols = targetModule.enumerateSymbols();
var targetFun = null;
for(var i = 0; i < symbols.length; i++) {
    targetFun =  ptr(symbols[i].address);
    if (symbols[i].name == "check" && symbols[i].type == "function"){
        const maxPatchSize = 64; // Do not write out of bounds, may be a temporary buffer!
        Memory.patchCode(targetFun.add(0x56), maxPatchSize, code => {
            const cw = new X86Writer(code);
            cw.putJmpAddress(targetFun.add(0x56).add(0x2));//跳转指令,跳转到success分支
            console.log(hexdump(targetFun.add(0x56).add(0x2), {
                offset: 0,
                length: 64,
                header: true,
                ansi: true
            }));
            cw.flush();
        });
        break;     
    }
}

代码中的0x56是跳转指令与check函数地址的偏移(offset),0x2是跳转指令的长度。

运行脚本,无论输入什么,都可以Pass。

r2pipe

官方文档如下,简单点说就是r2脚本编程的接口。

Radare2 provides a wide set of a features to automate boring work. It ranges from the simple sequencing of the commands to the calling scripts/another programs via IPC (Inter-Process Communication), called r2pipe.

举个栗子(本文基于的是python3.8), 获取通过r2分析得到的函数列表并且以json格式输出

代码语言:javascript
复制
import r2pipe

r2 = r2pipe.open("/bin/ls")
r2.cmd('aa')
print(r2.cmd("afl"))
print(r2.cmdj("aflj")) # evaluates JSONs and returns an object

基于此可以调试和破解程序。

fuzz

这个方法综合了上述的一些方式, 我们可以用暴力破解的方式来获取密码, 也可以利用libFuzzer 来自动化找出该程序潜在的bug. 这种方式的坏处是太暴力了, 让妹子不敢靠近(逃); 好处则是在一定程度上解放了大脑, 用计算机来帮我们计算, 算力越强就越有可能找到突破点!

对于libfuzz的使用,在漏洞挖掘(1) 中对libFuzzer进行了介绍。

写在最后

crackme0x04开始涉及到算法,crackerMe0x05之后难度会逐步增加,后续文章会陆续介绍。R2pipe和fuzz在这里挖个坑(主要是一两句说不清楚),哈哈。

公众号

更多逆向破解内容,欢迎关注我的微信公众号:无情剑客。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • crackme0x04
  • 流程图
  • 指令和函数
  • 伪代码
  • 逻辑分析
  • 破解
  • 输入正确的值
  • frida修改逻辑
  • 修改参数
  • 修改指令
  • r2pipe
  • fuzz
  • 写在最后
  • 公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档