通用tools

1、hook相关

1.1、获取进程名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 枚举手机上的所有进程 & 前台进程
import frida

# 获取设备信息
rdev = frida.get_remote_device()
# print(rdev)

# # 枚举所有的进程
# processes = rdev.enumerate_processes()
# for process in processes:
# print(process)

# 获取在前台运行的APP
front_app = rdev.get_frontmost_application()
print(front_app)

1.2、端口转发

1
2
3
4
import subprocess

subprocess.getoutput("adb forward tcp:27042 tcp:27042")
subprocess.getoutput("adb forward tcp:27043 tcp:27043")

1.3、python_hook

1.3.1、手动hook_附加的形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import frida
import sys

# 连接手机设备
rdev = frida.get_remote_device()

session = rdev.attach("车智赢+") # app名字

scr = """
Java.perform(function () {

// 包.类
var SecurityUtil = Java.use("com.autohome.ahkit.utils.SecurityUtil");

SecurityUtil.encodeMD5.implementation = function(str){
console.log("明文:",str);
var res = this.encodeMD5(str);
console.log("md5加密结果=",res);
return "305eb636-eb15-4e24-a29d-9fd60fbc91bf";
}

});
"""

script = session.create_script(scr)


def on_message(message, data):
print(message, data)


script.on("message", on_message)
script.load()
sys.stdin.read()

1.3.2、自启动hook_spawn形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import frida
import sys

rdev = frida.get_remote_device()
pid = rdev.spawn(["com.hupu.shihuo"])
session = rdev.attach(pid)

scr = """
Java.perform(function () {
var UpdateDialog = Java.use('com.azhon.appupdate.dialog.UpdateDialog');
UpdateDialog.show.implementation = function(ctx){
//this.show();
}
});
"""
script = session.create_script(scr)


def on_message(message, data):
print(message, data)


script.on("message", on_message)
script.load()
rdev.resume(pid)
sys.stdin.read()

1.4、反调试

1.4.1、Frida反调试

app运行时,都加载了哪些so文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import frida
import sys

rdev = frida.get_remote_device()
pid = rdev.spawn(["com.hupu.shihuo"])
session = rdev.attach(pid)

scr = """
Java.perform(function () {

var dlopen = Module.findExportByName(null, "dlopen");
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");

Interceptor.attach(dlopen, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
console.log("[dlopen:]", path);
},
onLeave: function (retval) {

}
});

Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
console.log("[dlopen_ext:]", path);
},
onLeave: function (retval) {

}
});


});
"""
script = session.create_script(scr)


def on_message(message, data):
print(message, data)


script.on("message", on_message)
script.load()
rdev.resume(pid)
sys.stdin.read()

1.4.2、strongR-frida-android反调试

有些app运行时会监测frida的相关特征,监测到之后就会直接闪退。

此时我们可以尝试使用strongR-frida-android来绕过监测。

1

将此处的frida-server解压,并上传到手机的 /data/local/tmp/目录,然后赋予可执行权限chmod 755 文件。(过程同之前的frida-server的处理)

1.5、hook_Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import frida
import sys

rdev = frida.get_remote_device()
session = rdev.attach("识货")

scr = """
Java.perform(function () {
var TreeMap = Java.use('java.util.TreeMap');
var Map = Java.use("java.util.Map");

TreeMap.put.implementation = function (key,value) {
if(key=="data"){
console.log(key,value);
}
var res = this.put(key,value);
return res;
}
});
"""
script = session.create_script(scr)


def on_message(message, data):
print(message, data)


script.on("message", on_message)
script.load()
sys.stdin.read()

1.6、hook_StringBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# com.che168.autotradercloud

import frida
import sys

rdev = frida.get_remote_device()
session = rdev.attach("识货")

scr = """
Java.perform(function () {
var StringBuilder = Java.use("java.lang.StringBuilder");

StringBuilder.toString.implementation = function () {
var res = this.toString();
console.log(res);
return res;
}

});
"""
script = session.create_script(scr)


def on_message(message, data):
print(message, data)


script.on("message", on_message)
script.load()
sys.stdin.read()

1.7、hook拦截器

1.7.1、hook_所有注册拦截器_python版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import frida
import sys

rdev = frida.get_remote_device()
session = rdev.attach("识货")

scr = """
Java.perform(function () {
var a = Java.use("cn.shihuo.modulelib.startup.core.c.a");
a.intercept.implementation = function (chain) {
var req = chain.request();

var httpUrl = req.url().toString();
if( httpUrl.indexOf("https://sh-gateway.shihuo.cn/v4/services/sh-goodsapi/app_swoole_shoe/preload/single") != -1 ){
console.log('执行前',httpUrl);
}

var res = this.intercept(chain);
return res;
}

});
"""
script = session.create_script(scr)


def on_message(message, data):
print(message, data)


script.on("message", on_message)
script.load()
sys.stdin.read()

1.7.2、hook_所有注册拦截器_js版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Java.perform(function () {
var Builder = Java.use('okhttp3.OkHttpClient$Builder');

Builder.addInterceptor.implementation = function (inter) {
//console.log("实例化:");

console.log(JSON.stringify(inter) );
//console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
return this.addInterceptor(inter);
};
})

// frida -Uf com.hupu.shihuo -l 6.hook.js
// frida -Uf com.hupu.shihuo -l 6.hook.js -o all_interceptor3.txt

1.8、hook_base64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import frida
import sys

rdev = frida.get_remote_device()
session = rdev.attach("识货")

scr = """
Java.perform(function () {
var Base64 = Java.use("android.util.Base64");
// 重载
Base64.encodeToString.overload('[B', 'int').implementation = function (bArr,val) {
var res = this.encodeToString(bArr,val);
console.log("加密了-->",res);
return res;
}
});
"""
script = session.create_script(scr)


def on_message(message, data):
print(message, data)


script.on("message", on_message)
script.load()
sys.stdin.read()

1.9、调用栈输出

console.log(Java.use(“android.util.Log”).getStackTraceString(Java.use(“java.lang.Throwable”).$new()));

1.10、hook内存中的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 去内存中中 libkeyinfo.so  getByteHash
var addr = Module.findExportByName("libkeyinfo.so", "getByteHash");
console.log(addr); //0xb696387d



Interceptor.attach(addr,{
onEnter:function (args){
this.x1 = args[2];
},
onLeave:function(retval) {
console.log("--------------------")
console.log(Memory.readCString(this.x1));
console.log(Memory.readCString(retval));
}

})

// frida -U -f com.achievo.vipshop -l 4.hook_getByteHash.js 重启app+hook(出问题)
// frida -UF -l 4.hook_getByteHash.js 手动启动+hook

1.10.1、延迟hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function do_hook() {

var addr = Module.findExportByName("libkeyinfo.so", "getByteHash");
console.log(addr); //0xb696387d


Interceptor.attach(addr, {
onEnter: function (args) {
this.x1 = args[2];
},
onLeave: function (retval) {
console.log("--------------------")
console.log(Memory.readCString(this.x1));
console.log(Memory.readCString(retval));
}

})

}

function load_so_and_hook() {
var dlopen = Module.findExportByName(null, "dlopen");
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");

Interceptor.attach(dlopen, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
// console.log("[dlopen:]", path);
this.path = path;
}, onLeave: function (retval) {
if (this.path.indexOf("libkeyinfo.so") !== -1) {
console.log("[dlopen:]", this.path);
do_hook();

}
}
});

Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();

this.path = path;
}, onLeave: function (retval) {
if (this.path.indexOf("libkeyinfo.so") !== -1) {
console.log("\nandroid_dlopen_ext加载:", this.path);
do_hook();

}
}
});
}

load_so_and_hook();

// frida -U -f com.achievo.vipshop -l delay_hook.js

2、加密相关

2.1、md5加密

1
2
3
4
5
6
7
import hashlib

obj = hashlib.md5()
obj.update("123123".encode('utf-8'))
res = obj.hexdigest()
print(res)
# 4297f44b13955235245b2497399d7a93

2.2、3DES加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import base64
from Crypto.Cipher import DES3

BS = 8
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)

# 3DES的MODE_CBC模式下只有前24位有意义
key = b'appapiche168comappapiche168comap'[0:24]
iv = b'appapich'

plaintext = pad("xxxxxxxxx").encode("utf-8")

# 使用MODE_CBC创建cipher
cipher = DES3.new(key, DES3.MODE_CBC, iv)
result = cipher.encrypt(plaintext)
res = base64.b64encode(result)
print(res)

2.3、AESEncrypt.encode+base64算法加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64


def aes_encrypt(data_string):
key = "d245a0ba8d678a61"
aes = AES.new(
key=key.encode('utf-8'),
mode=AES.MODE_ECB,
)
raw = pad(data_string.encode('utf-8'), 16)
return aes.encrypt(raw)


data_string = "abFeedHuatiCard0deliveryProjectId0lastId1limit20loginTokenpage8platformandroidtimestamp1678960253053uuid0d9686a78e2aa975v5.0.6"
res = aes_encrypt(data_string)
value = base64.encodebytes(res)
result = value.replace(b"\n", b'')
print(result)

3、经验之谈

3.1、抓包问题

在Charles上抓不到包解决:SocksDroid(推荐)

  • 手机系统代理删除
  • 基本配置 –> 图片找不到了,兄弟们自行搜索吧,很简单的

3.2、System.nanoTime()

安卓中System.nanoTime() 代表着 开机 ~ 当前时间差

1
2
3
# 例如
import random
nano_time = random.randint(5136066335773,7136066335773)

3.3、解决强制更新

  • frida反调试
    进入该app的安装目录,利用frida反调试找到相关更新的so文件,然后把 该so文件 删除

  • Hook

    • 附加,自己把手机app启动+启动Hook
    • spwan,自动app启动+启动Hook

注意:后端API没有更新的情况下。

3.4.1 Python传参

在Python中给frida的JavaScript脚本传入参数时,一般有如下几种情况:

  • 字符串/整型/浮点型等直接传递。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import frida

    rdev = frida.get_remote_device()
    session = rdev.attach("大姨妈") # com.yoloho.dayima

    scr = """
    rpc.exports = {
    encrypt:function(v1,v2,v3,v4,v5){

    console.log(v1,typeof v1);
    console.log(v2,typeof v2);
    console.log(v3,typeof v3);
    console.log(v4,typeof v4);
    console.log(v5,typeof v5);

    var v6 = parseInt(v5);
    console.log(v6,typeof v6);
    }
    }
    """
    script = session.create_script(scr)
    script.load()

    # 调用
    script.exports.encrypt(100, "wupeiqi", 19.2, -10, "-1")
  • 列表/字典

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import frida

    rdev = frida.get_remote_device()
    session = rdev.attach("大姨妈") # com.yoloho.dayima

    scr = """
    rpc.exports = {
    encrypt:function(v1,v2){
    console.log(v1,typeof v1, v1[0], v1[1]);
    console.log(v2,typeof v2, v2.name, v2.age);

    for(let key in v1){
    console.log(key, v1[key] )
    }

    for(let key in v2){
    console.log(key, v2[key] )
    }
    }
    }
    """
    script = session.create_script(scr)
    script.load()

    script.exports.encrypt([11, 22, 33], {"name": 123, "age": 456})
  • 字节,无法直接传递,需转换为列表。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import frida

    rdev = frida.get_remote_device()
    session = rdev.attach("大姨妈") # com.yoloho.dayima

    scr = """
    rpc.exports = {
    encrypt:function(v1,v2){
    console.log(v1,typeof v1);

    // 转换为java的字节数组
    var bs = Java.array('byte',v1);
    console.log(JSON.stringify(bs))
    }
    }
    """
    script = session.create_script(scr)
    script.load()

    arg_bytes = "武沛齐".encode('utf-8')
    byte_list = [i for i in arg_bytes]
    script.exports.encrypt(byte_list)
  • 某个类的对象,无法直接传递,可以将参数传入,然后再在JavaScript调用frida api构造相关对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import frida

    rdev = frida.get_remote_device()
    session = rdev.attach("大姨妈") # com.yoloho.dayima

    scr = """
    rpc.exports = {
    encrypt:function(v1,v2){

    const StringBuilder = Java.use('java.lang.StringBuilder');;
    var obj = StringBuilder.$new(); // obj = new StringBuilder()
    obj.append(v1);//obj.append(v1)
    obj.append(v2);//obj.append(v2)

    Java.perform(function () {
    var Crypt = Java.use("com.yoloho.libcore.util.Crypt");
    res = Crypt.encrypt_data(j2,obj,j3);
    });


    }
    }
    """
    script = session.create_script(scr)
    script.load()

    script.exports.encrypt("武沛齐", "666")

3.4.2 JavaScript

在frida的脚本中其实就用编写JavaScript代码,所以我们对于内部的执行过程完全是使用JavaScript语法来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import frida

rdev = frida.get_remote_device()
session = rdev.attach("大姨妈") # com.yoloho.dayima

scr = """
rpc.exports = {
encrypt:function(v1,v2){
console.log(v1,typeof v1, v1[0], v1[1]);
console.log(v2,typeof v2, v2.name, v2.age);

for(let key in v1){
console.log(key,v1[key])
}

for(let key in v2){
console.log(key,v2[key])
}
}
}
"""
script = session.create_script(scr)
script.load()

script.exports.encrypt([11, 22, 33], {"name": 123, "age": 456})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import frida

rdev = frida.get_remote_device()
session = rdev.attach("大姨妈") # com.yoloho.dayima

scr = """
rpc.exports = {
encrypt:function(bytesList){
// [11,22,33,11,22,42,13,4]

// 先处理拼接好的数据(字节数组)
var bArr = [];
for(var i=0;i<bytesList.length;i+=2){
var item = (parseInt(bytesList[i],16) << 4) + parseInt(bytesList[i+1],16);
bArr.push(item);
}

console.log(bArr);

// 转换为java的字节数组
var bs = Java.array('byte',bArr);


}
}
"""
script = session.create_script(scr)
script.load()
arg_bytes = "wupeiqi".encode('utf-8')
byte_list = [i for i in arg_bytes]
script.exports.encrypt(byte_list)

3.4.3 Frida相关

在编写frida的JavaScript脚本时,我们经常会:

  • 调用Java中已编写的好的类、方法等功能
  • 执行目标方法时,传入相关参数。

这种情况下,就需要使用frida相关API来完成JavaScript和Java中的调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import frida

rdev = frida.get_remote_device()
session = rdev.attach("大姨妈") # com.yoloho.dayima

scr = """
rpc.exports = {
encrypt:function(v1,v2,v3,v4){

// 1.整型和字符串直接用
console.log(v1,v2);

// 2.字节数组
var v3_obj = Java.array('byte',v3);
console.log(v3_obj, JSON.stringify(v3_obj));

// 3.TreeMap对象 obj.get("xx")
var TreeMap = Java.use("java.util.TreeMap");
var v4_obj = TreeMap.$new();
for(let key in v4){
//console.log(key,v4[key]);
v4_obj.put(key,v4[key])
}

console.log( v4_obj )
console.log( v4_obj.get("name") )
console.log( v4_obj.get("age") )

var keyset = v4_obj.keySet();
var it = keyset.iterator();
while(it.hasNext()){
var keystr = it.next().toString();
var valuestr = v4_obj.get(keystr).toString();
console.log(keystr, valuestr);
}
}
}
"""
script = session.create_script(scr)
script.load()

v3 = [i for i in "wupeiqi".encode('utf-8')]

script.exports.encrypt(10, "wupeiqi", v3, {"name": "root", "age": "18"})

3.5、脱壳

例如:arm pro、frida-dump、反射大师、BackDex32、FART、youpk

1
>pip install frida-dexdump

想要使用frida-dexdump来进行脱壳,必须做如下两个步骤:

  • 手机端,启动frida-server

  • 电脑端

  • 端口转发

  • 执行脱壳命令

    1
    frida-dexdump  -U -f 包名称

脱壳后,会生成相应的dex文件,例如:

1
>frida-dexdump -U -f com.jiuxianapk.ui 

__END__