BUUCTF 逆向题目 SimpleRev
题目地址:
https://buuoj.cn/challenges#SimpleRev
https://files.buuoj.cn/files/7458c5c0ce999ac491df13cf7a7ed9f1/SimpleRev
首先,查壳
信息:
文件名: H://BUUCTF/SimpleRev/SimpleRev
大小: 13128(12.82 KiB)
操作系统: Ubuntu Linux(ABI: 3.2.0)
架构: AMD64
模式: 64 位
类型: DYN
字节序: LE
使用IDA64打开
F5
unsigned __int64 Decry()
{
char v1; // [rsp+Fh] [rbp-51h]
int v2; // [rsp+10h] [rbp-50h]
int v3; // [rsp+14h] [rbp-4Ch]
int i; // [rsp+18h] [rbp-48h]
int v5; // [rsp+1Ch] [rbp-44h]
char src[8]; // [rsp+20h] [rbp-40h] BYREF
__int64 v7; // [rsp+28h] [rbp-38h]
int v8; // [rsp+30h] [rbp-30h]
__int64 v9[2]; // [rsp+40h] [rbp-20h] BYREF
int v10; // [rsp+50h] [rbp-10h]
unsigned __int64 v11; // [rsp+58h] [rbp-8h]
v11 = __readfsqword('(');
*(_QWORD *)src = 'SLCDN';
v7 = 0LL;
v8 = 0;
v9[0] = 'wodah';
v9[1] = 0LL;
v10 = 0;
text = join(key3, (const char *)v9);
strcpy(key, key1);
strcat(key, src);
v2 = 0;
v3 = 0;
getchar();
v5 = strlen(key);
for ( i = 0; i < v5; ++i )
{
if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
key[i] = key[v3 % v5] + 32;
++v3;
}
printf("Please input your flag:");
while ( 1 )
{
v1 = getchar();
if ( v1 == '\n' )
break;
if ( v1 == ' ' )
{
++v2;
}
else
{
if ( v1 <= '`' || v1 > 'z' )
{
if ( v1 > '@' && v1 <= 'Z' )
{
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
++v3;
}
}
else
{
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
++v3;
}
if ( !(v3 % v5) )
putchar(32);
++v2;
}
}
if ( !strcmp(text, str2) )
puts("Congratulation!\n");
else
puts("Try again!\n");
return __readfsqword(0x28u) ^ v11;
}
这里涉及数据在内存中存储的方式大小端问题。
涉及大小端存储问题,elf文件这种通常使用小端存储,而IDA会把内存中的数据自动转成大端存储,但是有些变量双击过去,在文本视图能直接看到转好的字符串,以key3为例
而有些却不能,这个时候就需要自己把字符串倒过来,比如我们在伪代码看到的 str 是 SLCDN ,可实际用这个字符串的时候应该用 NDCLS 同理,wodah 改成 hadow 。
数据在内存中存储的方式:大端模式与小端模式
所谓的大端模式(Big-endian),是指数据的高字节,保存在内存的低地址中,而数据的低字节,保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;
所谓小端模式(Little-endian), 是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内在的低地址中,这种存储模式将地址的高低和数据位 权有效结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致;
假设一个十六进制数0x12345678
大端的存储方式是:12,34,56,78,然后读取的时候也是从前往后读
小端的存储方式是:78,56,34,12,然后读取的时候是从后往前读取
我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
src = 'SLCDN' 则 src 小端存储 = 'NDCLS'
v9[0] = 'wodah' 则 v9[0] 小端存储 = 'hadow'
key1 = 'ADSFK'
key3 = 'kills'
text = join(key3, (const char *)v9) = 'killshadow'
key = 'ADSFKNDCLS'
#include <stdio.h>
#include <string.h>
int main() {
int v3 = 0;
char key[] = "adsfkndcls";
char str2[] = "killshadow";
int v5 = strlen(key);
for (int v2 = 0; v2 < v5; v2++) {
for (int v1 = 'A'; v1 <= 'z'; v1++) {
if (str2[v2] == (v1 - 39 - key[v3 % v5] + 97) % 26 + 97) {
printf("%c", v1);
++v3;
break;
}
}
}
return 0;
}
key = 'adsfkndcls'
text = 'killshadow'
v5 = len(key)
flag = ''
for v3, text_char in enumerate(text):
for v1 in range(65, 91):
if (v1 - 39 - ord(key[v3 % v5]) + 97) % 26 + 97 == ord(text_char):
flag += chr(v1)
print(flag)
运行结果为:KLDQCUDFZO
flag{KLDQCUDFZO}