使用MinGW构建的标准可执行文件中的陷阱内存访问 [英] Trap memory accesses inside a standard executable built with MinGW

查看:134
本文介绍了使用MinGW构建的标准可执行文件中的陷阱内存访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我的问题听起来像这样.

So my problem sounds like this.

我有一些平台相关的代码(嵌入式系统),可以写入在特定地址进行硬编码的MMIO位置.

I have some platform dependent code (embedded system) which writes to some MMIO locations that are hardcoded at specific addresses.

我使用标准可执行文件(主要用于测试)和模拟(因为在实际的硬件平台中查找基本错误需要更长的时间)内部的一些管理代码来编译此代码.

I compile this code with some management code inside a standard executable (mainly for testing) but also for simulation (because it takes longer to find basic bugs inside the actual HW platform).

为了减轻硬编码的指针,我只是将它们重新定义为内存池中的一些变量.而且效果很好.

To alleviate the hardcoded pointers, i just redefine them to some variables inside the memory pool. And this works really well.

问题在于某些MMIO位置(例如w1c)上存在特定的硬件行为,这使得正确"测试变得几乎不可能.

The problem is that there is specific hardware behavior on some of the MMIO locations (w1c for example) which makes "correct" testing hard to impossible.

这些是我想到的解决方案:

These are the solutions i thought of:

1-以某种方式重新定义对这些寄存器的访问,并尝试插入一些立即函数来模拟动态行为.确实不可用,因为有多种写入MMIO位置的方法(指针和填充物).

1 - Somehow redefine the accesses to those registers and try to insert some immediate function to simulate the dynamic behavior. This is not really usable since there are various ways to write to the MMIO locations (pointers and stuff).

2-以某种方式保留地址的硬编码,并通过段错误来捕获非法访问,找到触发的位置,准确提取访问的位置,进行处理并返回.我真的不确定这将如何工作(即使可能).

2 - Somehow leave the addresses hardcoded and trap the illegal access through a seg fault, find the location that triggered, extract exactly where the access was made, handle and return. I am not really sure how this would work (and even if it's possible).

3-使用某种仿真.这肯定会起作用,但将使在标准计算机上快速本机运行的全部目的无效.

3 - Use some sort of emulation. This will surely work, but it will void the whole purpose of running fast and native on a standard computer.

4-虚拟化?可能需要花费很多时间来实施.不确定如何获得收益.

4 - Virtualization ?? Probably will take a lot of time to implement. Not really sure if the gain is justifiable.

有人能在不深入的情况下实现这一目标吗?也许有一种方法可以某种方式操纵编译器来定义一个内存区域,每次访问都会为该内存区域生成一个回调.并不是x86/gcc方面的专家.

Does anyone have any idea if this can be accomplished without going too deep? Maybe is there a way to manipulate the compiler in some way to define a memory area for which every access will generate a callback. Not really an expert in x86/gcc stuff.

似乎不太可能以独立于平台的方式执行此操作,并且由于它仅是Windows,因此我将使用可用的API(似乎按预期工作).在这里找到这个问题:

It seems that it's not really possible to do this in a platform independent way, and since it will be only windows, i will use the available API (which seems to work as expected). Found this Q here:

在Win 7上设置了单步陷阱吗? /a>

Is set single step trap available on win 7?

我将把整个模拟的"注册文件放在多个页面中,加以保护,并触发一个回调,从该回调中我将提取所有必要的信息,执行我的工作,然后继续执行.

I will put the whole "simulated" register file inside a number of pages, guard them, and trigger a callback from which i will extract all the necessary info, do my stuff then continue execution.

谢谢大家的回复.

推荐答案

因此,解决方案(代码段)如下:

So, the solution (code snippet) is as follows:

首先,我有一个变量:

__attribute__ ((aligned (4096))) int g_test;

第二,在我的主要功能内,我执行以下操作:

Second, inside my main function, i do the following:

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;
}

这只是该功能的基本骨架.基本上,我保护变量所在的页面,我有一些链接列表,其中保存有指向该地址的函数和值的指针.我检查故障产生地址是否在列表中,然后触发回调.

This is only a basic skeleton for the functionality. Basically I guard the page on which the variable resides, i have some linked lists in which i hold pointers to the function and values for the address in question. I check that the fault generating address is inside my list then i trigger the callback.

在第一次命中时,页面保护将被系统禁用,但是我可以调用PRE_WRITE回调函数来保存变量状态.因为一步是通过EFlags发出的,所以它将紧随其后是一步异常(这意味着已写入变量),并且我可以触发WRITE回调.操作所需的所有数据都包含在ExceptionInformation数组中.

On first guard hit, the page protection will be disabled by the system, but i can call my PRE_WRITE callback where i can save the variable state. Because a single step is issued through the EFlags, it will be followed immediately by a single step exception (which means that the variable was written), and i can trigger a WRITE callback. All the data required for the operation is contained inside the ExceptionInformation array.

当有人尝试写入该变量时:

When someone tries to write to that variable:

*(int *)&g_test = 1;

先触发PRE_WRITE再执行WRITE

A PRE_WRITE followed by a WRITE will be triggered,

当我这样做时:

int x = *(int *)&g_test;

将发出READ.

通过这种方式,我可以以不需要修改原始源代码的方式来操纵数据流. 注意:这旨在用作测试框架的一部分,并且任何罚金都可以接受.

In this way i can manipulate the data flow in a way that does not require modifications of the original source code. Note: This is intended to be used as part of a test framework and any penalty hit is deemed acceptable.

例如,可以完成W1C(写入1以清除)操作:

For example, W1C (Write 1 to clear) operation can be accomplished:

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发出一个,并且要跟踪虚拟寄存器文件",因此会受到更大的损失.这样,根据注册的监视器,我只能保护特定的内存区域,也可以不保护任何区域.

This was possible also with seg-faults on illegal addresses, but i had to issue one for each R/W, and keep track of a "virtual register file" so a bigger penalty hit. In this way i can only guard specific areas of memory or none, depending on the registered monitors.

这篇关于使用MinGW构建的标准可执行文件中的陷阱内存访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆