吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7074|回复: 131
收起左侧

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

  [复制链接]
正己 发表于 2024-8-5 17:36

图片|800

一、课程目标

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

二、工具

1.某读app
2.proxypin
3.blutter

三、课程内容

1.Flutter简介

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概述和逆向技术发展时间线,带你快速了解

2.Flutter特征

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

3.Flutter抓包对抗

原理:

  1. Dart语言标准库的网络请求不走Wi-Fi代{过}{滤}理:Flutter使用的是Dart语言,其标准库中的网络请求不会通过代{过}{滤}理发送,这与许多其他应用不同。常规的抓包工具通常依赖于代{过}{滤}理来捕获网络流量,因此无法捕获Flutter应用的网络请求。
  2. Dart SDK中的证书信任:Dart SDK在Android平台上强制只信任系统目录下的证书。这意味着Flutter应用不会信任用户安装的证书,除非这些证书位于Android系统的/system/etc/security/cacerts目录中。这是通过Dart源码中的runtime/bin/security_context_linux.cc文件实现的。

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

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

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

ret = ssl->ctx->x509_method->session_verify_cert_chain(
              hs->new_session.get(), hs, &alert)
              ? ssl_verify_ok
              : ssl_verify_invalid;

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

1.hook_ssl_client

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

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")
        }
    });
}

2.reflutter之patch

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就能抓到包了
图片

3.Reqable&proxyPin(推荐)

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

4.Flutter反编译

1.快照

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

Symbol table '.dynsym' contains 6 entries:
Num:    Value          Size Type    Bind   Vis      Ndx Name
 0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
 1: 000000000014c000 29728 OBJECT  GLOBAL DEFAULT    7 _kDartVmSnapshotInstructi
 2: 0000000000153440 0x22bd30 OBJECT  GLOBAL DEFAULT    7 _kDartIsolateSnapshotInst
 3: 0000000000000200 15248 OBJECT  GLOBAL DEFAULT    2 _kDartVmSnapshotData
 4: 0000000000003dc0 0x147af0 OBJECT  GLOBAL DEFAULT    2 _kDartIsolateSnapshotData
 5: 00000000000001c8    32 OBJECT  GLOBAL DEFAULT    1 _kDartSnapshotBuildId

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

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

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

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

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

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

2.逆向方法

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

3.blutter实战

环境:python3.10
1.首先安装git
下载地址
图片|500
2.下载visual studio
下载地址
图片|500
3.下载安装,在工作负荷里勾选"使用C++的桌面开发"
图片

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

git clone https://github.com/worawit/blutter --depth=1

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

python .\scripts\init_env_win.py

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

python blutter.py .\arm64-v8a\ .\output

图片
图片

asm 对dart语言的反编译结果,里面有很多dart源代码的对应偏移
ida_script so文件的符号表还原脚本
blutter_frida.js目标应用程序的 frida 脚本模板
objs.txt对象池中对象的完整(嵌套)转储,对象池里面的方法和相应的偏移量
pp.txt对象池中的所有 Dart 对象

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

import hashlib  
import base64  
import requests  

headers = {  
    'user-agent': 'Dart/3.1 (dart:io)',  
    'content-type': 'application/json; charset=utf-8',  
    'accept-encoding': 'gzip',  
    'host': 'api.mandu.pro',  
    'Content-Length': '98',  
}  
def hash_and_encode(input_string):  
    sha256_hash = hashlib.sha256()  
    sha256_hash.update(input_string.encode('utf-8'))  
    hash_bytes = sha256_hash.digest()  
    base64_encoded = base64.b64encode(hash_bytes).decode('utf-8')  
    return base64_encoded  

input_string = "md123456"  
result = hash_and_encode(input_string)  

json_data = {  
    'account': 'xxx@qq.com',  
    'type': 1,  
    'password': result,  
}  

response = requests.post('https://api.xxx.pro/user/session', headers=headers, json=json_data)  
print(response.text)

六、视频及课件地址

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

七、其他章节

《安卓逆向这档事》一、模拟器环境搭建
《安卓逆向这档事》二、初识APK文件结构、双开、汉化、基础修改
《安卓逆向这档事》三、初识smail,vip终结者
《安卓逆向这档事》四、恭喜你获得广告&弹窗静默卡
《安卓逆向这档事》五、1000-7=?&动态调试&Log插桩
《安卓逆向这档事》六、校验的N次方-签名校验对抗、PM代{过}{滤}理、IO重定向
《安卓逆向这档事》七、Sorry,会Hook真的可以为所欲为-Xposed快速上手(上)模块编写,常用Api
《安卓逆向这档事》八、Sorry,会Hook真的可以为所欲为-xposed快速上手(下)快速hook
《安卓逆向这档事》九、密码学基础、算法自吐、非标准加密对抗
《安卓逆向这档事》十、不是我说,有了IDA还要什么女朋友?
《安卓逆向这档事》十二、大佬帮我分析一下
《安卓逆向这档事》番外实战篇1-某电影视全家桶
《安卓逆向这档事》十三、是时候学习一下Frida一把梭了(上)
《安卓逆向这档事》十四、是时候学习一下Frida一把梭了(中)
《安卓逆向这档事》十五、是时候学习一下Frida一把梭了(下)
《安卓逆向这档事》十六、是时候学习一下Frida一把梭了(终)
《安卓逆向这档事》十七、你的RPCvs佬的RPC
《安卓逆向这档事》番外实战篇2-【2024春节】解题领红包活动,启动!
《安卓逆向这档事》十八、表哥,你也不想你的Frida被检测吧!(上)
《安卓逆向这档事》十九、表哥,你也不想你的Frida被检测吧!(下)
《安卓逆向这档事》二十、抓包学得好,牢饭吃得饱(上)

八、参考文档

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

免费评分

参与人数 42威望 +1 吾爱币 +69 热心值 +41 收起 理由
yangjinkun + 1 + 1 用心讨论,共获提升!
gomane + 1 + 1 非常好的fultter逆向教程,解决我很多疑问,浅显易懂!!
junjia215 + 1 + 1 谢谢@Thanks!
Golive180 + 1 + 1 谢谢@Thanks!
zl20211222 + 1 + 1 谢谢@Thanks!
补色 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
crizquan + 1 + 1 我很赞同!
jxhuangwei + 1 + 1 谢谢@Thanks!
世忘nb + 1 + 1 谢谢@Thanks!
binghe01 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
yyyao2008 + 1 + 1 用心讨论,共获提升!
Mr.Xuan + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
笨笨家的唯一 + 1 + 1 我很赞同!
cajs0001 + 1 热心回复!
love1999 + 1 + 1 我很赞同!
GenW + 10 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
zhangxu888 + 1 + 1 我很赞同!
MFC + 1 + 1 谢谢@Thanks!
Koourl + 1 + 1 谢谢@Thanks!
204386979 + 1 + 1 我很赞同!
mengxinb + 1 + 1 感谢版主更新!
pdcba + 1 + 1 谢谢@Thanks!
ICanHearYou + 1 + 1 我很赞同!
似水流年2015 + 2 + 1 佬,能出一期uniapp打包的apk加弹窗的教程吗,用np一键加弹窗不生效
woyucheng + 1 + 1 谢谢@Thanks!
cuteapi + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
fengbolee + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Leaf08 + 1 谢谢@Thanks!
allspark + 1 + 1 用心讨论,共获提升!
RickSanchez + 1 + 1 我很赞同!
pp67868450 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
offerking + 1 + 1 我很赞同!
風信子 + 1 + 1 谢谢@Thanks!
janken + 1 + 1 热心回复!
Courser + 1 + 1 谢谢@Thanks!
chinawolf2000 + 1 + 1 热心回复!
laos + 1 + 1 谢谢@Thanks!
leospring + 1 + 1 谢谢@Thanks!
如果我是DJ? + 1 + 1 谢谢@Thanks!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
vmoranv + 1 我很赞同!
Skdf123 + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

laos 发表于 2024-8-5 23:29
正己 发表于 2024-8-5 23:24
decrypt方法,网上一堆教程

嗯我是已经解密了,但是因为它解密后的js还全是混淆的,然后就不知道怎么入手调试了
不苦小和尚 发表于 2024-8-29 18:10
正己 发表于 2024-8-29 15:49
算法助手一键开启

不行的,有各种检测,写了这个脚本也不行,xpose,frida,
[Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
function load_all_class() {
if (Java.available) {
    Java.perform(function () {
 
        var DexFileclass = Java.use("dalvik.system.DexFile");
        var BaseDexClassLoaderclass = Java.use("dalvik.system.BaseDexClassLoader");
        var DexPathListclass = Java.use("dalvik.system.DexPathList");
 var hookedClasses = new Set(); // 用于跟踪已经 hook 的类
        Java.enumerateClassLoaders({
            onMatch: function (loader) {
                console.log("loader:", loader);
                 
                // 为了排除 BootClassLoader 和不相关的 ClassLoader
                if ((loader.toString().indexOf("java.lang.BootClassLoader") === -1)) {
                    try {
                        var BaseDexClassLoader_obj = Java.cast(loader, BaseDexClassLoaderclass);
                        var pathList = BaseDexClassLoader_obj.pathList.value;
                        var pathList_obj = Java.cast(pathList, DexPathListclass);
                        var ElementsArray = pathList_obj.dexElements.value;
                        console.log("ElementsArray---->", ElementsArray);
                         
                        if(hookedClasses.has(className)){return}
                         
                        // 遍历 ElementsArray,查找指定的 .apk 文件
                        for (var index in ElementsArray) {
                            var element = ElementsArray[index];
                            try {
                                try { var dexfile = element.dexFile.value; } catch (e) {}
                                if (dexfile) {
                                    var dexfileobj = Java.cast(dexfile, DexFileclass);
                                    const classNames = [];
                                    const enumeratorClassNames = dexfileobj.entries();
 
                                    // 查找指定的 .apk 文件
                                    if (dexfileobj.toString().indexOf("/data/app/com.dlxx.mam.Internal-dJ7kxt_bZ0UnShdkHHubMg==/base.apk") >= 0 ) {
                                        console.log("Found .apk file in dexfile: ", dexfileobj);
                                         
                                        while (enumeratorClassNames.hasMoreElements()) {
                                            var className = enumeratorClassNames.nextElement().toString();
                                           // console.log("ClassName : " + className);
                                            classNames.push(className);
 
                                            // 指定加载某个类
                                            if (className=="com.tencent.tbs.core.webkit.WebView"  ) {
                                                var webviewClass = Java.use("com.tencent.tbs.core.webkit.WebView");
                                                      
                                                console.log("ClassName : " + className);
                                            // Hook WebView 的构造方法
                                            var constructors = webviewClass['$init'].overloads;
                                            for (var i = 0; i < constructors.length; i++) {
                                                var constructor = constructors[i];
                                                var signature = constructor.signature;
                                     
                                                // Hook 构造方法
                                                constructor.implementation = function () {
                                                    console.log('WebView constructor called with signature: ' + signature);
                                                    var instance = constructor.call(this, arguments);
                                     
                                                    // 在 WebView 实例创建后立即调用 setWebContentsDebuggingEnabled
                                                    if (instance) {
                                                        instance.setWebContentsDebuggingEnabled(true);
                                                    }
                                     
                                                    return instance;
                                                };
                                            }
                                     
                                            // Hook setWebContentsDebuggingEnabled 方法
                                            webviewClass.setWebContentsDebuggingEnabled.implementation = function (enabled) {
                                                console.log('setWebContentsDebuggingEnabled called with enabled: ' + enabled);
                                                this.setWebContentsDebuggingEnabled(enabled);
                                            };
 
                                          loader.loadClass(className);
                                              // 标记为已 hook
                                                hookedClasses.add(className);  
                                              
                                            }
                                        }
                                    }
                                }
                            } catch (e) {
                                console.log('异常了' + e);
                            }
                        }
                    } catch (e) {
                        console.log('异常了2' + e);
                    }
                }
            },
            onComplete: function () {
                console.log("[*]load_all_class success!");
            }
        })
    });
}
 楼主| 正己 发表于 2024-8-5 17:39
视频晚上剪辑

点评

技术突飞猛进,一日千里,可喜可贺  发表于 2024-10-27 00:39
Ly1988 发表于 2024-8-5 17:52
感谢分享,坐等
FtsOZz 发表于 2024-8-5 18:31
flutter开发的app还是很丝滑的
如果我是DJ? 发表于 2024-8-5 18:46
前排,已经跟不上正己大佬了
hyxhhh826 发表于 2024-8-5 18:49
大神出手了   厉害
honwam 发表于 2024-8-5 18:56
非常不错的教程,谢谢分享
leospring 发表于 2024-8-5 19:16
大佬又出新佳作 强
Arcticlyc 发表于 2024-8-5 19:28
FtsOZz 发表于 2024-8-5 18:31
flutter开发的app还是很丝滑的

reqable 就是用 flutter 开发的
PastYiHJ 发表于 2024-8-5 19:43
前来围观 感谢大佬
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-22 08:46

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表