-
Постов
289 -
Зарегистрирован
-
Победитель дней
22
Тип контента
Профили
Форумы
Загрузки
Блоги
Сообщения, опубликованные srg91
-
-
5 minutes ago, MasterGH said:
Для ret 8
Спасибо, исправлено.
Исправил картинки нескольких других статьях.
-
9 minutes ago, MasterGH said:
Просьба хостить скриншоты на форум
Готово. Да, так даже удобнее, я что-то машинально.
Спасибо.
Спасибо за отзывы. Если есть ошибки - пишите, я исправлю.
- 2
-
@Garik66 спросил - как использовать createthread, чтобы он не крашился.
Начал отвечать и это слегка вышло за рамки простого ответа в тему, поэтому решил выделить это в отдельный топик.
Я не очень хорошо знаю assembler, но попробую описать возможные причины.
createthread создает поток и просит его выполнить call с адресом переданной функции.
Например в данном случае createthread(my_function) в отдельном потоке выполнит call my_function:
Spoileralloc(my_function, 64) my_function: ret createthread(my_function)
И тут вступает в дело стек, да.
Вот эта штука (в Memory View вызывается через ПКМ на правом нижнем окне и выборе пункта Full stack):
После выполнения call my_function мы попадаем в нашу функцию, а в стек записывается адрес, куда нужно вернуться после выполнения этой функции (my_function). Собственно за возврат куда нужно и отвечает ret. Он берет первую запись из стека и делает jmp в правильное место. Собственно сам адрес возврата виден в стеке на скриншоте - это KERNEL32.BaseThreadInitThunk+24.
Без ret ассемблер вывалится за пределы функции и попытается выполнить команду - add [eax],al , хотя для нас это просто участок пустой памяти - 00 00. Ну а так как в EAX ничего приличного нет, то программа выдает ошибку - сорян, не могу записать в данный участок памяти и падает. Поэтому ret обязателен.
Из этого вытекает то, что при подходе к ret в последней записи в стеке (адрес в регистр ESP) должна быть с правильным адресом возврата, который был нам передан из call my_function. И основная причина крашей в том, что регистр ESP указывает не на тот адрес при выполнении команды ret. Программа выполняет jmp на неизвестный нам адрес и крешится из-за невозможности выполнить команды, на которые она попала (как с add [eax],al).
А что собственно двигает стек и адрес регистра ESP?
А двигают стек наши любимые команды push и pop. Собственно, когда мы пытаемся вызывать функцию и передаем параметры через push #100, на самом деле выполняются две команды - sub esp,4 и mov [esp],#100. Первая уменьшает адрес вершины стека на 4 байта, а вторая записывает на вершину стека значение 100. А pop eax сдвигает вершину в другую сторону - делает mov eax, [esp] и дальше сдвигает вершину стека назад add esp,4.
Поэтому после того, как мы сдвинули стек с помощью push при передаче параметров в функцию - после её выполнения нужно сдвинуть стек обратно!
Собственно следующий пример почти 100% скрешится:
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat globalalloc(my_function, 64) alloc(second_function, 64) my_function: push #100 call second_function ret second_function: mov eax,[esp+4] ret createthread(my_function) [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(my_function) unregistersymbol(my_function) dealloc(second_function)
Мы ввели новую функцию second_function, которая берет переданный аргумент и записывает его в EAX. Вроде бы всё просто, передали в функцию 100, получили в EAX ответ, а всё равно креш. Почему? А это видно на следующем скриншоте - на вершине стека теперь хранится значение 0x64 (=100):
Теперь когда выполнится ret он попытается прыгнуть на адрес 0x00000064, а не на 0x760E8744 как должен был.
Как же этого избежать? Есть несколько путей:
- сохраняем значение регистра esp и возвращаем его перед ret
- внимательно следим за стеком по мере выполнения нашей функции и к концу программы он сам будет в правильном месте
В целом первый способ очень неплох. Мы можем сохранить адрес регистра esp в памяти и вернуть его прямо перед вызовом ret. Например:
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat globalalloc(my_function, 64) alloc(second_function, 64) alloc(esp_addr, 4) esp_addr: dd 0 my_function: // сохраняем текущий адрес вершины стека mov [esp_addr],esp push #100 call second_function // загружаем обратно адрес вершины стека mov esp,[esp_addr] ret second_function: mov eax,[esp+4] ret createthread(my_function) [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(my_function) unregistersymbol(my_function) dealloc(second_function) dealloc(esp_addr)
Но это не совсем assembler-way, поэтому я предпочитаю следить за стеком самостоятельно.
Для этого нужно обращать внимание на то, как со стеком работает вызываемая функция.
Например, наша функция second_function не следит за стеком. Поэтому если мы делаем push для передачи параметров, то мы так же должны сдвинуть его назад. Это можно сделать через pop указав неиспользуемый регистр или просто добавить к адресу ESP 4 байта (размер адреса в 32 битной системе, в 64-битной - это 8 байт):
Spoiler// через pop - eax используется, выполним pop в неиспользуемый ebx push #100 call second_function pop ebx // через сдвиг esp push #100 call second_function add esp,4
Собственно таким образом мы двигаем стек за функцией. И в зависимости от количества переданных push - на такое же количество нужно сдвинуть и регистр ESP. Например для 3-х аргументов нужно выполнить 3 pop или добавить к esp 3 * 4 (3 - количество сдвигов, 4 - размер инструкции):
Spoiler// через pop push #100 push #200 push #300 call third_function pop ebx pop ebx pop ebx // через сдвиг esp push #100 push #200 push #300 call third_function add esp,c // 0xC = 4 * 3 = 12
В результате мы получим следующий код:
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat globalalloc(my_function, 64) alloc(second_function, 64) my_function: push #100 call second_function add esp,4 ret second_function: mov eax,[esp+4] ret createthread(my_function) [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(my_function) unregistersymbol(my_function) dealloc(second_function)
Единственное, есть небольшое исключение - функции, которые сами заботятся о стеке, после их вызова не нужно сдвигать регистр esp, просто делаем push и call. Их довольно просто отличить, посмотрим на примере следующего кода:
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat globalalloc(my_function, 64) globalalloc(second_function, 64) globalalloc(third_function, 64) my_function: // функция, не сдвигающая стек за собой push #100 call second_function add esp,4 // функция, сдвигающая стек за собой push #100 push #200 call third_function ret second_function: mov eax,[esp+4] ret third_function: mov eax,[esp+4] add eax,[esp+8] ret 8 createthread(my_function) [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(my_function) unregistersymbol(my_function) dealloc(second_function) unregistersymbol(second_function) dealloc(third_function) unregistersymbol(third_function)
Основное отличие - после call функции которая сама двигает стек не идет ни каких add esp,8 или не выполняются pop:
Так же можно обратить внимание на код самой функции, а точнее на ret 8. Параметр 8 указывает программе, что нужно не просто взять адрес из вершины стека и прыгнуть на него, а так же после этого сдвинуть стек на 8 байт, т.е. выполнить ret и add esp,8. Собственно и получается, что add делаем не мы, а сама функция.
Вот и получается, что следя за стеком и тем, как вызываемые функции заботятся о стеке - мы не получим крашей в вызове createthread.
- 12
-
Привет. Ты можешь открыть окно AutoAssembler и нажать F1 - откроется справка по командам AutoAssembler с примерами.
Минимальная информация так же есть на Wiki.
По самому ассемблеру наверное ничего не могу сказать (максимум что это Intel-синтаксис), но форуме есть пара тем про асм:
Spoiler -
Sleep предоставляется CE (из main.lua):
sleep(milliseconds): pauses for the number of specified milliseconds (1000= 1 sec...)
- 1
-
Второе задание - готово. Не открывал не IDA, ничем. Использовал небольшой, как со вторым TrainMe и датой )
Под спойлером напишу как взламывал.
SpoilerНу, так как мы знаем, что Taiwan пишет на нашем любимом Delphi и ничего не запаковано - значит мы вооружены
Первым путем попробовал сделал так - нашел операцию сравнения - в Delphi это отдельный call. Чтобы выяснить как он выглядит просто компилим минимальный проект с if A = B then и смотрим что внутри этого call. Найдя эту функцию в Unlock Me ставим бряк и аккуратно отфильтровываем все использования этой функции. Я умудрился профукать её вызов в проверке, поэтому пошел по другому.
Я нашел компоненты на форме через поиск TLabel в памяти, увидел что используется IdEncoderXXE. Это стандартный Indy компонент, а значит можно предположить что использован IdEncodeXXE.Encode Собственно, ищем его так же как и функцию сравнения и ставим бряк. Таким образом и попадаем на OnClick кнопки. А дальше видим, что вызывается та же функция сравнения, где в случае не выполнения условия Edit.Text == IdEncodeXXE.Encode(0xA0) приложение завершает работу. Ну и в аргументах функции сравнения видим наш пароль
Пароль (шифранул в base64, чтобы не спойлерить): LWMrKys=
-
5 hours ago, Taiwan said:
Можете выкладывать - что нарыли)
ну тут и так уже всё рассказали, я просто дампнул процесс через PETools и открыл через ILSpy => cкопировал строку с результатом и проверил.
После этого прочитал, что дампить нельзя ))
Собственно, если код не обфусцирован - то после ILSpy всё как на ладони. Да и после обфусцирования тоже - Knock-knock более менее ломается даже так:
Spoiler -
Just now, Taiwan said:
Бывает)) пароль и цвет лампочки после успешной авторизации - можешь в теме выложить.
Ну, раз задание с не дампить - подождем решения ) Могу скинуть в личку.
-
Упс, сначала читай - потом делай. Сдампил, посмотрел код, после этого внимательно прочитал задание )
Эх ))
-
20 minutes ago, MasterGH said:
От всего форума, от всех пользователей большой респект за видео. Супер
Спасибо Надеюсь следующее будет чуть более удачным.
Будем пробовать встроить получившийся скрипт в LUA форму с выбором машинки из выпадающего списка.
-
Записал видео со спавном машин рядом с игроком. Получилось ужасно долго, тихо и в конце похоже пропал звук.
Я решил всё равно выложить, я вряд ли смогу переснять, но если получится исправить - исправлю ссылку на видео.
Плюс если что есть текстовая статья - можно выложить её (но она как и видео - огромна).
В видео мы узнаем какие функции вызываются при создании автомобиля через чит-коды и набросаем небольшой скрипт, который будет создавать требуемый автомобиль перед игроком:
SpoilerИтоговый скрипт:
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat aobscan(RequestStream, 8B 4C 24 04 53 56 57 55 89 CD 8D 6C AD 00 8B 5C 24 18 8D 2C AD 00 00 00 00 8A 85 ?? ?? ?? ?? 3C 02) aobscan(loadRequestedStreams, 53 56 57 55 83 EC 10 80 ?? ?? ?? ?? 00 00 75 0E C6 ?? ?? ?? ?? 00 01 C6 ?? ?? ?? ?? 00 00) aobscan(CStreaming_releaseModel, 8B 4C 24 04 89 CA 8D 14 92 8D 14 95 00 00 00 00 80 A2 ?? ?? ?? ?? FE 81 F9 64 19 00 00 7C 08 81 F9 CD 1E 00 00) aobscan(releaseModel, 8B 4C 24 04 BA 64 19 00 00 8B 04 8D ?? ?? ?? ?? 0F BF 48 26 8D 81 64 19 00 00 01 CA 8D 04 80) aobscan(CVehicle_new, 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? C3 00 00 00 00 53 89 CB C7 03 ?? ?? ?? ?? 66 C7 83 ?? ?? ?? ?? 00 00 8B 43 64 56 85 C0 55 7C 12 B9) aobscan(CAutomobile_constructor, 53 56 57 55 81 EC A8 00 00 00 8B 84 24 C0 00 00 00 50 8B B4 24 C0 00 00 00 89 4C 24 0C) aobscan(CBike_constructor, 53 56 55 83 EC 10 8B 44 24 24 50 8B 6C 24 24 89 4C 24 10 E8 ?? ?? ?? ?? 8B 44 24 0C) aobscan(CBoat_constructor, 53 56 57 83 EC 08 8B 44 24 1C 50 8B 74 24 1C 89 4C 24 08 E8 ?? ?? ?? ?? 8B 44 24 04 B9 09 00 00 00) aobscan(CPlacable_setRotation, 53 89 CB 83 EC 18 D9 44 24 24 D9 44 24 20 D9 EE D9 EE D9 EE D9 C3 D9 FF DF E0 A9 00 04 00 00 74 07) aobscan(CWorld_add, 53 8B 5C 24 08 8A 43 50 56 24 07 0F B6 D0 83 FA 02 74 05 83 FA 03 75 10 8B 43 64 B9 ?? ?? ?? ?? 6A 01) aobscan(GetPlayerPed, 0F B6 05 ?? ?? ?? ?? 6B C0 2E 8B 04 C5 ?? ?? ?? ?? C3) globalalloc(create_vehicle_func, 2048) globalalloc(model_index, 4) label(exit) label(create_vehicle) model_index: // sabre turbo dd #206 create_vehicle_func: // modelIndex = 206 mov esi,[model_index] // v3 = loadedModelInfo[modelIndex].flags mov edi,esi lea edi,[edi+edi*4] lea edi,[edi*4] movzx ebx,byte ptr [edi+gta-vc.exe+54CDE1] push 1 push esi call RequestStream add esp,8 push 0 call loadRequestedStreams pop ecx // loadedModelInfo[modelIndex].available == 1 cmp byte ptr [edi+gta-vc.exe+54CDE0],1 jnz exit // v5 = v3 & 1; and ebx,01 { 1 } jnz @f push esi call CStreaming_releaseModel pop ecx push esi call releaseModel pop ecx @@: // check model flags for correct constructor mov edi,esi mov edi,[edi*4+"gta-vc.exe"+52C4D0] // get model handling id movsx eax,word ptr [edi+46] // DC - size of handling struct imul eax,eax,000000DC // calc model offset in handling array add eax,gta-vc.exe+577E60 add eax,14 // get model flags mov eax,[eax+cc] and eax,000f0000 ror eax,4*4 // cause Skimmer is a boat, not a plane O_o cmp esi,#190 jne @f mov eax,8 @@: // get correct constructor // == IS_BIKE cmp eax,1 jne @f push CBike_constructor push #1260 jmp create_vehicle @@: // IS_PLANE cmp eax,4 jne @f jmp exit @@: // IS_BOAT cmp eax,8 jne @f push CBoat_constructor push #1216 jmp create_vehicle @@: // else if is a car push CAutomobile_constructor push #1500 create_vehicle: // size param was been pushed above call CVehicle_new add esp,04 mov ebx,eax test ebx,ebx jz exit // mov constructor func to eax pop eax // vehicle:: mov ecx,ebx push 1 push esi // call vehicle::constructor call eax mov ebx,eax test ebx,ebx jz exit // mov to eax player struct call GetPlayerPed // rotate car as player push 0 fld dword ptr [eax+8] fld dword ptr [eax+4] fpatan fstp dword ptr [esp] push 0 push 0 // x into eax lea eax,[ebx+34] // placeble to ecx lea ecx,[ebx+04] call CPlacable_setRotation // move car to player call GetPlayerPed // small shift relatively to player push (float)5 // x fld dword ptr [eax+34] fld dword ptr [esp] fld dword ptr [eax+8] fmulp fsubp fstp dword ptr [ebx+34] // y fld dword ptr [esp] fld dword ptr [eax+4] fmulp fld dword ptr [eax+38] faddp fstp dword ptr [ebx+38] // z push [eax+3c] pop [ebx+3c] // remove our shift from stack add esp,4 // setup flags mov al,[ebx+50] and al,07 or al,20 mov [ebx+50],al // open the doors mov [ebx+00000230],1 push ebx call CWorld_add add esp,04 exit: ret [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(create_vehicle_func) dealloc(model_index) unregistersymbol(create_vehicle_func) unregistersymbol(model_index)
- 8
-
[offtop]Это было далекие года назад, вряд-ли Плюс в основном писал не я, я больше по скриптам - зеркало там забабахать, сохранение инвентаря, etc. У меня упорства никогда не хватает - закончить хоть одну игру )[/offtop]
-
7 hours ago, Vasabist said:
спасиб тебе, а не мне) буду разбираться. но все-таки, в чем там проблема то была. одним CE взломать нельзя, нужен раби? на самом деле нельзя найти базовый адрес? открыл твою новую тему, пока темный лес. да и первое видео, разрешение маленькое. пока недосмотрел.
у меня что-то не получается может кто еще подскажет. в целом нужно выйти на move_speed, но это всё ruby-объекты и я не уверен как они выглядят в памяти.
Опять же, получилось сделать небольшой скрипт, который можно использовать в любом месте игры. Просто добавь его через Memory Viewer - Tools - AutoAssembler, вставляешь текст и жмешь - File - Assign to current cheat table.
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat {$lua} if timer then timer.destroy() timer = nil end is_player_running = false function speedUp(...) if isKeyPressed(VK_SHIFT) then if not is_player_running then is_player_running = true autoAssemble([[ cur_command: dd turn_on createthread(run_script) ]]) end else if is_player_running then is_player_running = false autoAssemble([[ cur_command: dd turn_off createthread(run_script) ]]) end end end timer = createTimer() timer.setInterval(200) timer.setOnTimer(speedUp) timer.setEnabled(true) {$asm} globalalloc(run_script, 64) globalalloc(cur_command, 4) globalalloc(turn_on, 256) globalalloc(turn_off, 256) turn_on: db '$game_player.instance_variable_set(:@move_speed, 5)' #13 #10 0 turn_off: db '$game_player.instance_variable_set(:@move_speed, 4)' #13 #10 0 cur_command: dd turn_off run_script: push [cur_command] call RGSSEval pop eax ret [DISABLE] //code from here till the end of the code will be used to disable the cheat {$lua} if timer then timer.destroy() timer = nil is_player_running = nil end {$asm} dealloc(run_script) unregistersymbol(run_script) dealloc(turn_on) unregistersymbol(turn_on) dealloc(turn_off) unregistersymbol(turn_off) dealloc(cur_command) unregistersymbol(cur_command)
-
39 minutes ago, Garik66 said:
srg91, круто!!! Очень круто!!!
Только:
1. я бы перенёс твои статьи в раздел "Статьи для продвинутых". - тут уже нужен более продвинутый уровень в GH и программировании ИМХО.
2. может всё-таки уроки попробуешь в видео формате (с голосом конечно) - лучше 1 раз увидеть, чем много раз прочитать.
Спасибо
1. Да, конечно, можно и перенести. Просто мне кажется, такое ощущение из-за того, что это всё текстом. В целом ничего продвинутого вроде бы не происходит, но возможно я не правильно оцениваю. Поэтому мне кажется нет проблем, если перенесем - главное, что информация где-то есть )
2. В целом я структурировал текст, возможно попробую на досуге еще раз записать видео. Если получится - дозалью в "Видео" и добавлю ссылочку в начало статьи.
-
Spoiler10 hours ago, Vasabist said:
srg91, Серег, спасибо, но мне пока рано с луа. видимо у меня, просто непонимание самого перемещения по оси х и по оси у. раньше рассказывал, что на данной локации, по оси х нашел 2 разных адреса. один при заморозке пытается идти в сторону и возвращается на место. прибавляется +2 и отнимается.
второй адрес при заморозке. перемещается циклично при единичном нажатии на кнопку. прибавляется +256 и отнимается циклично
при перемещении на другую локацию, эти адреса меняют свои данные,.. то есть было [31,31]. (правая вертикальная грань прямоугольника, середина) при перемещение стало [1,51]. (ось оХ, координата левой грани, сторона прямоугольника по оси ОУ увеличились и поэтому значение середины оси оУ стало больше), но это все локальные значения,.. хотя мне и нужно увеличение скорости на локальных значениях.. там в игре кстати есть телепорт, перемещение с одной локации на другую, тоже интересно.
сделать скрипт на увеличение координаты не получается, вылетает.
push edx
mov edx,eax
cmp edx, адрес
pop edx
je returnhere
вот так скрипт работает, но при написании дополнительной строчки, просто еще раз там pop edx или inc edx - вылет
найти вроде скорость ГГ возможно, в cheat engine есть спидхак. но не факт, что не будет опять вылета. и инструкция анверно будет общей( с телепортом. вроде как можно
при нахождении базового адреса перемещения по оси ОХ, не получается найти... первый раз со смещением, значение перемещения оси ОХ совпадает, все нормально. второй раз без смещения, новый ардес нашел, нормально. третий же раз, значение перемещения по оси ОХ нового адреса не совпадает, заморозка не работает.. наверное руки не оттуда растут(
игра - легенда о царице опале
Привет. Получилось сделать через Ruby, лови скрипт:
Spoiler[ENABLE] // выделяем память на вызов функции globalalloc(run_script, 64) // выделяем память для текстовой команды на руби globalalloc(text, 1024) // Дописываем исходный класс text: // $game_system.se_play($data_system.escape_se) db 'class Game_Player < Game_Player' #13 #10 db ' alias orig_initialize initialize' #13 #10 db ' alias orig_update update' #13 #10 db #13 #10 db ' attr_accessor :move_speed' #13 #10 db ' attr_accessor :walk_speed' #13 #10 db ' attr_accessor :run_speed' #13 #10 db #13 #10 db ' def initialize' #13 #10 db ' orig_initialize' #13 #10 db #13 #10 db ' @walk_speed = @move_speed' #13 #10 db ' @run_speed = 5' #13 #10 db ' end' #13 #10 db #13 #10 db ' def running?' #13 #10 db ' return (@move_speed == @run_speed)' #13 #10 db ' end' #13 #10 db #13 #10 db ' def start_run' #13 #10 db ' $game_system.se_play($data_system.escape_se)' #13 #10 db ' @move_speed = @run_speed' #13 #10 db ' end' #13 #10 db #13 #10 db ' def start_walk' #13 #10 db ' @move_speed = @walk_speed' #13 #10 db ' end' #13 #10 db #13 #10 db ' def update' #13 #10 db ' if Input.press?(Input::SHIFT)' #13 #10 db ' if !running?' #13 #10 db ' start_run' #13 #10 db ' end' #13 #10 db ' else' #13 #10 db ' if running?' #13 #10 db ' start_walk' #13 #10 db ' end' #13 #10 db ' end' #13 #10 db #13 #10 db ' orig_update' #13 #10 db ' end' #13 #10 db 'end' #13 #10 db 0 // наш вызов функции run_script: // передаем нашу команду push text // вызываем Eval call RGSSEval // двигаем стек за собой pop eax ret createthread(run_script) [DISABLE] dealloc(run_script) unregistersymbol(run_script) dealloc(text) unregistersymbol(text)
Собственно пример использования:
SpoilerСделал через переопределение реальных классов, поэтому сработает только при включении чита в главном меню и только с новой игрой.
А так же будущие сохранения можно будет загрузить только предварительно включив чит.
Я думаю можно это обойти, но увы, на это у меня не было времени и скрипт превращается в кракозябры. Возможно немного позже
Если надоест звук - просто убери строчку:
db ' $game_system.se_play($data_system.escape_se)' #13 #10
Плюс расписал подробно, что делал - в статье.
Спасибо тебе, что поднял эту тему - было интересно.
-
Всем привет.
Спасибо Vasabist, который поднял тему RPG Maker. Он попросил сделать возможность ускорения персонажа при нажатии клавиши Shift.
Мы как-то давно с другом создавали игры на RPG Maker XP и я даже писал какие-то скрипты, расширяющие возможности персонажа.
Тогда уже был VX, но он нам не нравился из-за мелких моделей персонажей. В общем увидел тему, нахлынуло
Сами игры на RPG Maker XP - по сути большой набор Ruby-скриптов, которые выполняются движком RPG Maker с помощью Ruby-интерпретатора.
Сначала я предложил Vasabist использовать фильтры, так как повторяется ситуация с lua-играми - один код работает со множеством адресов.
Но потом вспомнил, что RPG Maker есть такой класс объектов как Events, который так же позволяет выполнить произвольный код при срабатывании определенных условий. Решил по-исследовать - думал выйти на функцию eval, которой скорее всего и выполняется пользовательский код.
Но всё оказалось даже проще, движок RPG Maker XP предоставляет функцию RGSSEval, которая выполняет произвольный код переданный в аргументах с доступом к глобальным переменным игры.
Собственно на RGSSEval выйти оказалось очень просто. Я скачал RPG Maker XP, создал на карте Event и попросил при взаимодействии с ним вывести строку "Hello, 1234444567":
После запустил игру и попробовал найти данную строку. И она нашлась! Ставим бряк на access и активируем Event. И в памяти мы видим следующее:
Да, ребят, это оказался memcpy. Но, это не главное. Главное что мы видим смещения адресов как RGSS104E.regex_error_code_to_str+52018, а это значит в движке RGSS104E присутствуют публичные функции, которые он любезно предоставляет нам. И открыв Memory View - View - Enumerate DLL's and Symbols мы видим RGSSEval:
Осталось понять как её использовать. В целом на это довольно просто выйти, жмем два раза на функцию данном окне или просто прыгаем на неё и выполняем Memory View - Tools - Dissect Code. В появившемся окне жмем старт и видим всех, кто использует RGSSEval:
Двойным нажатием на один из (Call) под RGSSEval прыгаем на место использования и видим, что у данной функции один аргумент (один push с выполняемой строкой перед вызовом) и она не двигает за собой стек (add esp,8 - восемь из-за того, чтобы не двигать стек два раза):
Собственно, можем копировать эти call-ы в наш AutoAssembler и пробовать вызывать. Для тестирования я набросал следующий скрипт:
Spoiler[ENABLE] // выделяем память на вызов функции globalalloc(run_script, 64) // выделяем память для текстовой команды на руби globalalloc(text, 1024) // Собственно просто просим вывести Hello, World text: db 'print "Hello, world!"' // наш вызов функции run_script: // передаем нашу команду push text // вызываем Eval call RGSSEval // двигаем стек за собой pop eax ret createthread(run_script) [DISABLE] dealloc(run_script) unregistersymbol(run_script) dealloc(text) unregistersymbol(text)
Запускаем и видим следующее:
Ура, функция работает как мы и ожидали. Проверить доступные глобальные переменные мы можем через print global_variables, либо обратившись к какой-нибудь из известных нам (или пока только мне..) переменных напрямую, например print $game_player. Можем творить!
Собственно весь код игры на Ruby можно увидеть в меню Tools - Script Editor в RPG Maker. И скорее всего код в вашем текущем проекте будет совпадать в большом количестве игр на данном движке. Поэтому можем попытаться написать обертки для стандартных функций для выполнения наших хитрых задач:
Для начала рекомендую пробовать написать обертку в своем проекте, а после внедрять её в Cheat Engine (еще лучше для начала прочитать краткий мануал по Ruby, но я так рвался закончить код что пропустил этот пункт). И сразу оговорюсь, обертки над классами будут работать только до того, как эти классы станут объектами. Поэтому наш будущий чит можно будет активировать лишь раз - до начала игры. Это поправимо, Ruby позволяет определять функции у инстансов на лету, но код становится менее лаконичным, поэтому я пошел по простому пути.
Собственно для того, чтобы ускорить нашего персонажа мы должны воздействовать на атрибут move_speed класса Game_Player. Он является приватным, поэтому для взаимодействия с ним лучше сделать его видимым для всех. Опять же нам повезло и у Game_Player есть метод update, в котором мы сможем проверять нажата ли клавиша Shift, но если бы его не было, пришлось бы менять move_speed из вне.
Приступим. Проще всего будет исправить Game_Player создав класс Game_Player унаследовав его от оригинала:
class Game_Player < Game_Player
Для изменения move_speed нам понадобится сохранять его оригинальное значение и назначать скорость бега, выделим для этого две переменные - walk_speed и run_speed. Ну и заодно сделаем move_speed публичным:
attr_accessor :move_speed attr_accessor :walk_speed attr_accessor :run_speed
Добавляем функции переключения скоростей и простую проверку на то, бежит ли персонаж:
def start_run @move_speed = @run_speed end def start_walk @move_speed = @walk_speed end def running? return (@move_speed == @run_speed) end
Осталось установить начальные значения переменным и добавить проверку на нажатый SHIFT. Для этого обернем функции initialize и update класса Game_Player с помощью функции alias:
alias orig_initialize initialize def initialize orig_initialize @walk_speed = @move_speed @run_speed = 5 end
Видите? Мы попросили обозначить родительский initialize как orig_initialize и вызывали его в нашей функции initialize.
Скорость бега я установил на 5 (оригинальная скорость 4). Если увеличивать больше - персонаж просто летает и им неудобно управлять.
Тоже самое сделаем с update. На нажатый SHIFT проверить очень просто - движок предоставляет объект Input, который знает нажата требуемая клавиша или нет (её я нашел в оригинальных скриптах игры):
def update if Input.press?(Input::SHIFT) if !running? start_run end else if running? start_walk end end orig_update end
Собственно и всё - скрипт готов. Если вы добавите его в оригинальные скрипты проекта, при нажатии клавиши SHIFT персонаж будет идти быстрее.
Осталось просто заменить наш print "Hello, World" на получившийся скрипт.
Выглядеть это будет следующим образом - каждую строку обрамляем в db 'xxxxx', где xxxx - строчка с кодом. В конце добавляем #13 #10 (каждое число через пробел) - это обычный Enter в конце строки (\n\r - если так привычнее). Я использовал для этого Sublime Text и его мультикурсоры, но можно воспользоваться простой заменой. Ну и не забываем 0 в конце, как конец текста. В итоге мы получим:
Spoilerdb 'class Game_Player < Game_Player' #13 #10 db ' alias orig_initialize initialize' #13 #10 db ' alias orig_update update' #13 #10 db #13 #10 db ' attr_accessor :move_speed' #13 #10 db ' attr_accessor :walk_speed' #13 #10 db ' attr_accessor :run_speed' #13 #10 db #13 #10 db ' def initialize' #13 #10 db ' orig_initialize' #13 #10 db #13 #10 db ' @walk_speed = @move_speed' #13 #10 db ' @run_speed = 5' #13 #10 db ' end' #13 #10 db #13 #10 db ' def running?' #13 #10 db ' return (@move_speed == @run_speed)' #13 #10 db ' end' #13 #10 db #13 #10 db ' def start_run' #13 #10 db ' @move_speed = @run_speed' #13 #10 db ' end' #13 #10 db #13 #10 db ' def start_walk' #13 #10 db ' @move_speed = @walk_speed' #13 #10 db ' end' #13 #10 db #13 #10 db ' def update' #13 #10 db ' if Input.press?(Input::SHIFT)' #13 #10 db ' if !running?' #13 #10 db ' start_run' #13 #10 db ' end' #13 #10 db ' else' #13 #10 db ' if running?' #13 #10 db ' start_walk' #13 #10 db ' end' #13 #10 db ' end' #13 #10 db #13 #10 db ' orig_update' #13 #10 db ' end' #13 #10 db 'end' #13 #10 db 0
Теперь просто заменяем db 'print "Hello, world!"' на получившийся код и можем запускать в главном меню игры
Итоговый скрипт:
Spoiler[ENABLE] // выделяем память на вызов функции globalalloc(run_script, 64) // выделяем память для текстовой команды на руби globalalloc(text, 1024) // Дописываем исходный класс text: db 'class Game_Player < Game_Player' #13 #10 db ' alias orig_initialize initialize' #13 #10 db ' alias orig_update update' #13 #10 db #13 #10 db ' attr_accessor :move_speed' #13 #10 db ' attr_accessor :walk_speed' #13 #10 db ' attr_accessor :run_speed' #13 #10 db #13 #10 db ' def initialize' #13 #10 db ' orig_initialize' #13 #10 db #13 #10 db ' @walk_speed = @move_speed' #13 #10 db ' @run_speed = 5' #13 #10 db ' end' #13 #10 db #13 #10 db ' def running?' #13 #10 db ' return (@move_speed == @run_speed)' #13 #10 db ' end' #13 #10 db #13 #10 db ' def start_run' #13 #10 db ' @move_speed = @run_speed' #13 #10 db ' end' #13 #10 db #13 #10 db ' def start_walk' #13 #10 db ' @move_speed = @walk_speed' #13 #10 db ' end' #13 #10 db #13 #10 db ' def update' #13 #10 db ' if Input.press?(Input::SHIFT)' #13 #10 db ' if !running?' #13 #10 db ' start_run' #13 #10 db ' end' #13 #10 db ' else' #13 #10 db ' if running?' #13 #10 db ' start_walk' #13 #10 db ' end' #13 #10 db ' end' #13 #10 db #13 #10 db ' orig_update' #13 #10 db ' end' #13 #10 db 'end' #13 #10 db 0 // наш вызов функции run_script: // передаем нашу команду push text // вызываем Eval call RGSSEval // двигаем стек за собой pop eax ret createthread(run_script) [DISABLE] dealloc(run_script) unregistersymbol(run_script) dealloc(text) unregistersymbol(text)
Итоговый скрипт на Ruby:
Spoilerclass Game_Player < Game_Player alias orig_initialize initialize alias orig_update update attr_accessor :move_speed attr_accessor :walk_speed attr_accessor :run_speed def initialize orig_initialize @walk_speed = @move_speed @run_speed = 5 end def running? return (@move_speed == @run_speed) end def start_run @move_speed = @run_speed end def start_walk @move_speed = @walk_speed end def update if Input.press?(Input::SHIFT) if !running? start_run end else if running? start_walk end end orig_update end end
Видео работы скрипта:
SpoilerДля видео дополнительно добавил звук ускорения - $game_system.se_play($data_system.escape_se)
Итого (или рубрика "О проблемах"):
- Скрипт прекрасно работает если включать его в меню, до запуска игры. Но не получится загрузить уже существующие сохранения, так как оно выполняет через дампы и нашему коду там просто не откуда взяться С этим можно бороться подключая код на-лету, но это уже в следующей серии.
- Так же скрипт ломает будущие сохранения - игра не сможет загрузиться, если чит не был включен. Увы, это частая проблема игр использующих моды, а мы фактически этим и занимались.
Вот и всё, народ )
- 5
-
23 minutes ago, saiberpro said:
Я думаю тут проще отталкиваться от самого RPG Maker в виду того что все классы и модули доступны для редактирования и изучения, но единственный минус это язык на котором написан Редактор это Ruby.
Собственно это я и хотел предложить
Для затравки кину данный скрипт, ну и пока сам попробую накидать изменение move_speed скриптом:
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat globalalloc(run_script, 64) globalalloc(text, 1024) text: db 'print "Hello, World!"' run_script: push text call RGSSEval pop eax ret createthread(run_script) [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(run_script) unregistersymbol(run_script) dealloc(text) unregistersymbol(text)
- 1
-
35 minutes ago, Vasabist said:
а как в будущем, сделать комбинацию стрелка+шифт. если шифт вообще не задействован в игре. и чтобы не только по оси Х, но и У. и что все-таки за два адреса отвечающие за перемещение по оси Х? так же и с осью У. там вообще разные координаты. одна как бы отвечает за перемещение на локальнйо карте, там значение небольшое, второе измеряется в тысячах, но не пойму, почему при заморозке адреса, ГГ циклично перемещается туда сюда(
я пробовал найти базовый адрес, вообще не получилось, первый адрес со смещением, значение адресов совпадает. второй - значения отличаются.
Не уверен, что подскажу про разные координаты, но с SHIFT сделать довольно просто.
Сначала думал написать про хоткеи на скрипт, но они переключают скрипт каждые сколько-то миллисекунд.
В общем если ты сделаешь правильную фильтрацию, предлагаю тебе вынести значение скорости в отдельную "переменную" (в скрипт speedUp).
После чего по таймеру проверять - нажат ли SHIFT:
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat {$lua} if timer then timer.destroy() timer = nil end function speedUp(...) if isKeyPressed(VK_SHIFT) then writeInteger('char_speed', 10) else writeInteger('char_speed', 1) end end timer = createTimer() timer.setInterval(200) timer.setOnTimer(speedUp) timer.setEnabled(true) {$asm} alloc(char_speed, 4) registersymbol(char_speed) char_speed: dd #1 [DISABLE] //code from here till the end of the code will be used to disable the cheat {$lua} if timer then timer.destroy() timer = nil end {$asm} char_speed: dd #1 dealloc(char_speed) unregistersymbol(char_speed)
- 1
-
Привет. Я думаю ты прав и эта инструкция работает с несколькими адресами.
Возможно тебе поможет видео от Garik66, в котором он объясняет как можно написать фильтр для такой инструкции:
Spoiler -
Всем привет.
Сегодня я расскажу о том, как создавать нужный автомобиль с помощью CE в GTA Vice City.
Как и говорил partoftheworlD, это очень просто, но раз возникают вопросы, я решил написать небольшой гайд.
Собственно, выйти на функции создания автомобилей очень просто - в игре есть читы, которые позволяют "вызывать" себе танк, катафалк и другие автомобили.
Поэтому выйдя на функцию обработки чита - мы найдем функции для создания автомобиля. Приступим.
Для начала вспомним несколько читов для создания автомобилей.
Я выбрал чит-код создания катафалка - THELASTRIDE , но так же можно использовать наш любимый чит создания танка - PANZER.
Собственно ввод кодов - это операция по сохранению текущего введенного символа в строку и последующая проверка получившейся строки на соответствие.
Каждый введенный символ записывается как в стек, поэтому в реальности эти читы выглядят как REZNAP и EDIRTSALEHT.
Но если поискать данный текст, мы его не найдем. Как же так спросите вы? На самом деле функция обработки чит-кодов срабатывает на вводе последней буквы, поэтому проще всего просто искать часть введенного кода.
Выйдем в меню игры, введем чит-код THELASTRIDE и попробуем поискать подстроку TSALEHT (не забываем переворачивать чит-код) в игре:
Ура, мы нашли текст по адресу 00A0F94F. Не будем его запоминать, он нам не пригодится, а сразу посмотрим, что же творится в памяти.
Нажмем на адрес правой кнопкой мыши и выбираем Browse this memory Region.
В памяти сразу поднимемся на пару строк вверх, обычно строки длиннее чем мы искали:
Ура, видим часть введенного нами кода. Так же могут присутствовать и другие куски текста, например на скришоте я немного побегал перед вводом чит-кода.
Теперь мы видим, что в реальности строка ввода чит-кода больше чем мы ожидали, а её начало находится там, где присутсвуют первые символы DW DIRTSAL, т.е. по адресу 00A0F94A. Добавим этот адрес в табличку (размер строки вычислен эмпирическим путем - посчитал не нулевые байты):
Теперь если в игре мы побегаем или введем другие чит-коды, увидим как они укладываются в памяти.
Посмотрим же, что использует этот буфер - попросим CE найти всех, кто использует данный адрес при вводе чит-кода
Сначала жмем ПКМ на адресе в главном окне CE и выбираем Find out what accesses this address:
Теперь переключимся в игру и введем любое слово, например HELLOGHL. Этим мы отсеим инструкции, которые используют наш адрес при обычном вводе. В окне появились следующие инструкции, мы будем их игнорировать:
SpoilerТеперь введем чит-код на создание автомобиля - THELASTRIDE. Внимательно следим за окном с инструкциями, т.к. в процессе ввода у меня появились еще несколько инструкций, которые используют адрес, но не влияют на появление машины. После ввода последней буквы видим следующую картину:
Уже можно заметить несколько функций, выполняющихся один раз. Для того, чтобы отсеять лишние инструкции, попробуем ввести другой чит-код, не относящийся к созданию автомобилей, например LEAVEMEALONE:
Ура, из предыдущих инструкций у нас осталось одна - инструкция mov byte ptr [00A0F94A] по адресу 004AC84A. Давайте выделим её и нажмем Show disassembler, чтобы увидеть, где она выполняется:
Собственно мы видим некое условие, после которого в наш буфер записывает 0x20, он же 32 он же пробел и вызывается некая функция gta-vc.exe+AE7C0 с аргументом AC. Заочно можно нас поздравить, потому что мы нашли нужную нам функцию, осталось только убедиться в этом.
Попробуйте тем же способом найти чит-функцию для создания танка. Я её уже нашел, давайте посмотрим:
Видно, что функция находится немного в другом месте, но отличие состоит только в том, какой аргумент передается в функцию - push записывает в стек A2 , вместо AC. В остальном код условия идентичен и я предлагаю попробовать просто вызывать функцию gta-vc.exe+AE7C0 с разными параметрами.
Для вызова функции мы будем использовать AutoAssembe и функцию createthread.
Зайдем в AutoAssembler (откроем Memory Viewer, меню Tools - AutoAssemble) и набросаем следующий скрипт:
Spoiler[ENABLE] // код, который выполнится при включении скрипта // выделяем память под нашу функцию, хватит и 64 байта alloc(our_func, 64) // начало нашей функции // фактически просто копируем код из игры our_func: // записываем в стек AC, он же 172 push 000000AC // вызываем некую функцию call gta-vc.exe+AE7C0 // убираем из стека наш AC pop ecx // конец функции ret // создаем поток, который прыгнет на нашу our_func и выполнит код createthread(our_func) [DISABLE] // код, который выполнится при выключении скрипта // просто освобождаем память от нашей функции dealloc(our_func)
Добавим его в таблицу через File - Assign to current cheat table и можно закрывать окно Auto assemble.
Скрипт выделяет в памяти игры 64 байта для себя, после чего записывает туда инструкции, которые мы скопировали из оригинальной игры и просит createthread выполнить их.
Обратите внимание, что аргументы в данную функцию передаются как push AC. В оригинальной игре после call мы можем заметить инструкцию pop ecx. Это значит, что функция не трогает стек и мы сам должны двигать его за ним (т.е. откатить push AC). Поэтому после функции выполняется pop и мы обязательно должны его скопировать, иначе ret будет пытаться вернуть поток не куда-то по адресу 0xAA123456, а по адресу 0x000000AC. Почему pop использует ecx? В данном случае - потому что левая пятка компилятора так решила и это ни на что не влияет (ecx спокойно можно заменить на eax и другие регистры).
Ура, скрипт в нашей таблице, давайте же активируем его! Смотрим на результат и...:
Наш скрипт вызвал чит-функцию создания катафалка! Наверное вы уже догадались, что если мы заменим push AC на push A2 мы увидим заспаунившися танк?
Получается, что AC - это катафалк, а A2 - танк. Поискав GTA Vice City Vehicle IDs, мы найдем, что действительно, A2 = 162 - это Vehicle ID танка, а AC = 172 - катафалк. Попробуем указать свой ID, один из списка, например 168 (такси). Передача аргумента превратится в push #168:
Можно сказать, что наш скрипт работает! Осталось вынести ID модели как переменную:
Spoiler[ENABLE] // код, который выполнится при включении скрипта // выделяем память под нашу функцию, хватит и 64 байта alloc(our_func, 64) // выделяем память под переменную alloc(our_vehicle_id, 4) // объявляем нашу our_func как переменную, // чтобы можно было вызывать createthread из другого скрипта registersymbol(our_func) // объявляем нашу our_vehicle_id как переменную, // чтобы можно было обращаться из таблицы registersymbol(our_vehicle_id) // записываем начальное занчение our_vehicle_id: // sabre turbo dd #206 // начало нашей функции // фактически просто копируем код из игры our_func: // записываем в стек наш ID машины push [our_vehicle_id] // вызываем некую функцию call gta-vc.exe+AE7C0 // убираем из стека наш AC pop ecx // конец функции ret [DISABLE] // код, который выполнится при выключении скрипта // удаляем информацию о функции dealloc(our_func) unregistersymbol(our_func) // удаляем информацию о переменной dealloc(our_vehicle_id) unregistersymbol(our_vehicle_id)
Я удалил из скрипта функцию вызова потока, чтобы переместить её в отдельный скрипт:
Spoiler[ENABLE] createthread(our_func) [DISABLE]
Теперь можно добавить в таблицу адрес our_vehicle_id, в который после записи скрипта будет записано число 206 (ID наикрутейшего Sabre Turbo).
Это число отвечает за то, что мы передаем в чит-функцию и меня его мы будем менять ID заспаунившегося автомобиля:
SpoilerРезультат:
И о проблемах
Скрипт фактически вызывает чит-код, поэтому мы начинаем слыть читерами, рейтинг в игре уменьшается и т.д. Это довольно легко обходится, т.к. если мы провалимся в функцию gta-vc.exe+AE7C0, перед самым ret есть две инструкции - add [gta-vc.exe+5B4F94],000003E8 и mov byte ptr [gta-vc.exe+60FB37],01 . Первая накидывает очки читерства (они хранятся отдельно) и устанавливает флаг - "этот парень использовал коды". Если их занопить - игра никогда не узнает о ваших проделках.
И вновь, потому что мы просто вызываем функцию чит-кода, она спаунит машины только на дорогу. Она находит ближайшую RoadPoint, прибавляет по оси Z несколько метров и спаунит авто. Поэтому создать авто прямо перед игроком так просто не получится.
Ну и основная проблема - это то, что в данной функции использует конструктор только для автомобилей. Он так же используется для вертолетов, но вот байки и лодки с ним заспаунить не получится (заспаунится, но крешится при посадке и выглядит, кхм...):
О том, как обойти эти проблемы и вызывать уже конструкторы классов мы узнаем в следующем уроке
С видеоформатом гайдов мне сложно работать (довольно тяжело как смотреть, так и записывать их), поэтому уроки в текстовом формате.
- 1
- 13
-
Всем привет. Я не особо опытный взломщик, поэтому пока взломал только новичковые пункты.
Ссылка на таблицу: https://yadi.sk/d/ystXS4wv3EbhfB
Код:
Spoiler1. Do not increase difficult level
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat "Train Me [v 2.0] By Taiwan.exe"+1FA6C5: db 90 90 90 [DISABLE] //code from here till the end of the code will be used to disable the cheat "Train Me [v 2.0] By Taiwan.exe"+1FA6C5: inc [eax+0C]
2. Set max score
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat alloc(newmem,2048) globalalloc(max_score, 4) label(returnhere) label(originalcode) label(exit) max_score: dd #2147483647 newmem: //this is allocated memory, you have read,write,execute access //place your code here originalcode: push [max_score] pop [edx+0C] lea edx,[ebp-04] exit: jmp returnhere "Train Me [v 2.0] By Taiwan.exe"+1FA734: jmp newmem nop returnhere: [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(newmem) "Train Me [v 2.0] By Taiwan.exe"+1FA734: add [edx+0C],eax lea edx,[ebp-04] //Alt: db 01 42 0C 8D 55 FC dealloc(max_score) unregistersymbol(max_score)
3. Set max record score after game
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat alloc(newmem,2048) globalalloc(record_score, 4) label(returnhere) label(originalcode) label(exit) record_score: dd #2147483647 newmem: //this is allocated memory, you have read,write,execute access //place your code here mov edi,[record_score] originalcode: mov [esi+0C],edi lea edx,[ebp-5C] exit: jmp returnhere "Train Me [v 2.0] By Taiwan.exe"+1FA50A: jmp newmem nop returnhere: "Train Me [v 2.0] By Taiwan.exe"+1FA502: db 90 90 90 90 90 90 [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(newmem) "Train Me [v 2.0] By Taiwan.exe"+1FA50A: mov [esi+0C],edi lea edx,[ebp-5C] //Alt: db 89 7E 0C 8D 55 A4 "Train Me [v 2.0] By Taiwan.exe"+1FA502: jng "Train Me [v 2.0] By Taiwan.exe"+1FA594 { ->Train Me [v 2.0] By Taiwan.exe+1FA594 } dealloc(record_score) unregistersymbol(record_score)
4. Beautiful date
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat alloc(newmem,2048) globalalloc(beautiful_date, 8) label(returnhere) label(originalcode) label(exit) beautiful_date: dq (double)117955 newmem: //this is allocated memory, you have read,write,execute access //place your code here originalcode: fstp qword ptr [ebp-20] fld qword ptr [beautiful_date] fstp qword ptr [ebp-20] wait push [ebp-1C] exit: jmp returnhere "Train Me [v 2.0] By Taiwan.exe"+1FA5ED: jmp newmem nop nop returnhere: [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(newmem) "Train Me [v 2.0] By Taiwan.exe"+1FA5ED: fstp qword ptr [ebp-20] wait push [ebp-1C] //Alt: db DD 5D E0 9B FF 75 E4 dealloc(beautiful_date) unregistersymbol(beautiful_date)
5. Beautiful time
[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat alloc(newmem,2048) globalalloc(beautiful_time, 8) label(returnhere) label(originalcode) label(exit) beautiful_time: dq (double)0.52426 newmem: //this is allocated memory, you have read,write,execute access //place your code here originalcode: fstp qword ptr [ebp-28] fld qword ptr [beautiful_time] fstp qword ptr [ebp-28] wait push [ebp-24] exit: jmp returnhere "Train Me [v 2.0] By Taiwan.exe"+1FA617: jmp newmem nop nop returnhere: [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(newmem) "Train Me [v 2.0] By Taiwan.exe"+1FA617: fstp qword ptr [ebp-28] wait push [ebp-24] //Alt: db DD 5D D8 9B FF 75 DC dealloc(beautiful_time) unregistersymbol(beautiful_time)
5. Beautiful time
Spoiler[ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat alloc(newmem,2048) globalalloc(beautiful_time, 8) label(returnhere) label(originalcode) label(exit) beautiful_time: dq (double)0.52426 newmem: //this is allocated memory, you have read,write,execute access //place your code here originalcode: fstp qword ptr [ebp-28] fld qword ptr [beautiful_time] fstp qword ptr [ebp-28] wait push [ebp-24] exit: jmp returnhere "Train Me [v 2.0] By Taiwan.exe"+1FA617: jmp newmem nop nop returnhere: [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(newmem) "Train Me [v 2.0] By Taiwan.exe"+1FA617: fstp qword ptr [ebp-28] wait push [ebp-24] //Alt: db DD 5D D8 9B FF 75 DC dealloc(beautiful_time) unregistersymbol(beautiful_time)
Видео:
SpoilerС рандомными значениями не совсем понял, пока разбираюсь.
Еще прошу дублировать задание текстом.
- 1
-
Смотри, ты регистрируешь этот адрес как символ, после чего можешь узнать его адрес через функцию getAddress:
Попробуй сделать так:
function potokF(senderThread) autoAssemble([[ aobscan(address3,90 5F 01 00 50 C3 00 01 00 50 C3 00 00) registersymbol(address3) ]]) offset = 4 someAddress = getAddress('address3') + offset end function CEButton6Click(sender) writeInteger(someAddress, 50) end
- 1
-
13 minutes ago, pachela said:
Может кто подсказать как реализуется, а то, что то у меня не получается.
Сначала при помощи AOBscaner мы находим адрес. Записываем его в переменную. Теперь мне нужно отталкиваясь от этого адреса найти новые адреса. Ну т.е. добавить смещение. Я пытался так
someVAlue = "address + 4"
Пробовал разные вариации. И с одинарной кавычкой и с квадратными скобками и т.п. Но результат один: В нужный мне адрес запись не происходит! Запись в АА не предлагать, я это умею делать, но мне нужно произвести либо writeInteger, а в АА я этого не умею делать. Либо пересчитывать значение с Интеджера в байты, а это мне кажется задачка посложнее, чем задать смещение уже имеющемуся адресу.
Адрес в памяти, даже если и выглядит как страшные "0x12345678" - по сути число.
Если ты делаешь это через LUA то просто возьми и прибавь к нему смещение без кавычек:
offset = 4 someAddress = mainAddress + offset
Запись в память по этому адресу:
writeInteger(someAddress, 666)
-
Судя по всему вопрос в экранировании, вот так кажется работает:
Spoilerstr = 'Config="(0.144; 6.299; 0.40)"' str2 = 'Config="%(0.144; 6.299; 0.40%)"' zamen = 'Config="(9.99; 9.99; 9.99)"' strs = string.gsub(str, str2, zamen) print(strs)
- 2
Использование createthread (или война со стеком)
in Статьи для продвинутых
Опубликовано · Изменено пользователем srg91
Исправил, спасибо.
Я тоже так думаю, но предыдущие статьи показали, что это продвинутый уровень