| 著作一覧 |
というわけで、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
微妙だなぁ。
ジェズイットを見習え |