-- 获取内存窗口中的反汇编视图对象
local disasmView = getMemoryViewForm().DisassemblerView
-- 定义64位和32位寄存器对应的配对关系(R8-R15没有32位子寄存器)
local registerPairs = {
{"RAX", "EAX"}, {"RBX", "EBX"}, {"RCX", "ECX"}, {"RDX", "EDX"},
{"RSI", "ESI"}, {"RDI", "EDI"}, {"RBP", "EBP"}, {"RSP", "ESP"},
{"R8", ""}, {"R9", ""}, {"R10", ""}, {"R11", ""},
{"R12", ""}, {"R13", ""}, {"R14", ""}, {"R15", ""}
}
-- 创建一个状态表,用于记录每个寄存器是否需要高亮显示,初始化为全部false
local highlightStates = {}
for _, pair in ipairs(registerPairs) do
highlightStates[pair[1]] = false
end
-- 创建主菜单项,添加到反汇编视图的右键弹出菜单中
local mainItem = createMenuItem(disasmView.PopupMenu)
mainItem.Caption = "寄存器高亮" -- 菜单文字
mainItem.ImageIndex = 38 -- 菜单图标索引
disasmView.PopupMenu.Items.add(mainItem)
-- 存储子菜单项的引用表,方便后续操作
local subMenuItems = {}
for _, pair in ipairs(registerPairs) do
local reg64, reg32 = pair[1], pair[2]
-- 为每个寄存器创建子菜单项
local subItem = createMenuItem(mainItem)
-- 设置菜单显示文字(有32位寄存器的显示为"RAX/EAX"格式)
if reg32 and reg32 ~= "" then
subItem.Caption = string.format("%s/%s", reg64, reg32)
else
subItem.Caption = reg64
end
subItem.Checked = false -- 初始未选中状态
mainItem.add(subItem) -- 将子菜单添加到主菜单
subMenuItems[reg64] = subItem -- 保存引用
end
-- 核心高亮函数:在反汇编后处理文本,为寄存器添加颜色标记
local function highlightDisasm(sender, address, LastDisassembleData, result, description)
for _, pair in ipairs(registerPairs) do
local reg64, reg32 = pair[1], pair[2]
-- 如果该寄存器被标记为高亮
if highlightStates[reg64] then
-- 创建大小写不敏感的正则模式(如"RAX"变为"[Rr][Aa][Xx]")
local pattern64 = reg64:gsub("%a", function(c)
return "[" .. c:upper() .. c:lower() .. "]"
end)
-- 在参数字符串中替换为带颜色的格式(B=背景色,C=前景色,N=重置)
LastDisassembleData.parameters = LastDisassembleData.parameters:gsub(
pattern64, '{BFFFF00}{C000000}%0{N}'
)
-- 对32位寄存器执行相同的操作
if reg32 and reg32 ~= "" then
local pattern32 = reg32:gsub("%a", function(c)
return "[" .. c:upper() .. c:lower() .. "]"
end)
LastDisassembleData.parameters = LastDisassembleData.parameters:gsub(
pattern32, '{BFFFF00}{C000000}%0{N}'
)
end
end
end
return result, description
end
-- 为每个子菜单项设置点击事件处理函数
for reg64, menuItem in pairs(subMenuItems) do
menuItem.OnClick = function(sender)
sender.Checked = not sender.Checked -- 切换选中状态
if disasmView then
disasmView.Repaint() -- 刷新反汇编视图
end
-- 更新寄存器高亮状态
highlightStates[reg64] = not highlightStates[reg64]
end
end
-- 将高亮函数注册到反汇编器的PostDisassemble事件,使其在每次反汇编后自动调用
getVisibleDisassembler().OnPostDisassemble = highlightDisasm
[Lua] 纯文本查看复制代码
-- 配置选项
local CONFIG = {
MIN_LENGTH = 2, -- 特征码最小长度
MAX_LENGTH = 128, -- 特征码最大长度
WILDCARD_FORMAT = '??', -- 通配符格式
ENABLE_CACHING = true, -- 启用缓存
MAX_CACHE_SIZE = 100, -- 缓存最大条目数
}
-- 缓存系统
local cache = {
moduleInfo = {},
scanResults = {}
}
-- LRU缓存辅助函数
local function addToCache(table, key, value, maxSize)
if maxSize and #table >= maxSize then
for k in pairs(table) do
table[k] = nil
break
end
end
table[key] = value
end
-- 获取模块信息
function getModuleInfo(addr)
if CONFIG.ENABLE_CACHING and cache.moduleInfo[addr] then
return cache.moduleInfo[addr]
end
local modules = enumModules()
local result = nil
for i = 1, #modules do
local module = modules[i]
local startAddr = module.Address
local endAddr = startAddr + getModuleSize(module.Name)
if addr >= startAddr and addr < endAddr then
result = {
module = module,
endAddr = endAddr
}
break
end
end
if CONFIG.ENABLE_CACHING then
addToCache(cache.moduleInfo, addr, result, CONFIG.MAX_CACHE_SIZE)
end
return result
end
-- 扫描器管理
local scanManager = {
scanner = nil,
foundList = nil,
currentModule = nil
}
local function initScanner(module)
if not scanManager.scanner or scanManager.currentModule ~= module then
if scanManager.scanner then
scanManager.scanner.destroy()
end
if scanManager.foundList then
scanManager.foundList.destroy()
end
scanManager.scanner = createMemScan()
scanManager.foundList = createFoundList(scanManager.scanner)
scanManager.currentModule = module
end
return scanManager.scanner, scanManager.foundList
end
-- 检查特征码唯一性
function checkAOB(pattern, module)
if not pattern or pattern == "" then
return false
end
local cacheKey = module.Name .. "|" .. pattern
if CONFIG.ENABLE_CACHING and cache.scanResults[cacheKey] ~= nil then
return cache.scanResults[cacheKey]
end
local memScanner, memFoundList = initScanner(module)
local base = module.Address
local size = getModuleSize(module.Name)
local endAddr = base + size
local success, result = pcall(function()
memScanner.firstScan(
soExactValue, vtByteArray, rtRounded, pattern, nil,
base, endAddr, "", fsmNotAligned, "", true, false, false, false
)
memScanner.waitTillDone()
memFoundList.initialize()
local count = memFoundList.count
memFoundList.deinitialize()
return count == 1
end)
if not success then
logError("扫描失败: " .. tostring(result))
result = false
end
if CONFIG.ENABLE_CACHING then
addToCache(cache.scanResults, cacheKey, result, CONFIG.MAX_CACHE_SIZE)
end
return result
end
-- 检查是否为前缀字节
function isPrefixByte(byteVal)
return (byteVal >= 0x40 and byteVal <= 0x49) or (byteVal == 0x0F)
end
-- 字符串生成
local function createStringBuilder()
local buffers = {
ce_original = {},
ce_spaced = {},
ida = {},
cescape = {},
mask = {}
}
return {
add = function(byteVal, isWildcard)
local str = isWildcard and CONFIG.WILDCARD_FORMAT or string.format('%02X', byteVal)
-- CE原始格式
buffers.ce_original[#buffers.ce_original + 1] = str
-- CE空格格式
if #buffers.ce_spaced > 0 then
buffers.ce_spaced[#buffers.ce_spaced + 1] = ' '
end
buffers.ce_spaced[#buffers.ce_spaced + 1] = str
-- IDA格式
if isWildcard then
buffers.ida[#buffers.ida + 1] = '?'
buffers.cescape[#buffers.cescape + 1] = '\\x?'
buffers.mask[#buffers.mask + 1] = '?'
else
buffers.ida[#buffers.ida + 1] = str
buffers.cescape[#buffers.cescape + 1] = '\\x' .. str
buffers.mask[#buffers.mask + 1] = 'x'
end
end,
-- 修复:正确移除末尾通配符及其格式
trimTrailingWildcards = function(self)
while #buffers.ce_original > CONFIG.MIN_LENGTH do
local lastByte = buffers.ce_original[#buffers.ce_original]
if lastByte == CONFIG.WILDCARD_FORMAT then
table.remove(buffers.ce_original)
if #buffers.ce_spaced > 0 then
table.remove(buffers.ce_spaced)
if #buffers.ce_spaced > 0 then
table.remove(buffers.ce_spaced)
end
end
table.remove(buffers.ida)
table.remove(buffers.cescape)
table.remove(buffers.mask)
else
break
end
end
end,
build = function()
return {
ce_original = table.concat(buffers.ce_original),
ce_spaced = table.concat(buffers.ce_spaced),
ida = table.concat(buffers.ida, ' '),
cescape = table.concat(buffers.cescape),
mask = table.concat(buffers.mask)
}
end
}
end
-- 处理x64前缀
local function processX64Prefix(base, offset, isX64, builder)
if not isX64 then return offset end
local byteVal = readBytes(base + offset, 1)
if byteVal and isPrefixByte(byteVal) then
builder.add(byteVal, byteVal == 0xCC)
return offset + 1
end
return offset
end
-- 格式化剪贴板文本
local function buildClipboardText(results)
return string.format(
"CE Style\n%s\n\nPEiD Style\n%s\n\nIDA Style\n%s\n\nCode style\n%s %s",
results.ce_original,
results.ce_spaced,
results.ida,
results.cescape,
results.mask
)
end
-- 核心函数:生成模糊特征码
function generateWildcardAOB(addr)
local startTime = os.clock()
local moduleInfo = getModuleInfo(addr)
if not moduleInfo then
showMessage(string.format("错误:地址 0x%X 不属于任何模块", addr))
return nil
end
local currentModule = moduleInfo.module
local isX64 = currentModule.Is64Bit or targetIs64Bit()
local builder = createStringBuilder()
local offset = 0
local totalBytes = 0
while offset < CONFIG.MAX_LENGTH do
-- 处理前缀
local newOffset = processX64Prefix(addr, offset, isX64, builder)
if newOffset ~= offset then
offset = newOffset
totalBytes = totalBytes + 1
end
-- 读取操作码
local opcode = readBytes(addr + offset, 1)
if not opcode then
logError(string.format("无法读取地址: 0x%X", addr + offset))
break
end
-- 获取指令长度
local instSize = getInstructionSize(addr + offset)
if instSize <= 0 then
logError(string.format("无法获取指令长度: 0x%X", addr + offset))
break
end
-- 添加操作码
builder.add(opcode, opcode == 0xCC)
totalBytes = totalBytes + 1
-- 操作数部分:逐个添加并检查唯一性
for i = 1, instSize - 1 do
local operandByte = readBytes(addr + offset + i, 1)
if not operandByte then
logError(string.format("无法读取操作数字节: 0x%X", addr + offset + i))
break
end
-- 添加操作数通配符
builder.add(operandByte, true)
totalBytes = totalBytes + 1
-- 每添加一个通配符后检查唯一性(提前终止)
if totalBytes >= CONFIG.MIN_LENGTH then
local results = builder.build()
if checkAOB(results.ce_original, currentModule) then
-- 找到唯一特征码,移除末尾冗余通配符
builder:trimTrailingWildcards()
local finalResults = builder.build()
writeToClipboard(buildClipboardText(finalResults))
local totalTime = os.clock() - startTime
showMessage(string.format(
'✅ 特征码已复制到剪贴板!\n\n' ..
'模块: %s\n' ..
'长度: %d 字节\n' ..
'耗时: %.3f 秒\n' ..
'地址: 0x%X\n\n' ..
'特征码:\n%s\n',
currentModule.Name,
#finalResults.mask,
totalTime,
addr,
finalResults.ce_original
))
return {finalResults.ce_original, currentModule.Name}
end
end
end
offset = offset + instSize
end
showMessage("❌ 无法找到唯一特征码。\n已经超出脚本特征码最大长度。\n建议:\n1. 换个地址\n2. 修改脚本特征码最大长度限制")
return nil
end
-- 进度窗口
local function showProgressWindow()
local form = createForm()
form.Caption = "正在生成特征码..."
form.Width = 350
form.Height = 120
form.Position = poScreenCenter
form.BorderStyle = bsDialog
local label = createLabel(form)
label.Caption = "正在扫描内存,请稍候..."
label.Left = 10
label.Top = 20
label.Width = 330
local progressBar = createProgressBar(form)
progressBar.Left = 10
progressBar.Top = 50
progressBar.Width = 330
progressBar.Height = 25
progressBar.Style = pbstMarquee
form.show()
form.Repaint()
return form
end
-- 添加菜单项
function addGenerateAOBMenu()
local disasmView = getMemoryViewForm().DisassemblerView
local popupMenu = disasmView.PopupMenu
for i = 0, popupMenu.Items.Count - 1 do
if popupMenu.Items[i].Caption == '生成特征码' then
return
end
end
local menuItem = createMenuItem(popupMenu)
menuItem.Caption = '生成特征码'
menuItem.ImageIndex = 9
menuItem.OnClick = function()
local addr = disasmView.SelectedAddress
if not addr then
showMessage("请先选中一个地址")
return
end
local progressForm = showProgressWindow()
createThread(function()
local success, result = pcall(generateWildcardAOB, addr)
synchronize(function()
if progressForm and not progressForm.destroyed then
progressForm.close()
progressForm.destroy()
end
if not success then
showMessage("生成失败: " .. tostring(result))
end
end)
end)
end
popupMenu.Items.add(menuItem)
end
-- 初始化
addGenerateAOBMenu()
-- 日志函数
function logError(msg)
if getCEVersion and getCEVersion() >= 7.0 then
printError(string.format("[AOB生成器] %s", msg))
else
print(string.format("[错误] %s", msg))
end
end