【问题标题】:How to run assembly code without creating a new process?如何在不创建新进程的情况下运行汇编代码?
【发布时间】:2012-06-02 05:41:57
【问题描述】:

该文件包含本机汇编代码,我想在当前进程中运行它。 但我不想创建真正的文件(.com 或 .exe),所以我尝试了:

...
using namespace std;
typedef void bitcode();
void testcode(){
    cout<<"test";
};
int main()
{
    bitcode *c=&testcode; // bitcode *c stands for the file containing the assembly code.
    bitcode *d=reinterpret_cast<bitcode*> (malloc(20));
    memcpy(d, c, 20);
    d(); // it doesn't work
    return 0;
};

但是,当我调用 d(); 时它不起作用。我想知道用 C/C++ 做这件事的正确方法是什么。

(我在 Windows 上,如果你能告诉我如何在 Linux 上制作它,我将不胜感激)

非常感谢!

PS:我不问“在另一个进程中运行可执行文件而不创建新进程。”

【问题讨论】:

标签: c++ c assembly native-code


【解决方案1】:

您需要映射一个可写页面,将代码写入其中,将页面切换为可执行,然后执行。

【讨论】:

  • 你的意思是我应该尽量避免DEP?你能详细告诉我如何做到这一点吗?非常感谢!
  • 请注意,有 wrong ways 这样做...
【解决方案2】:

Ignacio 已经描述了如何做的大致大纲。

由于您在 Windows 上,因此您需要的函数是 VirtualAlloc 而不是 malloc,特别是带有 PAGE_EXECUTE_READWRITE 标志以获得写入内存并执行它的权限。

然后,根据文档,您仍然需要在写入内存之后和执行之前使用VirtualProtect 显式设置执行标志。

最后,在使用内存后,您需要使用VirtualFree 而不是free 来释放它。

std::size_t size = 20;
void* mem = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// Write assembly opcodes to mem

DWORD old_protect;
VirtualProtect(mem, size, PAGE_EXECUTE_READWRITE, &old_protect);
typedef (void)(*fptr)();
fptr f = *reinterpret_cast<fptr*>(&mem);

f();

// Later …
VirtualFree(mem, 0, MEM_RELEASE);

注意reinterpret_cast 中添加的间接寻址。这是避免未定义行为所必需的:C++ 不允许在对象指针和函数指针之间进行转换。但是,上面的代码是实现定义的,因此就 C++ 而言是可以的。

此外,请注意,为了简单起见,我省略了上面的错误检查代码。您不得在实际代码中这样做。有关正确错误处理的信息,请参阅文档。

在 Linux 上,工作流程是类似的,只是对函数使用不同的名称。

【讨论】:

  • @linzuzeng 查看更新。显然需要VirtualProtect,即使权限相同。此外,确保您分配和保护的大小实际上对于您要执行的整个汇编代码足够大,否则执行将简单地尝试在受保护区域之外恢复。还要注意我的代码的其他更改——我应该更仔细地阅读文档。
  • @harold 是的,这就是问题所在:显然它有时即使使用错误的参数也能正常工作。例如VirtualProtect的最后一个参数不能为空。尽管如此,我知道使用 null 作为其最后一个值的代码(不是我的),并且多年来一直如此,没有任何问题。
【解决方案3】:

在 windows 上,这是我之前写的代码。它至少在我的机器上适用于 Win7+VS2010。

基本思想是调用 VirtualAlloc 来分配带有 PAGE_EXECUTE_READWRITE 标志的内存。 并以正确的方式调用它,即保持堆栈平衡。

#include "stdafx.h"
#include "windows.h"


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

int _tmain(int argc, _TCHAR* argv[])
{
    int *src = new int[64];
    int *dst = new int[64];
    int *dst2 = new int[64];

    for (int i = 0; i < 64; ++i){
        src[i] = i;
    }

    //fastercopy(dst,src, 64/2);

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

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

    //call emit code from assemble
    __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 
    }

    for (int i = 0; i < 64; ++i){
        printf("%d ",dst[i]);
    }

    //call emit code from function pointer
    typedef void (*FASTCALL)(void* dst, void* src, int len);
    FASTCALL fastcall;
    fastcall = (FASTCALL)address;
    fastcall(dst2,src,64/2);

    printf("\n");
    for (int i = 0; i < 64; ++i){
        printf("%d ",dst2[i]);
    }

    return 0;
}

【讨论】:

  • 如何使用汇编指令代码做同样的事情?例如 emitcode 是一个文本:move ax, add bx, ..ect
【解决方案4】:

..因为你也问过 linux
在这里,您可以使用mmap。
~例如:

foo.cpp:

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <assert.h>

void bar(int n) { printf("bar invoked: %d\n", n); }
typedef void(*fp_bar)(int);

typedef union
{
   unsigned char*   pb;
   void*            pv;
   void(*pf)();
} 
foocodes;

//some _unencrypted random foo code segment
const unsigned char ar_foos[]=
{
    0xbb,0x00,0x00,0x00,0x00,   //mov ebx addy of a function to invoke
    0xb8,0x0d,0x00,0x00,0x00,   //mov eax 13 ~int input arg 
    0x50,                       //push eax
    0xff,0xd3,                  //call absolute addy ebx
    0x5b,                       //pop
    0x90,                       //throw in a nop to make up 16 foos
    0xC3                        //return
};


int main()
{
    size_t foos_size=sizeof ar_foos;
    foocodes ufoos;
    fp_bar pbar=&bar;
    assert(4==sizeof pbar); //example requires a 32bit fn address

    ufoos.pv=mmap(
        NULL, 
        foos_size, 
        PROT_WRITE|PROT_EXEC, 
        MAP_PRIVATE|MAP_ANONYMOUS,
        -1, 
        0);

    memcpy(ufoos.pv, ar_foos, foos_size);    
    memcpy(ufoos.pb+1, &pbar, sizeof pbar); //poke in the bar fn address

    ufoos.pf(); //invoke foo codes

    return munmap(ufoos.pv, foos_size);    
}


&这里是命令行的东西:

$ uname -a
Linux violet-313 3.0.0-19-generic #33-Ubuntu SMP Thu Apr 19 19:05:57 UTC 2012 i686 i686 i386 GNU/Linux

$ gcc --version
gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
...

$ gcc -ofoo foo.cpp
$ ./foo
bar invoked: 13

(;ok,为了清楚起见,我也省略了错误处理;)

【讨论】:

    【解决方案5】:

    我不确定,但您是否在寻找 __asm 关键字

    更多信息: http://msdn.microsoft.com/en-us/library/aa297214(v=vs.60).aspx

    【讨论】:

      猜你喜欢
      • 2020-07-28
      • 1970-01-01
      • 1970-01-01
      • 2010-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-20
      相关资源
      最近更新 更多