目录
1.看位数
2.ida64打开
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int i; // [rsp+8h] [rbp-68h]
int j; // [rsp+Ch] [rbp-64h]
__int64 flag[6]; // [rsp+10h] [rbp-60h] BYREF
__int64 flag_encrypted[6]; // [rsp+40h] [rbp-30h] BYREF
flag_encrypted[5] = __readfsqword(0x28u);
puts("Let us play a game?");
puts("you have six chances to input");
puts("Come on!");
memset(flag, 0, 40);
for ( i = 0; i <= 5; ++i ) // 输入六次,每次指定的是四个字节,
{
printf("%s", "input: ");
a2 = (flag + 4 * i); // 感觉这个char**有误导,应该是当一级指针用
__isoc99_scanf("%d", a2); // 应该是输入的数字赋给flag,一共6个int32四字节,还剩余24字节
}
memset(flag_encrypted, 0, 40);
for ( j = 0; j <= 2; ++j ) // 进行三次循环即可获取上方输入的6个四字节数据
{
high_dword = flag[j]; // 分别获取高四位和低四位
low_dword = HIDWORD(flag[j]);
a2 = &dword_601060;
encode(&high_dword, &dword_601060);
LODWORD(flag_encrypted[j]) = high_dword; // 加密后的数据赋给encrypted数组
HIDWORD(flag_encrypted[j]) = low_dword;
}
if ( cmpare(flag_encrypted, a2) != 1 ) // 猜测是比较函数,但是只有一个形参却输入了两个参数
{
puts("NO NO NO~ ");
exit(0);
}
puts("Congratulation!\n");
puts("You seccess half\n");
puts("Do not forget to change input to hex and combine~\n");
puts("ByeBye");
return 0LL;
}
3.程序逻辑
flag是连续输入6次,每次4个字节,一共24字节,这里a2可能识别为二级指针,有点误导,实际上是当作一级指针使用,可以按键盘上\隐藏掉类型提示
由于flag是int64类型的数组,后续每次取flag[i]的高四位和低四位进行加密处理
加密之后的字符串有一个比较操作,在这个比较函数里面可以通过数学关系求出加密后的字符串值
4.关键函数
encode函数:
其中形参a2是dword类型,原始数据位于601060,转换后如下:
这个加密并不复杂,只需要将+号改为-号,调整语句顺序即可
但是v5循环后的终值要作为解密函数循环的初始值
我们需要动态调试,得到v5=0x62F35080
cmpare函数可以解得加密后的数组的值为:
a[0] = -548868226;
a[1] = 550153460;
a[2] = 3774025685;
a[3] = 1548802262;
a[4] = 2652626477;
a[5] = -2064448480;
5.解题脚本
#include<stdio.h>
void decode(unsigned int* high, int* a2)
{
unsigned int v3; // [rsp+1Ch] [rbp-24h]
unsigned int v4; // [rsp+20h] [rbp-20h]
int v5; // [rsp+24h] [rbp-1Ch]
unsigned int i; // [rsp+28h] [rbp-18h]
v3 = *high; // 高四字节
v4 = high[1]; // 低四字节
v5 = 0x62F35080; //v5初值即原来的循环终值,要动态调试才可以得到
for (i = 0; i <= 0x3F; ++i) // 加密循环,解密把顺序调换,+换成-即可,但是要注意v5的初始值
{
v4 -= (v3 + v5 + 20) ^ ((v3 << 6) + a2[2]) ^ ((v3 >> 9) + a2[3]) ^ 0x10;
v3 -= (v4 + v5 + 11) ^ ((v4 << 6) + *a2) ^ ((v4 >> 9) + a2[1]) ^ 0x20;
v5 -= 1166789954;
}
*high = v3;
high[1] = v4;
}
int main()
{
__int64 tmp;
int a[7];
int a2[4] = { 2,2,3,4 };
a[0] = -548868226;
a[1] = 550153460;
a[2] = 3774025685;
a[3] = 1548802262;
a[4] = 2652626477;
a[5] = -2064448480;
a[6] = 0; //补零保证数组不越界
for (int i = 0; i < 3; i++)
{
tmp ^=tmp ;
decode((unsigned int*)&a[i * 2], a2);//一次解密八个字节
tmp += a[i * 2]; //获取高四字节
tmp = tmp << 32; //每次获取八字节
tmp += a[i * 2 + 1]; //获取低四字节
printf("%llx", tmp); //必须是%llx
}
return 0;
}
得到flag{re_is_great!}
6.注意事项
直接用puts输出解密后的字符串是不行的,因为有一部分字节值为00
如果使用逐字节输出也是行不通的,得到的是错误答案
根据题目提示,要转成16进制输出,再用16进制转字符串