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

MemoryHook (.dll) включение-выключение функции


RockHammer

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

Осваиваюсь в новом .dll чите.

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

Собственно, функции включаются - запись работает. Но функция некорректно выключается. А именно - крашем приложения.

Сама функция записи в память:

DWORD WINAPI MemoryHook(int Size, int address, char ourByte[]){	DWORD oldProtect = 0;	VirtualProtect((void*)address, Size, PAGE_EXECUTE_READWRITE, &oldProtect);	memcpy((void*)address, (PBYTE) ourByte, Size);	VirtualProtect((void*)address, Size, oldProtect, &oldProtect);	return true;}

Она работает. А вот моя логика. Точнее - программы:

if (aFun1 == true) MemoryHook(2, Fun.Kasper1, KasperOneOn);		else if (aFun1 == false) MemoryHook(2, Fun.Kasper1, KasperOneOff);

Знаю, очень похоже на говнокод) Можно даже спутать))) Просто на switch'e не совсем удобно. Там за один вызов switch можно работать только с одной переменной, а у меня их - уйма.

Итак, aFun - булевая переменная. При включении Directx checkBox'a она меняет свое положение с false на true. Собственно, когда выключается - обратно.

Перевод логики кода: если чекбокс1 включен, то мемсетим адрес байтами включенной функции. Иначе, если чекбокс выключен - мемсетим адрес байтами выключенной функции.

Функция включается. Но не выключается (крашит).

Кто знает, как пофиксить?

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

Байт одинаковое количество и защита снимается\ставится верно, так? Все функции возвращают какое-то значение в зависимости от успешности их выполнения. memset, насколько я помню, здесь - исключение, но вот VirtualProtect точно что-то возвращает. Проверь, что она нормально срабатывает и пишутся ли (или нет) нужные байты после вызова memset.

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

Байт одинаковое количество и защита снимается\ставится верно, так? Все функции возвращают какое-то значение в зависимости от успешности их выполнения. memset, насколько я помню, здесь - исключение, но вот VirtualProtect точно что-то возвращает. Проверь, что она нормально срабатывает и пишутся ли (или нет) нужные байты после вызова memset.

memcpy, а не set) Да, байт одинаковое количество - всегда. Потому что если даже функция убивается - она заменяется нупами. Но ни один из байтов не удаляется. На счет возвращение значения... Где конкретно? VirtualProtect ничего не возвращает - он только записывает. Однако есть некоторые "неточности" в функции записи:

void WINAPI MemoryHook(int Size, int address, char ourByte[]){	DWORD oldProtect = 0; 	VirtualProtect((void*)address, Size, PAGE_EXECUTE_READWRITE, &oldProtect); //вот наша функция изменения защиты на нужную	memcpy((void*)address, (PBYTE) ourByte, Size); //пишем...	VirtualProtect((void*)address, Size, oldProtect, &oldProtect); //ставим оригинальную защиту... но... как мы можем знать оригинальную защиту? (остальное ниже)}

Итак, имхо, проблема в VirtualProtect. Последней записью мы пишем в память НУЛЕВУЮ защиту. (DWORD oldProtect = 0;) А откуда мы знаем что там был ноль? Ведь там могло быть что угодно.

Нужные байты пишутся (при включении), при выключении не успеваю трассировать.

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

Так это, при первом вызове VIrtualProtect в oldProtect как раз и запихивается старая защита. Ну, PAGE_EXECUTE_READ, к примеру. Мы ее сохраняем, затем пишем нужные байты через memcpy, а потом ставим сохраненную на место. Второму вызову все равно нужно скормить oldProtect еще раз, потому что без этой шляпы функция нормально отработать не сможет. На это, в принципе, внимание можно и не обращать. Ну и как написано в MSDN про virtualprotect:

 

"If the function succeeds, the return value is nonzero."

 

(если функция выполнилась успешно, то возвращаемое значение - ненулевое)

 

Поэтому ты такой Int lala = virtualprotect(...), а затем смотришь, что приехало в lala и что в oldprotect, если там не нули, то все хорошо.

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

Так это, при первом вызове VIrtualProtect в oldProtect как раз и запихивается старая защита. Ну, PAGE_EXECUTE_READ, к примеру. Мы ее сохраняем, затем пишем нужные байты через memcpy, а потом ставим сохраненную на место. Второму вызову все равно нужно скормить oldProtect еще раз, потому что без этой шляпы функция нормально отработать не сможет. На это, в принципе, внимание можно и не обращать. Ну и как написано в MSDN про virtualprotect:

 

"If the function succeeds, the return value is nonzero."

 

(если функция выполнилась успешно, то возвращаемое значение - ненулевое)

 

Поэтому ты такой Int lala = virtualprotect(...), а затем смотришь, что приехало в lala и что в oldprotect, если там не нули, то все хорошо.

 

Пофиксил уже. Помогло в последнем вызове VirtualProtect скармливать не oldProtect, а PAGE_EXECUTE_READ.

Однако открылся новый баг... Я худею с этих читов! Одно лечим - другое калечим!

В общем, после этого фикса стали не корректно работать некоторые функции. Они крашат.

У меня все данные (размеры, байты включения, адреса. в общем - все) лежит в переменных/структурах и в прочих хранилищах, так что при написании очередной функции - я просто передаю MemoryHook переменные с этими данными. Так вот. Стали не корректно работать сначала две функции. Я начал их отлаживать, чтобы понять где конкретно проблема. В итоге дошел до одной. Психанул, передал им все значения не из переменных - а в ручную. У меня была версия, что где-то в переменных или структурах ошибка. Передал все данные, скомпилил, заинжектил. Теперь при запуске крашит. 

Стоит отметить, что при запуске инициализируются только отрисовка менюшки и взлом античита (который не изменялся). Ну еще функция Beep(500,200).

Проблема в page_execute_read?

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

Гм. Файл я получил, но ничего такого в нем не нашел. Если закомментировать вызов функции ProtectHacker - то работает?

 

Работает, до включения другой функции.

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

Всё таки для паблик релизов нужно учитывать, то что VirtualProtect может завершиться провалом.

Return value
 
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
Ссылка на комментарий
Поделиться на другие сайты

То есть вылет происходит именно при вызове ProtectHacker, так?

 

Нет, ты не понял. В ProtectHacker есть функции записи в память. Массовые. Эта функция инициализируется в потоке, при конекте дллки. Когда функция не закомментирована (т.е. работает) - крашит сразу после инжекта. Теперь, когад функцию ProtectHacker закоментил - крашит при включении любой функции, которая использует запись в память. Думаю проблема как раз функции MemoryHook (запись в память)

 

Всё таки для паблик релизов нужно учитывать, то что VirtualProtect может завершиться провалом.

Return value
 
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.

 

В таком случае, как быть?

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

Нет, ты не понял. В ProtectHacker есть функции записи в память. Массовые. Эта функция инициализируется в потоке, при конекте дллки. Когда функция не закомментирована (т.е. работает) - крашит сразу после инжекта. Теперь, когад функцию ProtectHacker закоментил - крашит при включении любой функции, которая использует запись в память. Думаю проблема как раз функции MemoryHook (запись в память)

В таком случае, как быть?

Проверяй, что тебе возвращает VirtualProtect и всё, в общем-то)

Если возвращает 0, то в логи или куда там ты ошибки пишешь добавляешь запись, например:

logs.put("Error 0x%X while accessing memory (здесь примерное место в коде, где возникла ошибка (название метода класса/функции или номер строки))", GetLastError());

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

Проверяй, что тебе возвращает VirtualProtect и всё, в общем-то)

Если возвращает 0, то в логи или куда там ты ошибки пишешь добавляешь запись, например:

logs.put("Error 0x%X while accessing memory (здесь примерное место в коде, где возникла ошибка (название метода класса/функции или номер строки))", GetLastError());

Как будучи дллкой создать MessageBox (о том, что ошибка при записи)?

Или нет, еще лучше: как создать отладочную консоль? Но не запускать ее при инжекте дллки, а только когда условие сработает (что VirtualProtect вернет нулик)

Вот переделанный код (пока что не тестил): 

DWORD WINAPI MemoryHook(int Size, int address, char ourByte[]){	DWORD oldProtect; 	DWORD ReadWriteProtect = VirtualProtect((void*)address, Size, PAGE_EXECUTE_READWRITE, &oldProtect);	//VirtualProtect((void*)address, Size, PAGE_EXECUTE_READWRITE, &oldProtect); 	DWORD ReadProtect = VirtualProtect((void*)address, Size, oldProtect, &oldProtect); 	DWORD ReadWriteProtect;	if (ReadWriteProtect = 0) { //проверочка		//сообщенька, что печалька случилась. выходим из функции.	} else if (ReadWriteProtect != 0) {		memcpy((void*)address, (PBYTE)ourByte, Size); //если все норм - мемсетим адреса		DWORD ReadProtect; //ставим прежнюю защиту		if (ReadProtect = 0) {  //если неудачное восстановление защиты, то...			//соощенька, что при восстановлении защиты памяти - печалька случилась. ну и return, конечно же.		} else if (ReadProtect != 0) {			//сообщенька, что все прошло успешно, все радуются пляшут и повсюду котята)		}	}	return true;}

З.ы.ы. попутный вопрос, как менять цвет текста в консоли, не меняя цвет бекграунда? Хочу прикрепить такую штучку, что если успешная запись - текст выводится зеленым. Если нет - красным.

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

Боже, ну и кастыль ты сделал)

По консолям я не спец, это можешь легко загуглить.

Сделай себе функцию обёртку (wrapper) над VirtualProtect.

// address - указатель на блок памяти; blockSize - размер блока памяти; newProtect - новая защита памяти; указатель на старую защиту памяти (опциональный параметр) bool SetMemoryProtect(void* address, size_t blockSize, unsigned int newProtect, unsigned int* oldProtect = 0){   DWORD filler;   if (blockSize < 4)      blockSize = 4;   if (!oldProtect)      oldProtect = &filler;     return VirtualProtect(address, blockSize, newProtect, oldProtect) != 0;}

Если функция вернет true, значит всё в порядке, если false, то пиши в логи. Можешь вообще в самой этой функции сделать кстати говоря и запись в логи, тогда вообще париться не придется.

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

// пример без восстановления оригинальной защиты памятиint main(){   void* address = 0x00403000;   if (SetMemoryProtect(address, 12, PAGE_EXECUTE_READWRITE, 0))   {      // тут что-то делаем памятью   }   return 0;}// пример с восстановлением оригинальной защиты памятиint main(){   void* address = 0x00403000;   unsigned int oldProtect = 0;   if (SetMemoryProtect(address, 12, PAGE_EXECUTE_READWRITE, &oldProtect))   {      // тут что-то делаем с памятью      // восстановление оригинальной защиты памяти      SetMemoryProtect(address, 12, oldProtect, 0);   }   return 0;}
Ссылка на комментарий
Поделиться на другие сайты

 

Боже, ну и кастыль ты сделал)

Спасибо) Я старался) Костыли - это то, на чем держится мой софт)))

// address - указатель на блок памяти; blockSize - размер блока памяти; newProtect - новая защита памяти; указатель на старую защиту памяти (опциональный параметр) bool SetMemoryProtect(void* address, size_t blockSize, unsigned int newProtect, unsigned int* oldProtect = 0){   DWORD filler; //Зачем инициализировалась эта переменная?    if (blockSize < 4) //откуда мы взяли четверку?      blockSize = 4; //странная операция   if (!oldProtect)  // Если oldProtect не равно... чему?      oldProtect = &filler; //то присваиваем значение oldProtect - filler'у. Зачем?     return VirtualProtect(address, blockSize, newProtect, oldProtect) != 0; //тут, как я понимаю, описан положительный исход.}

Не совсем понятно, что делает эта функция. Поставил комменты для конкретизации.

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

1. DWORD filler; filler не была инициализирована, а просто объявлена (и в ней может содержаться любой системный мусор. Почему я её не инициализировал? Потому что она служит только заглушкой, поэтому нам абсолютно без разницы какое значение будет в ней содержаться).

2. blockSize = 4; Даже если ты в VirtualProtect передашь размер блока менее 4х байт,с протект будет выставлен всё равно на 4 байт, я так сделал, чтобы минимизировать время затраты на работу функции VirtualProtect (хоть и на пару тактов процессора).

3. if (!oldProtect); если oldProtect равен нулю (т.е. мы не передали свой указатель), то в него будет записан указатель на filler, так как 4й параметр у функции VirtualProtect - обязательный.

4. return ...; нет, функция вернет истину (если результат вызова VirtualProtect вернет число не равное нулю) и ложь (в случае если вызов VirtualProtect вернёт 0).

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

2. blockSize = 4; Даже если ты в VirtualProtect передашь размер блока менее 4х байт,с протект будет выставлен всё равно на 4 байт, я так сделал, чтобы минимизировать время затраты на работу функции VirtualProtect (хоть и на пару тактов процессора).

Ok, а если размер, который я передам - больше четырех?

 

И, пример использования:

// пример с восстановлением оригинальной защиты памятиint main(){   void* address = 0x00403000;   unsigned int oldProtect = 0;   if (SetMemoryProtect(address, 12, PAGE_EXECUTE_READWRITE, &oldProtect)) //не совсем понятно условие... Если... Что? Мб if(SetMemoryProtect == true) или false?   {      // тут что-то делаем с памятью      /* ну да, memcpy(и погнали) */      // восстановление оригинальной защиты памяти      SetMemoryProtect(address, 12, oldProtect, 0); /* второй аргумент - размер? и четвертый... Т.е. никуда не записываем прежнюю защиту? */   }   return 0;}
Ссылка на комментарий
Поделиться на другие сайты

1. DWORD filler; filler не была инициализирована, а просто объявлена (и в ней может содержаться любой системный мусор. Почему я её не инициализировал? Потому что она служит только заглушкой, поэтому нам абсолютно без разницы какое значение будет в ней содержаться).

2. blockSize = 4; Даже если ты в VirtualProtect передашь размер блока менее 4х байт,с протект будет выставлен всё равно на 4 байт, я так сделал, чтобы минимизировать время затраты на работу функции VirtualProtect (хоть и на пару тактов процессора).

3. if (!oldProtect); если oldProtect равен нулю (т.е. мы не передали свой указатель), то в него будет записан указатель на filler, так как 4й параметр у функции VirtualProtect - обязательный.

4. return ...; нет, функция вернет истину (если результат вызова VirtualProtect вернет число не равное нулю) и ложь (в случае если вызов VirtualProtect вернёт 0).

VirtualProtect работает 4к блоками xD

ты собирался сказать что то вроде:

lea eax,[Buffer]invoke VirtualProtect,[Address],[PatchSize],PAGE_EXECUTE_READWRITE,eaxinvoke RtlMoveMemory,[Address],[Patch],[PatchSize]lea eax,[Buffer]invoke VirtualProtect,[Address],[PatchSize],[eax],eax

проверка на фейл VirtualProtect - явно на любителя костылей. кст я забил на такое давно. ставлю протект на весь модуль и работаю с ним как с обычным буфером.

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

VirtualProtect работает 4к блоками xD

ты собирался сказать что то вроде:

lea eax,[Buffer]invoke VirtualProtect,[Address],[PatchSize],PAGE_EXECUTE_READWRITE,eaxinvoke RtlMoveMemory,[Address],[Patch],[PatchSize]lea eax,[Buffer]invoke VirtualProtect,[Address],[PatchSize],[eax],eax

проверка на фейл VirtualProtect - явно на любителя костылей. кст я забил на такое давно. ставлю протект на весь модуль и работаю с ним как с обычным буфером.

0. "VirtualProtect работает 4к блоками xD" - идем на MSDN читать описание параметра dwSize.

1. "проверка на фейл VirtualProtect - явно на любителя костылей" - VirtualProtect не всегда возвращает успешный результат.

2. "Ставлю протект на весь модуль" - размер модуля может быть пару килобайт, а может быть и пару мегабайт/гигабайт. Модифицируя защиту памяти для всего модуля ты создаешь угрозу безопасности, помимо этого может произойти нарушение работы программы.

3. "Работаю с ним как с обычным буфером буффером" - рокхаммер тоже модифицирует память напрямую (memcpy).

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

3. "Работаю с ним как с обычным буфером буффером" - рокхаммер тоже модифицирует память напрямую (memcpy).

Конечно. Потому что не знаю как делать иначе. + memcpy легче в реализации.

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

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

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

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