- AndroidSec
- 1. Dalvik下DexClassLoader动态加载关键函数链(基于Android4.4)
- 2. Art下DexClassLoader动态加载关键函数链(基于Android8.0)
- 3. Art下InMemoryDexClassLoader动态加载关键函数链(基于Android8.0)
- 4. Dalvik下RegisterNatives动态注册关键函数链(基于Android4.4)
- 4. Art下RegisterNatives动态注册关键函数链(基于Android8.1)
- 2020-07-08 新增dex2apk.py
- 2020-09-19 将自己之前改的的DumpApkInfo工具引入
- 2020-10-09 增加unicorn trace arm64的基本代码
- 2020-10-10 增加一个byte数组转hexString的dex
- 2020-10-31 增加hook_init_array代码
- 2020-11-02 增加hook_constructors代码
- 2021-03-31 增加主动获取context方法
- 2021-04-06 增加hook RegisterNative函数的脚本
- 2021-04-14 增加在native层遍历HashMap代码
- 2021-04-17 get到一个新姿势:更改进程名
- 2021-04-24 又找到一个anti-frida的方式
- 2021-05-23 frida hook enum class 的方式
- 2021-06-03 frida 无法hook上函数总结
- 2021-07-28 将注册的类加入双亲委派链
- 2021-11-10 增加两个打印java调用栈的frida脚本
2020-07-08 新增dex2apk.py
一个用于将脱壳下来的众多dex重新组装成apk的脚本,免去Android安全测试人员在逆向过程中不停grep
的操作
将重新组合的apk
拖入jadx
和jeb
,展示结果如下:
DumpApkInfo可以用来dump加壳信息、签名信息、APK包名等等功能,具体参见子模块readme
2020-10-09 增加unicorn trace arm64的基本代码
大概效果如下
基于unicorn和capstone来trace函数执行流程并记录寄存器信息,具体自己看代码吧,只是一个demo
2020-10-10 增加一个byte数组转hexString的dex
手动封装了okio.ByteString
的函数,并打包成dex,避免frida在hook APP时无法使用ByteString
的转hex方法,frida使用方式
首先将dex push进/data/local/tmp/
目录下,然后chmod
给予dex
执行权限,frida调用时
Java.perform(function (){
var okio = Java.openClassFile("/data/local/tmp/okio.dex")
okio.load()
var ByteString = Java.use("com.Simp1er.okio.ByteString")
ByteString.$new(key_bytes).hex()// 其实接下来就是ByteString的函数调用了
})
参考: ByteString.java
2020-10-31 增加hook_init_array代码
通过hooklinker
的call_array
函数,hook得到init_array
地址,在Android8.1 64位程序和32为程序上都测试成功,其他的请自己测试更改。
2020-11-02 增加hook_constructors代码
通过hooklinker
的async_safe_format_log
函数,hook得到init_array
以及.init_proc
地址,在Android8.1 64位程序和32为程序上都测试成功,其他的请自己测试更改。
64位效果如下:
32位效果如下:
2021-03-31 增加主动获取context方法
利用ActivityThread
的单例模式获取到应用的上下文Context
用于后续利用
2021-04-06 增加hook RegisterNative函数的脚本
由于jni函数无论是动态注册或者静态注册的函数都会在加载过程中都会调用RegisterNative
函数注册JNI
函数最终函数地址,因此可以通过hook
RegisterNative
函数获取JNI
函数最终地址以及函数实现所在模块,最终效果如下:
注意:仅在Android 8.1.0_r1
下测试成功,其他版本可能失效
20210730补充:更新由于可能存在匿名内存块的情况导致无法通过ModuleMap
找到相应模块的情况处理
2021-04-14 增加在native层遍历HashMap
代码
在native
层遍历HashMap
中key
和value
类型都为String
的脚本
传入一个JNIEnv
和HashMap
的对象即可
最终效果如下
xxx => azU7Bc002xAAJ9QEYW1mGJrO8f0Ed9QH0FqzYdOlL8/Md/QHpENnwiLJhCBZB5mhQtIXdU8Pnw43BRB7hD6QY3VDdZfUF9QH1BfUB9
bbbb => HHnB_FCRa80QdwWegx+jn98jVfguVXqGwR3kh9ROtBHavXaYZV+qLX+lUnG4LVQfyqsJ/zFo0JH2gRVVSi98GPkuj9GWADR18oS+VyJ2XhLUQYev/wQDCMFWSAYaGABE2SOBT
cccc => JAE5zHBA7W55NB1VcTLaT8wI/An8Ae8A+wn5Gvsa7wj6CPQN/Qv6CPg=
dddd => hwIABwRLPF9s5QJ40ATaaW1cNymwhLCe
android.os.Process
中的setArgV0()
函数可用于改变APP
的进程名。感谢卓桐大佬!!Android修改进程名
注意:这里由于Java.lang.Process
类是默认导入的包,因此在使用时需要单独import
导入android.os.Process
类。
import android.os.Process;
try {
// https://bbs.pediy.com/thread-253676.htm
Method setArgV0 = Process.class.getDeclaredMethod("setArgV0", String.class);
setArgV0.setAccessible(true);
setArgV0.invoke(null,"com.tencent.mm");
} catch (Throwable e) {
// java.lang.NoSuchMethodException: setArgV0 [class java.lang.String]
e.printStackTrace();
Log.i("nicai", "onCreate: ");
}
这里测试APP
的包名为com.test.changeprocessname
但是当使用ps -e
命令查看进程会发现找不到这个进程,最终效果如下
frida在注入进程后如果脚本中使用registerClass
这个API
,在内存空间中会出现frida
为前缀的vdex/odex
,即使frida
退出后,本次进程的内存空间仍旧还存在这样的痕迹。
展示如下图
anti-anti
的方式是在调用registerClass
这个API
之前执行以下语句
Java.classFactory.tempFileNaming.prefix = "onetwothree"
..
Java.registerClass(...)
自定义后效果
当hook
Enum
类时,this
指针就是Enum
类中具体成员的值。
比如我这里写了一个枚举类,代码如下
public enum enumClazz {
OAID("QAID"),TOKEN("TOKEN"), VIP("1");
private String value;
enumClazz(String value){
this.value = value;
}
public String getValue(){
return this.value;
}
public void setValue(String value){
this.value = value;
Log.e("enum", "className: " + this );
}
}
在调用时使用方法像这样
enumClazz.OAID.setValue("OAID_value");
enumClazz.TOKEN.setValue("TOKEN_value");
enumClazz.VIP.setValue("VIP_value");
此时调用setValue(String)
函数打印出来的日志如下
2021-05-23 16:24:31.258 26207-26207/com.simp1er.enumdemo E/enum: className: OAID
2021-05-23 16:24:31.262 26207-26207/com.simp1er.enumdemo E/enum: className: TOKEN
2021-05-23 16:24:31.265 26207-26207/com.simp1er.enumdemo E/enum: className: VIP
可以发现其实对应的this
指针就是对应调用的枚举成员。
最终使用frida
进行hook
时,主要代码如下
function hook(){
Java.perform(function(){
var enumClazz = Java.use('com.simp1er.enumdemo.enumClazz')
enumClazz.setValue.implementation = function(){
var value = arguments[0];
console.log('class =>',this,", value =>",value) // this即代表enum类的对象名称。
// console.log('class =>',this.getString())
return this.setValue(value)
}
});
}
效果如下
class => OAID , value => OAID_value
class => TOKEN , value => TOKEN_value
class => VIP , value => VIP_value
但是参考中的this.getString()
方法并没有成功。
参考:https://bbs.pediy.com/thread-258772.htm#enum%E6%B5%8B%E8%AF%95
如果函数hook不到,
- 首先,想想这个函数是不是真的执行到了;
- 其次,想想函数所在类是不是接口类、是不是抽象类(检查方法可以通过Objection-WallBreaker搜索是否有对应实例或者直接看官方文档,如果是抽象类要找对应继承类中是否有对应函数的实现、如果是接口类找对应implements)
- 再次,可以试试
Java.deoptimizeEverything()
函数
-- 记一次hook不到有感
Hooking to Context doesn't work
将通过Java.registerClass() API
注册的类所在的dexclassLoader
加入系统classLoader
双亲委派链中,保证能够Class.forName
函数调用注册的函数时能够成功。
参考今天早上看到的一个repo中Xposed
打印调用栈的方式,写出对应的frida
脚本。