собенности
- переход на следующий адрес по инструкциям ветвления вычисляется Lua кодом по ret, jmp, jmp condition до исполнения кода
- определение опкодов ветвления по readmem без дизассемблериования
- по тестам последний брейкпоинт снимается на ближайшем цикле
Пример лога до близжайшего цикла, когда поднимается из рутины вверх
> RET :00454684 - C3 - ret
> -->> :0045468C - 5B - pop ebx
> RET :0045468F - C3 - ret
> -->> :00454695 - C3 - ret
> RET :00454695 - C3 - ret
> -->> :00437F36 - 5B - pop ebx
> RET :00437F37 - C3 - ret
> -->> :004272EB - 5B - pop ebx
> RET :004272EC - C3 - ret
> -->> :004273E9 - 5E - pop esi
> RET :004273EA - C3 - ret
> -->> :00437A2E - 5F - pop edi
> RET :00437A34 - C3 - ret
> -->> :0043B749 - 5B - pop ebx
> RET :0043B74D - C3 - ret
> -->> :00427195 - 5F - pop edi
> RET :00427198 - C3 - ret
> -->> :004376BB - 8B 45 FC - mov eax,[ebp-04]
> RET :004376C2 - C2 0400 - ret 0004
> -->> :0043B880 - 89 46 0C - mov [esi+0C],eax
> RET :0043B88A - C3 - ret
> -->> :0043BFF4 - 84 C0 - test al,al
> isCJMP :0043BFF6 - 75 09 - jne 0043C001
> RET :0043C003 - C3 - ret
> -->> :0044DFAD - 5E - pop esi
> RET :0044DFAF - C3 - ret
> -->> :00437A2E - 5F - pop edi
\--[[
Версия: 0.01.b1
Выход из рутины до близжайшего цикла
]]--
mainAddress = 0x0045B5A4 -- адрес некоторого параметра в игре
nextAddress = nil
is64bits = targetIs64Bit()
tableInstruction ={}
tableBreakpointsAddress ={}
function ContaintsBeakPoint(address)
for i = 1, #tableBreakpointsAddress do
if tableBreakpointsAddress[i] == address then
return true
end
end
return false
end
\-- Возвращает адрес по ret
function GetNextAddressFromRET(address)
if is64bits then
nextAddress = readPointer(RSP)
else
nextAddress = readPointer(ESP)
end
return nextAddress
end
\-- Возвращает адрес по Jmp
function GetNextAddressFromJMP(address)
local disassembledstring = nil
if is64bits then
local disassembledstring = disassemble(RIP)
else
local disassembledstring = disassemble(EIP)
end
local _, opcode, _, _ = splitDisassembledString(disassembledstring)
return GetAddressFromOpcode(opcode)
end
function ToBits(num, bits)
local t = {}
for b = bits, 1, -1 do
rest = math.fmod(num,2)
t[b] = math.floor(rest)
num = (num-rest)/2
end
if num == 0 then
return t
else
return {'Not enough bits to represent this number'}
end
end
function GetEFLAGS()
local bitsTable = ToBits(EFLAGS, 16)
local tableEFLAGS =
{
OF = bitsTable[17-12],
DF = bitsTable[17-11],
SF = bitsTable[17-8],
ZF = bitsTable[17-7],
AF = bitsTable[17-5],
PF = bitsTable[17-3],
CF = bitsTable[17-1]
}
--for k,v in pairs(tableEFLAGS) do
-- print (k..' = '..v)
--end
--
--print(EFLAGS)
--local s = ''
--for i=1,#bitsTable do
-- s = s..bitsTable[i]
--end
--print(s)
return tableEFLAGS
end
\-- Возвращает адрес из опкода jmp dword ptr [edi*4+07895D88] или jmp 07895D88
\--print(string.format('%08X', newAddress))
function GetAddressFromOpcode (opcode)
local rightLine = string.match(opcode, '%S*%s*(%S*)')
local isPointer = string.match(opcode, '%[')
if isPointer then
rightLine = string.match(opcode, '%[(.*)%]')
--00454664 - 8B 83 60030000 - mov eax,[ebx+00000360]
if string.match(opcode, 'eax') then rightLine = string.gsub(rightLine, 'eax', EAX) end
if string.match(opcode, 'ebx') then rightLine = string.gsub(rightLine, 'ebx', EBX) end
if string.match(opcode, 'ecx') then rightLine = string.gsub(rightLine, 'ecx', ECX) end
if string.match(opcode, 'edx') then rightLine = string.gsub(rightLine, 'edx', EDX) end
if string.match(opcode, 'esi') then rightLine = string.gsub(rightLine, 'esi', ESI) end
if string.match(opcode, 'edi') then rightLine = string.gsub(rightLine, 'edi', EDI) end
if string.match(opcode, 'esp') then rightLine = string.gsub(rightLine, 'esp', ESP) end
if string.match(opcode, 'ebp') then rightLine = string.gsub(rightLine, 'ebp', EBP) end
if is64bits then
if string.match(opcode, 'rax') then rightLine = string.gsub(rightLine, 'rax', RAX) end
if string.match(opcode, 'rbx') then rightLine = string.gsub(rightLine, 'rbx', RBX) end
if string.match(opcode, 'rcx') then rightLine = string.gsub(rightLine, 'rcx', RCX) end
if string.match(opcode, 'rdx') then rightLine = string.gsub(rightLine, 'rdx', RDX) end
if string.match(opcode, 'rsi') then rightLine = string.gsub(rightLine, 'rsi', RSI) end
if string.match(opcode, 'rdi') then rightLine = string.gsub(rightLine, 'rdi', RDI) end
if string.match(opcode, 'rsp') then rightLine = string.gsub(rightLine, 'rsp', RSP) end
if string.match(opcode, 'rbp') then rightLine = string.gsub(rightLine, 'rbp', RBP) end
if string.match(opcode, 'r8') then rightLine = string.gsub(rightLine, 'r8', R8) end
if string.match(opcode, 'r9') then rightLine = string.gsub(rightLine, 'r9', R9) end
if string.match(opcode, 'r10') then rightLine = string.gsub(rightLine, 'r10', R10) end
if string.match(opcode, 'r11') then rightLine = string.gsub(rightLine, 'r11', R11) end
if string.match(opcode, 'r12') then rightLine = string.gsub(rightLine, 'r12', R12) end
if string.match(opcode, 'r13') then rightLine = string.gsub(rightLine, 'r13', R13) end
if string.match(opcode, 'r14') then rightLine = string.gsub(rightLine, 'r14', R14) end
if string.match(opcode, 'r15') then rightLine = string.gsub(rightLine, 'r15', R15) end
end
return getAddress('%['..rightLine..'%]')
end
return getAddress(rightLine)
end
\-- Возвращает адрес по Jmp condition, когда тот выполняется или не выполняется
function GetNextAddressFromConditionJMP(address, size)
local _,opcode,_,_ = splitDisassembledString(disassemble(address))
local leftString = string.match(opcode, '%S*')
local eflags = GetEFLAGS()
if (leftString == 'jnz' or leftString == 'jne') then if eflags.ZF == 0 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'je' or leftString == 'jz') then if eflags.ZF == 1 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jg' or leftString == 'jnle') then if eflags.ZF == 0 and eflags.SF == eflags.OF then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jb' or leftString == 'jc' or leftString == 'jnae') then if eflags.CF == 1 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jae') then if eflags.CF == 0 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'ja') then if eflags.CF == 0 and eflags.ZF == 0 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jbe') then if eflags.CF == 1 and eflags.ZF == 1 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jl' or leftString == 'jnge') then if eflags.SF ~= eflags.OF then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jle' or leftString == 'jng') then if eflags.ZF == 1 or eflags.SF ~= eflags.OF then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jna') then if eflags.CF == 1 or eflags.ZF == 1 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jc') then if eflags.CF == 1 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jp' or leftString == 'jpe') then if eflags.PF == 1 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jnp' or leftString == 'jpo') then if eflags.PF == 0 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jnb' or leftString == 'jnc') then if eflags.CF == 0 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jnbe') then if eflags.CF == 0 and eflags.ZF == 0 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jno') then if eflags.OF == 0 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jns') then if eflags.SF == 0 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jo') then if eflags.OF == 1 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'js') then if eflags.SF == 1 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jge' or leftString == 'jnl') then if eflags.CF == 1 and eflags.OF == 1 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jrcxz') then if RCX == 0 then return GetAddressFromOpcode(opcode) end
elseif (leftString == 'jecxz') then if ECX == 0 then return GetAddressFromOpcode(opcode) end
end
return address + size
end
function IsRet(address)
local value = readBytes(address,1, false)
return value == 0xC3 or value == 0xCB or value == 0xC2 or value == 0xCA
end
function IsCall(address)
local value = readBytes(address,1, false)
return value == 0xFF or value == 0xE8 -- or value == 0x9A
end
function IsJMP(address)
local value = readBytes (address, 1, false)
if value == 0xFF then
if readBytes (address + 1, 1, false) == 05 then
return false
end
end
return value == 0xEB or value == 0xE9 or value == 0xFF -- or value == 0xEA
\--[[
078921A8 - EB 38 - jmp 078921E2
0789242D - E9 E2000000 - jmp 07892514
07894F4C - FF E2 - jmp edx
07895C67 - FF 24 BD 885D8907 - jmp dword ptr [edi*4+07895D88]
]]--
end
function IsConditionJMP(address)
local value = readBytes (address, 1, false)
return
value == 0x77 or value == 0x73 or value == 0x72 or value == 0x76 or
value == 0xE3 or value == 0x74 or value == 0x7F or value == 0x7D or
value == 0x7C or value == 0x7E or value == 0x75 or value == 0x71 or
value == 0x7B or value == 0x79 or value == 0x70 or value == 0x7A or
value == 0x78 or value == 0x0F
end
\-- Возвращает адрес, на который, будет прыжок
function GetNextAddress(addressXIP)
-- Определить на какой инструкции мы находимся, чтобы узнать на какую следующу инструкцию ставить бряк
-- ret - прыжок по смещению ESP/RSP
-- jmp - прыжок без условия
-- je, jne, jxx - прыжок с улосвием
-- Определить размер инструкции, тип инструкции на текущем addressXIP
local findIndex = -1
for i = 1, #tableInstruction do
if tableInstruction[i].XIP == addressXIP then
findIndex = i
break
end
end
local size = 0
local isRet = false
local isJMP = false
local isCJMP = false
if findIndex == -1 then
-- Если нет данных
---------------- ЗАПОМИНАТЬ РАЗМЕР ИНСТРУКЦИИ (чтобы не дизассемблировать повторно)
size = getInstructionSize(addressXIP)
isRet = IsRet(addressXIP)
isJMP = IsJMP(addressXIP)
isCJMP = IsConditionJMP(addressXIP)
table.insert(tableInstruction, {XIP = addressXIP, SIZE = size, ISRET = isRet, ISJMP = isJMP, ISCJMP = isCJMP})
else
-- Если данные есть
size = tableInstruction[findIndex].SIZE
isRet = tableInstruction[findIndex].ISRET
isJMP = tableInstruction[findIndex].ISJMP
isCJMP = tableInstruction[findIndex].ISCJMP
end
---------------
if isRet then
print('> RET :' ..disassemble(nextAddress))
nextAddress = GetNextAddressFromRET(addressXIP)
print('> -->> :' ..disassemble(nextAddress))
elseif isJMP then
print('> isJMP :' ..disassemble(nextAddress))
nextAddress = GetNextAddressFromJMP(addressXIP)
elseif isCJMP then
print('> isCJMP :' ..disassemble(nextAddress))
nextAddress = GetNextAddressFromConditionJMP(addressXIP, size)
else
nextAddress = addressXIP + size
end
return nextAddress
end
function debugger_onBreakpoint()
local isContaintsBreakPoint = false
-- Удалить прошлый брейкпоинт
if(nextAddress ~= nil) then
isContaintsBreakPoint = ContaintsBeakPoint(nextAddress)
if isContaintsBreakPoint then
debug_removeBreakpoint(nextAddress)
end
end
-- Поставить брейкпоинт на следующую инструкцию не входя в call-ы
local XIP = 0
if is64bits then XIP = EIP else XIP = RIP end
nextAddress = GetNextAddress(XIP)
if not ContaintsBeakPoint(nextAddress) then
debug_setBreakpoint(nextAddress, 1, bptExecute, bpmDebugRegister)
table.insert(tableBreakpointsAddress, nextAddress)
end
return 1 --1 не показывать дизассемблер
end
if getOpenedProcessID() == 0 then
openProcess('test.exe')
end
\--bptWrite
\--bptAccess
debug_removeBreakpoint()
debug_setBreakpoint(mainAddress, 4, bptWrite, bpmDebugRegister)
Справка
77 cb JA rel8 D Valid Valid Jump short if above (CF=0 and ZF=0).
73 cb JAE rel8 D Valid Valid Jump short if above or equal (CF=0).
72 cb JB rel8 D Valid Valid Jump short if below (CF=1).
76 cb JBE rel8 D Valid Valid Jump short if below or equal (CF=1 or ZF=1).
72 cb JC rel8 D Valid Valid Jump short if carry (CF=1).
E3 cb JCXZ rel8 D N.E. Valid Jump short if CX register is 0.
E3 cb JECXZ rel8 D Valid Valid Jump short if ECX register is 0.
E3 cb JRCXZ rel8 D Valid N.E. Jump short if RCX register is 0.
74 cb JE rel8 D Valid Valid Jump short if equal (ZF=1).
7F cb JG rel8 D Valid Valid Jump short if greater (ZF=0 and SF=OF).
7D cb JGE rel8 D Valid Valid Jump short if greater or equal (SF=OF).
7C cb JL rel8 D Valid Valid Jump short if less (SF≠ OF).
7E cb JLE rel8 D Valid Valid Jump short if less or equal (ZF=1 or SF≠ OF).
76 cb JNA rel8 D Valid Valid Jump short if not above (CF=1 or ZF=1).
72 cb JNAE rel8 D Valid Valid Jump short if not above or equal (CF=1).
73 cb JNB rel8 D Valid Valid Jump short if not below (CF=0).
77 cb JNBE rel8 D Valid Valid Jump short if not below or equal (CF=0 andZF=0).
73 cb JNC rel8 D Valid Valid Jump short if not carry (CF=0).
75 cb JNE rel8 D Valid Valid Jump short if not equal (ZF=0).
7E cb JNG rel8 D Valid Valid Jump short if not greater (ZF=1 or SF≠ OF).
7C cb JNGE rel8 D Valid Valid Jump short if not greater or equal (SF≠ OF).
7D cb JNL rel8 D Valid Valid Jump short if not less (SF=OF).
7F cb JNLE rel8 D Valid Valid Jump short if not less or equal (ZF=0 and SF=OF).
71 cb JNO rel8 D Valid Valid Jump short if not overflow (OF=0).
7B cb JNP rel8 D Valid Valid Jump short if not parity (PF=0).
79 cb JNS rel8 D Valid Valid Jump short if not sign (SF=0).
75 cb JNZ rel8 D Valid Valid Jump short if not zero (ZF=0).
70 cb JO rel8 D Valid Valid Jump short if overflow (OF=1).
7A cb JP rel8 D Valid Valid Jump short if parity (PF=1).
7A cb JPE rel8 D Valid Valid Jump short if parity even (PF=1).
7B cb JPO rel8 D Valid Valid Jump short if parity odd (PF=0).
78 cb JS rel8 D Valid Valid Jump short if sign (SF=1).
74 cb JZ rel8 D Valid Valid Jump short if zero (ZF = 1).
0F 87 cw JA rel16 D N.S. Valid Jump near if above (CF=0 and ZF=0). Not supported in 64-bit mode.
0F 87 cd JA rel32 D Valid Valid Jump near if above (CF=0 and ZF=0).
0F 83 cw JAE rel16 D N.S. Valid Jump near if above or equal (CF=0). Not supported in 64-bit mode.
0F 83 cd JAE rel32 D Valid Valid Jump near if above or equal (CF=0).
0F 82 cw JB rel16 D N.S. Valid Jump near if below (CF=1). Not supported in 64-bit mode.
0F 82 cd JB rel32 D Valid Valid Jump near if below (CF=1).
0F 86 cw JBE rel16 D N.S. Valid Jump near if below or equal (CF=1 or ZF=1). Not supported in 64-bit mode.
0F 86 cd JBE rel32 D Valid Valid Jump near if below or equal (CF=1 or ZF=1).
0F 82 cw JC rel16 D N.S. Valid Jump near if carry (CF=1). Not supported in 64-bit mode.
0F 82 cd JC rel32 D Valid Valid Jump near if carry (CF=1).
0F 84 cw JE rel16 D N.S. Valid Jump near if equal (ZF=1). Not supported in 64-bit mode.
0F 84 cd JE rel32 D Valid Valid Jump near if equal (ZF=1).
0F 84 cw JZ rel16 D N.S. Valid Jump near if 0 (ZF=1). Not supported in 64-bit mode.
0F 84 cd JZ rel32 D Valid Valid Jump near if 0 (ZF=1).
0F 8F cw JG rel16 D N.S. Valid Jump near if greater (ZF=0 and SF=OF). Notsupported in 64-bit mode.
0F 8F cd JG rel32 D Valid Valid Jump near if greater (ZF=0 and SF=OF).
0F 8D cw JGE rel16 D N.S. Valid Jump near if greater or equal (SF=OF). Not supported in 64-bit mode.
0F 8D cd JGE rel32 D Valid Valid Jump near if greater or equal (SF=OF).
0F 8C cw JL rel16 D N.S. Valid Jump near if less (SF≠ OF). Not supported in 64-bit mode.
0F 8C cd JL rel32 D Valid Valid Jump near if less (SF≠ OF).
0F 8E cw JLE rel16 D N.S. Valid Jump near if less or equal (ZF=1 or SF≠ OF). Not supported in 64-bit mode.
0F 8E cd JLE rel32 D Valid Valid Jump near if less or equal (ZF=1 or SF≠ OF).
0F 86 cw JNA rel16 D N.S. Valid Jump near if not above (CF=1 or ZF=1). Not supported in 64-bit mode.
0F 86 cd JNA rel32 D Valid Valid Jump near if not above (CF=1 or ZF=1).
0F 82 cw JNAE rel16 D N.S. Valid Jump near if not above or equal (CF=1). Not supported in 64-bit mode.
0F 82 cd JNAE rel32 D Valid Valid Jump near if not above or equal (CF=1).
0F 83 cw JNB rel16 D N.S. Valid Jump near if not below (CF=0). Not supported in 64-bit mode.
0F 83 cd JNB rel32 D Valid Valid Jump near if not below (CF=0).
0F 87 cw JNBE rel16 D N.S. Valid Jump near if not below or equal (CF=0 and ZF=0). Not supported in 64-bit mode.
0F 87 cd JNBE rel32 D Valid Valid Jump near if not below or equal (CF=0 and ZF=0).
0F 83 cw JNC rel16 D N.S. Valid Jump near if not carry (CF=0). Not supported in 64-bit mode.
0F 83 cd JNC rel32 D Valid Valid Jump near if not carry (CF=0).
0F 85 cw JNE rel16 D N.S. Valid Jump near if not equal (ZF=0). Not supported in 64-bit mode.
0F 85 cd JNE rel32 D Valid Valid Jump near if not equal (ZF=0).
0F 8E cw JNG rel16 D N.S. Valid Jump near if not greater (ZF=1 or SF≠ OF). Not supported in 64-bit mode.
0F 8E cd JNG rel32 D Valid Valid Jump near if not greater (ZF=1 or SF≠ OF).
0F 8C cw JNGE rel16 D N.S. Valid Jump near if not greater or equal (SF≠ OF). Not supported in 64-bit mode.
0F 8C cd JNGE rel32 D Valid Valid Jump near if not greater or equal (SF≠ OF).
0F 8D cw JNL rel16 D N.S. Valid Jump near if not less (SF=OF). Not supported in 64-bit mode.
0F 8D cd JNL rel32 D Valid Valid Jump near if not less (SF=OF).
0F 8F cw JNLE rel16 D N.S. Valid Jump near if not less or equal (ZF=0 and SF=OF). Not supported in 64-bit mode.
0F 8F cd JNLE rel32 D Valid Valid Jump near if not less or equal (ZF=0 and SF=OF).
0F 81 cw JNO rel16 D N.S. Valid Jump near if not overflow (OF=0). Not supported in 64-bit mode.
0F 81 cd JNO rel32 D Valid Valid Jump near if not overflow (OF=0).
0F 8B cw JNP rel16 D N.S. Valid Jump near if not parity (PF=0). Not supported in 64-bit mode.
0F 8B cd JNP rel32 D Valid Valid Jump near if not parity (PF=0).
0F 89 cw JNS rel16 D N.S. Valid Jump near if not sign (SF=0). Not supported in 64-bit mode.
0F 89 cd JNS rel32 D Valid Valid Jump near if not sign (SF=0).
0F 85 cw JNZ rel16 D N.S. Valid Jump near if not zero (ZF=0). Not supported in 64-bit mode.
0F 85 cd JNZ rel32 D Valid Valid Jump near if not zero (ZF=0).
0F 80 cw JO rel16 D N.S. Valid Jump near if overflow (OF=1). Not supported in 64-bit mode.
0F 80 cd JO rel32 D Valid Valid Jump near if overflow (OF=1).
0F 8A cw JP rel16 D N.S. Valid Jump near if parity (PF=1). Not supported in 64-bit mode.
0F 8A cd JP rel32 D Valid Valid Jump near if parity (PF=1).
0F 8A cw JPE rel16 D N.S. Valid Jump near if parity even (PF=1). Not supported in 64-bit mode.
0F 8A cd JPE rel32 D Valid Valid Jump near if parity even (PF=1).
0F 8B cw JPO rel16 D N.S. Valid Jump near if parity odd (PF=0). Not supported in 64-bit mode.
0F 8B cd JPO rel32 D Valid Valid Jump near if parity odd (PF=0).
0F 88 cw JS rel16 D N.S. Valid Jump near if sign (SF=1). Not supported in 64-bit mode.
0F 88 cd JS rel32 D Valid Valid Jump near if sign (SF=1).
0F 84 cw JZ rel16 D N.S. Valid Jump near if 0 (ZF=1). Not supported in 64-bit mode.
0F 84 cd JZ rel32 D Valid Valid Jump near if 0 (ZF=1).
Если трейсить трейслогом 1000 инструкций поверх call, то видим многократное повторение пути внутри цикла между 00437A34 и 0044DFAF.
С помощью скрипта можно выйти на цикл не используя трейслог
Можно использоваться функции определения куда прыгнет поток, до его выполнения.
Можно оперировать таблицей адресов с брейкпоинтами в пошаговой отладке.