是elf格式的64位程序,无壳
int64指的是64位有符号整型
fastcall是一种函数调用规定
按理来说main函数的写法上是只能有2个参数的,不知道为什么这里有3个
初看main函数的逻辑是输入的第一个参数和"zer0pts{CENSORED}"一样就成功了
所以直接把"zer0pts{CENSORED}"提交,错了。
看一下这些函数,名字格式是sub_xxx的函数时ida没能识别的函数,后面的数字是函数的起始地址
这里可以看到,init函数在main函数之前调用了,进去看看init函数干了什么
signed指的是有符号数,不写的情况下默认是signed。
db指的是1个字节,dw:2, dd:4, dq:8。
offset指的是偏移量
按道理来讲,&off_200DF0的值应该是这个变量的地址,也就是上图中的0x200DF0
所以v4>0。
这里前面是把(&funcs_889 + i)解引用,解引用之后是一个函数地址,然后再把这个函数地址强制类型转化成函数,后面跟的(a1, a2, a3)是这个函数的参数。
因为这里for循环给的条件是i != v4,所以for循环里面不会调用sub_6A0,只会调用6E0和795这两个函数,6E0函数里面啥也没有,现在进到795里面去看一下。
可以看到,它把off_201028的值设为了sub_6EA函数的地址,我们来看看off_201028里面存的原本是什么东西。
可以看到off_201028是函数表中的数据,原本存的是strcmp函数的地址,这么说来,main函数中,每次调用strcmp函数的时候,实际上调用的其实是sub_6EA函数,接下来好好看看sub_6EA中的内容。
首先i获取了输入的flag字符串的长度,然后把字符串的每8个字节作为一个整体,减去qword_201060数组中对应的8个字节,最后再执行qword_201090函数,返回结果,qword_201090的值在795函数中被赋值成了&strcmp,所以在减完之后的字符串要与"zer0pts{CENSORED}"相等,于是可以写这样一个脚本
#include <iostream>
using namespace std;
int main()
{
char p[] = "zer0pts{********CENSORED********}";
uint64_t k[5] = {0, 0x410A4335494A0942, 0x0B0EF2F50BE619F0, 0x4F0A3A064A35282B, 0};
for (int i = 0; i < 4; i++)
{
*(uint64_t *)&(p[i * 8]) += k[i];
}
cout << p;
return 0;
}
//flag{l3ts_m4k3_4_DETOUR_t0d4y}
得到flag