selph
selph
发布于 2021-12-04 / 791 阅读
0
0

利用VEH混淆API调用流程

本文主要内容主要参考自:攻击技术研判|DRIDEX木马巧用VEH混淆API调用流程 - FreeBuf网络安全行业门户

昨天初次学习VEH的基本使用,网上搜索关于VEH的东西的时候发现了这篇文章,这VEH使用的手法真是太巧妙了,且听我慢慢道来

简介

这个技术使用了int3+ret的组合,同时对抗动态分析和静态分析,基于VEH能够修改线程上下文的特点,int3在无调试器的状态下会被VEH接住,通过新增栈的数据来为两次ret跳转提供返回地址,从而使得在这里进行了一次函数调用

实验环境:VS2022

代码实现&原理

这个技术的目的是隐蔽调用函数,方式是通过异常处理进行:

  1. 首先我们需要一个函数地址,这里图省事就直接获取了
  2. 其次我们需要触发一个异常,用断点异常也好,除零异常也罢,只要能触发异常就行
  3. 然后紧接着在异常触发后面跟进一个ret指令,用于函数调用

到此已经为函数调用准备好了条件,接下来的处理就需要在VEH里进行了

void showFlag() {
	printf("API Obfuscation Call\r\n");
}

int main(int argc) {
	AddVectoredExceptionHandler(0, (PVECTORED_EXCEPTION_HANDLER)Handler);
	DWORD a = (DWORD)showFlag;		// 通过某种方式把要隐蔽调用的API地址放到寄存器里
	DWORD b = 0;
	int c;
	if (argc > INT_MAX)goto LABEL;	// 不存在的跳转让ret后面在静态分析时出现标签
	c = a / b;						// 触发VEH,修改esp内容,为之后两次跳转做准备
	__asm {
		ret;						// 跳转到隐蔽函数处
	}
LABEL:
	printf("normal function code:%d\r\n", c);
	return 0;
}

VEH处理函数如下:具体是在做什么事情,先看下面的调试分析会比较好理解

LONG NTAPI Handler(
	struct _EXCEPTION_POINTERS* ExceptionInfo
) {
	ExceptionInfo->ContextRecord->Eip +=2;	// 跳过触发异常的指令
	ExceptionInfo->ContextRecord->Esp -= 4;	
	*(DWORD*)ExceptionInfo->ContextRecord->Esp = ExceptionInfo->ContextRecord->Eip + 1;	// push 跳过ret指令之后的地址
	ExceptionInfo->ContextRecord->Esp -= 4;
	*(DWORD*)ExceptionInfo->ContextRecord->Esp = ExceptionInfo->ContextRecord->Eax;		// push 要调用的函数地址
	return EXCEPTION_CONTINUE_EXECUTION;
}

具体是什么意思呢,我们打开VS在发生异常的地方下个断点:

image-20211203115600810

查看此时的栈和寄存器:(这里仅把关键的部分摘出来了)

EIP = 008010B6 ESP = 0019FF18 

0x0019FF0C  ad 10 80 00  ?.€.
0x0019FF10  00 00 00 00  ....
0x0019FF14  40 10 80 00  @.€.
0x0019FF18  97 12 80 00  ?.€.	// 当前ESP
0x0019FF1C  01 00 00 00  ....

接下来在VEH处理程序执行完的时候再下个断点,等断下来的时候再次来查看这里的寄存器和栈:

image-20211203120109992

eip被修改为了0x008010b8,异常触发的指令地址是0x008010b6,这里跳过了触发异常的指令

栈的变化如下:

0x0019FF0C  00 00 00 00  ....
0x0019FF10  90 10 80 00  ?.€.	// 当前ESP 00801090
0x0019FF14  b9 10 80 00  ?.€.	// 		  008010b9
0x0019FF18  97 12 80 00  ?.€.	// 之前ESP
0x0019FF1C  01 00 00 00  ....

栈里多了两个成员,0x0019FF10处的地址是我们获取的函数地址,而0x0019FF14处的地址是从异常处跳过触发异常的指令后,再跳过ret指令后的地址

通过ret进入函数调用,当函数返回的时候需要从栈里拿到一个返回地址,这个地址就作为函数的返回地址使用,从而完成了函数调用并返回

使用效果

程序直接点开,会执行被VEH处理异常:

image-20211203120829966

通过动态调试器打开,调试器会接管这个异常

通过IDA打开,主函数会被截断:

image-20211203121037970


评论