トップ «前の日記(2008-09-02) 最新 次の日記(2008-09-04)» 編集

日々の破片

Subscribe with livedoor Reader
著作一覧

2008-09-03

_ 続き

というわけで、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でできるかどうかとか、とか、試してないことはあるけど。

_ VC6用

#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
微妙だなぁ。

2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|

ジェズイットを見習え