• Windows下动态加载可执行代码原理简述


    xiaotie同学比较蛋疼,问C#里面能不能动态加载SIMD的汇编代码。C#我不知道,反正c/c++下面这事情很好做。顺手花了几个小时写了个例子和这篇博客。

    总的来说,windows下要动态加载binary的话,基本上分以下几步。

    1. 首先要得到可执行代码的binary。 无论是在程序里面编译也好,或者从什么地方读出来,或者用户手把手的敲进去。

    2. 为这些binary分配一段具有可执行属性的内存。这是因为有Data Execution Prevention的存在,一般用malloc和new分配的内存页面不具有可执行这个属性,一旦执行就会产生STATUS_ACCESS_VIOLATION的异常。

    3. 下一步就是要跳转到这段内存上去执行了,可能要写一些汇编指令,在调用的过程中要注意栈是否平衡。

    基本上就是这三点了,下面来看具体的代码:

    第一步,拿到可执行的binary。这里我写了一个用SIMD指令的fastercopy函数,然后编译以后把它的二进制代码写成一个数组。

    int emitcode[] = 
    {
    0x83ec8b55,0x565340ec,0x0c758b57,0x8b087d8b,
    0x348d104d,0xcf3c8dce,0x6f0fd9f7,0x6f0fce04,
    0x0f08ce4c,0x10ce546f,0xce5c6f0f,0x646f0f18,
    0x6f0f20ce,0x0f28ce6c,0x30ce746f,0xce7c6f0f,
    0x04e70f38,0x4ce70fcf,0xe70f08cf,0x0f10cf54,
    0x18cf5ce7,0xcf64e70f,0x6ce70f20,0xe70f28cf,
    0x0f30cf74,0x38cf7ce7,0x7508c183,0xf8ae0fad,
    0x5e5f770f,0x5de58b5b,0xccccccc3};

    fastercopy函数是这样写的

    View Code
    void fastercopy(void* dst, void* src, int len)
    {
    __asm {
    mov esi, [src]
    // source array
    mov edi, [dst] // destination array
    mov ecx, [len] // number of QWORDS (8 bytes)

    lea esi, [esi
    +ecx*8] // end of source
    lea edi, [edi+ecx*8] // end of destination
    neg ecx // use a negative offset
    copyloop:
    movq mm0, qword ptr[esi
    +ecx*8]
    movq mm1, qword ptr[esi
    +ecx*8+8]
    movq mm2, qword ptr[esi
    +ecx*8+16]
    movq mm3, qword ptr[esi
    +ecx*8+24]
    movq mm4, qword ptr[esi
    +ecx*8+32]
    movq mm5, qword ptr[esi
    +ecx*8+40]
    movq mm6, qword ptr[esi
    +ecx*8+48]
    movq mm7, qword ptr[esi
    +ecx*8+56]
    movntq qword ptr[edi
    +ecx*8], mm0
    movntq qword ptr[edi
    +ecx*8+8], mm1
    movntq qword ptr[edi
    +ecx*8+16], mm2
    movntq qword ptr[edi
    +ecx*8+24], mm3
    movntq qword ptr[edi
    +ecx*8+32], mm4
    movntq qword ptr[edi
    +ecx*8+40], mm5
    movntq qword ptr[edi
    +ecx*8+48], mm6
    movntq qword ptr[edi
    +ecx*8+56], mm7
    add ecx,
    8
    jnz copyloop
    sfence
    // flush write buffer
    emms
    }
    }

    第二步,分配内存了。这里调用的是VirtualAlloc这个windows函数。在最后一个参数指定PAGE_EXECUTE_READWRITE属性。

        void* address = NULL;
    address
    = VirtualAlloc(NULL,
    sizeof(emitcode),
    MEM_COMMIT
    |MEM_RESERVE,
    PAGE_EXECUTE_READWRITE);

    memcpy(address,emitcode,
    sizeof(emitcode));

      

    第三步,调用这个存在address中的代码。

    可以自己写汇编代码调用:

        __asm {
    push 20h
    mov eax,dword ptr [src]
    push eax
    mov ecx,dword ptr [dst]
    push ecx
    mov ecx, dword ptr [address]
    call ecx
    add esp,0Ch
    }

    也可以使用函数指针来调用,反正知道函数的原型是啥。

        typedef void (*FASTCALL)(void* dst, void* src, int len);
    FASTCALL fastcall;
    fastcall
    = (FASTCALL)address;
    fastcall(dst2,src,
    64/2);

    这些实验是在Win7+VS2010 debug版本做的

    如果园子们在其他环境下做不出来,概不负责。

    如果在Win7+VS2010也做不出来,也概不负责。

    工程文件打包下载在这里(https://files.cnblogs.com/aoaoblogs/EmitSIMD.7z)

    据说博客要配图才有说服力,七夕到了,送些福利给QS们

      

  • 相关阅读:
    mysql 查找数组格式的字符串中是否包含某个值
    假期总结
    shell循环结构解析:for/while/case
    ansible笔记(15):循环(二)with_items/with_list/with_together/with_flattened
    ansible笔记(14):循环(一)
    解决报错Failed to start LSB: Bring up/down networking:MAC地址导致
    实现ENSP模拟器与物理主机、虚拟机通信
    zabbix4.2配置监控华为路由器:基于ENSP模拟器
    Grafana展示zabbix监控数据
    zabbix4.2配置监控TCP连接状态
  • 原文地址:https://www.cnblogs.com/aoaoblogs/p/2127143.html
Copyright © 2020-2023  润新知