明日は帰る。
■
続き。
まず、ソフトウェア割り込みを使ったWindowsでのシステムコール呼び出しは、Linux等の方法と大体一緒だ。eaxレジスタにシステムコール番号を入れて、ソフトウェア割り込みをかけてやればよい。割り込み番号は0x2e。引数は全てスタックに積んでおく。
pushl *** # なんか引数 movl $0x***, %eax # *** = システムコール番号 int $0x2e
システムコール番号は、http://www.metasploit.com/users/opcode/syscalls.htmlが参考になる。
で、ここで少し問題になるのが、機能とシステムコールの対応だ。ここらへんは、多分、名前から機能推測、って方法しかないかもしれない。あとは、ReactOSのソース(/ntoskrnl以下)見るとか。
んで、昨日のソース。あれは、「自分自身を終了するプログラム」。
まず、システムコール一覧から、それっぽいシステムコールを探してくる。「NtTerminateProcess」ってのがそれっぽい。NtTerminateProcessは
NTSTATUS NTAPI NtTerminateProcess( IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus );
こんな感じ。引数は多分、終了するプロセスのハンドルとプログラムの終了コード。
自分自身のプロセスのハンドルは多分0xFFFFFFFF(NtCurrentProcess?)なので昨日のソースになる。
ちなみに、システムコール呼び出しは、いちいち自分でソフトウェア割り込みかけなくてもいいように、ラッパーが用意してある。ntdll.dllというDLLがそれ。
#include <windows.h> typedef DWORD NTSTATUS; NTSTATUS WINAPI NtTerminateProcess( HANDLE ProcessHandle, NTSTATUS ExitStatus ); int mainCRTStartup() { NtTerminateProcess( (HANDLE)-1, 0xff ); }
こーいうコードにして、ntdllをリンクすると、システムコール呼び出しをやってくれる。sysenterとsyscallがCPU依存、システムコール番号がOS依存であることを考えると、こっちのほうがよいかもしれない。
Windows Hello World
というわけで、Hello Worldを書いてみる。NtWriteFileで標準出力に書き込めば…って標準出力はどこに?
標準出力は、TEB(Thread Environment Block)のが指してるPEB(Process Environment Block)を読めばわかる…らしい。んでTEBは、fsレジスタが指してるとこ+0x18の位置に。これは直接拾ってきてもいいし、NtCurrentTebを呼び出してもいい、と、http://undocumented.ntinternals.net/に書いてあった。
TEBとPEBの構造も、そこに書いてあったので、少し拝借して、Hello Worldはこんな感じだ。…と思ったんだけど、これだと動かない。何故だ!!
#include <windows.h> #include <winnt.h> typedef DWORD NTSTATUS; typedef void *PIO_APC_ROUTINE; typedef struct _IO_STATUS_BLOCK /* from iotypes.h */ { union { NTSTATUS Status; PVOID Pointer; }; ULONG_PTR Information; } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; NTSTATUS NTAPI NtWriteFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, IN ULONG Length, IN PLARGE_INTEGER ByteOffset OPTIONAL, IN PULONG Key OPTIONAL ); typedef struct _CLIENT_ID { /* from winddk.h */ HANDLE UniqueProcess; HANDLE UniqueThread; } CLIENT_ID, *PCLIENT_ID; typedef struct _RTL_USER_PROCESS_PARAMETERS { ULONG MaximumLength; ULONG Length; ULONG Flags; ULONG DebugFlags; PVOID ConsoleHandle; ULONG ConsoleFlags; HANDLE StdInputHandle; HANDLE StdOutputHandle; HANDLE StdErrorHandle; /* and any data */ } *PRTL_USER_PROCESS_PARAMETERS; typedef struct _PEB_LDR_DATA *PPEB_LDR_DATA; typedef struct _PEB { BOOLEAN InheritedAddressSpace; BOOLEAN ReadImageFileExecOptions; BOOLEAN BeingDebugged; BOOLEAN Spare; HANDLE Mutant; PVOID ImageBaseAddress; PPEB_LDR_DATA LoaderData; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; /* and any data */ } *PPEB; typedef struct _TEB { NT_TIB Tib; PVOID EnvironmentPointer; CLIENT_ID Cid; PVOID ActiveRpcInfo; PVOID ThreadLocalStoragePointer; PPEB Peb; /* and any data... */ } TEB, *PTEB; int mainCRTStartup( void ) { PTEB teb = NtCurrentTeb(); PRTL_USER_PROCESS_PARAMETERS params = teb->Peb->ProcessParameters; IO_STATUS_BLOCK sb; NtWriteFile( params->StdOutputHandle, NULL, NULL, NULL, &sb, "a\r\n", 3UL, 0, 0 ); }
説としては、ReactOSのWriteFileを見る限り、ハンドルがコンソール用だとWriteConsoleAを呼ぶようになってるので、実は、NtWriteFileはファイルにしか対応してないんじゃないか、とか。
結論:APIを使いましょう。
あした帰ったあと、やる気が出たら続く。多分続かない。