导语:知名恶意软件Poweliks曾使用过的一个后门技术,在注册表启动位置创建一个特殊的注册表键值,通过mshta来执行payload。对于这个特殊的注册表键值,在正常情况下无法对其访问,这其中的原理是什么呢?如何读取、创建以及如何删除呢?
0x00 前言
知名恶意软件Poweliks曾使用过的一个后门技术,在注册表启动位置创建一个特殊的注册表键值,通过mshta来执行payload
对于这个特殊的注册表键值,在正常情况下无法对其访问,这其中的原理是什么呢?如何读取、创建以及如何删除呢?本文将要一一介绍
0x01 简介
本文将要介绍以下内容:
·隐藏注册表的原理
·隐藏注册表的实现
·程序编写上需要注意的问题
0x02 原理
注册表键值名称经过特殊构造: 以”\0”作为开头,后面加上任意字符(不能为数字)
对于Windows系统,”\0”(即0x0000)会被识别为字符串的结束符,所以在对该字符串读取的过程中,遇到开头的”\0”,会被解析成结束符,提前截断,导致读取错误
而使用Native API设定注册表,需要使用结构体OBJECT_ATTRIBUTES作为参数, 指定读取的字符串长度
只要长度设定正常,就能够读取正确的字符串,避免这个bug
所以,我们可以通过Native API来创建这个特殊的注册表名
更为重要的是,像regedit.exe和其他对注册表的操作,通常会调用Win32 API,这就导致该注册表无法被读取,也就实现了所谓的”隐藏”
综上,创建方法为: 通过Native API创建一个以”\0”开头的键值
0x03 编写程序实现
通过Native API实现对注册表的操作,可供参考的工程地址:
https://www.codeproject.com/Articles/14508/Registry-Manipulation-Using-NT-Native-APIs
作者Dan Madden,他的代码使用了类的封装
个人倾向于使用最基本的api实现,于是参考他的代码,重新设计
对于Native API,需要的结构如下:
1.获取Native API的地址
注册表操作的相关Native API可从ntdll.dll中获得
关键代码如下:
HINSTANCE hinstStub = GetModuleHandle(_T("ntdll.dll")); NtOpenKey = (LPNTOPENKEY)GetProcAddress(hinstStub, "NtOpenKey");
2.Native API的重定义和声明
Native API在使用前需要重定义和声明
部分关键代码如下:
typedef NTSTATUS (STDAPICALLTYPE NTOPENKEY) ( IN HANDLE KeyHandle, IN ULONG DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); typedef NTOPENKEY FAR * LPNTOPENKEY; LPNTOPENKEY NtOpenKey;
3. 特殊结构体的使用
注册表操作相关Native API会使用到如下结构体,需要定义和声明
·InitializeObjectAttributes ·_STRING ·_UNICODE_STRING ·_OBJECT_ATTRIBUTES ·_KEY_INFORMATION_CLASS ·_KEY_BASIC_INFORMATION ·_KEY_VALUE_PARTIAL_INFORMATION ·_KEY_VALUE_INFORMATION_CLASS· ·RtlInitAnsiString ·RtlAnsiStringToUnicodeString
Dan Madden的工程实现了创建隐藏注册表项(注册表项名称以\0开头),该注册表项下的键值通过正常的Native API实现创建、读取、删除
通过最基本api的实现过程不再赘述,封装好的API源代码可参考文末给出的链接
测试Dan Madden工程包含的功能:
1.创建隐藏注册表项
MyCreateHiddenKey("\\Registry\\Machine\\Software\\testhidden");
使用注册表工具regedit.exe无法打开该键值,如下图
2.在该注册表下创建注册表键值
先获得该注册表项的句柄:
hKey = MyOpenHiddenKey("\\Registry\\Machine\\Software\\testhidden");
创建注册表项下的键值test1并赋值:
MySetValueKey(hKey,"test1","0123456789abcdef",REG_SZ);
读取该注册表项下键值test1的内容:
MyQueryValueKeyString(hKey,"test1");
删除该注册表项下的键值test1:
MyDeleteValueKey(hKey,"test1");
删除注册表项:
MyDeleteKey(hKey);
程序输出如下图,成功对隐藏注册表项下的正常键值进行操作
接下来,对Dan Madden的工程添加新的功能:创建、读取、删除隐藏注册表键值,思路如下:
对于注册表项的隐藏,在注册表项的名称首位填”\0”即可
对应注册表键值的隐藏,原理上也是在键值的名称首位填”\0”,但在参数传递上需要注意更多问题
1.不需要修改的功能
创建注册表键、打开注册表键和删除注册表键的功能不需要修改,使用正常的名称即可
2.设置注册表键值
对应源代码中的MySetHiddenValueKey
传入参数使用char型数组,,用来定义注册表键值名称,内容为”\0abcd”
由于”\0”的存在,所以无法直接使用strlen计算数组长度
变通方法:
计算从偏移1开始的数组长度,最终再加1
即len = strlen(buf+1)+1
Native API NtSetValueKey用来设定键值,定义如下:
typedef NTSTATUS (STDAPICALLTYPE NTSETVALUEKEY) ( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN ULONG TitleIndex, /* optional */ IN ULONG Type, IN PVOID Data, IN ULONG DataSize );
第二个参数指定键值名称,需要使用结构体UNICODE_STRING
正常情况下,我们需要先使用RtlInitAnsiString将传入的buf数组转换成结构体ANSI_STRING,再使用RtlAnsiStringToUnicodeString将其转换成结构体UNICODE_STRING,作为参数
由于”\0”的存在,无法使用RtlAnsiStringToUnicodeString
所以,我们需要自己实现结构体ANSI_STRING向结构体UNICODE_STRING的转换
ANSI向UNICODE的转换,在长度计算上,乘以2即可
数组内容上,奇数位赋值,偶数为填0x00
当然,我们需要一个中转数组TempBuff实现数组内容的转换
关键代码如下:
ValueName.Length = asName.Length*2; ValueName.MaximumLength = asName.MaximumLength*2; char *TempBuff; TempBuff = (char*)malloc(ValueName.Length); for(int i=0;i<asName.Length;i++) { TempBuff[i*2] = asName.Buffer[i]; TempBuff[i*2+1] = 0x00; } ValueName.Buffer = (WCHAR *)TempBuff;
第四个参数,指定键值内容,需要将传入的char数组转换为WCHAR
关键代码:
WCHAR wszValue[1024]; unsigned int n ; for (n=0; n<strlen(csData); n++) { wszValue[n] = (WCHAR)csData[n]; } wszValue[n++] = L'\0';
3.读取注册表键值
对应源代码中的MyQueryHiddenValueKeyString
参照2,需要注意”\0”的影响
4、删除注册表键值
对应源代码中的MyDeleteHiddenValueKey
参照2,需要注意”\0”的影响
实际测试:
创建注册表项test2,创建隐藏注册表键值\0test2,创建正常注册表键值test2
直接打开,如下图
能够正常访问注册表键值test2,但无法访问注册表键值\0test2
如下图
而我们编写的程序能够正常读取,如下图
至此,成功实现对注册表键值的隐藏
以上功能代码已开源,地址如下:
https://github.com/3gstudent/HiddenNtRegistry
0x04 powershell实现
可参考Brian Reitz的工程,地址如下:
https://gist.github.com/brianreitz/feb4e14bd45dd2e4394c225b17df5741
具体说明可参考:
实现了在HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run下创建键值\0abcd,内容为mshta javascript:alert(1)
使用我们编写的程序成功读取该键值,如下图
0x05 补充
PSReflect-Functions包含多个通过powershell调用API的实例代码,地址如下:
https://github.com/jaredcatkinson/PSReflect-Functions
0x06 小结
本文介绍了Poweliks使用过的注册表隐藏技术,分析原理,编写c程序实现功能,测试powershell实现代码