Skip to content

Commit 8cbb9e2

Browse files
authored
Feature: Add DetourFindRemotePayload and improve other payload-related methods (microsoft#81)
Other improvements: - Makes the pcbData parameter in DetourFindPayload and DetourFindPayloadEx optional, so that if an application only needs to search for the presence of a payload, they can ignore the size by passing nullptr. - Makes the pvData parameter in DetourCopyPayloadToProcess const, so that a pointer to a const C++ object can be passed instead of the object needing to be const_casted or being non-const. - Adds DetourCopyPayloadToProcessEx, which has the same interface than DetourCopyPayloadToProcess, but it returns the address of the payload in the remote module, if the program later wants to write to it. - Add payload example and extra unit tests covering new APIs. Fixes microsoft#79 Co-authored-by: Charles Milette <[email protected]>
1 parent 0a3ab89 commit 8cbb9e2

16 files changed

+742
-108
lines changed

samples/Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ all:
9191
@$(MAKE) /NOLOGO /$(MAKEFLAGS)
9292
cd "$(MAKEDIR)\dynamic_alloc"
9393
@$(MAKE) /NOLOGO /$(MAKEFLAGS)
94+
cd "$(MAKEDIR)\payload"
95+
@$(MAKE) /NOLOGO /$(MAKEFLAGS)
9496
cd "$(MAKEDIR)"
9597

9698
clean:
@@ -156,6 +158,8 @@ clean:
156158
@$(MAKE) /NOLOGO /$(MAKEFLAGS) clean
157159
cd "$(MAKEDIR)\dynamic_alloc"
158160
@$(MAKE) /NOLOGO /$(MAKEFLAGS) clean
161+
cd "$(MAKEDIR)\payload"
162+
@$(MAKE) /NOLOGO /$(MAKEFLAGS) clean
159163
cd "$(MAKEDIR)"
160164
-rmdir lib32 2>nul
161165
-rmdir lib64 2>nul
@@ -224,6 +228,8 @@ realclean:
224228
@$(MAKE) /NOLOGO /$(MAKEFLAGS) realclean
225229
cd "$(MAKEDIR)\dynamic_alloc"
226230
@$(MAKE) /NOLOGO /$(MAKEFLAGS) realclean
231+
cd "$(MAKEDIR)\payload"
232+
@$(MAKE) /NOLOGO /$(MAKEFLAGS) realclean
227233
cd "$(MAKEDIR)"
228234
-rmdir lib32 2>nul
229235
-rmdir lib64 2>nul
@@ -309,6 +315,8 @@ test:
309315
@$(MAKE) /NOLOGO /$(MAKEFLAGS) test
310316
cd "$(MAKEDIR)\dynamic_alloc"
311317
@$(MAKE) /NOLOGO /$(MAKEFLAGS) test
318+
cd "$(MAKEDIR)\payload"
319+
@$(MAKE) /NOLOGO /$(MAKEFLAGS) test
312320
cd "$(MAKEDIR)"
313321

314322
##

samples/payload/Makefile

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
##############################################################################
2+
##
3+
## Makefile for Detours Test Programs.
4+
##
5+
## Microsoft Research Detours Package
6+
##
7+
## Copyright (c) Microsoft Corporation. All rights reserved.
8+
##
9+
10+
!include ..\common.mak
11+
12+
LIBS=$(LIBS) kernel32.lib
13+
CFLAGS=$(CFLAGS) /EHsc
14+
15+
all: dirs \
16+
$(BIND)\payload.exe \
17+
$(BIND)\payloadtarget.exe
18+
19+
clean:
20+
-del *~ 2>nul
21+
-del $(BIND)\payload.* 2>nul
22+
-del $(BIND)\payloadtarget.* 2>nul
23+
-rmdir /q /s $(OBJD) 2>nul
24+
25+
realclean: clean
26+
-rmdir /q /s $(OBJDS) 2>nul
27+
28+
dirs:
29+
@if not exist $(BIND) mkdir $(BIND) && echo. Created $(BIND)
30+
@if not exist $(OBJD) mkdir $(OBJD) && echo. Created $(OBJD)
31+
32+
$(OBJD)\payload.obj : payload.cpp
33+
34+
$(BIND)\payload.exe : $(OBJD)\payload.obj $(DEPS)
35+
link\
36+
/SUBSYSTEM:CONSOLE\
37+
$(LINKFLAGS)\
38+
$(LIBS)\
39+
/PDB:"$(@R).pdb"\
40+
/OUT:"$@"\
41+
$**\
42+
43+
$(OBJD)\payloadtarget.obj : payloadtarget.cpp
44+
45+
$(BIND)\payloadtarget.exe : $(OBJD)\payloadtarget.obj $(DEPS)
46+
link\
47+
/SUBSYSTEM:CONSOLE\
48+
$(LINKFLAGS)\
49+
$(LIBS)\
50+
/PDB:"$(@R).pdb"\
51+
/OUT:"$@"\
52+
$**\
53+
54+
##############################################################################
55+
56+
test: $(BIND)\payload.exe $(BIND)\payloadtarget.exe
57+
$(BIND)\payload.exe
58+
59+
################################################################# End of File.

samples/payload/payload.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#include <iostream>
2+
#include <string>
3+
#include <windows.h>
4+
#include <detours.h>
5+
6+
#include "payloadguid.hpp"
7+
8+
HANDLE hChildProcess = NULL;
9+
HANDLE hChildThread = NULL;
10+
11+
__declspec(noreturn) void HandleApiFailure(const char* api)
12+
{
13+
DWORD lastErr = GetLastError();
14+
std::cout << "payload.exe: " << api << " failed (" << lastErr << ')' << std::endl;
15+
16+
if (hChildThread != NULL)
17+
{
18+
CloseHandle(hChildThread);
19+
}
20+
21+
if (hChildProcess != NULL)
22+
{
23+
TerminateProcess(hChildProcess, 1);
24+
CloseHandle(hChildProcess);
25+
}
26+
27+
ExitProcess(1);
28+
}
29+
30+
std::wstring GetProcessFileName(HANDLE process)
31+
{
32+
DWORD exeLocation_size = MAX_PATH + 1;
33+
34+
std::wstring exeLocation;
35+
exeLocation.resize(exeLocation_size);
36+
37+
if (!QueryFullProcessImageNameW(process, 0, &exeLocation[0], &exeLocation_size))
38+
{
39+
HandleApiFailure("QueryFullProcessImageNameW");
40+
}
41+
42+
exeLocation.resize(exeLocation_size);
43+
return exeLocation;
44+
}
45+
46+
void StartChild()
47+
{
48+
std::wstring target = GetProcessFileName(GetCurrentProcess());
49+
target.erase(target.rfind(L'\\') + 1);
50+
target += L"payloadtarget.exe";
51+
52+
STARTUPINFOW si = { sizeof(si) };
53+
PROCESS_INFORMATION pi;
54+
if (!CreateProcessW(target.c_str(), NULL, NULL, NULL, false,
55+
CREATE_SUSPENDED, NULL, NULL, &si, &pi))
56+
{
57+
HandleApiFailure("CreateProcessW");
58+
}
59+
60+
hChildProcess = pi.hProcess;
61+
hChildThread = pi.hThread;
62+
}
63+
64+
template<typename T>
65+
volatile T* InjectPayload(HANDLE hProcess, T payload, REFGUID guid)
66+
{
67+
return static_cast<volatile T*>(
68+
DetourCopyPayloadToProcessEx(hProcess,guid, &payload, sizeof(payload)));
69+
}
70+
71+
int main()
72+
{
73+
StartChild();
74+
75+
// give the child a handle to ourself
76+
HANDLE targetHandleToParent;
77+
if (!DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(),
78+
hChildProcess, &targetHandleToParent, 0, false, DUPLICATE_SAME_ACCESS))
79+
{
80+
HandleApiFailure("DuplicateHandle");
81+
}
82+
83+
if (!InjectPayload(hChildProcess, targetHandleToParent, PARENT_HANDLE_PAYLOAD))
84+
{
85+
HandleApiFailure("DetourCopyPayloadToProcessEx");
86+
}
87+
88+
// inject a payload in ourself containing zero data
89+
// the goal is for the child process to find this payload
90+
// and fill it with random data, to test DetourFindRemotePayload
91+
volatile random_payload_t* payloadAddr =
92+
InjectPayload<random_payload_t>(GetCurrentProcess(), 0, RANDOM_DATA_PAYLOAD);
93+
if (!payloadAddr)
94+
{
95+
HandleApiFailure("DetourCopyPayloadToProcessEx");
96+
}
97+
98+
if (!ResumeThread(hChildThread))
99+
{
100+
HandleApiFailure("ResumeThread");
101+
}
102+
103+
CloseHandle(hChildThread);
104+
hChildThread = NULL;
105+
106+
if (WaitForSingleObject(hChildProcess, INFINITE) == WAIT_FAILED)
107+
{
108+
HandleApiFailure("WaitForSingleObject");
109+
}
110+
111+
DWORD exitCode;
112+
if (!GetExitCodeProcess(hChildProcess, &exitCode))
113+
{
114+
HandleApiFailure("GetExitCodeProcess");
115+
}
116+
117+
// the exit code should match the random data the child process gave us
118+
random_payload_t payload = *payloadAddr;
119+
if (exitCode == payload)
120+
{
121+
std::cout << "Success, exit code (0x" << std::uppercase << std::hex << exitCode
122+
<< ") matches payload content (0x" << payload << ')' << std::endl;
123+
return 0;
124+
}
125+
else
126+
{
127+
std::cout << "Error, exit code (0x" << std::uppercase << std::hex << exitCode
128+
<< ") does not matches payload content (0x" << payload << ')' << std::endl;
129+
return 1;
130+
}
131+
}

samples/payload/payloadguid.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#pragma once
2+
#include <guiddef.h>
3+
4+
// {C2569A74-12FE-4E06-8F02-8DF13E39A266}
5+
const GUID PARENT_HANDLE_PAYLOAD =
6+
{ 0xc2569a74, 0x12fe, 0x4e06, { 0x8f, 0x2, 0x8d, 0xf1, 0x3e, 0x39, 0xa2, 0x66 } };
7+
8+
// {CB5230ED-04FA-4C47-B606-AC09B2777601}
9+
const GUID RANDOM_DATA_PAYLOAD =
10+
{ 0xcb5230ed, 0x4fa, 0x4c47, { 0xb6, 0x6, 0xac, 0x9, 0xb2, 0x77, 0x76, 0x1 } };
11+
12+
typedef unsigned int random_payload_t;

samples/payload/payloadtarget.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#define _CRT_RAND_S
2+
#include <stdlib.h>
3+
4+
#include <iostream>
5+
#include <windows.h>
6+
#include <detours.h>
7+
8+
#include "payloadguid.hpp"
9+
10+
HANDLE hParent = NULL;
11+
12+
__declspec(noreturn) void HandleApiFailure(const char* api)
13+
{
14+
DWORD lastErr = GetLastError();
15+
std::cout << "payloadtarget.exe: " << api << " failed (" << lastErr << ')' << std::endl;
16+
17+
if (hParent)
18+
{
19+
CloseHandle(hParent);
20+
}
21+
22+
ExitProcess(1);
23+
}
24+
25+
int main()
26+
{
27+
DWORD payloadSize;
28+
void* payloadAddr = DetourFindPayloadEx(PARENT_HANDLE_PAYLOAD, &payloadSize);
29+
if (!payloadAddr || payloadSize != sizeof(HANDLE))
30+
{
31+
HandleApiFailure("DetourFindPayloadEx");
32+
}
33+
34+
hParent = *static_cast<HANDLE*>(payloadAddr);
35+
36+
DWORD randomPayloadSize;
37+
void* randomPayload = DetourFindRemotePayload(hParent, RANDOM_DATA_PAYLOAD, &randomPayloadSize);
38+
if (!randomPayload || randomPayloadSize != sizeof(random_payload_t))
39+
{
40+
HandleApiFailure("DetourFindRemotePayload");
41+
}
42+
43+
random_payload_t randomData;
44+
if (rand_s(&randomData) != 0)
45+
{
46+
HandleApiFailure("rand_s");
47+
}
48+
49+
50+
if (!WriteProcessMemory(hParent, randomPayload, &randomData, sizeof(randomData), NULL))
51+
{
52+
HandleApiFailure("WriteProcessMemory");
53+
}
54+
55+
CloseHandle(hParent);
56+
hParent = NULL;
57+
58+
// conversion to int return type is potentially undefined
59+
ExitProcess(randomData);
60+
}

0 commit comments

Comments
 (0)