首页
社区
课程
招聘
[原创]《安卓逆向这档事》番外实战篇3-拨云见日之浅谈Flutter逆向
发表于: 2024-8-5 17:43 19177

[原创]《安卓逆向这档事》番外实战篇3-拨云见日之浅谈Flutter逆向

2024-8-5 17:43
19177

1.了解Flutter基本概念以及识别特征
2.了解Flutter应用的抓包对抗策略
3.了解Flutter反编译以及实战

1.某读app
2.proxypin
3.blutter

Flutter是Google构建在开源的Dart VM之上,使用Dart语言开发的移动应用开发框架,可以帮助开发者使用一套Dart代码就能快速在移动iOS 、Android上构建高质量的原生用户界面,同时还支持开发Web和桌面应用。
Flutter引擎是一个用于高质量跨平台应用的可移植运行时,由C/C++编写。它实现了Flutter的核心库,包括动画和图形、文件和网络I/O、辅助功能支持、插件架构,以及用于开发、编译和运行Flutter应用程序的Dart运行时和工具链。引擎将底层C++代码包装成 Dart代码,通过dart:ui暴露给 Flutter框架层。
flutter开源地址
flutter官网

[原创]Flutter概述和逆向技术发展时间线,带你快速了解

在逆向分析前,我们首先要确定测试目标是否用Flutter开发的。当使用Flutter构建Android APP时,在assets文件夹下会有dexopt和flutter_assets两个文件夹
图片
lib文件夹会有两个so文件:libapp.so和libflutter.so(flutter动态链接库,与实际业务代码无关)
图片

原理:

通过分析Flutter应用程序抛出的错误,可以定位到触发错误的源代码位置,错误指向了handshake.cc:352,这是一个处理SSL握手的源代码位置。

为了绕过SSL验证,需要找到一个合适的hook点,即源代码中可以被拦截和修改以改变程序行为的位置。ssl_verify_peer_cert函数是一个可能的hook点,但经过测试,仅仅修改这个函数的返回值并不能成功绕过SSL验证。
进一步分析源代码后,发现session_verify_cert_chain函数可以作为另一个hook点。这个函数在验证证书链时,如果证书验证失败,会返回一个错误。

session_verify_cert_chain函数定义在ssl_x509.cc,在该方法里可以看到有ssl_client和ssl_server两个字符串可以辅助定位方法
图片

在libflutter.so里搜索ssl_client定位到方法,内存搜刮函数前10字节定位,在运行时将返回函数改为true即可绕过证书链检查实现抓包(这里以64位的so为例)
图片

reFlutter开源地址
图片
1.pip3 install reflutter pip安装对应的库
2.输入命令:reflutter flutter.apk
选择1流量监控和拦截,输入PC端的IP地址后(cmd窗口输入ipconfig),将获取到release.RE.apk,但此apk尚未签名,需要我们手动签名(输入命令的过程需要全局代理)
图片

3.使用MT管理器或者uber-apk-signer.jar签名,输入命令:java -jar uber-apk-signer-1.2.1.jar --apk release.RE.apk。然后将重签名的apk安装到真机或者模拟器上。
4.设置BurpSuite的代理,端口为8083,绑定所有地址,并且勾选All interfaces,使非代理意识的客户端直接连接到侦听器。
BurpSuitePro-2.1
图片
图片
5.设置Drony的wifi代理主机名端口和BurpSuite一致,然后触发app就能抓到包了
图片

Reqable或者proxyPin直接抓包即可(工具下载看上一课)
图片
图片

使用readelf -s命令读取保存快照信息的libapp.so将会输出下面的内容

“快照”指的是 Flutter 应用在编译过程中生成的特定数据结构,用于加速应用的启动和运行。具体来说,快照包括四种类型:

_kDartVmSnapshotData: 代表 isolate 之间共享的 Dart 堆 (heap) 的初始状态。有助于更快地启动 Dart isolate,但不包含任何 isolate 专属的信息。

_kDartVmSnapshotInstructions:包含 VM 中所有 Dart isolate 之间共享的通用例程的 AOT 指令。这种快照的体积通常非常小,并且大多会包含程序桩 (stub)。

_kDartIsolateSnapshotData:代表 Dart 堆的初始状态,并包含 isolate 专属的信息。

_kDartIsolateSnapshotInstructions:包含由 Dart isolate 执行的 AOT 代码。

其中_kDartIsolateSnapshotInstructions是最为重要的,因为包含了所有要执行的AOT代码,即业务相关的代码。

1.(静态)解析libapp.so,即写一个解析器,将libapp.so中的快照数据按照其既定格式进行解析,获取业务代码的类的各种信息,包括类的名称、其中方法的偏移等数据,从而辅助逆向工作。
关于Flutter快照的具体刨析只需要看下面引用的两篇文章
Reverse engineering Flutter apps (Part 1) (tst.sh)
Reverse engineering Flutter apps (Part 2) (tst.sh)
2.(动态)编译修改过的libflutter.so并且重新打包到APK中,在启动APP的过程中,由修改过的引擎动态链接库将快照数据获取并且保存。

PS:不同版本的Dart引擎其快照格式不同,所以静态的方法就需要频繁跟着版本更新迭代,成本极高,而动态也需要重新编译对应版本的链接库。同时如果APP作者抹除版本信息和hash信息,则无从下手,且重打包APK极易被检测到。

静态方法推荐工具:blutter
动态方法推荐工具:reFlutter

环境:python3.10
1.首先安装git
下载地址

2.下载visual studio
下载地址

3.下载安装,在工作负荷里勾选"使用C++的桌面开发"
图片

4.clone项目(全程运行在代理环境否则会导致无法下载),或者下载解压到指定文件夹

5.进入到blutter文件夹,cmd窗口运行初始化脚本

图片
6.要打开x64 Native Tools Command Prompt,它可以在Visual Studio文件夹中找到
图片
7.把需要反编译的flutterapp用压缩包打开,提取v8a里的libflutter.solibapp.so(现在基本上是64位)解压到blutter文件夹,并创建一个输出结果的文件夹
图片
8.在刚才打开的x64窗口运行下面的命令(全局代理!),等待运行完后会在output文件下生成一些脚本信息
PS:blutter目前支持最新的版本的dart快照解析,如果这个跑不起来可以参考第四步手动配置

图片
图片

9.接下来ida加载libapp.so,然后ida左上角点击file,再点击Script file加载符号解析脚本
图片
10.至此可以看到so里的相关函数以显现
图片
协议实现:

百度云
阿里云
哔哩哔哩
教程开源地址
PS:解压密码都是52pj,阿里云由于不能分享压缩包,所以下载exe文件,双击自解压

[原创]Flutter概述和逆向技术发展时间线,带你快速了解
blutter
reFlutter
[翻译]Flutter 逆向初探
[原创]一种基于frida和drony的针对flutter抓包的方法
Android-Flutter逆向
Flutter Android APP 逆向系列 (一)

E/flutter (10371):  [ERROR:flutter/runtime/dart_isolate.cc(805)] Unhandled exception: 
 E/flutter (10371):  HandshakeException: Handshake error in client (OS Error: 
 E/flutter (10371):  NO_START_LINE(pem_lib.c:631)
 E/flutter (10371):  PEM routines(by_file.c:146)
 E/flutter (10371):  NO_START_LINE(pem_lib.c:631)
 E/flutter (10371):  PEM routines(by_file.c:146)
 E/flutter (10371):  CERTIFICATE_VERIFY_FAILED: self signed certificate in certificate chain(handshake.cc:352))
E/flutter (10371):  [ERROR:flutter/runtime/dart_isolate.cc(805)] Unhandled exception: 
 E/flutter (10371):  HandshakeException: Handshake error in client (OS Error: 
 E/flutter (10371):  NO_START_LINE(pem_lib.c:631)
 E/flutter (10371):  PEM routines(by_file.c:146)
 E/flutter (10371):  NO_START_LINE(pem_lib.c:631)
 E/flutter (10371):  PEM routines(by_file.c:146)
 E/flutter (10371):  CERTIFICATE_VERIFY_FAILED: self signed certificate in certificate chain(handshake.cc:352))
ret = ssl->ctx->x509_method->session_verify_cert_chain(
              hs->new_session.get(), hs, &alert)
              ? ssl_verify_ok
              : ssl_verify_invalid;
ret = ssl->ctx->x509_method->session_verify_cert_chain(
              hs->new_session.get(), hs, &alert)
              ? ssl_verify_ok
              : ssl_verify_invalid;
function hook_dlopen() {
    var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
    Interceptor.attach(android_dlopen_ext, {
        onEnter: function (args) {
            var so_name = args[0].readCString();
            if (so_name.indexOf("libflutter.so") >= 0) this.call_hook = true;
        }, onLeave: function (retval) {
            if (this.call_hook) hookFlutter();
        }
    });
}
 
function hook_ssl_verify_result(address) {
    Interceptor.attach(address, {
            onEnter: function(args) {
                console.log("Disabling SSL validation")
            },
            onLeave: function(retval) {
                console.log("Retval: " + retval);
                retval.replace(0x1);
            }
        });
    }
function hookFlutter() {
    var m = Process.findModuleByName("libflutter.so");
    //利用函数前10字节定位
    var pattern = "FF C3 01 D1 FD 7B 01 A9 FC 6F 02 A9FA 67 03 A9 F8 5F 04 A9 F6 57 05 A9 F4 4F 06 A9 08 0A 80 52 48 00 00 39";
    var res = Memory.scan(m.base, m.size, pattern, {
        onMatch: function(address, size){
            console.log('[+] ssl_verify_result found at: ' + address.toString());
        // Add 0x01 because it's a THUMB function
        // Otherwise, we would get 'Error: unable to intercept function at 0x9906f8ac; please file a bug'
            hook_ssl_verify_result(address);
        },
        onError: function(reason){
            console.log('[!] There was an error scanning memory');
        },
        onComplete: function() {
            console.log("All done")
        }
    });
}
function hook_dlopen() {
    var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
    Interceptor.attach(android_dlopen_ext, {
        onEnter: function (args) {
            var so_name = args[0].readCString();
            if (so_name.indexOf("libflutter.so") >= 0) this.call_hook = true;
        }, onLeave: function (retval) {
            if (this.call_hook) hookFlutter();
        }
    });
}
 
function hook_ssl_verify_result(address) {
    Interceptor.attach(address, {
            onEnter: function(args) {
                console.log("Disabling SSL validation")
            },
            onLeave: function(retval) {
                console.log("Retval: " + retval);
                retval.replace(0x1);
            }
        });
    }
function hookFlutter() {
    var m = Process.findModuleByName("libflutter.so");
    //利用函数前10字节定位
    var pattern = "FF C3 01 D1 FD 7B 01 A9 FC 6F 02 A9FA 67 03 A9 F8 5F 04 A9 F6 57 05 A9 F4 4F 06 A9 08 0A 80 52 48 00 00 39";
    var res = Memory.scan(m.base, m.size, pattern, {
        onMatch: function(address, size){
            console.log('[+] ssl_verify_result found at: ' + address.toString());
        // Add 0x01 because it's a THUMB function
        // Otherwise, we would get 'Error: unable to intercept function at 0x9906f8ac; please file a bug'

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 9
支持
分享
最新回复 (5)
雪    币: 254
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
竟然是前排
2024-8-5 21:42
0
雪    币: 441
活跃值: (731)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
前排围观
2024-8-13 10:41
0
雪    币: 2607
活跃值: (156)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
板凳来了 支持~!
2024-8-21 00:52
0
雪    币: 4922
活跃值: (4622)
能力值: ( LV10,RANK:171 )
在线值:
发帖
回帖
粉丝
5
感谢分享!
2024-8-22 13:57
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6

D:\blutter>python blutter.py .\arm64-v8a\ .\output

Dart version: 2.10.5, Snapshot: 8ee4ef7a67df9845fba331734198a953, Target: android arm64

flags: product no-dwarf_stack_traces_mode no-causal_async_stacks lazy_async_stacks no-lazy_dispatchers use_bare_instructions dedup_instructions no-"asserts" arm64-sysv no-null-safety

Dart version <2.15, force "no-analysis" option

Cloning into 'D:\blutter\dartsdk\v2.10.5'...

remote: Enumerating objects: 1848, done.

remote: Counting objects: 100% (1848/1848), done.

remote: Compressing objects: 100% (1427/1427), done.

remote: Total 1848 (delta 123), reused 1231 (delta 97), pack-reused 0 (from 0)

Receiving objects: 100% (1848/1848), 978.08 KiB | 12.54 MiB/s, done.

Resolving deltas: 100% (123/123), done.

remote: Enumerating objects: 22, done.

remote: Counting objects: 100% (22/22), done.

remote: Compressing objects: 100% (21/21), done.

remote: Total 22 (delta 0), reused 10 (delta 0), pack-reused 0 (from 0)

Receiving objects: 100% (22/22), 89.97 KiB | 467.00 KiB/s, done.

Updating files: 100% (22/22), done.

remote: Enumerating objects: 2432, done.

remote: Counting objects: 100% (2432/2432), done.

remote: Compressing objects: 100% (1910/1910), done.

remote: Total 2432 (delta 535), reused 1618 (delta 489), pack-reused 0 (from 0)

Receiving objects: 100% (2432/2432), 7.62 MiB | 11.33 MiB/s, done.

Resolving deltas: 100% (535/535), done.

Updating files: 100% (2600/2600), done.

-- Configuring done (1.1s)

-- Generating done (0.0s)

-- Build files have been written to: D:/blutter/build/dartvm2.10.5_android_arm64

[117/257] Building CXX object CMakeFiles\dartvm2.10.5_android_arm64.dir\runtime\vm\profiler_service.cc.obj

D:\blutter\dartsdk\v2.10.5\runtime\vm/scope_timer.h(38): warning C4566: 由通用字符名称“\u00B5”表示的字符不能在当前代码页(936)中表示出来

[124/257] Building CXX object CMakeFiles\dartvm2.10.5_android_arm64.dir\runtime\vm\regexp_assembler.cc.obj

D:\blutter\external\icu-windows\include\unicode/stringoptions.h(1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失

D:\blutter\external\icu-windows\include\unicode/uchar.h(3156): warning C4819: 该文件包含不能在当前代码页(936)中表示的字 符。请将该文件保存为 Unicode 格式以防止数据丢失

[134/257] Building CXX object CMakeFiles\dartvm2.10.5_android_arm64.dir\runtime\vm\regexp.cc.obj

D:\blutter\external\icu-windows\include\unicode/uniset.h(1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符 。请将该文件保存为 Unicode 格式以防止数据丢失

D:\blutter\external\icu-windows\include\unicode/uset.h(1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。 请将该文件保存为 Unicode 格式以防止数据丢失

D:\blutter\external\icu-windows\include\unicode/stringoptions.h(1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失

D:\blutter\external\icu-windows\include\unicode/uchar.h(3156): warning C4819: 该文件包含不能在当前代码页(936)中表示的字 符。请将该文件保存为 Unicode 格式以防止数据丢失

D:\blutter\external\icu-windows\include\unicode/uniset.h(714): warning C4819: 该文件包含不能在当前代码页(936)中表示的字 符。请将该文件保存为 Unicode 格式以防止数据丢失

[140/257] Building CXX object CMakeFiles\dartvm2.10.5_android_arm64.dir\runtime\vm\regexp_parser.cc.obj

D:\blutter\external\icu-windows\include\unicode/stringoptions.h(1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失

D:\blutter\external\icu-windows\include\unicode/uchar.h(3156): warning C4819: 该文件包含不能在当前代码页(936)中表示的字 符。请将该文件保存为 Unicode 格式以防止数据丢失

D:\blutter\external\icu-windows\include\unicode/uniset.h(1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符 。请将该文件保存为 Unicode 格式以防止数据丢失

D:\blutter\external\icu-windows\include\unicode/uset.h(1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。 请将该文件保存为 Unicode 格式以防止数据丢失

D:\blutter\external\icu-windows\include\unicode/uniset.h(714): warning C4819: 该文件包含不能在当前代码页(936)中表示的字 符。请将该文件保存为 Unicode 格式以防止数据丢失

[257/257] Linking CXX static library dartvm2.10.5_android_arm64.lib

-- Install configuration: "Release"

-- Installing: D:/blutter/dartsdk/v2.10.5/../../packages/lib/dartvm2.10.5_android_arm64.lib

-- Installing: D:/blutter/dartsdk/v2.10.5/../../packages/include/dartvm2.10.5

    此处省略一堆下载

-- Installing: D:/blutter/dartsdk/v2.10.5/../../packages/lib/cmake/dartvm2.10.5_android_arm64/dartvm2.10.5_android_arm64Config.cmake

-- Installing: D:/blutter/dartsdk/v2.10.5/../../packages/lib/cmake/dartvm2.10.5_android_arm64/dartvm2.10.5_android_arm64ConfigVersion.cmake

-- Configuring done (1.1s)

-- Generating done (0.0s)

-- Build files have been written to: D:/blutter/build/blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis

[1/22] Building CXX object CMakeFiles\blutter_dartvm2.10.5...arm64_no-compressed-ptrs_no-analysis.dir\cmake_pch.cxx.obj

FAILED: CMakeFiles/blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis.dir/cmake_pch.cxx.obj

D:\VISUAL~1\VC\Tools\MSVC\1441~1.341\bin\Hostx64\x64\cl.exe  /nologo /TP -DDART_PRECOMPILED_RUNTIME -DDART_TARGET_OS_ANDROID -DDART_TARGET_OS_WINDOWS_UWP -DEXCLUDE_CFE_AND_KERNEL_PLATFORM -DFRIDA_TEMPLATE_DIR=\"D:/blutter/scripts\" -DHAS_SHARED_CLASS_TABLE -DHAS_TYPE_REF -DNDEBUG -DNO_CODE_ANALYSIS -DNO_INIT_LATE_STATIC_FIELD -DNO_LAST_INTERNAL_ONLY_CID -DNO_METHOD_EXTRACTOR_STUB -DOLD_MAP_NO_IMMUTABLE -DOLD_MAP_SET_NAME -DPRODUCT -DTARGET_ARCH_ARM64 -DU_USING_ICU_NAMESPACE=0 -D_HAS_EXCEPTIONS=0 -external:ID:\blutter\packages\include\dartvm2.10.5 -external:ID:\blutter\blutter\..\external\capstone\include\capstone -external:W0 /DWIN32 /D_WINDOWS /EHsc /O2 /Ob2 /DNDEBUG -std:c++20 -MD /Oy /GR- /sdl- /Oi /GL /Gy /Zc:wchar_t /Zc:inline /YcD:/blutter/build/blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis/CMakeFiles/blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis.dir/cmake_pch.hxx /FpD:/blutter/build/blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis/CMakeFiles/blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis.dir/./cmake_pch.cxx.pch /FID:/blutter/build/blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis/CMakeFiles/blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis.dir/cmake_pch.hxx /showIncludes /FoCMakeFiles\blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis.dir\cmake_pch.cxx.obj /FdCMakeFiles\blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis.dir\ /FS -c D:\blutter\build\blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis\CMakeFiles\blutter_dartvm2.10.5_android_arm64_no-compressed-ptrs_no-analysis.dir\cmake_pch.cxx

D:/blutter/blutter/src/pch.h(43): error C2061: 语法错误: 标识符“LinkedHashSet”

D:/blutter/blutter/src/pch.h(46): error C2061: 语法错误: 标识符“LinkedHashSet”

D:/blutter/blutter/src/pch.h(54): error C2065: “kLinkedHashSetCid”: 未声明的标识符

D:/blutter/blutter/src/pch.h(57): error C2065: “kLinkedHashSetCid”: 未声明的标识符

ninja: build stopped: subcommand failed.

Traceback (most recent call last):

  File "D:\blutter\blutter.py", line 229, in <module>

    main(args.indir, args.outdir, args.rebuild, args.vs_sln, args.no_analysis)

  File "D:\blutter\blutter.py", line 211, in main

    main2(libapp_file, libflutter_file, outdir, rebuild_blutter, create_vs_sln, no_analysis)

  File "D:\blutter\blutter.py", line 202, in main2

    build_and_run(input)

  File "D:\blutter\blutter.py", line 187, in build_and_run

    cmake_blutter(input)

  File "D:\blutter\blutter.py", line 145, in cmake_blutter

    subprocess.run([NINJA_CMD], cwd=builddir, check=True)

  File "E:\python\Lib\subprocess.py", line 571, in run

    raise CalledProcessError(retcode, process.args,

subprocess.CalledProcessError: Command '['ninja']' returned non-zero exit status 1.


大佬报错啊,这是什么问题。。

2024-9-1 00:43
0
游客
登录 | 注册 方可回帖
返回
//