著作一覧 |
というわけで、vsnprintfに対してやってみた。
が、難しいことがわかった。
とりあえず、VS2008なら次のようにすればできる。
#include <stdio.h> #include <malloc.h> #include <string.h> #include <windows.h> int alt_vsnprintf(char* buffer, size_t count, const char* format, va_list argptr) { char* alt = (char*)_alloca(strlen(format) + 32); int len = sprintf(alt, "***%s", format); if (*(alt + len - 1) == '\n') len--; strcpy(alt + len, "***\n"); return _vsnprintf_l(buffer, count, alt, NULL, argptr); } static int (*altvsnprintf_p)(char*, size_t, const char*, va_list) = alt_vsnprintf; static unsigned char* palt; int _tmain(int argc, _TCHAR* argv[]) { DWORD old; int x[] = { 32, 33 }; char buff[128]; va_list aptr = (va_list)&x; unsigned char* org = (unsigned char*)vsnprintf; palt = (unsigned char*)&altvsnprintf_p; VirtualProtect(org, 8, PAGE_EXECUTE_READWRITE, &old); *org = '\xff'; *(org + 1) = '\x25'; memcpy(org + 2, &palt, 4); *(org + 6) = *(org + 7) = 0; vsnprintf(buff, sizeof(buff), "%d-%d", aptr); printf(buff); return 0; }
デバッグモード、リリースモードのいずれでも、実行すると、32-33ではなく**32-33**が出力される。
これは、元のvsnprintfの利用をあきらめて、代替関数はvsnprintf_lを呼ぶようにしているからできているだけだ。vsnprintf_lが存在しないVC6でどうやって実現すれば良いか? (gccを使う場合は、gnulibのソースからvsnprintfと必要な関数(vasnprintf)を取り込めば良いのだが)
問題は、vsnprintfのコードを書き変えて、代替関数へジャンプさせる点にある。
書き換えるために、元のコードをどこかへ保持しなければならない。
これ自体は、それほど難しくなく、retに出会うまでコピーしてコピーした領域を実行可能属性に変更し、代替関数からそこへ飛べばよいからだ。
しかし、実際には、その中で相対アドレスを利用したcall(helper関数。gnulibのvasnprintf相当)があるため、単純にコピーすると飛び先が異なるので即死することにある。
かといって、書き変えるとすると、ニアな相対アドレスへのcallの代わりに48ビットを利用するcallへ書き換える必要があり、それは厄介だ。……やればできるけど、やりたかないね。
そこで、代替関数側で、完全に処理を奪うというのが簡単なのだが、vc6には、代替関数側で利用できる実装がないというところまで。
というか、それを外部からロードされたDLLでできるかどうかとか、とか、試してないことはあるけど。
#if _MSC_VER < 1300 { FILE str; int ret; str._flag = _IOWRT | _IOSTRG; str._base = str._ptr = buffer; str._cnt = count; ret = _output(&str, alt, argptr); putc('\0', &str); return ret; } #else return _vsnprintf_l(buffer, count, alt, NULL, argptr); #endif微妙だなぁ。
ジェズイットを見習え |