Flutter框架App逆向

前言:

Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。Flutter应用程序是用Dart编写的,这是一种由Google在7年多前创建的语言。

拦截 HTTPS 流量

我们不能通过将代理CA添加到系统CA存储来绕过SSL验证。为了解决这个问题,我们必须深入研究libflutter.so,并找出我们需要修补或hook的,以验证我们的证书。Dart使用Google的BoringSSL来处理与SSL相关的所有内容,幸运的是Dart和BoringSSL都是开源的。
当向Burp发送HTTPS流量时,Flutter应用程序实际上会抛出一个错误,我们可以将其作为起点:

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

我们需要做的第一件事是在BoringSSL库中找到这个错误。该错误实际上已向我们显示了触发错误的位置:handshake.cc:352。Handshake.cc确实是BoringSSL库的一部分,并且包含了执行证书验证的逻辑。第352行的代码如下所示,这很可能就是我们看到的错误。行数并不完全匹配,但这很可能是版本差异的结果。

if (ret == ssl_verify_invalid) { OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); ssl_send_alert(ssl, SSL3_AL_FATAL, alert); }

这是ssl_verify_peer_cert函数的一部分,该函数返回ssl_verify_result_t枚举,它在第2290行的ssl.h中被定义:

enum ssl_verify_result_t BORINGSSL_ENUM_INT { ssl_verify_ok, ssl_verify_invalid, ssl_verify_retry, };

如果我们可以将ssl_verify_peer_cert的返回值更改为ssl_verify_ok (=0),那么我们就可以继续了。然而,在这个方法中有很多事情正在发生,Frida只能更改函数的返回值。如果我们更改这个值,它仍会因为上面的ssl_send_alert()函数调用而失败(相信我,我试过)。

让我们找一个更好的hook的方法。handshake.cc的代码段正上方是以下代码,这是验证链的方法的实际部分:

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函数在第362行的ssl_x509.cc中被定义。此函数还返回原始数据类型(布尔值),并且是一个更好的hook选项。如果此函数中的检查失败,则它仅通过OPENSSL_PUT_ERROR报告问题,但它没有像ssl_verify_peer_cert函数那样的问题。OPENSSL_PUT_ERROR是err.h中第418行被定义的宏,其中包含源文件名。这与用于Flutter应用程序的错误的宏相同。

#define OPENSSL_PUT_ERROR(library, reason) \ ERR_put_error(ERR_LIB_##library, 0, reason, __FILE__, __LINE__)

既然我们知道要hook哪个函数了,现在我们要做的就是在libflutter.so中找到它。在session_verify_cert_chain函数中多次调用OPENSSL_PUT_ERROR宏,这样可以使用Ghidra轻松的找到正确的方法。因此,将库导入Ghidra,使用Search -> Find Strings并搜索x509.cc。

这里只有4个XREF,因此很容易找到一个看起来像session_verify_cert_chain的函数:

其中一个函数取2个整数,1个“undefined未定义”,并且包含一个对OPENSSL_PUT_ERROR(FUN_00316500)的单独调用。在我的libflutter.so版本中为FUN_0034b330。现在你要做的是从一个导出函数计算该函数的偏移量并将其hook。我通常会采用一种懒惰的方法,复制函数的前10个字节,并检查该模式出现的频率。如果它只出现一次,我就知道我找到了这个函数,并且我可以hook它。这很有用,因为我经常可以为库的不同版本使用相同的脚本。 使用基于偏移的方法,这更加困难。这很有用,因为我可以经常对不同版本的库使用相同的脚本。对于基于偏移量的方法,更加困难。

所以,现在我们让Frida在libflutter.so库中搜索这个模式:

Java.perform(function () {
var m = Process.findModuleByName("libflutter.so");
var pattern = "2d e9 f0 4f a3 b0 82 46 50 20 10 70";
var res = Memory.scan(m.base, m.size, pattern, {
        onMatch: function(address, size){
            console.log('[+] ssl_verify_result found at: ' + address.toString());
        },
        onError: function(reason){
            console.log('[!] There was an error scanning memory');
        },
        onComplete: function() {
            console.log("All done")
        }
    });
});

在我的Flutter应用程序上运行此脚本的结果如下:

[+] ssl_verify_result found at: 0x08647fd0
All done

现在,我们只需使用Interceptor将返回值更改为1 (true):

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);
            }
        }); 
}
            
Java.perform(function () {
var m = Process.findModuleByName("libflutter.so");
var pattern = "2d e9 f0 4f a3 b0 82 46 50 20 10 70";
var res = Memory.scan(m.base, m.size, pattern, {
        onMatch: function(address, size){
            console.log('[+] ssl_verify_result found at: ' + address.toString());
            hook_ssl_verify_result(address.add(0x01)); 
        },
        onError: function(reason){
            console.log('[!] There was an error scanning memory');
        },
        onComplete: function() {
            console.log("All done")
        }
    });
});

设置Drony并使用此脚本Hook应用程序后,我们就可以抓到包了:


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269