看流星社区

 找回密码
 注册账号
查看: 2059|回复: 0

破译A盾禁止加载驱动失效之谜

[复制链接]

该用户从未签到

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

代码:
//禁止驱动加载
NTSTATUSDisEnableDriverLoading()
{
intbRet;

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)
{
returnSTATUS_UNSUCCESSFUL;
}
//计算reload后的地址,不然判断不对
ulNtLoadDriverBase=ulNtLoadDriverBase-SystemKernelModuleBase+ImageModuleBase;
ulZwSetSystemInformationBase=ulZwSetSystemInformationBase-SystemKernelModuleBase+ImageModuleBase;
ulReloadSeSinglePrivilegeCheck=ulSeSinglePrivilegeCheck-SystemKernelModuleBase+ImageModuleBase;

//hookreloadSeSinglePrivilegeCheck

bRet=HookFunctionByHeaderAddress(ulReloadSeSinglePrivilegeCheck,ulSeSinglePrivilegeCheck,SeSinglePrivilegeCheckHookZone,&SeSinglePrivilegeCheckPatchCodeLen,&SeSinglePrivilegeCheckRet);
if(bRet)
{
bRet=FALSE;
bRet=HookFunctionByHeaderAddress(
NewSeSinglePrivilegeCheck,
ulReloadSeSinglePrivilegeCheck,
SeSinglePrivilegeCheckHookZone,
&SeSinglePrivilegeCheckPatchCodeLen,
&SeSinglePrivilegeCheckRet
);
if(bRet)
{
SeSinglePrivilegeCheckHooked=TRUE;
//DbgPrint("hookSeSinglePrivilegeChecksuccess\n");
}
}
returnSTATUS_SUCCESS;
}

//权限检查的时候返回失败来达到禁止加载驱动
BOOLEAN__stdcallNewSeSinglePrivilegeCheck(
__inLUIDPrivilegeValue,
__inKPROCESSOR_MODEPreviousMode
)
{
ULONGulPage;

if(!bIsInitSuccess)
goto_FunctionRet;

//取返回地址
_asm
{
moveax,dwordptr[ebp+4]
movulPage,eax
}
//KdPrint(("ulPage:%08x\r\n",ulPage));
//RPsGetCurrentProcess=ReLoadNtosCALL(L"PsGetCurrentProcess",SystemKernelModuleBase,ImageModuleBase);
if(!RPsGetCurrentProcess)
{
goto_FunctionRet;
}
if(RPsGetCurrentProcess()==ProtectEProcess)
{
goto_FunctionRet;
}

if(ulPage>=ulNtLoadDriverBase&amp;&amp;ulPage<=ulNtLoadDriverBase+ulNtLoadDriverSize)
returnFALSE;

if(ulPage>=ulZwSetSystemInformationBase&amp;&amp;ulPage<=ulZwSetSystemInformationBase+ulZwSetSystemInformationSize)
returnFALSE;

_FunctionRet:
OldSeSinglePrivilegeCheck=(SeSinglePrivilegeCheck_1)SeSinglePrivilegeCheckHookZone;
returnOldSeSinglePrivilegeCheck(
PrivilegeValue,
PreviousMode
);
}
[/code]

然而,我发现他的禁止驱动加载的模块在xp下无效,在win7也同样无效。为什么呢?

我们先找一份加载驱动的代码,既然它说驱动是通过ZwLoadDriver来加载的,那么我们直接找一份这样的代码来测试一下:

代码:
#include<windows.h>
#include<stdio.h>

typedefstruct_LSA_UNICODE_STRING{
USHORTLength;
USHORTMaximumLength;
PVOIDBuffer;
}LSA_UNICODE_STRING,*PLSA_UNICODE_STRING;

typedefLSA_UNICODE_STRINGUNICODE_STRING,*PUNICODE_STRING;

//申明ntdll中使用的函数
typedefDWORD(CALLBACK*RTLANSISTRINGTOUNICODESTRING)(PVOID,PVOID,DWORD);
RTLANSISTRINGTOUNICODESTRINGRtlAnsiStringToUnicodeString;
typedefDWORD(CALLBACK*RTLFREEUNICODESTRING)(PVOID);
RTLFREEUNICODESTRINGRtlFreeUnicodeString;
typedefDWORD(CALLBACK*ZWLOADDRIVER)(PVOID);
ZWLOADDRIVERZwLoadDriver;

intLoadDriver(char*szDrvName,char*szDrvPath)
{
//修改注册表启动驱动程序
charszSubKey[200],szDrvFullPath[256];
LSA_UNICODE_STRINGbuf1;
LSA_UNICODE_STRINGbuf2;
intiBuffLen;
HKEYhkResult;
charData[4];
DWORDdwOK;
iBuffLen=sprintf(szSubKey,"System//CurrentControlSet//Services//%s",szDrvName);
szSubKey[iBuffLen]=0;
dwOK=RegCreateKey(HKEY_LOCAL_MACHINE,szSubKey,&amp;hkResult);
if(dwOK!=ERROR_SUCCESS)
returnfalse;
Data[0]=1;
Data[1]=0;
Data[2]=0;
Data[3]=0;
dwOK=RegSetValueEx(hkResult,"Type",0,4,(constunsignedchar*)Data,4);
dwOK=RegSetValueEx(hkResult,"ErrorControl",0,4,(constunsignedchar*)Data,4);
dwOK=RegSetValueEx(hkResult,"Start",0,4,(constunsignedchar*)Data,4);
GetFullPathName(szDrvPath,256,szDrvFullPath,NULL);
printf("Loadingdriver:%s/r/n",szDrvFullPath);
iBuffLen=sprintf(szSubKey,"//??//%s",szDrvFullPath);
szSubKey[iBuffLen]=0;
dwOK=RegSetValueEx(hkResult,"ImagePath",0,1,(constunsignedchar*)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(&amp;buf1,&amp;buf2,1);
//加载驱动程序
dwOK=ZwLoadDriver(&amp;buf1);
RtlFreeUnicodeString(&amp;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;
returntrue;
}

intmain(intargc,char*argv[])
{
printf("LoaddriverwithZwLoadDriver()/r/n");
printf("Date:8thMay2007/r/n");
printf("Modifedby:GaRY<wofeiwo_at_gmail_dot_com>/r/n/r/n");
if(argc!=3)
{
printf("Usage:%s<DriverFilename><DriverPath>/r/n",argv[0]);
exit(-1);
}
HMODULEhNtdll=NULL;
hNtdll=LoadLibrary("ntdll.dll");

//从ntdll.dll里获取函数
if(!hNtdll)
{
printf("LoadLibrary(NTDLL.DLL)Error:%d/n",GetLastError());
returnfalse;
}

RtlAnsiStringToUnicodeString=(RTLANSISTRINGTOUNICODESTRING)
GetProcAddress(hNtdll,"RtlAnsiStringToUnicodeString");
RtlFreeUnicodeString=(RTLFREEUNICODESTRING)
GetProcAddress(hNtdll,"RtlFreeUnicodeString");
ZwLoadDriver=(ZWLOADDRIVER)
GetProcAddress(hNtdll,"ZwLoadDriver");

//注册驱动程序
if(LoadDriver(argv[1],argv[2])==false)returnfalse;
returntrue;
}
[/code]

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

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

代码:
nt!NtLoadDriver:
83df63296a48push48h
83df632b68a860cb83pushoffsetnt!??::FNODOBFM::`string'+0x8ca8(83cb60a8)
83df6330e8f3eaecffcallnt!_SEH_prolog4(83cc4e28)
83df633533dbxorebx,ebx
83df6337895de4movdwordptr[ebp-1Ch],ebx
83df633a648b3524010000movesi,dwordptrfs:[124h]
83df63418a863a010000moval,byteptr[esi+13Ah]
83df63478845e0movbyteptr[ebp-20h],al
83df634a3ac3cmpal,bl
83df634c0f840b010000jent!NtLoadDriver+0x133(83df645d)
83df6352ff75e0pushdwordptr[ebp-20h]
83df6355ff3544f6fb83pushdwordptr[nt!SeLoadDriverPrivilege+0x4(83fbf644)]
83df635bff3540f6fb83pushdwordptr[nt!SeLoadDriverPrivilege(83fbf640)]
83df6361e8b8480a00callnt!SeSinglePrivilegeCheck(83e9ac1e)
83df636684c0testal,al
83df6368750ajnent!NtLoadDriver+0x4b(83df6374)
83df636ab8610000c0moveax,0C0000061h
83df636fe958010000jmpnt!NtLoadDriver+0x1a2(83df64cc)
83df6374895dfcmovdwordptr[ebp-4],ebx
83df63778b4508moveax,dwordptr[ebp+8]
83df637a8b0d20d7db83movecx,dwordptr[nt!MmUserProbeAddress(83dbd720)]
83df63803bc1cmpeax,ecx
83df63827202jbnt!NtLoadDriver+0x5d(83df6386)
83df63848bc1moveax,ecx
83df63868b08movecx,dwordptr[eax]
83df6388894dd4movdwordptr[ebp-2Ch],ecx
83df638b8b4804movecx,dwordptr[eax+4]
83df638e894dd8movdwordptr[ebp-28h],ecx
83df639166395dd4cmpwordptr[ebp-2Ch],bx
83df63957511jnent!NtLoadDriver+0x7f(83df63a8)
83df6397c745fcfeffffffmovdwordptr[ebp-4],0FFFFFFFEh
83df639eb80d0000c0moveax,0C000000Dh
83df63a3e924010000jmpnt!NtLoadDriver+0x1a2(83df64cc)
[/code]

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

代码:
0:kd>uZwLoadDriver
nt!ZwLoadDriver:
83c9589cb89b000000moveax,9Bh
83c958a18d542404leaedx,[esp+4]
83c958a59cpushfd
83c958a66a08push8
83c958a8e8911c0000callnt!KiSystemService(83c9753e)
83c958adc20400ret4
nt!ZwLoadKey:
83c958b0b89c000000moveax,9Ch
83c958b58d542404leaedx,[esp+4]
[/code]

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


至于通过ZwSetSystemInformation来加载驱动的例子暂时没找到,如果在这一方案上有问题,估计也可能是对比的地址不合适所致。这里就不测试了


后记:不知什么时候开始,A盾的官网就上不去了?而且网上能看到关于这个软件的帖子都是2012~2013年的了,也不知这个bug后来的版本得到修正没有!挺有教育意义的一个工具就这样悄无声息,消声匿迹了???
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-3-19 10:35

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表