5 minutes
Monitor and Intercept Windows API Calls with Frida
I was searching online tools similar to strace to track Windows binary WinAPI calls.
Not so much interesting content surfaced at first glance.
Many articles referred to an old tool: API Monitor from rohitab. However, this program is no longer actively maintained and its source code is not available.
Another option was using frida. It’s easy to install and use, is scriptable, and can be adapted to several tasks.
It can easily log Windows API calls made by a binary, and generally track any other function or piece of code executed by a process.
Basic usage
After installing the tool, it can be used to launch a process and decide what to track.
Track a WinAPI or a WinAPI pattern
C:\Code> frida-trace.exe -i "WriteFile" .\HelloWorld.exe
Instrumenting...
WriteFile: Loaded handler at "C:\Code\__handlers__\KERNELBASE.dll\WriteFile.js"
WriteFile: Loaded handler at "C:\Code\__handlers__\KERNEL32.DLL\WriteFile.js"
Hello World!
Started tracing 2 functions. Web UI available at http://localhost:27098/
/* TID 0x10e8 */
8 ms WriteFile()
8 ms | WriteFile()
8 ms WriteFile()
8 ms | WriteFile()
Track a module
C:\Code> frida-trace.exe -i "KERNELBASE.dll" .\HelloWorld.exe
QueryUnbiasedInterruptTimePrecise: Loaded handler at "C:\Code\__handlers\KERNELBASE.dll\QueryUnbiasedInterruptTimePrecise.js"
UrlHashW: Loaded handler at "C:\Code\__handlers\KERNELBASE.dll\UrlHashW.js"
PathCchAddExtension: Loaded handler at "C:\Code\__handlers\KERNELBASE.dll\PathCchAddExtension.js"
GetAppliedGPOListInternalA: Loaded handler at "C:\Code\__handlers\KERNELBASE.dll\GetAppliedGPOListInternalA.js"
...
RegDeleteValueA: Loaded handler at "C:\Code\__handlers\KERNELBASE.dll\RegDeleteValueA.js"
OpenThreadToken: Loaded handler at "C:\Code\__handlers\KERNELBASE.dll\OpenThreadToken.js"
PathRelativePathToA: Loaded handler at "C:\Code\__handlers\KERNELBASE.dll\PathRelativePathToA.js"
SetPriorityClass: Loaded handler at "C:\Code\__handlers\KERNELBASE.dll\SetPriorityClass.js"
VerifyPackageFamilyNameA: Loaded handler at "C:\Code\__handlers_KERNELBASE.dllVerifyPackageFamilyNameA.js"
Warning: Skipping "GetCurrentThread": unable to intercept function at 7609FF20; please file a bug
Warning: Skipping "GetCurrentProcess": unable to intercept function at 75FCEEB0; please file a bug
Warning: Skipping "DebugBreak": unable to intercept function at 76048DC0; please file a bug
Warning: Skipping "LoadAppInitDlls": unable to intercept function at 76002D70; please file a bug
Warning: Skipping "DeleteProcThreadAttributeList": unable to intercept function at 75FCACE0; please file a bug
Started tracing 1773 functions. Web UI available at http://localhost:27116/
Hello World!
/* TID 0x1d50 */
1433 ms WTSGetServiceSessionId()
1433 ms WTSGetServiceSessionId()
1433 ms WTSGetServiceSessionId()
1433 ms WTSGetServiceSessionId()
1434 ms WTSGetServiceSessionId()
1434 ms WTSGetServiceSessionId()
1434 ms WTSGetServiceSessionId()
1434 ms GetLastError()
...
1441 ms WTSGetServiceSessionId()
1442 ms WTSGetServiceSessionId()
1442 ms GetLastError()
1442 ms FlsGetValue()
1442 ms WriteFile()
1442 ms GetLastError()
1442 ms GetProcAddressForCaller()
1442 ms FlsGetValue()
1442 ms GetLastError()
1442 ms FlsGetValue()
1442 ms WriteFile()
1442 ms GetLastError()
1442 ms FlsGetValue()
1442 ms GetLastError()
1442 ms FlsGetValue()
1442 ms GetModuleHandleW()
1442 ms GetModuleHandleW()
1442 ms WTSGetServiceSessionId()
1442 ms WTSGetServiceSessionId()
1442 ms WTSGetServiceSessionId()
1442 ms WTSGetServiceSessionId()
1442 ms WTSGetServiceSessionId()
1442 ms WTSGetServiceSessionId()
1442 ms LoadLibraryExW()
1442 ms | WTSGetServiceSessionId()
1442 ms GetProcAddressForCaller()
1442 ms AppPolicyGetProcessTerminationMethod()
1442 ms GetModuleHandleExW()
1442 ms | WTSGetServiceSessionId()
Process terminated
As you can see it logs every time a hooked function is called by the binary.
Writing custom hooks
The details of how Frida performs the code hooking procedure will not be discussed in this blog article. However, it is possible to write custom hooks in JavaScript to, for example, print the arguments with which a function is called and potentially other relevant information for analysis, such as data buffers.
For example, a WriteFile
JS hook could be written matching the WinAPI signature.
Take a look at WriteFile
signature.
BOOL WriteFile(
[in] HANDLE hFile,
[in] LPCVOID lpBuffer,
[in] DWORD nNumberOfBytesToWrite,
[out, optional] LPDWORD lpNumberOfBytesWritten,
[in, out, optional] LPOVERLAPPED lpOverlapped
);
Write js code.
'use strict'
var WriteFile = Module.findExportByName("kernel32.dll", "WriteFile");
if (WriteFile) {
Interceptor.attach(WriteFile, {
onEnter: function (args) {
// HANDLE hFile
this.hFile = args[0].toInt32();
console.log("[+] WriteFile called with hFile: " + this.hFile);
// LPCVOID lpBuffer
this.lpBuffer = args[1];
console.log("[+] lpBuffer: " + this.lpBuffer);
// DWORD nNumberOfBytesToWrite
this.nNumberOfBytesToWrite = args[2].toInt32();
console.log("[+] nNumberOfBytesToWrite: " + this.nNumberOfBytesToWrite);
// LPDWORD lpNumberOfBytesWritten
this.lpNumberOfBytesWritten = args[3];
console.log("[+] lpNumberOfBytesWritten: " + this.lpNumberOfBytesWritten);
// LPOVERLAPPED lpOverlapped
this.lpOverlapped = args[4];
console.log("[+] lpOverlapped: " + this.lpOverlapped);
// Print buffer content (optional, for debugging)
if (this.lpBuffer && this.nNumberOfBytesToWrite > 0) {
var bufferContent = Memory.readByteArray(this.lpBuffer, this.nNumberOfBytesToWrite);
console.log("[+] Buffer Content: " + hexdump(bufferContent, { length: this.nNumberOfBytesToWrite }));
}
},
onLeave: function (retval) {
console.log("[+] WriteFile returned: " + retval.toInt32());
if (this.lpNumberOfBytesWritten) {
var bytesWritten = Memory.readU32(this.lpNumberOfBytesWritten);
console.log("[+] Bytes Written: " + bytesWritten);
}
}
});
} else {
console.log("[-] WriteFile not found in kernel32.dll");
}
And execute frida:
PS C:\Code> frida -l .\writefile-hook.js .\HelloWorld.exe
Spawned `.\HelloWorld.exe`. Resuming main thread!
[+] WriteFile called with hFile: 200
Hello World![+] lpBuffer: 0x12fe6d0
[+] nNumberOfBytesToWrite: 12
[+] lpNumberOfBytesWritten: 0x12fe6cc
[+] lpOverlapped: 0x0
[+] Buffer Content: 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 21 Hello World!
[+] WriteFile returned: 1
[+] Bytes Written: 12
[+] WriteFile called with hFile: 200
[+] lpBuffer: 0x12fe724
[+] nNumberOfBytesToWrite: 2
[+] lpNumberOfBytesWritten: 0x12fe720
[Local::HelloWorld.exe ]-> [+] lpOverlapped: 0x0
[+] Buffer Content: 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
00000000 0d 0a ..
[+] WriteFile returned: 1
[+] Bytes Written: 2
Process terminated
[Local::HelloWorld.exe ]->
Thank you for using Frida!
Wonderful. It logs the call, it prints arguments and it’s also able to read the buffer and print content.
Frida is a powerful and, at the same time, simple-to-use tool that can be used to track WinAPI calls in a process for malware analysis, development, and reverse engineering purposes.
855 Words
2025-01-22 15:24