首页
论坛
课程
招聘
[原创]通过NtContinue修改3环程序执行流程
2021-9-25 05:27 17094

[原创]通过NtContinue修改3环程序执行流程

2021-9-25 05:27
17094

挺简单的一个小玩意,可以通过ContinueCall去执行想要执行的函数,执行结束后会返回到指定的SavedContext。逆向分析的时候对ContinueCall或者NtContinue进行F8步过的话直接会跟丢,或者程序会挂掉。(这个思路没见有人说过啊,我估计这也是很鸡肋的东西,对反调试的帮助应该不算很大,也就发着玩玩了,大火看个乐)

不仅可以用GetSavedContext获取context,也可以自定义构造,那样的话就灵活更多了。

#include "ContinueCall.h"

/*内部函数*/

/*
* ContinueCallRoutine
* 功能 - 调用指定的函数并返回到指定的context
* 参数 - SavedContext:返回的context
*	   - TargetProc:指定函数
*	   - Params:参数指针
*	   - ParamSize:参数大小
*	   - Result:返回值缓冲区
*/
__declspec(naked) VOID ContinueCallRoutine(PCONTEXT SavedContext, PROC TargetProc, PVOID Params, SIZE_T ParamSize, PDWORD Result) {
	__asm {
		push ebp
		mov ebp, esp

		sub esp, ParamSize
		mov edi, esp;
		mov esi, Params;
		mov ecx, ParamSize;
		rep movsb;
		mov eax, TargetProc;
		call eax;
		mov ebx, Result;
		mov[ebx], eax;
	}

	NtContinue(SavedContext, TRUE);
}

/*
* GetEsp
* 功能 - 返回当前函数执行中的ESP
*/
__declspec(naked)ULONG GetEsp() {
	__asm {
		mov eax, esp;
		add eax, 0x4;
		ret
	}
}

/*
* GetEip
* 功能 - 返回此函数的返回值
*/
__declspec(naked)ULONG GetEip() {
	__asm {
		mov eax, [esp];
		ret
	}
}

/*导出函数*/

/*
* NtContinue
* 功能 - 执行系统调用NtContinue
*/
ULONG WINAPI NtContinue(PCONTEXT ContextRecord, BOOLEAN TestAlert) {
	static const PVOID addr = GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtContinue");

	if (!addr) {
		return FALSE;
	}
	__asm {
		mov eax, [addr]
		push[TestAlert]
		push[ContextRecord]
		call eax
	}
	return TRUE;
}

/*
* GetSavedContext
* 功能 - 获取此函数返回时的对应Context
* 参数 - SavedContext:保存获取的Context
*/
__declspec(naked) VOID WINAPI GetSavedContext(PCONTEXT SavedContext) {
	__asm {
		push ebp
		mov ebp, esp
	}

	__asm {
		push esi
		push edi
	}

	SavedContext->ContextFlags = CONTEXT_ALL;
	if (!GetThreadContext(GetCurrentThread(), SavedContext)) {
		DEBUG_PRINT("GetThreadContext Failed\n");
		goto END;
	}
	__asm {
		pop edi
		pop esi
		mov eax, SavedContext
		mov[eax + CONTEXT.Edi], edi;
		mov[eax + CONTEXT.Esi], esi;
		mov ebx, [ebp];
		mov[eax + CONTEXT.Ebp], ebx;
		mov ebx, [ebp + 0x4];
		mov[eax + CONTEXT.Eip], ebx;
		mov ebx, ebp;
		add ebx, 0xc;
		mov[eax + CONTEXT.Esp], ebx;
	}

END:
	__asm {
		mov esp, ebp
		pop ebp
		ret 0x4
	}
}

/*
* ContinueCall
* 功能 - 执行目标函数,执行结束后会返回到SavedContext
* 参数 - 其余:与ContinueCallRoutine相关
*	   - pFirst:此函数是否是第一次执行
*/
VOID WINAPI ContinueCall(PCONTEXT SavedContext, PROC TargetProc, PVOID Params, DWORD ParamNum, PDWORD Result, PBOOLEAN pFirst) {
	CONTEXT NewContext;
	CONTEXT Context;
	PCONTEXT pSavedContext;

	if (!SavedContext) {
		GetSavedContext(&Context);
		pSavedContext = &Context;
	}
	else {
		pSavedContext = SavedContext;
	}
	if (!*pFirst) {
		goto END;
	}
	*pFirst = FALSE;

	memcpy(&NewContext, pSavedContext, sizeof(CONTEXT));
	NewContext.Esp = (DWORD)GetEsp() - 0x400;
	((PDWORD)NewContext.Esp)[0] = 0;

	((PDWORD)NewContext.Esp)[1] = (DWORD)pSavedContext;
	((PDWORD)NewContext.Esp)[2] = (DWORD)TargetProc;
	((PDWORD)NewContext.Esp)[3] = (DWORD)Params;
	((PDWORD)NewContext.Esp)[4] = (DWORD)ParamNum * sizeof(DWORD);
	((PDWORD)NewContext.Esp)[5] = (DWORD)Result;
	NewContext.Eip = (DWORD)ContinueCallRoutine;

	NtContinue(&NewContext, TRUE);

END:
	return;
}

测试例子1

int add(int a, int b) {
	return a + b;
}

int main()
{
	int a[2] = { 1,3 };
	int result;
	BOOLEAN bFirst = TRUE;
	CONTEXT context;
	
	GetSavedContext(&context);
	printf("saved context end\n");
	ContinueCall(&context, (PROC)add, a, 2, (PDWORD)&result, &bFirst);

	printf("%d + %d = %d\n", a[0], a[1], result);
END:
	system("pause");

	return 0;
}

/*
输出结果:
saved context end
saved context end
1 + 3 = 4
*/

测试例子2

int a[2] = { 1,3 };
int result;
CONTEXT context;
BOOLEAN bFirst = TRUE;

int add(int a, int b) {
	return a + b;
}

void func1() {
	printf("func1 start\n");
	ContinueCall(&context, (PROC)add, a, 2, (PDWORD)&result, &bFirst);
	printf("func1 end\n");
}

void func2() {
	printf("func2 start\n");
	GetSavedContext(&context);
	func1();
	printf("func2 end\n");
}

int main()
{
	func2();
	printf("%d + %d = %d\n", a[0], a[1], result);
	system("pause");

	return 0;
}

/*
输出结果:
func2 start
func1 start
func1 start
func1 end
func2 end
1 + 3 = 4
*/

测试例子3

int a[2] = { 1,3 };
int result;
CONTEXT context;
BOOLEAN bFirst = TRUE;

int add(int a, int b) {
	return a + b;
}

void func1() {
	printf("func1 start\n");
	ContinueCall(&context, (PROC)add, a, 2, (PDWORD)&result, &bFirst);
	printf("func1 end\n");
}

void func2() {
	printf("func2 start\n");
	func1();
	printf("func2 end\n");
}

int main()
{
	GetSavedContext(&context);
	if (bFirst) {
		func2();
	}
	printf("%d + %d = %d\n", a[0], a[1], result);
	system("pause");

	return 0;
}

/*
输出结果:
func2 start
func1 start
1 + 3 = 4
*/



第五届安全开发者峰会(SDC 2021)10月23日上海召开!限时2.5折门票(含自助午餐1份)

上传的附件:
收藏
点赞2
打赏
分享
最新回复 (4)
雪    币: 207
活跃值: 活跃值 (1078)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yy虫子yy 活跃值 2021-9-25 11:43
2
0
NtContinue相当于jmp,F8就会直接跟丢
用NtContinue来实现call调用,调用完还要通过CONTEXT返回
效率会比较低,不过对于程序接口防止第三方调用稍微有点作用
雪    币: 5861
活跃值: 活跃值 (1260)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
黑洛 活跃值 1 2021-9-25 13:25
3
0
暗桩这种东西,单独一种或许很简单,每样都来一点就很麻烦了。 
雪    币: 223
活跃值: 活跃值 (632)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kakasasa 活跃值 2021-9-27 16:37
4
0
mark
雪    币: 2011
活跃值: 活跃值 (943)
能力值: (RANK:770 )
在线值:
发帖
回帖
粉丝
海风月影 活跃值 18 2021-9-30 11:50
5
0
类似:setjmp/longjmp
游客
登录 | 注册 方可回帖
返回