exorcise Опубликовано 26 августа, 2014 Поделиться Опубликовано 26 августа, 2014 В байт-коде игры есть инструкция mov eax, [ecx]. Так вот в регистре ecx содержится адрес, как его подменить? я пишу программку на C++ и надо через неё подменить этот адрес. Ссылка на комментарий Поделиться на другие сайты Поделиться
Coder Опубликовано 26 августа, 2014 Поделиться Опубликовано 26 августа, 2014 Устанавливаешь брйкпоинт на этот кусок кода.Когда брейпоинт срабатывает, вызываешь GetThreadContext, подменяешь нужный тебе регистр, вызываешь SetThreadContext.Введи в поиске "аппаратный перехват", найдёшь мой топик с видеоуроком, там будет всё что тебе нужно, только малость код адаптировать придется. Ссылка на комментарий Поделиться на другие сайты Поделиться
exorcise Опубликовано 26 августа, 2014 Автор Поделиться Опубликовано 26 августа, 2014 Устанавливаешь брйкпоинт на этот кусок кода.Когда брейпоинт срабатывает, вызываешь GetThreadContext, подменяешь нужный тебе регистр, вызываешь SetThreadContext.Введи в поиске "аппаратный перехват", найдёшь мой топик с видеоуроком, там будет всё что тебе нужно, только малость код адаптировать придется.Так я уже нашёл кусок кода. Есть адрес, как именно в этом месте подменить регистр? Ведь на каждом байте меняются регистры.Можно пример как с адреса считать регистр, а затем переписать его? Ссылка на комментарий Поделиться на другие сайты Поделиться
keng Опубликовано 26 августа, 2014 Поделиться Опубликовано 26 августа, 2014 /*----------------------------------------------------------------------------*/ Можно, конечно, написать про использование inline-ассемблера. А-ля: DWORD GetMyData(){ DWORD result = 0; __asm mov result,ecx return result;} Но, я так понимаю, тебе надо сделать именно инжект кода. Регистры меняются не "каждый байт", а тогда, когда их меняют, за исключением всяких там esp и прочих ebp. Скажем, ecx не поменяется, пока его не поменяют. Алгоритм такой, я думаю: 0. Выделить где-нибудь 4 байта под переменную, хранящую значение.1. Заменить нужный кусок кода инструкцией вида "JMP 0x01234567", где 0x01234567 - адрес процедуры вроде GetMyData выше. Как заменить? JMP в опкодах - 0xE9, дальше нужно посмотреть, какой адрес больше - процедуры GetMyData или куска кода, который меняем. Из большего вычитаем меньшее (хотя вроде можно и наоборот), так как прыжки на адрес всегда делаются относительно текущей позиции - типа на 100 байт вперёд или на 100 байт назад.2. В процедуре делаем это самое "mov result,ecx" и выходим.3. Используем полученный result. В принципе, malloc(), virtualprotectex(), memcpy(), readprocessmemory() - вот и всё необходимое, помимо получения pID процесса (но тут уже варианты на твой вкус). PS: Таки getthreadcontext() будет всё равно попроще, я думаю. /*----------------------------------------------------------------------------*/ Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 26 августа, 2014 Поделиться Опубликовано 26 августа, 2014 В байт-коде игры есть инструкция mov eax, [ecx]. Так вот в регистре ecx содержится адрес, как его подменить? я пишу программку на C++ и надо через неё подменить этот адрес. Сводится к замене одной цепочки байт на другую (или другие цепочки байт по адресам). Цепочки получаются через дизассемблер/ассемблер. Запись в память на С++, изменение и возвращение прав на запись у страницы памяти, открытие и поиск процесса. Все что достаточно знать. Подробности можно поискать в Интернете или примерах исходников трейнеров. Ссылка на комментарий Поделиться на другие сайты Поделиться
exorcise Опубликовано 27 августа, 2014 Автор Поделиться Опубликовано 27 августа, 2014 А зачем вообще права менять? права стоят "чтение, выполнение" но всё прекрасно пишется в память. Это я имею ввиду запись байт-кода. А значения не пишутся, я ставлю полный доступ и не возвращаю исходное состояние.DWORD GetMyData(){ DWORD result = 0; __asm mov result,ecx return result;}^ это интересная штука. но как ей показать адрес с которого нужно считать ecx?да, я сделаю инжект кода. я вот думал сделать так:записать прямо в игре на пустой байт-код "00000000" своё значение например "500" и заменить инструкцию с "mov eax, [ecx]" на "mov eax, [game+offset]" но возникла проблема. Каждый запуск точка входа игры меняется и соответственно адрес game+offset тоже. Я то могу получить в dword этот адрес и смещение, но как его перевести в byte? или как записать dword в память. но ещё надо как-то "перевернуть" байты. поэтому я подумал сложно это. может проще просто подменить ecx. расскажите про getthreadcontext как им осуществить задуманное?а про выделение памяти тоже думал. тогда я бы просто прыгнул туда. но если моя программа закроется то память тоже, и не будет работать. А если не освобождать память, то это же тоже плохо. Но это ладно, я хотел через malloc сделать, но не понял как. Ссылка на комментарий Поделиться на другие сайты Поделиться
keng Опубликовано 27 августа, 2014 Поделиться Опубликовано 27 августа, 2014 /*----------------------------------------------------------------------------*/ int foo = 0;while(!_kbhit()) { __asm { push eax mov eax,[foo] inc eax mov [foo],eax pop eax } printf("foo address: 0x%X foo value: %d\n", &foo, foo); Sleep(1000);} Попробуй этот примерчик в отладчике поковырять, лучше - в OllyDbg каком-нибудь или том же Cheat Engine. Может, наведёт на мысли. /*----------------------------------------------------------------------------*/ Ссылка на комментарий Поделиться на другие сайты Поделиться
exorcise Опубликовано 27 августа, 2014 Автор Поделиться Опубликовано 27 августа, 2014 ну тут ты увеличил foo на 1 и переписал его. но &foo что делает? указатель на адрес содержажий это значение в памяти на данный момент? которая существует пока открыта программа. я не понимаю как в конкретном адресе это сделать. инструкции ведь хранятся по определенным адресам и выполняются в определенный момент.просто тут в этом примере значение храниться у тебя в программе. а у меня в дпугой. да ещё и в регистре.просто если я напишуmov [foo],eaxто регистр будет из моей программы, а надо из чужой. Ссылка на комментарий Поделиться на другие сайты Поделиться
keng Опубликовано 27 августа, 2014 Поделиться Опубликовано 27 августа, 2014 /*----------------------------------------------------------------------------*/ Верно, обращение по амперсанду выдаёт адрес переменной в памяти, обращение по имени - значение переменной. Точно так же, если к тебе пришёл адрес переменной, а ты хочешь получить значение из этого адреса - делаешь звёздочку: int foo = 0; // Объявляем переменную типа int с именем fooint* pFoo = &foo; // Объявляем указатель на int с именем pFoo, кладём в него // указатель на fooint bar = *pFoo; // Объявляем переменную типа int с именем bar и кладём в него //значение указателя pFoo В результате bar будет равно 0, то есть изначальному значению foo. То же самое, что и: int foo = 0;int bar = foo; Приведу ещё раз ассемблерную вставку из своего примера: __asm { push eax mov eax,[foo] inc eax mov [foo],eax pop eax } 0. Сохраняем регистр EAX в стек - мало ли, что там было до нашей функции. Вдруг важное?1. Кладём в EAX значение (не адрес!) переменной foo2. Увеличиваем eax на 13. Кладём в переменную foo новое значение То же самое, что и: foo++;или:foo += 1;или:foo = foo + 1; Дальше мы сообщаем в консоль значение foo и адрес foo в памяти. Если посмотреть этот код в отладчике, то увидим мы нечто такое: CPU DisasmAddress Hex dump Command 00023C0F |. 50 |PUSH EAX00023C10 |. A1 5C810200 |MOV EAX,DWORD PTR DS:[foo]00023C15 |. 40 |INC EAX00023C16 |. A3 5C810200 |MOV DWORD PTR DS:[foo],EAX00023C1B |. 58 |POP EAX Наша задача - как-то впихнуть в это место код, который скопирует EAX в какую-нибудь переменную, т.е. в какой-то другой адрес памяти. Вроде этого: CPU DisasmAddress Hex dump Command 00023C0F |. 50 |PUSH EAX00023C10 |. A1 5C810200 |MOV EAX,DWORD PTR DS:[foo]00023C15 |. 40 |INC EAX00023C16 |. A3 5C810200 |MOV DWORD PTR DS:[foo],EAX MOV [0x1234567],EAX00023C1B |. 58 |POP EAX Но просто так взять и записать кусок кода посреди программы мы не можем. Почему? Потому что компилятор - умный. И при компиляции весь код, переводя его в машинные инструкции (колоночка hex dump) кладёт их поближе друг к другу, но что самое важное - все переходы по коду (jmp, je, jne и так далее) осуществляются относительно текущего адреса. Следовательно, если порядок байт нарушить, впихнув лишних, то переходы, которые раньше ссылались на правильно заданные при компиляции места, начнут ссылаться в какую-нибудь далёкую страну, например SEG_FAULT. Приятный момент тут в том, что после компиляции в исполняемый файл код всегда будет лежать по одним и тем же адресам, т.е. адрес инструкции "MOV DWORD PTR DS:[foo],EAX" как был равен 0x00023C16, так там и останется. Нужно как-то впихнуть сюда ещё 5 байт (столько команда MOV и занимает вместе соперандами). Текущий код нельзя расширить, но можно поменять. Тут-то нам напомощь и спешит команда JMP. Стираем текущую команду MOV, вместо неё пихаем JMPна другой адрес, а там делаем так: MOV DWORD PTR DS:[foo],EAXMOV [0x1234567],EAXRET В итоге оригинальный код начинает выглядеть так: CPU DisasmAddress Hex dump Command 00023C0F |. 50 |PUSH EAX00023C10 |. A1 5C810200 |MOV EAX,DWORD PTR DS:[foo]00023C15 |. 40 |INC EAX00023C16 |. A3 5C810200 |JMP 0x123456700023C1B |. 58 |POP EAX Как такое сделать? Вот пример:0xCF3C62 - оригинальная инструкция программы.00CF3C08 - JMP SHORT 00CF3C62 - безусловный переход на оригинальную инструкцию, перемещённую в другое место. Правда, нам ещё надо вернуться обратно, и команда RET нам в этом не поможет - она прыгает на адрес, который лежит в стеке, а мы с ним ничего не делали. Поэтому нужен ещё один переход: Он на выделенной строчке. Теперь у нас есть место, куда ещё можно впихнуть какие-то команды. Мы знаем адрес переменной bar, куда и хотели записать значение регистра. Делаем: MOV DWORD PTR DS:[bar],EAX - то же самое, что и просто MOV [CF8160],EAX. CF8160 - указатель на bar, т.е. её адрес в памяти. Скобки означают, что мы используем значение по этому адресу, туда-то и кладём EAX. Примерно вот так инъекции кода в Cheat Engine и работают, разве что память выделяется функцией, а не берётся в текущем адресном пространстве программы (так делали чуток раньше, лет 5-10 назад). В общем, адрес кода, который надо заменить на JMP, можно посмотреть в отладчике. Там же можно посмотреть, какие байты записывать. И адрес переменной, куда будет записываться результат, тоже можно узнать там. Записывать нужно через WriteProcessMemory, выделить кусок памяти - через malloc, или же в Cheat Engine найти адрес такого куска. В моём примере переходы заменены на SHORT JMP, потому что прыгают недалеко (+\-127 байт), в нормальной ситуации это был бы такой опкод (машинный код): E9 AA BB CC DD AABBCCDD - перевёрнутый (это важно!) адрес, куда нужно прыгать. 0. VirtualAllocEx (длина байткода копирования в переменную + 5 (длина JMP) +длина байткода оригинальной инструкции)2. WriteProcessMemory (адрес функции, "E9 перевёрнутый_адрес_для_прыжка"3. WriteProcessMemory (адрес VirtualAllocEx, "байткод оригинальной инструкции"3. WriteProcessMemory (адрес VirtualAllocEx + длина байткода оригинальной инструкции, "байткод копирования в переменную")4. WriteProcessMemory (адрес VirtualAllocEx + длина байткода копирования в переменную + длина байткода оригинальной инструкции, "E9 на перевёрнутый адрес функции + 5*") *+5, потому что иначе мы прыгнем на начало нашего же JMP, а нам надо на следующую инструкцию после него. /*----------------------------------------------------------------------------*/ 2 Ссылка на комментарий Поделиться на другие сайты Поделиться
exorcise Опубликовано 27 августа, 2014 Автор Поделиться Опубликовано 27 августа, 2014 keng, спасибо за подробнейшее объяснение. А почему бы не писать в адресное пространство самой игры? Вот смотри. В игре есть огромный кусок байт-кода который содержит нули, тоесть 0x00000000. и такого пространства много. Почему бы не записать туда?Но куда бы я не записал, как перевернуть адрес? Допустим я программно выделил память у меня есть адрес в DWORD памяти которую я выделил, как перевернуть эти байты?а VirtualAllocEx тоже не простая штука. Ей ещё какие-то привилегии надо. Как их получить? Ссылка на комментарий Поделиться на другие сайты Поделиться
keng Опубликовано 27 августа, 2014 Поделиться Опубликовано 27 августа, 2014 /*----------------------------------------------------------------------------*/ Дык можно и в него писать, я не спорю. Только нельзя писать, куда попало. Надо или найти блок байт, который не используется (В отладчике Cheat Engine меню Tools - Scan for code caves), или же выделить через VirtualAllocEx, для неё нужно указать флажки, размер блока и pID процесса, который можно получить, например, через OpenProcess (пример моего кода): GetMemoryForCave proc bsize:dwordLOCAL phndl:dwordinvoke OpenProcess,PROCESS_ALL_ACCESS,NULL,pid.if eax != 0 mov phndl,eax invoke VirtualAllocEx,phndl,NULL,bsize,MEM_COMMIT,PAGE_EXECUTE_READWRITE push eax invoke CloseHandle,phndl pop eax.endif retGetMemoryForCave endp Как переворачивать адрес? Ну, это массив. Руками! WriteBuffer proc t:dword,f:dword,b:dword,s:dword push eax push ebx mov ebx, mov dword ptr [ebx],0E8h mov eax,t sub eax,f sub eax,5 mov byte ptr [ebx+1],al mov byte ptr [ebx+2],ah shr eax,16 mov byte ptr [ebx+3],al mov byte ptr [ebx+4],ah mov eax,s sub eax,5 cmp eax,0 je $+10 mov byte ptr [ebx+5],090h inc ebx dec eax jne $-11 pop ebx pop eax ret WriteBuffer endp /*----------------------------------------------------------------------------*/ 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
exorcise Опубликовано 27 августа, 2014 Автор Поделиться Опубликовано 27 августа, 2014 А если куда попало, то при вызове этого кода игрой может быть краш?руками? Если я создаю трейнер. Мне нужно чтобы он это умел. Ссылка на комментарий Поделиться на другие сайты Поделиться
keng Опубликовано 27 августа, 2014 Поделиться Опубликовано 27 августа, 2014 /*----------------------------------------------------------------------------*/ 1. Да. И чаще всего - будет.2. Руками - в смысле, алгоритм напиши. У тебя есть массив байт, тебе его надоразвернуть задом наперёд. /*----------------------------------------------------------------------------*/ Ссылка на комментарий Поделиться на другие сайты Поделиться
exorcise Опубликовано 27 августа, 2014 Автор Поделиться Опубликовано 27 августа, 2014 а почему регистр xmm* не принимает никаких цифр? только цифры из адресов movss xmm0, [*] почему нельзя так movss xmm0, 100500? < это бы сразу всё решило Ссылка на комментарий Поделиться на другие сайты Поделиться
keng Опубликовано 27 августа, 2014 Поделиться Опубликовано 27 августа, 2014 /*----------------------------------------------------------------------------*/ Ещё как принимает, но для него команды нужны специальные. Это один из регистров SSE - штуки-расширения процессора, которая позволяет работать со 128-битными числами. Это расширение часто используется в играх, где чего-нибудь много, например стратегиях - там много юнитов. Подробнее можно вот [тут] почитать. /*----------------------------------------------------------------------------*/ Ссылка на комментарий Поделиться на другие сайты Поделиться
exorcise Опубликовано 3 сентября, 2014 Автор Поделиться Опубликовано 3 сентября, 2014 keng, какой командой сделать так: xmm0, 10.0 Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 3 сентября, 2014 Поделиться Опубликовано 3 сентября, 2014 keng, какой командой сделать так: xmm0, 10.0http://goo.gl/gN6l1W Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения