【逆向分析】BUUCTF 逆向题目 SimpleRev
2023-12-3 00:3:0 Author: 利刃信安攻防实验室(查看原文) 阅读量:15 收藏

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'

上面的代码是将 key 的字符全部转成小写即 key = 'adsfkndcls'
分析str2

if ( !strcmp(text, str2) ) 由此段代码可知,当 str2 与 text 的字符串相同时,条件满足。这里可以得知,str2 是正确的 flag 经过一些列处理得到的,我们现在只需要把处理过程逆一下,就可以的到正确的 flag
用C++写脚本
#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;}

用Python3写脚本
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}

文章来源: http://mp.weixin.qq.com/s?__biz=MzU1Mjk3MDY1OA==&mid=2247508371&idx=1&sn=383f37e0ae4411d712d267a0a8493e09&chksm=fbfb115ecc8c9848648e9f25dee6388823e2ac7781c453df9cb014aeee6b74ed490151d0b186&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh