EZXOR分析

我们拿到题后先运行一遍看看

Ok,上调试器,这里选择使用x64dbg动态调试.

此时,我们是在ntdll中,并非用户模块中,这是因为设置里勾选了系统断点

我们再次点击运行就可以中断到程序入口点了AddressOfEntryPoint + ImageBase(未开随机基址的情况下) 具体可参考PE结构

我们直接搜字符串,看看有没有可以利用的字符串来帮助我们定位到关键点

发现这正是程序输出的,我们双击它

调试器已经帮助我们转到了这里,把这个字符串的地址给了rax,再赋给rcx

由于是64位程序,我们可以得知是fastcall调用约定,在Windows系统上rcx就是第一个参数,rdx是第二个参数r8 r9分别存储第三第四个参数,多余的会被压入栈中

由此断定,下面那个call就是printf函数

进入这个call,我们直接给它一个标签

为了防止标签冲突,我们在前面加个下划线

之后我们按减号键返回

我们再看下面那个lea rax, ds:[0x00007FF70BD71016]

我们发现这个地址里存着%s

字符串以00结尾

疑似是scanf函数

我们在这个call下断点

断下后我们F8单步步过,发现程序阻塞住了, 让我们输入flag, 由此我们断定这个call就是scanf,防止命名冲突,我们给它标签: _scanf

输入flag后单步走到下一个call

观察此时寄存器状态

Rcx也就是第一个参数为我们输入的flag

Edx也就是第二个参数是一个常数

我们通过这个信息,可以判断函数原型为 func(char* arg1, int arg2);

我们跟F7(单步步入)进去看一看,没病走两步~

注意这三条指令,它们把我们的参数1和参数2分别放入了[rbp+0x20]和[rbp+0x28],我们发现第二个参数放进去的是一个字节,由此我们推翻我们之前的定论

函数原型应为:func(char* arg1, unsigned char arg2);

往下走,走过那个jmp后发现是个循环

没错,strlen比较的正是我们第一个参数(我们输入的flag),这个函数返回的数据给了rax寄存器

我们观察此处寄存器状态,发现它把我们flag的每个字节都给al,然后与我们第二个参数进行异或,rcx作为一个迭代计数器(index)但第二个参数为什么变成了6呢?往上翻发现

原来是自己加了自己

由此我们可以推出函数具体做的事情:

Int func(char* a1, unsigned char a2)

{

Int tmp = a2 * 2;// 或a2 + a2;

For(int i = 0; i < strlen(a1); i++)

{

// *(a1 + i) = *(a1 + i) ^ a2;

a1[i] = a1[i] ^ a2;

}

}

此处我们推断的函数不关心返回值

我们可以选择jb 0x00007FF70BD615FE指令的下一行,运行至此

然后一路f8返回出去

我们发现它拿着异或加密完的数据跟另一个数据比较

这个跳转,我们走过去是肯定要失败的,我们既然都弄明白了它的算法,我们又知道异或算法可逆,那我们知道了key,我们只需要把它比较的密文再进行一次异或运算就能拿到正确的flag了,我们直接编写代码

char buffer[255] = "BGUER@}GuYSYEghY5Y^6tY7uYC|{";

for (size_t i = 0; i < strlen(buffer); i++)

{

printf("%c", buffer[i] ^ 6);

}

运行得到flag:DASCTF{As_U_Can_3_X0r_1s_Ez}

posted @ 2022-11-24 12:12  N0t3  阅读(166)  评论(0编辑  收藏  举报