mirror of
https://github.com/decompals/wibo.git
synced 2025-12-12 14:46:09 +00:00
Implement ntdll NtWriteFile
This commit is contained in:
@@ -278,6 +278,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS)
|
|||||||
wibo_add_fixture_bin(NAME test_rtl SOURCES test/test_rtl.c)
|
wibo_add_fixture_bin(NAME test_rtl SOURCES test/test_rtl.c)
|
||||||
wibo_add_fixture_bin(NAME test_ntquery SOURCES test/test_ntquery.c)
|
wibo_add_fixture_bin(NAME test_ntquery SOURCES test/test_ntquery.c)
|
||||||
wibo_add_fixture_bin(NAME test_ntreadfile SOURCES test/test_ntreadfile.c)
|
wibo_add_fixture_bin(NAME test_ntreadfile SOURCES test/test_ntreadfile.c)
|
||||||
|
wibo_add_fixture_bin(NAME test_ntwritefile SOURCES test/test_ntwritefile.c)
|
||||||
wibo_add_fixture_bin(NAME test_pipe_io SOURCES test/test_pipe_io.c)
|
wibo_add_fixture_bin(NAME test_pipe_io SOURCES test/test_pipe_io.c)
|
||||||
wibo_add_fixture_bin(NAME test_namedpipe SOURCES test/test_namedpipe.c)
|
wibo_add_fixture_bin(NAME test_namedpipe SOURCES test/test_namedpipe.c)
|
||||||
wibo_add_fixture_bin(NAME test_sysdir SOURCES test/test_sysdir.c)
|
wibo_add_fixture_bin(NAME test_sysdir SOURCES test/test_sysdir.c)
|
||||||
|
|||||||
@@ -1504,7 +1504,7 @@ BOOL WIN_FUNC GetFileInformationByHandle(HANDLE hFile, LPBY_HANDLE_FILE_INFORMAT
|
|||||||
lpFileInformation->dwVolumeSerialNumber = 0;
|
lpFileInformation->dwVolumeSerialNumber = 0;
|
||||||
lpFileInformation->nFileSizeHigh = static_cast<DWORD>(static_cast<uint64_t>(st.st_size) >> 32);
|
lpFileInformation->nFileSizeHigh = static_cast<DWORD>(static_cast<uint64_t>(st.st_size) >> 32);
|
||||||
lpFileInformation->nFileSizeLow = static_cast<DWORD>(st.st_size & 0xFFFFFFFFULL);
|
lpFileInformation->nFileSizeLow = static_cast<DWORD>(st.st_size & 0xFFFFFFFFULL);
|
||||||
lpFileInformation->nNumberOfLinks = 0;
|
lpFileInformation->nNumberOfLinks = 1;
|
||||||
lpFileInformation->nFileIndexHigh = 0;
|
lpFileInformation->nFileIndexHigh = 0;
|
||||||
lpFileInformation->nFileIndexLow = 0;
|
lpFileInformation->nFileIndexLow = 0;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
#include "processes.h"
|
#include "processes.h"
|
||||||
#include "strutil.h"
|
#include "strutil.h"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -199,6 +201,90 @@ NTSTATUS WIN_FUNC NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE Ap
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NTSTATUS WIN_FUNC NtWriteFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
|
||||||
|
PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset,
|
||||||
|
PULONG Key) {
|
||||||
|
HOST_CONTEXT_GUARD();
|
||||||
|
DEBUG_LOG("NtWriteFile(%p, %p, %p, %p, %p, %p, %u, %p, %p) ", FileHandle, Event, ApcRoutine, ApcContext,
|
||||||
|
IoStatusBlock, Buffer, Length, ByteOffset, Key);
|
||||||
|
(void)ApcRoutine;
|
||||||
|
(void)ApcContext;
|
||||||
|
(void)Key;
|
||||||
|
|
||||||
|
if (!IoStatusBlock) {
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
IoStatusBlock->Information = 0;
|
||||||
|
|
||||||
|
auto file = wibo::handles().getAs<FileObject>(FileHandle);
|
||||||
|
if (!file || !file->valid()) {
|
||||||
|
IoStatusBlock->Status = STATUS_INVALID_HANDLE;
|
||||||
|
return STATUS_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useOverlapped = file->overlapped;
|
||||||
|
bool useCurrentFilePosition = (ByteOffset == nullptr);
|
||||||
|
bool writeToEndOfFile = false;
|
||||||
|
if (ByteOffset) {
|
||||||
|
if (*ByteOffset == FILE_USE_FILE_POINTER_POSITION) {
|
||||||
|
useCurrentFilePosition = true;
|
||||||
|
} else if (*ByteOffset == FILE_WRITE_TO_END_OF_FILE) {
|
||||||
|
writeToEndOfFile = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<off_t> offset;
|
||||||
|
if (!useCurrentFilePosition && !writeToEndOfFile) {
|
||||||
|
offset = static_cast<off_t>(*ByteOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useOverlapped && useCurrentFilePosition) {
|
||||||
|
IoStatusBlock->Status = STATUS_INVALID_PARAMETER;
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pin<kernel32::EventObject> ev;
|
||||||
|
if (Event) {
|
||||||
|
ev = wibo::handles().getAs<kernel32::EventObject>(Event);
|
||||||
|
if (!ev) {
|
||||||
|
IoStatusBlock->Status = STATUS_INVALID_HANDLE;
|
||||||
|
return STATUS_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
ev->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updateFilePointer = file->isPipe ? true : !useOverlapped;
|
||||||
|
|
||||||
|
if (writeToEndOfFile && !offset.has_value()) {
|
||||||
|
if (!file->isPipe) {
|
||||||
|
struct stat st{};
|
||||||
|
if (fstat(file->fd, &st) != 0) {
|
||||||
|
int err = errno ? errno : EIO;
|
||||||
|
NTSTATUS status = wibo::statusFromErrno(err);
|
||||||
|
IoStatusBlock->Status = status;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
offset = static_cast<off_t>(st.st_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto io = files::write(file.get(), Buffer, static_cast<size_t>(Length), offset, updateFilePointer);
|
||||||
|
NTSTATUS status = STATUS_SUCCESS;
|
||||||
|
if (io.unixError != 0) {
|
||||||
|
status = wibo::statusFromErrno(io.unixError);
|
||||||
|
}
|
||||||
|
|
||||||
|
IoStatusBlock->Status = status;
|
||||||
|
IoStatusBlock->Information = static_cast<ULONG_PTR>(io.bytesTransferred);
|
||||||
|
|
||||||
|
if (ev && status == STATUS_SUCCESS) {
|
||||||
|
ev->set();
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("-> 0x%x\n", status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
NTSTATUS WIN_FUNC NtAllocateVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits,
|
NTSTATUS WIN_FUNC NtAllocateVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits,
|
||||||
PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect) {
|
PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect) {
|
||||||
HOST_CONTEXT_GUARD();
|
HOST_CONTEXT_GUARD();
|
||||||
@@ -417,6 +503,8 @@ NTSTATUS WIN_FUNC NtQueryInformationProcess(HANDLE ProcessHandle, PROCESSINFOCLA
|
|||||||
static void *resolveByName(const char *name) {
|
static void *resolveByName(const char *name) {
|
||||||
if (strcmp(name, "NtReadFile") == 0)
|
if (strcmp(name, "NtReadFile") == 0)
|
||||||
return (void *)ntdll::NtReadFile;
|
return (void *)ntdll::NtReadFile;
|
||||||
|
if (strcmp(name, "NtWriteFile") == 0)
|
||||||
|
return (void *)ntdll::NtWriteFile;
|
||||||
if (strcmp(name, "NtAllocateVirtualMemory") == 0)
|
if (strcmp(name, "NtAllocateVirtualMemory") == 0)
|
||||||
return (void *)ntdll::NtAllocateVirtualMemory;
|
return (void *)ntdll::NtAllocateVirtualMemory;
|
||||||
if (strcmp(name, "NtProtectVirtualMemory") == 0)
|
if (strcmp(name, "NtProtectVirtualMemory") == 0)
|
||||||
|
|||||||
108
test/test_ntwritefile.c
Normal file
108
test/test_ntwritefile.c
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#include "test_assert.h"
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <string.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
|
||||||
|
#ifndef STATUS_SUCCESS
|
||||||
|
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *kTempFileName = "ntwritefile_fixture.tmp";
|
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI *NtWriteFile_t)(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG,
|
||||||
|
PLARGE_INTEGER, PULONG);
|
||||||
|
|
||||||
|
static NtWriteFile_t load_ntwritefile(void) {
|
||||||
|
HMODULE mod = GetModuleHandleW(L"ntdll.dll");
|
||||||
|
if (!mod) {
|
||||||
|
mod = LoadLibraryW(L"ntdll.dll");
|
||||||
|
}
|
||||||
|
TEST_CHECK(mod != NULL);
|
||||||
|
FARPROC proc = GetProcAddress(mod, "NtWriteFile");
|
||||||
|
TEST_CHECK(proc != NULL);
|
||||||
|
NtWriteFile_t fn = NULL;
|
||||||
|
TEST_CHECK(sizeof(fn) == sizeof(proc));
|
||||||
|
memcpy(&fn, &proc, sizeof(fn));
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_back(HANDLE file, char *buffer, DWORD expectedLength) {
|
||||||
|
TEST_CHECK(SetFilePointer(file, 0, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
|
||||||
|
DWORD read = 0;
|
||||||
|
memset(buffer, 0, expectedLength + 1);
|
||||||
|
TEST_CHECK(ReadFile(file, buffer, expectedLength, &read, NULL));
|
||||||
|
TEST_CHECK_EQ(expectedLength, read);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
NtWriteFile_t fn = load_ntwritefile();
|
||||||
|
|
||||||
|
DeleteFileA(kTempFileName);
|
||||||
|
HANDLE file =
|
||||||
|
CreateFileA(kTempFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
HANDLE event = CreateEventA(NULL, TRUE, TRUE, NULL);
|
||||||
|
TEST_CHECK(event != NULL);
|
||||||
|
|
||||||
|
IO_STATUS_BLOCK iosb;
|
||||||
|
memset(&iosb, 0, sizeof(iosb));
|
||||||
|
|
||||||
|
char payload[] = "hello";
|
||||||
|
SetLastError(ERROR_GEN_FAILURE);
|
||||||
|
DWORD before = GetLastError();
|
||||||
|
NTSTATUS status = fn(file, event, NULL, NULL, &iosb, payload, (ULONG)(sizeof(payload) - 1), NULL, NULL);
|
||||||
|
TEST_CHECK_EQ((NTSTATUS)STATUS_SUCCESS, status);
|
||||||
|
TEST_CHECK_EQ(status, iosb.Status);
|
||||||
|
TEST_CHECK_EQ((ULONG_PTR)(sizeof(payload) - 1), iosb.Information);
|
||||||
|
TEST_CHECK_EQ(before, GetLastError());
|
||||||
|
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(event, 0));
|
||||||
|
|
||||||
|
char buffer[16];
|
||||||
|
read_back(file, buffer, 5);
|
||||||
|
TEST_CHECK(memcmp(buffer, "hello", 5) == 0);
|
||||||
|
TEST_CHECK(ResetEvent(event));
|
||||||
|
|
||||||
|
LARGE_INTEGER useCurrent;
|
||||||
|
useCurrent.QuadPart = -2; // FILE_USE_FILE_POINTER_POSITION
|
||||||
|
TEST_CHECK(SetFilePointer(file, 1, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
|
||||||
|
IO_STATUS_BLOCK overwriteIosb;
|
||||||
|
memset(&overwriteIosb, 0, sizeof(overwriteIosb));
|
||||||
|
char middle[] = "abc";
|
||||||
|
SetLastError(ERROR_GEN_FAILURE);
|
||||||
|
before = GetLastError();
|
||||||
|
status = fn(file, event, NULL, NULL, &overwriteIosb, middle, (ULONG)(sizeof(middle) - 1), &useCurrent, NULL);
|
||||||
|
TEST_CHECK_EQ((NTSTATUS)STATUS_SUCCESS, status);
|
||||||
|
TEST_CHECK_EQ(status, overwriteIosb.Status);
|
||||||
|
TEST_CHECK_EQ((ULONG_PTR)(sizeof(middle) - 1), overwriteIosb.Information);
|
||||||
|
TEST_CHECK_EQ(before, GetLastError());
|
||||||
|
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(event, 0));
|
||||||
|
|
||||||
|
read_back(file, buffer, 5);
|
||||||
|
TEST_CHECK(memcmp(buffer, "habco", 5) == 0);
|
||||||
|
TEST_CHECK(ResetEvent(event));
|
||||||
|
|
||||||
|
LARGE_INTEGER appendPos;
|
||||||
|
appendPos.QuadPart = -1; // FILE_WRITE_TO_END_OF_FILE
|
||||||
|
IO_STATUS_BLOCK appendIosb;
|
||||||
|
memset(&appendIosb, 0, sizeof(appendIosb));
|
||||||
|
char tail[] = "!";
|
||||||
|
SetLastError(ERROR_GEN_FAILURE);
|
||||||
|
before = GetLastError();
|
||||||
|
status = fn(file, event, NULL, NULL, &appendIosb, tail, (ULONG)(sizeof(tail) - 1), &appendPos, NULL);
|
||||||
|
TEST_CHECK_EQ((NTSTATUS)STATUS_SUCCESS, status);
|
||||||
|
TEST_CHECK_EQ(status, appendIosb.Status);
|
||||||
|
TEST_CHECK_EQ((ULONG_PTR)(sizeof(tail) - 1), appendIosb.Information);
|
||||||
|
TEST_CHECK_EQ(before, GetLastError());
|
||||||
|
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(event, 0));
|
||||||
|
|
||||||
|
read_back(file, buffer, 6);
|
||||||
|
TEST_CHECK(memcmp(buffer, "habco!", 6) == 0);
|
||||||
|
|
||||||
|
TEST_CHECK(CloseHandle(event));
|
||||||
|
TEST_CHECK(CloseHandle(file));
|
||||||
|
TEST_CHECK(DeleteFileA(kTempFileName));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user