CreateRemoteThreadEx在调用链上失败了吗? [英] CreateRemoteThreadEx fails on call-chain?

查看:55
本文介绍了CreateRemoteThreadEx在调用链上失败了吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好,



我对Windows开发很新,以前从未做过任何与注射有关的事情,所以我的问题可能很简单:

我有以下代码,它打算将2个函数注入另一个进程的内存中,并且它部分有效。我将其中一个函数作为远程线程运行:



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:

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:

//在why.exe中的0x________处的未处理异常:0x________:访问冲突执行位置0x ________。

// Unhandled exception at 0x________ in why.exe: 0x________: Access violation executing location 0x________.





这是我的代码:





Here is my code:

// 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;
}





我很不高兴。



更新:



调用实际发生了,但似乎我调用任何无法返回的函数。如果我修改barfunc,如:



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:

static DWORD* barFunc(size_t* pData) {
    *pData = 9;
    std::cout << "987654321" << std::endl;
    return NULL;
}



我甚至可以看到终端上的输出但它没有返回。我在fooThreadFunc中放置了另一个cout,如:


I can even see the output on the terminal but it does not return. I've placed an other cout in fooThreadFunc like:

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){}







提前致谢:

~lev



我的工作基于以下文章:

三种方法将你的代码注入另一个进程 [ ^ ]

推荐答案

这并不是您所期望的真正解决方案,而是更多地描述了注射过程的限制以及为什么您想做的事情并不容易实现。

我说简单并非不可能,因为一些非常软化的技术可用于克服主机p中缺少的内存地址重定位rocess。但这更多地导致病毒创建的黑暗攻击而非合法研究(即使注入应限于具有相同权利的进程,事实上没有任何优势)。

问题是内存解决了你的问题注入的代码中的代码在被黑客进程中无效,因此您无法调用任何库函数,也无法加载任何数据。

如果您仔细阅读基于代码所在的文章,您将会看看在所有理论的基础上明确说明了你从代码中调用的唯一函数是kernel32库中的LoadLibrary和FreeLibrary,并且预计将在两个进程中加载​​到同一地址并且链接到进程内存空间(重定位)

因此,如果您尝试以下简化代码:

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:
#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);
	}
}



它会起作用,因为注入的例程不使用任何函数或数据,在远程进程中会有不同的地址(甚至附加到相同的过程)。

只需在注入的代码中启用printf,你就会获得内存访问异常。

我希望能够清楚地知道。


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.


允许printf工作的小黑客,但它不能在不同的进程中工作。

我会在这里停止......

A small hack to allow printf to work, but it will not work in a different process.
I'll stop here...
#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);
	}
}


其实我的装配水平也下降了,我想我设法弄清楚发生了什么那里:



当我直接调用remoteBarFunc和从另一个注入的方法调用它时,我已经开始比较remoteBarFunc的调用流程的区别。 br />


所以当我直接调用它时,asm看起来像这样:

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:
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: 当我从其他注入的函数调用它时,它看起来像这样: br />


BUT: when I call it from the other injected function it looks like this:

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??                   ?? ?????? 





所以我开始关注waht是__RTC_CheckEsp,结果证明是RuntimeCheck。所以我把它关闭了默认两者(/ RTC1,等同于/ RTCsu)(/ RTC1)然后它突然全部关闭开始工作:D



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


这篇关于CreateRemoteThreadEx在调用链上失败了吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆