Rumah >Operasi dan penyelenggaraan >Keselamatan >Bagaimana untuk merebut pakej rangkaian apk di frida
Cari mata kait dari perspektif sistem, bukannya menangkap paket demi menangkapnya.
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(); } }
Kod penting klien adalah pada client.newCall(). Bermula dari panggilan antara muka di sini, ia akhirnya akan dipanggil ke rangka kerja okhttp pada asalnya ialah SDK Kemudian, AOSP telah disepadukan ke dalam sistem, jadi ia boleh diklasifikasikan ke dalam lapisan rangka kerja.
Lapisan rangka kerja tidak terperinci, terutamanya kelas java berikut:
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
Malah, client.newCall akhirnya akan mendapatkan sambungan melalui URL
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection di sini ialah sebenarnya Contoh HttpURLConnectionImpl Kelas ini mempunyai kaedah getInputStream getOutputStream, yang secara dalaman memanggil getBufferedRequestBody dan getResponse HttpEngine. Pada mulanya, saya cuba mengaitkan dua antara muka ini Contohnya, selepas mengaitkan getResponse, respons boleh dicetak.
Kemudian saya mendapati Permintaan hanya boleh mengeluarkan pengepala, bukan badan. Jadi saya melibatkan diri dalam analisis dan mendapati bahawa fungsi getBufferedRequestBody boleh digunakan untuk mendapatkan sink. Akhirnya, RetryableSink digunakan sebagai titik terobosan Sebagai contoh, mengaitkan fungsi tulisnya boleh mencetak badan. Fungsi tulis sepadan dengan urlConnection.getOutputStream().tulis pada peringkat aplikasi.
Kemudian saya menemui Permintaan, dan fungsi getBufferedReuqestBody mungkin dipanggil lebih daripada sekali, jadi akan ada masalah penduaan data Kemudian, saya menemui CacheStrategy$Factory.get point to hook, dan mendapati bahawa terdapat masih pertindihan data. Didapati bahawa semua cangkuk di atas mempunyai kelemahan
Penduaan data
panggilan bukan okhttp tidak boleh ditangkap
Kemudian timbunan panggilan bermula dari hantar, sendmsg, tulis, recv, dan baca lapisan asli juga dicetak. Selepas bergelut selama tiga hari, saya memutuskan untuk berhenti merawat dan menggunakan alat sebaliknya.
okhttp流程:sdk接口->okhttp框架->native(libc)
android.util.Log tidak mencetak
var Logd = function Logd(tag, msg) { Java.use("android.util.Log").d(tag, msg); }; Logd('http-body-', '11111111111111');//该log不打印 Logd('http-body', '11111111111111');//该log打印
Kelas dalaman tanpa nama memerlukan renungan untuk mendapatkan ahli
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"); } };
pencetakan Android.os.Bundle, Bundle perlu dipisahkan
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); };
Sebenarnya, perangkap yang disebutkan di atas bukanlah satu-satunya perangkap yang saya temui Pada mulanya, saya juga telah mencuba beberapa penyelesaian pemintas rangkaian frida Saya juga mengkaji dengan teliti penyelesaian Interceptor okhttp bahawa apl itu juga menggunakan pemintas, jadi konflik berlaku dan penyelesaiannya tidak dapat digunakan .
Saya juga menganalisis smali apl semata-mata, mencari timbunan panggilan dan permintaan rangkaian Pada akhirnya, terdapat hanya beberapa keuntungan yang agak kecil, yang mungkin tidak berguna kepada pembaca, tetapi saya merekodkannya supaya saya boleh mengingati mereka kemudian.
java.net.URL pemintasan
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调用概率比较大,但是不一定对网络进行请求
Pemintasan tempat di mana json digunakan sebelum apl memanggil permintaan http, ini hanya satu daripadanya
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); } }
jejaki kelas berkaitan 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() { } }); }); };
Request$Builder interception
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; }
property_get pemintasan
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); } });
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; };
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; } } } });Walaupun anda secara langsung memaksa pengesahan untuk mengembalikan benar, anda masih tidak boleh log masuk kerana ralat masalah SSL yang sama berlaku. Saya menemui jawapannya selepas mencari di Baidu. Buka bungkusan apktool, kemudian ubah suai
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>
untuk membungkus semula tandatangan dan menjalankannya dan apl boleh log masuk seperti biasa kali ini pengesahan, pelayan tidak melakukan pengesahan. 4. Berakhir Saya terus bergelut dari petang Selasa hingga Jumaat Akhirnya, mencari mata kait dari HttpEngine di peringkat sistem bukanlah kaedah yang baik, dan keburukan sudah jelas. Oleh itu, pada hari Ahad, saya menggunakan alat tangkapan paket dan pelbagai kaedah yang terdapat pada Baidu untuk menyelesaikan masalah yang dihadapi secara beransur-ansur. Berikut ialah dua beg yang ditangkap:
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":"成功"}}
Atas ialah kandungan terperinci Bagaimana untuk merebut pakej rangkaian apk di frida. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!