{"id":15049465,"url":"https://github.com/matthewgeorgy/wintrace","last_synced_at":"2025-04-10T02:12:34.923Z","repository":{"id":213974045,"uuid":"414015032","full_name":"matthewgeorgy/wintrace","owner":"matthewgeorgy","description":"simple tracing utility for windows","archived":false,"fork":false,"pushed_at":"2024-09-03T00:16:50.000Z","size":432,"stargazers_count":23,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-10T02:12:20.014Z","etag":null,"topics":["c","c89","win32","windows"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/matthewgeorgy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-10-06T00:02:32.000Z","updated_at":"2025-04-08T15:25:30.000Z","dependencies_parsed_at":"2023-12-29T22:41:20.389Z","dependency_job_id":"56e25652-ef1e-4418-b8b8-0c5429b3ec90","html_url":"https://github.com/matthewgeorgy/wintrace","commit_stats":null,"previous_names":["matthewgeorgy/wintrace"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthewgeorgy%2Fwintrace","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthewgeorgy%2Fwintrace/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthewgeorgy%2Fwintrace/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthewgeorgy%2Fwintrace/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/matthewgeorgy","download_url":"https://codeload.github.com/matthewgeorgy/wintrace/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248142904,"owners_count":21054671,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","c89","win32","windows"],"created_at":"2024-09-24T21:20:36.492Z","updated_at":"2025-04-10T02:12:34.891Z","avatar_url":"https://github.com/matthewgeorgy.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wintrace\n\n`wintrace` is a simple tracing utility for Windows programs.\nIt's essentially an `strace`/`ltrace` equivalent like on Linux, but for Windows.\n\nIt currently supports a wide range of Win32 functions, as well as some functions from the CRT. See\nbelow for a complete list of supported functions (COMING SOON...).\n\n## Usage\n\n`wintrace` is a CLI tool, so it's very simple to start using.\n\nThe following is an available list of options for use with `wintrace`,\nwhich can be obtained by running `wintrace /?`.\n\n```\nUsage: wintrace [options...] \u003cexe\u003e [args...]\n\nOptions:\n  /c            Show function call count\n  /p            Show process ID\n  /t            Show thread ID\n  /T:fns        Trace only fns, a comma separated list of function names\n  /o:file       Output to file\n  /?            Show available options\n```\n\nHere is some sample output, taken from running the `test_fileapi` test:\n```\n\u003e wintrace /c /t /p tests\\test_fileapi.exe\n\n[7008] \u003c3484\u003e (1)  CreateDirectoryA(\"Foo\", 0x0000000000000000) = 1\n[7008] \u003c3484\u003e (1)  CreateFileA(\"Foo\\bar.txt\", 3221225472, 0, 0x0000000000000000, 2, 128, 0x0000000000000000) = 0x00000000000000A0\n[7008] \u003c3484\u003e (1)  WriteFile(0x00000000000000A0, 0x00007FF6A3FE3000, 14, 0x0000000000FEFC60, 0x0000000000000000) = 1\n[7008] \u003c3484\u003e (1)  SetFilePointer(0x00000000000000A0, 0, 0x0000000000000000, 0) = 0\n[7008] \u003c3484\u003e (1)  ReadFile(0x00000000000000A0, 0x0000000000FEFC70, 199, 0x0000000000FEFC54, 0x0000000000000000) = 1\n[7008] \u003c3484\u003e (1)  DeleteFileA(\"Foo\\bar.txt\") = 1\n[7008] \u003c3484\u003e (1)  RemoveDirectoryA(\"Foo\") = 1\n\n[7008] \u003c3484\u003e (1)  CreateDirectoryW(\"Foo\", 0x0000000000000000) = 1\n[7008] \u003c3484\u003e (1)  CreateFileW(\"Foo\\bar.txt\", 3221225472, 0, 0x0000000000000000, 2, 128, 0x0000000000000000) = 0x0000000000000058\n[7008] \u003c3484\u003e (2)  WriteFile(0x0000000000000058, 0x00007FF6A3FE3000, 14, 0x0000000000FEFC60, 0x0000000000000000) = 1\n[7008] \u003c3484\u003e (2)  SetFilePointer(0x0000000000000058, 0, 0x0000000000000000, 0) = 0\n[7008] \u003c3484\u003e (2)  ReadFile(0x0000000000000058, 0x0000000000FEFC70, 199, 0x0000000000FEFC54, 0x0000000000000000) = 1\n[7008] \u003c3484\u003e (1)  DeleteFileW(\"Foo\\bar.txt\") = 0 (ERROR: 32)\n[7008] \u003c3484\u003e (1)  RemoveDirectoryW(\"Foo\") = 0 (ERROR: 145)\n```\n\nThe full format `wintrace`'s output follows as:\n\n`[Process ID] \u003cThread ID\u003e (Call Cnt) FunctionName(Args...) = Return Value`.\n\n## Limitations\n\nThe only current limitation with using `wintrace` is that it requires your program to be compiled with\nthe DLL version of the CRT (C Runtime Library), `msvcrt.lib`. There are a few ways to do this with `cl`:\n\n- Compiling your whole program with the `/MD` switch in MSVC  (`cl /MD ...`),\nor `/MDd` for the debug version, `msvcrtd.lib`.\n\n- Linking your program directly to `msvcrt.lib` (or `msvcrtd.lib` with `link`\n(`link ... msvcrt.lib ...`).\n\nSee the Microsoft Docs on `cl` and `link` for more details. To specify `msvcrt/d.lib` in Visual Studio,\ncheck either your Visual Studio Documentation, or the Microsoft Docs, to see how to specify\nthis lib in your project's property pages (I won't detail it here since it changes between version releases\nof Visual Studio).\n\n`wintrace` must also be built for the correct target platform of the program you wish to test. That is\nto say, if you must build `wintrace` for x64 if you want to test an x64 executable (same story for x86).\n\nIn addition, `wintrace`'s numerical output (like the numbers found in function parameters is only\nformatted correctly for x64. You can still build and run `wintrace` for x86, but note that some of the\nnumerical values will be incorrect. This will be addressed soon.\n\n## How does it work?\n\nTo understand how `wintrace` works internally, you must first understand (at a basic level) how DLL's\nwork under Windows. I will not go into too much detail about how they work, as it is a well documented topic that you can\nread about extensively online (namely MSDN). I will only focus on the parts that are relevant to wintrace and how it works.\n\n### The Import Address Table (IAT)\n\nOne of the core components of the Windows PE (portable executable) file format is the \"Import Address Table\" (IAT for short).\nThe IAT is a table containing a list of all the functions that are to be dynamically linked to your program through a DLL. It contains\ntwo pieces of information which we care about:\n1) The function's name (as an ASCII string).\n2) The function's address (function pointer).\n\nWhen a PE is loaded into memory, one thing that needs to be done is that the function pointers for the dynamically-linked functions need\nto be resolved. Say we need to use the Win32 function `HeapAlloc`, which comes from `kernel32.dll`. When we run our program, Windows first\nneeds to load `kernel32.dll` into memory (if it hasn't already). Once the DLL is loaded, Windows then needs to set the function pointer for\n`HeapAlloc` in our program's IAT to point to the address of `HeapAlloc` loaded into memory in the DLL. That way, our program knows where the\nfunction is in order to execute it (remember! `HeapAlloc` is DYNAMICALLY linked, not STATICALLY linked, so the function is not part of the .exe).\n\nI've skipped over some of the details, but at its core this is how dynamic linking works under Windows, and it will suffice in order to understand\nhow wintrace works.\n\n### DLL Injection\n\n`wintrace` primarily works through a technique called \"DLL Injection\". It is a technique that allows you to run code inside of another process\nby forcing it to load a DLL. This is because when you load a DLL, the `DllMain()` function of that DLL will then be invoked (if it exists). This is\nwhere you put the code that you want to run. There are several ways of doing this, but the method I've chosen involves the use of a few Win32 functions\nwhich I'll discuss below.\n\n`CreateProcess` is a function that allows you to spawn a new process given an executable file name.\n\n`LoadLibrary` is the function you use for loading a DLL into your program.\n\n`CreateRemoteThread` is a function that allows you to create a new thread inside of an existing process. (You might be thinking \"Wow, this sounds like a\nhuge security hole!\", and you'd be right; except, you can only create a remote thread for a process that *your program created*, such as with `CreateProcess` :) ).\n\nThe process for doing DLL injection on a targeted .exe then looks something like this:\n\n0. Get the program name and DLL name you wish to target.\n1. Create a process with the program (.exe) name, with the `CREATE_SUSPENDED` flag so that the main thread does not immediately begin executing.\n2. Create a remote thread in the process, setting the thread's entry function is `LoadLibrary`, and passing it the name of DLL you want.\n3. Wait for the remote thread to finish executing (namely, it's `DllMain()`).\n4. Resume the main thread of the target exe.\n\n### Putting it all together\n\nSo we understand these two concepts now, but what are they good for? Well remember, wintrace is a tracing application - we want to be able to monitor the functions\nthat are being executed in real-time. How can this be done?\n\nSimple! We simply define a function \"hook\" that essentially wraps a function we want to trace, and give it some additional behaviour. Here's a simple example:\n```\nLPVOID\nHeapAlloc_Hooked(HANDLE hHeap,\n\t\t  \t\t DWORD dwFlags,\n\t\t  \t\t SIZE_T dwBytes)\n{\n\tLPVOID\t\tResult;\n\n\tprintf(\"HeapAlloc(0x%p, 0x%u, 0x%u) = \", hHeap, dwFlags, dwBytes);\n\tResult = HeapAlloc(hHeap, dwFlags, dwBytes);\n\tprintf(\"0x%p\\n\", Result);\n\n\treturn Result;\n}\n\n```\n\nAs you can see, this hooked function gives us the information we want: it prints the call to `HeapAlloc()`, the parameters that were passed, and the return values!\n\nSince we'll have many functions like this that we wish to hook, it would be a good idea to put them all into a `.dll` (not a `.lib` because we want to hook precompiled\nbinaries).\n\nSo now that we've got function hooks, how can we get our (already compiled) program to use them? Well we can just overwrite the IAT using some functions in Win32 from\nthe `DbgHelp` library. All we need to do is open our running program in memory, walk through all the functions in the IAT and overwrite their pointers. While this may sound\ncrazy, it's perfectly fine to do - if you do it correctly of course :). (While it should be obvious, I will also point out that we can only overwrite functions for which we\nhave hooks for).\n\n### wintrace implementation\n\nSo how does wintrace do this? wintrace has two halves: `wintrace.exe` and `wintrace.dll`. `.exe` belongs to the `core\\` folder; `.dll` belongs to the `dll\\` folder.\n\n`wintrace.dll` contains two things: the hooked function definitions and a `DllMain()` function. The hooked functions are exactly as I described above; Win32 hooks are\nprefixed with `Wt`, CRT hooks are prefixed with `wt_`, and the hooks can be found in their respective folders under `dll\\`. The `DllMain()` is where we actually\noverwrite the IAT, which is done by calling the function `PatchIAT()`.\n\nThe purpose of `wintrace.exe` is to actually perform DLL injection into the target application\nfollowing the procedure outlined above. This can be seen in the main function of `core\\main.c`.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthewgeorgy%2Fwintrace","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmatthewgeorgy%2Fwintrace","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthewgeorgy%2Fwintrace/lists"}