Hello World с прямыми системными вызовами под Windows. Без ntdll.dll, kernel32.dll, ... Есть решение?

Чтобы прояснить вопрос, я опубликую короткий пример для 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");
}

Этот код можно скомпилировать для 32-битной Linux x86 следующим образом:

gcc -Wall -Wextra -nostartfiles -nostdlib -O3 -m32 hello32.c -o hello32

Теперь я хочу сделать то же самое для Windows (только для развлечения и для понимания некоторых внутренних вещей). В настоящее время я использую Win2K SP4 (внутри виртуальной машины) для тестирования. Поэтому номер системного вызова (который может быть изменен в разных версиях 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;
}

Код можно скомпилировать с помощью mingw под консолью Win2k следующим образом:

C:\data\hello_write>gcc -Wall hello.c -o hello.exe

Этот код работает для меня. Он создает hello.txt с содержанием "Привет, пиши!". Но эта версия связана с msvcrt.dll и kernel32.dll (kernel32.dll использует ntdll.dll...).

На следующем шаге я пытаюсь создать двоичный файл без каких-либо зависимостей (без dlls, только настоящий статический двоичный файл -> нет 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, ...). Может быть, кто-то знает хорошие ссылки. Решение не должно быть для Win2k. Решение для>= Win 2K или для компилятора msvc также будет правильным;-) (и извините за мой плохой английский)

0 ответов

Другие вопросы по тегам