调试恶意dll注册的windows服务

2,826 阅读3分钟

调试恶意dll注册的windows服务

看到一个国外厂商发了报告,其中的样本为dll,是注册系统服务后运行的样本。于是尝试一番找到了调试的方法,记录和分享一下。 报告链接:blog.talosintelligence.com/tinyturla-n…

由于dll文件无法独立运行,必须依赖宿主进程,因此需要使用 svchost.exe 承载这个样本dll。经过查看各位前辈的经验以及书籍资料。总结调试过程如下。

1. 创建系统服务

编写程序利用 Win32 API 创建系统服务,可以指定服务组名称。用 sc 创建服务似乎无法指定服务组名称。代码如下,注意编译为与调试样本相匹配的版本:

#include <windows.h>
#include <stdio.h>

int main()
{
	SC_HANDLE schSCManager;
	SC_HANDLE schService;
	
	// Get a handle to the SCM database.

	schSCManager = OpenSCManager(
		NULL,						// Local computer
		NULL,						// ServiceActive database
		SC_MANAGER_CREATE_SERVICE);	// full access rights

	if (NULL == schSCManager)
	{
		printf("OpenSCManager failed (%d)\n", GetLastError());
		return 1;
	}

	// Create the service

	schService = CreateService(
		schSCManager,				// SCM database
		TEXT("test"),				// name of service
		TEXT("My Sample Service"),	// service name to display
		SERVICE_ALL_ACCESS,			// desired access
		SERVICE_WIN32_SHARE_PROCESS,// service type: share service
		SERVICE_DEMAND_START,		// start type
		SERVICE_ERROR_NORMAL,		// error control type
		TEXT("C:\\Windows\\System32\\svchost.exe -k MyServiceGroup"),	// path to service's binary
		NULL,						// no load ordering group
		NULL,						// no tag identifier
		NULL,						// no dependencies
		NULL,						// LocalSystem account
		NULL						// no password
	);

	if (schService == NULL)
	{
		printf("Create Service failed %d\n", GetLastError());
		CloseServiceHandle(schSCManager);
		return 1;
	}
	else printf("Service installed sccuessfully\n");

	CloseServiceHandle(schService);
	CloseServiceHandle(schSCManager);

	return 0;
}

本次调试的样本是64 位的,因此编译为 x64 版本。

用管理员权限启动cmd后运行程序,查看运行是否成功。

image-20240430110902477.png

2. 配置注册表

2.1 创建服务组 MyServiceGroup

在注册表目录 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost 下,创建自己的服务组,单独存放要调试的服务。

右侧创建 多字符串值 ,名称修改为 “MyServiceGroup”,也可以修改为其他自定义的名称,注意区别于系统的服务组即可。

打开新创建的 多字符串值 键,窗口中填写服务名称 test,这里要和步骤 1 的代码中指定的名称一致。

image-20240411195411515.png

2.2 设置服务参数

在注册表目录 HKLM\SYSTEM\CurrentControlSet\services\test\ 下,即步骤 1 创建的服务。

创建子项 Parameters 项,右侧新建 可扩充字符串值 类型的键,名称为 ServiceDll,值为 DLL 文件路径:C:\test\out.dll

image-20240411194744176.png

3. 修改函数入口点指令

找到样本的导出函数 ServiceMain 的入口点,修改汇编指令为 EB FE,这个指令的意思是跳转到当前位置,这样就会陷入无限循环,这个时候附加进程后再还原指令,就能从开始位置调试服务了。

注意,不能改成 CC,会导致后续启动服务时启动失败。

使用 PE 工具定位导出函数 ServiceMain 的入口点在文件中的偏移地址。

image-20240430111518050.png

修改后将样本文件存放到步骤 2 指定的目录 C:\test\out.dll

4. 启动服务

注意:先打开 procexp64.exe,为查看新启动的 svchost.exe 进程的 PID 做准备。

用管理员权限启动 cmd,执行 sc 命令启动服务:

image-20240430111955934.png

5. 用调试器附加 svchost

procexp64.exe 查看新创建的 svchost.exe 进程,鼠标指向该进程后,弹出的浮窗中会显示该进程加载的服务组名称,找到自己创建的 MyServiceGroup 对应的 svchost.exe 进程。

image-20240430112551081.png

管理员权限打开 x64dbg,附加进程 704,找到 out.dll 的加载基址。

image-20240430112948225.png

6. 修改入口点

根据内存地址和导出函数 ServiceMain 的 RVA ,计算出实际的 VA 是 7FEF303DD10,跳转到该地址。

在该地址下断点,之后再将 EB FE 修改为正确的指令 48 83 即可。

image-20240430113316846.png

即可开始正常调试样本了。

image-20240430113419812.png

请求C2:

image-20240430150522843.png

参考资料

《深入解析Windows操作系统第6版上册》第4章第2小节 服务

window服务调试(一):winodw服务运行原理

window服务调试(二):调试windows服务方法

使用Windbg&OllyDbg从头调试windows服务

[原创]记录自己调试windows服务的操作

简述:ServiceMain主函数的动态调试