| 著作一覧 |
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
ジェズイットを見習え |