刚刚开始学Android逆向,发现Frida是个好东西,于是赶紧下载研究一番。
下载源码编译,切换到最新版16.0.11, 编译之前注意先更新nodejs
| 
      1
      
      2
      | wget -qO-https://deb.nodesource.com/setup_16.x | sudo -E bash -sudo apt-get install -y nodejs | 
然后执行
| 
      1
      
      2
      | make core-android-arm64 make tools-linux-x86_64 PYTHON=$HOME/miniconda3/bin/python | 
就可以成功生成frida-server以及frida相关tools,于是push到手机上执行
| 
      1
      
      2
      | redfin:/data/local/tmp # ./frida-server                                                       {"type":"error","description":"Error: Java API not available","stack":"Error: Java API not available\n    at _checkAvailable (frida/node_modules/frida-java-bridge/index.js:298:1)\n    at _.perform (frida/node_modules/frida-java-bridge/index.js:203:1)\n    at /internal-agent.js:490:6","fileName":"frida/node_modules/frida-java-bridge/index.js","lineNumber":298,"columnNumber":1} | 
报错了...额,继续往下试试
把手机上的名为com.example.myapplication的demo app放到前台,然后注入一个helloword级别的js
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      
      8
      
      9
      | setTimeout(    function() {        Java.perform(function() {            console.log("Hello frida!")        })    })//test.js | 
然后执行
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      
      8
      
      9
      
      10
      
      11
      
      12
      
      13
      | (base) /data/code/OpenSource/crack/frida/build/frida-linux-x86_64/bin(16.0.11✔) ./frida -U -l ../../../../frida_script/test.js com.example.myapplication     ____    /_  |   Frida 16.0.11-A world-classdynamic instrumentation toolkit   | (_| |    > _  |   Commands:   /_/|_|       help-> Displays the helpsystem   . . . .       object?   -> Display information about 'object'   . . . .       exit/quit -> Exit   . . . .   . . . .   More info at https://frida.re/docs/home/   . . . .   . . . .   Connected to AOSP on redfin (id=0A051FDD4003BW)Failed to spawn: cannot read properties of undefined (reading 'getRunningAppProcesses') | 
咳,出师不利,不过反正我也是做c++开发的,虽然不懂javascript, 连蒙带猜也能看个差不多,那就研究研究报错原因吧。
从现有信息来看,第一怀疑对象应该是那个报错"Error: Java API not available", 也指明了报错位置at _checkAvailable (frida/nodemodules/frida-java-bridge/index.js:298:1) at .perform (frida/node_modules/frida-java-bridge/index.js:203:1)\n ,那就在源码里搜索下。
发现这个index.js位于build/tmp-android-arm64/frida-gum/bindings/gumjs/node_modules/frida-java-bridge目录,
然后看下perform实现
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      
      8
      
      9
      
      10
      
      11
      
      12
      
      13
      
      14
      
      15
      
      16
      | perform (fn) {   this._checkAvailable();   if(!this._isAppProcess() || this.classFactory.loader !==null) {     try{       this.vm.perform(fn);     } catch (e) {       Script.nextTick(() => { throw e; });     }   } else{     this._pendingVmOps.push(fn);     if(this._pendingVmOps.length ===1) {       this._performPendingVmOpsWhenReady();     }   } } | 
这个函数第一行就是_checkAvailable,跟进去看看
| 
      1
      
      2
      
      3
      
      4
      
      5
      | _checkAvailable () {  if(!this.available) {    throw new Error('Java API not available');  }} | 
果然报错就是在这里,那么关键的判断就是available了,再跳过去看看
| 
      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
      | get available () {  returnthis._tryInitialize();}_tryInitialize () {  if(this._initialized) {    returntrue;  }  if(this._apiError !==null) {    throw this._apiError;  }  let api;  try{    api =getApi();    this.api =api;  } catch (e) {    this._apiError =e;    throw e;  }  if(api ===null) {    returnfalse;  //只有这里返回为false  }  const vm =new VM(api);  this.vm =vm;  Types.initialize(vm);  ClassFactory._initialize(vm, api);  this.classFactory =new ClassFactory();  this._initialized =true;  returntrue;} | 
那么只有一种可能,就是getApi()返回为null,再跟进去看看,这个getApi的有几层跳转如下
| 
      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
      | //index.jsconst getApi =require('./lib/api');//./lib/api.jslet { getApi, getAndroidVersion } =require('./android');try{  getAndroidVersion();} catch (e) {  getApi =require('./jvm').getApi;}module.exports =getApi;//./lib/android.jsfunction getApi () {  if(cachedApi ===null) {    cachedApi =_getApi();  }  returncachedApi;}function _getApi () {  const vmModules =Process.enumerateModules()    .filter(m => /^lib(art|dvm).so$/.test(m.name))    .filter(m => !/\/system\/fake-libs/.test(m.path));  if(vmModules.length ===0) {    returnnull; //这里返回了null  }  //以下代码省略...} | 
看代码的意思是进程内没找到加载的libart.so或者libdvm.so,真奇怪了,你可是android进程,没有虚拟机咋跑的?
| 
      1
      
      2
      
      3
      | redfin:/# ps -ef | grep com.example.myapplication                                                                                                                                                                u0_a108      2716821776011:18:29?     00:00:03com.example.myapplicationroot         28255282503514:11:06pts/100:00:00grep com.example.myapplication | 
然后检查下maps
| 
      1
      
      2
      | redfin:/# cat /proc/27168/maps | grep libart.so                                                                                                                                                                  1|redfin:/# | 
果然没有,见鬼了,我把maps的输出保存下来,在编辑器里查看才发现了端倪,原来进程加载的so里有一个libartd.so,因为这个手机是pixel5,系统是我自己下载AOSP编译的,选择的是eng版,这样编出来的系统so都是debug版,也就意味着后缀都有一个d,难怪frida找不到。
知道了原因,那解决方案就简单了,把那段查找libart.so的代码改成查找libartd.so即可。不过还有一个问题,这个frida-java-bridge是编译的时候从网上下载的,我们本地修改会被覆盖,那么就得研究下frida的编译系统了,让它使用我们本地的frida-java-bridge。
首先在源码里搜索frida-java-bridge,果不其然,是在一个generate-runtime.py里面,代码如下
| 
      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
      | XACT_DEPS ={    "frida-java-bridge": "6.2.3",    "frida-objc-bridge": "7.0.2",    "frida-swift-bridge": "2.0.6"}defgenerate_runtime(backends, arch, endian, input_dir, gum_dir, capstone_incdir, libtcc_incdir, quickcompile, output_dir):    frida_compile =output_dir /"node_modules"/".bin"/make_script_filename("frida-compile")    ifnotfrida_compile.exists():        pkg_files =[output_dir /"package.json", output_dir /"package-lock.json"]        forf inpkg_files:            iff.exists():                f.unlink()        (output_dir /"tsconfig.json").write_text("{ \"files\": [], \"compilerOptions\": { \"typeRoots\": [] } }", encoding="utf-8")        node_modules =output_dir /"node_modules"        ifnode_modules.exists():            shutil.rmtree(node_modules)        npm =os.environ.get("NPM", make_script_filename("npm"))        try:            subprocess.run([npm, "init", "-y"],                           capture_output=True,                           cwd=output_dir,                           check=True)            subprocess.run([npm, "install"] +[f"{name}@{version_spec}"forname, version_spec inRELAXED_DEPS.items()],                           capture_output=True,                           cwd=output_dir,                           check=True)            subprocess.run([npm, "install", "-E"] +[f"{name}@{version_spec}"forname, version_spec inEXACT_DEPS.items()],                           capture_output=True,                           cwd=output_dir,                           check=True) <=========这里下载了EXACT_DEPS里面的依赖项 | 
看来是编译的时候,用npm install -E把frida-java-bridge下载下来了,那么接下来就是要把这个依赖项改成我们本地的。
首先下载一个到本地
| 
      1
      | git clone https://github.com/frida/frida-java-bridge.git | 
然后全局查找libart.so,改成libartd.so
 
这时候需要用到npm link把我们本地的frida-java-bridge注册到系统中
| 
      1
      
      2
      
      3
      
      4
      
      5
      
      6
      
      7
      
      8
      
      9
      
      10
      
      11
      
      12
      
      13
      
      14
      | (base) /data/code/OpenSource/crack/frida/frida-java-bridge (main ✗) npm linkup to date, audited 3packages in574msfound 0vulnerabilities(base) /data/code/OpenSource/crack/frida/frida-java-bridge (main ✗) npm install   added 214packages, andaudited 215packages in1s62packages are looking forfunding  run `npm fund` fordetailsfound 0vulnerabilities | 
然后要让frida中的编译脚本指向我们这个,也就是不要用npm install了,需要改成npm link, 修改generate-runtime.py代码如下
 
然后再重新编译, 生成后推到手机执行
 
果然没有报错了,非常完美!
更多【frida-server运行报错问题的解决】相关视频教程:www.yxfzedu.com