#include "utils.h" // ======================= // ====== windows.h ====== // ======================= typedef struct _PROCESS_INFORMATION { void* hProcess; void* hThread; u32 dwProcessId; u32 dwThreadId; } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION; typedef struct _OVERLAPPED { u32* Internal; u32* InternalHigh; union { struct { u32 Offset; u32 OffsetHigh; } DUMMYSTRUCTNAME; void* Pointer; } DUMMYUNIONNAME; void* hEvent; } OVERLAPPED, *LPOVERLAPPED; #define SUCCEEDED(hr) (((i32)(hr)) >= 0) #if defined(_MSC_VER) && !defined(__clang__) #define va_start(ap, x) ((void)(__va_start(&ap, x))) #define va_arg(ap, t) \ ((sizeof(t) > sizeof(__int64) || (sizeof(t) & (sizeof(t) - 1)) != 0) \ ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \ : *(t* )((ap += sizeof(__int64)) - sizeof(__int64))) #define va_end(ap) ((void)(ap = (va_list)0)) #elif defined(__clang__) #define va_list __builtin_va_list #define va_start __builtin_va_start #define va_arg __builtin_va_arg #define va_end __builtin_va_end #endif #define STD_INPUT_HANDLE ((u32)-10) #define STD_OUTPUT_HANDLE ((u32)-11) #define STD_ERROR_HANDLE ((u32)-12) extern void* GetStdHandle(u32 nStdHandle); extern i32 WriteFile(void* hFile, const void* lpBuffer, u32 nNumberOfBytesToWrite, u32* lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); extern i32 ReadProcessMemory(void* hProcess, const void* lpBaseAddress, void* lpBuffer, size_t nSize, size_t* lpNumberOfBytesRead); // ======================== // ====== !windows.h ====== // ======================== u8 is_alpha(char c) { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); } u8 is_decimal_digit(char c) { return '0' <= c && c <= '9'; } u8 is_hexadecimal_digit(char c) { return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'); } // TODO: Improve basic functions perfs // max_size: str allocated size size_t str_len(const char* str, size_t max_size) { const char* c = str; size_t count = 0U; while (*c != '\0' && count < max_size) { c++; count++; } return count; } void str_cat(char* out, const char* str, size_t c_str) { char* cur_out = out; const char* cur_str = str; u32 index = 0U; while(index < c_str) { *cur_out = *cur_str; cur_out++; cur_str++; index++; } } void u64toa(u64 number, char* str, u32 base) { static char codes[16U] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', }; u32 remainder = 0U; u32 size = 0U; if (number == 0U) { str[0U] = codes[0U]; } for(; number != 0U; size++) { remainder = number % base; number /= base; str[size] = codes[remainder]; } for(u32 i = 0; i < size / 2U; i++) { char c = str[i]; str[i] = str[size - i - 1]; str[size - i - 1] = c; } } u64 atou64(const char* str, u32 len) { u64 number = 0U; u32 base = 10U; const char* c = str; if (str[0] == '0' && str[1] == 'x') { base = 16U; c += 2U; } for (; (u32)(c - str) < len; c++) { number *= base; u32 offset = 0U; if ('0' <= *c && *c <= '9') { offset = 48U; } else if ('a' <= *c && *c <= 'f') { offset = 87U; } else if ('A' <= *c && *c <= 'F') { offset = 55U; } else { return 0U; } number += (*c - offset); } return number; } void fill_chars(u8* ptr, u8 c, u32 len) { for (u32 index = 0U; index < len; index++) { *ptr = c; ptr++; } } void print(const char* format, ...) { va_list args; va_start(args, format); char formatted_str[4096U] = { 0 }; char* cur_char = formatted_str; const char* cur_fmt = format; while(*cur_fmt != '\0') { if(*cur_fmt == '%') { cur_fmt++; u64 fill_count = 0U; // padding length if (*cur_fmt == '.') { cur_fmt++; const char* num_start = cur_fmt; u8 num_len = 0U; while (is_decimal_digit(*cur_fmt)) { cur_fmt++; num_len++; } if (num_len > 0U) { fill_count = atou64(num_start, num_len); } } // TODO: length modifiers // conversion specifiers if (*cur_fmt == 's') { const char* str = va_arg(args, const char*); if(str != NULL) { size_t length = str_len(str, sizeof(formatted_str)); i64 relative_fill_count = fill_count - length; if (relative_fill_count > 1) { fill_chars((u8*)cur_char, ' ', (u32)relative_fill_count); cur_char += relative_fill_count; } str_cat(cur_char, str, length); cur_char += length; } } else if(*cur_fmt == 'c') { i32 c = va_arg(args, i32); if (fill_count > 1U) { fill_chars((u8*)cur_char, ' ', (u32)fill_count - 1U); cur_char += fill_count; } *cur_char = (i8)c; cur_char++; } else if (*cur_fmt == 'x') { u64 number = va_arg(args, u64); u32 str_len = 0; if (number > 0xfffffffffffffff) { str_len = 16U; } else if (number > 0xffffffffffffff) { str_len = 15U; } else if (number > 0xfffffffffffff) { str_len = 14U; } else if (number > 0xffffffffffff) { str_len = 13U; } else if (number > 0xfffffffffff) { str_len = 12U; } else if (number > 0xffffffffff) { str_len = 11U; } else if (number > 0xfffffffff) { str_len = 10U; } else if (number > 0xffffffff) { str_len = 9U; } else if (number > 0xfffffff) { str_len = 8U; } else if (number > 0xffffff) { str_len = 7U; } else if (number > 0xfffff) { str_len = 6U; } else if (number > 0xffff) { str_len = 5U; } else if (number > 0xfff) { str_len = 4U; } else if (number > 0xff) { str_len = 3U; } else if (number > 0xf) { str_len = 2U; } i64 relative_fill_count = fill_count - str_len; if (relative_fill_count > 1) { fill_chars((u8*)cur_char, '0', (u32)relative_fill_count); cur_char += fill_count; } u64toa(number, cur_char, 16U); cur_char += str_len; } else if(*cur_fmt == 'b') { u64 number = va_arg(args, u64); u32 str_len = 64U - __lzcnt(number); i64 relative_fill_count = fill_count - str_len; if (relative_fill_count > 1) { fill_chars((u8*)cur_char, '0', (u32)relative_fill_count); cur_char += fill_count; } u64toa((u64)number, cur_char, 10U); cur_char += str_len; } else if(*cur_fmt == 'u') { u64 number = va_arg(args, u64); u32 str_len = 1U; if (number > 9999999999999999999U) { str_len = 20U; } else if (number > 999999999999999999U) { str_len = 19U; } else if (number > 99999999999999999U) { str_len = 18U; } else if (number > 9999999999999999U) { str_len = 17U; } else if (number > 999999999999999U) { str_len = 16U; } else if (number > 99999999999999U) { str_len = 15U; } else if (number > 9999999999999U) { str_len = 14U; } else if (number > 999999999999U) { str_len = 13U; } else if (number > 99999999999U) { str_len = 12U; } else if (number > 9999999999U) { str_len = 11U; } else if (number > 999999999U) { str_len = 10U; } else if (number > 99999999U) { str_len = 9U; } else if (number > 9999999U) { str_len = 8U; } else if (number > 999999U) { str_len = 7U; } else if (number > 99999U) { str_len = 6U; } else if (number > 9999U) { str_len = 5U; } else if (number > 999U) { str_len = 4U; } else if (number > 99U) { str_len = 3U; } else if (number > 9U) { str_len = 2U; } i64 relative_fill_count = fill_count - str_len; if (relative_fill_count > 1U) { fill_chars((u8*)cur_char, '0', (u32)relative_fill_count); cur_char += fill_count; } u64toa(number, cur_char, 10U); cur_char += str_len; } } else { *cur_char = *cur_fmt; cur_char++; } cur_fmt++; } va_end(args); void* standard_err = GetStdHandle(STD_ERROR_HANDLE); SUCCEEDED(WriteFile(standard_err, formatted_str, (u32)(cur_char - formatted_str), NULL, NULL)); } /* Read debugged process memory at address */ u8 read_memory(const void* addr, void* buffer, size_t size) { extern PROCESS_INFORMATION proc_info; size_t b_read = 0U; i32 hr = ReadProcessMemory(proc_info.hProcess, addr, buffer, size, &b_read); return hr > 0 && b_read == size; }