Kernel-mode HTTP/HTTPS client and TLS/DTLS transport library for Windows drivers.
Проект был создан из-за отсутсвия на GitHub толковых библиотек для работы с http в контексте ядра Windows
#include <ntddk.h>
#include "ktls_lib.h" // TLS / DTLS transport
#include "kdns_lib.h" // DNS resolver
#include "khttp_lib.h" // High-level HTTPNTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(RegistryPath);
DriverObject->DriverUnload = DriverUnload;
KhttpGlobalInit();
// Your code here...
return STATUS_SUCCESS;
}
VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
UNREFERENCED_PARAMETER(DriverObject);
KhttpGlobalCleanup();
}High-level, blocking HTTP client with automatic TCP/TLS handling.
typedef struct _KHTTP_RESPONSE {
ULONG StatusCode;
ULONG BodyLength;
PCHAR Body;
} KHTTP_RESPONSE, *PKHTTP_RESPONSE;Supported methods (all return NTSTATUS with PKHTTP_RESPONSE):
KhttpGetKhttpPostKhttpPutKhttpPatchKhttpDeleteKhttpHead
PKHTTP_RESPONSE Response = NULL;
NTSTATUS Status = KhttpGet(
"http://httpbin.org/get",
NULL,
NULL,
&Response
);
if (NT_SUCCESS(Status) && Response) {
DbgPrint("Status: %lu, Body: %lu bytes\n",
Response->StatusCode, Response->BodyLength);
KhttpFreeResponse(Response);
}PKHTTP_RESPONSE Response = NULL;
NTSTATUS Status = KhttpPost(
"https://httpbin.org/post",
"Content-Type: application/json\r\n",
"{\"secure\":true,\"kernel\":\"mode\",\"tls\":\"1.3\"}",
NULL,
&Response
);
if (NT_SUCCESS(Status) && Response) {
DbgPrint("Status: %lu\n", Response->StatusCode);
KhttpFreeResponse(Response);
}// GET
KhttpGet("https://jsonplaceholder.typicode.com/posts/1", NULL, NULL, &Response);
// POST
KhttpPost(
"https://jsonplaceholder.typicode.com/posts",
"Content-Type: application/json\r\n",
"{\"title\":\"test\",\"body\":\"content\",\"userId\":1}",
NULL, &Response);
// PUT
KhttpPut(
"https://jsonplaceholder.typicode.com/posts/1",
"Content-Type: application/json\r\n",
"{\"id\":1,\"title\":\"updated\",\"body\":\"modified\",\"userId\":1}",
NULL, &Response);
// PATCH
KhttpPatch(
"https://jsonplaceholder.typicode.com/posts/1",
"Content-Type: application/json\r\n",
"{\"title\":\"patched\"}",
NULL, &Response);
// DELETE
KhttpDelete("https://jsonplaceholder.typicode.com/posts/1", NULL, NULL, &Response);
// HEAD
KhttpHead("https://ya.ru/", NULL, NULL, &Response);Upload files using multipart/form-data encoding. Supports automatic chunked transfer for large files
// Allocate file data in NonPagedPool
PVOID FileData = ExAllocatePoolWithTag(NonPagedPool, FileSize, 'FILE');
RtlCopyMemory(FileData, SourceData, FileSize);
KHTTP_FILE File = {
.FieldName = "document",
.FileName = "test.bin",
.ContentType = "application/octet-stream",
.Data = FileData,
.DataLength = FileSize
};
PKHTTP_RESPONSE Response = NULL;
NTSTATUS Status = KhttpPostMultipart(
"https://httpbin.org/post",
"Authorization: Bearer token123\r\n",
NULL,
0,
&File,
1,
NULL,
&Response
);
if (NT_SUCCESS(Status) && Response) {
DbgPrint("Upload success: %lu\n", Response->StatusCode);
KhttpFreeResponse(Response);
}
ExFreePoolWithTag(FileData, 'FILE');// Form fields
KHTTP_FORM_FIELD Fields[] = {
{ .Name = "title", .Value = "My Photo" },
{ .Name = "description", .Value = "Uploaded from Windows kernel mode driver" }
};
// Files
KHTTP_FILE Files[] = {
{
.FieldName = "file1",
.FileName = "document1.txt",
.ContentType = "text/plain",
.Data = ImageData,
.DataLength = ImageSize
},
{
.FieldName = "file2",
.FileName = "document2.bin",
.ContentType = "application/octet-stream",
.Data = JsonData,
.DataLength = JsonSize
}
};
KHTTP_CONFIG Config = {
.UseHttps = TRUE,
.TimeoutMs = 30000,
.UserAgent = "KernelHTTP/1.0",
.MaxResponseSize = 5 * 1024 * 1024,
.ProgressCallback = ProgressCallback,
.CallbackContext = NULL
};
PKHTTP_RESPONSE Response = NULL;
NTSTATUS Status = KhttpPostMultipart(
"https://httpbin.org/post",
NULL,
Fields,
2,
Files,
2,
&Config,
&Response
);VOID ProgressCallback(
ULONG BytesSent,
ULONG TotalBytes,
PVOID Context
)
{
UNREFERENCED_PARAMETER(Context);
if (TotalBytes > 0) {
ULONG Percent = (BytesSent * 100) / TotalBytes;
DbgPrint("[PROGRESS] %lu%% (%lu/%lu bytes)\n",
Percent, BytesSent, TotalBytes);
}
}
KHTTP_CONFIG Config = {
.UseHttps = TRUE,
.TimeoutMs = 120000,
.ProgressCallback = ProgressCallback,
.CallbackContext = NULL,
.UseChunkedTransfer = TRUE
};// For large files (5MB+)
PVOID FileData = ExAllocatePoolWithTag(NonPagedPool, 5 * 1024 * 1024, 'FILE');
if (!FileData) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// Fill with sequential pattern
for (ULONG i = 0; i < (5 * 1024 * 1024) / 4; i++) {
((ULONG*)FileData)[i] = i;
}
KHTTP_FILE File = {
.FieldName = "largefile",
.FileName = "large5mb.bin",
.ContentType = "application/octet-stream",
.Data = FileData,
.DataLength = 5 * 1024 * 1024
};
KHTTP_CONFIG Config = {
.UseHttps = TRUE,
.TimeoutMs = 120000, // 2 minutes
.MaxResponseSize = 5 * 1024 * 1024,
.UseChunkedTransfer = TRUE,
.ChunkSize = 64 * 1024, // 64KB chunks
.ProgressCallback = NULL
};
PKHTTP_RESPONSE Response = NULL;
NTSTATUS Status = KhttpPostMultipartChunked(
"http://192.168.56.1:8080/upload",
NULL,
NULL, 0,
&File, 1,
&Config,
&Response
);
if (NT_SUCCESS(Status) && Response) {
DbgPrint("Upload success: %lu bytes\n", File.DataLength);
KhttpFreeResponse(Response);
}
ExFreePoolWithTag(FileData, 'FILE');// Stream file from disk without loading into memory
UNICODE_STRING FilePath;
RtlInitUnicodeString(&FilePath, L"\\??\\C:\\test_file.bin");
KHTTP_FILE File = {
.FieldName = "file",
.FileName = "test_file.bin",
.ContentType = "application/octet-stream",
.UseFileStream = TRUE,
.FilePath = &FilePath,
.Data = NULL,
.DataLength = 0
};
KHTTP_CONFIG Config = {
.UseHttps = TRUE,
.TimeoutMs = 300000, // 5 minutes
.UseChunkedTransfer = TRUE,
.ChunkSize = 256 * 1024, // 256KB chunks
.ProgressCallback = ProgressCallback
};
PKHTTP_RESPONSE Response = NULL;
NTSTATUS Status = KhttpPostMultipartChunked(
"https://192.168.56.1:8443/upload",
NULL,
NULL, 0,
&File, 1,
&Config,
&Response
);| File Size | Transfer Method | Chunk Size | Timeout | Notes |
|---|---|---|---|---|
| < 2 MB | Regular | N/A | 30s | Fast, single buffer |
| 2-10 MB | Chunked | 64KB | 2min | Progress tracking available |
| 10-100 MB | Chunked | 256KB | 5min | Use larger chunks |
| > 100 MB | Streaming (disk) | 256-512KB | 10min+ | Avoid loading into memory |
Low-level encrypted socket abstraction.
PKTLS_SESSION Session = NULL;
NTSTATUS Status = KtlsConnect(
INETADDR(192,168,56,1),
4443,
KTLS_PROTO_TCP, // or KTLS_PROTO_UDP for DTLS
"192.168.56.1",
&Session
);
if (NT_SUCCESS(Status)) {
KtlsSetTimeout(Session, 9000);
ULONG Sent, Recv;
CHAR Buffer[4096];
// Send HTTP request
const char* Request = "GET / HTTP/1.1\r\nHost: 192.168.56.1\r\nConnection: close\r\n\r\n";
KtlsSend(Session, (PVOID)Request, (ULONG)strlen(Request), &Sent);
// Receive response
KtlsRecv(Session, Buffer, sizeof(Buffer) - 1, &Recv);
if (Recv > 0) {
Buffer[Recv] = '\0';
DbgPrint("Received: %s\n", Buffer);
}
KtlsClose(Session);
}PKTLS_SESSION Session = NULL;
NTSTATUS Status = KtlsConnect(
INETADDR(192,168,56,1),
4443,
KTLS_PROTO_UDP, // DTLS over UDP
"192.168.56.1",
&Session
);
if (NT_SUCCESS(Status)) {
KtlsSetTimeout(Session, 9000);
ULONG Sent, Recv;
CHAR Buffer[1024];
// Send message
const char* Message = "Hello DTLS";
KtlsSend(Session, (PVOID)Message, (ULONG)strlen(Message), &Sent);
// Receive echo
KtlsRecv(Session, Buffer, sizeof(Buffer) - 1, &Recv);
if (Recv > 0) {
Buffer[Recv] = '\0';
DbgPrint("Echo: %s\n", Buffer);
}
KtlsClose(Session);
}Requirements:
- Buffers for
KtlsRecvmust be fromNonPagedPool. - Every successful
KtlsConnectmust be followed byKtlsClose.
ULONG Ip = 0;
NTSTATUS Status = KdnsResolve(
"ya.ru",
INETADDR(8,8,8,8), // Google DNS
3000, // 3 seconds timeout
&Ip
);
if (NT_SUCCESS(Status)) {
DbgPrint("IP: %u.%u.%u.%u\n",
(Ip >> 0) & 0xFF,
(Ip >> 8) & 0xFF,
(Ip >> 16) & 0xFF,
(Ip >> 24) & 0xFF);
}The library includes extensive stress tests covering:
Connection Stress
- 100x rapid TLS handshake cycles
- Connection pool exhaustion tests
- Timeout boundary tests (1ms to 60s)
HTTP/HTTPS Stress
- 1000 sequential GET requests
- 100 rapid-fire POST requests
- Mixed method rotation (GET/POST/PUT/DELETE)
- Large payload handling (10MB+ responses)
File Upload Stress
- 50x rapid small file uploads (1KB each)
- 10x large file uploads (50MB each)
- Continuous streaming (100MB+)
- 100 files in single multipart request
Memory & Resource Stress
- 1000x malloc/free cycles
- Buffer overflow protection tests
- Memory leak detection
- Resource cleanup verification
Edge Cases
- Zero-byte uploads
- Maximum URL length (8KB+)
- Invalid data handling
- Network interruption simulation
Stress tests run automatically on driver load. Configuration options:
#define STRESS_ITERATIONS 1000 // Test intensity
#define STRESS_FILE_SIZE (50*1024*1024) // 50MB
#define STRESS_CONCURRENT 100 // Parallel requestsResults are logged to DbgView with pass/fail counters and performance metrics.
Located in test server/, this Go program provides a dual TLS/DTLS echo endpoint on 0.0.0.0:4443 for testing KtlsConnect, KtlsSend, and KtlsRecv.
Build and run:
cd "test server"
go mod init test_server
go get github.com/pion/dtls/v2
go run main.goIt generates an in-memory self-signed certificate and echoes back any data received over both TCP (TLS) and UDP (DTLS).
| Test Category | Tests | Status | Endpoints |
|---|---|---|---|
| DNS Resolution | 1 | ✅ PASS | Google DNS (8.8.8.8) |
| TLS/DTLS | 2 | ✅ PASS | Local test server |
| HTTP Methods | 3 | ✅ PASS | httpbin.org, ya.ru |
| HTTPS Requests | 4 | ✅ PASS | httpbin.org, jsonplaceholder |
| REST API (HTTP) | 5 | ✅ PASS | jsonplaceholder.typicode.com |
| REST API (HTTPS) | 4 | ✅ PASS | jsonplaceholder.typicode.com |
| File Upload (Small) | 1 | ✅ PASS | httpbin.org (1KB) |
| File Upload (With Form) | 1 | ✅ PASS | example.com (2KB) |
| File Upload (Multiple) | 1 | ✅ PASS | httpbin.org (512B + 1KB) |
| File Upload (Large) | 1 | ✅ PASS | Local server (5MB chunked) |
| File Upload (Streaming) | 1 | ✅ PASS | Local server (disk streaming) |
| Connection Stress | 3 | ✅ PASS | 100+ cycles, timeout tests |
| HTTP/HTTPS Stress | 4 | ✅ PASS | 1000+ sequential, 100+ parallel |
| Upload Stress | 4 | ✅ PASS | 50x small, 10x large, streaming |
| Memory Stress | 4 | ✅ PASS | 1000+ cycles, leak detection |
| Edge Cases | 4 | ✅ PASS | Zero-byte, max URL, interruption |
| Total | 43 | ✅ 100% |
- Protocol Tests: DNS resolution, TLS handshake, DTLS handshake
- HTTP/HTTPS: All methods (GET, POST, PUT, PATCH, DELETE, HEAD)
- REST API: Full CRUD operations with JSON payloads
- File Uploads: Single file, multiple files, form fields, chunked transfer, streaming
- Progress Tracking: Callback support for monitoring upload progress
- Large Files: 5MB chunked upload with sequential pattern verification
- Streaming: Direct disk-to-network streaming without memory buffering
- Stress Tests: Connection cycles, memory pressure, concurrent requests, resource exhaustion
- Stability: BSOD prevention, graceful error handling, resource leak detection
All tests are implemented in wkhttp_tests.c and run automatically on driver load:
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(RegistryPath);
DriverObject->DriverUnload = DriverUnload;
// Initialize library
NTSTATUS Status = KhttpGlobalInit();
if (!NT_SUCCESS(Status)) {
return Status;
}
// Tests run here automatically
// See wkhttp_tests.c for details
return STATUS_SUCCESS;
}typedef struct _KHTTP_CONFIG {
BOOLEAN UseHttps; // Use HTTPS instead of HTTP
ULONG TimeoutMs; // Request timeout in milliseconds
PCHAR UserAgent; // Custom User-Agent header
ULONG MaxResponseSize; // Maximum response body size
ULONG DnsServerIp; // Custom DNS server (0 = use 8.8.8.8)
BOOLEAN UseChunkedTransfer; // Enable chunked transfer encoding
ULONG ChunkSize; // Chunk size for uploads (default 64KB)
PKHTTP_PROGRESS_CALLBACK ProgressCallback; // Upload progress callback
PVOID CallbackContext; // User context for callback
} KHTTP_CONFIG, *PKHTTP_CONFIG;KHTTP_CONFIG Config = {
.UseHttps = TRUE,
.TimeoutMs = 60000, // 60 seconds
.UserAgent = "MyDriver/1.0",
.MaxResponseSize = 10 * 1024 * 1024, // 10MB
.DnsServerIp = INETADDR(8,8,8,8),
.UseChunkedTransfer = TRUE,
.ChunkSize = 128 * 1024, // 128KB chunks
.ProgressCallback = MyProgressCallback,
.CallbackContext = MyContext
};