Click here to Skip to main content
15,884,629 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hi All,

I'm quite new to development in windows and have never done anything related to injection before, so probably my question is an easy one:
I have the following code which intends to inject 2 functions into the memory of another process, and it partially works. I run one of the functions as a remote thread:

C++
HANDLE hThread = CreateRemoteThreadEx(processHandler,
		NULL,
		0,
		(LPTHREAD_START_ROUTINE)remoteFooThreadFunc,
		(LPVOID)remoteParam,
		0,
		NULL,
		&dwThreadId);


The thread works as a charm.
I can also call the other function directly (since for the sake of simplicity I attack my own process), so copying the function is fine.

BUT:

When I try to call this function from the thread function I get an exception:
Quote:
// Unhandled exception at 0x________ in why.exe: 0x________: Access violation executing location 0x________.


Here is my code:

C++
// This example program fails at: '((barFunc_T)(param->func))(¶m->data);',
// the section tagged with '@WHY'
// The program was made using 'Microsoft Visual Studio Express 2013 for Windows Desktop'.
// It can be inserted into any default console application with one modification under:
// Property Pages -> Configuration Properties -> C/C++ -> All Options
// 'Enable Function-Level Linking'
// has to be set to:
// 'No (/Gy-)'

#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <string>

DWORD_PTR inject(HANDLE hProcess, DWORD_PTR data, size_t dataSize)
{
	DWORD_PTR remoteMemoryAddress = NULL;
	SIZE_T bytesTesferred = 0; // number of bytes written/read to/from the remote process;
	remoteMemoryAddress = (DWORD_PTR)VirtualAllocEx(hProcess,
		0,
		dataSize,
		MEM_COMMIT,
		PAGE_EXECUTE_READWRITE);
	if (remoteMemoryAddress == NULL) {
		return NULL;
	}
	bool succeed = WriteProcessMemory(hProcess,
		(LPVOID)remoteMemoryAddress,
		(LPCVOID)data,
		dataSize,
		&bytesTesferred);

	if (!succeed){
		return NULL;
	}
	return remoteMemoryAddress;
}

typedef struct FooParam_t {
	size_t data;
	FooParam_t* next;
	DWORD_PTR func;
} FooParam;

static DWORD* barFunc(size_t* pData) {
	*pData = 9;
	return NULL;
}
static void afterBarFunc(void){}

typedef DWORD* (*barFunc_T)(size_t* pData);
static DWORD* fooThreadFunc(FooParam* param) {

	while (param != NULL){
		// this works as a charm
		param->data = 42;

		// but the next line causes crash if uncommented
		//((barFunc_T)(param->func))(¶m->data); // @WHY

		// Unhandled exception at 0x________ in why.exe:
		// 0x________: Access violation executing location 0x________.
		param = param->next;
	}
	return NULL;
}
static void afterFooThreadFunc(void){}


bool test() {
	HANDLE processHandler = GetCurrentProcess(); //for the sake of simplicity
	DWORD_PTR remoteBarFunc = inject(processHandler,
		(DWORD_PTR)barFunc,
		(size_t)(((LPBYTE)afterBarFunc) - ((LPBYTE)barFunc)));

	FooParam param2;
	param2.data = 0;
	param2.next = NULL;
	param2.func = remoteBarFunc;
	FooParam param1;
	param1.data = 0;
	param1.next = (FooParam*)inject(processHandler,
		(DWORD_PTR)¶m2,
		sizeof(FooParam));

	param1.func = remoteBarFunc;
	DWORD_PTR remoteParam = inject(processHandler,
		(DWORD_PTR)¶m1,
		sizeof(FooParam));

	DWORD_PTR remoteFooThreadFunc = inject(processHandler,
		(DWORD_PTR)fooThreadFunc,
		(size_t)(((LPBYTE)afterFooThreadFunc) - ((LPBYTE)fooThreadFunc)));

	DWORD dwThreadId = 0;
	HANDLE hThread = CreateRemoteThreadEx(processHandler,
		NULL,
		0,
		(LPTHREAD_START_ROUTINE)remoteFooThreadFunc,
		(LPVOID)remoteParam,
		0,
		NULL,
		&dwThreadId);

	if (hThread == NULL) {
		return false;
	}

	// remoteBarFunc can even be directly called, since:
	// processHandler = GetCurrentProcess();
	size_t test = 0;
	((barFunc_T)(remoteBarFunc))(&test);
	if (test != 9) {
		return false;
	}

	WaitForSingleObject(hThread, INFINITE);
	FooParam buffer1;
	buffer1.data = 0;
	buffer1.next = NULL;
	ReadProcessMemory(processHandler,
		(LPVOID)(remoteParam),
		&buffer1,
		sizeof(FooParam),
		NULL);

	FooParam buffer2;
	buffer2.data = 0;
	buffer2.next = NULL;
	ReadProcessMemory(processHandler,
		(LPVOID)(buffer1.next),
		&buffer2,
		sizeof(FooParam),
		NULL);

	return (buffer1.data == 42 && buffer2.data == 42);
}

#include "stdafx.h"


int _tmain(int argc, _TCHAR* argv[])
{

	std::string in;
	std::cout << "TestCase "
		<< (test() ? "succeeded" : "   failed")
		<< " : (injecting thread function that calls injected function)"
		<< std::endl;

	std::cin >> in;
	return 0;
}


I'm quite stucked.

UPDATE:

The call actually happens, but it seems like if I call any function it is unable to return. If I modify barfunc like:
C#
static DWORD* barFunc(size_t* pData) {
    *pData = 9;
    std::cout << "987654321" << std::endl;
    return NULL;
}

I can even see the output on the terminal but it does not return. I've placed an other cout in fooThreadFunc like:
C#
static DWORD* fooThreadFunc(FooParam* param) {

    while (param != NULL){
        ((barFunc_T)(param->func))(&param->data); // @WHY
        std::cout << "123456789" << std::endl;
        param = param->next;
    }
    return NULL;
}
static void afterFooThreadFunc(void){}



Thanks in advance:
~lev

My work is based on the following article:
Three Ways to Inject Your Code into Another Process[^]
Posted
Updated 6-Feb-15 1:20am
v4
Comments
Frankie-C 6-Feb-15 8:01am    
I made a fast check, but too complex to solve in short I need more time to look inside.
Anyway some points: the copied procedure seems uncorrect checking it in assembly, so maybe you have problems on copying. Second point when you are in a different process you should avoid pointers, because memory is not valid, even library function addresses.
L.Lev.B 6-Feb-15 16:51pm    
Hello Frankie,
Thanks for the reply! Actually this issue is strange to me since it seems to me like the function is called properly, but it has problems when it is trying to return.
I've tryed to spearate the problem so I've created a new example.
The inject function is the same and this time I'm not even trying to use any other thread.
//#define fntype WINAPI
#define fntype _cdecl
//#define fntype _stdcall
typedef size_t(fntype *barFunc_T)(void);
static size_t fntype barFunc(void) {
printf("%d",1);
printf("%d",2);
return 42;
}
static void afterBarFunc(void){}
bool test() {
HANDLE processHandler = GetCurrentProcess(); //for the sake of simplicity
barFunc_T remoteBarFunc = (barFunc_T) inject(processHandler,
(DWORD_PTR)barFunc,
(size_t)(((LPBYTE)afterBarFunc) - ((LPBYTE)barFunc)));

return (42 == remoteBarFunc());
}

this test shows me that
1) if I comment out both printf-s in barFunc it works fine, the function returns 42.
2) if I uncomment both printf-s in barFunc it crashes AFTER executing the first printf. It prints out '1', but it seems like that in cannot return. Like if the call stack would be massed up... I was wondering about if it could be caused by the different calling conventions like _cdecl & _stdcall, but the functioncall fails if I try to declare barFunc as _stdcall...
Anyway, thanks for your help again! And waiting your input!
L.Lev.B 7-Feb-15 12:00pm    
Hello Frankie,
Thanks for the comment!
OK, I understand that why I cannot call lets say printf within my injected code.
BUT
What I still miss to understand is that if I inject funcA and funcB, why can't my funcA call funcB (if it gets the address of funcB in the remote process space as a parameter)?
BTW I'm not trying to make any virus. I'm working on a bot for a game that does not have the proper interfaces for a bot. :)
Thanks again,
~lev
Frankie-C 7-Feb-15 15:00pm    
I was jocking about virus :-)
About the func address passed by param that doesn't work, look at my second solution. I created the structure in the remote process memory ... in your sample it is in the calling process. Again an adress resolution problem ;)
You may want take a vew to this article for something more sofisticated here
Anyway if this is satisfying to you don't forget to accept solution.

This is not really a solution as you would expect, but more a description of what are the limits of the injection process and why what you want to do is not 'easy' to achive.
I sayd easy not impossible, because some very sofisticated techniques can be used to overcome the missing relocation of memory addresses in the host process. But this lead more to dark hacking for virus creation than legitimate research (even if the injection should be limited to processes with same rights so de facto giving no advantages).
The problem is that the memory addresses you have in the injected code are not valid in the hacked process, so you cannot call any library function, nor load any data.
If you read more carefully the article from where you based your code you'll see that at the base of all the theory is clearly stated the assumption that the only functions you'll call from your code are LoadLibrary and FreeLibrary that are in kernel32 library and are expected to be loaded at same address in both processes and linked to process memory space (relocation).
So if you try the following simplified code:
C++
#include <windows.h>
#include <stdio.h>

typedef DWORD_PTR __stdcall (*TInjFun)(LPVOID);

void *inject(HANDLE hProcess, LPVOID data, size_t dataSize)
{
	LPVOID remoteMemoryAddress = 0;
	SIZE_T bytesTesferred = 0; // number of bytes written/read to/from the remote process;
	remoteMemoryAddress = VirtualAllocEx(hProcess,
		0,
		dataSize,
		MEM_COMMIT,
		PAGE_EXECUTE_READWRITE);
	if (remoteMemoryAddress == 0) {
		return 0;
	}
	BOOL succeed = WriteProcessMemory(hProcess,
		(LPVOID)remoteMemoryAddress,
		(LPCVOID)data,
		dataSize,
		&bytesTesferred);
 
	if (!succeed){
		return NULL;
	}
	return remoteMemoryAddress;
}

DWORD_PTR __stdcall InjFun(LPVOID p)
{
	//printf("%d-", (DWORD_PTR)p);	//Enable this and the function will fail!
	return (DWORD_PTR)p+1;
}
void EndInj(void){};

int main(int argc, char *argv[])
{
	TInjFun InjectedFun = inject(GetCurrentProcess(), InjFun, (DWORD_PTR)EndInj-(DWORD_PTR)InjFun);

	//Access directly the injected code
	for (int i=0; i<10; i++)
	{
		int r = InjectedFun((LPVOID)i);
		if (r != i+1)
		{
			printf ("Error. Expected %d got %d.\n", i+1, r);
			break;
		}
		//printf("%d\n", r);
		printf ("Expected %d got %d - OK.\n", i+1, r);
	}

	//Access it using remote thread
	for (int i=0; i<10; i++)
	{
		DWORD dwThreadId = 0;

		HANDLE hThread = CreateRemoteThreadEx(GetCurrentProcess(), NULL, 0,
											  (LPTHREAD_START_ROUTINE)InjectedFun,
											  (LPVOID)i, 0, NULL, &dwThreadId);
 
		if (hThread == NULL)
		{
			printf("Error Creating remote thread!\n");
			return -1;
		}

		WaitForSingleObject(hThread, INFINITE);

		DWORD_PTR r = 0;
		GetExitCodeThread(hThread, &r);
		CloseHandle(hThread);

		if (r != i+1)
		{
			printf ("Error. Expected %d got %d.\n", i+1, r);
			break;
		}
		printf ("Expected %d got %d - OK.\n", i+1, r);
	}
}

it will work because the injected routine make no use of any function or data, that in the remote process will have different addresses (even attaching to the same process).
Just enable the printf inside the injected code and you'll get memory access exception.
I hope to have been clear enaugh.
 
Share this answer
 
v3
A small hack to allow printf to work, but it will not work in a different process.
I'll stop here...
C++
#include <windows.h>
#include <stdio.h>

typedef DWORD_PTR __stdcall (*TInjFun)(LPVOID);

void *inject(HANDLE hProcess, LPVOID data, size_t dataSize)
{
	LPVOID remoteMemoryAddress = 0;
	SIZE_T bytesTesferred = 0; // number of bytes written/read to/from the remote process;
	remoteMemoryAddress = VirtualAllocEx(hProcess,
		0,
		dataSize,
		MEM_COMMIT,
		PAGE_EXECUTE_READWRITE);
	if (remoteMemoryAddress == 0) {
		return 0;
	}
	BOOL succeed = WriteProcessMemory(hProcess,
		(LPVOID)remoteMemoryAddress,
		(LPCVOID)data,
		dataSize,
		&bytesTesferred);
 
	if (!succeed){
		return NULL;
	}
	return remoteMemoryAddress;
}

typedef struct
{
	int val;
	void *pfun;
	char str[16];
} PassData;

DWORD_PTR __stdcall InjFun(PassData *p)
{
	((int (__cdecl *)(char *, ...))p->pfun)(p->str, p->val);
	return p->val+1;
}
void EndInj(void){};

int main(int argc, char *argv[])
{
	TInjFun InjectedFun = inject(GetCurrentProcess(), InjFun, (DWORD_PTR)EndInj-(DWORD_PTR)InjFun);
	//Note: we create the data structure to pass parameter to remote process in the
	//      remote process address space ;-)
	PassData *RemData = VirtualAllocEx(GetCurrentProcess(), NULL, 256, MEM_COMMIT, PAGE_READWRITE);
	strcpy(RemData->str, "%d - ");
	RemData->pfun = printf;

	//Access directly the injected code
	for (int i=0; i<10; i++)
	{
		RemData->val = i;
		int r = InjectedFun((LPVOID)RemData);
		if (r != i+1)
		{
			printf ("Error. Expected %d got %d.\n", i+1, r);
			break;
		}
		printf ("Expected %d got %d - OK.\n", i+1, r);
	}

	//Access it using remote thread
	for (int i=0; i<10; i++)
	{
		DWORD dwThreadId = 0;

		RemData->val = i;
		HANDLE hThread = CreateRemoteThreadEx(GetCurrentProcess(), NULL, 0,
											  (LPTHREAD_START_ROUTINE)InjectedFun,
											  (LPVOID)RemData, 0, NULL, &dwThreadId);
 
		if (hThread == NULL)
		{
			printf("Error Creating remote thread!\n");
			return -1;
		}

		WaitForSingleObject(hThread, INFINITE);

		DWORD_PTR r = 0;
		GetExitCodeThread(hThread, &r);
		CloseHandle(hThread);

		if (r != i+1)
		{
			printf ("Error. Expected %d got %d.\n", i+1, r);
			break;
		}
		printf ("Expected %d got %d - OK.\n", i+1, r);
	}
}
 
Share this answer
 
v5
Actually I went down on the level of assembly too and I think I managed to figure out what is going on there:

I've started to compare what is the difference of the call flow of remoteBarFunc when I call it directly and when I call it from another injected methode.

So when I call it directly the asm looks like this:
C++
0019A53C 8B F4                mov         esi,esp  
0019A53E 8D 45 A8             lea         eax,[ebp-58h]  
0019A541 50                   push        eax  
0019A542 FF 55 E8             call        dword ptr [ebp-18h]  
0019A545 83 C4 04             add         esp,4  
0019A548 3B F4                cmp         esi,esp  
0019A54A E8 99 6E FF FF       call        001913E8

    __RTC_CheckEsp:
    001913E8 E9 83 65 00 00    jmp    00197970  
        00197970 75 01             jne    00197973  
        00197972 C3                    ret

BUT: when I call it from the other injected function it looks like this:
C++
00F3002E 8B 45 08             mov         eax,dword ptr [ebp+8]  
00F30031 8B F4                mov         esi,esp  
00F30033 50                   push        eax  
00F30034 8B 4D 08             mov         ecx,dword ptr [ebp+8]  
00F30037 8B 51 08             mov         edx,dword ptr [ecx+8]  
00F3003A FF D2                call        edx  
00F3003C 83 C4 04             add         esp,4  
00F3003F 3B F4                cmp         esi,esp  
00F30041 E8 B2 6F FF FF       call        00D56FF8
    00D56FF8??                   ?? ?????? 


So I've started to look after waht is this __RTC_CheckEsp and it turned out to be the RuntimeCheck. So I've turned it off by setting it to "Default" from "Both (/RTC1, equiv. to /RTCsu) (/RTC1)" and it suddenly all started to work :D
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900