Windows 2000/XP/2003/Vista のTickCount の違いを解析
Vista には GetTickCount64 という関数があって 起動してからの時間を 64bit で取得できるのに対して、それ以前のOSは 31bit でしか取得できません。
そのあたりどうなってるのか詳しく解析してみました。
まず、全てのOSはカーネル内で、 KeUpdateSystemTime を呼び出すごとに TickCount の値をカウントアップしています。
(もちろん、関数が呼ばれても時間が十分経過しない時はカウンタは動作しない)
Windows 2000も 内部では64bit で TickCount を持っている
mov eax,[KeTickCount] mov ebx,eax jg L0046871E mov ebx,FFDF0000h mov ecx,[ebx+14h] mov edx,[ebx+18h] add ecx,[L0046CB94] adc edx,00000000h mov [ebx+1Ch],edx mov [ebx+14h],ecx mov [ebx+18h],edx mov ebx,eax mov ecx,eax mov edx,[L0046CB88] add ecx,00000001h adc edx,00000000h mov [L0046CB8C],edx mov [KeTickCount],ecx mov [L0046CB88],edx push eax mov eax,[FFDF0000h] |
アドレス 0xffdf0000 というオフセットが使われていますが、これはユーザーメモリから見ると
0x7ffe0000 に当たります。
KeTickCount に下位の TickCount の値が入っています
…が、桁あふれした場合、 L0046CB8C と L0046CB88 の値を1増やしていることが分かります。
つまり、Windows 2000でも、内部的には、 TickCount を 64bit で保持している というわけです。
なんで、2箇所に上位のTickCount を代入してるの?
上位、下位、上位 の順番に代入しています。
もし、繰り上がりがあった時に値の参照があった場合下位だけ 0、上位はまだ値を増やしていない
というケースを避けるためです。
2箇所に代入した上位の値が同じなら、代入が完了してると判断できます。
それまで、処理を待てばいいわけです
Windows 2000や、XP のユーザーモードでこの値を取り出せるか?
普通は、取り出すのは無理です。
ユーザー関数に NtQueryTickCount という関数がありますが これで取り出せるのは、 32bit
ただ、int WINDDK KeQueryTickCount(uint_64) という関数があり、カーネルモードのドライバなどで取り出すことが可能です。
Windows Server 2003は取り出せる?
はい、取り出せます。
実は、 0x7ffe0320 に下位、0x7ffe0324 と 0x7ffe0328 に上位の値が入っています。
2000 や XP はこの値が 常に0です。
普通じゃなければ取り出せるか?
NTOSKRNL を書き換えてみました。
さっきのコードの後ろに次のようなコードを追加します
jmp stab reward: ---------------------------------------------- stab: mov eax,FFDF0300h mov [ebx+8h],edx mov [ebx+0h],ecx mov [ebx+4h],edx mov ah,0 mov eax,[eax] jmp reward |
これでOK
では、改造した カーネルのチェックサムを書き換えたら、起動実験してみましょう。
うまく起動できたら、うさみみハリケーンでモニタしてみます。
おお、うまく更新されてるようです。
Comments