欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]破译A盾禁止加载驱动失效之谜

[原创]破译A盾禁止加载驱动失效之谜

离职在家有一段日子了,闲来无事,看下了A盾的代码(版本为0.2.5),从资料中看到他禁止驱动加载的逻辑是:
系统加载驱动要调用ZwLoadDriver或者ZwSetSystemInformation函数来实现。而这两个函数又都必调用SeSinglePrivilegeCheck来检查权限,所以A盾利用inline hook技术hook了SeSinglePrivilegeCheck函数,当指令流执行到此处时,通过[ebp+4]得到调用函数的返回地址,这个返回地址当然位于发起调用的函数体内,对比一下这个地址是不是在ZwLoadDriver或者ZwSetWindowsInformation函数体中,由此判断是不是系统在加载驱动。如果是在加载驱动返回false即可禁止,否则调用原来的SeSinglePrivilegeCheck函数。
相关的代码如下:

//禁止驱动加载

NTSTATUS DisEnableDriverLoading()

{

	int bRet;

	ulZwSetSystemInformationBase = GetSystemRoutineAddress(1,L"ZwSetSystemInformation");

	ulNtLoadDriverBase = GetSystemRoutineAddress(1,L"ZwLoadDriver");

	if (ulNtLoadDriverBase &&

		ulZwSetSystemInformationBase)

	{

		ulNtLoadDriverSize = SizeOfProc(ulNtLoadDriverBase);

		ulZwSetSystemInformationSize = SizeOfProc(ulZwSetSystemInformationBase);

	}

	ulSeSinglePrivilegeCheck = GetSystemRoutineAddress(1,L"SeSinglePrivilegeCheck");

	if (!ulSeSinglePrivilegeCheck ||

		!ulNtLoadDriverBase ||

		!ulZwSetSystemInformationBase)

	{

		return STATUS_UNSUCCESSFUL;

	}

	//计算reload后的地址,不然判断不对

	ulNtLoadDriverBase = ulNtLoadDriverBase - SystemKernelModuleBase+ImageModuleBase;

	ulZwSetSystemInformationBase = ulZwSetSystemInformationBase - SystemKernelModuleBase+ImageModuleBase;

	ulReloadSeSinglePrivilegeCheck = ulSeSinglePrivilegeCheck - SystemKernelModuleBase+ImageModuleBase;

	//hook reload SeSinglePrivilegeCheck

	bRet = HookFunctionByHeaderAddress(ulReloadSeSinglePrivilegeCheck,ulSeSinglePrivilegeCheck,SeSinglePrivilegeCheckHookZone,&SeSinglePrivilegeCheckPatchCodeLen,&SeSinglePrivilegeCheckRet);

	if(bRet)

	{

		bRet = FALSE;

		bRet = HookFunctionByHeaderAddress(

			NewSeSinglePrivilegeCheck,

			ulReloadSeSinglePrivilegeCheck,

			SeSinglePrivilegeCheckHookZone,

			&SeSinglePrivilegeCheckPatchCodeLen,

			&SeSinglePrivilegeCheckRet

			);

		if (bRet)

		{

			SeSinglePrivilegeCheckHooked = TRUE;

			//DbgPrint("hook SeSinglePrivilegeCheck success\n");

		}

	}

	return STATUS_SUCCESS;

}

//权限检查的时候返回失败来达到禁止加载驱动

BOOLEAN __stdcall NewSeSinglePrivilegeCheck(

	__in  LUID PrivilegeValue,

	__in  KPROCESSOR_MODE PreviousMode

	)

{

	ULONG ulPage;

	if (!bIsInitSuccess)

		goto _FunctionRet;

	//取返回地址

	[COLOR="Red"]_asm

	{

		mov eax,dword ptr[ebp+4]

		mov ulPage,eax

	}[/COLOR]

	//KdPrint(("ulPage:%08x\r\n",ulPage));

	//RPsGetCurrentProcess = ReLoadNtosCALL(L"PsGetCurrentProcess",SystemKernelModuleBase,ImageModuleBase);

	if (!RPsGetCurrentProcess)

	{

		goto _FunctionRet;

	}

	if (RPsGetCurrentProcess() == ProtectEProcess)

	{

		goto _FunctionRet;

	}

	[COLOR="red"]if (ulPage >= ulNtLoadDriverBase && ulPage <= ulNtLoadDriverBase+ulNtLoadDriverSize)

		return FALSE;[/COLOR]

	if (ulPage >= ulZwSetSystemInformationBase && ulPage <= ulZwSetSystemInformationBase+ulZwSetSystemInformationSize)

		return FALSE;

_FunctionRet:

	OldSeSinglePrivilegeCheck = (SeSinglePrivilegeCheck_1)SeSinglePrivilegeCheckHookZone;

	return OldSeSinglePrivilegeCheck(

		PrivilegeValue,

		PreviousMode

		);

}


然而,我发现他的禁止驱动加载的模块在xp下无效,在win7也同样无效。为什么呢?
我们先找一份加载驱动的代码,既然它说驱动是通过ZwLoadDriver来加载的,那么我们直接找一份这样的代码来测试一下:
#include <windows.h>

#include <stdio.h>

typedef struct _LSA_UNICODE_STRING {

    USHORT Length;

    USHORT MaximumLength;

    PVOID Buffer;

} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING; 

typedef LSA_UNICODE_STRING UNICODE_STRING, *PUNICODE_STRING;

// 申明ntdll中使用的函数

typedef DWORD (CALLBACK* RTLANSISTRINGTOUNICODESTRING)(PVOID, PVOID,DWORD);

RTLANSISTRINGTOUNICODESTRING RtlAnsiStringToUnicodeString;

typedef DWORD (CALLBACK* RTLFREEUNICODESTRING)(PVOID);

RTLFREEUNICODESTRING RtlFreeUnicodeString;

typedef DWORD (CALLBACK* ZWLOADDRIVER)(PVOID);

ZWLOADDRIVER ZwLoadDriver;

int LoadDriver(char * szDrvName, char * szDrvPath)

{

    //修改注册表启动驱动程序

    char szSubKey[200], szDrvFullPath[256];

    LSA_UNICODE_STRING buf1;

    LSA_UNICODE_STRING buf2;

    int iBuffLen;

    HKEY hkResult;

    char Data[4];

    DWORD dwOK;

    iBuffLen = sprintf(szSubKey,"System//CurrentControlSet//Services//%s",szDrvName);

    szSubKey[iBuffLen]=0;

    dwOK = RegCreateKey(HKEY_LOCAL_MACHINE,szSubKey,&hkResult);

    if(dwOK!=ERROR_SUCCESS)

        return false;

    Data[0]=1;

    Data[1]=0;

    Data[2]=0;

    Data[3]=0;

    dwOK=RegSetValueEx(hkResult,"Type",0,4,(const unsigned char *)Data,4);

    dwOK=RegSetValueEx(hkResult,"ErrorControl",0,4,(const unsigned char *)Data,4);

    dwOK=RegSetValueEx(hkResult,"Start",0,4,(const unsigned char *)Data,4);

    GetFullPathName(szDrvPath, 256, szDrvFullPath, NULL);   

    printf("Loading driver: %s/r/n", szDrvFullPath);

    iBuffLen = sprintf(szSubKey,"//??//%s",szDrvFullPath);

    szSubKey[iBuffLen]=0;

    dwOK=RegSetValueEx(hkResult,"ImagePath",0,1,(const unsigned char *)szSubKey,iBuffLen);

    RegCloseKey(hkResult); 

    iBuffLen = sprintf(szSubKey,"//Registry//Machine//System//CurrentControlSet//Services//%s",szDrvName);

    szSubKey[iBuffLen]=0;

    buf2.Buffer = (PVOID)szSubKey;

    buf2.Length = iBuffLen;

    RtlAnsiStringToUnicodeString(&buf1,&buf2,1);

    //加载驱动程序

    dwOK = ZwLoadDriver(&buf1);

    RtlFreeUnicodeString(&buf1);

    iBuffLen=sprintf(szSubKey,"%s%s//Enum","System//CurrentControlSet//Services//",szDrvName);

    szSubKey[iBuffLen]=0;

    //删除注册表项

    RegDeleteKey(HKEY_LOCAL_MACHINE,szSubKey);

    iBuffLen=sprintf(szSubKey,"%s%s//Security","System//CurrentControlSet//Services//",szDrvName);

    szSubKey[iBuffLen]=0;

    RegDeleteKey(HKEY_LOCAL_MACHINE,szSubKey);

    iBuffLen=sprintf(szSubKey,"%s%s","System//CurrentControlSet//Services//",szDrvName);

    szSubKey[iBuffLen]=0;

    RegDeleteKey(HKEY_LOCAL_MACHINE,szSubKey);

    iBuffLen=sprintf(szSubKey,"////.//%s",szDrvName);

    szSubKey[iBuffLen]=0;

    return true;

}

int main(int argc, char *argv[])

{

    printf("Load driver with ZwLoadDriver( )/r/n");

    printf("Date: 8th May 2007/r/n");

    printf("Modifed by: GaRY <wofeiwo_at_gmail_dot_com>/r/n/r/n");

    if(argc != 3)

    {

        printf("Usage: %s <DriverFilename> <DriverPath>/r/n", argv[0]);

        exit(-1);

    }

    HMODULE hNtdll = NULL;

    hNtdll = LoadLibrary( "ntdll.dll" ); 

    //从ntdll.dll里获取函数

    if ( !hNtdll )

    {

        printf( "LoadLibrary( NTDLL.DLL ) Error:%d/n", GetLastError() );

        return false;

    }

    RtlAnsiStringToUnicodeString = (RTLANSISTRINGTOUNICODESTRING)

        GetProcAddress( hNtdll, "RtlAnsiStringToUnicodeString");

    RtlFreeUnicodeString = (RTLFREEUNICODESTRING)

        GetProcAddress( hNtdll, "RtlFreeUnicodeString");

    ZwLoadDriver = (ZWLOADDRIVER)

        GetProcAddress( hNtdll, "ZwLoadDriver");

    //注册驱动程序

    if(LoadDriver(argv[1], argv[2]) == false) return false;

    return true;

} 


通过调试发现在进入SSDT之前,传入eax的SSDT的索引号为0x61,而在win7这个索引号是0x9b。
如下所示:

进入到内核层时,对应的是NtLoadDriver,如下所示,其中红色的正是被hook的目标函数:
nt!NtLoadDriver:

83df6329 6a48            push    48h

83df632b 68a860cb83      push    offset nt! ?? ::FNODOBFM::`string'+0x8ca8 (83cb60a8)

83df6330 e8f3eaecff      call    nt!_SEH_prolog4 (83cc4e28)

83df6335 33db            xor     ebx,ebx

83df6337 895de4          mov     dword ptr [ebp-1Ch],ebx

83df633a 648b3524010000  mov     esi,dword ptr fs:[124h]

83df6341 8a863a010000    mov     al,byte ptr [esi+13Ah]

83df6347 8845e0          mov     byte ptr [ebp-20h],al

83df634a 3ac3            cmp     al,bl

83df634c 0f840b010000    je      nt!NtLoadDriver+0x133 (83df645d)

83df6352 ff75e0          push    dword ptr [ebp-20h]

83df6355 ff3544f6fb83    push    dword ptr [nt!SeLoadDriverPrivilege+0x4 (83fbf644)]

83df635b ff3540f6fb83    push    dword ptr [nt!SeLoadDriverPrivilege (83fbf640)]

[COLOR="Red"]83df6361 e8b8480a00      call    nt!SeSinglePrivilegeCheck (83e9ac1e)[/COLOR]

83df6366 84c0            test    al,al

83df6368 750a            jne     nt!NtLoadDriver+0x4b (83df6374)

83df636a b8610000c0      mov     eax,0C0000061h

83df636f e958010000      jmp     nt!NtLoadDriver+0x1a2 (83df64cc)

83df6374 895dfc          mov     dword ptr [ebp-4],ebx

83df6377 8b4508          mov     eax,dword ptr [ebp+8]

83df637a 8b0d20d7db83    mov     ecx,dword ptr [nt!MmUserProbeAddress (83dbd720)]

83df6380 3bc1            cmp     eax,ecx

83df6382 7202            jb      nt!NtLoadDriver+0x5d (83df6386)

83df6384 8bc1            mov     eax,ecx

83df6386 8b08            mov     ecx,dword ptr [eax]

83df6388 894dd4          mov     dword ptr [ebp-2Ch],ecx

83df638b 8b4804          mov     ecx,dword ptr [eax+4]

83df638e 894dd8          mov     dword ptr [ebp-28h],ecx

83df6391 66395dd4        cmp     word ptr [ebp-2Ch],bx

83df6395 7511            jne     nt!NtLoadDriver+0x7f (83df63a8)

83df6397 c745fcfeffffff  mov     dword ptr [ebp-4],0FFFFFFFEh

83df639e b80d0000c0      mov     eax,0C000000Dh

83df63a3 e924010000      jmp     nt!NtLoadDriver+0x1a2 (83df64cc)

而A盾中却是通过对比ZwLoadDriver(注意此函数的开头为"Zw")的代码区域来实现禁止驱动加载。那ZwLoadDriver在内核中又是怎么回事呢?通过windbg查看一下:
0: kd> u ZwLoadDriver

nt!ZwLoadDriver:

83c9589c b89b000000      mov     eax,9Bh

83c958a1 8d542404        lea     edx,[esp+4]

83c958a5 9c              pushfd

83c958a6 6a08            push    8

83c958a8 e8911c0000      call    nt!KiSystemService (83c9753e)

83c958ad c20400          ret     4

nt!ZwLoadKey:

83c958b0 b89c000000      mov     eax,9Ch

83c958b5 8d542404        lea     edx,[esp+4]


看来只是简单地处理一下就交给系统去处理了。真正实现加载驱动的代码并不在这里。即这里并没有直接调用被hook的函数SeSinglePrivilegeCheck ,所以在调用SeSinglePrivilegeCheck时 取得的返回地址,是不可能在ZwLoadDriver函数体内的,这也就是在判断返回地址是否在ZwLoadDriver时永远为不满足的原因。
到此就明白了为什么禁止驱动加载失效了。
总结一下:A盾通过hook SeSinglePrivilegeCheck 来得到调用者的返回地址,按逆向出来的结果看,应该是比较这个返回地址在不在NtLoadDriver函数体中,而不是判断在不在ZwLoadDriver中。
通过把里面相关的代码捉出来,笔者实现了一个禁止加载驱动的代码,也许是因为NtLoadDriver没导出,所以我是通过ssdt指定的偏移得到NtLoadDriver的地址。代码上传在下面,兼容xp,win7平台。相信其他平台(非64 位)改一下偏移值应该也能跑起来。
附上成功禁止加载驱动的图:

至于通过ZwSetSystemInformation来加载驱动的例子暂时没找到,如果在这一方案上有问题,估计也可能是对比的地址不合适所致。这里就不测试了
后记:不知什么时候开始,A盾的官网就上不去了?而且网上能看到关于这个软件的帖子都是2012~2013年的了,也不知这个bug后来的版本得到修正没有!挺有教育意义的一个工具就这样悄无声息,消声匿迹了???

上传的附件 9b.png
QQ截图20150921104114.png
disloaddrv.zip

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功