首页
社区
课程
招聘
[原创]FRIDA 使用经验交流分享
发表于: 2021-1-11 10:29 40928

[原创]FRIDA 使用经验交流分享

2021-1-11 10:29
40928

Frida 是非常灵活的 Hook 框架,支持多平台。
在这里就不过多介绍了,详细可以参看官网:https://frida.re/
使用 Frida也挺长时间了,结合平时实战的经验,系统的梳理了一下开发环境、调试环境、整合的常用脚本和新特性分享给大家,在这里抛个砖。

一开始使用 frida 时,直接写个 js 脚本然后注入就完事了,优点是快捷方便,缺点也很明显就是 hook 代码写多了都挤在一个 js 里冗长,事后阅读起来比较费事,也不利于模块化和代码复用。
TypeScript + npm 的方式搭建 frida 开发环境,使得在用 IDE 编写 frida 时可以有代码补全提示同时 TypeScript 又能使开发更容易达到模块化,代码复用更方便。

Frida 使用 TypeScript, 环境的搭建很简单。

用 pycharm 打开构建好环境的工程,编写 frida ts 脚本已经可以看到代码补全提示了。

代码补全

将上面生成的目标文件 _agent.js 注入到目标进程即可。

能愉快的单步调试 frida 的 js 脚本,可以方便不少。

首先运行 frida 脚本

或者

启动后会回显 Inspector 正在监听 9229 默认端口

打开 chrome://inspect, 点击 Open dedicated DevTools for Node

chrome_dev

此时 debug 已经连接,切换至 Sources,按 Command + P 加载要调试的脚本,即可下断调试了。

chrome_dev_dbg

添加调试器 Attaching to Node.js/Chrome,端口默认即可。Attach to 应选择 Node.js < 8 started with --debug, 下面的自动重连选项可选可不选。

pycharm_nodejs

触发断点需要在 debug 窗口切换到 script 选项卡,右键要调试的脚本,选择 Open Actual Source,在新打开的 Actual Source 窗口设置好断点后,需要再取消/启用一次所有断点作为激活,发现断点上打上对勾才真正可用了。

pycharm_scripts

接下来就可以愉快的调试了

pycharm_debug

FridaContainer 整合了网上流行的和平时常用的 frida 脚本,为逆向工作提效之用。
该仓库已经申请了开源,地址:https://github.com/deathmemory/FridaContainer

整理了一些普通反调试的绕过或定位的方法。因为很多反调试的手段是通过读取各个状态文件,查找特征字符串来判断是否被调试,而读取文件内容又通常都用到了 fgets 函数,如此我们就直接 hook 此函数加入过滤规则就能过滤掉许多反调试检测。

例如:
/proc/<pid>/status 检测 TracerPid: 0State: S (sleeping)SigBlk: 0000000000001204
/proc/<pid>/stat 检测 t (tracing stop)
/proc/<pid>/wchan 检测 SyS_epoll_wait

Hook fgets 后,触发时首先获取一下 LR 寄存器的值,保存一下现场,之后返回信息时可以把 LR 带出来,方便定位。然后调用原函数,对赋值的 buffer 进行过滤就可以了。

FridaContainer 调用:

小结:

当上面的方法无法绕过反调试时,可以再 Hook 一些常用的退出函数来定位反调点,比如 hook exit, kill ,再总结出一些其他过反调的方法,思路类似。

我们也在 FridaContainer 里面集成了 Frida 版的 JustTrustMe 来过 SSL Pinning 检测。

此部分代码主要借鉴了:https://codeshare.frida.re/@akabe1/frida-multiple-unpinning/

支持 20 种类库的SSL 验证绕过:

FridaContainer 调用:

小结:

主要的出发点就是想摆脱 Xposed 框架,调试时减少被检测的风险,要绕过检测只绕 Frida 一个就好了。

这里的 dump dex 是 dump 二代壳,通过 hook art so 的 art::DexFile::OpenCommon (_ZN3art7DexFile10OpenCommonEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_PNS0_12VerifyResultE) dump 动态加载的 dex 文件。

通过 frida 动态加载自定义的 dex,在 dex 中实现类的枚举和主动加载,来尝试加载所有的类,使其触发 dex 动态加载,再通过之前的 hook ,将加载的 dex dump 下来。

FridaContainer 调用:

也有通过搜索内存的方式将 dex dump 出来 FRIDA-DEXDump

有时候遇到动态加载的 dex 又不是用的 application 的 loader ,Java.use 可能会提示找不到类,遇到这种情况可以用修改 java loader 的方式来应对。
比如遇到利用 InMemoryDexClassLoader 来加载内存中的 dex 时,可以 Hook 它,当其触发时先走原流程,让其动态加载 dex ,然后利用此时的 loader object 修改 Java.classFactory.loader,再使用 Java.use 来获取类就可以了,使用后记得恢复现场,否则会崩溃。

动态加载实例:

动态加载实例

完整代码:

使用:

有时 dex 使用了一些非常规的动态加载方式,找 loader 定位起来也比较麻烦。假设我们只想调用某 jni 函数,看输入输出值的话,还可以用 frida java 反射的方式调用。

整体调用流程:

拼装Obj:

上面都是在反射调用的角度简单介绍了 staticVaMethod, vaMethod, constructor 的使用方法。

用 Frida 也可以实现 java 层的 trace 。
因为基于 Frida 框架,如果直接 trace 所有的类效率太慢,也容易崩溃。所以这里是以白名单的方式实现的。核心方法就是枚举所有类,按过滤名单,匹配需要 trace 的类,Hook 目标类的所有方法(可指定),在方法被调用时,将其入参和返回值记录下来。

核心逻辑:
遍历所有类,Hook 白名单中的类及方法。

调用方式:

具体实现:

通过 python/android/traceLogCleaner.py 脚本收集 trace 日志,将回传的日志按线程格式化输出日志,并且对字节数组,尝试进行 string 和 hex 转换以方便搜索。

格式化 trace 效果:

格式化_trace_效果

单条日志:

单条日志

小结:

优点:用 Frida 做 java 方法级的 trace ,优点就是方便、灵活、轻量级。
缺点:限于 Frida 框架,该方式效率较低,trace 方法过多容易崩溃,所以无法做全量 trace。
推荐用于轻量级的 Java 方法 trace 可以有效的定位核心算法。

利用 Frida 的 Java.vm.getEnv() 获取 JNIEnv 指针,再根据 jni 结构体函数偏移,可以获取到各个 jni 函数地址,之后就可以根据需要进行 Hook 了。
例如

核心逻辑:

应用:

返回效果

FridaContainer 调用:

此功能因功能比较固定、使用频率高,也单独拆出了一个仓库:https://github.com/deathmemory/fridaRegstNtv

这里参考了 jnitrace 做了简化和嵌入版。

具体实现:

因为有了上面的基础,使 Jni 的 trace 变得简洁了

因为性能问题,在信息收集时去除了 jnitrace 中的 Thread.backtraceDebugSymbol.fromAddress 可能会造成线程阻塞的因素,对 trace 效果没有太多影响。

格式化日志输出效果:

jnitrace_formated

FridaContainer 调用:

参考:jnitrace

小结:
jni 的 trace 做了速度上的优化,去除了一些线程阻塞的因素,使得在 jni 全量 trace 下也可以很好的运行。结合上面的 Java trace ,寻常难度的算法定位效率可以有一个很好的提高。

Stalker 部分的内容,bmax 大佬已经发了一篇贴子,大家可以跳转看一下。
地址:https://bbs.pediy.com/thread-264680.htm

参考:
AntiFrida

以上是我们对使用 Frida 经验的一些总结。在逆向工程里,个人认为有两个方面对逆向的提效是帮助非常大的,一个是 trace 一个是算法识别。而 Frida 在有了 Stalker 的加持下,这两个方面都可以实现。在轻量级和便捷性上,首选推荐。

FridaContainer:https://github.com/deathmemory/FridaContainer
fridaRegstNtv:https://github.com/deathmemory/fridaRegstNtv
以上两个仓库还请大佬们多多拍砖,提建议。
抛砖引玉,希望能和业界大佬多多交流。感谢。

$ git clone git://github.com/oleavr/frida-agent-example.git
$ cd frida-agent-example/
$ npm install
$ git clone git://github.com/oleavr/frida-agent-example.git
$ cd frida-agent-example/
$ npm install
 
$ npm run watch
# 或者
$ frida-compile agent/index.ts -o _agent.js -w
$ npm run watch
# 或者
$ frida-compile agent/index.ts -o _agent.js -w
$ frida -U -f com.example.android --no-pause -l _agent.js
$ frida -U -f com.example.android --no-pause -l _agent.js
 
frida -l </Users/name/path/test.js> --debug --runtime=v8 <port/name>
frida -l </Users/name/path/test.js> --debug --runtime=v8 <port/name>
session = dev.attach(app.pid)
script = session.create_script(jscode, runtime="v8")
session.enable_debugger()
session = dev.attach(app.pid)
script = session.create_script(jscode, runtime="v8")
session.enable_debugger()
Chrome Inspector server listening on port 9229
Chrome Inspector server listening on port 9229
 
 
 
 
 
static anti_fgets() {
  const tag = 'anti_fgets';
  const fgetsPtr = Module.findExportByName(null, 'fgets');
  DMLog.i(Anti.tag, 'fgets addr: ' + fgetsPtr);
  if (null == fgetsPtr) {
    return;
  }
  var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']);
  Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) {
    if (null == this) {
      return 0;
    }
    var logTag = null;
    // 进入时先记录现场
    const lr = FCCommon.getLR(this.context);
    // 读取原 buffer
    var retval = fgets(buffer, size, fp);
    var bufstr = (buffer as NativePointer).readCString();
 
    if (null != bufstr) {
      if (bufstr.indexOf("TracerPid:") > -1) {
        buffer.writeUtf8String("TracerPid:\t0");
        logTag = 'TracerPid';
      }
      //State:    S (sleeping)
      else if (bufstr.indexOf("State:\tt (tracing stop)") > -1) {
        buffer.writeUtf8String("State:\tS (sleeping)");
        logTag = 'State';
      }
      // ptrace_stop
      else if (bufstr.indexOf("ptrace_stop") > -1) {
        buffer.writeUtf8String("sys_epoll_wait");
        logTag = 'ptrace_stop';
      }
      else if (bufstr.indexOf(") t") > -1) {
        buffer.writeUtf8String(bufstr.replace(") t", ") S"));
        logTag = 'stat_t';
      }
 
      // SigBlk
      else if (bufstr.indexOf('SigBlk:') > -1) {
        buffer.writeUtf8String('SigBlk:\t0000000000001204');
        logTag = 'SigBlk';
      }
      if (logTag) {
        DMLog.i(tag + " " + logTag, bufstr + " -> " + buffer.readCString() + ' lr: ' + lr
                + "(" + FCCommon.getModuleByAddr(lr) + ")");
      }
    }
    return retval;
  }, 'pointer', ['pointer', 'int', 'pointer']));
}
static anti_fgets() {
  const tag = 'anti_fgets';
  const fgetsPtr = Module.findExportByName(null, 'fgets');
  DMLog.i(Anti.tag, 'fgets addr: ' + fgetsPtr);
  if (null == fgetsPtr) {
    return;
  }
  var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']);
  Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) {
    if (null == this) {
      return 0;
    }
    var logTag = null;
    // 进入时先记录现场
    const lr = FCCommon.getLR(this.context);
    // 读取原 buffer
    var retval = fgets(buffer, size, fp);
    var bufstr = (buffer as NativePointer).readCString();
 
    if (null != bufstr) {
      if (bufstr.indexOf("TracerPid:") > -1) {
        buffer.writeUtf8String("TracerPid:\t0");
        logTag = 'TracerPid';
      }
      //State:    S (sleeping)
      else if (bufstr.indexOf("State:\tt (tracing stop)") > -1) {
        buffer.writeUtf8String("State:\tS (sleeping)");
        logTag = 'State';
      }
      // ptrace_stop
      else if (bufstr.indexOf("ptrace_stop") > -1) {
        buffer.writeUtf8String("sys_epoll_wait");
        logTag = 'ptrace_stop';
      }
      else if (bufstr.indexOf(") t") > -1) {
        buffer.writeUtf8String(bufstr.replace(") t", ") S"));
        logTag = 'stat_t';
      }
 
      // SigBlk
      else if (bufstr.indexOf('SigBlk:') > -1) {
        buffer.writeUtf8String('SigBlk:\t0000000000001204');
        logTag = 'SigBlk';
      }
      if (logTag) {
        DMLog.i(tag + " " + logTag, bufstr + " -> " + buffer.readCString() + ' lr: ' + lr
                + "(" + FCCommon.getModuleByAddr(lr) + ")");
      }
    }
    return retval;
  }, 'pointer', ['pointer', 'int', 'pointer']));
}
FCAnd.anti.anti_debug();
FCAnd.anti.anti_debug();
 
 
 
FCAnd.anti.anti_ssl_unpinning();
FCAnd.anti.anti_ssl_unpinning();
 
 
 
FCAnd.dump_dex_common();
FCAnd.dump_dex_common();
 
 
 
function anti_InMemoryDexClassLoader(callbackfunc) {
    //  dalvik.system.InMemoryDexClassLoader
    const InMemoryDexClassLoader = Java.use('dalvik.system.InMemoryDexClassLoader');
    InMemoryDexClassLoader.$init.overload('java.nio.ByteBuffer', 'java.lang.ClassLoader')
        .implementation = function (buff, loader) {
        this.$init(buff, loader);
        var oldcl = Java.classFactory.loader;
        Java.classFactory.loader = this;
        callbackfunc();
        Java.classFactory.loader = oldcl; // 恢复现场
    }
}
function anti_InMemoryDexClassLoader(callbackfunc) {
    //  dalvik.system.InMemoryDexClassLoader
    const InMemoryDexClassLoader = Java.use('dalvik.system.InMemoryDexClassLoader');
    InMemoryDexClassLoader.$init.overload('java.nio.ByteBuffer', 'java.lang.ClassLoader')
        .implementation = function (buff, loader) {
        this.$init(buff, loader);
        var oldcl = Java.classFactory.loader;
        Java.classFactory.loader = this;
        callbackfunc();
        Java.classFactory.loader = oldcl; // 恢复现场
    }
}
FCAnd.anti.anti_InMemoryDexClassLoader(function(){
    const cls = Java.use('find/same/multi/dex/class');
    ...
});
FCAnd.anti.anti_InMemoryDexClassLoader(function(){
    const cls = Java.use('find/same/multi/dex/class');
    ...
});
 
// 获取 so 基址
var base = Module.findBaseAddress('libxxxx.so');
// 根据偏移获取 jni 函数地址
var jnifunc_ptr = libsgmainso.add(0xE729);
// 声明 jni 函数
var jnifunc = new NativeFunction(jnifunc_ptr, 'pointer', ['pointer', 'pointer', 'int', 'pointer']);
// ********* 拼装 obj *********
// JNIEnv
var env = Java.vm.getEnv();
// 调用
var retval = jnifunc(env.handle,  ptr(0),  10401,  obj);
// 获取 so 基址
var base = Module.findBaseAddress('libxxxx.so');
// 根据偏移获取 jni 函数地址
var jnifunc_ptr = libsgmainso.add(0xE729);
// 声明 jni 函数
var jnifunc = new NativeFunction(jnifunc_ptr, 'pointer', ['pointer', 'pointer', 'int', 'pointer']);
// ********* 拼装 obj *********
// JNIEnv
var env = Java.vm.getEnv();
// 调用
var retval = jnifunc(env.handle,  ptr(0),  10401,  obj);
// staticVaMethod 实现 Integer.valueOf(7)
const Integer_jcls = env.findClass('java/lang/Integer');
const Integer_valueOf = env.getStaticMethodId(Integer_jcls, 'valueOf', '(I)Ljava/lang/Integer;');
const invorkeStaticOjbectMethod = env.staticVaMethod('pointer', ['int']);
var pIn2 = invorkeStaticOjbectMethod(env.handle, Integer_jcls, Integer_valueOf, 7);
// 利用 constructor | vaMethod 组装 HashMap obj
// new HashMap().put('INPUT', 'xxxxxxxxxx')
const HashMap_jcls = env.findClass('java/util/HashMap');
const invokeHashmap_constructor = env.constructor([]);
const HashMap_init = env.getMethodId(HashMap_jcls, '<init>', '()V');
var HashMap_obj = invokeHashmap_constructor(env.handle, HashMap_jcls, HashMap_init);
const HashMap_put = env.getMethodId(HashMap_jcls, 'put', '(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;');
const invokeOjbectMethod = env.vaMethod('pointer', ['pointer', 'pointer']);
invokeOjbectMethod(env.handle, HashMap_obj, HashMap_put, env.newStringUtf('INPUT'), env.newStringUtf('xxxxxxxxxx'));
// staticVaMethod 实现 Integer.valueOf(7)
const Integer_jcls = env.findClass('java/lang/Integer');
const Integer_valueOf = env.getStaticMethodId(Integer_jcls, 'valueOf', '(I)Ljava/lang/Integer;');
const invorkeStaticOjbectMethod = env.staticVaMethod('pointer', ['int']);
var pIn2 = invorkeStaticOjbectMethod(env.handle, Integer_jcls, Integer_valueOf, 7);
// 利用 constructor | vaMethod 组装 HashMap obj
// new HashMap().put('INPUT', 'xxxxxxxxxx')
const HashMap_jcls = env.findClass('java/util/HashMap');
const invokeHashmap_constructor = env.constructor([]);
const HashMap_init = env.getMethodId(HashMap_jcls, '<init>', '()V');
var HashMap_obj = invokeHashmap_constructor(env.handle, HashMap_jcls, HashMap_init);
const HashMap_put = env.getMethodId(HashMap_jcls, 'put', '(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;');
const invokeOjbectMethod = env.vaMethod('pointer', ['pointer', 'pointer']);
invokeOjbectMethod(env.handle, HashMap_obj, HashMap_put, env.newStringUtf('INPUT'), env.newStringUtf('xxxxxxxxxx'));
 
 
/**
 * java 方法追踪
 * @param clazzes 要追踪类数组 ['M:Base64', 'E:java.lang.String'] ,类前面的 M 代表 match 模糊匹配,E 代表 equal 精确匹配
 * @param whitelist 指定某类方法 Hook 细则,可按白名单或黑名单过滤方法。
 *                  { '类名': {white: true, methods: ['toString', 'getBytes']} }
 * @stackFilter 按匹配字串打印堆栈。如果要匹配 bytes 数组需要十进制无空格字串,例如:"104,113,-105"
 */
FCAnd.traceArtMethods(
    ['M:MainActivity', 'E:java.lang.String'],
    {'java.lang.String': {white: true, methods:['substring', 'getChars']}},
    "match_str_show_stacks"
);
/**
 * java 方法追踪
 * @param clazzes 要追踪类数组 ['M:Base64', 'E:java.lang.String'] ,类前面的 M 代表 match 模糊匹配,E 代表 equal 精确匹配
 * @param whitelist 指定某类方法 Hook 细则,可按白名单或黑名单过滤方法。
 *                  { '类名': {white: true, methods: ['toString', 'getBytes']} }
 * @stackFilter 按匹配字串打印堆栈。如果要匹配 bytes 数组需要十进制无空格字串,例如:"104,113,-105"
 */
FCAnd.traceArtMethods(
    ['M:MainActivity', 'E:java.lang.String'],
    {'java.lang.String': {white: true, methods:['substring', 'getChars']}},
    "match_str_show_stacks"
);
Java.enumerateLoadedClassesSync().forEach((curClsName, index, array) => {
    dest_cls.forEach((destCls) => {
        // 按规则匹配是否需要 trace
        if (match(destCls, curClsName)) {
            // trace 核心方法
            traceArtMethodsCore(curClsName);
            return false; // end forEach
        }
    });
});
// Hook 核心逻辑
function traceArtMethodsCore(clsname: string) {
    let cls = Java.use(clsname);
    // 枚举方法
    let methods = cls.class.getDeclaredMethods();
    methods.forEach(function (method: any) {
        ...
        // 枚举重载
        let methodOverloads = cls[methodName].overloads;
        methodOverloads.forEach(function (overload: any) {
            ...
            // Hook
            overload.implementation = function () {
                // ... send entry msg
                // 利用 js 参数特性 arguments ,调用原函数以适配所有 Hook 方法的传参
                const retval = this[methodName].apply(this, arguments);
                // ... send exit msg
                return retval;
            }
        }
    }
}
Java.enumerateLoadedClassesSync().forEach((curClsName, index, array) => {
    dest_cls.forEach((destCls) => {
        // 按规则匹配是否需要 trace
        if (match(destCls, curClsName)) {
            // trace 核心方法
            traceArtMethodsCore(curClsName);
            return false; // end forEach
        }
    });
});

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 30
支持
分享
打赏 + 201.00雪花
打赏次数 2 雪花 + 201.00
 
赞赏  Editor   +1.00 2021/01/15 精品文章~
赞赏  暴强   +200.00 2021/01/12 加油!
最新回复 (39)
雪    币: 97697
活跃值: (200759)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
2
支持。
2021-1-11 10:40
0
雪    币: 102
活跃值: (2030)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
mark
2021-1-11 11:02
0
雪    币: 6573
活跃值: (3873)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
4
支持
2021-1-11 13:32
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
支持
2021-1-11 13:47
0
雪    币: 764
活跃值: (2040)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
2021-1-11 14:31
0
雪    币: 154
活跃值: (3786)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
7
支持
2021-1-11 15:41
0
雪    币: 3211
活跃值: (2553)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
支持
2021-1-11 17:38
0
雪    币: 60
活跃值: (1497)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9

大佬既然你用typescript写脚本,有没有遇到过 typescript 中写好的函数,无法在控制台里【再次主动调用】这个问题。是这样的,我用javascript写好的脚本,比如:

function main()
{
    console.log("hello");
}

setImmediate(main);

当我使用 frida -U ... 注入后,我是可以在控制台主动调用 main 函数的 ,比如输入 【main()】,就可以调用了。


【但是】,如果我上面的代码是用 typescript 写的话,在控制台 输入 【main()】,是不能调用 main 函数的,会提示我不能识别这个函数。


目前觉得就是 typescript 写的代码不能【实时更新】,但 javascript的可以。不知道大佬解决过没有,如果知道请赐教。

最后于 2021-1-11 19:36 被滚动不息的球编辑 ,原因:
2021-1-11 19:34
0
雪    币: 4423
活跃值: (3088)
能力值: ( LV10,RANK:175 )
在线值:
发帖
回帖
粉丝
10
滚动不息的球 大佬既然你用typescript写脚本,有没有遇到过 typescript 中写好的函数,无法在控制台里【再次主动调用】这个问题。是这样的,我用javascript写好的脚本,比如:function& ...
遇到了,用 ts 是用 frida-compile 编译过的,被编译后不能像写 js 那样直接调用了,可以用 `rpc.exports` 来代替。
另外 ts 可以实时更新的,用 npm build watch 就行,文中有提到。
2021-1-11 19:56
0
雪    币: 60
活跃值: (1497)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
DMemory 遇到了,用 ts 是用 frida-compile 编译过的,被编译后不能像写 js 那样直接调用了,可以用 `rpc.exports` 来代替。 另外 ts 可以实时更新的,用 npm build ...

谢谢大神回复,但其实我说的【实时更新】你理解成了【实时编译】,我说的【实时更新】是注入后,写一个新的function,写好后,马上就能在【控制台】调用。


谢谢大神给的提示,我马上试下,如果不嫌弃的话希望能给个例子~因为我理解的 rpc.exports 是当 【python】调用【javascript函数】的情景下使用的,不知道对于解决这个问题,有什么帮助

最后于 2021-1-11 20:15 被滚动不息的球编辑 ,原因:
2021-1-11 20:00
0
雪    币: 4423
活跃值: (3088)
能力值: ( LV10,RANK:175 )
在线值:
发帖
回帖
粉丝
12
滚动不息的球 DMemory 遇到了,用 ts 是用 frida-compile 编译过的,被编译后不能像写 js 那样直接调用了,可以用 `rpc.exports` 来代替。 ...
实时更新这部分没有理解错哈,我说的就是那意思,比如你用 ts 写完代码 npm watch 实时生成了 js 对吧,这时候你用 frida 把 js 注入到进程里,然后接着修改 ts ,随后被自动编译成了 js ,
此时!你注入的 js 内容也已经实时更新了。跟之前直接注入 js  是一样的,你可以试试哈
2021-1-11 20:29
0
雪    币: 60
活跃值: (1497)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
DMemory 实时更新这部分没有理解错哈,我说的就是那意思,比如你用 ts 写完代码 npm watch 实时生成了 js 对吧,这时候你用 frida 把 js 注入到进程里,然后接着修改 ts ,随后被自动编译 ...

明白了,所以大神是通过 【用 python 来启动 ts 脚本】 来解决 typescript 不能在控制台直接调用函数的问题的,这样就相当于是在 python 里去主动调用导出的函数,这样理解对吗?但是这样的话,也要每次重新启动 python 脚本,才能使用在 typescript 中新写的函数。

最后于 2021-1-11 20:55 被滚动不息的球编辑 ,原因:
2021-1-11 20:46
0
雪    币: 4423
活跃值: (3088)
能力值: ( LV10,RANK:175 )
在线值:
发帖
回帖
粉丝
14
我是用 frida 命令行注入脚本的,python 非必要的情况下一般不用。在控制台直接调用 rpc.exports 就行。
比如,在 ts 里这样赋值
```
rpc.exports = {
    main() { ...... }
}
```
然后直接在控制台调用:
> rpc.exports.main()
就可以了。
2021-1-11 20:58
0
雪    币: 60
活跃值: (1497)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
15
DMemory 我是用 frida 命令行注入脚本的,python 非必要的情况下一般不用。在控制台直接调用 rpc.exports 就行。 比如,在 ts 里这样赋值 ``` rpc.exports = { ...
万分感谢!懂了
2021-1-11 21:00
0
雪    币: 3139
活跃值: (588)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16

大佬,方便留个联系方式吗?chrome调试frida脚本起不来,frida命令行注入js根本没有显示

Chrome Inspector server listening on port 9229


最后于 2021-1-12 00:05 被小hanger编辑 ,原因:
2021-1-11 23:57
0
雪    币: 4423
活跃值: (3088)
能力值: ( LV10,RANK:175 )
在线值:
发帖
回帖
粉丝
17
wx_GodA 大佬,方便留个联系方式吗?chrome调试frida脚本起不来,frida命令行注入js根本没有显示Chrome Inspector server listening on port&nbsp ...
可以发私信哈。注入没显示?有什么报错吗?可以贴一下图说明一下
2021-1-12 10:09
0
雪    币: 443
活跃值: (1157)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
18
感谢分享
2021-1-12 10:14
0
雪    币: 3139
活跃值: (588)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19

私信不了,啥报错都没有直接就执行了

最后于 2021-1-12 10:23 被小hanger编辑 ,原因:
2021-1-12 10:22
0
雪    币: 4423
活跃值: (3088)
能力值: ( LV10,RANK:175 )
在线值:
发帖
回帖
粉丝
20
wx_GodA 私信不了,啥报错都没有直接就执行了
看版本是 12.8.20 ,可以更新一下版本试试。
私信你了。
2021-1-12 10:37
0
雪    币: 30
活跃值: (1307)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
你发的,我最近也开始整理,很好,加油。!
2021-1-12 11:48
0
雪    币: 3545
活跃值: (886)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
22
大佬  我在npm run watch  就报错了
export { default as v1 } from './v1.js';
^^^^^^
SyntaxError: Unexpected token 'export'
2021-1-12 11:54
0
雪    币: 4423
活跃值: (3088)
能力值: ( LV10,RANK:175 )
在线值:
发帖
回帖
粉丝
23
陌殇 大佬 我在npm run watch 就报错了

语法错误,你可以在 js 里做好导出,再把 js 引入

最后于 2021-1-12 14:15 被DMemory编辑 ,原因:
2021-1-12 14:09
0
雪    币: 3139
活跃值: (588)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
我试了mac的12.8.20可以成功,win10换成frida 14版的也不行,感觉不是版本的问题,我懵了。。。。
2021-1-12 14:30
0
雪    币: 4423
活跃值: (3088)
能力值: ( LV10,RANK:175 )
在线值:
发帖
回帖
粉丝
25
wx_GodA 我试了mac的12.8.20可以成功,win10换成frida 14版的也不行,感觉不是版本的问题,我懵了。。。。
加了参数但没有回显监听成功,可能端口被占用、权限限制、或者其他问题,需要排查一下了
2021-1-12 15:11
0
游客
登录 | 注册 方可回帖
返回
//