著作一覧 |
llfutureで、処理系のメッセージをちゃんとしてくれ、というのを聴いていて、思った。少なくともMSのコンパイラみたいに、日本語で出たほうがまだましだよな。
どういう手段が良いだろうか? 文字列を機械的に抽出してgettextのmo化しておいて、vsnprintfの呼び出しをフックしてそこで取り換えるってのはどうだろうか? rb_raiseとrb_compile_errorあたりを見ると、最終的にはstaticなerr_snprintfに行くし、そこでvsnprintfを呼び出している。
遅そうだけど、漏れは少なそうだし、遅いったって、どうせテスト時点でしか使わない……と考えると、拡張ライブラリでやるのが良さそうだ。コードの中から呼び出しを探してjmpテーブルを書き変えれば良いのだからどうにかなりそうだ。
で、とりあえず、フックできるかどうか試してみる。
#include <stdio.h> #include <malloc.h> #include <string.h> #include <windows.h> int somefunc(char* p, int c) { // 置き換えのターゲット return printf(p, c); } int altfunc(char* p, int c) { // これでフックする char* alt = (char*)_alloca(strlen(p) + 32); int len = sprintf(alt, "***%s", p); if (*(alt + len - 1) == '\n') len--; strcpy(alt + len, "***\n"); return altfunc(alt, c); } int main(int argc, char* argv[]) { // int (*fnc)(char*p, int c) = somefunc; unsigned char* org = (unsigned char*)somefunc; // 後の操作のためuchar_t*にする // int (*alt)(char*p, int c) = altfunc; unsigned char* alt = (unsigned char*)altfunc; int diff = org - alt; // とりあえず、near_label jmp決め打ちでいいや DWORD old; #if_DEBUG printf("%p: %02X %02X %02X %02X %02X\n", alt, *alt, *(alt + 1), *(alt + 2), *(alt + 3), *(alt + 4)); printf("%p: %02X %02X %02X %02X %02X\n", org, *org, *(org + 1), *(org + 2), *(org + 3), *(org + 4)); #endif if (!VirtualProtect(org, 5, PAGE_EXECUTE_READWRITE, &old) || !VirtualProtect(alt, 5, PAGE_EXECUTE_READWRITE, &old)) { printf("error %d\n", GetLastError()); return 1; } else { unsigned char temp[5]; memcpy(temp, org, 5); memcpy(org, alt, 5); *(DWORD*)(org + 1) -= diff; memcpy(alt, temp, 5); *(DWORD*)(alt + 1) += diff; } return somefunc("hello %d\n", 32); }
で、デバッグモードでやってみる。
c:\home\test\Visual Studio 2008\Projects\TestHello\Debug>testhello 0100109B: E9 C0 03 00 00 010010FA: E9 01 03 00 00 ***hello 32***
よっしゃ。できた。VirtualProtect APIがミソだな。ページ単位の設定だから2回呼ぶ必要はないはずだけど、逆にやり過ぎても大丈夫なようだ。
ではプロダクトモード
c:\home\test\Visual Studio 2008\Projects\TestHello\Release>testhello hello 32
え? なぜだ? 死ぬならわかるが……
デバッガで逆アセンブルリストでステップを追う。でも読み込んだアドレスの内容がjmpテーブルっぽく無い。なぜだ?
int somefunc(char* p, int c) { return printf(p, c); 00171000 jmp dword ptr [__imp__printf (1720ACh)] --- ソース ファイルがありません。 ------------------------------------------------------------ 00171006 int 3
すげ。いきなりprintf呼ぶのか。というか、jmpテーブル無しで直接、関数のアドレス持ってるし。というか、ますます死なないのが不思議になる。
00171189 add esp,8 } return somefunc("hello %d\n", 32); 0017118C push 20h 0017118E push offset string "hello %d\n" (172130h) 00171193 call dword ptr [__imp__printf (1720ACh)] } 00171199 mov ecx,dword ptr [esp+20h] 0017119D add esp,8 001711A0 pop edi 001711A1 pop esi 001711A2 pop ebp 001711A3 pop ebx 001711A4 xor ecx,esp 001711A6 call __security_check_cookie (1711AFh) 001711AB add esp,0Ch
ジェズイットを見習え |