Skip to content

Instantly share code, notes, and snippets.

@pmttavara
Created April 10, 2019 06:53
Show Gist options
  • Save pmttavara/4abeeca21ee749f5138defca889837f6 to your computer and use it in GitHub Desktop.
Save pmttavara/4abeeca21ee749f5138defca889837f6 to your computer and use it in GitHub Desktop.
Simple C Win32 Hello World program without preprocessor directives or library imports. Build with `cl *.c`.
__pragma(comment(linker, "/entry:main"));
__pragma(comment(linker, "/subsystem:console"));
void *(*GetProcAddress)(void *module, const char *name);
void *(*LoadLibraryA)(const char *name);
void (*ExitProcess)(unsigned int return_code);
int (*MessageBoxA)(void *hWnd, const char *lpText, const char *lpCaption, unsigned int uType);
static inline void load_proc(void *lib, void *fn, char *name) {
*(void **)fn = GetProcAddress(lib, name);
}
static inline void dynamic_link(void) {
void *k32 = 0; // grab thread environment block to find loader data
char *teb_pointer = (char *)__readgsqword(0x30);
char *peb_pointer = *(char **)(teb_pointer + 0x60);
char *ldr_data_pointer = *(char **)(peb_pointer + 0x18);
char *entry_ptr = *(char **)(ldr_data_pointer + 0x20);
while (*(void **)(entry_ptr + 0x30)) { // search for kernel32.dll
unsigned short *libbuf = *(unsigned short **)(entry_ptr + 0x50);
int liblen = *(unsigned short *)(entry_ptr + 0x48) / 2;
int cmplen = sizeof(L"kernel32.dll") / 2;
if (cmplen < liblen) cmplen = liblen;
int i = 0;
for (; i < cmplen; i += 1) {
int ch1 = L"kernel32.dll"[i];
int ch2 = libbuf[i];
if (ch1 >= 'a' && ch1 <= 'z') ch1 &= ~0x20;
if (ch2 >= 'a' && ch2 <= 'z') ch1 &= ~0x20;
if (ch1 != ch2) break;
}
if (i == cmplen) {
k32 = *(void **)(entry_ptr + 0x20);
break;
}
entry_ptr = *(char **)entry_ptr;
}
if (!k32) __debugbreak();
// parse kernel32.dll to grab function tables
char *dos = (char *)k32;
char *pe = dos + *(unsigned int *)(dos + 0x3C);
char *export_table = dos + *(unsigned int *)(pe + 0x88);
int number_of_name_pointers = *(unsigned int *)(export_table + 0x18);
unsigned int *export_address_table = (unsigned int *)(dos + *(unsigned int *)(export_table + 0x1C));
unsigned int *name_pointer_table = (unsigned int *)(dos + *(unsigned int *)(export_table + 0x20));
unsigned short *ordinal_table = (unsigned short *)((char *)k32 + *(unsigned int *)(export_table + 0x24));
// binary search for GetProcAddress
int low = 0;
int index = 0;
int high = number_of_name_pointers - 1;
int compare_result = 1;
while (compare_result != 0) {
if (compare_result > 0) {
low = index;
} else if (compare_result < 0) {
high = index;
}
index = (high + low) / 2;
const char *procedure_name = (const char *)((char *)k32 + name_pointer_table[index]);
const char *getprocaddress_name = "GetProcAddress";
for (;;) { // perform strcmp()
if (*getprocaddress_name != *procedure_name) {
compare_result = *(unsigned char *)getprocaddress_name - *(unsigned char *)procedure_name;
break;
}
if (!*getprocaddress_name) {
compare_result = 0;
break;
}
getprocaddress_name += 1;
procedure_name += 1;
}
}
*(void **)&GetProcAddress = ((char *)k32 + export_address_table[ordinal_table[index]]);
load_proc(k32, &LoadLibraryA, "LoadLibraryA");
void *user32 = LoadLibraryA("user32.dll");
load_proc(k32, &ExitProcess, "ExitProcess");
load_proc(user32, &MessageBoxA, "MessageBoxA");
}
int main() {
dynamic_link();
MessageBoxA(0, "Hello, world!\n", "Message", 0);
ExitProcess(0);
}
@pmttavara
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment