红帽2019 calc
唉, 用Arm的mac真的挺痛苦的,但是mac上的IDA又是正版,Intel mac还有点老
这道题就是mac的pd没法动调,qemu也不行,只好又回到win的本子上用7.6了..讲真,7.7在m1上跑的真的快
因为静态分析是在mac上完成的,所以静态部分可能写的不是很全,不过也没分析出来太多东西.废话到此为止
这题一共输入了三次, 而且看上去对这三个东西的操作是相同的, 我想试试直接动调看看它对输入做了什么(其实是自己动调能力太弱,想针对性的训练一下)
每次输入之后都有一个很长的sleep, 先把它们扬了才能动调
输入一个1234abcd, 栈上的cin参数变成了这样:
应该是个内存地址0x1D7FF4658D30, 跳一下发现了对应的堆上内存存放着刚刚输入的1234abcd, 打上一个内存硬件断点, 直接执行遇到了一个C++的错误,不对,再来.
第二次没有下内存断点,结合内存在堆上推测出两个奇怪的函数应该分别是new和构造函数(瞎猜的), 在读入一个之后, 执行了两个函数设置了两个变量的值,之后分别执行了三次这样奇怪的东西:
现在能确定input里面存的还是指针, 感觉是一个类似string的东西, 下面得摸一摸FF0和A90两个函数,看看它们到底干了什么. 再来一遍惊奇的发现输入竟然不在input指向的地址里了,我猜这应该是IDA的锅, input在BASE+8490H,而输入的abcd1234则在堆的最末尾BASE+F5C0H,感觉这是构造之后, string太长重新申请但是指针没跟过去...
管他呢,先分析这两个函数, 发现FF0里还是调用了A90,而且FF0还是个递归函数.那不妨先看A90, A90里又发现了这种奇怪的片段,我现在觉得这片段没啥用, 应该是编译器加上的,先进行一个忽略, 第一次调用, a2和a3传的都是输入的这组void* [2], 这样可以猜测第二个和第三个参数应该是并列的, 检查第一个参数,发现第一个参数都是表达式左值,基本上是一个不参与运算,当返回值用的东西, 审计过后发现FF0函数也同样是把它当返回值用那这样逻辑就有一点眉目了:把a2和a3进行一个处理,结果放回a1,
下面通过一个调用动态看看到底是进行了什么处理(不特殊说明测试输入都使用1234abcd了)
__int64 __fastcall sub_7FF666A81A90(__int64 a1, __int64 *a2, _QWORD *a3)
{
int v6; // ebp
__int64 v7; // r8
__int64 v8; // rax
__int64 v9; // rsi
__int64 v10; // r11
int v11; // er10
__int64 v12; // r9
__int64 v13; // rax
void *v14; // rcx
_DWORD v16[2]; // [rsp+20h] [rbp-58h] BYREF
void *Block[2]; // [rsp+28h] [rbp-50h] BYREF
__int64 v18; // [rsp+38h] [rbp-40h]
v16[1] = HIDWORD(a1);
v16[0] = 0;
*(_OWORD *)Block = 0i64;
v18 = 0i64;
maybe_new((const void **)Block, 0i64, v16);
maybe_cons((__int64)Block);
v16[0] = 0;
sub_7FF666A83CE0(Block, ((__int64)(a3[1] - *a3) >> 2) - 1 + ((a2[1] - *a2) >> 2), v16);
v6 = 0;
v7 = *a2;
if ( (unsigned __int64)(a2[1] - *a2) >= 4 )
{
v8 = a3[1] - *a3;
v9 = 0i64;
v10 = 0i64;
do
{
v11 = 0;
if ( (unsigned __int64)v8 >= 4 )
{
v12 = v10;
do
{
*(_DWORD *)((char *)Block[0] + v12) += *(_DWORD *)(*a2 + v10) * *(_DWORD *)(*a3 + v9 + v12);
++v11;
v12 += 4i64;
v8 = a3[1] - *a3;
}
while ( v11 != v8 >> 2 );
v7 = *a2;
}
++v6;
v10 += 4i64;
v9 -= 4i64;
}
while ( v6 != (a2[1] - v7) >> 2 );
}
v13 = maybe_cons((__int64)Block);
sub_7FF666A81700(a1, v13);
return a1;
}
直接进这个函数然后再看看参数指向了哪里.
它让我意识到我犯了一个错误,不是IDA处理错误, 这个4, 3, 2, 1不是指针指向的地方错了,而是真的就是4321,和输入相关,我们把输入变成987123试试
奇怪,这次是数字了,但是你发现上一张图的字符4321下面也有数字4321,而且正好都是逆序的, 我猜想...
ord('a') - ord('0') => 0x31
这个结果印证了我的猜想,这中间某步应该是把输入都减掉'0'的ascii,应该是要把数字字符转数字. 这样可以猜测输入要求上是数字.(话说这也太巧了,我要是没输入abcd, 也就不出1234,那我是不是也就不会走这儿的弯路..离谱)
那对于数字的处理就好猜了,无非是运算嘛,我们直接跑完这个函数, 看看返回值是啥样的
FF0(987123) == 974411817129, emm 这啥也看不出来,我们输入小一点
FF0(15) == 225, 袜, 这不一眼平方么, 上面的987123的平方也印证了这个结果.
我们发现FF0用的是三个参数, 第一个参数是返回值,第二个参数是底数,第三个参数发现正好是2, 这样就可以猜出来第三个应该是指数, 那9A0是干嘛的?
此时v10存的是一个15^2 == 225, 而A90又把v10传进去了,我们看一眼124是4,运算结果是900, 那么结果很明了了:
FF0<=>pwr(res, p, q) : res = p^q
A90<=>mul(res, x, y) : res = x*y
把三个输入都做了这样的操作后,我们来到这里, LABEL_47是win后面的,换言之就是不能跳, 动调到这也明白了,inputi[0]是起始地址,inputi[1]是结尾地址, v25是数字长度(一个数字占4), 这里大概就是验证的一部分了, 至少我输入15 20 25 是过不去这里的.读代码之后知道这里是相当于一个格式验证, 第一个数不能大于第三个数, 第二个数不能大于第一个数
尝试输入100, 50, 150后成功bypass到下一部分
最后这里v46的值是750000,
这个v56是3375000
3652264, 不是整的数,多少有点奇怪, 动调出550函数是减, 7E0是加, 这应该是个自己实现的高精度类 再继续往下走就直接failed了.我决定直接分析这些函数到底最后对输入做了什么
$(x+y)^3 - 3\times x\times(y^2) - 3\times y\times(x^2) == (z+4)^3 - 12(z^2) - z\times 48 - 22$
最后这些式子化简的约束是x^3+y^3-z^3==42,flag就是三个数字合一起的, 著名的"宇宙终极问题"
知乎问题链接, 把它们按上面的大小排序之后合一起md5就是flag
不过网上的wp都是比赛wp或者干脆是瞎扯淡, 比赛wp往往没有思考的过程和思路,我认为对我没什么帮助
瞎扯淡的wp就是看了别人wp在瞎扯淡, 我很鄙视这种人.抄了flag还不够还非要假装自己会了, 虽然我有的时候也出来抄flag, 但是至少不出来瞎说(
这是一道从凌晨四点做到六点的题,脑子多少有些不灵光了(