编写一个通用指针搜索器(1)
文章目录
什么是指针搜索?
在这无尽的数字深渊中,吾是探寻者,吾名KK,内存的支配者,数据的操纵者。在寂静的虚空中,我听见了那二进制的低语,它们在呼唤我,渴求着与我融合。
觉醒之刻已然降临,逻辑之门洞开,真理在我面前坠入了完美的无缪境界。在我的意志下,无形的数据扭曲,形成了一道道力量的漩涡,现在,就在这一刻,显现吧!
编织,编织,再编织,用我内存中的崩坏之力,那无可匹敌的崩坏之力,让万象化为虚无,从深渊的边缘喷薄而出。这不仅是技术的巅峰,这是人类智慧与意志的最强大展现。
这不是简单的攻击,这是对内存的终极掌控,这就是最高形态的Hack,这就是——PointerSearch-X !!
什么是指针搜索?(正经脸)
ASLR导致程序内存地址在启动程序时始终不同。所谓的“静态”地址是相对于程序代码(BinaryFile)的地址。有了静态地址,一旦找到它,你就可以稳定计算出这个地址,因为加载程序(BinaryFile)的地址很容易找到。不幸的是,并非所有感兴趣的内存都是“静态的”,因为这些要么需要代码黑客(通常称为ASM HACK),要么需要指针链(找到此链的过程通常被称为指针搜索)。
指针搜索通常被用于自动化寻找较为复杂的指针链,对于很简单的指针链,只需要调试器就可以找到了。当然,指针搜索经常也适用于那些无法使用调试器的场景。
指针链一般通常被表示为:[[[[[Main+0xdf18] +0x190] +0xc8] +0xc8] ...]
类似的东西。Main
表示静态模块(程序自身和自身加载的动态库)。
在 PointerSearch-X 中表示为 Main+57112@400@200@200...
。
以下文章将讲解如何非常粗暴的实现指针搜索。
以下的代码 完全不考虑性能和设计模式
因为性能优化/设计模式的种类有太多,毕竟一千个人眼中有一千个哈姆雷特,这篇文章的目标是简单,实现目标为主。
|
|
它的 maps 类似于这样
|
|
前两列数字(55fff3d0c000-55fff3d12000)代表开始和结束地址,中间表示权限(r–p),最后一列表示具体信息(/home/ubuntu/hello/target/release/hello),我们只需要关心这些。
编写一个简单的解析器:
|
|
内存扫描
内存扫描的逻辑实际上非常简单,首先我们需要读取内存,然后就是遍历数组,得益于linux的procfs,只需要读取 /proc/pid/mem
这个文件就行了。
|
|
我们只需要遍历每个可读的内存范围,target_value 是目标值,类型是i32,4字节。现代程序中几乎都是小端序并且内存对齐的,所以完全可以setp_by 4,然后这四个字节转为i32如果等于target_value,则把结果储存到result_addr中。
实现代码:
|
|
后续过滤result_addr中的地址即可。
实际上关于内存扫描的内容到这里就已经足够了,已经知道了1+1=2,那么2-1=多少,应该不用我多说了。
PS: 这里 [vvar]
[vdso]
[vsyscall]
三个区域虽然可读,但几乎绝对不会是我们需要的。
指针扫描
在64位程序中,指针实际上只是内存中的8个字节,首先我们应该知道程序中有哪些指针,我们需要获取每个指针的地址以及指针所保存的引用地址。
那么如何判断内存中某8个字节是否是一个指针?
最简单的办法,判断这个地址是不是在 pages
范围中,如果是,那它就很有可能是一个指针。
前面提到,我们需要创建一个索引 Map
,然后最大偏移范围内的所有指针值,BTreeMap
对于这个需求几乎是完美用例。
代码实现起来非常简单:
|
|
得到了指针之间的引用关系,扫描指针链非常容易:
|
|
从Map
中遍历saddr
到 saddr + range
范围内的所有内容,然后递归重复。
saddr
为指针链的开始地址0x7ffcf116c4c0
,怎么得到的?前面测试程序中已经直接打印出来了,不过别忘了前面的map储存了程序中所有指针,在未知的情况下我们可以遍历某个范围内的任意指针作为saddr。
taddr
为指针链的结束地址0x560d66743ba0
,也就是目标地址,它也是测试程序中打印出来的,当然也可以通过内存扫描得到。
range
为偏移范围32
,也就是在 BB
结构体中,最大可以偏移32的范围。
depth
为深度,只用做记录当前深度。
max_dep
为最大深度3
,最大指针链长度不会超过3。
运行结果 +0x0=0x7ffcf116c4c0->0x560d66743bc0+0x8=0x560d66743bc8->0x560d66743ba0 ...
,
它从0x7ffcf116c4c0
开始,读取了一个指针得到0x560d66743bc0
,然后+0x8
,也就是从BB结构体中 +8
的位置 0x560d66743bc8
,再读取一个指针最后得到0x560d66743ba0
。
对比前面测试程序的输出 0x7ffcf116c4c0->0x560d66743bc0->0x560d66743ba0
这和预期中一样!!!
当然,这个程序问题很大,例如性能非常差,输出结果后面会乱一大坨,不过它只是作为一个例子学习,只要包含正确结果就够了,至于如何修复/优化,就交给读者了。
完整代码:
|
|
文章作者
上次更新 2023-06-27