出于这些目的,我在 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_DIRECT
和SCSI_PASS_THROUGH
)DeviceIoControl
,所以以上并不是你能做的最复杂的事情。
另一方面,我还挂钩了诸如打开文件(或注册表项)之类的东西,并保留了返回句柄的列表以及它们的字符串形式。通过这种方式,我可以实现非常复杂的人类可读的日志记录场景。
基本原理
嵌套字典之类的东西存在的原因是它可以很容易地扩展到我喜欢的任何 DLL 中挂钩任何函数。当然,它也可以硬编码,但我曾遇到过挂钩数十个函数的场景。