为了使问题更清楚,我将发布一个 Linux 32 位的简短示例。
void _start()
{
const char text[] = "hello world\n";
long rv = 0;
long exit_code = 0;
// write system call to standard output
asm volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx"
: "=a" (rv) \
: "0" (4),"ri" ((long)1),"c" ((long)text), "d" ((long)(12))
: "memory");
// exit system call - exit with exit code 0
rv = 0;
asm volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx"
: "=a" (rv)
: "0" (1),"ri" (exit_code) : "memory");
}
可以像这样为 Linux 32 位 x86 编译此代码:
gcc -Wall -Wextra -nostartfiles -nostdlib -O3 -m32 hello32.c -o hello32
现在我想对 Windows 做同样的事情(只是为了好玩和了解一些内部结构)。目前我使用Win2K SP4(在 VM 内)进行测试。因此,系统调用号(可能会在不同的 Windows 版本之间更改)和其他一些值适用于 Win2K SP4。对于编译,我使用 mingw 编译器。
对于 Windows,我尝试将“Hello,write!\n”写入文件,而不是将 Hello world 打印到控制台。我已经有一个可以直接进行系统调用并写入文件的版本(另外它不使用包含)。源代码如下所示:
#define FILENAME L"\\??\\C:\\data\\hello_write\\hello.txt"
// system call ids for Windows 2000 SP4
#define SYS_CALL_ID_NT_CLOSE 0x0018
#define SYS_CALL_ID_NT_CREATE_FILE 0x0020
#define SYS_CALL_ID_NT_WRITE_FILE 0x00ED
#define SYS_CALL_ID_RTL_INIT_UNICODE_STRING 0x7C8B
// many makros and defines are from the reactos source code
#ifndef VOID
#define VOID void // from winnt.h:75
#endif
//#define NTOSAPI __declspec(dllimport) // ntddk.h:62 only it the ntdll.dll is used
#define NTOSAPI static // for the own system call wrapper functions
#define DDKAPI __stdcall // from winddk.h
#define CONST const // from ddk/winddk.h:66 or. windef.h:39
#define NULL 0 // windef.h:46
#define OBJ_CASE_INSENSITIVE 64L // from ntdef.h:11
#define SYNCHRONIZE 0x100000L // from winnt.h:230
#define GENERIC_WRITE 0x40000000 // from winnt.h:241
#define FILE_SHARE_READ 0x00000001 // from winnt.h:265
#define FILE_SHARE_WRITE 0x00000002 // from winnt.h:266
#define FILE_OPEN_IF 0x00000003 // from winnt.h:298
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 // from winnt.h:307
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 // from winnt.h:308
#define FILE_NON_DIRECTORY_FILE 0x00000040 // from winnt.h:309
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) // from ddk/ntstatus.h:35
// from windef.h:15
#define InitializeObjectAttributes(p,n,a,r,s) { \
(p)->Length = sizeof(OBJECT_ATTRIBUTES); \
(p)->RootDirectory = (r); \
(p)->Attributes = (a); \
(p)->ObjectName = (n); \
(p)->SecurityDescriptor = (s); \
(p)->SecurityQualityOfService = NULL; \
}
typedef unsigned long ULONG_PTR, *PULONG_PTR; // basetsd.h:100 - __int64 verion not necessary
typedef unsigned long DWORD; // from windef.h
typedef char CHAR; // from winnt.h:77
typedef long LONG; // from winnt.h:79
typedef unsigned short USHORT,*PUSHORT; // from winnt.h:82
typedef unsigned long ULONG,*PULONG; // from winnt.h:83
typedef void *PVOID,*LPVOID; // from winnt.h:86
typedef unsigned short wchar_t; // from winnt:100
typedef wchar_t WCHAR; // from winnt:105
typedef WCHAR *PWCHAR,*LPWCH,*PWCH,*NWPSTR,*LPWSTR,*PWSTR; // from winnt.h:106
typedef CONST WCHAR *LPCWCH,*PCWCH,*LPCWSTR,*PCWSTR; // from winnt.h:107
typedef CHAR *PCHAR,*LPCH,*PCH,*NPSTR,*LPSTR,*PSTR; // from winnt.h:108
typedef PVOID HANDLE; // from winnt.h:151
typedef HANDLE *PHANDLE,*LPHANDLE; // from winnt.h:154
typedef long long LONGLONG; // from winnt.h:167 __int64 durch long long ersetzt
typedef DWORD ACCESS_MASK, *PACCESS_MASK; // from winnt.h:1751
typedef LONG NTSTATUS, *PNTSTATUS; // from ntdef.h:28
typedef union _LARGE_INTEGER { // from winnt.h:2404
struct {
DWORD LowPart;
LONG HighPart;
};
LONGLONG QuadPart;
} LARGE_INTEGER, *PLARGE_INTEGER;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING; // from ntdef.h:33
typedef struct _OBJECT_ATTRIBUTES { // from ntdef.h:51
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
typedef struct _IO_STATUS_BLOCK { // from ddk/winddk.h:785
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
// remove _ANONYMOUS_UNION and DUMMYUNIONNAME --> nameless union
// *PIO_STATUS_BLOCK is added instead of
// DECLARE_INTERNAL_OBJECT(IO_STATUS_BLOCK) see ddk/winddk.h:102
typedef VOID DDKAPI // from ddk/winddk.h:780
(*PIO_APC_ROUTINE)(
/*IN*/ PVOID ApcContext,
/*IN*/ PIO_STATUS_BLOCK IoStatusBlock,
/*IN*/ ULONG Reserved);
long ntSysCall(long nr, long param)
{
long rv;
__asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x2e ; pop %%ebx"
: "=a" (rv)
: "0" (nr),"ri" (param) : "memory");
return rv;
}
__declspec(dllimport)
VOID
DDKAPI
RtlInitUnicodeString(
/*IN OUT*/ PUNICODE_STRING DestinationString,
/*IN*/ PCWSTR SourceString);
NTOSAPI
VOID
DDKAPI
MyRtlInitUnicodeString(
/*IN OUT*/ PUNICODE_STRING DestinationString,
/*IN*/ PCWSTR SourceString)
{
DestinationString->Length = 0;
PCWCH i;
for (i = SourceString; *i; i++) {
DestinationString->Length++;
}
DestinationString->Length *= sizeof(WCHAR);
DestinationString->MaximumLength = DestinationString->Length + sizeof(WCHAR);
DestinationString->Buffer = (PWSTR)SourceString; // from PCWSTR to PWSTR
}
NTOSAPI
NTSTATUS
DDKAPI
MyCreateFile(
/*OUT*/ PHANDLE FileHandle,
/*IN*/ ACCESS_MASK DesiredAccess,
/*IN*/ POBJECT_ATTRIBUTES ObjectAttributes,
/*OUT*/ PIO_STATUS_BLOCK IoStatusBlock,
/*IN*/ PLARGE_INTEGER AllocationSize /*OPTIONAL*/,
/*IN*/ ULONG FileAttributes,
/*IN*/ ULONG ShareAccess,
/*IN*/ ULONG CreateDisposition,
/*IN*/ ULONG CreateOptions,
/*IN*/ PVOID EaBuffer /*OPTIONAL*/,
/*IN*/ ULONG EaLength)
{
return ntSysCall(SYS_CALL_ID_NT_CREATE_FILE,
(long)&FileHandle);
}
NTOSAPI
NTSTATUS
DDKAPI
MyWriteFile(
/*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*/)
{
return ntSysCall(SYS_CALL_ID_NT_WRITE_FILE,
(long)&FileHandle);
}
NTOSAPI
NTSTATUS
DDKAPI
MyClose(
/*IN*/ HANDLE Handle)
{
return ntSysCall(SYS_CALL_ID_NT_CLOSE,
(long)&Handle);
}
int main()
{
UNICODE_STRING fileNameUnicodeStringMy;
WCHAR filenameMy[] = FILENAME;
MyRtlInitUnicodeString(&fileNameUnicodeStringMy, filenameMy);
OBJECT_ATTRIBUTES objectAttributes;
InitializeObjectAttributes(&objectAttributes, &fileNameUnicodeStringMy,
OBJ_CASE_INSENSITIVE, NULL, NULL);
HANDLE hFile;
IO_STATUS_BLOCK IoStatus;
// It should use FILE_SYNCHRONOUS_IO_NONALERT or FILE_SYNCHRONOUS_IO_ALERT
// otherwise it failed
NTSTATUS rv = MyCreateFile(&hFile,
SYNCHRONIZE | GENERIC_WRITE,
&objectAttributes,
&IoStatus,
NULL,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN_IF,
//FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE,
FILE_SYNCHRONOUS_IO_ALERT | FILE_NON_DIRECTORY_FILE,
NULL,
0);
if (rv != STATUS_SUCCESS) {
return 1;
}
LPSTR text = "Hello, write!\n";
rv = MyWriteFile(hFile, NULL, NULL, NULL,
&IoStatus, text, 14, NULL, NULL);
if (rv != STATUS_SUCCESS) {
return 2;
}
rv = MyClose(hFile);
if (rv != STATUS_SUCCESS) {
return 3;
}
return 0;
}
代码可以在 Win2k 控制台下使用 mingw 编译,如下所示:
C:\data\hello_write>gcc -Wall hello.c -o hello.exe
这段代码对我有用。它会创建一个内容为“Hello, write!”的 hello.txt。但是这个版本是针对 msvcrt.dll 和 kernel32.dll 链接的(kernel32.dll 使用 ntdll.dll ...)。
对于下一步,我尝试创建一个没有任何依赖项的二进制文件(没有 dll,只有一个真正的静态二进制文件 --> 没有 ntdll.dll,没有 kernel32.dll,没有 msvcrt.dll)
为了创建一个真正的静态二进制文件,我将int main()替换为void __main(void)(并将所有return x;更改为return;在 main 函数内)。之后我编译:
gcc -Wall -nostdlib -nostartfiles -nodefaultlibs hello.c
编译工作。但是程序什么都不做(不起作用)(可以启动但没有输出文件(可能没有执行))。现在我的问题;-)
怎么了?必须做什么才能通过直接系统调用和没有任何库(没有 ntdll.dll,没有 kernel32.dll,...)来获得有效的“hello write”。也许有人知道一些很好的参考资料。该解决方案不得针对 Win2k。>= Win 2K 或 ms vc 编译器的解决方案也是 fin ;-) (抱歉我的英语不好)