吾爱破解一道题目,GPT4秒破!

大家好,我是厂长。

今天的文章有点特殊,是来自我在B站发布的一个视频。这个视频是讲解的吾爱破解论坛春节期间的一个逆向解题活动中的一道题目。

但因为我的大意,在人家活动还未结束的时候就把解题视频发布了出来,给他们造成了很多麻烦。虽然后来视频删掉了,但还是要给吾爱破解论坛道个歉。

如今活动已经结束,可以公开解题过程了。把这个视频重新以文章的形式发布一下。

题目还是挺有趣的,大家可以仔细看到最后。

以下是正文内容:


大过年的,我的逆向学习群里有几个卷王不过春节,居然在那里玩什么破解题目,一开始我还没注意,专心在家炸金花,后来发现玩的人越来越多,探讨氛围越来越浓,我也来了兴趣。

90e9f3b1f804aa5aba2626eb39c02d22.jpeg

一了解才知道,原来是吾爱破解论坛搞了个春节解题领红包的活动。群里有小伙伴卡住了逆不动向我求助,于是我花了点时间研究了一下。没想到还挺有意思

4ac9e6f3e7c5f58e2a416c2d434b1278.png

首先下载到这个题目的程序来运行一下,提示需要输入一个密码,先随便输入一个试试,不出所料,打印了一个Error退出了。

fc7302bf98f57350f8082f79148d5c6f.png

来,大型伺候,祭出逆向分析神器IDA,看看它有多少斤两。

3d7876f5ee6c9587661b781052f071b4.png

IDA自动分析出了main函数, 先来大体浏览一下main函数的结构。从这些字符串信息看得出来,整个判断逻辑还是比较清晰简单的。

ed861412c7b6810f89a357ef793655f6.png

这里是第一处比较和判断,如果判断不通过,跳到红线的分支,直接打印Error信息。否则顺着绿色的线来到下一个代码块。

0ecb62836c2d84c95cc063f35bb069e1.png416ddb0a39bd7ef43affa9cffbd44729.png

92c8e2c21ba732fbc568f99061de8008.png

然后这里执行了一些操作之后,又继续比较判断,如果判断不通过,顺着这里的绿线,打印一个Wrong信息。

4bd2a6e701e611129056c251779671f4.png

就这样一层层的校验判断,就能最终通向成功的地方,这里打印一个Success字符串!

a322c7e300c0d34178bc52082335c2a7.png

如果仅仅只是想让程序打印出Success字符串,我们可以一路修改条件跳转指令的标志位来实现,也就是暴力破解,这个我们之前的视频里已经给大家演示过。但这道题目是要输入正确的flag,光打印Success是没有意义的,所以还得分析出正确的flag才算是破解这道题目。

好吧,看汇编指令总是让人头大,祭出IDA的大招,F5大法,反编译出C语言。

这一晃眼,也看不太出来是怎么个逻辑。

不过我注意到了这里这个函数调用,在访问全局的变量。

5c7a711bbcb0ccf543eea63835b3d60d.png

点进去发现这是一个字符串,大胆猜想,这个字符串肯定与我们要的flag有关系!它可能被加了密!

8ff801577e49412f8413de3e5a16702b.png

然后一不小心,我瞅见了这个字符串旁边还有一个字符串,这里居然有一段提示,告诉我们破解的方法就是凯撒密码

0218fd9a7a5e46e0de2331edaca7cf2c.png

凯撒密码是一种很古老也很简单的密码,简单来说,它是把明文中的字母按照字母表上向前(或向后)按照一个固定数目进行偏移后替换成对应的字符,所有字符替换完成后就得到了密文。

7427cb401017ee69d588165f09dbfbf6.png

这个加密强度非常低,也没有秘钥,只要知道了这个偏移值,就能还原出来了。

结合刚刚的这个加密字符串,和这个提示信息,我自然而然的想到,那这个字符串肯定就是移位后的结果。

我的第一反应是想着写个程序去暴力尝试,对这个加密后的字符串通过各种不同的偏移值去还原,全部打印出来,然后根据打印出的结果,去人工筛选出最有可能的那一组来。因为真正的那个字符串肯定里面是一些有意义的单词,根据这个特征就能筛选。

于是我打开IDE,准备来编码,工程才刚建了一半,我突然想到,为啥不让AI来试试呢?

于是我打开了GPT4.0,把这个字符串给了它,并告诉它,这个字符串可能是凯撒加密的结果,让它给我推导出可能的原始明文。

75c0691fccd2f65fa6b9b30c15349d6c.png

几秒钟的时间,它还真给我分析出来了。这个偏移就是3,所有字符向前移动3位,得到的字符串里面就有有意义的字符串!

bce4eff9a27a033e3dc0909e2bd322e1.png

我赶紧拿着这个字符串试了一下,没想到居然还是给我报Error!

5e90d248a104b4554516ef812bce1cdd.png

但这个字符串怎么看都长得像是flag。然后我凭借之前打CTF的经验,flag的格式,最后这里应该还有一个右花括号。我加了一个右花括号,又尝试了一次,嘿,这一次成功了,打印了Success!

1e99443d8ffcd33a7eaadcb4647adab7.png

不过我还是好奇,这个flag怎么就少了一个字符呢?凯撒加密不会改变长度啊!

于是我重新在IDA中打开了那个字符串。没想到在字符串的后面还有一个字节0x80。

902a3b107a9332f8ccce0fe423b59b87.png

因为它已经超出了ASCII字符的范围,IDA在分析字符串的时候,把它给漏掉了。而0x80向前移动三位,刚好就是右花括号!真的坑爹啊!

9b0c55d3b228a9f8c8d755cab24d54e9.png

到这里为止,这道题目就算是破解成功了!GPT真的香啊,日常工作辅助真是个好帮手!

不过这样就破解了,总感觉有点偷奸耍滑的味道,假设没有那个提示的情况下,我们又怎么拿到flag呢?

接下来我们就来正式分析一下这道题目。

首先,我们看一下这第一个地方的判断跳转,看看这里是在判断和比较什么

9d79839be391edf00183483c28f82767.png

我们使用WinDbg打开这个程序

aa2494ec60cf96197f18b28e5e42ee9f.png

我们在这第一个跳转前的判断指令这里下个断点,然后运行一下。

程序现在提示需要输入,我们随便输点啥,比如我输入一个xuanyuan,回车!

059d87eb7e81d7c57b370304865f67ba.png

程序中断到了我刚才的断点,这里是在拿ebp-18h这个地址处的值和24h,也就是十进制的36来比较。

e667ea355abb8fbc91c561780b2b518b.png

来看一下ebp-18h处的内存数据现在是什么。可以看到,这里是8,注意是小端字节序。

6581ec626b2c6d7be629ec6ba6dd43b0.png

我们再往前看看,我们刚刚输入的字符串就在这附近。而8刚好就是我输入字符串的长度。难道这个位置是一个字符串长度的变量?

大胆假设,小心验证,我再增加两个字符试试。重启启动调试程序,重新把断点给打上,输入xuanyuanzf,再次中断下来,再次查看ebp-18h处的内存。可以看到这里果然变成了10。

bb5641391353bd4c9e41a2afb9ae4dea.png

那看来,这条比较指令,就是在比较输入的内容长度是不是36个字符。也就是说,flag的长度就是36。

大家再观察一下这段内存的数据,前面这一段是字符串,然后这里是字符串的长度信息,长度后面这里又有一个0f,也就是15。这几个数据之间有没有什么联系呢?

再结合反编译出来的这段伪代码,这里在给一个地方赋值为15,然后这里又在给某个地方赋值为0。

结合这一系列的信息,可以大胆猜测一下,这里是不是某个结构体变量,结构体变量前面是一段字符串的buffer,16个字节长度,最大容量是15个字节,然后这16个字节后面是字符串的长度,长度后面的这个字段是buffer的容量。

其实啊,这里就是C++中的string对象!

6cccae17387876cfb52fab68c274c1a1.png

你问我为什么知道,一方面是结合之前看过string的实现源码,另一方面是结合IDA分析出来左边这里有大量C++标准库的函数,这肯定是个C++编写的程序。综合这些信息猜出来的。

当然目前只是猜测,我们在Visual Studio中来看一下这个string类型确认一下。

这里我定义了两个string变量,然后分别赋值了字符串,其中str1的长度小于16个字符,str2的长度大于16个字符。

程序最后这里把两个变量的地址打印了出来。

13176242b5c6ca9c4ecb5d15d5ca8c1f.png

我们来调试看一下,这两个变量的内存数据分布情况:

先看看str1,可以看到这里的内存分布跟刚才在WinDbg中看到的比较像,也是字符串后面,跟的就是字符串的长度,然后是一个容量数据,也是0x0F,15。

69e44d7999fc6b0bae1d0f4caa24c55c.png

有一点不同的是,它前面这里还有4个字节,这里我们先不去管它。

再看看str2,这里又有一点区别,后面这里确实是字符串长度,容量位置的字段现在变成了0x1F。前面这里字符串没有了,但如果我们把前面四个字节作为一个指针,再次查看内存就可以看到这个字符串。

e4f55acd897319560b14b13f2ace1c09.png

也就是说,这个string对象里面的buffer,默认可以存放15个字符,如果超出这个长度,就会另外开辟内存,然后用一个指针来指向它。

接下来,我们直接来查看string的源码,来验证一下。

string是C++标准库中定义的类型,在VisualStudio中,我们把光标定位上去,它会自动找到它的定义位置,它实际上是basic_string这个泛型通过typedef创建的类型。转到basic_string这个类的定义里去,这里面有很多函数,我们不管,直接拉到类型定义的最下方,看它里面的成员变量。

这里有个mysize和myres两个成员变量,通过它后面的注释可以看到,说的就是当前的长度和存储容量。

e41dcc3475db269cc58fbe28c9030b04.png

再往前,这里定义了一个union,联合体,联合体是同一个位置可以定义多个不同的变量,根据情况可以具体选择使用哪一个。

这里面就有两个变量,一个是buffer数组,长度是16。另一个就是一个指针。

e8aba802ff235e6987f791f9e985e08c.png

当字符串长度小于16的时候,就直接装在这个buffer里。如果超过了,就使用这个指针来定位字符串数据。就和我们刚才这个示例中的str1和str2的情况对应上了。

现在你知道了string对象的基本结构,回过头来看这道破解题目里的操作,就容易的多了。

首先来看最开始的这四行代码,它实际上就是在初始化一个string对象,把前面我们看到的那个加密后的flag赋值给一个string对象。3f6b64c4afd6f2babb284271b3892a35.png

我们为IDA创建一个自定义的string对象。比如叫xy_string,里面给它添加三个成员变量,第一个是一个union,里面有两个字段,分别是16个字节的buffer数组和一个四字节的指针。第二个字段是int类型的length变量,最后一个是int类型的存储容量字段。

22d8504073bf9358f1806c9db823be86.png

定义在一个头文件中,然后通过IDA的菜单将这个自定义的类型导入进来,接下来我们就可以使用这个类型了。

比如main函数这前面四行,是在用加密后的flag在初始化这个string对象,那我们把相关的变量名字和类型都重新定义一下。IDA会自动根据类型的长度重新分析,将多余的变量去掉,重新按照新的类型字段来解释。

efdf1258b335d545839ead0b589ed150.png

可以看到,更换类型以后,看起来就要清晰直观许多了。

174f4a657203ba9bcf1e9ea5d375a264.png

接下来,我们把我们输入的这个字符串,地址刚刚在WinDbg里面看到过是ebp-28h这个偏移,这个位置也是一个string类型的对象,我们同样更改它的类型为我们刚刚创建的xy_string类型。

78b317bf11dac8f55ea8cb9648d1bf2b.png

反编译的代码逻辑再一次清晰了不少。

然后这里呢,其实也是一个string对象,这个函数是在进行string对象的复制。

11c899cdea5512e7a774639511af1f7d.png

把encrypt_flag复制了一份,你问我为什么知道,调试一下观察这段内存前后的变化就能看得出来。我们把这个复制后的string变量,也更改一下类型和名字。

913dbb6e131ce68a41ffc81e29826574.png

OK,到现在为止,程序的代码逻辑基本上已经能看懂了。

然后这个F91FE0这个函数很关键,点进去一看,一堆复杂的逻辑,这一看就是一段纯算法的代码。先不用管,我们就关心一下它的输入输出是什么。它的输入有几个参数,其中就把加密后的flag的字符串的地址和长度传了过去。然后第二个参数是-3。第一个参数是什么呢?

d0f8a9692973f4f508fc7b8c8f31534b.png

我回到WinDbg中,重新启动调试这个程序,这一次输入一个长度为36位的字符串,比如这样一个字符串:xuanyuanzhifengxuanyuanzhifengxuanyu

5cd074d6d11af428f1d73cdc8b6e8e28.png

我们在调用F91FE0的位置打个断点,根据IDA中的提示,第一个参数是通过ecx传递的。而这里ecx的值是ebp-40h这个地址,来看看里面是啥。

貌似看不出什么来。

a23c4ee106ff18cf20782af7f6e3ca92.png

我们单步执行一下,F10,再次来看看ebp-40h,这里发生了一个变化,以这个值作为指针来观察看看,注意看,这里看到flag了!

5b97c487de25b35a7ea96291d8caadf3.png

那这个函数实际上就是在解密flag,把这个加密后的flag字符串还原出来,-3这个参数就是凯撒密码中向前面移动3个字符!

来给这个string变量换个名字,取名为decrypt_flag。

a66458be12ceb122e05a0c7f90373c39.png

现在后面的逻辑就更清晰了。后面就是在拿input_passwd这个变量和decrypt_flag这个变量,两个在做一些更进一步的匹配校验。剩下的我就不再分析了,现在flag已经到手,题目破解完成!

< END >

最近厂长整理了一份程序员学习大礼包,包含数据结构与算法的最核心知识点,有兴趣的小伙伴可以扫码添加微信,备注“礼包”。

de123bdeb84b87280308b9ff705ff1e9.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值