0


Buuctf [网鼎杯 2020 青龙组]jocker 题解

一.主函数逻辑

1.提示:// positive sp value has been detected, the output may be wrong!,出现了堆栈不平衡的情况

2.VirtualProtect函数的作用是取消encrypt函数所在区域的读写保护,为下方给函数脱壳做准备

3.首先输入一个字符串input,判断长度是否为24,然后复制到str中;

4.wrong函数对input串加密,加密后由omg函数进行判断是否满足条件,但是根据这两条函数得到的flag是假的,也就图一乐

5.真正的flag要根据encrypt和finally函数以及str串(原始的input)来求得

二.wrong函数和omg函数--假flag

1.wrong函数

很普通的一个加密

2.omg函数

很普通的一个判断

其中arr数组的内容可以通过4030c0区域提取得到,选中数据右键->convert->以dword形式输出

3.假flag

根据这两个函数可以写出解题脚本,但是输出的是假flag: flag{fak3_alw35_sp_me!!}

不过这个假flag就是我们要输入的字符串,下面还有作用

#include <stdio.h>
int main()
{
    unsigned int input[25] = {
    0x00000066, 0x0000006B, 0x00000063, 0x00000064, 0x0000007F, 0x00000061, 0x00000067, 0x00000064,
    0x0000003B, 0x00000056, 0x0000006B, 0x00000061, 0x0000007B, 0x00000026, 0x0000003B, 0x00000050,
    0x00000063, 0x0000005F, 0x0000004D, 0x0000005A, 0x00000071, 0x0000000C, 0x00000037, 0x00000066,0
    };
    int chr;
    for (int i = 0; i <= 23; ++i)
    {
        if ((i & 1) != 0)
            input[i] += i;    //-号修改为+号
        else
            input[i] ^= i;
        printf("%c", input[i]);
    }
    //flag{fak3_alw35_sp_me!!}
    //假密码
    return 0;
}

三.encrypt和finally函数--真flag

直接点击encrypt或finally函数会提示错误,并显示汇编代码,如果是框图形式可以按空格键转换一下

查看汇编代码不难发现有sp栈指针错误

1.打开sp指针偏移显示

在options>general>disassembly中,勾选上stackpointer

此时可以发现在地址和十六进制指令中间多了一列蓝色数字,这行数字代表的就是本条指令执行后sp指针的偏移值会是多少(应该是根据bp来计算的)

2.修复sp指针偏移值

我们回到主函数反汇编窗口,按tab键转换为汇编窗口,往下翻找到call 指令部分

选中call __Z7encryptPc这行指令,按alt+k,将新旧sp偏移值改成0;

这样操作后可以发现偏移值正常了,然后对下方的call __Z7finallyPc做相同操作

按f5也可以发现主函数没有了sp指针错误的提示

不过仍然进不去encrypt函数,finally函数也是很丑陋的一大段,这是由于encrypt函数还没有被去壳

3.动态调试得到正确的encrypt和finally函数

回到main函数,在**if ( encrypt(Str) )**这里下断点,开始动态调试,输入上面的假flag即可通过omg判断

(1)得到正确的encrypt函数

在main中点击encrypt函数,仍然不能反汇编,我们找到encrypt函数定义处对应的汇编代码

从定义头一直到endp,还有下方这一块区域,全部选中之后按u(undefined)取消定义,再在定义头按p即可识别为函数

取消定义重新识别后按f5成功得到encrypt函数

在这里可以dump下来403040和Buffer串的内容:

unsigned int dword_403040[19] = {
0x0000000E, 0x0000000D, 0x00000009, 0x00000006, 0x00000013, 0x00000005, 0x00000058, 0x00000056,
0x0000003E, 0x00000006, 0x0000000C, 0x0000003C, 0x0000001F, 0x00000057, 0x00000014, 0x0000006B,
0x00000057, 0x00000059, 0x0000000D
};

unsigned char Buffer[]="hahahaha_do_you_find_me?";

值得一提的是动态调试结束后encrypt函数又会识别错误,这是因为每次运行程序会执行脱壳代码,而回到静态调试时会回到脱壳前的状态,所以导致识别错误

(2)得到正确的finally函数

在上一步的基础上,继续往下翻找到__Z7finallyPc,也是相同处理方法(包括下方一大段数据)

finally函数重新识别后在静态分析中也有效

得到反汇编代码:

四.解题脚本

1.flag前一段

根据encrypt函数的内容可以写出这个脚本,得到flag{d07abccf8a410c

显然还缺少了一段,这段需要分析finally函数

#include <stdio.h>
int main()
{
    unsigned int dword_403040[19] = {
    0x0000000E, 0x0000000D, 0x00000009, 0x00000006, 0x00000013, 0x00000005, 0x00000058, 0x00000056,
    0x0000003E, 0x00000006, 0x0000000C, 0x0000003C, 0x0000001F, 0x00000057, 0x00000014, 0x0000006B,
    0x00000057, 0x00000059, 0x0000000D
    };
    unsigned char Buffer[25] = "hahahaha_do_you_find_me?";
    unsigned char flag[25] = { 0 };
    for (int i = 0; i < 19; i++)
    {
        flag[i] = dword_403040[i] ^ Buffer[i];
        printf("%c", flag[i]);
    }
    //flag{d07abccf8a410c
    return 0;
}

2.flag第二段

这个非常迷惑,分析上面得到的反汇编代码无处下手,因为判断条件中有个随机数,看了其他大佬的wp

"%tp&:"这个字符串是最后一段,flag的最后一个字符是'}'根据题目描述的异或操作,可能是这段字符使用同一个值异或,所以只要由**':'^n='}'**得到这个用于异或的值即可求出其他字符

char n = ':' ^ '}', arr[6] = "%tp&:";
    for (int i = 0; i < 5; i++)
    {
        arr[i] ^= n;
        printf("%c", arr[i]);
    }
    //b37a}

所以flag{d07abccf8a410cb37a}

五.参考文章

1.[网鼎杯 2020 青龙组]jocker(详解)

2.IDA出现"sp-analysis failed"和F5(反编译)失败

3.IDA为什么产生 sp-analysis failed 错误?

4.C/C++ 函数的调用约定详解

5.【汇编 C】C语言常用的三种函数调用约定:__cdecl、__stdcall、__fastcall

6._stdcall与_cdecl区别

对于sp栈指针的修复以及函数调用,栈平衡后续会写一篇文章专门讲解

标签: c语言 安全 Reverse

本文转载自: https://blog.csdn.net/OrientalGlass/article/details/129469388
版权归原作者 OrientalGlass 所有, 如有侵权,请联系我们删除。

“Buuctf [网鼎杯 2020 青龙组]jocker 题解”的评论:

还没有评论