Выход из рутины до ближайшего цикла
-
собенности
- переход на следующий адрес по инструкциям ветвления вычисляется 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.
С помощью скрипта можно выйти на цикл не используя трейслогМожно использоваться функции определения куда прыгнет поток, до его выполнения.
Можно оперировать таблицей адресов с брейкпоинтами в пошаговой отладке.