前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一种注册表沙箱的思路、实现——研究Reactos中注册表函数的实现1

一种注册表沙箱的思路、实现——研究Reactos中注册表函数的实现1

作者头像
方亮
发布2019-01-16 15:42:57
7230
发布2019-01-16 15:42:57
举报
文章被收录于专栏:方亮方亮

        因为我们沙箱注入了一个DLL到了目标进程,并且Hook了一系列NtXX(NtOpenKey)函数,所以我们在注入的代码中是不能使用RegXX(RegOpenKey等)这类函数的。因为RegXX系列函数在底层使用了NtXX系列函数,如果在注入DLL执行Hook后的逻辑中使用了RegXX系列函数,将会导致递归调用的问题,就让程序产生“蛋生鸡,鸡生蛋”这样的“思考”,可是程序不知道停止,最终脑袋用完了就挂了。于是使用Nt函数实现我们曾经习惯使用的RegXX函数是必要的。(转载请指明出处

        编写这块代码时,我参考了reactos注册表相关的源码。它的源码写的很好,但是也存在一定的漏洞,我会在之后介绍。

        列出一个比较简单的RegXX函数的实现:

代码语言:javascript
复制
LONG WINAPI RegOpenKeyExW(HKEY hKey,
              LPCWSTR lpSubKey,
              DWORD ulOptions,
              REGSAM samDesired,
              PHKEY phkResult)
{
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING SubKeyString;
    HANDLE KeyHandle;
    NTSTATUS Status;
    LONG ErrorCode = ERROR_SUCCESS;

    if (!phkResult)
    {
        return ERROR_INVALID_PARAMETER;
    }

    Status = MapDefaultKey(&KeyHandle, hKey);
    if (!NT_SUCCESS(Status))
    {
        return RtlNtStatusToDosError(Status);
    }

    if (lpSubKey != NULL)
        RtlInitUnicodeString(&SubKeyString, (LPWSTR)lpSubKey);
    else
        RtlInitUnicodeString(&SubKeyString, (LPWSTR)L"");

    InitializeObjectAttributes(&ObjectAttributes,
                               &SubKeyString,
                               OBJ_CASE_INSENSITIVE,
                               KeyHandle,
                               NULL);

    Status = NtOpenKey((PHANDLE)phkResult,
                       samDesired,
                       &ObjectAttributes);
    if (!NT_SUCCESS(Status))
    {
        ErrorCode = RtlNtStatusToDosError(Status);
    }

    ClosePredefKey(KeyHandle);

    return ErrorCode;
}

        总的来说,该函数的实现流程是:

  1. 参数合法性判断
  2. 用MapDefaultKey将HKEY转换成HANDLE
  3. 组装ObjectAttributes
  4. 调用Nt式函数
  5. 关闭第一步获得的HANDLE

        我们发现其他很多Reg函数都是走这个套路的,比如RegSetKeyValue和RegEnumKeyEx等。

        因为我们Hook的是Nt式函数,我们在函数中可以获取键对应的HANDLE,而不会得到HKEY。于是我们关心的是HKEY和HANDLE转换的过程。我们查看MapDefaultKey

代码语言:javascript
复制
#define MAX_DEFAULT_HANDLES   6
static HANDLE DefaultHandleTable[MAX_DEFAULT_HANDLES];
#define IsPredefKey(HKey)                                                      \
    (((ULONG_PTR)(HKey) & 0xF0000000) == 0x80000000)
#define GetPredefKeyIndex(HKey)                                                \
    ((ULONG_PTR)(HKey) & 0x0FFFFFFF)
static NTSTATUS MapDefaultKey(OUT PHANDLE RealKey, IN HKEY Key)
{
    PHANDLE Handle;
    ULONG Index;
    BOOLEAN DoOpen, DefDisabled;
    NTSTATUS Status = STATUS_SUCCESS;

    if (!IsPredefKey(Key)) {
        *RealKey = (HANDLE)((ULONG_PTR)Key & ~0x1);
        return STATUS_SUCCESS;
    }

    /* Handle special cases here */
    Index = GetPredefKeyIndex(Key);
    if (Index >= MAX_DEFAULT_HANDLES) {
        return STATUS_INVALID_PARAMETER;
    }
    RegInitialize(); /* HACK until delay-loading is implemented */
    RtlEnterCriticalSection (&HandleTableCS);

    if (Key == HKEY_CURRENT_USER)
        DefDisabled = DefaultHandleHKUDisabled;
    else
        DefDisabled = DefaultHandlesDisabled;

    if (!DefDisabled) {
        Handle = &DefaultHandleTable[Index];
        DoOpen = (*Handle == NULL);
    }
    else {
        Handle = RealKey;
        DoOpen = TRUE;
    }

    if (DoOpen) {
        /* create/open the default handle */
        Status = OpenPredefinedKey(Index,
                                   Handle);
    }

    if (NT_SUCCESS(Status)) {
        if (!DefDisabled)
            *RealKey = *Handle;
        else
            *(PULONG_PTR)Handle |= 0x1;
    }

    RtlLeaveCriticalSection (&HandleTableCS);

    return Status;
}

          我来解释下上述代码,我们先来看一些定义

代码语言:javascript
复制
#define HKEY_CLASSES_ROOT           (( HKEY ) (ULONG_PTR)((LONG)0x80000000) )
#define HKEY_CURRENT_USER           (( HKEY ) (ULONG_PTR)((LONG)0x80000001) )
#define HKEY_LOCAL_MACHINE          (( HKEY ) (ULONG_PTR)((LONG)0x80000002) )
#define HKEY_USERS                  (( HKEY ) (ULONG_PTR)((LONG)0x80000003) )
#define HKEY_PERFORMANCE_DATA       (( HKEY ) (ULONG_PTR)((LONG)0x80000004) )
#define HKEY_PERFORMANCE_TEXT       (( HKEY ) (ULONG_PTR)((LONG)0x80000050) )
#define HKEY_PERFORMANCE_NLSTEXT    (( HKEY ) (ULONG_PTR)((LONG)0x80000060) )
#if(WINVER >= 0x0400)
#define HKEY_CURRENT_CONFIG         (( HKEY ) (ULONG_PTR)((LONG)0x80000005) )
#define HKEY_DYN_DATA               (( HKEY ) (ULONG_PTR)((LONG)0x80000006) )

          看到这些主键的值都是0x80000000+,IsPredefKey(Key)函数就是判断hKey是否为这些主键。如果不是主键,则可能是我们通过RegOpenKey等函数打开的主键下的子键(RegOpenKey(HKEY_CLASS_ROOT,L".txt",&hKey); hKey可能是形如0x000003AB这样的值),对于这样的子键Hkey,其通过(HANDLE)((ULONG_PTR)Key & ~0x1)就可以转换为HANDLE了。

        如果是以上主键,这些主键的后28位是一个数组的Index,该数组保存的其对应的HANDLE。但是这个版本的Reactos只保存了MAX_DEFAULT_HANDLES(6)个元素,于是HKEY_PERFORMANCE_TEXT、HKEY_DYN_DATA和HKEY_PERFORMANCE_NLSTEXT这样主键就会被认为不合法了。

代码语言:javascript
复制
if (Index >= MAX_DEFAULT_HANDLES){
        //HKEY_PERFORMANCE_TEXT、HKEY_PERFORMANCE_NLSTEXT、HKEY_DYN_DATA
        return STATUS_INVALID_PARAMETER;
    }

        拿到Index后使用OpenPredefinedKey打开这些主键的HANDLE

代码语言:javascript
复制
static NTSTATUS
OpenPredefinedKey(IN ULONG Index,
                  OUT HANDLE Handle)
{
    NTSTATUS Status;

    switch (Index)
    {
        case 0: /* HKEY_CLASSES_ROOT */
            Status = OpenClassesRootKey (Handle);
            break;

        case 1: /* HKEY_CURRENT_USER */
            Status = RtlOpenCurrentUser (MAXIMUM_ALLOWED,
                                         Handle);
            break;

        case 2: /* HKEY_LOCAL_MACHINE */
            Status = OpenLocalMachineKey (Handle);
            break;

        case 3: /* HKEY_USERS */
            Status = OpenUsersKey (Handle);
            break;
#if 0
        case 4: /* HKEY_PERFORMANCE_DATA */
            Status = OpenPerformanceDataKey (Handle);
            break;
#endif

        case 5: /* HKEY_CURRENT_CONFIG */
            Status = OpenCurrentConfigKey (Handle);
            break;

        case 6: /* HKEY_DYN_DATA */
            Status = STATUS_NOT_IMPLEMENTED;
            break;

        default:
            WARN("MapDefaultHandle() no handle creator\n");
            Status = STATUS_INVALID_PARAMETER;
            break;
    }

    return Status;

       这些获取HANDLE的方法在底层都是通过NtOpenKey实现的,如获取HKEY_CURRENT_USER键的路径的函数

代码语言:javascript
复制
NTSTATUS
NTAPI
RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess,
                   OUT PHANDLE KeyHandle)
{
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING KeyPath;
    NTSTATUS Status;
    PAGED_CODE_RTL();

    /* Get the user key */
    Status = RtlFormatCurrentUserKeyPath(&KeyPath);
    if (NT_SUCCESS(Status))
    {
        /* Initialize the attributes and open it */
        InitializeObjectAttributes(&ObjectAttributes,
                                   &KeyPath,
                                   OBJ_CASE_INSENSITIVE,
                                   NULL,
                                   NULL);
        Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);

        /* Free the path and return success if it worked */
        RtlFreeUnicodeString(&KeyPath);
        if (NT_SUCCESS(Status)) return STATUS_SUCCESS;
    }

    /* It didn't work, so use the default key */
    RtlInitUnicodeString(&KeyPath, RtlpRegPaths[RTL_REGISTRY_USER]);
    InitializeObjectAttributes(&ObjectAttributes,
                               &KeyPath,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);
    Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);

    /* Return status */
    return Status;
}

        经过以上各步的转换,我们就可以获得HKEY和HANDLE对应的关系了。但是实际我们却不会去关心这个过程,因为我们在Nt式函数里获得就是HANDLE,我们不用去执行这个转换过程。但是作为知识,既然研究到这儿,就得好好研究透。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2012年06月11日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档