吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7551|回复: 43
收起左侧

[CTF] 高一人的第一次正经逆向——吾爱2023新年领红包Windows题分析

  [复制链接]
DNLINYJ 发表于 2023-2-6 00:43
本帖最后由 DNLINYJ 于 2023-2-7 21:24 编辑

作为一个啥都会点的高一人,逆向一直是拿来玩玩和用在不能说的地方()

今年看到吾爱有逆向题目领红包,就过来试试水(^^ゞ

1. Windows 初级题

首先将程序拉进 ExeinfoPe 查壳, 一看没壳,还是32位,先拉 IDA 里面去静态分析

初级题查壳.png

拉进 IDA 之后直接 F5 看伪C

初级题IDA.png

整体的逻辑也很简单,先检查输入的字符串长度是不是29,符合的话就进入循环与预定的flag比较,如果正确打印Success,错误打印Wrong。

初级题分析.png

把伪C代码放进 ChatGPT 分析,也得到了相同的结论

ChatGPT初级题分析.png

双击dword_43F000跳转到 IDA View,先调整一下数组大小,再导出数组

初级题调整数组大小.png

初级题导出数组.png

导出数组后用Python打印预定的flag,得到flag为 flag{52PoJie2023HappyNewYear}

初级题Python.png

2. Windows 中级题

2.1 脱壳

首先运行一下程序,发现程序需要输入UID和Key,并且用了GUI

中级运行1.png

将程序拉进 ExeinfoPe 查壳,UPX的壳

中级题查壳.png

用upx -d脱壳报错,怀疑对解压缩的结构体动了手脚,直接拉进x64dbg用rsp定律脱壳

中级题UPX尝试脱壳.png

拖进x64dbg发现有反调试,直接SharpOD一套带走,成功加载

中级X64dbg反反调试.png

用RSP定律脱壳,到了OEP直接拿Scylla Dump,运气不错可以自动修IAT

同时发现导入表中有 GetDlgItemTextW,大概率用来获取用户输入

中级X64DBG_RSP.png

中级Scylla.png

2.2 初步静态分析

将修好IAT的脱壳后程序扔进 IDA 里面分析,寻找 GetDlgItemTextW 的交叉引用,发现有两个函数引用

IDA_1.png

进入sub_7FF6B7371A20 发现这是一个获取用户输入的函数 至于输入的是UID还是Key暂时还不知道 继续查看交叉引用找到主要逻辑函数sub_7FF6B73711D0

IDA_2.png

IDA_3.png

同时在sub_7FF6B7371A20中看到了 qword_7FF6B7387C90 + 偏移 的函数调用方式,怀疑是自己实现的导入表,点进去一看,确实是-O-

qword_7FF6B7387C90_1.png

qword_7FF6B7387C90_2.png

修改 qword_7FF6B7387C90 数组长度为20 (因为qword_7FF6B7387C90的第0位和第17-19位为零),导出地址之后一个个输入到x64dbg里面获取具体的API,最后处理结果为

qword_7FF6B7387C90[1] : user32.GetDlgItemInt
qword_7FF6B7387C90[2] : user32.GetDlgItemTextW
qword_7FF6B7387C90[3] : user32.SendMessageW
qword_7FF6B7387C90[4] : user32.LoadIconW
qword_7FF6B7387C90[5] : user32.MessageBoxW
qword_7FF6B7387C90[6] : user32.EndDialog
qword_7FF6B7387C90[7] : user32.GetDlgItem
qword_7FF6B7387C90[8] : user32.SetFocus
qword_7FF6B7387C90[9] : user32.GetDlgCtrlID
qword_7FF6B7387C90[10] : user32.SetWindowPos
qword_7FF6B7387C90[11] : user32.OffsetRect
qword_7FF6B7387C90[12] : user32.CopyRect
qword_7FF6B7387C90[13] : user32.GetWindowRect
qword_7FF6B7387C90[14] : user32.GetDesktopWindow
qword_7FF6B7387C90[15] : user32.GetParent
qword_7FF6B7387C90[16] : user32.SendDlgItemMessageW

由于sub_7FF6B7371A20中调用的是 user32.GetDlgItemInt (qword_7FF6B7387C90[1]),于是可以确定sub_7FF6B7371A20为获取UID的函数 v10为返回的UID

同理可得sub_7FF6B7371FC0为获取Key的函数 v18为返回的Key字符串

之后将UID和Key作为参数调用sub_7FF6B7372110 返回值存储在v12中

2.3 sub_7FF6B7372110算法分析

直接进入sub_7FF6B7372110,一眼发现有一个循环没有结束条件,直接看汇编,还原代码

补充:看其他师傅的文章发现这个循环貌似是没问题的,算我这里分析出了问题`(>﹏<)′

sub_7FF6B7372110_1.png

sub_7FF6B7372110_2.png

分析代码后,发现主要是将输入的Key作为一个int数组,循环这个数组,将每个元素的前后16位调换+异或特定魔数后,输入sub_7FF6B7371D70进行处理

并将处理后的值于一个同样经过sub_7FF6B7371D70处理过的数组qword_7FF6B73868F0比较,如果数组中的所有值相等,返回0,反之返回v11

ChatGPT的解释也验证了我们的分析 (见下图)

sub_7FF6B7372110_3.png

sub_7FF6B7372110_ChatGPT.png

这就是说,只要我们输入的值处理后与qword_7FF6B73868F0的值相等,就可以让sub_7FF6B7372110返回0,用Python写出逆运算算法之后算出Key数组如下

wchar_t Key_1[] = {102, 108, 97, 103, 123, 61135, 61135, 13074, 13075, 4441, 4429, 30503, 30519, 4424, 4422, 13181, 13165, 4422, 4439, 65446, 65441, 4432, 4443, 13164, 13152, 4432, 4430, 30577, 30576, 4400, 4407, 13074, 13075, 0};

结果,将Key1输入时依旧报错,看来这并不是判断Key是否正确的函数 / \

Key_1.png

2.4 动态调试 + 更多的静态分析

switch ( (unsigned __int16)a3 ) 处下断点,对应汇编 cmp eax, 1 处,输入Key,点击确定,发现这一处被触发了3次,同时eax第一次为1,剩下两次为0x300,说明对Key做校验的函数在0x300的分支中

动态调试_1.png

动态调试_2.png

回到 IDA ,0x300的分支先通过比较字符串得到v6的值,再根据v6走不同的分支,具体解释可以看ChatGPT给出的解释

ChatGPT_2.png

同时根据调试器的结果,可以判断第一次走的是 v6 == 0 的分支,第二次走的是 v6 != 0 的分支,而 v6 != 0 的分支调用了 user32.MessageBoxW (qword_7FF6B7387C90[5]),可以确定第二次走0x300分支为输出结果,那么第一次走0x300分支就是对Key进行校验了

根据调试器中a4输出的结果,发现第一次的a4为 sub_7FF6B7372110 的返回值,第二次的a4是一个flag,用于指定MessageBoxW输出的值

动态调试_3.png

进入 v6 == 0 分支,可以分析出 sub_7FF6B73725E0 和 sub_7FF6B7372840 分别为 GetUID 和 GetKey (具体分析懒得写了,基本上有两种方法判断,一是从控件的资源ID判断,UID为1000,Key的为1001;二是从返回值和具体的前后文判断),初步分析的代码如下

IDA_4.png

2.4 CheckKey分析

CheckKey的整体逻辑也很简单: 将经过sub_7FF6B73726E0处理过的ProcessedKey数组与v16数组比较,如果全部正确,返回值为4,反正返回值为3,计算魔数那里在还原的时候直接照抄即可

CheckKey_1.png

而v16数组的值为 char v16[] = "flag{!!!_HAPPY_NEW_YEAR_2023!!!}",将其转换为unsigned int数组结果为 {0x67616c66, 0x2121217b, 0x5041485f, 0x4e5f5950, 0x595f5745, 0x5f524145, 0x33323032, 0x7d212121},也就是说,当我们输入的Key经过sub_7FF6B73726E0的运算处理后与v16相等时,这个Key就是正确的,所以我们将v16经过sub_7FF6B73726E0的逆运算后,即可解出这道题的Key

补充:后面看其他师傅的解释才知道sub_7FF6B73726E0是一个tea的解密函数,还是经验不足呀 (⊙ˍ⊙)

逆运算的C++代码如下

void reverse_sub_7ff6b73726e0(unsigned int* arg1, _DWORD* a2, int a3, unsigned int a4)
{
        unsigned int v5 = arg1[0];
        unsigned int v6 = arg1[1];

        for (int i = 0; i < 32; i++) {
                a4 += a3;
                v5 += (a2[1] + (v6 >> 5)) ^ (a4 + v6) ^ (*a2 + 16 * v6);
                v6 += (a2[3] + (v5 >> 5)) ^ (a4 + v5) ^ (a2[2] + 16 * v5);
        }
        arg1[0] = v5;
        arg1[1] = v6;
}

其中这里还有一个问题,就是 reverse_sub_7ff6b73726e0 中 a4 的初始值如何确定,将 sub_7FF6B73726E0 算法还原之后进行动态调试,得到下面的表格,可以很明显的看出 sub_7FF6B73726E0 最后会将 a4 递减至0,所以 reverse_sub_7ff6b73726e0 中 a4 的初始值为0

表格.png

至于a2和a3的值可直接从CheckKey里的魔数生成部分中抄下来

CheckKey_2.png

最后还原出 ProcessedKey 数组的值应为 {0x805b431,0xc46f31a2,0x67d178e8,0xb1d33200,0x17d8e19b,0xc1266b7d,0xc5bbd440,0xfb25dbda}

还原ProccessedKey.png

2.5 得到最终的Key

由于ProcessedKey的值由 ProcessKey 函数得来,我们需要对ProcessKey进行分析,进而得到真正的用户应该输入的Key

根据ChatGPT的解释,ProcessKey是用来将字符串转换成整数的,同时提醒了我们Key的长度应该是8的倍数

ChatGPT_3.png

然后让ChatGPT写出ProcessKey的逆函数,但是调试之后怪怪的......

ChatGPT_4.png

随后查看a1的值,发现ProcessedKey的前8个字符已经被还原,并且全部字符为大写,再根据上面对ProcessKey的解释,猜测出用户输入应该是ProcessedKey数组的16进制

调试_1.png

撸几行代码测试一下,最终得到正确的Key,成功拿下ヾ(^▽^*)))

Python.png

Final.png

文章末尾附上注册机代码如下

#include <iostream>

// 由ChatGPT生成
wchar_t* reverse_sub_7FF6B73724A0(unsigned int input) {
        wchar_t* result = new wchar_t[9];
        result[8] = 0;
        for (int i = 7; i >= 0; i--) {
                result[i] = input % 16;
                if (result[i] < 10) {
                        result[i] += 48;
                }
                else {
                        result[i] += 55;
                }
                input /= 16;
        }
        return result;
}

void sub_7ff6b73726e0(unsigned int* arg1, unsigned int* a2, int a3, unsigned int a4) {
        unsigned int v5 = arg1[0];
        unsigned int v6 = arg1[1];

        for (int i = 0; i < 32; i++) {
                a4 += a3;
                v5 += (a2[1] + (v6 >> 5)) ^ (a4 + v6) ^ (*a2 + 16 * v6);
                v6 += (a2[3] + (v5 >> 5)) ^ (a4 + v5) ^ (a2[2] + 16 * v5);
        }
        arg1[0] = v5;
        arg1[1] = v6;
}

int main()
{
        int v11 = 0x11111111;
        for (int i = 0; i < 14; ++i)
        {
                v11 += 0x11111111;
        }

        int UID;
        std::cout << "UID: ";
        std::cin >> UID;
        std::cout << "\n";

        int k = v11 + UID;
        int v17[4];
        while ((k & 0x80000000) == 0)
                k = k + k + 9;
        for (int m = 0; m < 4; ++m)
                v17[m] = (m + 1) * (k + 1);

        unsigned int* v16 = new unsigned int[8] { 0x67616c66, 0x2121217b, 0x5041485f, 0x4e5f5950, 0x595f5745, 0x5f524145, 0x33323032, 0x7d212121 };
        for (int n = 0; n < 4; n++)
                sub_7ff6b73726e0((v16 + (2 * n)), (unsigned int*)v17, (unsigned int)k, 0);

        wchar_t* a1;

        std::cout << "Key: ";

        for (int n = 0; n < 8; n++)
        {
                a1 = reverse_sub_7FF6B73724A0(v16[n]);
                std::wcout << a1;
        }

        std::cout << "\n";

        return 0;
}

3. 总结

这篇文章其实还有一些东西没写,像 0x37异或字符串算法,Block数组的还原 都没有详细去讲,因为这两个一个是没多大用处,一个可以直接抄IDA生成的伪C代码,所以也就不浪费空间写这两个了

这次的Windows中级题相对来说难度还行,主要是不熟悉Windows原生的Gui函数拖延了很多时间,在调试和理解代码这方面也花了很多时间,不过确实也学到了很多东西 (^^ゞ

免费评分

参与人数 12吾爱币 +18 热心值 +11 收起 理由
qiuming86 + 1 + 1 我很赞同!
RedamancyJJ + 1 我很赞同!
飞腾小子 + 1 + 1 6666666666666
yp17792351859 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Tonyha7 + 2 + 1 用心讨论,共获提升!
lax123 + 1 + 1 我很赞同!
max2012 + 1 + 1 我很赞同!
taichao + 1 + 1 谢谢@Thanks!
成熟的美羊羊 + 2 + 1 &amp;amp;#128515;666
ICEY + 3 + 1 太强了
CrazyNut + 3 + 1 用心讨论,共获提升!
156608225 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

156608225 发表于 2023-2-6 00:54
ChatGPT都用上了可还行。。。
头像被屏蔽
86618513 发表于 2023-2-6 01:39
爱飞的猫 发表于 2023-2-7 01:52
melchior 发表于 2023-2-6 14:34
感谢大佬分享,有个小问题想问下:

windows初级里这一步导出43F000这一步里,上图是怎么操作得来的?

把变量类型改成 DWORD 或 uint32_t 再导出就是对的了
taichao 发表于 2023-2-6 08:41
初级题看看还行,中级的直接懵逼。
darksied 发表于 2023-2-6 08:57
ChatGPT功能这么强了?
w759003376 发表于 2023-2-6 09:28
看起来挺厉害了
galepai 发表于 2023-2-6 09:33
大神啊。
这一看,自己的道路还非常漫长啊。。
天行键丶 发表于 2023-2-6 09:39
darksied 发表于 2023-2-6 08:57
ChatGPT功能这么强了?

很强,可以节约点时间,让gpt帮忙分析下代码逻辑,但真正强的还是楼主。gpt有时候会输出莫名其妙的东西的,得自己会分辨。不过对比市面上其他ai,chatgpt直接乱杀。
bohasun 发表于 2023-2-6 10:01
ChatGPT真的强
wasdzjh 发表于 2023-2-6 10:17
chatgpt这么强,赶紧安装一个
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-31 01:11

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表