在 Win64 上实现Ring3 级 HIPS

 

自从更换了显卡,每天必不可少的事情就是玩游戏。不过某些汉化版的游戏实在太不像
话,在安装游戏的同时,竟然还把我的IE主页给修改了!由于Win64上没有可用的HIPS (不
喜欢杀毒软件),而且游戏安装程序基本上都是32位游戏,所以我赶紧拿出以前写的Win32
Ring3 HIPS来防止游戏安装时乱改我的IE 主页。结果呢?在玩《Crysis》64位的时候,竟
然又被修改了IE 主页!经测试发现,《Crysis》64位的 EXE 文件被修改过,每次运行都会
先修改 IE主页,再执行游戏。由于是64位 EXE,原先的 Win32 Ring3 HIPS肯定是没有用
了,只好写一个Win64 Ring3 HIPS。
首先说说修改IE 主页是怎么一回事儿。IE 主页不是记录在一个ini文件里的,而是记
录在注册表里,它的具体位置是:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\MAIN\Start Page
修改这个键值就能改变IE主页。由于大部分人都是使用 IE,而如果一打开 IE就自动
连接到某个网站,就能给那个网站带来巨大的流量,那个网站就能获取巨额的广告利益。所
以这也就是为什么大多数游戏软件都喜欢乱改IE主页的原因。不过要利用我的电脑给它赚
钱,自然是痴心妄想,于是便有了下文。
知彼知己,百战不殆。我们写一个64 位程序,来实现修改IE主页:

  
#include <stdio.h>
#include <Windows.h>
#define OBJ_CASE_INSENSITIVE 0x00000040L
typedef struct _UNICODE_STRING
{
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
    ULONG           Length;
    HANDLE          RootDirectory;
    PUNICODE_STRING ObjectName;

    ULONG           Attributes;
    PVOID           SecurityDescriptor;
    PVOID           SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
typedef NTSTATUS (WINAPI *ZWOPENKEY)
(
    __out  PHANDLE KeyHandle,
    __in   ACCESS_MASK DesiredAccess,
    __in   POBJECT_ATTRIBUTES ObjectAttributes
);
typedef NTSTATUS (WINAPI *ZWSETVALUEKEY)
(
    __in      HANDLE KeyHandle,
    __in      PUNICODE_STRING ValueName,
    __in_opt  ULONG TitleIndex,
    __in      ULONG Type,
    __in_opt  PVOID Data,
    __in      ULONG DataSize
);
typedef NTSTATUS (WINAPI *ZWCLOSE)
(  
    __in  HANDLE Handle
);
typedef VOID (WINAPI *RTLINITUNICODESTRING)
(
    __inout   PUNICODE_STRING DestinationString,
    __in_opt  PCWSTR SourceString
);
RTLINITUNICODESTRING RtlInitUnicodeString;
ZWSETVALUEKEY ZwSetValueKey;
ZWOPENKEY ZwOpenKey;
ZWCLOSE ZwClose;
//设置 ObjectAttributes的值
VOID InitializeObjectAttributes (OUT POBJECT_ATTRIBUTES
InitializedAttributes, IN PUNICODE_STRING ObjectName, IN ULONGAttributes, IN
HANDLE RootDirectory, IN PSECURITY_DESCRIPTOR SecurityDescriptor)
{
    InitializedAttributes->Length = sizeof( OBJECT_ATTRIBUTES );
    InitializedAttributes->RootDirectory = RootDirectory;    

    InitializedAttributes->Attributes = Attributes;
    InitializedAttributes->ObjectName = ObjectName;
InitializedAttributes->SecurityDescriptor = SecurityDescriptor;
    InitializedAttributes->SecurityQualityOfService = NULL;
    return;
}
//初始化用到的api
void InitApi()
{
//获得 ZwSetValueKey的函数指针

ZwSetValueKey=(ZWSETVALUEKEY)GetProcAddress(LoadLibraryW(L"ntdll.dll"),"ZwS
etValueKey");    
//获得 ZwOpenKey的函数指针

ZwOpenKey=(ZWOPENKEY)GetProcAddress(LoadLibraryW(L"ntdll.dll"),"ZwOpenKey")
;
//获得 ZwClose的函数指针
    ZwClose=(ZWCLOSE)GetProcAddress(LoadLibraryW(L"ntdll.dll"),"ZwClose");
RtlInitUnicodeString=(RTLINITUNICODESTRING)GetProcAddress(LoadLibraryW(L"nt
dll.dll"),"RtlInitUnicodeString");
    printf("ZwSetValueKey: %llx\n",(LONGLONG)ZwSetValueKey);
    printf("ZwOpenKey: %llx\n",(LONGLONG)ZwOpenKey);
    printf("ZwClose: %llx\n",(LONGLONG)ZwClose);
}
int main()
{
    printf("My PID: %ld\n",GetCurrentProcessId());
    InitApi();
    UNICODE_STRING path;//定义注册表路径字符串
    UNICODE_STRING name;//定义名称字符串
    UNICODE_STRING data;//定义数据字符串
    OBJECT_ATTRIBUTES oa;//定义操作对象
HANDLE myhandle=NULL;//定义返回句柄
// 初始化操作对象和初始化注册表路径字符串
InitializeObjectAttributes(&oa,&path,OBJ_CASE_INSENSITIVE,NULL,NULL);RtlIni
tUnicodeString(&path,L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Internet
Explorer\\MAIN");
//初始化名称字符串(其实是Start Page,这里只是为了方便测试)
    RtlInitUnicodeString(&name,L"Start_Page");    
//初始化数据字符串
RtlInitUnicodeString(&data,L"www.jybase.net");
//打开注册表
    ZwOpenKey(
                &myhandle,//返回句柄
                KEY_WRITE,//权限
                &oa//操作对象
                );
    //设置注册表键值
NTSTATUS st=ZwSetValueKey(
                myhandle,//当前句柄
                &name,//键名
                0,
                REG_SZ,//方式
                data.Buffer,//字符串缓冲
                data.Length//字符串长度
                );
    printf("[ZwSetValueKey] returned: 0x%x\n", st);
//关闭注册表
    ZwClose(myhandle);
    printf("Press [ENTER] to continue...\n");
    getchar();
    return 0;
}

这里顺便说一句,64位系统里绝大多数函数都是__fastcall约定,但是我在定义函数
原型的时候还是使用了__stdcall(WINAPI是__stdcall的马甲)约定,难道不会出错吗?
我带着这个疑问去问了“毛豆”的工程师,他的回答是,VS2010非常智能,在你编译的时
候自动把__stdcall替换成__fastcall了。如果大家对这个回答感到很郁闷,请不要怪我。
进入主题,如何反修改IE主页。我的思路是:Hook ZwSetValueKey。首先根据
ZwSetValueKey的第一个参数KeyHandle得到它的KeyName。若KeyName是
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\MAIN,就检查ValueName,
否则放过;如果ValueName是 Start Page,就弹出主动防御提示框。否则放过。
根据 KeyHandle得到 KeyName在网上是没有现成代码的。经研究,可以使用ZwQueryKey
得到。不过ZwQueryKey得到的还不是可见的字符串,还要经过字符串的转化处理。实现代
码如下:

int WideToMultiByte(const WCHAR* wChar, char* mChar)
{
    int aLen = WideCharToMultiByte(CP_ACP, 0, wChar, wcslen(wChar) + 1, NULL,
0, NULL, NULL);
    return WideCharToMultiByte(CP_ACP, 0, wChar, wcslen(wChar) + 1, mChar,
aLen, NULL, NULL);
}
char *KeyHandleToKeyName(HANDLE KeyHandle)

{
    ULONG len = 0;
    wchar_t *wszText=(wchar_t *)malloc(1024);
    char *pszKeyName=(char *)malloc(2048);
    memset(wszText,0,1024);
    //KeyNameInformation=3
    ZwQueryKey(KeyHandle, 3, wszText, 1024, &len);
    WideToMultiByte(wszText+2, pszKeyName);
    return pszKeyName;
}

接下来到核心伪函数的处理部分。首先拿出我以前写好的Win64 Hook模板(已经做好
了挂钩引擎、Ring3 全局钩子和反动态调用API绕过钩子,具体代码请参考本人往期文章),
加上一个 Fake_ZwSetValueKey即可:

long Fake_ZwSetValueKey
(
    __in      HANDLE KeyHandle,
    __in      PUNICODE_STRING ValueName,
    __in_opt  ULONG TitleIndex,
    __in      ULONG Type,
    __in_opt  PVOID Data,
    __in      ULONG DataSize
)
{
    long (WINAPI
*pZwSetValueKey)(HANDLE,PUNICODE_STRING,ULONG,ULONG,PVOID,ULONG);
pZwSetValueKey=(long (WINAPI
*)(HANDLE,PUNICODE_STRING,ULONG,ULONG,PVOID,ULONG))GetOriginalFunction(

ONG_PTR)Fake_ZwSetValueKey );  
//如果不是IE\\MAIN下面的键值就忽略
if( stricmp(KeyHandleToKeyName(KeyHandle),"\\Registry\\Machine\\SOFTWARE\\
Microsoft\\Internet Explorer\\MAIN")!=0 )  
        return pZwSetValueKey(KeyHandle, ValueName, TitleIndex, Type, Data,
DataSize);
    //如果是[主页键]就警告,否则忽略
    if( wcsicmp(ValueName->Buffer,L"Start_Page")==0 ) //should be [Start Page]
    {
        int mbwr=MessageBoxA(0,cs(cs(" 进程 ID 为
",CStr(GetCurrentProcessId())),"的程序试图修改IE主页,是否允许?"),"HIPS警
告",4);
        if(mbwr!=7)
            return pZwSetValueKey(KeyHandle, ValueName, TitleIndex, Type,
Data, DataSize);
        else
            return 0xC0000022;
    }
    return pZwSetValueKey(KeyHandle, ValueName, TitleIndex, Type, Data,
DataSize);
}

在里面有一些自定义函数,如cs、CStr等,都是为了方便字符串操作的。虽然C++的
CString 都有类似的功能,但是我不喜欢CString,所以自己实现了:

//连接两个char*字符串
char *cs(char *str1, char *str2)
{
    long newstrlen=strlen(str1)+strlen(str2)+1;
    char *newstr=(char*)malloc(newstrlen);
    memcpy(newstr,str1,strlen(str1));
    memcpy(newstr+strlen(str1),str2,strlen(str2)+1);
    return newstr;
}
//把整数转化为字符串
char *CStr(long n)
{
    char *newstr=(char*)malloc(11);
    memset(newstr,0x0,11);
    ltoa(n,newstr,10);
    return newstr;
}
文章几句话带过的东西,在研究的时候可不是那么轻易得到的。特别要注意,在伪函数
中动态调用的 MessageBoxA 必须要用 LoadLibrary,否则在 CUI 程序中会崩溃。另外在
Fake_CreatePeocessW(实现 Ring3 全局钩子)中注入 DLL 前要检查启动的进程类型,如果
是 64 位进程才注入 dll,如果是 32 位进程则不要注入,否则会导致要启动的 32 位进程无
法启动,甚至导致自身程序崩溃。实现判断是否64位进程可以用IsWow64Process来实现:

BOOL IsWow64(HANDLE hProc)
{
    BOOL bIsWow64 = FALSE;
    IsWow64Process(hProc, &bIsWow64);
    return bIsWow64;
}

测试方法:

1.复制全部文件到C盘根目录
2.使用 InjectDllx64.exe把 hookdll.dll注入到explorer.exe
3.运行 NtSetValueKey.exe
注意:测试时请关闭UAC

测试截图:
被拦截:
然而问题大家都看出来了,就是无法阻止32 位进程修改 IE主页!这个想要直接解决基
本上是不可能,因为32位程序不能给64位程序注入DLL,64位程序也不能给32位程序注
入 DLL。如果真的要实现一个主动防御系统,必须有两个部分:针对64位进程的部分和针
对 32位进程的部分。针对64位进程的部分我已经实现了,针对32位进程的部分我就提出
个思路(具体代码请翻看我在2009年发表的文章):编写一个32位程序,使用WMI监视进
程创建,如果有进程被创建,就注入Hook ZwSetValueKey的 32 位 DLL。不过这毕竟不是完
美的方法。我会继续研究Win64上实现 HIPS,如果成功,会第一时间发表在《黑客防线》
上。如果已经有读者想到了比这个好的方法,也请不吝赐教。
另外本文也只是个提出一个“解决方法”,而不是“解决方案”。如果要实现可用的HIPS,
还有很长的路要走。比如增加记住用户选择的项目,增加意外处理防止把程序搞崩溃了。最
重要的是不能直接使用MessageBox弹框框,而是要使用消息机制把信息发送到另外一个程
序里进行集中的提示处理。要注意的事项太多,不胜枚举。还请有兴趣的朋友自行研究。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值