導語:知名惡意軟件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實現代碼