攻防世界-逆向-难度1
根据提示应该是异或加密,找到密文和密钥,再异或回去就得到原文。
参考
https://blog.csdn.net/qq_63699339/article/details/130657034
官方wp
逆向解法
梳理一下程序执行逻辑
1.在main函数之前的init-array段首先执行了sub84A
在 ELF (Executable and Linkable Format) 文件中,Initialization Function Table 是一种特殊的数据结构,用于存储在程序启动时(在 main 函数执行之前)需要调用的一系列初始化函数的地址。这个机制允许程序或库执行必要的初始化任务,例如设置全局变量的初始状态、注册回调函数、初始化硬件设备或执行其他任何启动前准备工作。
如果 sub_840 的地址被包含在 ELF 的 Initialization Function Table 中,那么它会在程序的主逻辑开始执行之前被自动调用。这种自动调用是由程序的启动代码(通常是由编译器和链接器自动生成的一部分)负责的,该代码会遍历 Initialization Function Table 中的所有条目,并按照它们出现的顺序调用每个函数。
2.进行了第一次异或处理
3.再来看main函数,sub_A90
函数是做什么的
4.点进去
5.查一下_cxa_atexit
的资料后就很清楚了,_cxa_atexit在exit函数中注册进程退出回调函数来实现析构
简单理解就是执行exit退出,会调用一个回调函数,这个回调函数就是sub916
6.再回到main函数,流程逻辑就明确了
整个程序对s1进行两次异或,第一次的密钥是(2 * i + 65)
, 第二次的密钥是 s2[i]
,各循环33次
分析程序执行逻辑
ida64打开,字符串表看一下有没有什么线索
猜测这串字符跟flag有关,ctrl+x查看引用
有三个函数引用了,逐个查看
unsigned __int64 sub_84A()
{
signed int i; // [rsp+Ch] [rbp-14h]
unsigned __int64 v2; // [rsp+18h] [rbp-8h]
v2 = __readfsqword(0x28u);
for ( i = 0; i <= 33; ++i )
s1[i] ^= 2 * i + 65;
return __readfsqword(0x28u) ^ v2;
}
unsigned __int64 sub_916()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
v1 = __readfsqword(0x28u);
if ( !strcmp(s1, s2) )
puts("Congratulations!");
else
puts("Wrong!");
return __readfsqword(0x28u) ^ v1;
}
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
signed int i; // [rsp+Ch] [rbp-34h]
char s[40]; // [rsp+10h] [rbp-30h]
unsigned __int64 v6; // [rsp+38h] [rbp-8h]
v6 = __readfsqword(0x28u);
sub_A90(sub_916, a2, a3);
fgets(s, 35, stdin);
for ( i = 0; i <= 33; ++i )
s1[i] ^= s[i];
return 0LL;
}
可以看到对s1(“qasxcytgsasxcvrefghnrfghnjedfgbhn”)处理了两次
for ( i = 0; i <= 33; ++i )
s1[i] ^= 2 * i + 65;
for ( i = 0; i <= 33; ++i )
s1[i] ^= s[i];
由此可得:
s1 = 'qasxcytgsasxcvrefghnrfghnjedfgbhn'
s2=[ 0x56, 0x4E, 0x57, 0x58, 0x51, 0x51, 0x09, 0x46, 0x17, 0x46,
0x54, 0x5A, 0x59, 0x59, 0x1F, 0x48, 0x32, 0x5B, 0x6B, 0x7C,
0x75, 0x6E, 0x7E, 0x6E, 0x2F, 0x77, 0x4F, 0x7A, 0x71, 0x43,
0x2B, 0x26, 0x89, 0xFE, 0x00]
for i in range(0,33):
print(chr(ord(s1[i]) ^ (2 * i + 65) ^ s2[i]), end='')
总结
- ELF文件存在Initialization Function Table 数据结构,里面定义的函数会先于main函数被执行。
- _cxa_atexit是exit执行前的时刻,调用传入的回调函数,实现析构。__cxa_atexit 主要用于C++环境中,以确保全局和静态对象的析构函数能够正确地被调用。