Reputation: 41
I've been trying to create a .bmp file using functions like BitBlt, CreateCompatibleDC... in the kernel. The problem is whenever I run this code it creates a .bmp file successfully, the only issue is that the file is 1 kB of size. Even thought it's supposed to be way more
Here is my code:
#include "Drawing.h"
NTSTATUS NTAPI
__NE_CreateFile(
_In_ LPCWSTR FilePath,
_Inout_ PHANDLE FileHandle
);
NTSTATUS NTAPI
__NE_WriteFile(
_In_ HANDLE FileHandle,
_In_ PVOID Buffer,
_In_ ULONG BufferSize
);
NTSTATUS NTAPI
__NE_CloseFile(
_In_ HANDLE FileHandle
);
BOOLEAN NTAPI
__NE_CaptureScreenshot(
VOID
);
NTSTATUS NTAPI
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
) {
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);
// Ignore this, it's supposed to retrieve GDI routines
BOOLEAN Stats = InitializeSequence();
if (Stats == FALSE) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to initialize sequence.\xA"));
}
__NE_CaptureScreenshot();
return STATUS_SUCCESS;
}
NTSTATUS NTAPI
__NE_CreateFile(
_In_ LPCWSTR FilePath,
_Inout_ PHANDLE FileHandle
) {
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
KEVENT Event;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING FileName;
RtlInitUnicodeString(&FileName, FilePath);
KeInitializeEvent(&Event, NotificationEvent, FALSE);
InitializeObjectAttributes(
&ObjectAttributes,
&FileName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL
);
Status = ZwCreateFile(
FileHandle,
GENERIC_WRITE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_CREATE,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if (NT_SUCCESS(Status) == FALSE) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[IN] Failed to create file. Status: 0x%08X, IoStatus: 0x%08X\xA", Status, IoStatusBlock.Status));
return Status;
}
return STATUS_SUCCESS;
}
NTSTATUS NTAPI
__NE_WriteFile(
_In_ HANDLE FileHandle,
_In_ PVOID Buffer,
_In_ ULONG BufferSize
) {
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
Status = ZwWriteFile(
FileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
Buffer,
BufferSize,
NULL,
NULL
);
if (NT_SUCCESS(Status) == FALSE) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to write to file.\xA"));
return Status;
}
return STATUS_SUCCESS;
}
NTSTATUS NTAPI
__NE_CloseFile(
_In_ HANDLE FileHandle
) {
NTSTATUS Status;
Status = ZwClose(FileHandle);
if (NT_SUCCESS(Status) == FALSE) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to close file.\xA"));
return Status;
}
return STATUS_SUCCESS;
}
BOOLEAN NTAPI
__NE_CaptureScreenshot(
VOID
) {
BITMAP Bitmap = { 0 };
HANDLE FileHandle = { 0 };
HDC ScreenDC = NtUserGetDC(NULL);
if (ScreenDC == NULL) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to get screen DC.\xA"));
return FALSE;
}
HDC CompatibleDC = GreCreateCompatibleDC(ScreenDC);
if (CompatibleDC == NULL) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to create compatible DC.\xA"));
return FALSE;
}
INT ScreenWidth = GreGetDeviceCaps(ScreenDC, HORZRES);
INT ScreenHeight = GreGetDeviceCaps(ScreenDC, VERTRES);
HBITMAP CompatibleBitmap = NtGdiCreateCompatibleBitmap(
ScreenDC,
ScreenWidth,
ScreenHeight
);
if (CompatibleBitmap == NULL) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to create compatible bitmap.\xA"));
return FALSE;
}
NtGdiSelectBitmap(CompatibleDC, CompatibleBitmap);
BOOLEAN BitBlitStatus = NtGdiBitBlt(
CompatibleDC,
0,
0,
ScreenWidth,
ScreenHeight,
ScreenDC,
0,
0,
SRCCOPY
);
if (BitBlitStatus == FALSE) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to bitblt.\xA"));
return FALSE;
}
NtGdiExtGetObjectW(CompatibleBitmap, sizeof(BITMAP), &Bitmap);
BITMAPFILEHEADER BitmapFileHeader = { 0 };
BITMAPINFOHEADER BitmapInfoHeader = { 0 };
BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfoHeader.biWidth = Bitmap.bmWidth;
BitmapInfoHeader.biHeight = Bitmap.bmHeight;
BitmapInfoHeader.biPlanes = 1;
BitmapInfoHeader.biBitCount = 32;
BitmapInfoHeader.biCompression = BI_RGB;
BitmapInfoHeader.biSizeImage = Bitmap.bmWidthBytes * Bitmap.bmHeight;
BitmapInfoHeader.biXPelsPerMeter = 0;
BitmapInfoHeader.biYPelsPerMeter = 0;
BitmapInfoHeader.biClrUsed = 0;
BitmapInfoHeader.biClrImportant = 0;
DWORD BitmapSize = BitmapInfoHeader.biSizeImage;
HANDLE DIBHandle = ExAllocatePool2(
POOL_FLAG_NON_PAGED,
BitmapSize,
'dib0'
);
if (DIBHandle == NULL) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to allocate DIB handle.\xA"));
return FALSE;
}
NtGdiGetDIBitsInternal(
ScreenDC,
CompatibleBitmap,
0,
(UINT)Bitmap.bmHeight,
DIBHandle,
(PBITMAPINFO)&BitmapInfoHeader,
DIB_RGB_COLORS,
0
);
__NE_CreateFile(
L"\\??\\C:\\Screenshot.bmp",
&FileHandle
);
DWORD DIBSize = BitmapSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
BitmapFileHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
BitmapFileHeader.bfSize = DIBSize;
BitmapFileHeader.bfType = 0x4D42;
PVOID buffer = ExAllocatePool2(
POOL_FLAG_NON_PAGED,
DIBSize,
'buf0'
);
if (buffer == NULL) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Failed to allocate buffer.\xA"));
ExFreePool(DIBHandle);
return FALSE;
}
RtlCopyMemory(buffer, &BitmapFileHeader, sizeof(BITMAPFILEHEADER));
RtlCopyMemory((PBYTE)buffer + sizeof(BITMAPFILEHEADER), &BitmapInfoHeader, sizeof(BITMAPINFOHEADER));
RtlCopyMemory((PBYTE)buffer + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER), DIBHandle, BitmapSize);
__NE_WriteFile(
FileHandle,
(LPSTR)buffer,
DIBSize
);
ExFreePool(buffer);
ExFreePool(DIBHandle);
__NE_CloseFile(FileHandle);
return TRUE;
}
I've tried to change the Size calculations etc.. but none of it worked. I followed this PoC https://learn.microsoft.com/en-us/windows/win32/gdi/capturing-an-image
EDIT:
BOOLEAN NTAPI
__NE_CaptureScreenshot(
VOID
) {
HDC hScreenDC = NtUserGetDC(NULL);
if (!hScreenDC) return FALSE;
int width = GreGetDeviceCaps(hScreenDC, HORZRES);
int height = GreGetDeviceCaps(hScreenDC, VERTRES);
HDC hMemDC = GreCreateCompatibleDC(hScreenDC);
HBITMAP hBitmap = NtGdiCreateCompatibleBitmap(hScreenDC, width, height);
NtGdiSelectBitmap(hMemDC, hBitmap);
NtGdiBitBlt(hMemDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
BITMAPINFO bmi = { 0 };
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
PVOID pBits = ExAllocatePool2(POOL_FLAG_NON_PAGED, width * height * 4, 'BMP0');
if (!pBits) return FALSE;
NtGdiGetDIBitsInternal(hMemDC, hBitmap, 0, height, pBits, &bmi, DIB_RGB_COLORS, 0);
HANDLE hFile;
UNICODE_STRING fileName;
OBJECT_ATTRIBUTES objAttr;
IO_STATUS_BLOCK ioStatusBlock;
RtlInitUnicodeString(&fileName, L"\\??\\C:\\screen.bmp");
InitializeObjectAttributes(&objAttr, &fileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
NTSTATUS status = ZwCreateFile(&hFile, GENERIC_WRITE, &objAttr, &ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (NT_SUCCESS(status) == FALSE)
return FALSE;
BITMAPFILEHEADER bmfHeader = { 0 };
bmfHeader.bfType = 0x4D42;
bmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bmfHeader.bfSize = bmfHeader.bfOffBits + (width * height * 4);
ZwWriteFile(hFile, NULL, NULL, NULL, &ioStatusBlock, &bmfHeader, sizeof(BITMAPFILEHEADER), NULL, NULL);
ZwWriteFile(hFile, NULL, NULL, NULL, &ioStatusBlock, &bmi.bmiHeader, sizeof(BITMAPINFOHEADER), NULL, NULL);
ZwWriteFile(hFile, NULL, NULL, NULL, &ioStatusBlock, pBits, width * height * 4, NULL, NULL);
ZwClose(hFile);
return TRUE;
}
This code creates a correct .bmp but it's blank black
Upvotes: 1
Views: 100