吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1142|回复: 22
上一主题 下一主题
收起左侧

[其他原创] Cheat Engine 高亮 + 生成特征码脚本

  [复制链接]
跳转到指定楼层
楼主
wjx8885577 发表于 2025-12-3 19:13 回帖奖励
本帖最后由 wjx8885577 于 2025-12-4 16:25 编辑

1.菜单创建:在CE反汇编窗口的右键菜单中添加"寄存器高亮"主菜单,包含所有通用寄存器的子菜单
2.交互操作:点击寄存器名称可切换该寄存器的高亮状态(勾选/取消勾选)
3.高亮效果:选中的寄存器在反汇编代码中会显示为黄色背景+黑色文字
4.实时更新:切换后立即重绘视图,无需重启或刷新
5.大小写兼容:能匹配不同大小写格式的寄存器名称




1.智能长度:自动确定最短且唯一的特征码长度
2.多格式输出:同时生成CE、IDA、PEiD和C语言格式的特征码
3.指令感知:识别x64指令前缀(REX/0F)和操作数部分
4.线程安全:使用独立线程避免界面卡顿
5.范围限定:仅在目标模块内搜索,提高效率和准确性


特征码会生成4种风格的代码,类似于下面:
[Asm] 纯文本查看 复制代码
CE Style
E8????????41524989

PEiD Style
E8 ?? ?? ?? ?? 41 52 49 89

IDA Style
E8 ? ? ? ? 41 52 49 89

Code style
\xE8\x?\x?\x?\x?\x41\x52\x49\x89 x????xxxx




把脚本放进CE的autorun目录里面,启动CE后脚本就会自动运行

[Lua] 纯文本查看 复制代码
-- 获取内存窗口中的反汇编视图对象
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(
                        '&#9989; 特征码已复制到剪贴板!\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("&#10060; 无法找到唯一特征码。\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


autorun.7z (4.51 KB, 下载次数: 12)

免费评分

参与人数 11吾爱币 +17 热心值 +10 收起 理由
注册个id + 1 + 1 用心讨论,共获提升!
qiujunjian1 + 1 + 1 谢谢@Thanks!
TNB + 1 + 1 谢谢@Thanks!
hrh123 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
De蓝 + 1 + 1 热心回复!
sanjos + 1 用心讨论,共获提升!
lsq132273 + 1 + 1 热心回复!
wym20181818 + 1 + 1 用心讨论,共获提升!
a454635280 + 1 + 1 感谢你的分享
MJ_B + 1 + 1 热心回复!
helian147 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
bachelor66 发表于 2025-12-4 09:13
这个太漂亮了                                    
沙发
souny 发表于 2025-12-3 19:30
3#
shuaigeA 发表于 2025-12-3 19:33
4#
zt185 发表于 2025-12-3 19:37
好软件,试试这版CE!
5#
yenfenwo 发表于 2025-12-3 20:44
这个太棒了 !
6#
钱迷 发表于 2025-12-3 20:47
终于有人发布了,谢谢
7#
北七夜 发表于 2025-12-3 20:59
有过检测的ce吗
8#
qxiaoguiq 发表于 2025-12-3 21:07
感谢楼主分享
9#
鸭子咯咯哒~ 发表于 2025-12-3 23:02
学习学习
10#
zhouxinyi 发表于 2025-12-4 00:49
可以,不用重复造轮子了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-12-5 08:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表