一、如何遍历驱动
在内核中,可以通过大概下面几种方法获取,
0x1. 遍历驱动对象(Driver_Object)的DriverSection域
0x2. 遍历KPCR结构体的PsLoadedModuleList域
0x3. ZwQuerySystemInformation的11号功能
0x4. 内存暴力搜索
在应用层中,可以通过psapi的EnumDeviceDrivers API来获取,其关键是调用ZwQuerySystemInformation。
二、Anti原理
平时在用OD调试的时候,肯定会用到很多OD的插件。可以发现,这些插件都是dll文件,然后被OD加载调用。那既然OD加载了这么多插件,为何不直接遍历所有进程模块,如果发现有可疑的模块(例如:stringOD模块),那就可以直接判断调试器正在运行。但是,OD的进程被SOD隐藏了,如果不借助ARK工具,在任务管理器里面是看不到的。图1是XueTr观察到的。
但是可以遍历系统驱动模块,因为驱动模块是没有隐藏的,所以可以通过程序遍历得到。如图2所示。但是,要排除掉系统的dbghelp.dll的干扰。
可见,只要将驱动路径向上2个目录,来到OllyDbg目录,判断是否存在dbghelp.dll,即可确定是否有调试器的存在。如图3所示。
三、代码
1: #include "stdafx.h"
2:
3: #include <stdio.h>
4: #include <windows.h>
5: #include <Psapi.h>
6: #include <shlwapi.h> //PathFileExists
7: #pragma comment(lib, "psapi.lib")
8: #pragma comment(lib, "shlwapi.lib")
9: #define ARRAY_SIZE 1024
10:
11: int main(int argc, char *argv[])
12: {
13: DWORD cbNeeded = 0;
14: LPVOID drivers[ARRAY_SIZE] = {0};
15: int cDrivers = 0, i = 0;
16: if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) &&
17: cbNeeded < sizeof(drivers))
18: {
19: char szDriver[ARRAY_SIZE] = {0};
20: char szPath[ARRAY_SIZE] = {0};
21: char szDbgHelp[ARRAY_SIZE] = {0};
22: char szSystemPath[ARRAY_SIZE] = {0};
23: cDrivers = cbNeeded / sizeof(LPVOID);
24: bool bDetect = FALSE;
25: //得到C:\Windows\system32\dbghelp.dll
26: GetSystemDirectory(szSystemPath, sizeof(szSystemPath));
27: strcat_s(szSystemPath, "\\dbghelp.dll");
28: //printf("There are %d drivers\n", cDrivers);
29: for (i = 0; i < cDrivers; i++)
30: {
31: // 驱动名
32: if (GetDeviceDriverBaseName(drivers[i], szDriver, sizeof(szDriver) / sizeof(LPVOID)))
33: {
34: printf("%d:%s\n", i+1, szDriver);
35: // 驱动完整路径
36: GetDeviceDriverFileName(drivers[i], szPath, sizeof(szPath));
37: //只判断非系统驱动
38: if (szPath[1] == ‘?‘)
39: {
40: int len = strlen(szPath);
41: //printf("%d:%s\n", i+1, szPath);
42: // 得到驱动上一级目录
43: do
44: {
45: len--;
46: } while (szPath[len] != ‘\\‘);
47: // 得到驱动上 上一级目录
48: do
49: {
50: len--;
51: } while (szPath[len] != ‘\\‘);
52: szPath[len + 1] = ‘\0‘; // 字符串截断
53: // 去除驱动路径的前4个字符"\??\"
54: for (int j = 0; j < len; j++)
55: {
56: szPath[j] = szPath[j + 4];
57: }
58: sprintf_s(szDbgHelp, "%sdbghelp.dll", szPath);
59: // 判断文件是否存在
60: if (PathFileExists(szDbgHelp))
61: {
62: // 排除系统的dbghelp.dll
63: if (_strcmpi(szSystemPath, szDbgHelp) != 0)
64: {
65: bDetect = TRUE;
66: break;
67: }
68: else
69: {
70: bDetect = FALSE;
71: }
72: }
73: }
74: }
75: }
76: if (bDetect)
77: {
78: printf_s("Detect OD\n");
79: printf_s("Path: %s\n", szPath);
80: printf_s("SOD Name: %s\n", szDriver);
81: }
82: else
83: {
84: printf_s("Do not Detect OD\n");
85: }
86: getchar();
87: }
88: }
四、效果
在XP和32位的win7下面,驱动是不需要签名的,载入OD即可加载驱动。
五、反调试之策
可以通过断链隐藏SOD的驱动名,一般是fengyue0.sys
隐藏代码:
1: VOID HideDriver(PDRIVER_OBJECT pDriverObject)
2: {
3: PLDR_DATA_TABLE_ENTRY pLdrData = NULL;
4: PLIST_ENTRY pCur, pHead = NULL;
5: UNICODE_STRING uDriverName;
6:
7: // 初始化要隐藏的驱动名
8: RtlInitUnicodeString(&uDriverName, L"fengyue0.sys");
9:
10: pLdrData = (PLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
11:
12: // 得到当前sys模块,并设置为第一个(head)以及当前(cur)模块
13: pCur = pHead = pLdrData->InLoadOrderLinks.Flink;
14:
15: __try
16: {
17:
18: do
19: {
20: // 这句pLdrData = (PLDR_DATA_TABLE_ENTRY)pCur是等价的
21: // 因为pCur在LDR_DATA_TABLE_ENTRY结构体的第一个域(成员)
22: pLdrData = CONTAINING_RECORD(pCur, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
23: if (pLdrData->BaseDllName.Length > 0 &&
24: pLdrData->BaseDllName.Buffer != NULL)
25: {
26: if (RtlCompareUnicodeString(&uDriverName, &(pLdrData->BaseDllName), FALSE) == 0)
27: {
28: KdPrint(("驱动隐藏\n"));
29: // 断链
30: pLdrData->InLoadOrderLinks.Blink->Flink =
31: pLdrData->InLoadOrderLinks.Flink;
32:
33: pLdrData->InLoadOrderLinks.Flink->Blink =
34: pLdrData->InLoadOrderLinks.Blink;
35:
36: // 断掉的链指向自己
37: pLdrData->InLoadOrderLinks.Flink =
38: (PLIST_ENTRY)&pLdrData->InLoadOrderLinks.Flink;
39:
40: pLdrData->InLoadOrderLinks.Blink =
41: (PLIST_ENTRY)&pLdrData->InLoadOrderLinks.Flink;
42:
43: KdPrint(("PsLoadedModuleList success \r\n"));
44: break;
45: }
46: }
47:
48: pCur = pCur->Flink;
49: } while (pCur != pHead);
50: }
51:
52: __except(EXCEPTION_EXECUTE_HANDLER)
53: {
54: KdPrint(("PsLoadedModuleList Error \r\n"));
55: }
驱动载入后,再次运行之前的anti程序,就找不到驱动名了,反调试已经没用了。如图5。
六、总结
这种anti手法,局限性比较低,因为StrongOD的驱动没有进行隐藏。反调试手段很多,大家可以随意发挥,只要能检测到。