BUUCTF 逆向题目 [GXYCTF2019]luck_guy
题目地址:
https://buuoj.cn/challenges#[GXYCTF2019]luck_guy
https://files.buuoj.cn/files/c4ae5d67227e2354c3b512d6bd8b180a/attachment.zip
首先,查壳
信息:
文件名: H:/第七届“强网杯”全国网络安全挑战赛/BUUCTF/[GXYCTF2019]luck_guy/luck_guy
大小: 9176(8.96 KiB)
操作系统: Ubuntu Linux(16.04.12,ABI: 2.6.32)
架构: AMD64
模式: 64 位
类型: EXEC
字节序: LE
使用IDA64打开文件
主程序main
关键函数patch_me
关键函数getflag
unsigned __int64 get_flag()
{
unsigned int v0; // eax
int i; // [rsp+4h] [rbp-3Ch]
int j; // [rsp+8h] [rbp-38h]
__int64 s[5]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v5; // [rsp+38h] [rbp-8h]
v5 = __readfsqword(0x28u);
v0 = time(0LL);
srand(v0);
for ( i = 0; i <= 4; ++i )
{
switch ( rand() % 200 )
{
case 1:
puts("OK, it's flag:");
memset(s, 0, sizeof(s));
strcat((char *)s, f1);
strcat((char *)s, &f2);
printf("%s", (const char *)s);
break;
case 2:
printf("Solar not like you");
break;
case 3:
printf("Solar want a girlfriend");
break;
case 4:
strcpy((char *)s, "icug`of\x7F");
strcat(&f2, (const char *)s);
break;
case 5:
for ( j = 0; j <= 7; ++j )
{
if ( j % 2 == 1 )
*(&f2 + j) -= 2;
else
--*(&f2 + j);
}
break;
default:
puts("emmm,you can't find flag 23333");
break;
}
}
return __readfsqword(0x28u) ^ v5;
}
flag为f1+f2,f1已知为GXY{do_not_,所以需要求f2 switch case需要的顺序为:case 4、case 5、case 1
s = 9180147350284624745LL; ,按H转为十六进制为0x7F666F6067756369LL;,使用时需要逆序
flag就是由f1和f2组成,f1已经告诉GXY{do_not,现在只需要求f2就行。
case4给f2赋值
case5,对f2进行处理
所以顺序是: case4>case5>case1
这里涉及数据在内存中存储的方式大小端问题。
涉及大小端存储问题,elf文件这种通常使用小端存储,而IDA会把内存中的数据自动转成大端存储,但是有些变量双击过去,在文本视图能直接看到转好的字符串,以f1为例
而有些却不能,这个时候就需要自己把字符串倒过来,比如我们在伪代码看到的 7F666F6067756369 ,可实际用这个字符串的时候应该用 69637567606F667F。
规律:
IDA中赋值__int64是大端,双引号包裹字符串char是小端。
大端
小端
数据在内存中存储的方式:大端模式与小端模式
所谓的大端模式(Big-endian),是指数据的高字节,保存在内存的低地址中,而数据的低字节,保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;
所谓小端模式(Little-endian), 是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内在的低地址中,这种存储模式将地址的高低和数据位 权有效结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致;
假设一个十六进制数0x12345678
大端的存储方式是:12,34,56,78,然后读取的时候也是从前往后读
小端的存储方式是:78,56,34,12,然后读取的时候是从后往前读取
我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
继续分析代码
判断奇偶的程序,偶数就每个减1,奇数就每个都减2
编写代码
flag = 'GXY{do_not_'
f2 = [0x7F, 0x66, 0x6F, 0x60, 0x67, 0x75, 0x63, 0x69][::-1]
for i, value in enumerate(f2):
flag += chr(value - (2 if i % 2 == 1 else 1))
print(flag)
flag{do_not_hate_me}