続き。
まず、ソフトウェア割り込みを使った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を使いましょう。
あした帰ったあと、やる気が出たら続く。多分続かない。