typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
パースする一般的な文字列型は、C++のstd::stringです。同様の概念は、後にSwift.Stringのデータ型でも見られます。std::stringのLSB(Least Significant Bit)には、短い文字列(22バイト以下)の場合は0が、長い文字列の場合は1が格納されます。長い文字列の場合、取得したい文字列へのポインタは、接続されているプロセスのProcess.pointerSizeの2倍の値が格納されます。この知識を試して、文字列を取得する方法を確認するために、この簡単なプログラムを見てみましょう。
#include <iostream>
void print_std_string(std::string arg_1)
{
std::cout << arg_1 << std::endl;
}
int
main(void)
{
std::string my_string = "FRIDA is great, you should check it out at frida.re";
print_std_string(my_string);
return 0;
}
このプログラムは、単にprint_std_string(std::string arg_1)関数を呼び出して、画面に表示しているだけです。このようにして、std::stringのパラメータを取得し、検査することが簡単にできます。FRIDAのREPLでこのプログラムを起動し、バイナリ上でModule.enumerateExportsSync()を実行すると、名前がつぶれていることに気づきます。しかし、テスト関数に選んだ名前のおかげで、_Z16print_std_stringNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEという名前のつぶれた関数を見つけることができます。これが、Interceptor.attachを使用したい関数です。
Interceptor.attach(Module.getExportByName(null, '_Z16print_std_stringNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE'), {
onEnter (args) {
const LSB = args[0].readU8() & 1;
console.log('LSB: ' + LSB);
const stdString = args[0].add(Process.pointerSize*2).readPointer().readUtf8String();
console.log("std::string: " + stdString);
}
});
そして、この小さなスクリプトを実行すると、次のような出力が得られます。 LSB: 1
std::string: FRIDA is great, you should check it out at frida.re
[Local::a.out]-> Process terminated
このテストはclang++ 12.0.0を使用して行われており、小さな文字列を格納するためにユニオンを実装しているGCCなどのコンパイラでは、メモリのレイアウトが異なる可能性があることに留意する必要があります。
0 件のコメント:
コメントを投稿