跳转至

搜索汇编模式

第一个脚本定位组成出站数据包并将其发送到游戏服务器的函数。它的工作原理是在游戏的汇编代码中搜索包含特定代码序列的函数。

 BASEADDRESS = getAddress("Game.exe")
 function LocatePacketCreation(packetType)
  for address = BASEADDRESS, (BASEADDRESS + 
0x2ffffff) do
    local push = readBytes(address, 1, false)
    local type = readInteger(address + 1)
    if (push == 0x68 and type == packetType and 
call == 0xE8) then
      return address
    end
  end
  return 0
end
FUNCTIONHEADER = { 0xCC, 0x55, 0x8B, 0xEC, 0x6A }
 function LocateFunctionHead(checkAddress)
  if (checkAddress == 0) then return 0 end
  for address = checkAddress, (checkAddress - 
0x1fff), -1 do
    local match = true
    local checkheader = readBytes(address, 
#FUNCTIONHEADER, true)
    for i, v in ipairs(FUNCTIONHEADER) do
      if (v ~= checkheader[i]) then
        match = false
        break
      end
    end
    if (match) then return address + 1 end
  end
  return 0
end
 local funcAddress = LocateFunctionHead(LocatePac
ketCreation(0x64))
if (funcAddress ~= 0) then
  print(string.format("0x%x",funcAddress))
else
  print("Not found!")
end

脚本首先获取 Cheat Engine 附加到的模块的基地址①。获得基地址后,定义 LocatePacketCreation() 函数②。这个函数遍历游戏中前 0x2FFFFFF 字节的内存③,搜索代表以下 x86 汇编代码的序列:

PUSH type ; 数据为:0x68 [4字节 type]
CALL offset ; 数据为:0xE8 [4字节 offset]

函数检查 type 是否等于 packetType,但不关心函数偏移量是什么④。一旦找到这个序列,函数就返回。

接下来定义 LocateFunctionHead() 函数⑤。该函数从给定地址回溯最多 0x1FFF 字节⑥,并在每个地址检查类似这样的汇编代码桩⑦:

INT3 ; 0xCC
PUSH EBP ; 0x55
MOV EBP, ESP ; 0x8B 0xEC
PUSH [-1] ; 0x6A 0xFF

这段代码桩会出现在每个函数的开头,因为它是设置函数栈帧的函数序言(prologue)的一部分。一旦找到这段代码,函数就返回代码桩地址加 1⑧(第一个字节 0xCC 是填充)。

为了把各步骤串起来,用我要找的 packetType(随意取 0x64)调用 LocatePacketCreation(),并把得到的地址传给 LocateFunctionHead() 函数⑨。这实际上定位了第一个把 packetType 压入函数调用的函数,并把它的地址存储在 funcAddress 中。这段代码桩展示了结果:

INT3 ; LocateFunctionHead 回溯到这里
PUSH EBP ; 并返回这个地址
MOV EBP, ESP
PUSH [-1]
--snip--
PUSH [0x64] ; LocatePacketCreation 返回这个地址
CALL [something]

这个 35 行的脚本可以在不到一分钟内自动定位 15 个不同的函数。