如何查看传递给 RegOpenKeyEx 的参数,并设置条件断点?

逆向工程 调试符号 风袋
2021-06-27 04:02:19

我将 WinDbg 附加到我没有源代码的进程。我已经设置了一个断点bm ADVAPI32!_RegOpenKeyExW@20dv 的输出是:

Unable to enumerate locals, HRESULT 0x80004005
Private symbols (symbols.pri) are required for locals.
Type ".hh dbgerr005" for details.

kP 的输出是:

0:000> kP
ChildEBP RetAddr  
001ae174 5b73a79c ADVAPI32!_RegOpenKeyExW@20
001ae1cc 5b77bb20 msenv!?ReadSecurityAddinSetting@@YG_NPAGK@Z+0x8a
001ae468 5b781aad msenv!?    QueryStatusCmd@CVSCommandTarget@@QAEJPBVCIcmdGuidCmdId@@PBU_GUID@@KQAU_tagOLECMD@@PAU_tagOLECMDTEXT@@@Z+0x254
001ae49c 5b786073 msenv!?IsCommandVisible@CVSShellMenu@@QAEJPBVCIcmdGuidCmdId@@_N@Z+0xbf
001ae4e4 5b785fd2 msenv!?IsCommandVisible@CSurfaceCommandingSupport@@UAGJABU_COMMANDTABLEID@@_NPAH@Z+0xa0
. . .

我该怎么做才能查看传递的参数的值(尤其是第二个:LPCTSTR lpSubKey)?另外,我该怎么做才能根据值设置条件断点?

我有 Visual Studio 调试器和 WinDbg。我也愿意尝试其他工具。

4个回答

这在 WinDbg 中是可行的,但语法不是最好的。如果您只是想快速获得结果,其他答案可能会更好。也就是说,这就是你要做的。

RegOpenKeyExW 是一个 stdcall 函数,看起来您使用的是 32 位版本的 Windows,因此当您输入该函数时,第一个参数将是 at ESP+4,第二个是 atESP+8等。它的签名是:

LONG WINAPI RegOpenKeyEx(
  _In_        HKEY hKey,
  _In_opt_    LPCTSTR lpSubKey,
  _Reserved_  DWORD ulOptions,
  _In_        REGSAM samDesired,
  _Out_       PHKEY phkResult
);

因此,ESP+8您将找到一个指向以空字符结尾的 Unicode 字符串 ( lpSubKey)的指针您可以通过在 RegOpenKeyExW 上设置一个无条件断点,然后使用它du poi(esp+8)来打印字符串来测试这一点

要进行下一部分——当 的值lpSubKey等于某个模式时停止,您可以查看MSDN 的文档,其中包含执行此操作的示例。我目前无法访问 Windows 机器,但以下内容应该可以满足您的需求(改编自 MSDN 示例):

bp advapi32!RegOpenKeyExW "$$<c:\\commands.txt"

然后c:\commands.txt使用以下内容创建

.if (poi(esp+8) != 0) { as /mu ${/v:SubKey} poi(esp+8) } .else { ad /q ${/v:SubKey} }
.if ($spat(@"${SubKey}", "Foo*") == 0)  { gc } .else { .echo SubKey }

您可以"Foo*"用任何有效的字符串通配符替换上面的内容

分解一下,第一个.if设置一个别名,其名称SubKey等于 unicode 字符串的内容esp+8第二行将该字符串与模式匹配"Foo*"如果不匹配,则gc用于继续执行而不停止。如果匹配,则.echo SubKey打印出字符串,然后中断。

出于这些目的,我在 PaiMei特别是 PyDbg 方面获得了最佳体验“Grayhat Python”这本书引起了我的注意,我决定试一试。

剧本

这是我在动态分析中一直使用的一个有点通用的骨架() 努力。我已经对其进行了调整,因此它会挂钩您有兴趣挂钩的函数的 Unicode 版本。

但是,答案可能并不准确,因为您可以有条件地实现中断,但您必须将交互式调试器部分添加到脚本中。如果您想尝试而不是继续,则必须通过修改行来告诉调试器这样做return DBG_CONTINUE

import sys
import ctypes
import traceback
try:
    from pydbg import *
    from pydbg.defines import *
    from utils import hooking
except:
    print "ERROR: you need pydbg and utils.hooking from PAIMEI."
    sys.exit(-1)

reg = None

class reg_pydbg(pydbg):
    @staticmethod
    def __getlen(mbi, address):
        # What's the maximum number of bytes we can read?
        _maxlen = 64*1024
        absmaxlen = (mbi.BaseAddress + mbi.RegionSize) - address
        if absmaxlen > _maxlen:
            return _maxlen
        return absmaxlen

    def rootkey_const(self, key):
        if 0x80000000 == key:
            return "HKCR"
        elif 0x80000001 == key:
            return "HKCU"
        elif 0x80000002 == key:
            return "HKLM"
        elif 0x80000003 == key:
            return "HKU"
        elif 0x80000004 == key:
            return "HKEY_PERFORMANCE_DATA"
        elif 0x80000050 == key:
            return "HKEY_PERFORMANCE_TEXT"
        elif 0x80000060 == key:
            return "HKEY_PERFORMANCE_NLSTEXT"
        elif 0x80000005 == key:
            return "HKEY_CURRENT_CONFIG"
        elif 0x80000006 == key:
            return "HKEY_DYN_DATA"
        elif 0x80000007 == key:
            return "HKEY_CURRENT_USER_LOCAL_SETTINGS"
        return "0x%08X" % (key)

    def readmem(self, address, len = 0):
        try:
            mbi = self.virtual_query(address)
        except:
            return None, "%08X <invalid ptr>" % (address)

        if mbi.Protect & PAGE_GUARD: # no way to display contents of a guard page
            return None, "%08X <guard page>" % (address)

        if 0 == len: # try to make a good guess then
            len = self.__getlen(mbi, address)

        try:
            explored = self.read_process_memory(address, len)
        except:
            return None, "%08X <ReadProcessMemory failed>" % (address)

        return explored, None

    def readstring(self, address, unicodeHint = False, returnNone = False):
        if 0 == address:
            if returnNone:
                return None
            return "<nullptr>"

        explored, retval = self.readmem(address)

        if not explored:
            if returnNone:
                return None
            return retval

        explored_string = None

        if not unicodeHint:
            explored_string = self.get_ascii_string(explored)

        if not explored_string:
            explored_string = self.get_unicode_string(explored)

        if not explored_string:
            explored_string = self.get_printable_string(explored)

        return explored_string

def exit_RegOpenKeyExW(dbg, args, ret):
    keyname = dbg.readstring(args[1], True)
    print "RegOpenKeyExW(%s, %s, ...) -> %s (%d)" % (dbg.rootkey_const(args[0]), keyname, ctypes.FormatError(ret), ret)
    return DBG_CONTINUE

class reghooks:
    fct2hook = {
        "advapi32.dll" :
            {
            "RegOpenKeyExW"                     : { "args" : 5, "entry" : None, "exit" : exit_RegOpenKeyExW },
            },
    }

    hooked = {}
    hookcont = None
    dbg = None

    def __init__ (self, dbg):
        self.hookcont = hooking.hook_container()
        self.hooked = {}
        self.dbg = dbg
        dbg.set_callback(LOAD_DLL_DEBUG_EVENT, self.handler_loadDLL)

    def hookByDLL(self, dll):
        if not dll.name.lower() in self.hooked:
            for key,value in self.fct2hook.items():
                if key.lower() == dll.name.lower():
                    self.hooked[dll.name.lower()] = 1
                    print "%s at %08x" % (dll.name, dll.base)
                    for func,fctprops in value.items():
                        entry = None; exit = None; args = 0
                        if "entry" in fctprops and None != fctprops["entry"]:
                            print "\tentry hook " + func
                            entry = fctprops["entry"]
                        if "exit" in fctprops and None != fctprops["exit"]:
                            print "\texit hook " + func
                            exit = fctprops["exit"]
                        if "args" in fctprops and None != fctprops["args"]:
                            args = fctprops["args"]
                        if None != entry or None != exit:
                            funcaddr = self.dbg.func_resolve(dll.name, func)
                            self.hookcont.add(self.dbg, funcaddr, args, entry, exit)
        else:
            self.hooked[dll.name.lower()] += 1
        return

    @staticmethod
    def handler_loadDLL(dbg):
        global reg
        dbg.hide_debugger()
        last_dll = dbg.get_system_dll(-1)
        reg.hookByDLL(last_dll)
        return DBG_CONTINUE

def main():
    dbg = reg_pydbg()
    dbg.load("C:\\Windows\\regedit.exe")
    global reg
    reg = reghooks(dbg)
    dbg.run()

if __name__ == "__main__":
    main()

示例输出

>C:\Python26\python.exe hookreg.py
advapi32.dll at 75840000
        exit hook RegOpenKeyExW
RegOpenKeyExW(HKCU, Software\Microsoft\Windows\CurrentVersion\Applets\Regedit, ...) -> The operation completed successfully. (0)
RegOpenKeyExW(HKLM, SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink, ...) -> The operation completed successfully. (0)
RegOpenKeyExW(HKLM, SOFTWARE\Microsoft\Windows NT\CurrentVersion\LanguagePack\DataStore_V1.0, ...) -> The operation completed successfully. (0)
RegOpenKeyExW(HKLM, SOFTWARE\Microsoft\Windows NT\CurrentVersion\LanguagePack\SurrogateFallback, ...) -> The operation completed successfully. (0)

解释

重要的部分是:

"RegOpenKeyExW" : { "args" : 5, "entry" : None, "exit" : exit_RegOpenKeyExW },

和功能exit_RegOpenKeyExW您还可以修改上面的内容,使其仅在进入时挂钩RegOpenKeyExW而不是在退出时挂钩,或者两者都做。取决于你想要达到的目标。

我已经使用这种方法来解码通过 发送的缓冲区(SCSI_PASS_THROUGH_DIRECTSCSI_PASS_THROUGHDeviceIoControl,所以以上并不是你能做的最复杂的事情。

另一方面,我还挂钩了诸如打开文件(或注册表项)之类的东西,并保留了返回句柄的列表以及它们的字符串形式。通过这种方式,我可以实现非常复杂的人类可读的日志记录场景。

基本原理

嵌套字典之类的东西存在的原因是它可以很容易地扩展到我喜欢的任何 DLL 中挂钩任何函数。当然,它也可以硬编码,但我曾遇到过挂钩数十个函数的场景。

使用 OllyDbg 你可以这样做

F:\odbg110>regedit & tasklist /fi "imagename eq rege*"

Image Name                   PID Session Name     Session#    Mem Usage
========================= ====== ================ ======== ============
regedit.exe                 2820 Console                 0      2,512 K

F:\odbg110>ollydbg -P 2820

F:\odbg110>

在 OllyDbg 中点击Alt+ g(goto) 输入advapi32.RegOpenKeyExW并点击Enter. 如果您使用的是 Windows XP SP3,它应该如下所示:

77DD6AAF ADVAPI32.RegOpenKeyExW U>/$  8BFF          MOV     EDI, EDI

Hit Ctrl+ F4(条件日志断点)

在“条件”编辑框中输入 say(没有通配符需要有效的转义模式)

UNICODE [[esp+8]]  ==  "system\\CurrentControlSet\\Services\\Beep" 

在“解释”编辑框中输入您想要的任何解释

log RegOpenKeyExW subkey

在表达式编辑框中输入

[esp+8]

在“解码值”中选择“指向Unicode字符串的指针”

pause program on condition
log value of expression always
log function arguments on condition 

(所有这些都可以在不暂停调试对象的情况下完成)(动态中断插入)

现在regedit去玩一下,然后选择“Beep Service”

OllyDbg 会中断并且也会记录函数参数;并且您将无停顿地记录所有其他字符串,如下所示

Log data
Address    Message
77DD6AAF   COND: log reop Subkey = 0007FBC8 "SYSTEM\CurrentControlSet\Services\aswMonFlt"
77DD6AAF   COND: log reop Subkey = 0007FBC8 "SYSTEM\CurrentControlSet\Services\aswRdr"
77DD6AAF   COND: log reop Subkey = 0007FBC8 "SYSTEM\CurrentControlSet\Services\aswRvrt"
77DD6AAF   COND: log reop Subkey = 0007FBC8 "SYSTEM\CurrentControlSet\Services\aswSnx"
77DD6AAF   COND: log reop Subkey = 0007FBC8 "SYSTEM\CurrentControlSet\Services\aswSP"
77DD6AAF   COND: log reop Subkey = 0007FBC8 "SYSTEM\CurrentControlSet\Services\aswTdi"
77DD6AAF   COND: log reop Subkey = 0007FBC8 "SYSTEM\CurrentControlSet\Services\aswVmm"
77DD6AAF   COND: log reop Subkey = 0007FBC8 "SYSTEM\CurrentControlSet\Services\avast! Antivirus"
77DD6AAF   COND: log reop Subkey = 0007FBC8 "SYSTEM\CurrentControlSet\Services\Beep"
77DD6AAF   CALL to RegOpenKeyExW from regedit.01008B23
             hKey = HKEY_LOCAL_MACHINE
             Subkey = "SYSTEM\CurrentControlSet\Services\Beep"
             Reserved = 0
             Access = 2000000
             pHandle = regedit.01019098
77DD6AAF   Conditional breakpoint at ADVAPI32.RegOpenKeyExW

下载Rohitab 的 API Monitor并在RegOpenKeyEx上设置 API 捕获过滤器以下只是其工作原理及其提供的信息的示例屏幕截图。

根据您的描述,这应该完全符合您的需要。

在此处输入图片说明

在此处输入图片说明