[原创]frida-server运行报错问题的解决
2023-4-5 15:59:15 Author: bbs.pediy.com(查看原文) 阅读量:12 收藏

[原创]frida-server运行报错问题的解决

2天前 1692

[原创]frida-server运行报错问题的解决

刚刚开始学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

{"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-class dynamic instrumentation toolkit

   | (_| |

    > _  |   Commands:

   /_/ |_|       help      -> Displays the help system

   . . . .       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 () {

  return this._tryInitialize();

}

_tryInitialize () {

  if (this._initialized) {

    return true;

  }

  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) {

    return false;  //只有这里返回为false

  }

  const vm = new VM(api);

  this.vm = vm;

  Types.initialize(vm);

  ClassFactory._initialize(vm, api);

  this.classFactory = new ClassFactory();

  this._initialized = true;

  return true;

}

那么只有一种可能,就是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.js

const getApi = require('./lib/api');

// ./lib/api.js

let { getApi, getAndroidVersion } = require('./android');

try {

  getAndroidVersion();

} catch (e) {

  getApi = require('./jvm').getApi;

}

module.exports = getApi;

// ./lib/android.js

function getApi () {

  if (cachedApi === null) {

    cachedApi = _getApi();

  }

  return cachedApi;

}

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) {

    return null; // 这里返回了null

  }

  //以下代码省略...

}

看代码的意思是进程内没找到加载的libart.so或者libdvm.so,真奇怪了,你可是android进程,没有虚拟机咋跑的?

那就看下这个进程加载了什么,首先找到进程id

1

2

3

redfin:/

u0_a108      27168 21776 0 11:18:29 ?     00:00:03 com.example.myapplication

root         28255 28250 35 14:11:06 pts/1 00:00:00 grep com.example.myapplication

然后检查下maps

果然没有,见鬼了,我把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"

}

def generate_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")

    if not frida_compile.exists():

        pkg_files = [output_dir / "package.json", output_dir / "package-lock.json"]

        for f in pkg_files:

            if f.exists():

                f.unlink()

        (output_dir / "tsconfig.json").write_text("{ \"files\": [], \"compilerOptions\": { \"typeRoots\": [] } }", encoding="utf-8")

        node_modules = output_dir / "node_modules"

        if node_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}" for name, version_spec in RELAXED_DEPS.items()],

                           capture_output=True,

                           cwd=output_dir,

                           check=True)

            subprocess.run([npm, "install", "-E"] + [f"{name}@{version_spec}" for name, version_spec in EXACT_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 link

up to date, audited 3 packages in 574ms

found 0 vulnerabilities

(base) /data/code/OpenSource/crack/frida/frida-java-bridge (main ✗) npm install  

added 214 packages, and audited 215 packages in 1s

62 packages are looking for funding

  run `npm fund` for details

found 0 vulnerabilities

然后要让frida中的编译脚本指向我们这个,也就是不要用npm install了,需要改成npm link, 修改generate-runtime.py代码如下
runtime

然后再重新编译, 生成后推到手机执行
无报错

果然没有报错了,非常完美!

[2023春季班]2023,新的征程,脱壳机更新、iOS/eBPF、赠送云手机套装!一块裸板虚拟化五个容器云手机!3月25日起同时上调价格并赠送新设备!

返回


文章来源: https://bbs.pediy.com/thread-276755.htm
如有侵权请联系:admin#unsafe.sh