逆向攻防世界CTF系列37-crackme

参考https://blog.csdn.net/xiao__1bai/article/details/120230397

image-20241116162958244

nspack的壳,查了一下好像是北斗的一个壳

没找到什么脱壳软件,只能手动脱壳了

手动脱壳的最终要的是ESP定律

ESP定律的原理就是“堆栈平衡”原理

涉及的汇编知识:

call命令:

  1. 向堆栈中压入下一行程序的地址;
  2. JMP 到 call 的子程序地址处。

例如:

00401029 E8 DA240A00 call 004A3508

0040102E 5A pop edx

在执行了00401029以后,程序会将0040102E(下一条指令地址)压入堆栈,然后 JMP 到 004A3508 地址处

RETN命令:

将当前的 ESP 中指向的地址出栈;JMP 到这个地址。

堆栈平衡可以简单的理解为在一个函数的ret之前,中间入栈和出栈的次数要相等(也就是一定要保证在 RETN 这条指令之前,ESP指向的是我们压入栈中的地址)

软件加壳就是隐藏了OEP(或者用了假的OEP), 只要找到程序真正的OEP,就可以立刻脱壳,当程序运行到OEP这个位置,程序控制权会交还给原程序, 一般脱壳的步骤需要寻找这个OEP点

可以看下面这篇文章

手动脱壳教程-CSDN博客逆向基础:软件手动脱壳技术入门_ollydbg脱壳教程-CSDN博客

1.这个是加了 ASPACK 壳程序的入口时各个寄存器的值:(入壳,解压缩)
EAX 00000000
ECX 0012FFB0
EDX 7FFE0304 //堆栈值
EBX 7FFDF000 //堆栈值
ESP 0012FFC4
EBP 0012FFF0
ESI 77F57D70 ntdll.77F57D70
EDI 77F944A8 ntdll.77F944A8
EIP 0040D000 ASPACK.

2.这个是 ASPACK 壳 JMP 到 OEP(Original Enter Point)后的寄存器的值:(出壳,回主程序 OEP)
EAX 004010CC ASPACK.004010CC //保存了当前OEP的值
ECX 0012FFB0
EDX 7FFE0304 //堆栈值
EBX 7FFDF000 //堆栈值
ESP 0012FFC4
EBP 0012FFF0
ESI 77F57D70 ntdll.77F57D70
EDI 77F944A8 ntdll.77F944A8
EIP 004010CC ASPACK.004010CC
除了EIP不同以外,eax 保存当前 OEP 值,其他都一模一样啊!

ESP定律的适用范围是几乎全部的压缩壳,部分加密壳。只要是在JMP到OEP后,ESP=0012FFC4的壳,理论上我们都可以使用

是不是只能下断12FFA4的访问断点?
当然不是,那只是ESP定律的一个体现,我们运用的是ESP定律的原理,而不应该是他的具体数值,不能说12FFA4,或者12FFC0就是ESP定律,他们只是ESP定律的一个应用罢了!

内存断点:当下读写断点时,调试器会修改断点处读写属性,如果程序对此数据读写的话,会产生读写异常,调试器捕捉此异常

内存断点分为:内存访问断点,内存写入断点。

我们知道,在程序运行的时候会有3种基本的状态产生:
读取–>写入–>执行。
004AE242 A1 00104000 mov eax,dword ptr ds:[004AE24C] //004AE24C处的内存读取
004AE247 A3 00104000 mov dword ptr ds:[004AE24C],eax //004AE24C处的内存写入
004AE24C 83C0 01 add eax,1 //004AE24C处的内存执行

  1. 当对004AE24C下内存访问断点的时候,可以中断在004AE242也可以中断在004AE247和004AE24C。(因为访问包含执行、写入、读取)
  2. 当对004AE24C下内存写入断点的时候,只能中断在004AE247。
  3. 当执行004AE24C的时候,只能中断在004AE24C。

其实将这么多我做一个方法小总结:就是对第一次push时的esp地址做硬件断点,因为堆栈平衡,最后esp还会还原到这个地址,我们接下来的jmp就是ope了

光讲有点难懂,直接实操

od直接打开

image-20241116210213706

直接看到亮眼的push,F8一下,发现ESP变化

image-20241116210303591

右键ESP,数据窗口跟随(为了下硬件断点)

image-20241116210539981

46这个地方一下个断点,F9

image-20241116211620135

popfd,看esp又是0019ff70了,再F8

jmp跳出,jmp跳出壳后就是解压缩的代码了,右键分析当前代码把.text.段中数据重新反汇编成汇编代码,同IDA热键C

image-20241116212149325

image-20241116212226543

然后OllyDump出来

image-20241116212714320

image-20241116212729134

扔进IDA

image-20241116204740900

写解密代码,很简单了这里

key = "this_is_not_flag"
enc = [18, 4, 8, 20, 36, 92, 74, 61, 86, 10, 16, 103, 0, 65, 0, 1, 70, 90, 68, 66, 110, 12, 68, 114, 12, 13, 64, 62, 75, 95, 2, 1, 76, 94, 91, 23, 110, 12, 22, 104, 91, 18]

for i in range(len(enc)):
    print(chr(enc[i] ^ ord(key[i % 16])),end='')

flag{59b8ed8f-af22-11e7-bb4a-3cf862d1ee75}

这里解释一下下断点为什么是word?《IDA权威指南中的解释》

在这里插入图片描述

我们之前下断点是46,是两字节的所以是word