Итак, решение (фрагмент кода) выглядит следующим образом:
Прежде всего, у меня есть переменная:
__attribute__ ((aligned (4096))) int g_test;
Во-вторых, внутри моей основной функции я делаю следующее:
AddVectoredExceptionHandler(1, VectoredHandler);
DWORD old;
VirtualProtect(&g_test, 4096, PAGE_READWRITE | PAGE_GUARD, &old);
Обработчик выглядит следующим образом:
LONG WINAPI VectoredHandler(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
static DWORD last_addr;
if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) {
last_addr = ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
ExceptionInfo->ContextRecord->EFlags |= 0x100; /* Single step to trigger the next one */
return EXCEPTION_CONTINUE_EXECUTION;
}
if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) {
DWORD old;
VirtualProtect((PVOID)(last_addr & ~PAGE_MASK), 4096, PAGE_READWRITE | PAGE_GUARD, &old);
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
Это всего лишь базовый каркас для функциональности.В основном я защищаю страницу, на которой находится переменная, у меня есть несколько связанных списков, в которых я держу указатели на функцию и значения для рассматриваемого адреса.Я проверяю, что адрес, генерирующий ошибку, находится внутри моего списка, затем я запускаю обратный вызов.
При первом попадании защиты защита страницы будет отключена системой, но я могу вызвать мой обратный вызов PRE_WRITE, где я могу сохранитьпеременное состояние.Поскольку через EFlags выдается один шаг, за ним сразу же следует исключение из одного шага (что означает, что переменная была записана), и я могу вызвать обратный вызов WRITE.Все данные, необходимые для операции, содержатся в массиве ExceptionInformation.
Когда кто-то пытается записать в эту переменную:
*(int *)&g_test = 1;
Будет запущен PRE_WRITE, за которым следует WRITE,
Когда я сделаю:
int x = *(int *)&g_test;
Будет выпущено ЧТЕНИЕ.
Таким образом, я могу манипулировать потоком данных таким образом, который не требует изменений исходного источника.код.Примечание: Это предназначено для использования в качестве части тестовой структуры, и любое попадание в штраф считается приемлемым.
Например, может быть выполнена операция W1C (запись 1 для очистки):
void MYREG_hook(reg_cbk_t type)
{
/** We need to save the pre-write state
* This is safe since we are assured to be called with
* both PRE_WRITE and WRITE in the correct order
*/
static int pre;
switch (type) {
case REG_READ: /* Called pre-read */
break;
case REG_PRE_WRITE: /* Called pre-write */
pre = g_test;
break;
case REG_WRITE: /* Called after write */
g_test = pre & ~g_test; /* W1C */
break;
default:
break;
}
}
Это было возможно и с ошибками сегмента на недопустимых адресах, но мне пришлось выдавать по одному для каждого R / W и отслеживать «виртуальный регистровый файл», чтобы получить больший штрафной удар.Таким образом, я могу охранять только определенные области памяти или нет, в зависимости от зарегистрированных мониторов.