xctf战疫大赛re-天津垓解法
本帖最后由 Dyingchen 于 2020-3-11 12:04 编辑xctf战疫大赛re-天津垓解法这次比赛在攻防世界平台上面举办,有兴趣的可以自己去看看,真的是神仙打架,而我这种萌新只能瑟瑟发抖,闲话不多说进入正题
下载题目文件习惯性查一下程序信息:
得到程序是一个无壳64位的控制台程序,还附带了一个dll
那么直接先用IDA载入分析,同时打开x64dbg分析程序,然后接下来程序一闪就消失了我立马反应过来程序有反调试,应该是检测到了IDA所以闪退了,因为我电脑上面的x64dbg是有sharpod插件的所以不应该是x64dbg的问题。
所以我关掉IDA直接用x64dbg分析,来到程序载入跑起来的界面
显然这里需要一个授权的字符串,直接到x64dbg里面搜索字符串定位到获取输入的点
在搜索字符串的时候能够看到一堆检测的代码,把退出跳转全部给nop掉,这样就不会妨碍分析了
发现下面有两个函数,都给下上断点,随便输入一个字符串进行测试一下发现断不下来于是跟进到4011f6函数里面分析
发现了获取输入的call,下断,成功断下但是发现字符串长度不够(0x12)程序马上就要退出了,于是修改汇编使其跑到真假码对比的地方
紧接着发现程序在进行循环对比,结合上面有大量的赋值mov语句可以判定应该是在进行某种加密,于是重新打开IDA分析得到如下逻辑
根据要求需要满足v43 = ~(j & b) & (j | b),并且v43和数组v1的值相等
这里看起来一头雾水,因为之前去掉了反调试所以现在直接用IDA对程序进行动态调试(注意用自带调试器调试时路径里面不能有中文否则IDA报错)在内存中可以看到v39的字符串被替换成了一个字符串而不是之前的数据现在可以直接把IDA反编译出来的逻辑复制过来使用,写一个脚本进行爆破其中:v39为上图的字符串str是我们输入进去的字符串,v43是对比的数据
#include <iostream>
using namespace std;
int main()
{
long long int a={17,8,6,10,15,20,42,59,47,3,47,4,16,72,62,0,7,16};
long long int b={0x52,0x69,0x73,0x69,0x6E,0x67,0x5F,0x48,0x6F,0x70,0x70,0x65,0x72,0x21};
long long int find=0;
int i=0;
int j=0;
while(i!=18)
{
j=0;
while(j!=255)
{
find = ~(j & b) & (j | b);
if(find==a)
{
cout<<(char)j;
break;
}
j++;
}
i++;
}
return 0;
}
得到授权字符串为:Caucasus@s_ability
于是输入字符串进入下一步
然后程序要求你找到flag,但是从这里开始程序就没法搜索到任何可以定位的字符串了于是我找到程序的退出点进行堆栈回溯
然后定位到关键点
找到这个函数头的地址:10040164d 然后去IDA分析
然后发现IDA反编译出来的代码是这样的
初看我还以为代码被v了然后我在IDA里面找到了一个解码的函数
然后我突然发现之前定位关键点的时候在x64dbg里面程序是正常的说明程序在跑到校检的地方的时候这些字节码会被还原成原来的代码所以进行动态调试
输入之前得到的授权字符串:Caucasus@s_ability
然后按g跳转到10040164d
此时的代码应该被还原了所以按c转换成汇编代码,右键新建函数
然后按F5反编译成伪代码
得到如下结果
终于得到逻辑了,然后根据逻辑我们发现这是一段运算大概逻辑是
校对码的每一位 = 19683*输进去的字符串每一位%(-2147483637)
中间有些数据IDA有时候会反编译成&unk_D83E7这种类型的地址,
此时需要等到程序运行过赋值之后鼠标移动上去就可以发现是一个64位的数据
解决了一堆问题之后
现在我们终于可以写脚本了
#include <iostream>
using namespace std;
int main()
{
long long int a={2007666,2125764,1909251,2027349,2421009,1653372,2047032,2184813,2302911,2263545,1909251,2165130,1968300,2243862,2066715,2322594,1987983,2243862,1869885,2066715,2263545,1869885,964467,944784,944784,944784,728271,1869885,2263545,2283228,2243862,2184813,2165130,2027349,1987983,2243862,1869885,2283228,2047032,1909251,2165130,1869885,2401326,1987983,2243862,2184813,0xD83E7,2184813,2165130,1987983,2460375};//这些我们输入进去字符串之后经过变换之后需要对比的数据
long long int find=0;
int is_find=0;
int i=0;
while(i!=52)
{
while(is_find==0)
{
if((19683*find)%(-2147483637)==a)
{
cout<<(char)find;
i++;
find=0;
is_find=0;
break;
}
find++;
}
}
return 0;
}
最后得到flag
flag{Thousandriver_is_1000%_stronger_than_zero-one}
最后再附上原题
楼主想问一下第一个算授权码的时候那个j!=255咋来的啊
我看网上最后一步算出flag的脚本里面也有255
Number=
for i in range(0x33):
for n in range(255):
if Number == 19683 * n % 0x8000000B:
Result.append(chr(n))
break
print Result
一直搞不明白这个255是从哪里分析来的 感谢分享,思路非常清晰~ 感谢分享,思路非常清晰~ 厉害啊。。。。。 感谢分享,这种比赛不错呀,挺好玩的 这种比赛不错呀,挺好玩的 谢谢楼主了,感觉复现一下。 厉害。。。。。。。。。。。。 厉害啊。。。。。学习了 虽然看不懂但看起来很厉害