深入分析 Windows API – LoadLibrary 的内部完成 Part 1 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

深入分析 Windows API – LoadLibrary 的内部完成 Part 1

申博_安全预警 申博 119次浏览 已收录 0个评论

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-媒介

在这里,我们再次来到内核,本日我们将议论Windows API中最重要的功用之一。做这项研讨的效果来自几周前我正在研讨的一个项目,事先我正在编写一个DLL的ReflectiverLoader,但我没法使其事变(末了它与一些reloc的器械有关),因而我以为发明我的毛病的最好要领是看看Windows是怎样处置惩罚加载库历程的。。

我将集合议论挪用LoadLibrary时实行的内核代码,因而Userland上的统统内容我都邑浏览它。另一方面,我不会深切内核中的每一个挪用/指令,置信那边有许多代码。我将重点存眷我以为最重要的功用和构造。

LoadLibrary

在剖析中,我将运用这个代码段:

int WinMain(...) {
    HMODULE hHandle = LoadLibraryW(L"kerberos.dll");
    return 0;
}

我运用Unicode函数,由于内核只处置惩罚这类字符串,以是我节约了一些时候来做研讨。
当LoadLibraryW被实行时,起首发作的事变是实行被重定向到KernelBase.dll(这与Windows自Windows 7接纳的新MinWin内核有关,更多信息),在KernelBase中,函数RtlInitUnicodeStringEx第一个被挪用,接下来它猎取UNICODE_STRING并将其参数通报给LoadLibrary(这是一个构造而不是字符串)。接下来,我们进入函数LdrLoadDLL(Prefix Ldr == Loader),个中 r9中的参数是一个out参数,它具有加载模块的句柄。在此以后我们进入函数LdrpLoadDll的私有版本,这两个函数是Userland的统统代码都将被实行的处所。经由一些完整性搜检并进入更多函数后,我们终究进入了内核代码的第一步。要实行的内核函数是NtOpenSection,这也是我将在这篇文章中重点议论的。在这里,我们能够在进入内核之前看到挪用客栈。
深入分析 Windows API - LoadLibrary 的内部完成 Part 1

NtOpenSection函数

我们起首须要晓得的是“Section”代表甚么,在Windows驱动顺序文档的内存治理章节中有一个名为 “Section Objects and Views”的局部,个中“Section Object”透露表现能够同享的存储地区,而且该工具为历程供应了将文件映射到其存储地点空间的机制(这险些援用了doc)。

请记着,只管Windows Kernel被以为险些完整是用C编写的,但它有点面向工具(它不是100%面向工具,没有严厉遵照继续准绳),这就是为何我们通常在内核中议论Object whitin。在这类状况下是“Section Object”。

因而,考虑到一个局部的界说,完整能够明白NtOpenSection是第一个在加载库时被实行的内核函数。
让我们最先,起首让我们看看这个函数将收到的参数。如你所见,将有3个参数(我们在x64上,以是依照__fastcall挪用商定,前4个参数进入寄存器)。

  • rcx -> PHANDLE pointer that receives the handle to the Object
  • rdx -> ACCESS_MASK requested access to the Object
  • r8 -> POBJECT_ATTRIBUTES pointer to the OBJECT_ATTRIBUTES of the DLL

这三个参数能够鄙人图中看到:

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

ACCESS_MASK是以下值的组合,能够在winnt.h头中取得:

#define SECTION_QUERY                0x0001
#define SECTION_MAP_WRITE            0x0002
#define SECTION_MAP_READ             0x0004
#define SECTION_MAP_EXECUTE          0x0008

险些和其他的内核实行函数一样,这个函数起首会猎取PreviousMode,然后将会有另一个搜检,在内核函数中也很罕见,它将搜检PHANDLE值是不是凌驾MmUserProbeAddress,若是第二次搜检失足,将弹出毛病998(“无效接见内存地位”)。

前些日子, Project Zero的@benhawkes披露了一个与预览形式搜检有关的Windows 内核破绽,请务必浏览他的文章,这很棒(老是运用Project Zero文章)https://googleprojectzero.blogspot.com /2019/03/windows-kernel-logic-bug-class-access.html

若是两个搜检都经由过程,代码将进入“ObOpenObjectByName”,这个函数将吸收rdx中的一个范例为Section的工具,此工具是从MmSectionObjectType地点中检索的。

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

从如今最先,我们进入“真正的”内核代码,起首要搜检我们是不是在 rcx中收到OBJECT_ATTRIBUTES和在 rdx中收到OBJECT_TYPE,若是统统顺遂,内核将从LookAside List 8取得一个池(KTHREAD-> PPLookAsideList) [8] .P),我不会深切研讨LookAside列表的内容,而是将它们视为某种缓存。(你能够在这里浏览更多内容)接下来将挪用函数ObpCaptureObjectCreateInformation,经由一些完整性搜检后,代码将存储一个OBJECT_CREATE_INFORMATION构造,个中包罗来自之前的池中OBJECT_ATTRIBUTES的数据。

若是Object属性具有工具名( UNICODE_STRING ),该称号将被复制到 r9 参数中指向的地点中,但略加修正,最大长度将更改成 F8h

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

从谁人函数返回后,最先变得风趣。起首我们从这里取得一个指向KTHREAD(gs:188h)的指针,我们取得了一个指向KPROCESS(KTHREAD + 98h– > ApcState + 20h– > Process)的指针,如你所知,KPROCESS是EPROCESS的第一个元素(有点像内核历程中的PEB)。因而,若是你获得一个指向KPROCESS的指针,你也有一个指向EPROCESS的指针。

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

如许,内核猎取UniqueProcessId(EPROCESS + 2E0h),这些代码也会取得指向成员GenericMapping的指针,该成员是0xc构造OBJ_TYPE_INITIALIZER内部的偏移量,它位于偏移量40h中的构造OBJECT_TYPE内。在此以后,将挪用函数SepCreateAccessStateFromSubjectContext,望文生义,我们在挪用此函数后会吸收到ACCESS_STATE工具(指针作为rdx中的参数通报),此函数属于组件“平安参考监视器”,此组件重要供应搜检接见和权限的函数,你能够经由过程前缀Se辨认这些函数。

下一步,多是此历程中最重要的一步,是实行函数ObpLookupObjectName。一样,这个名字给了我们一点关于这个要领功用的信息,在这里代码将基于一个名字(在本例中为DLL称号)来寻觅一个工具。经由过程检察函数Graph,我们能够看出它是一个重要的函数。

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

明白这些函数的一个异常有价值的方面是晓得函数希冀的参数是甚么,许多内核函数没有在WDK上记录下来,以是我们有两个挑选, 第一种要领是内核逆向,实验明白哪些参数被通报给函数,第二种要领更快,就是在Google上搜刮函数,你可能会进入ReactOS,这是一个超等棒的项目(有点像开源Windows),这个项目中有许多函数险些完整婚配Windows内核,这是明白内核中许多器械的好要领,以是一定要接见该项目。

关于这个函数参数,请看下图:

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

在这个函数中,起首要初始化构造OBP_LOOKUP_CONTEXT,接下来我们经由过程挪用ObReferenceObjectByHandle取得对“KnownDlls”目次工具的援用,该工具包罗已加载到内存中的Section Objects列表,而且每一个对应于来自“KnownDlls”注册表项的一个DLL。

Spoiler: 正如你在Userland挪用客栈中所看到的,NtOpenSection之前的函数称为LdrpFindKnownDll,这意味着若是我们实验加载的DLL不在“KnownDlls”列表中,我们将收到毛病。

CVE-2017-17215-HG532命令注入漏洞分析

漏洞描述 华为 HG532 系列路由器是一款为家庭和小型办公用户打造的高速无线路由器产品。 该漏洞被用来作为病毒 Mirai 的升级版变种 OKIRU/SATORI,payload由蜜罐所捕获而被发现的,首次披露是由checkpoint所披露,漏洞利用的是upnp服务存

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

接下来代码将运用DLL的称号盘算Hash值,它将搜检此Hash值是不是与“KnownDlls”中的一个Hash值婚配,若是没有婚配那末函数将返回毛病“c0000034:“找不到工具名“。 从这里最先,流程重如果在返回Userland之前清算统统内容。

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

Another Spoiler:在第2局部,我们将看到Userland在收到毛病“c0000034”时怎样回响反映。疾速预览将会找到DLL,并挪用函数NtOpenFile。

KnownDll

如今让我们假定我们正在寻觅的DLL在已知Dll列表中,由于我懒得再次编译代码,我们将“kerberos.dll”增添到此列表中。我们能够在以下注册表中找到此列表:

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\KnownDLLs

注重!我们须要提拔权限来实行此操纵,在我的例子中,我只是将本身设置为该密钥的统统者并增添了DLL。

鄙人图中,你能够看到Kerberos DLL怎样作为已知DLL的一局部加载(没有搜检太多,但我置信称号必需是大写,由于hash是运用DLL的大写称号盘算的,,然则有些状况下像“kernel32.dll”是小写的,以是我须要对此举行更多的研讨)。

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

我们能够看到函数ObpLookupObjectName此次作为NTSTATUS是怎样返回0而非“c0000034”。

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

关于这类状况,我们将直接从函数ObpLookupObjectName最先,特别是从盘算散列的点最先(在这两种状况,代码流都是雷同的)。此次我们将经由过程检察以下伪代码来检察hash的盘算要领:

注重!此函数未记录,因而极可能完成从一个版本的Windows更改成另一个版本,以至从一个SP更改成下一个版本。特别是我正在研讨这个版本的内核:Windows 8.1 Kernel Version 9600 MP (2 procs) Free x64

// Credit to Hex-Ray xD
QWORD res = 0;
DWORD hash = 0;
DWORD size = Dll.Length >> 1;
PWSTR dll_buffer = unicode_string_dll.Buffer;

if (size > 4) {
    do {
        QWORD acc = dll_buffer;
        if (!(Dll_Buffer & ff80ff80ff80ff80h))
            acc = (QWORD *) Dll_Buffer & ffdfffdfffdfffdfh;
        }
        /* This code is really executed in the else statement, the if
        statement is a while that goes element by element substracting 
        20h from every element between 61h and 7Ah, of course that's 
        much slower than this */
        size -= 4;
        dll_buffer += 4;
        res = acc + (res >> 1) + 3 * res;
    } while (size >= 4)
    hash = (DWORD) res + (res >> 20h)
    /* If size is not a multiple of 4 the last iteration
    would be done using the while explained before */
}

obpLookupCtx.HashValue = hash;
obpLookupCtx.HashIndex = hash % 25;

若是你运用DLL称号“kerberos.dll”实行此操纵,愿望你将取得20h对应于十进制值32 的HashIndex ,若是你仔细搜检我的图片,个中“kerberos.dll”是作为已知DLL的一局部加载,并在hash列中举行搜检,你能够看到值是32。接下来,该函数搜检写入OBP LOOKUP CONTEXT构造的盘算散列是不是与该局部的散列和盘算索引相婚配:

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

若是第一次搜检顺遂,则代码OBJECT_HEADER_NAME_INFO将运用公式猎取ObjectHeader - ObpInfoMaskToOffset - ObpInfoMaskToOffset[InfoMask & 3],而且依据我们作为参数通报给函数LoadLibrary的称号再次搜检工具的称号。若是希望顺遂,那末OBP_LOOKUP_CONTEX的成员工具和EntryLink将被添补,经由屡次搜检,这个构造将被复制到外部参数指针中,我们将从这个函数返回。该函数有两个out参数,返回后,第一个参数将有指向工具的指针,第二个参数将有指向添补的OBP_LOOKUP_CONTEX构造的指针。

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

若是搜检函数吸收的参数(此处),FoundObject值将为rsp + 68h,而OBP_LOOKUP_CONTEX构造将为rsp + 48h。别的看看这个工具怎样还没有翻开任何句柄,这将发作在我们本日要进修的末了一个函数ObpCreateHandle中,这个函数将会从工具中猎取句柄。

这个函数也有许多代码,由于这已很长了,我不会细致引见(或许在其他帖子中我能够细致引见,由于它是一个异常风趣的函数)。

ObpCreateHandle将吸收的最重要的参数是在rcx上,它将从OB_OPEN_REASON罗列中吸收一个值。以下之一:

ObCreateHandle      =   0
ObOpenHandle        =   1
ObDuplicateHandle   =   2
ObInheritHandle     =   3
ObMaxOpenReason     =   4

然后在rdx函数中希冀援用工具(DLL Section Object),并在r9函数中吸收ACCESS_STATE构造,个中包罗ACCESS_MASK和其他风趣的内容。

我们考虑到这一点,而且在这类状况下晓得OB_OPEN_REASON罗列的值将是ObOpenHandle,让我们最先。该函数将做的第一件事是搜检我们试图猎取的处置惩罚顺序是不是用于内核工具(换句话说,我们正在实验猎取内核句柄)。若是不是这类状况,那末函数将检索KTHREAD->ApcState->Process->(EPROCESS) ObjectTable对应于HANDLE_TABLE构造的ObjectTable(),在一些搜检以后,将挪用函数ExAcquireResourceSharedLite以猎取PrimaryToken的资本(当我说资本时,我指的是某种互斥体的ERESOURCES构造,你能够在这里浏览更多有关资本的信息)。

若是已猎取资本,则将挪用函数SeAccessCheck,这些函数搜检是不是能够授与对特定工具的要求接见权限。若是授与了这些权限,我们进入函数ObpIncrementHandleCountEx,它卖力从我们试图猎取句柄的节工具和一样平常工具范例计数中递增句柄计数(这个函数只增添计数器,但这个其实不意味着句柄是翻开的。这能够经由过程运转!object [object]来搜检,你会注重到HandleCount已递增,然则你将看不到对这个搜检历程的句柄!handle的任何援用)

末了句柄将翻开。为了节约一些时候,我将展现一些伪代码怎样完成,我将在代码中增添解释。(再次由Hex-Rays资助的伪代码)。

// I'm goint to simplify, there will be no check nor casts
HANDLE_TABLE * HandleTable = {};
HANDLE_TABLE_ENTRY * NewHandle = {};
HANDLE_TABLE_FREE_LIST * HandlesFreeList = {};

// Get reference to the Object and his attributes (rsp+28h), to get
// the object we use the Object Header (OBJECT_HEADER) which is 
// obtained from the Object-30h (OBJECT_HEADER+30h->Body) 
QWORD LowValue = 
    (((DWORD) Attributes & 7 << 11) | (Dll_object - 30h << 10) | 1)
// Get the type, Object-18h (OBJECT_HEADER+18h->TypeIndex)
HIDWORD(HighValue) = Dll_Object - 18h
// Get the requested access 
LODWORD(HighValue) = ptrAccessState.PrevGrantedAccess & 0xFDFFFFFF;
// Get the HANDLE_TABLE from the process
HandleTable = KeGetCurrentThread()->ApcState.Process->ObjectTable;
// Calculate index based on Processor number 
indexTable = Pcrb.Number % nt!ExpUuidSequenceNumberValid+0x1;

// Get the List of Free Handles
HandlesFreeList = HandleTable->FreeLists[indexTable];
if(HandlesFreeList) {
    Lock(HandlesFreeList); // This is more complex than this
    // Get the First Free Handle
    NewHandle = HandlesFreeList->FirstFreeHandleEntry;
    if (NewHandle) {
        // Make the Free handles list point to the next free handle
        tmp = NewHandle->NextFreeHandleEntry;
        HandlesFreeList->FirstFreeHandleEntry = tmp;
        // Increment Handle count
        ++HandlesFreeList->HandleCount;
    }
    UnLock(HandlesFreeList);
}

if (NewHandle) {
    // Obtain the HandleValue, just to return it
    tmp = *((NewHandle & 0xFFFFFFFFFFFFF000) + 8)
    tmp1 = NewHandle - (NewHandle & 0xFFFFFFFFFFFFF000) >> 4;
    HandleValue = tmp + tmp1*4;
    // Assign pre-computed values to the handle so it
    // knows to which object points, whick type of object it
    // is and which permissions where granted
    NewHandle->LowValue = LowValue;
    NewHandle->HighValue = HighValue;
}

末了,该函数将返回句柄值rsp+48。从如今最先直到返回用户域Userland,统统都与清算机械状况(构造,单个列表,接见状况等等)有关,当我们终究抵达Userland(LdrpFindKnowDll)时,我们将具有句柄,STATUS将为0。

深入分析 Windows API - LoadLibrary 的内部完成 Part 1

这个句柄与LoadLibrary在完成统统操纵时将返回的模块句柄无关,这只是一个将在“内部”运用的Section工具的句柄。更重要的是,在这一点上,DLL以至没有被加载到历程的地点空间中,我们将在第2局部中看到这是怎样发作的。

结论

如你所见,内核中有许多代码,并不是统统都是简朴直接的,我敢肯定事变异常庞杂。请记着我们将进入更庞杂的器械。另一方面,我留下了大批没有批评也没有说起的代码、构造、列表等,我只是试着总结我以是为最重要的器械。固然,若是你有任何迷惑和题目,请坚决果断与我联络。我愿望你喜好它并在第2局部见到你。

Spring Cloud Config Server 途径穿越与恣意文件读取破绽剖析 – 【CVE-2019-3799】

漏洞复现 GET /foo/default/master/..%252F..%252F..%252F..%252Fetc%252fpasswd HTTP/1.1
Host: localhost:8888 漏洞分析 Spring Cloud Config是Spirng Cloud下用于分布式配置管理的组件,分为Config-Server和Config-Client两个角色。 C


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明深入分析 Windows API – LoadLibrary 的内部完成 Part 1
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址