ホームページ  >  記事  >  運用・保守  >  フリダでAPKネットワークパッケージを取得する方法

フリダでAPKネットワークパッケージを取得する方法

WBOY
WBOY転載
2023-05-16 19:16:381447ブラウズ

1. 落とし穴の分析に重点を置きます

パケットをキャプチャするためにパケットをキャプチャするのではなく、システムの観点からフック ポイントを探します。

1.okhttp 呼び出しプロセス

public static final MediaType JSON
= MediaType.get("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}

クライアントの重要なコードは client.newCall() にあります。上記は okhttp 公式 Web サイトの例です。ここでのインターフェース呼び出しから始まり、最終的には okhttp フレームワークに呼び出されますが、okhttp は元々 SDK でしたが、その後 AOSP がシステムに統合されたため、フレームワーク層に分類されます。

フレームワーク層については詳しく説明しませんが、主に次の Java クラスです:

com.android.okhttp.internal.huc.HttpURLConnectionImpl
com.android.okhttp.internal.http.HttpEngine
com.android.okhttp.internal.http.RetryableSink
com.android.okhttp.internal.http.CacheStrategy$Factory

実際には、client.newCall は最終的に URL 経由で接続を取得します

HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

ここでの urlConnection は次のとおりです。実際には HttpURLConnectionImpl のインスタンス。このクラスには getInputStream getOutputStream メソッドがあり、HttpEngine の getBufferedRequestBody と getResponse を内部でそれぞれ呼び出します。最初はこの2つのインターフェースをフックしてみましたが、例えばgetResponseをフックするとレスポンスが出力できるようになりましたが、

後になって、Requestはヘッダーのみ出力できてボディは出力できないことがわかりました。そこで、解析に没頭したところ、getBufferedRequestBody 関数を使用してシンクを取得できることがわかりました。最後に、RetryableSink がブレークスルー ポイントとして使用されます。たとえば、その write 関数をフックすると、本文を出力できます。 write 関数は、アプリ レベルの urlConnection.getOutputStream().write に対応します。

後で、リクエストの場合、getBufferedReuqestBody 関数が複数回呼び出される可能性があるため、データの重複の問題が発生することがわかりました。その後、フックする CacheStrategy$Factory.get ポイントを見つけて、データの重複がまだ存在することがわかりました。上記のフックにはすべて欠点があることが判明しました。

  • データの重複

  • #non-okhttp 呼び出しはキャプチャできません

次に、ネイティブ層の send、sendmsg、write、recv、read から始まるコールスタックも出力されます。 3日間悩んだ末、ついに治療を諦め、代わりに道具を使うことにしました。

okhttp流程:sdk接口->okhttp框架->native(libc)
2. フリーダが分析プロセス中に踏んだ落とし穴 (重要なポイントはコメントにあります)

  1. android.util.Log が出力されません

    var Logd = function Logd(tag, msg) {
    Java.use("android.util.Log").d(tag, msg);
    };
    
    
    Logd('http-body-', '11111111111111');//该log不打印
    Logd('http-body', '11111111111111');//该log打印
  2. 匿名の内部クラスではメンバーを取得するためにリフレクションが必要です

    var printRequest = function(request) {
    var Buffer = Java.use("com.android.okhttp.okio.Buffer");
    var bodyField = request.getClass().getDeclaredField('body');
    bodyField.setAccessible(true);
    
    if (request == null) return;
    Logd('http', 'printRequest: request' + request);
    //var requestBody = request.body();//gadget直接报错
    var requestBody = bodyField.get(request);
    
    var requestBodyClass = requestBody.getClass();
    var ClassInstanceArray = Java.array('java.lang.Class', []);
    
    //var contentLengthMethod = requestBodyClass.getMethod("contentLength");//gadget直接报错
    var contentLengthMethod = requestBodyClass.getMethod("contentLength", ClassInstanceArray);
    
    contentLengthMethod.setAccessible(true);
    var ObjectInstanceArray = Java.array('java.lang.Object', []);
    var contentLength = requestBody ? contentLengthMethod.invoke(requestBody, ObjectInstanceArray) : 0;
    //if (contentLength == 0) contentLength = contentLen;
    Logd('http', 'printRequest contentLength: ' + contentLength);
    if (contentLength > 0) {
    var BufferObj = Buffer.$new();
    requestBody.writeTo(BufferObj);
    Logd(TAG, "\nrequest body :\n" + BufferObj.readString() + "\n");
    }
    };
  3. android.os.Bundle の印刷には Bundle unparcel が必要です

    var printIntentAndExtras = function printIntentAndExtras(intentObj) {
    if (intentObj == null) return;
    var Intent = Java.use("android.content.Intent");
    var Bundle = Java.use("android.os.Bundle");
    var bundleObj = Intent.getExtras.call(intentObj);
    
    if (bundleObj != null) {
    Bundle.getSize.call(bundleObj, null);//调用getSize即可反序列化
    }
    
    Logd(TAG, ‘printIntentAndExtras ’ + bundleObj);
    };
実際、私が遭遇した落とし穴は上記の落とし穴だけではありません。最初は、frida ネットワーク インターセプト ソリューションもいくつか試しました。okhttp のインターセプター ソリューションも注意深く研究しました。最終的に、アプリも同様であることがわかりました。インターセプターを使用したため、競合が発生し、ソリューションを使用できませんでした。

また、コール スタックとネットワーク リクエストを探して、アプリの詳細を純粋に分析しました。最終的には、比較的小さな利益がいくつかしかなく、読者には役に立たないかもしれませんが、記録しました。後で思い出せるように。

  1. java.net.URL インターセプション

    var URLHook = function() {
    var URL = Java.use('java.net.URL');
    URL.openConnection.overload().implementation = function() {
    var retval = this.openConnection();
    Logd('URL', openConnection' + retval);
    return retval;
    };
    };//URL.openConnection调用概率比较大,但是不一定对网络进行请求
  2. アプリが http リクエストを呼び出す前に json を使用するインターセプト。これはそのうちの 1 つです

    var jsonHook = function() {
    var xx = Java.use('e.h.a.a');//app smali
    var xxa_method = xx.a.overload('org.json.JSONObject', 'java.lang.String', 'java.lang.String');
    xxa_method.implementation = function(jsonObj, str1, str2) {
    Logd("json", jsonObj + " str1: " + str1 + " str2" + str2);
    xxa_method.call(this, jsonObj, str1, str2);
    }
    }
  3. trace http 関連クラス

    var traceAllHttpClass = function() {
    Java.perform(function() {
    Java.enumerateLoadedClasses({
    onMatch: function(name, handle) {
    /*"e.h.a.a$a",起初也拦截过app的该混淆类*/
    if (name.indexOf("com.android.okhttp.Http") != -1 || name.indexOf("com.android.okhttp.Request") != -1
    || name.indexOf("com.android.okhttp.internal") != -1) {
    traceClass(name);//对这三个class进行trace
    }
    },
    onComplete: function() {
    }
    });
    });
    };
  4. Request$Builder インターセプション

    var BuilderClass = Java.use('com.android.okhttp.Request$Builder')
    
    BuilderClass.build.implementation = function () {
    //LOG('com.android.okhttp.HttpUrl$Builder.build overload', { c: Color.Light.Cyan });
    //printBacktrace();
    var retval = this.build();
    Logd(TAG, "retval:" + retval);
    printRequest(retval);
    return retval;
    }
  5. property_get インターセプション

    var nativePropertyGetAddr = Module.findExportByName(null, '__system_property_get');
    Interceptor.attach(nativePropertyGetAddr, {
    onEnter: function onEnter(args) {
    this._name = args[0].readCString();
    this._value = args[1];
    },
    onLeave: function onLeave(retval) {
    if (this._name.indexOf("ro.build.id") != -1) {
    var virtualDevice = getVirtualDevice();
    if (DEBUG_PROP) Logd(TAG, "__system_property_get fake " + this._name + "=>to " + virtualDevice.build_id);
    
    this._value.writeUtf8String(virtualDevice.build_id);
    }
    
    var strFilter = /^ro\./g;
    if (DEBUG_PROP && this._name.match(strFilter) != null) Logd(TAG, "__system_property_get " + this._name);
    }
    });
2. デバイス android_id

var DEBUG_PROP = false;
var DEVICE_CONFIG = "/sdcard/.device";

function getVirtualDevice() {
var nativeOpen = new NativeFunction(Module.findExportByName(‘libc.so’, 'open'), 'int', ['pointer', 'int']);
var nativeRead = new NativeFunction(Module.findExportByName('libc.so', 'read'), 'int', ['int', 'pointer', 'int']);
var fd = nativeOpen(Memory.allocUtf8String(DEVICE_CONFIG), 0);
var mem = Memory.alloc(1024);
var readLen = nativeRead(fd, mem, 1024);
var json = JSON.parse(mem.readCString(readLen));
return json;
}

Secure.getString.implementation = function () {
var retval = this.getString(arguments[0], arguments[1]);
if (DEBUG_PROP) Logd(TAG, "Settings.Secure get " + arguments[1] + " val " + retval);

if (arguments[1].indexOf("android_id") != -1) {
var virtualDevice = getVirtualDevice();
return virtualDevice.android_id;
}

return retval;
};
によって引き起こされるユーザーの有効期限に対処する

3. パケット キャプチャ ツール フィドルを使用してパケットをキャプチャし、トラップから抜け出す

1 .fiddle プロキシ設定は正常です、アプリですが、ログインできません

adb ログを分析した後、プロセスで java.security.cert.CertPathValidatorException が出力されました。また、frida がパケットをインターセプトし、証明書をバイパスすることに関する投稿もいくつか見ました。前に。最初にブルート フォース検索を試してください:

Java.perform(function(){
const groups = Java.enumerateMethods('*!verify/u');
var classes = null;
for(var i in groups){
var classes = groups[i]['classes'];

for(var i in classes){
Java.use(classes[i]['name'])
.verify
.overload('java.lang.String', 'javax.net.ssl.SSLSession')
.implementation = function() {
printBacktrace();
LOG("[+] invoke verify", { c: Color.Red });
return true;
}
}
}
});
直接検証を強制して true を返したとしても、同じ SSL 問題エラーが発生するためログインできません。百度で検索したら答えが見つかりました。 apktool を解凍し、

res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<!--添加fiddle证书可信任
<certificates src="user" />
-->
</trust-anchors>
</base-config>
</network-security-config>
を変更して署名を再パッケージし、実行します。Fiddle がパッケージをキャッチし、アプリは正常にログインできました。今回は幸運でした。アプリの SSL 検証には一方向のアプリしかありません検証、サーバーは検証を実行しませんでした。

4. 終了

火曜日の午後から金曜日まで寝返りを打っていましたが、HttpEngine からシステムレベルでフックポイントを見つける方法はあまり良い方法ではなく、デメリットとしては、すでにクリアしています。そのため、日曜日に私はパケット キャプチャ ツールと Baidu で見つけたさまざまな方法を使用して、遭遇した問題を徐々に解決しました。

キャッチされた 2 つのパッケージは次のとおりです:

HTTP/1.1 200 OK
Date: Sun, 16 Aug 2020 06:27:34 GMT
Content-Type: application/json
Content-Length: 101
Connection: keep-alive
Grpc-Metadata-Content-Type: application/grpc
Vary: Origin
Vary: Accept-Encoding

{"result":{"errno":"OK","errmsg":"成功"},"data":{"version":"xxxxxxxx-351e-40cf-aaa9-3177d6df9b7f"}}
-----------------------------------
HTTP/1.1 200 OK
Date: Sun, 16 Aug 2020 06:27:34 GMT
Content-Type: application/json
Content-Length: 99
Connection: keep-alive
Grpc-Metadata-Content-Type: application/grpc
Vary: Origin
Vary: Accept-Encoding

{"result":{"errno":"OK","errmsg":"成功"},"data":{"nodeToken":"xxxxxxxc24d79f55c0b07beaf50cb566"}}
POST https://tap-xxxxxxx.xxxxxx.com/api/v2/Android/analytics/basic HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cjbcjdsabcjvbXVCJ9.eyJ1aWQiOjE4ODMzMDEsInNlY3JldCI6IjAzNzE0M2Y3LTExMTUtNGY2Yi1iNzQxLWUyMjc5ZDM3MGY3MCIsImV4cCI6MTU5NzgxNjQ0MiwiaXNzIjoiZ3Vlc3QgbG9naW4ifQ.W3SiO0-afbhxPITjRinnhyWhZLy1bzZhYexm5VCWklI
X-Device-ID: 9xxxxxxx84d4542e
X-Loc: ["China","Shanghai","Shanghai","","ChinaUnicom","31.224349","121.4767528","Asia/Shanghai","UTC+8","310000","86","CN","AP","xxx.166.xxx.xxx"]
X-App-Version: 2.2.0
Content-Type: application/json; charset=utf-8
Content-Length: 208
Host: xx-xxxx.xxxxxx.com
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.7.2

{"deviceID":"9xxxxxxx84d4542e","model":"V1813BA","systemVersion":"9","version":"2.2.0","location":{"latitude":xx.x99x990990991,"longitude":xxx.26689769073256},"network":{"g2":0,"g3":0,"g4":4,"g5":0,"wifi":4}}

-----------------------------------
HTTP/1.1 200 OK
Date: Sun, 16 Aug 2020 06:27:35 GMT
Content-Type: application/json
Content-Length: 43
Connection: keep-alive
Grpc-Metadata-Content-Type: application/grpc
Vary: Origin
Vary: Accept-Encoding

{"result":{"errno":"OK","errmsg":"成功"}}

以上がフリダでAPKネットワークパッケージを取得する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。