Перейти к содержанию

Отладка в Lua. Снятие показаний регистров


Рекомендуемые сообщения

В этом коротком туторе я покажу пример снятия данных регистров.

post-3-1305873036,47_thumb.png


pmAddress=getAddress("1F72152C")
max = 10
count = 0

function debugger_onBreakpoint() -- срабатывает всегда, когда срабатывает брейкпоинт
count = count + 1
-- если понадобятся все регистры или некоторые, то вы можете исправить код ниже
s = string.format("%3d) 0x%08X: EAX = 0x%08X EBX = 0x%08X ECX = 0x%08X",count, EIP, EAX, EBX, ECX)
print(s)
if count>=max then
debug_removeBreakpoint(pmAddress);
end
--Breakpoint continue methods: co_run=0, co_stepinto=1, co_stepover=2
debug_continueFromBreakpoint(co_run)
-- return 1 --I handled it so dont tell the user
-- return 0 --unexpected breakpoint, show the the user
end

--Breakpoint triggers: bptExecute=0, bptAccess=1, bptWrite=2
--Variable types: (ref http://ce.colddot.nl/browser/Cheat%20Engine%206/bin/defines.lua)
debug_setBreakpoint(pmAddress, vtDword, bptWrite)

Обращу внимание, что именно по этому примеру можно делать другие не менее полезные. Помимо различных сравнений и ведения логов можно сделать определение адресов на прерывающихся инструкциях больше одного. Результат будет означать, что инструкции за все время своей работы работали только с одним адресом...

Так же обращу внимание на "частоты" срабатываний тех или иных инструкций, это тоже можно вести в логе.

В нужный момент можно "не отпускать процесс" и проанализировать ситуацию.

Можно вести трассировку...

Сравнение структур!

И т.п. что вам в голову придёт.

И ещё. Если вам надо снять брейкпоинты установленные через LUA, то это можно сделать вручную в функциях окна меню дизассемблера.

Ссылка на комментарий
Поделиться на другие сайты

  • 1 месяц спустя...

Ведение лога инструкций, которые работают с адресом, если в момент срабатывания бряка по адресу находится заданное вами значение.

В моем примере выводятся инструкции, которые записывают значение 1080 типа Integer по адресу "Test.exe+5B5A4". Дальше смотрите код.


Автор: MasterGH 2011

Скрпит для
идентификации инструкции работающием с адресом, когда в нём оказывается определённое значения.

Требования:
- предварительно приаттачить 32-х разрядный процесс игры через иконку Компьютера в главном окне CE
- Если тип данных сравниваемого значения не Integer, то изменить в скрипте "readInteger"

Информация по Lua поддержке: http://ce.colddot.nl/browser/Cheat%20Engine%206/bin/main.lua
Информация по Lua константам: http://ce.colddot.nl/browser/Cheat%20Engine%206/bin/defines.lua

Хотите доработать скрипт?
-- Вы можете создать графический интерфейс пользователя
-- Вы можете сделать так чтобы не выводились повторяющиеся инструкции

]]--

-- Значения для настройки

local myValue = 1080 -- сравниваемое значение, в моем случае 1080типа Integer
local myValueSize = 4 -- размер данных по адресу, в моём случае Integer, значит "4"
local myAddress = getAddress("Test.exe+5B5A4") -- получение адреса в целочисленный тип, мой адрес "Test.exe+5B5A4"

-- тип бряка "ставим на запись"
local typeBreak = 2 --( другие: bptExecute=0, bptAccess=1, bptWrite=2)

-- Если игра вылетает, то попробовать типы отладки: VEHDebug и Kerneldebug
local typeDebug = 1 -- тип отладки (0=default, 1=windows debug, 2=VEHDebug, 3=Kerneldebug)

local criticalCount = 5000 -- если лог постоянно показыватеся, что его нельзя остановить, то этот предел остановит отладку
local count = 0
-----------------------------------

-- Вызвать в консоли эту функцию если нужно удалить бряк и преркатить слежение
-- В этой функции ничего менять не надо
function RemoveBreakpoint()
debug_removeBreakpoint(myAddress);
end

-----------------------------------

-- Функция обработки события точки останова
-- В этой функции можно поменять readInteger на нужный тип данных
function debugger_onBreakpoint()
-- если значение по адресу равно 1080, то выводить инструкции в лог
if readInteger(myAddress)== myValue then
print(disassemble(getPreviousOpcode(EIP)))
count = count + 1
if (count > criticalCount) then
RemoveBreakpoint()
end
end
return 1 -- не показывать отладчик, если 0 - то показывать
end

-- Далее ничего менять не надо
debugProcess(typeDebug) -- установка режима отладки

-- Утановка брейкпоинта
-- В этой функции ничего менять не надо
debug_setBreakpoint(myAddress, myValueSize, typeBreak) -- установка самого брякак
--[[

Скриншот на котором показано как работать с этим скриптом:

post-3-1311013361,7_thumb.png

Желаю успехов!

Ссылка на комментарий
Поделиться на другие сайты

Заготовка скрипта для ведения лога данных на функции TranslateMessage.

Функция TranslateMessage переводит сообщения виртуальных клавиш в символьные сообщения. Символьные сообщения помещаются в очередь сообщений вызывающего потока для прочтения в следующий раз, когда поток вызовет функцию GetMessage или PeekMessage.

Теория (на практике всю теорию не проверял, только где-то как-то кусками)

В игре подгружаются модули dll, которые вызывают translateMessage. Например directXInput8 dll-ка вызывала эту функцию когда я кликнул левой кнопкой мыши по области окна игры. Я мог бы остановить текущую отладку в этот момент и повесить условный бряк уже на функцию обработки именно этого уже оттранслированного сообщения. Ведь именно его обработка это начало пути определённых действий в коде... концом пути уже будет бряк на некотором адресе.

В OllyDbg можно найти развилку между начальным и конечным путями при автоматическом трейсинге по ретам. И уже на этой развилке хорошо думать, почему именно эта развилка кода имела место быть...


Скрипт установки условного брейкпоинта на функции TranslateMessage.

В параметрах сравнения участвует код сообщения "UINT message"
Если сообщение равно сравниваемому, то отладчик прервётся и в поле лога выведет параметры структуры MSG

BOOL TranslateMessage(
const MSG* lpMsg // Указатель на структуру MSG, которая содержит информацию о сообщении извлеченную из очереди сообщений вызывающего потока при помощи использования функции GetMessage или PeekMessage.
);

typedef struct {
HWND hwnd; // Дескриптор окна, оконная процедура которого принимает сообщение.
UINT message; // Определяет код сообщения. Приложения могут использовать только младшее слово; старшее слово зарезервировано системой.
WPARAM wParam; // Определяет дополнительную информацию о сообщении. Точное значение зависит от значения члена структуры message.
LPARAM lParam; // Определяет дополнительную информацию о сообщении. Точное значение зависит от значения члена структуры message.
DWORD time; // Определяет время, в которое сообщение было помещено в очередь.
POINT pt; // Устанавливает позицию курсора, в экранных координатах, в момент, когда сообщение было помещено в очередь.
} MSG, *PMSG;


Пример использования.

Пример1. Установка условного брейкпоинта функции TranslateMessage в сравнении с параметром MSG:
-- Сработает когда пользователь отпустит левую кнопку мышки

WM_LBUTTONUP = 0x202 (другие коды сообщени ищите в Интернете)
StartDebug_TranslateMessageCondition_onMSG(WM_LBUTTONUP)

Пример2. Установика безусловного брейкпоинта с логировнием всех сообщений:

StartDebug_TranslateMessage()

Пример3. Если необходимо только прерваться на некоторой функции без сбора параметров:

StartDebugSomeFunction(functionName)

Пример4. Если необоходимо остановить отладку, то вызывать:

StopDebug()
]]--

local mode = 0
local addressesBreakPoint = {}
local countAddressesBreakPoint = 0

function AddBreakPointExecut(addressFunction)
countAddressesBreakPoint = table.getn(addressesBreakPoint)
countAddressesBreakPoint = countAddressesBreakPoint + 1
addressesBreakPoint[countAddressesBreakPoint] = addressFunction
debug_setBreakpoint(addressFunction, 1, bptExecute)
end

function DeleteAllBreakPoint()
for i=1, countAddressesBreakPoint do
debug_removeBreakpoint(addressesBreakPoint[i])
end
countAddressesBreakPoint = 0
end

-- Единая функция обработки сообщений о прерывании
function debugger_onBreakpoint()

local withIn = false
for i=1, countAddressesBreakPoint do
if (EIP == addressesBreakPoint[i]) then
withIn = true
break
end
end

if (not withIn) then
return 1
end

if ( (mode == 1) or (mode == 2) ) then
local pMSG = readInteger(ESP+0x4)
local MSG = readInteger(pMSG+0x4)

if ((mode == 1) and (MSG ~= _iMSGBreak)) then
return 1
end

local s = string.format(" • Call to %s from \"%s\" •\n\r\n>> hWnd = %X\n\r\n>> MSG = %X\n\r\n>> wParam = %X\n\r\n>> lParam = %08X\n\r\n>> Time = %X\n\r\n>> Point = %i x %i\n\r\n", getNameFromAddress(EIP), getNameFromAddress(readInteger(ESP)),
readInteger(pMSG),
MSG,
readInteger(pMSG+0x8),
readInteger(pMSG+0xC),
readInteger(pMSG+0x10),
readInteger(pMSG+0x14), readInteger(pMSG+0x18))

if (mode == 1) then -- WM_LBUTTONUP
print(s) -- записываем в лог LuaEngine текст из loglist
return 0 -- ставим бряк
else -- в случае если условие не выполнено
print(s) -- записываем в лог LuaEngine текст из loglist при не выполненом условии
return 1 -- продолжаем без бряка
end
end

return 0
end

function StartDebug_TranslateMessageCondition_onMSG(iMSGBreak)
DeleteAllBreakPoint()
mode = 1
_iMSGBreak = iMSGBreak
local addressFunction = getAddress("TranslateMessage")
AddBreakPointExecut(addressFunction)
print(string.format("Включен режим остановки на фунции TranslateMessage с условием: MSGBreak == %X",iMSGBreak))
end

function StartDebug_TranslateMessage()
DeleteAllBreakPoint()
mode = 2
local addressFunction = getAddress("TranslateMessage")
AddBreakPointExecut(addressFunction)
print("Включен режим остановки на фунции TranslateMessage без условий")
end

function StartDebugSomeFunction(functionName)
mode = 0
local addressFunction = getAddress(functionName)
AddBreakPointExecut(addressFunction)
end

function StopDebug()
DeleteAllBreakPoint()
_iMSGBreak = 0
end
--[[

Ссылка на комментарий
Поделиться на другие сайты

Учить вообще ничего не надо и стоит ли задаваться такими вопросами?. Надо сразу писать код по примерам и пытаться писать свой код. В это время голова будет запоминать то, что надо.

Ссылка на комментарий
Поделиться на другие сайты

  • 10 месяцев спустя...

если нужно удалить бряк и преркатить слежение

function RemoveBreakpoint()

debug_removeBreakpoint(myAddress);

end

столкнулся с ситуациями когда некоторые участки кода не реагируют на функцию

debug_removeBreakpoint(myAddress);

интуитивно предполагаю что это связано с "высокочастотными" участками кода.

иногда помогает выйти из ситуации простая приостановка процесса.

debug_removeBreakpoint(myAddress)

pause()

unpause()

....

но не всегда.

и даже попытки удалить бряк вручную из панели "список точек останова", порой не приводит к успеху.

независимо от типа отладки (0=default, 1=windows debug, 2=VEHDebug, 3=Kerneldebug)

В связи с этим, два вопроса.

1. Если кто-то сталкивался с похожей ситуацией, то какими способами решается проблема?

2. Неплохо-бы попросить ДаркБайта обогатить набор функций Lua в СЕ

неким способом "жёсткого" безусловного выхода из режима "debug"

но это к Андрею, думаю вопрос.

*прим. наблюдалось на версиях CE 6,1 и 6,2 , на разных компах и версиях Win.

Ссылка на комментарий
Поделиться на другие сайты

>> 1. Если кто-то сталкивался с похожей ситуацией, то какими способами решается проблема?

Я к сожалению не сталкивался

>>неким способом "жёсткого" безусловного выхода из режима "debug"

Выход из отладки

1) detachIfPossible() - вообще вырубить отладчик (функция должна работать в CE 6.2)

2) debug_removeBreakpoint(address) лишь удаляет брейкпоинты (можно постараться удалить все). Поэтому это не совсем выход из отладочного режима.

Возможно функцию

debug_removeBreakpoint(address)

не стоит вызывать в недрах debugger_onBreakpoint(), когда debugger_onBreakpoint() работает очень часто, а вызывать из чего-то другого. Из функции таймера или потока. Да-да =), На Lua можно работать с потоками существует специальный класс Thread Class.

К сожалению я не могу помочь примерами кода как работать с потоками или как с таймером. Мне лень. Я могу что-то подправить или подсказать.

Ссылка на комментарий
Поделиться на другие сайты

  • 5 лет спустя...

а как можно трейсить step by step?

например как встроенный трейсер, чтоб записал весь лог?

как он пошагово идет?

debug_continueFromBreakpoint?

а если несколько потоков то это как то разделяется или в куче будет?

вроде в последней CE с контекстом потоков что то есть

Изменено пользователем X86Jumps
Ссылка на комментарий
Поделиться на другие сайты

  • 2 недели спустя...

Есть два варианта пошаговой трассеровки

1. debug_continueFromBreakpoint(continueMethod) -- описание в справке

2. Самому рассчитывать каким должен быть следующий адрес и на него ставить брейкпоинт. Этот способ показался мне быстрее, но уже не помню на сколько. Код в репозитории.

 

В 30.08.2017 в 03:42, X86Jumps сказал:

а если несколько потоков то это как то разделяется или в куче будет?

вроде в последней CE с контекстом потоков что то есть

Что-то есть, надо разбираться. Я один и тот же поток отслеживал через второй способ.

Ссылка на комментарий
Поделиться на другие сайты

22 минуты назад, X86Jumps сказал:

т.е. снимать один бряк и ставить следующий?

странно что это получается быстрее чем lua функцией))

 

Да.

Есть какие-то shadow брейкпоинты, у меня их было довольно много из-за первого способа (если не ошибаюсь больше 15 на Сталкере Зов Припяти), когда трейсил на адресе патронов.

Когда я стал использовать способ2, shadow бряки не включались. Было на мой взгляд быстрее. В общем надо все проверять, замерять и прочее.

Ссылка на комментарий
Поделиться на другие сайты

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.