Frida 検出は、アプリケーションのリバース エンジニアリングを防ぐためによく使用される一般的な Android リバース エンジニアリング手法です。Frida 検出が発生した場合は、次の方法で回避できます。
-
Magisk Hide モジュールを使用する: Magisk は強力な Android ルート ツールであり、root 権限を非表示にするのに役立つ Magisk Hide モジュールが付属しています。これは、Frida 検出を回避するのに役立ちます。
-
Xused フレームワークを使用する: Xused フレームワークは、Frida 検出のバイパスなど、高度な Android リバース エンジニアリング手法の実装に役立ちます。Xused モジュールを使用して、アプリケーション情報を非表示にすることができます。
-
Frida Gadget を使用する: Frida Gadget は、検出を回避するのに役立つ Frida 検出用の小さなアプリです。Frida と併用すると、高度な Android リバース エンジニアリングを実現できます。
APK ケース分析
frida を使用してアタッチするときにプロセスが終了することが判明した場合、最初にプロセスがどこで終了するかを確認します
function hook_dlopen() {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
console.log("load " + path);
}
}
}
);
}
dlopen を介して、モジュール libnative-lib.so の実行時にプロセスが終了することがわかります。
モジュール libnative-lib.so を分析し、キーの場所を見つけます。
プロセスを終了するには 2 つの場所があり、関数 check_loop はマップ メモリ内のキー文字列を検索することによって frida を検出します。
void __fastcall __noreturn check_loop(void *a1)
{
for ( i = 0; ; ++i )
{
fd = open("/proc/self/maps", 0);
if ( fd >= 1 )
{
while ( read(fd, buf, 0x200u) >= 1 )
{
v11 = buf;
v10 = "frida";
haystack = buf;
needle = "frida";
if ( strstr(buf, "frida") )
{
v1 = getpid();
kill(v1, 9);
}
if ( sscanf(buf, "%x-%lx %4s %lx %*s %*s %s", &buf[516], &buf[512], &v5, &v4, s) == 5
&& v5 == 114
&& v6 == 112
&& !v4 )
{
strlen(s);
}
}
}
close(fd);
sleep(1u);
}
}
ブレークポイントを介してこれら 2 つのプロセスの終了位置をマークし、その後の戻りと表示を容易にします。
変更する場所は 2 つあり、IDA キーパス nop を直接再パッケージ化するか、frida フックを使用します。次に、frida フックを使用して 2 つのキー ポイントを nop してプロセスを終了します。
1.nop 0x9498 での終了呼び出しをドロップします。
2.0x92C2でnop kill
実装コードは以下の通り
//指令输出
function dis(address, number) {
for (var i = 0; i < number; i++) {
var ins = Instruction.parse(address);
console.log("address:" + address + "--dis:" + ins.toString());
address = ins.next;
}
}
/*call_function
static void call_function(const char* function_name __unused,
linker_ctor_function_t function,
const char* realpath __unused //加载的当前模块路径
)
}*/
function hook() {
//call_function("DT_INIT", init_func_, get_realpath());
var linkermodule = Process.getModuleByName("linker");
var call_function_addr = null;
var symbols = linkermodule.enumerateSymbols();
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
if (symbol.name.indexOf("__dl__ZL13call_functionPKcPFviPPcS2_ES0_") != -1) {
call_function_addr = symbol.address;
}
}
Interceptor.attach(call_function_addr, {
onEnter: function (args) {
var type = ptr(args[0]).readUtf8String();
var address = args[1];
var sopath = ptr(args[2]).readUtf8String();
console.log("loadso:" + sopath + "--addr:" + address + "--type:" + type);
if (sopath.indexOf("libnative-lib.so") != -1) {
var libnativemodule = Process.getModuleByName("libnative-lib.so");
var base = libnativemodule.base;
//nop kill
// .text:000092BA B loc_92BC
// .text:000092BC ; ---------------------------------------------------------------------------
// .text:000092BC
// .text:000092BC loc_92BC ; CODE XREF: check_loop(void *)+5E↑j
// .text:000092BC BLX getpid
// .text:000092C0 MOVS R1, #9 ; sig
// .text:000092C2 BLX kill
console.log("---------------- nop kill begin -------------")
dis(base.add(0x92BA).add(1), 10);
//方式一修改内存
// Memory.protect(base.add(0x92C2), 4, 'rwx');
// base.add(0x92C2).writeByteArray([0x00, 0xbf, 0x00, 0xbf]);
//方式二修改内存
var patchaddr=base.add(0x92C2);
Memory.patchCode(patchaddr, 4, patchaddr => {
var cw = new ThumbWriter(patchaddr);
cw.putNop();
var cw = new ThumbWriter(patchaddr.add(0x2));
cw.putNop();
cw.flush();
});
console.log("++++++++++++ nop kill end +++++++++++++")
dis(base.add(0x92BA).add(1), 10);
// nop exit
// .text:0000948E CMP R0, #0
// .text:00009490 BEQ loc_949C
// .text:00009492 B loc_9494
// .text:00009494 ; ---------------------------------------------------------------------------
// .text:00009494
// .text:00009494 loc_9494 ; CODE XREF: anti_frida_loop(void)+26↑j
// .text:00009494 MOV.W R0, #0xFFFFFFFF ; status
// .text:00009498 BLX exit
console.log("--------------- nop exit begin -------------")
dis(base.add(0x948E).add(1), 10); //keystone
Memory.protect(base.add(0x9498), 4, 'rwx');
base.add(0x9498).writeByteArray([0x00, 0xbf, 0x00, 0xbf]);
console.log("+++++++++++++++ nop exit end +++++++++++++")
dis(base.add(0x948E).add(1), 10);
}
}
})
}
function main(){
hook();
}
setImmediate(main);
要約:
Frida 検出をバイパスする方法は数多くありますが、考えられる方法のいくつかを次に示します。
1. Frida Bypass プラグインを使用します。これは、Frida の検出を自動的にバイパスし、Frida によるアプリケーションの監視を防止できる Frida プラグインです。
2. Frida Gadget ライブラリを使用します。これは、検出を回避するために実行時に Frida に挿入できる動的 Frida ライブラリです。
3. アプリケーション コードを変更します。アプリケーション コードを変更すると、Frida はアプリケーションを監視できなくなります。
4. 検出防止フレームワークを使用する: Xused フレームワーク、VirtualXused、Magisk など、一部の検出防止フレームワークは Frida の検出をバイパスできます。
5. 他のツールを使用する: Frida に加えて、Xused、Cydia Substrate など、アプリケーションの監視に使用できる他のツールがあります。これらのツールを使用すると、Frida はアプリケーションを監視できなくなります。