导语:知名恶意软件Poweliks曾使用过的一个后门技术,在注册表启动位置建立一个特殊的注册表键值,经过mshta来执行payload。对于这个特殊的注册表键值,在正常状况下没法对其访问,这其中的原理是什么呢?如何读取、建立以及如何删除呢?javascript
0x00 前言java
知名恶意软件Poweliks曾使用过的一个后门技术,在注册表启动位置建立一个特殊的注册表键值,经过mshta来执行payloadgit
对于这个特殊的注册表键值,在正常状况下没法对其访问,这其中的原理是什么呢?如何读取、建立以及如何删除呢?本文将要一一介绍github
0x01 简介shell
本文将要介绍如下内容:api
·隐藏注册表的原理数组
·隐藏注册表的实现工具
·程序编写上须要注意的问题post
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”的影响
四、删除注册表键值
对应源代码中的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实现代码