Uncovering RPC Servers through Windows API Analysis

渗透技巧 6个月前 admin
326 0 0

Intro 介绍

Have you ever tried to reverse a simple Win32 API? If not, let’s look at one together today! This article serves as a hand-holding walkthrough and documents in detail how I analyzed a simple Win32 API: LogonUserA. Throughout the article, we’ll go over how to use some of IDA’s most common features and look for some “poorly-documented” Microsoft structures.
你有没有尝试过反转一个简单的Win32 API?如果没有,今天让我们一起来看看一个!本文用作手动演练,并详细记录了我如何分析简单的 Win32 API: LogonUserA 。在整篇文章中,我们将介绍如何使用 IDA 的一些最常见功能,并寻找一些“文档记录不佳”的Microsoft结构。

Are you ready? If so, then grab your IDA or Ghidra and a cup of coffee, and let’s get started!
准备好了吗?如果是这样,那么拿起你的IDA或Ghidra和一杯咖啡,让我们开始吧!

Uncovering RPC Servers through Windows API Analysis

Advapi32!LogonUser 阿德瓦皮32!登录用户

Per the official Microsoft MSDN documentation, The LogonUser function attempts to log a user on to the local computer and returns a handle to a token that represents the logged-on user.” The function declaration is (note the Hungarian notation):
根据官方Microsoft MSDN 文档,LogonUser 函数尝试将用户登录到本地计算机,并返回表示登录用户的令牌的句柄。函数声明是(注意匈牙利符号):

BOOL LogonUserA(
[in] LPCSTR lpszUsername,
[in, optional] LPCSTR lpszDomain,
[in, optional] LPCSTR lpszPassword,
[in] DWORD dwLogonType,
[in] DWORD dwLogonProvider,
[out] PHANDLE phToken
);

From the parameters, we can assume that if we supply valid credentials, we will receive a valid token handle in return. That is the whole purpose of LogonUserA and red teamers can use the token handle to impersonate the specified user.
从参数中,我们可以假设如果我们提供有效的凭据,我们将收到一个有效的令牌句柄作为回报。这就是 LogonUserA 的全部目的,红队成员可以使用令牌句柄来模拟指定的用户。

Advapi32!LogonUserA 阿德瓦皮32!登录用户A

The requirements section in the LogonUserA MSDN documentation states that Advapi32.dll dynamic-link library (DLL) exports the function. First, let’s open it up in IDA. The steps are listed as follows:
LogonUserA MSDN 文档中的要求部分指出动态 Advapi32.dll 链接库 (DLL) 导出函数。首先,让我们在IDA中打开它。步骤如下:

Open IDA, click “New”, and double-click on the Advapi32.dll DLL located under C:\windows\system32\Advapi32.dll.
打开 IDA,单击“新建”,然后双击位于 下的 Advapi32.dll C:\windows\system32\Advapi32.dll DLL。

Uncovering RPC Servers through Windows API Analysis
Uncovering RPC Servers through Windows API Analysis

After selecting Advapi32.dll, IDA will ask for additional loading options for the DLL.
选择 Advapi32.dll 后,IDA 将要求为 DLL 提供其他加载选项。

Uncovering RPC Servers through Windows API Analysis

I like the tip given out by @herrcore which is having an IDA decompiler window and the graph mode window side by side like this:
我喜欢@herrcore给出的提示,它有一个 IDA 反编译器窗口和图形模式窗口并排,如下所示:

Uncovering RPC Servers through Windows API Analysis

Another tool tip is “Synchronizing” the decompiler window to with the graph window. You can do this by right-clicking on the graph window, hovering over the “Synchronize with” button, and clicking on the name representing the decompiler window (in this case, Pseudocode-A). Now when you click on any line of the code, both windows will jump to the corresponding line.
另一个工具提示是将反编译器窗口与图形窗口“同步”。您可以通过右键单击图形窗口,将鼠标悬停在“同步”按钮上,然后单击表示反编译器窗口的名称(在本例中为 Pseudocode-A )。现在,当您单击代码的任何行时,两个窗口都将跳转到相应的行。

Uncovering RPC Servers through Windows API Analysis

Alright. Let’s get back to LogonUserA! When you click “yes” upon loading LogonUserA into IDA, IDA will attempt to download the public symbol from the Microsoft server. For this article, I have already downloaded a file and that is why IDA has populated the name of the function and its parameters for me.
好。让我们回到 LogonUserA !当您在将登录用户 A 加载到 IDA 时单击“是”时,IDA 将尝试从Microsoft服务器下载公共符号。在本文中,我已经下载了一个文件,这就是为什么 IDA 为我填充了函数的名称及其参数。

Uncovering RPC Servers through Windows API Analysis

Right off the bat, we can see the function LogonUserA acts as a “Wrapper function” to LogonUserCommonA. Something worth noting is that IDA also adds four “Zeroes” onto the stack as the arguments for LogonUserCommonA which indicates LogonUserCommonA might take additional arguments than LogonUserA.
马上,我们可以看到该函数充当 “包装器函数 LogonUserA ” 到 LogonUserCommonA .值得注意的是,IDA 还在堆栈中添加了四个“零”,因为其指示 LogonUserCommonA 的参数 LogonUserCommonA 可能需要比 . LogonUserA

Uncovering RPC Servers through Windows API Analysis

Addvapi32!LogonUserCommonA
阿德瓦皮32!LogonUserCommonA

After double-clicking on the LogonUserCommonA function, IDA will show the decompiled code for LogonUserCommonA in both of our windows. (IDA double click)
双击 LogonUserCommonA 该函数后,IDA 将在我们的两个窗口中显示反 LogonUserCommonA 编译代码。(IDA双击)

Uncovering RPC Servers through Windows API Analysis

After double-clicking, LogonUserCommonA will first call RtlInitAnsiString and RtlAnsiStringToUnicodeString with our supplied arguments such as “username, domain, and password”. Those calls simply turn our American National Standards Institute (ANSI) encoded string arguments into the UNICODE_STRING type — which is what Windows accepts as string arguments. Microsoft has well documented the reasoning behind this.
双击后,将首先调用 RtlInitAnsiString 并使用 RtlAnsiStringToUnicodeString 我们提供的参数,例如“用户名, LogonUserCommonA 域和密码”。这些调用只是将我们的美国国家标准协会 (ANSI) 编码的字符串参数转换为 UNICODE_STRING 类型,这是 Windows 接受的字符串参数。Microsoft已经很好地记录了这背后的原因。

Advapi32!LogonUserCommonW
阿德瓦皮32!登录用户共享W

After converting our supplied username, domain, and password to UNICODE_STRINGs, the LogonUserCommonA will proceed to call LogonUserCommonW
将我们提供的用户名、域和密码转换为UNICODE_STRINGs后, LogonUserCommonA 将继续调用 LogonUserCommonW

Uncovering RPC Servers through Windows API Analysis

Inside the LogonUserCommonW function, we can see that it makes another function call to LogonUserEXEXW.
在函数内部,我们可以看到它对 进行了 LogonUserEXEXW 另一个 LogonUserCommonW 函数调用。

Uncovering RPC Servers through Windows API Analysis

Double-clicking on LogonUserEXEXW, the graph window will jump to the .idata section (tl;dr, .idata section stores the import directory information about a portable executable) of the binary and we can scroll up to see which DLL the advapi32 is importing LogonUserEXEXW from SspiCli.dll.
双击 LogonUserEXEXW ,图形窗口将跳转到 .idata 部分 (tl;dr,.idata部分存储有关二进制文件的可移植可执行文件的导入目录信息,我们可以向上滚动以查看advapi32从哪个DLL导入 LogonUserEXEXW SspiCli.dll 。

Uncovering RPC Servers through Windows API Analysis

SspiCli!LogonUserEXEXW 啪!登录用户EXEXW

In this scenario, we need to open a new IDA instance for the SspiCli.dll and, if we allow IDA to download public symbols, IDA will happily populate the function names and parameters.
在这种情况下,我们需要为 打开 SspiCli.dll 一个新的 IDA 实例,如果我们允许 IDA 下载公共符号,IDA 将愉快地填充函数名称和参数。

To use the search function, press Ctrl + F and type LogonUserEXEXW into the function name window.
要使用搜索功能,请在函数名称窗口中键入 Ctrl + F LogonUserEXEXW 。

Uncovering RPC Servers through Windows API Analysis

Next, click on the LogonUserEXEXW. IDA will then jump to the function in both of our windows (you can see the setup below).
接下来,单击 LogonUserEXEXW .然后,IDA 将跳转到我们两个窗口中的功能(您可以在下面看到设置)。

Uncovering RPC Servers through Windows API Analysis

Skimming through the function body, the first part of the function appears to verify the logonType and logonProvider arguments. IDA gratefully decompiled the code into a switch case for us so that we can see it better.
浏览函数体时,函数的第一部分似乎验证 logonType 了 and logonProvider 参数。IDA感激地为我们解编译了代码,以便我们更好地看到它。

Uncovering RPC Servers through Windows API Analysis

The questions here are, “What are some of the logonType and logOnProviders Microsoft has created?” and, “What are the differences between them?”
这里的问题是,“ logOnProviders Microsoft创造了什么 logonType ?”和“它们之间有什么区别?”

If we go back and revisit the function prototype for LogonUserA, we can see the dwLogonType and dwLogonProvider’s descriptions at the bottom of the page. However, it is not very straightforward as to what each of the names is represented by numbers we have seen in IDA.
如果我们返回并重新访问 的 LogonUserA 函数原型,我们可以在页面底部看到 和 dwLogonType dwLogonProvider 的描述。但是,对于我们在IDA中看到的数字表示的每个名称并不是很简单。

My solution is to find it with Visual Studio or rather Windows SDK header files. We can find one of the names specified under dwLogonType (e.g., LOGON32_LOGON_BATCH) and copy-paste it to our test Visual Studio project with header files (Windows.h) included.
我的解决方案是使用Visual Studio或Windows SDK头文件找到它。我们可以找到在(例如,)下 dwLogonType 指定的名称之一, LOGON32_LOGON_BATCH 并将其复制粘贴到我们的测试Visual Studio项目中,其中包含头文件( Windows.h )。

Uncovering RPC Servers through Windows API Analysis

To follow it, hold Ctrl and left-click on the highlighted name. Once this action is performed, you will see the numbers match with the switch statement IDA decompiled.
要关注它,请按住 Ctrl 并左键单击突出显示的名称。执行此操作后,您将看到数字与反编译的 switch 语句 IDA 匹配。

Uncovering RPC Servers through Windows API Analysis

logOnType is defined as, “The type of logon operation to perform” . This parameter can be one of the following values, defined in Winbase.h.” Similarly, we can see the logOnProvider specified in Winbase.h, and according to Microsoft, a logOnProvider specifies the authentication provider, the default provider is the “negotiate” provider. This parameter can be one of the following values.”
logOnType 定义为“要执行的登录操作的类型”。此参数可以是以下值之一,在 中 Winbase.h 定义。同样,我们可以看到 中 Winbase.h 指定的, logOnProvider 并且根据Microsoft,a logOnProvider 指定身份验证提供程序,默认提供程序是“协商”提供程序。此参数可以是以下值之一。

NOTE: We will cover what a logon provider means in future blog posts
注意:我们将在以后的博客文章中介绍登录提供程序的含义

Uncovering RPC Servers through Windows API Analysis

The second part of the IDA-decompiled function body (shown below) notices that the logOn32MsvAuthPkgID and LogOn32NegoAuthPkgId are all defined in the .data section, which are initialized static variables.
IDA 反编译函数体的第二部分(如下所示)注意到 logOn32MsvAuthPkgID 和 LogOn32NegoAuthPkgId 都在本节 .data 中定义,它们是初始化的静态变量。

Uncovering RPC Servers through Windows API Analysis
Uncovering RPC Servers through Windows API Analysis

For people who don’t know, the 0FFFFFFFFh is interpreted as-1 as a signed integer, hence we are entering the “if” statement and can ignore the RtlEnterCriticalSection for now. Within the nested “if” statement, there is a function call (e.g., L32pInitiLsa) that will reveal its decompiled code when double-clicking on the function name.
对于不知道的人,被 0FFFFFFFFh 解释为 -1 有符号整数,因此我们输入“if”语句,现在可以忽略。 RtlEnterCriticalSection 在嵌套的“if”语句中,有一个函数调用(例如, L32pInitiLsa )将在双击函数名称时显示其反编译代码。

From SspiCli!LogonUserEXEXW to SspiCli!L32pInitLsa & SspiCli!LsaLookupAuthenticationPackage
来自斯皮克利!LogonUserEXEXW to SspiCli!L32pInitLsa & SspiCli!LsaLookupAuthenticationPackage

IDA will decompile the code for us and the result will resemble the example image below.
IDA 将为我们反编译代码,结果将类似于下面的示例图像。

Uncovering RPC Servers through Windows API Analysis

You’ll note that two constant strings used the RtlInitString function and later, that string is used with a function call to the LsaLookupAuthenticationPackage function. Luckily for us, the LsaLookupAuthenticationPackage is well documented here. From the function prototype, we can figure out the strings MICROSOFT_AUTHENTICATION_PACKAGE_V1_0 and Negotiate are simply the name of authentication packages. Upon successful call, the function returns the package identifier (pkgID) and saves them to the previously mentioned .data section.
您会注意到,两个常量字符串使用了该函数,后来,该 RtlInitString 字符串与函数调用 LsaLookupAuthenticationPackage 一起使用。对我们来说幸运的是,这里 LsaLookupAuthenticationPackage 有很好的记录。从函数原型中,我们可以找出字符串 MICROSOFT_AUTHENTICATION_PACKAGE_V1_0 , Negotiate 并且只是身份验证包的名称。成功调用后,该函数返回包标识符 ( pkgID ) 并将它们保存到前面提到的 .data 部分。

Back to SspiCli!LogonUserEXEXW
回到斯皮克利!登录用户EXEXW

Returning from L32pInitLsa, we continue to the third part of the function. In the section where we called Addvapi32!LogonUserCommonA, we passed four additional zeroes at the end, like so:
从 返回 L32pInitLsa ,我们继续函数的第三部分。在我们调用 Addvapi32!LogonUserCommonA 的部分中,我们在末尾传递了四个额外的零,如下所示:

Uncovering RPC Servers through Windows API Analysis

These zeros are saved onto the stack and never used within Advapi32!LogonUserCommonA and Advapi32!LogonUserCommonW, IDA thinks that they are used here by SspiCli!LogonUserEXEXW . The four zeros are arguments a8, a9, a10, and a11 and it’s being checked to see if the argument is NULL, if so, it will initiate it for us.
这些零被保存到堆栈中,并且从未在 和 Advapi32!LogonUserCommonW 中使用 Advapi32!LogonUserCommonA 过,IDA 认为它们在这里被 使用 SspiCli!LogonUserEXEXW 。四个零是参数,正在检查参数 a8, a9, a10, and a11 是否为 NULL,如果是,它将为我们启动它。

Note: I did not spend too much time trying to understand this part of the code as it was irrelevant in the RPC context, so if you know those “Zeros” serves a different purpose, please send me a dm!
注意:我没有花太多时间试图理解这部分代码,因为它在 RPC 上下文中无关紧要,所以如果你知道这些“零”有不同的目的,请给我发一个 dm!

Uncovering RPC Servers through Windows API Analysis

From SspiCli!LogonUserEXEXW to SspiCli!L32GetDefaultDomainName
来自斯皮克利!LogonUserEXEXW to SspiCli!L32获取默认域名

The 4th part of the function is interesting because there are a couple of new functions called here, L32GetDefaultDomainName and L32pLogonUser.
该函数的第 4 部分很有趣,因为这里调用了几个新函数, L32GetDefaultDomainName 并且 L32pLogonUser .

The first “if” statement checks to see if the first byte of our supplied domain argument is equal to decimal 46 which if you look up the ASCII table, it indicates the “.” character. According to Microsoft documents, If this parameter is “.”, the function validates the account by using only the local account database.
第一个“if”语句检查我们提供的域参数的第一个字节是否等于十进制46,如果您查找ASCII表,它将指示“.”字符。根据Microsoft文档,如果此参数为“.”,则该函数仅使用本地帐户数据库验证帐户。

Uncovering RPC Servers through Windows API Analysis

Double clicking on the function name in IDA will show the decompiled code for L32GetDefaultDomainName. This code is not very important in our case, but what it does is to calls LsaLookupGetDomainInfo to get the local computer name, and put the local computer nameit at a global variable Logon32DomainName, and then goto LABEL_6 sets the user-supplied domain to Logon32DomainName.
双击 IDA 中的函数名称将显示 的 L32GetDefaultDomainName 反编译代码。在我们的例子中,这段代码不是很重要,但它的作用是调用 LsaLookupGetDomainInfo 以获取本地计算机名称,并将本地计算机名称放在全局变量 Logon32DomainName 中,然后将 goto LABEL_6 用户提供的域设置为 Logon32DomainName 。

Uncovering RPC Servers through Windows API Analysis

Returning from L32GetDefaultDomainName, the function casts our supplied password (already UNICODE_STRING type) to UNICODE_STRING again (not exactly sure why), and the second “if” statement determines the authentication package ID based on our supplied logOnProvider argument. If it’s larger than 4, use MSV1_0 package ID; if not, use Negotiate package ID obtained from the previous lsalookupAuthenticationPackage.
从 L32GetDefaultDomainName 返回,该函数将我们提供的密码(已经 UNICODE_STRING 键入)强制 UNICODE_STRING 转换为 Again(不确定原因),第二个“if”语句根据我们提供的 logOnProvider 参数确定身份验证包 ID。如果大于 4,请使用 MSV1_0 package ID ;如果不是, Negotiate package ID 请使用从上一个 lsalookupAuthenticationPackage .

Uncovering RPC Servers through Windows API Analysis

From SspiCli!LogonUserExExW to SspiCli!L32pLogonUser
来自斯皮克利!LogonUserExExW to SspiCli!L32p登录用户

Finally, we will arrive at the function call L32pLogonUser, double-clicking on the function name will point us to the decompiled function code.
最后,我们将到达 函数调用 L32pLogonUser ,双击函数名称将指向反编译的函数代码。

The LogonLsaHandle is a global variable that was populated via SspiCli!L32pInitLsa mentioned previously. There is a new string being initialized “LogonUser API” and a new variable which looks like the length of a buffer containing our supplied arguments.
这是 LogonLsaHandle 前面提到的填充 SspiCli!L32pInitLsa 的全局变量。有一个正在初始化的新字符串“LogonUser API”和一个新变量,它看起来像包含我们提供的参数的缓冲区的长度。

Uncovering RPC Servers through Windows API Analysis

The next portion of the code allocates a heap memory for the length of the buffer; the function first initializes the _MSV1_0_LOGON_SUBMIT_TYPE to 82. Entering the “if” statement, if the LogonProvider is not 4 (which is LOGON32_PROVIDER_VIRTUAL), then the _MSV1_0_LOGON_SUBMIT_TYPE variable will be assigned with the value 2. The rest of the code fills the buffer with our user-supplied arguments and padding. Another note here is that IDA identified the AuthInformation buffer as _WORD *AuthInformation, which means each dereference of the AuthInformation would point to a word (two bytes).
代码的下一部分为缓冲区的长度分配堆内存;该函数首先将 初始化为 _MSV1_0_LOGON_SUBMIT_TYPE 82 。输入 “if” 语句,如果不是 LogonProvider 4(即 LOGON32_PROVIDER_VIRTUAL ),则将为 _MSV1_0_LOGON_SUBMIT_TYPE 变量赋值 2。代码的其余部分使用用户提供的参数和填充缓冲区。这里的另一个注意事项是,IDA 将 AuthInformation 缓冲区标识为 _WORD *AuthInformation ,这意味着 的每个取消引用 AuthInformation 都将指向一个单词(两个字节)。

Uncovering RPC Servers through Windows API Analysis

The final buffer AuthInformation looks similar to the image below where the first four bytes store the 0x52 (the length of the buffer) and the rest of the buffer contains our supplied username, password, and domain in UNICODE_STRING. Please note that the password here has been redacted for obvious reasons.
最终缓冲区 AuthInformation 类似于下图,其中前四个字节存储0x52(缓冲区的长度),缓冲区的其余部分包含我们在 中 UNICODE_STRING 提供的用户名、密码和域。请注意,由于显而易见的原因,此处的密码已被编辑。

Uncovering RPC Servers through Windows API Analysis

From SspiCli!LogonUserEXEXW to SspiCli!L32pLogonUser to SspiCli!SspipLogonUser
来自斯皮克利!LogonUserEXEXW to SspiCli!L32pLogonUser to SspiCli!SspipLogonUser

After the buffer is initialized, the call will make another function call to SspiLogonUser. From the function prototype, IDA has nicely displayed the arguments in the pseudocode window, we are taking the LsaHandle global variable assigned previously via L32pInitLsa, an integer which is either MSV1_0 authentication package ID or Negotiate package ID global variable that was assigned previously via L32pInitLsa, our newly allocated authentication buffer, Authentication buffer’s length, and other arguments.
初始化缓冲区后,调用将对 进行 SspiLogonUser 另一个函数调用。从函数原型来看,IDA 在伪代码窗口中很好地显示了参数,我们采用之前通过分配的全局变量,一个整数,它是之前通过MSV1_0身份验证包 ID 或协商包 ID LsaHandle 全局变量 L32pInitLsa L32pInitLsa ,我们新分配的身份验证缓冲区、身份验证缓冲区的长度和其他参数。

Uncovering RPC Servers through Windows API Analysis

A sneak peek into SspiCli!SspipLogonUser and NdrClientCall3
先睹为快!SspipLogonUser 和 NdrClientCall3

Stepping into the SspipLogonUser function, we can see it’s almost a wrapper function with an “if else” statement. A global variable named SSPISRV_SecpLsaInprocDispatch is checked to see if it has been assigned a value; this is a check to see if the function is called inside of the lsass.exe process. In this scenario, it will redirect the execution flow to the NdrClientCall3.
进入 SspipLogonUser 函数,我们可以看到它几乎是一个带有“if else”语句的包装函数。检查名为的 SSPISRV_SecpLsaInprocDispatch 全局变量以查看是否已为其赋值;这是检查是否在 lsass.exe 进程内部调用函数。在这种情况下,它会将执行流重定向到 . NdrClientCall3

Uncovering RPC Servers through Windows API Analysis

NdrClientCall3 is a powerful function that allows the developer to make a call to an RPC server without worrying about all the parameters packed behind the scene, But which RPC server/interface is this function trying to call? From the function prototype of NdrClientCall3, we know the first argument is MIDL_STUBLESS_PROXY_INFO. A quick search lead us to Microsoft Rust documentation on this type (you can also find it in the RPCNDR.h file shipped with Windows SDK).
NdrClientCall3 是一个强大的函数,允许开发人员调用 RPC 服务器,而不必担心幕后打包的所有参数,但是这个函数试图调用哪个 RPC 服务器/接口?从 NdrClientCall3 的函数原型中,我们知道第一个参数是 MIDL_STUBLESS_PROXY_INFO 。快速搜索引导我们找到Microsoft这种类型的 Rust 文档(您也可以在 Windows SDK 附带的 RPCNDR.h 文件中找到它)。

Uncovering RPC Servers through Windows API Analysis

The first field of MIDL_STUBLESS_PROXY_INFO is called MIDL_STUB_DESC. We can take it apart further by right-clicking on the field name.
的第一个 MIDL_STUBLESS_PROXY_INFO 字段称为 MIDL_STUB_DESC 。我们可以通过右键单击字段名称来进一步拆开它。

Uncovering RPC Servers through Windows API Analysis

As the description of MIDL_STUB_DESC from official Microsoft documentation says about the first field RpcInterfaceInformation: “For a nonobject RPC interface on the server-side, it points to an RPC server interface structure. On the client side, it points to an RPC client interface structure. It is null for an object interface.” Since we are not inside of an RPC server binary but rather an RPC client that’s making a call to an RPC server, the RpcInterfaceInformation pointer contains a pointer to the RPC client interface structure. Note that in the description, it mentioned: “The data structure is defined in the header file Rpcdcep.h. See the header file for syntax block and member definitions.” This will come in handy for the next step.
正如官方Microsoft文档中对MIDL_STUB_DESC的描述中关于第一个字段 RpcInterfaceInformation :“对于服务器端的非对象 RPC 接口,它指向 RPC 服务器接口结构。在客户端,它指向 RPC 客户端接口结构。对于对象接口,它为空。由于我们不在 RPC 服务器二进制文件中,而是在调用 RPC 服务器的 RPC 客户端内部,因此 RpcInterfaceInformation 指针包含指向 RPC 客户端接口结构的指针。请注意,在描述中,它提到:“数据结构在头文件 Rpcdcep.h 中定义。有关语法块和成员定义,请参阅头文件。这将在下一步派上用场。

Double-click the sspirpc_Proxyinfo in IDA to verify.
双击 IDA 中的 进行 sspirpc_Proxyinfo 验证。

Uncovering RPC Servers through Windows API Analysis

We will see the other window jump to the .rdata section.
我们将看到另一个窗口跳转到该 .rdata 部分。

Uncovering RPC Servers through Windows API Analysis

Next, click on sspirpc_StubDesc@@3U_MIDL_STUB_DESC@@B, as it is the pointer to the MIDL_STUB_DESC struct.
接下来,单击 sspirpc_StubDesc@@3U_MIDL_STUB_DESC@@B ,因为它是指向结构的 MIDL_STUB_DESC 指针。

Uncovering RPC Servers through Windows API Analysis

Once we arrive at the MIDL_STUB_DESC struct, click on unk_7FF86CF454B0, as it is the pointer pointing to RpcInterfaceInformation.
到达 MIDL_STUB_DESC 结构后,单击 unk_7FF86CF454B0 ,因为它是指向的指针 RpcInterfaceInformation 。

Uncovering RPC Servers through Windows API Analysis
Pointer to RpcInterfaceInformation 指向 RpcInterfaceInformation 的指针
Uncovering RPC Servers through Windows API Analysis

Now, remember the previous Microsoft documentation on fields within the RpcInterfaceInformation? What if we want to find additional useful information about RpcInterfaceInformation such as what exact fields are in structures like RPC_SYNTAX_IDENTIFIERPRPC_DISPATCH_TABLEPRPC_PROTSEQ_ENDPOINT, and what the pointer InterpreterInfo points toward? Recall in the documentation, it mentioned that we can find details in Rpcdcep.h, which ships within the Windows SDK kit.
现在,还记得Microsoft前面关于 ? RpcInterfaceInformation 如果我们想找到其他有用的信息, RpcInterfaceInformation 例如结构中的确切字段,如、、 RPC_SYNTAX_IDENTIFIER PRPC_PROTSEQ_ENDPOINT 、 PRPC_DISPATCH_TABLE 以及指针 InterpreterInfo 指向什么,该怎么办?回想一下,在文档中,它提到我们可以在 Rpcdcep.h 中找到详细信息,该工具包包含在 Windows SDK 工具包中。

I opened it in my favorite text editor (Visual Studio) code and inspected the content of Rpcdcep.h. We can search RpcInterfaceInformation and see some references to the name.
我在我最喜欢的文本编辑器(Visual Studio)代码中打开了它,并检查了. Rpcdcep.h 我们可以搜索 RpcInterfaceInformation 并查看对该名称的一些引用。

Uncovering RPC Servers through Windows API Analysis

The RpcInterfaceInformation appears to be a type of RPC_SERVER_INTERFACE in this case. To jump to the definition, hold Crtl and left-click on the RPC_SERVER_INTERFACE name.
在这种情况下,RpcInterfaceInformation 似乎是一种类型 RPC_SERVER_INTERFACE 。要跳转到定义,请按住 Crtl 并左键单击 RPC_SERVER_INTERFACE 名称。

Uncovering RPC Servers through Windows API Analysis

Recall the ones from Microsoft documentation says: “For a nonobject RPC interface on the server-side, it points to an RPC server interface structure. On the client side, it points to an RPC client interface structure”? This explains it.
回想一下Microsoft文档中的那些:“对于服务器端的非对象 RPC 接口,它指向 RPC 服务器接口结构。在客户端,它指向 RPC 客户端接口结构“?这就解释了。

Uncovering RPC Servers through Windows API Analysis

If we go back to the Visual Studio code and inspect the _RPC_SYNTAX_IDENTIFIER structure, we can see that it not only contains a globally unique identifier (GUID) but also an RPC_VERSION which contains MajorVersion and MinorVersion.
如果我们回到Visual Studio代码并检查 _RPC_SYNTAX_IDENTIFIER 结构,我们可以看到它不仅包含一个全局唯一标识符(GUID),还 RPC_VERSION 包含一个包含 MajorVersion 和 MinorVersion .

Uncovering RPC Servers through Windows API Analysis
Uncovering RPC Servers through Windows API Analysis

If we do the same thing on PRPC_DISPATCH_TABLE, the pointer will point to a structure named RPC_DISPATCH_TABLE. See below for an example.
如果我们在 上 PRPC_DISPATCH_TABLE 做同样的事情,指针将指向一个名为 RPC_DISPATCH_TABLE .有关示例,请参见下文。

Uncovering RPC Servers through Windows API Analysis

How about PRPC_PROTSEQ_ENDPOINT? Aha! PRPC_PROTSEQ_ENDPOINT contains RpcProtocolSequence and Endpoint, which makes sense because both the RPC client/server runtime library have to know which protocol sequence and port to use for connections.
怎么样 PRPC_PROTSEQ_ENDPOINT ?啊哈! PRPC_PROTSEQ_ENDPOINT 包含 RpcProtocolSequence 和 Endpoint ,这是有意义的,因为 RPC 客户端/服务器运行时库都必须知道用于连接的协议序列和端口。

Uncovering RPC Servers through Windows API Analysis

Now there are only two unknown fields within the structure: DefaultManagerEpv and InterpreterInfo. We can use the help from IDA to look at it within the DLL.
现在结构中只有两个未知字段: DefaultManagerEpv 和 InterpreterInfo 。我们可以使用 IDA 的帮助在 DLL 中查看它。

With the help of IDA…
在IDA的帮助下…

Going back to IDA, we can manually lay out the memory structures from the knowledge obtained previously. We can click on an address (in this case, it’s 00000001800295E4) and hit Alt + Q to apply a local type to the structure known to IDA.
回到 IDA,我们可以根据之前获得的知识手动布置内存结构。我们可以单击一个地址(在本例中为 00000001800295E4 ),然后点击将 Alt + Q 本地类型应用于 IDA 已知的结构。

Uncovering RPC Servers through Windows API Analysis

With the field of RPC_CLIENT_INTERFACE identified, proceed to check out DefaultManagerEpv and InterpreterInfo.
使用已识别的 RPC_CLIENT_INTERFACE 字段,继续签出 DefaultManagerEpv 和 InterpreterInfo 。

Uncovering RPC Servers through Windows API Analysis

Since we are still in SspiCli.dll’s IDA view, only the InterpreterInfo field was initialized. We can double-click on ?sspirpc_ServerInfo@@3U_MIDL_SERVER_INFO_@@B to jump to address offset.
由于我们仍在 SspiCli.dll 的 IDA 视图中,因此仅初始化了 InterpreterInfo 字段。我们可以双击 ?sspirpc_ServerInfo@@3U_MIDL_SERVER_INFO_@@B 跳转到地址偏移量。

Uncovering RPC Servers through Windows API Analysis

From Microsoft public symbol that IDA has downloaded for us when first started, IDA has identified this struct as _MIDL_SERVER_INFO_. A quick search revealed that this struct is defined within another file named rpcndr.h, which is also shipped within Windows SDK.
从 IDA 首次启动时为我们下载Microsoft公共符号中,IDA 已将此结构标识为 _MIDL_SERVER_INFO_ 。快速搜索显示,此结构是在另一个名为 rpcndr.h 的文件中定义的,该文件也包含在 Windows SDK 中。

Uncovering RPC Servers through Windows API Analysis

We can manually map the memory layout according to the structure definition again in IDA.
我们可以在 IDA 中再次根据结构定义手动映射内存布局。

Uncovering RPC Servers through Windows API Analysis

If we click on the off_180029740, we will see that the memory contains one function offset. I will not describe what the function does, as it is not relevant to this blog post.
如果我们点击 off_180029740 ,我们将看到内存包含一个函数偏移量。我不会描述该函数的作用,因为它与这篇博文无关。

Uncovering RPC Servers through Windows API Analysis

RPC Server Side RPC 服务器端

Up to this point, we simply looked at the RPC usage from the SspiCli.dll (i.e., the RPC client)…but what about the server side? Remember: our original goal is to identify which function is invoked on the RPC server side!
到目前为止,我们只是从 SspiCli.dll (即 RPC 客户端)查看 RPC 使用情况…但是服务器端呢?请记住:我们最初的目标是确定在 RPC 服务器端调用哪个函数!

For the RPC runtime library to make a call to the RPC server, the RPC_CLIENT_INTERFACE.InterfaceId.SyntaxGUID has to be the same both on the client side and the server side. We can pull the bytes together from RPC_CLIENT_INTERFACE.InterfaceId.SyntaxGUIDand put them into PowerShell.
要使 RPC 运行时库调用 RPC 服务器, RPC_CLIENT_INTERFACE.InterfaceId.SyntaxGUID 客户端和服务器端的调用必须相同。我们可以将 RPC_CLIENT_INTERFACE.InterfaceId.SyntaxGUID 字节放在一起并将它们放入PowerShell中。

Uncovering RPC Servers through Windows API Analysis

A quick search on 4f32adc8–6052–4a04–8701–293ccf2096f0 reveals the RPC interface belongs to an RPC server hosted from SspiSrv.dll.
快速搜索 会 4f32adc8–6052–4a04–8701–293ccf2096f0 发现 RPC 接口属于托管自 的 SspiSrv.dll RPC 服务器。

Recall in the NdrClientCall3 function prototype, the second argument is opNum, which we can think of as the index for a function within a function table that the RPC server stored within the .rdata section. The RPC runtime library, which will handle all the parameters packing and find the process hosting the SspiSrv.dll and its interface(s), passes the arguments to the process RPC runtime library along with the interface information. The RPC runtime library on the RPC server side unpacks all arguments and invokes the function the index points to within the RPC server function table.
回想一下,在 NdrClientCall3 函数原型中,第二个参数是 opNum,我们可以将其视为 RPC 服务器存储在 .rdata 该部分中的函数表中函数的索引。RPC 运行时库将处理所有参数打包并查找托管的进程 SspiSrv.dll 及其接口,将参数与接口信息一起传递给进程 RPC 运行时库。RPC 服务器端的 RPC 运行时库解压缩所有参数,并在 RPC 服务器函数表中调用索引指向的函数。

We now know the NdrClientCall3 in SspipLogonUser calls to SspiSrv.dll and the opNum is 12. To search for the interface GUID within SspiSrv.dll, open a new instance of IDA, load SspiSrv.dll, press Alt + B to open up the binary search window.
我们现在知道 NdrClientCall3 in SspipLogonUser 调用 和 SspiSrv.dll opNum is 12。要在 SspiSrv.dll 中搜索接口 GUID,请打开 IDA 的新实例,加载 SspiSrv.dll ,按 Alt + B 打开二进制搜索窗口。

Uncovering RPC Servers through Windows API Analysis

Next, we can just put the first four bytes of the InterfaceId into the String box and hit “OK”.
接下来,我们可以将前 InterfaceId 四个字节放入 String 框中并点击“确定”。

Uncovering RPC Servers through Windows API Analysis

In this example, IDA found one instance of the byte sequence within the SspiSrv.dll.
在此示例中,IDA 在 . SspiSrv.dll

Uncovering RPC Servers through Windows API Analysis

Double-clicking on it will show us something we are already seen.
双击它将向我们显示我们已经看到的东西。

Uncovering RPC Servers through Windows API Analysis

This is the RpcInterfaceInformation pointer within SspiSrv.dll, which looks the same as the one we saw previously in SspiCli.dll. How can we find the function table for the interface, now that we are inside the RPC server? The answer lies within the _RPC_SERVER_INTERFACE structure.
这是 中的 SspiSrv.dll 指针,它看起来与我们之前在 SspiCli.dll 中看到的 RpcInterfaceInformation 指针相同。现在我们在 RPC 服务器内部,我们如何找到接口的功能表?答案就在结构中 _RPC_SERVER_INTERFACE 

In IDA, we can do a cross-reference by hovering over unk_7ff86CF454B0 and pressing x. From there, we can see the code where this particular memory address was referenced.
在 IDA 中,我们可以通过将鼠标悬停在 unk_7ff86CF454B0 x .从那里,我们可以看到引用此特定内存地址的代码。

Uncovering RPC Servers through Windows API Analysis

In this example, the address was referenced twice in two different sections of the code: once in the .text section within the function SspiSrvInitialize and once within .rdata. We recall that the RpcInterfaceInformation is a field of the MIDL_STUB_DESC structure, so it is likely the second cross-reference here points to the MIDL_STUB_DESC. Double-clicking on the .rdata cross-reference should bring us to a new view in the window, like so:
在此示例中,地址在代码的两个不同部分中被引用了两次:一次在函数 SspiSrvInitialize 内的 .text 部分中,一次在 .rdata 中。我们回想一下,这是 RpcInterfaceInformation MIDL_STUB_DESC 结构的一个字段,所以这里的第二个交叉引用很可能指向 MIDL_STUB_DESC .双击 .rdata 交叉引用应该会将我们带到窗口中的新视图,如下所示:

Uncovering RPC Servers through Windows API Analysis

The MIDL_user_allocate and MIDL_user_free gives it away that it is indeed our MIDL_STUB_DESC structure.
并 MIDL_user_allocate MIDL_user_free 透露它确实是我们的结构 MIDL_STUB_DESC 

Cool, now let’s walk through it again following the previous steps.
很酷,现在让我们按照前面的步骤再次浏览它。

  1. Double-click on unk_7FF86CF454B0 o into the RpcInterfaceInformation structure
    双击 unk_7FF86CF454B0 o 进入 RpcInterfaceInformation 结构
  2. Double-click on off_7FF86CF443D0 to find RPC_SERVER_INTERFACE.InterpreterInfo (Note: the structure will now be RPC_SERVER_INTERFACE instead of RPC_CLIENT_INTERFACE)
    双击 off_7FF86CF443D0 查找 RPC_SERVER_INTERFACE.InterpreterInfo (注意:结构现在将 RPC_SERVER_INTERFACE 代替 RPC_CLIENT_INTERFACE )
Uncovering RPC Servers through Windows API Analysis

3. Once we arrived at RPC_SERVER_INTERFACE.InterpreterInfo we can find MIDL_SERER_INFO.Dispatchable by double-clicking off_7FF86CF44180
3.到达后 RPC_SERVER_INTERFACE.InterpreterInfo ,我们可以通过双击 off_7FF86CF44180 找到 MIDL_SERER_INFO.Dispatchable

Uncovering RPC Servers through Windows API Analysis

We should now see a bunch of different methods compared to what was on the client side DLL.
与客户端 DLL 上的方法相比,我们现在应该看到一堆不同的方法。

Uncovering RPC Servers through Windows API Analysis

Now we find the dispatch table and the last question remains: which one are we calling from LogonUserA? Well, the answer is easy to find! Remember the OpNum used in the call NdrClientCall3? This is the index used by the RPC runtime library to determine which function the RPC client is trying to call. We can simply count to the 13th function (Note: the index starts at 0) function in the function table which is SspiLogonUser. As the function name suggests, we believe it has something to do with LogonUserA. If you don’t believe me, you can set up a breakpoint with a debugger and find out for yourself.
现在我们找到了调度表,最后一个问题仍然存在:我们从哪一个调用 LogonUserA ?好吧,答案很容易找到!还记得通话 NdrClientCall3 中使用的吗 OpNum ?这是 RPC 运行时库用来确定 RPC 客户端尝试调用哪个函数的索引。我们可以简单地计数到函数表中的第 13 个函数(注意:索引从 0 开始)函数,即 SspiLogonUser 。正如函数名称所暗示的那样,我们认为它与 有关 LogonUserA 。如果你不相信我,你可以用调试器设置一个断点,然后自己找出来。

Finally 最后

We have arrived at the end of this blog post. If you find any mistakes, please DM me on X and I will adjust accordingly! This blog post is meant for people like me who are interested in reverse engineering and are looking for a hand-holding article that covers some of the basic IDA usage and reverse engineering methodologies.
我们已经到达了这篇博文的末尾。如果您发现任何错误,请在X上给我发DM,我会做出相应的调整!这篇博文是为像我这样对逆向工程感兴趣的人准备的,他们正在寻找一篇涵盖一些基本IDA用法和逆向工程方法的手把手文章。

Credits 学分

I want to give my thanks to all the people and blog posts who helped me better understand RPC! Additionally, I want to give some shoutouts to many of my colleagues such as Evan, and Lee at SpecterOps, and friends who helped me to understand the topic.
我要感谢所有帮助我更好地了解RPC的人和博客文章!此外,我想向我的许多同事致敬,例如SpecterOps的Evan和Lee,以及帮助我理解这个主题的朋友。

https://specterops.io/wp-content/uploads/sites/3/2022/06/RPC_for_Detection_Engineers.pdf
https://posts.specterops.io/wmi-internals-part-3-38e5dad016be
https://csandker.io/2021/02/21/Offensive-Windows-IPC-2-RPC.html
https://www.fortinet.com/blog/threat-research/the-case-studies-of-microsoft-windows-remote-procedure-call-serv

 

原文始发于Kai Huang:Uncovering RPC Servers through Windows API Analysis

版权声明:admin 发表于 2023年10月20日 上午8:58。
转载请注明:Uncovering RPC Servers through Windows API Analysis | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...