吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3282|回复: 17
收起左侧

[CTF] 攻防世界 Reverse Lucknum

  [复制链接]
NightGlow 发表于 2023-7-19 20:03
第一种方法:
  1. 首先将文件拖入IDA 64,查找main函数

  2. 观察发现main函数中存在一个判断。结合右侧“sorry”提示,估计为false。观察主函数汇编。

; Attributes: bp-based frame

; int __cdecl main(int argc, const char **argv, const char **envp)
public main
main proc near ;这两行是对 main 函数的定义,这是程序的入口点。

var_34= dword ptr -34h ;定义了一些局部变量。这些变量存储在栈中,其位置相对于基指针 rbp。
s= byte ptr -30h ;它声明了一个名为 s 的变量,该变量是一个字节大小的内存位置,位于基指针 rbp 下 0x30 字节的位置
var_28= qword ptr -28h ;这个声明定义了一个名为 var_28 的局部变量,它是一个 qword(Quad Word,也就是 8 字节或 64 位)大小的变量,位于基指针 rbp 下 0x28 个字节的位置
var_20= qword ptr -20h ;这个声明定义了一个名为 var_20 的局部变量,它也是一个 qword 大小的变量,位于 rbp 下 0x20 个字节的位置
var_18= qword ptr -18h ;这个声明定义了一个名为 var_18 的局部变量,它也是一个 qword 大小的变量,位于 rbp 下 0x18 个字节的位置
var_10= dword ptr -10h ;这个声明定义了一个名为 var_10 的局部变量,它是一个 dword(Double Word,也就是 4 字节或 32 位)大小的变量,位于 rbp 下 0x10 个字节的位置
var_C= byte ptr -0Ch ;这个声明定义了一个名为 var_C 的局部变量,它是一个 byte(1 字节)大小的变量,位于 rbp 下 0xC 个字节的位置

; __unwind {
push    rbp ;这个指令将当前的基指针 rbp 压入栈中
mov     rbp, rsp ;将当前的栈指针 rsp(指向栈顶的指针)赋值给 rbp。这是为了设置新的栈帧
sub     rsp, 40h ;从栈指针 rsp 中减去 0x40 (也就是64)。在大多数系统中,栈是向下增长的,所以减去一个数实际上是在栈上分配新的空间
mov     rax, 6E30637B67616C66h ;将16进制数6E30637B67616C66移动到rax寄存器中。rax是一个64位的寄存器
mov     rdx, 74406C7574407267h ;将16进制数74406C7574407267移动到rdx寄存器中
mov     qword ptr [rbp+s], rax ;将 rdx 寄存器中的值复制到内存中。内存的地址是通过 rbp(基指针)和 var_28 的偏移量计算得到的
mov     [rbp+var_28], rdx ;将 rdx 寄存器中的值复制到内存中的一个位置,这个位置是由基指针 rbp 和 var_28 的偏移量确定的。在这个例子中,var_28 是一个在之前定义的局部变量
mov     rax, 5F7230665F6E3069h ;将16进制数 5F7230665F6E3069 复制到 rax 寄存器中
mov     rdx, 6D756E5F6B63756Ch ;将16进制数 5F7230665F6E3069 复制到 rax 寄存器中
mov     [rbp+var_20], rax ;将 rax 寄存器中的值复制到内存中的一个位置,这个位置是由基指针 rbp 和 var_20 的偏移量确定的
mov     [rbp+var_18], rdx ;将 rdx 寄存器中的值复制到内存中的一个位置,这个位置是由基指针 rbp 和 var_18 的偏移量确定的
mov     [rbp+var_10], 7D723362h ;将16进制数 7D723362 复制到内存中的一个位置,这个位置是由基指针 rbp 和 var_10 的偏移量确定的
mov     [rbp+var_C], 0 ;将0值移动到内存的一个位置
mov     [rbp+var_34], 0 ;将0值移动到内存的一个位置
lea     rax, [rbp+var_34] ;将rbp+var_34的地址加载到rax寄存器中。lea指令是"load effective address"的缩写,它将源操作数的有效地址加载到目标操作数(这里是rax)中
mov     rsi, rax ;将rax寄存器中的值复制到rsi寄存器
lea     rdi, unk_2004 ;将unk_2004的地址加载到rdi寄存器中
mov     eax, 0 ;将0值移动到eax寄存器中
call    ___isoc99_scanf ;调用函数___isoc99_scanf
mov     eax, [rbp+var_34] ;将内存中的一个值移动到eax寄存器中
cmp     eax, 7 ;比较eax寄存器中的值和7
jnz     short loc_11C5 ;如果上一条cmp指令的结果不为零(也就是eax中的值不等于7),则跳转到loc_11C5
mov                rdi, rax ; s ;这条指令将 rax 寄存器中的值复制到 rdi 寄存器中,注意这里提示了:s 
call        _puts ;这条指令调用了 puts 函数,这是一个 C 标准库函数,用于在控制台上打印一个字符串。在这条指令执行之前,字符串的地址应该已经被放在 rdi 寄存器中,所以这个 puts 调用将打印出存储在 rax 中的地址指向的字符串。

jmp                short loc_11D1 ;这是一个无条件跳转指令,它将控制流跳转到地址 

栈图:

Higher addresses
+---------------------+
|                     | <- Previous stack frame
+---------------------+
|     Return Addr     |
+---------------------+
|       Old rbp       | <- rbp (after "push rbp" and "mov rbp, rsp")
+---------------------+
|                     |
|                     |
|                     |
|                     | <- rsp (after "sub rsp, 40h")
|                     |
|                     |
|                     |
|                     |
+---------------------+
|     var_10 (dword)  | <- rbp - 0x10
+---------------------+
|     var_18 (qword)  | <- rbp - 0x18
+---------------------+
|     var_20 (qword)  | <- rbp - 0x20
+---------------------+
|     var_28 (qword)  | <- rbp - 0x28
+---------------------+
|     s (byte)        | <- rbp - 0x30
+---------------------+
|     var_34 (dword)  | <- rbp - 0x34
+---------------------+
|                     | <- Future stack growth
Lower addresses
  1. 通过观察cmp     eax, 7,我们可以得出,如果当我们输入的值为7时,会跳转出我们想要的答案。放到Linux去进行尝试。

获得了flag为:flag{c0ngr@tul@ti0n_f0r_luck_numb3r}

第二种方法:
  1. 我们观察如果为True 跳转的代码块
    lea     rax, [rbp+s]
    mov     rdi, rax        ; s
    call    _puts
    jmp     short loc_11D1
  2. 发现调了一个_puts函数,看看里面会显示点什么。发现这段代码会将 char s里面的字符打印出来。推测在前面的几串hex有关。
    通过编写代码进行尝试:
hexStrings = ["6E30637B67616C66","74406C7574407267",\
    "5F7230665F6E3069","6D756E5F6B63756C","7D723362"]  # 定义一个存储hex的列表 

flagStrings = [] # 定义一个空列表

for i in hexStrings:
    byte_data = bytes.fromhex(i) # bytes.fromhex(string): 这个方法是 bytes 类型的一个类方法,它接受一个包含十六进制数字的字符串,并返回一个字节串对象,这个字节串对象的内容是根据字符串中的十六进制数字解析得到的
    ascii_str = byte_data.decode('ASCII') #这个方法是 bytes 类型的一个实例方法,它用来将字节串解码成字符串,这里我们转换成ASCII。
    flagStrings.append(ascii_str[::-1])
print("".join(flagStrings))

结果flag为:flag{c0ngr@tul@ti0n_f0r_luck_numb3r}

第三种方法:

直接用IDA PRO的F5反编译查看

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int v4; // [rsp+Ch] [rbp-34h] BYREF
  char s[48]; // [rsp+10h] [rbp-30h] BYREF

  strcpy(s, "flag{c0ngr@tul@ti0n_f0r_luck_numb3r}");
  v4 = 0;
  __isoc99_scanf(&unk_2004, &v4);
  if ( v4 == 7 )
    result = puts(s);
  else
    result = puts("sorry!");
  return result;
}

flag为:flag{c0ngr@tul@ti0n_f0r_luck_numb3r}

免费评分

参与人数 10威望 +1 吾爱币 +27 热心值 +8 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
XDev136 + 1 我很赞同!
niuma114514 + 1 鼓励转贴优秀软件安全工具和文档!
jy02856914 + 1 + 1 我很赞同!
lixingchen + 1 + 1 虽然看不懂,但是感觉好厉害的样子
mobei12138 + 1 + 1 我很赞同!
wobushi + 1 + 1 我很赞同!
fulingjie + 1 我很赞同!
zz564335 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ncww 发表于 2023-7-21 10:45
能有个详细解说就好了,我们这些小白看的头晕
se34218 发表于 2023-7-21 12:08
xiong1017 发表于 2023-7-21 12:11
路过并看了一眼 发表于 2023-7-20 22:09
哇,太棒啦
Do_zh 发表于 2023-7-21 08:43
这是个说明书吧。
marco527 发表于 2023-7-21 08:49
看不懂,不过还是要支持一下楼主!
ydna1234 发表于 2023-7-21 08:58
LZ最好能给个说明,对于某些小白来说(比如我)
wzbAwxl 发表于 2023-7-21 09:25
看不懂啊 呜呜呜
exiaowe 发表于 2023-7-21 10:20
好,就是看不懂而已
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 16:16

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表