Rise的自留地

记录生活中的点滴,分享编程技术和游戏开发经验。

0%

OKHttp

OKHttp请求流程

OKHttp内部的大致请求流程图如下所示:

ART

ART 代表 Android Runtime,其处理应用程序执行的方式完全不同于 Dalvik,Dalvik 是依靠一个 Just-In-Time (JIT) 编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运 行。ART 则完全改变了这套做法,在应用安装时就预编译字节码到机器语言,这一机制叫 Ahead-Of-Time (AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。

Activity

生命周期

frida 代码结构:frida-core: Frida core library intended for static linking into bindingsfrida-gum: Low-leve……

frida 代码结构:

frida-core: Frida core library intended for static linking into bindings
frida-gum: Low-level code instrumentation library used by frida-core
bindings:
frida-python: Frida Python bindings
frida-node: Frida Node.js bindings
frida-qml: Frida Qml plugin
frida-swift: Frida Swift bindings
frida-tools: Frida CLI tools
capstone: instruction disammbler

这几年手游加速器很火。以光环为代表,进行C层的HOOK,既可以实现加速,又可以免Root保证手机安全。

a. 什么是加速?

加速就是改变游戏的运行速度。

b. 怎么样才能加速?

  • javascript 这个示例代码使用Frida框架来调用Unity游戏引擎中的Mono类方法。它首先通过attach到目标进程和获取相关模块的基址来获取Mono类和方法信息。然后,它构造了一个包含参数的调用,并使用mono_runtime_invoke函数来调用方法。最后,它使用mono_object_to_string函数来获取返回值并打印出来。请注意,这个示例代码仅供参考,实际使用时需要根据具体情况进行修改。
 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
// attach到目标进程
const processName = 'your_process_name';
const targetProcess = Process.getFromName(processName);
const module = Process.getModuleByName('UnityPlayer.dll');
const baseAddr = module.base;

// 获取Mono类和方法信息
const monoImage = Module.findBaseAddress('mono.dll');
const monoClass = MonoApi.mono_class_from_name(monoImage, 'YourNamespace', 'YourClassName');
const monoMethod = MonoApi.mono_class_get_method_from_name(monoClass, 'YourMethodName', -1);

// 构造参数
const arg1 = MonoApi.mono_string_new(monoImage, Memory.allocUtf8String('your_string_argument'));
const arg2 = MonoApi.mono_object_new(monoImage, monoClass);

// 构造调用
const args = Memory.alloc(2 * Process.pointerSize);
Memory.writePointer(args, arg1);
Memory.writePointer(args.add(Process.pointerSize), arg2);
const result = Memory.alloc(Process.pointerSize);

// 调用方法
MonoApi.mono_runtime_invoke(monoMethod, null, args, result);

// 获取返回值
const returnValue = MonoApi.mono_object_to_string(result, null);
console.log(Memory.readUtf16String(returnValue));
  • C 这个示例代码使用C语言和Frida框架来调用Unity游戏引擎中的Mono类方法。它首先初始化Frida并连接到目标进程,然后获取UnityPlayer.dll模块基址和Mono类和方法信息。接着,它构造了一个包含参数的调用,并使用frida_runtime_invoke_method函数来调用方法。最后,它使用frida_value_to_string函数来获取返回值并打印出来。请注意,这个示例代码仅供参考,实际使用时需要根据具体情况进行修改。
 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
#include <frida-core.h>

// Frida回调函数
static void on_message(FridaSession *session, const gchar *message, GBytes *data, gpointer user_data)
{
    // 处理回调消息
}

int main(int argc, char **argv)
{
    // 初始化Frida
    frida_init();

    // 连接到目标进程
    FridaSession *session = frida_attach("your_process_name");

    // 获取UnityPlayer.dll模块基址
    FridaModule *module = frida_session_find_module(session, "UnityPlayer.dll");
    guint64 base_addr = frida_module_get_base_address(module);

    // 获取Mono类和方法信息
    gpointer mono_image = frida_module_find_export_by_name(module, "mono_image_open_from_data_with_name");
    gpointer mono_class = frida_session_create_script(session, "YourNamespace", "YourClassName", NULL);
    gpointer mono_method = frida_class_find_method_by_name(mono_class, "YourMethodName");

    // 构造参数
    gpointer arg1 = frida_value_from_string("your_string_argument");
    gpointer arg2 = frida_value_new_object(mono_class);

    // 构造调用
    GArray *args = g_array_new(FALSE, FALSE, sizeof(GValue *));
    g_array_append_val(args, *((GValue *) arg1));
    g_array_append_val(args, *((GValue *) arg2));
    gpointer result = frida_value_new();

    // 调用方法
    frida_runtime_invoke_method(mono_method, NULL, args, result);

    // 获取返回值
    gchar *return_value = frida_value_to_string(result);
    g_print("%s\n", return_value);

    // 释放资源
    g_free(return_value);
    g_array_unref(args);
    frida_value_free(result);
    frida_value_free(arg1);
    frida_value_free(arg2);
    g_object_unref(mono_method);
    g_object_unref(mono_class);
    g_object_unref(mono_image);
    g_object_unref(module);
    g_object_unref(session);
    frida_deinit();

    return 0;
}

整理一下之前的学习的一些常用frida的技巧,以备哪天快速查找。

  • 常规的java层的函数hook
1
2
3
var manActivity=Java.use("com.kanxue.algorithmbase.MainActivity");
var res= manActivity.encodeFromJni_71(input)
console.log("input:",input,"output:",res);
  • 主动调用函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//主动调用静态函数
var FridaActivity2 = Java.use("com.kanxue.algorithmbase.MainActivity");
FridaActivity2.setStatic_bool_var(); 
//主动调用非静态函数
Java.choose("com.example.androiddemo.Activity.FridaActivity2", {
  onMatch: function (instance) {
    instance.setBool_var();
  },
  onComplete: function () {
  }
});
  • 主动设置成员变量
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
var FridaActivity3 = Java.use("com.example.androiddemo.Activity.FridaActivity3");
//设置非静态成员变量的值
FridaActivity3.static_bool_var.value = true;
console.log(FridaActivity3.static_bool_var.value);
Java.choose("com.example.androiddemo.Activity.FridaActivity3", {
  onMatch: function (instance) {
  //设置非静态成员变量的值
  instance.bool_var.value = true;
  //设置有相同函数名的成员变量的值
  instance._same_name_bool_var.value = true;
  console.log(instance.bool_var.value, instance._same_name_bool_var.value);
  },
  onComplete: function () {
    }
});
  • hook内部类
1
2
3
4
5
var InnerClasses = Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses");
console.log(InnerClasses);
InnerClasses.check1.implementation = function () {
    return true;
};
  • 根据条件判断动态hook多个函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
var class_name = "com.example.androiddemo.Activity.FridaActivity4$InnerClasses";
var InnerClasses = Java.use(class_name);
var all_methods = InnerClasses.class.getDeclaredMethods();
for (var i = 0; i < all_methods.length; i++) {
  var method = (all_methods[i]);
  var methodStr = method.toString();
  var substring = methodStr.substr(methodStr.indexOf(class_name) + class_name.length + 1);
  var methodname = substring.substr(0, substring.indexOf("("));
  console.log(methodname);
  InnerClasses[methodname].implementation = function () {
    console.log("hook_mul_function:", this);
    return true;
  }
}
  • interface中的函数进行hook。需要先切换classloader
 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
var FridaActivity5 = Java.use("com.example.androiddemo.Activity.FridaActivity5");
Java.choose("com.example.androiddemo.Activity.FridaActivity5", {
  onMatch: function (instance) {
  console.log(instance.getDynamicDexCheck().$className);
  }, onComplete: function () {

  }
});
Java.enumerateClassLoaders({
  onMatch: function (loader) {
    try {
      if (loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")) {
        console.log(loader);
        Java.classFactory.loader = loader;  
     }
    } catch (error) {
    }
  }, onComplete: function () {
  }
});
var DynamicCheck = Java.use("com.example.androiddemo.Dynamic.DynamicCheck");
console.log(DynamicCheck);
DynamicCheck.check.implementation = function () {
  console.log("DynamicCheck.check");
  return true;
}
  • frida动态加载dex然后再调用dex中的函数
1
2
3
4
var ddex2 = Java.openClassFile("/data/local/tmp/ddex2.dex");
ddex2.load();
var DecodeUtils = Java.use("com.example.androiddemo.DecodeUtils");
console.log("DecodeUtils.decode_p:", DecodeUtils.decode_p());
  • hook构造函数
1
2
3
4
5
6
7
var a = Java.use("com.tlamb96.kgbmessenger.b.a");
  //hook 构造函数
  a.$init.implementation = function (i, str, str2, z) {
  this.$init(i, str, str2, z);
  console.log("a.$init:", i, str, str2, z);
  print_stack();       //打印了调用栈
};
  • 打印java堆栈
1
2
3
4
5
6
7
8
9
function print_stack() {
    Java.perform(function () {
        var Exception = Java.use("java.lang.Exception");
        var instance = Exception.$new("print_stack");
        var stack = instance.getStackTrace();
        console.log(stack);
        instance.$dispose();
    });
}
  • 调用NativeFunction来写入文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
function write_reg_dat2() {
    //把C函数定义为NativeFunction来写文件
    var addr_fopen = Module.findExportByName("libc.so", "fopen");
    var addr_fputs = Module.findExportByName("libc.so", "fputs");
    var addr_fclose = Module.findExportByName("libc.so", "fclose");

    console.log("addr_fopen:", addr_fopen, "addr_fputs:", addr_fputs, "addr_fclose:", addr_fclose);
    var fopen = new NativeFunction(addr_fopen, "pointer", ["pointer", "pointer"]);
    var fputs = new NativeFunction(addr_fputs, "int", ["pointer", "pointer"]);
    var fclose = new NativeFunction(addr_fclose, "int", ["pointer"]);

    var filename = Memory.allocUtf8String("/sdcard/reg.dat");
    var open_mode = Memory.allocUtf8String("w+");
    var file = fopen(filename, open_mode);
    console.log("fopen file:", file);

    var buffer = Memory.allocUtf8String("EoPAoY62@ElRD");
    var ret = fputs(buffer, file);
    console.log("fputs ret:", ret);

    fclose(file);
}
  • 将指针以字符串的方式打印
1
2
3
4
5
function print_string(addr) {
    var base_hello_jni = Module.findBaseAddress("libhello-jni.so");
    var addr_str = base_hello_jni.add(addr);
    console.log("addr:", addr, " ", ptr(addr_str).readCString());
}
  • 延时hook。当用frida来启动应用时。hook的so还没有加载。所以要延时到加载完这个so后。才能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
function hook_dlopen() {
    var dlopen = Module.findExportByName(null, "dlopen");
    Interceptor.attach(dlopen, {
        onEnter: function (args) {
            this.call_hook = false;
            var so_name = ptr(args[0]).readCString();
            if (so_name.indexOf("libhello-jni.so") >= 0) {
                console.log("dlopen:", ptr(args[0]).readCString());
                this.call_hook = true;
            }

        }, onLeave: function (retval) {
            if (this.call_hook) {
                inline_hook();
            }
        }
    });
    // 高版本Android系统使用android_dlopen_ext
    var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
    Interceptor.attach(android_dlopen_ext, {
        onEnter: function (args) {
            this.call_hook = false;
            var so_name = ptr(args[0]).readCString();
            if (so_name.indexOf("libhello-jni.so") >= 0) {
                console.log("android_dlopen_ext:", ptr(args[0]).readCString());
                this.call_hook = true;
            }

        }, onLeave: function (retval) {
            if (this.call_hook) {
                inline_hook();
            }
        }
    });
}
  • 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
function hook_gettimeofday() {
    var addr_gettimeofday = Module.findExportByName(null, "gettimeofday");
    var gettimeofday = new NativeFunction(addr_gettimeofday, "int", ["pointer", "pointer"]);

    var source = [
        'struct timeval {',
        '    int tv_sec;',
        '    int tv_usec;',
        '};',
        'void modify_time(struct timeval* tv, int tv_sec, int tv_usec) {',
        '  tv->tv_sec = tv_sec;',
        '  tv->tv_usec = tv_usec;',
        '}',
    ].join('\n');

    var cm = new CModule(source);
    var modify_time = new NativeFunction(cm.modify_time, 'void', ["pointer", "int", "int"]);

    Interceptor.replace(addr_gettimeofday, new NativeCallback(function (ptr_tz, ptr_tzp) {

        var result = gettimeofday(ptr_tz, ptr_tzp);
        if (result == 0) {
            console.log("hook gettimeofday:", ptr_tz, ptr_tzp, result);
            //modify_time(ptr_tz, 0xAAAA, 0xBBBB);
            var t = new Int32Array(ArrayBuffer.wrap(ptr_tz, 8));
            t[0] = 0xAAAA;
            t[1] = 0xBBBB;
            console.log(hexdump(ptr_tz));
        }
        return result;
    }, "int", ["pointer", "pointer"]));
}
  • frida打patch补丁。可以用来搞反调试的
 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
function dis(address, number) {
    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);
                Memory.patchCode(patchaddr, 4, patchaddr => {
                    var cw = new ThumbWriter(patchaddr);
                    cw.putNop();
                    cw = new ThumbWriter(patchaddr.add(0x2));
                    cw.putNop();
                    cw.flush();
                });
                /*                Memory.protect(base.add(0x8d96),4,'rwx');
                                base.add(0x8d96).writeByteArray([0x00,0xbf,0x00,0xbf]);*/
                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');
                base.add(0x8E78).writeByteArray([0x00, 0xbf, 0x00, 0xbf]);
                console.log("+++++++++++++++++++++++")
                dis(base.add(0x8E6E).add(1), 10);
            }
        }
    })
}
  • 用stalker来trace
 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
function hook_native(){
    var base_addr=Module.getBaseAddress("libnative-lib.so");
    var encodeFromJni_71=base_addr.add(0x156B4);
    Interceptor.attach(encodeFromJni_71,{
        onEnter:function(args){
            this.tid=Process.getCurrentThreadId();
            console.log("enter encodeFromJni_71 tid:",this.tid);
            Stalker.follow(this.tid, {
                events: {
                    call: true, // CALL instructions: yes please
                    // Other events:
                    ret: false, // RET instructions
                    exec: false, // all instructions: not recommended as it's
                                 //                   a lot of data
                    block: false, // block executed: coarse execution trace
                    compile: false // block compiled: useful for coverage
                },
                onCallSummary:function(summary){        //调用的地址和调用的次数
                    for(var iter in summary){
                        var module= Process.getModuleByAddress(ptr(iter))
                        if(module.name.indexOf("libnative-lib.so")!=-1){
                            console.log(ptr(iter).sub(module.base));
                        }
                    }
                },
                onReceive:function(events){             //调用的流程,地址1是哪里发生的调用。地址2是调用到了哪里
                    console.log("enter onReceive")
                    var eventsData=Stalker.parse(events,{
                        annotate: true,
                        stringify: true
                    });
                    for(var idx in eventsData){
                        var dataSp=eventsData[idx];
                        var addr1=dataSp[1];
                        var addr2=dataSp[2];
                        try{
                            var module1=Process.getModuleByAddress(ptr(addr1));
                            if(module1.name.indexOf("libnative-lib.so")!=-1){
                                var module2=Process.getModuleByAddress(ptr(addr2));
                                console.log(dataSp[0],module1.name,addr1,module2.name,addr2);
                            }
                        }catch(err){
                            console.log(dataSp);
                        }
                    }
                }
            })
        },onLeave(retval){
            Stalker.unfollow(this.tid);
        }
    })
}
  • 比较通用的native函数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
//打印参数
function hexdumpMem(addr){
    if(Process.findRangeByAddress(addr)){
        return hexdump(ptr(addr),{length:0x40})+"\r\n"
    }else{
        return ptr(addr)+"\r\n";
    }
}
//比较通用的hook地址并且打印5个参数。如果参数是地址就打印下内存信息
function nativeHookFunction(addr){
    var base_addr=Module.getBaseAddress("libnative-lib.so");
    var hook_addr=base_addr.add(addr);
    Interceptor.attach(hook_addr,{
        onEnter:function(args){
            this.logs=[];
            this.logs.push("call",addr);
            this.arg0=args[0];
            this.arg1=args[1];
            this.arg2=args[2];
            this.arg3=args[3];
            this.arg4=args[4];
            this.arg5=args[5];
            this.logs.push("arg0:",hexdumpMem(this.arg0));
            this.logs.push("arg1:",hexdumpMem(this.arg1));
            this.logs.push("arg2:",hexdumpMem(this.arg2));
            this.logs.push("arg3:",hexdumpMem(this.arg3));
            this.logs.push("arg4:",hexdumpMem(this.arg4));
            this.logs.push("arg5:",hexdumpMem(this.arg5));
        },onLeave:function(retval){
            this.logs.push("arg0 leave:",hexdumpMem(this.arg0));
            this.logs.push("arg1 leave:",hexdumpMem(this.arg1));
            this.logs.push("arg2 leave:",hexdumpMem(this.arg2));
            this.logs.push("arg3 leave:",hexdumpMem(this.arg3));
            this.logs.push("arg4 leave:",hexdumpMem(this.arg4));
            this.logs.push("arg5 leave:",hexdumpMem(this.arg5));
            this.logs.push("retval leave:",hexdumpMem(retval));
            console.log(this.logs);
        }
    })
}
  • Frida打印java中的byte数组
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
function Bytes2HexString(arrBytes) {
    var str = "";
    for (var i = 0; i < arrBytes.length; i++) {
        var tmp;
        var num = arrBytes[i];
        if (num < 0) {
            //此处填坑,当byte因为符合位导致数值为负时候,需要对数据进行处理
            tmp = (255 + num + 1).toString(16);
        } else {
            tmp = num.toString(16);
        }
        if (tmp.length === 1) {
            tmp = "0" + tmp;
        }
        str += tmp;
    }
    return str;
}

1
2
3
4
5
6
double round(double value, unsigned int decimal_places) {
    double multiplier = 1.00;
    if (decimal_places > 0)
        multiplier = std::pow(10, decimal_places);
    return std::floor(value * multiplier + 0.5) / multiplier;
}

使用visibility

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#if defined _WIN32 || defined __CYGWIN__
    #ifdef MY_NO_EXPORT
        #define API
    #else
        #define API __declspec(dllexport)
    #endif
#else
    #ifdef __GNUC__
        #define API  __attribute__((visibility("default")))
    #else
        #define API
    #endif
#endif

#if defined __cplusplus
    #define EXTERN extern "C"
#else
    #include <stdarg.h>
    #include <stdbool.h>
    #define EXTERN extern
#endif

#define MY_API EXTERN API

GNU C 的一大特色就是attribute 机制。 试想这样的情景,程序调用某函数A,A函数存在于两个动态链接库liba.so,libb.so中,并且程序执行需要链接这两个库,此时程序调用的A函数到底是来自于a还是b呢? 这取决于链接时的顺序,比如先链接liba.so,这时候通过liba.so的导出符号表就可以找到函数A的定义,并加入到符号表中,链接libb.so的时候,符号表中已经存在函数A,就不会再更新符号表,所以调用的始终是liba.so中的A函数。 为了避免这种混乱,所以使用

ETH 2.0 节点分为执行客户端共识客户端验证软件,同步数据节点无论是快照同步还是完整存档,都需要同步启动执行客户端共识客户端

  1. 安装配置执行客户端Nethermind
1
2
3
sudo apt-get update && sudo apt-get install libsnappy-dev libc6-dev libc6 unzip
wget https://github.com/NethermindEth/nethermind/releases/download/1.14.5/nethermind-linux-amd64-1.14.5-380bf9c-20221029.zip
unzip nethermind-linux-amd64-1.14.5-380bf9c-20221029.zip -d nethermind

添加下面配置到nethermind/configs/mainnet.cfg