Capstone、Unicorn、Keystone
出自同一个组织之手
三个框架的区别与特点
Capstone
在 2014 年黑帽大会上由新加坡南阳理工大学发布的
Capstone
反汇编引擎框架 官网Capstone
https://github.com/aquynh/capstone- 多平台 win + Mac + Linux
- 多架构 x86 + arm + mips + ppc
- 多语言接口
python 安装pip install capstone
Unicorn
在 2015 年黑帽大会上由新加坡南阳理工大学发布的 CPU 模拟器
Unicorn
CPU 模拟器 官方网站- 支持 Arm,Arm64,X86,等等
- 语言支持 Haskell, Ruby, Python, Java, Go, .NET, Delphi/Pascal & MSVC available.等
python 安装pip install unicorn
Keystone
在 2016 年黑帽大会上由新加坡南阳理工大学发布的, frida 源码中也有使用。
Keystone
新一代汇编框架 官方网站- 支持 Arm,Arm64,X86,等等
- 支持多平台
- 支持多语言
python 安装pip install keystone-engine
- 衍生产品 IDA插件keypatch
实例篇
Keystone
需求:对指定的地址进行反汇编输出
FRIDA 中使用
// addr=地址, number 显示条数
function dis(addr, number){
// 利用 Keystone 驱动读取 汇编指令
for(var i=0; i<number;i++){
var ins=Instruction.parse(addr);
console.log('addr: '+ addr + " --dis: "+ins.toString()); // 指定地址反汇编
addr = ins.next;
// 例子
if(ins.mnemonic.startsWith("bl")){
console.log("this is a function call ins!!")
//hook();
//ins.operands
}
}
}
直接python使用:
官方示例
from capstone import *
CODE = b"\x55\x48\x8b\x05\xb8\x13\x00\x00"
md = Cs(CS_ARCH_X86, CS_MODE_64)
for i in md.disasm(CODE, 0x1000):
print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
解锁反调试
解锁的方法很多,这里只是结合 Capstone 的一个例子
Frida 中有 Capstone 驱动的ArmWriter
, 可以利用其来解锁反调试 官方说明文档 其中可以用putNop
来达到效果对指定的地址写入 nop 指令。当然也可以用 断点指令
putBreakpoint()
等等
这里我们来实战一个,自动检测 Frida 的 bybass。(app附件下载)
- 包名: com.example.test
- 目标: frida 运行程序就会被关闭
- So: libnative-lib.so
分析:
该种情况最常见的就是被kill掉了
导入表中可以看到这些关键词
找到 so 加载中较早的hook点
call_function
就是一个较早的点 稳当call_function("DT_INIT", init_func_, get_realpath());
参数1
: 要执行的函数的类型,参数2
: 当前地址参数3
: so 路径
// 这是视频教程的 Demo,视频apk并不适用
// addr=地址, number 显示条数
function dis(address, number) {
// 利用 Keystone 驱动读取 汇编指令
for (var i = 0; i < number; i++) {
var ins = Instruction.parse(address);
console.log("address:" + address + "--dis:" + ins.toString()); // 指定地址反汇编
address = ins.next;
}
}
//libc->strstr()
function hook() {
//call_function("DT_INIT", init_func_, get_realpath());
var linkermodule = Process.getModuleByName("linker");
var call_function_addr = null;
var symbols = linkermodule.enumerateSymbols();
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
//LogPrint(linkername + "->" + symbol.name + "---" + symbol.address);
// 符号, 导出函数
if (symbol.name.indexOf("__dl__ZL13call_functionPKcPFviPPcS2_ES0_") != -1) {
call_function_addr = symbol.address;
//LogPrint("linker->" + symbol.name + "---" + symbol.address)
}
}
Interceptor.attach(call_function_addr, {
onEnter: function (args) {
var type = ptr(args[0]).readUtf8String();
var address = args[1];
var sopath = ptr(args[2]).readUtf8String();
console.log("loadso:" + sopath + "--addr:" + address + "--type:" + type);
if (sopath.indexOf("libnative-lib.so") != -1) {
var libnativemodule = Process.getModuleByName("libnative-lib.so");
var base = libnativemodule.base;
dis(base.add(0x8D8E).add(1), 10);
var patchaddr = base.add(0x8d96);
// 方法1:
Memory.patchCode(patchaddr, 4, patchaddr => {
var cw = new ThumbWriter(patchaddr);
cw.putNop();
cw = new ThumbWriter(patchaddr.add(0x2));
cw.putNop();
cw.flush();
});
// 方法2:
// console.log("+++++++++++++++++++++++")
// dis(base.add(0x8D8E).add(1), 10);
// console.log("----------------------")
// dis(base.add(0x8E6E).add(1), 10);
// Memory.protect(base.add(0x8E78), 4, 'rwx');//4个字节可写权限
// base.add(0x8E78).writeByteArray([0x00, 0xbf, 0x00, 0xbf]);
// console.log("+++++++++++++++++++++++")
// dis(base.add(0x8E6E).add(1), 10);
}
}
})
}
function main() {
hook();
}
setImmediate(main);
文章app下载:
此处内容需要评论回复后(审核通过)方可阅读。
14 comments
学习了,正好在研究反调试方面
..........
恭喜您的附件成功把评论区变成了垃圾场。
app申请下载 复现学习 谢谢博主
获取APP附件
下载附件学习一下
下载附件复现一下
0000
great!
膜拜学习
下载附件研究一下。
下载附件研究一下。
谢谢
实践下,弄清楚选择0x8D8E和0x8d96地址的原因