我想把函数的机器码复制到指定地址执行,懂的指点下

时间:2022-06-04 17:10:23
我现在有个需求,就是从服务器上接收一段函数的机器代码和参数然后在本地运行,如:

int fun (void)
{
    typedef void (pFun *)(char *);   //这个是函数的申明

    char code[1024];                 //这里放接收回来的代码内容
    char par[64];                    //参数

    getcode(code , par) ;            //从网络接收到代码和参数
    ((pFun)code)();                  //执行代码
                                     //这里出错。。。。。。。。。。。。。。

    ......
}

懂的告诉我下,我刚怎么做才行。
谢谢了。

13 个解决方案

#1


@___@ 太强了

不过定义在栈上的是不行的,看看VirtualAlloc这个函数和它的MEM_EXECUTE这个权限。还有这个机器码局限很多啊,不能有大jump,也不能调用api,写的人一定很强,偶像。

#2


有一系列问题需要解决:代码是32位还是64位的,是否特定于CPU(for intel or AMD),是完全重定位的还是需要一些初始化的?... 呵呵,是不是很麻烦呢?
其实也可以简单点, 假设客户端可以正确区分并正确请求相应的代码, 而代码用一个dll封装,那么程序的逻辑就可以简化成从服务器下载一个DLL文件并执行之,这样就可以思路清晰地实现楼主的要求了.(如果要求DLL直接从内存中加载也是可以的,google一下.)

#3


偶像。。关注下

#4


这个太强了。
这个太强了。

#5


谢谢楼上两位指点,看来我还得仔细琢磨下了。
用库也行,就是怕太大了,很多个函数呢。
网络传输的时候太大不行,一个函数就好几百字节呢,用UDP的,拆包也麻烦。

#6


初步考虑下,觉得原因如下:
我把代码拷贝到栈中,所以它的地址应该是SS:EBP,段寄存器是SS,而代码段寄存器是CS,地址应该是CS:EIP,
所以直接把SS:EBP强制转换为CS:EIP是不对的,这就要考虑重定位问题了,啊啊,太麻烦了,再去研究下怎么弄
吧,有结果了把这些东东全部帖上来,希望大家多多指点。

#7


哈哈,弄出来了。
一般情况下,CS是可读可执行不可写、SS是可读可写可执行、DS是可读可写不可执行。
为了防止栈溢出、操作系统会禁止对SS的执行,64位机已经在硬件上做了这个工作;32位机上要在栈上执行代码
只要用VirtualAlloc这个函数分配一段内存,打上可执行标记即可(superarhow老兄说的对啊),64位机没试过。
平常没玩过远程注入啊,其实这个技术已经很成熟了,刚看到别人的例子了。

伪代码如下:

UINT fun (LPVOID m_lpPar)
{
    ::MessageBox(NULL , "垃圾目标,封我号,扣我装备,我Cao你NN。" , "提示" , 0);
    return 0;
}

void test(void)
{
    typedef UINT (*pfun)(LPVOID);
    unsigned char * p = (unsigned char *)fun;
    int i = 0 ;
    while(p[i++] != 0xc3); //计算函数的长度。

    LPVOID m_lpThread = VirtualAlloc(NULL , i , MEM_RESERVE | MEM_COMMIT , PAGE_EXECUTE_READWRITE); //打上可执行标记、可读写

    DWORD m_dwNum = 0;   //函数的内容写进去
    WriteProcessMemory(GetCurrentProcess() , m_lpThread , p , i , &m_dwNum );

    AfxBeginThread((AFX_THREADPROC)m_lpThread, NULL , 0 , 0 , 0 , NULL);
    ......  //OK了,不过只能在发行状态运行哦。
    
    VirtualFree(m_lpThread, i , MEM_DECOMMIT | MEM_RELEASE);
    ......

}

#8


我保证,你接收到的代码,这样100%用不起

"垃圾目标,封我号,扣我装备,我Cao你NN。"
"提示"

在目标进程,这两个字符串 绝对是未定义的
函数 MessageBoxA 也不能保证地址一致

#9


能说明原因吗?
你试过了吗?

如果你没试过,你就不要臆断哦,会误导人的。
可惜这里不能发图,要不我就把运行的过程发上来。

另:
直接调用效果一样,((pfun)m_lpThread)(NULL);

#10


我看错了,老兄。哈哈

发过来的机器码中没有API调用,这个是本地的。
用API和静态数据是会出错。

#11


我不骗你,你的目的说得很明确,是从网络接受到的代码片断。

UINT   fun   (LPVOID   m_lpPar) 

        ::MessageBox(NULL   ,   "垃圾目标,封我号,扣我装备,我Cao你NN。"   ,   "提示"   ,   0); 
        return   0; 


代码里面,那两个字符串并不是保存在这段代码里面的.

如果真要做的话,建议:
1. 保证远程系统和目标系统一致,或者你自己提供一个DLL来wrap LoadLibrayA 和 GetProcAddress 函数
2. 字符串和函数地址请放在一个结构里面
3. 调用的时候,把这个结构的地址传给 thread.

看代码:


////////////////////////////////////////////////////////////////////////////
// 远程代码片断

typedef HINSTANCE (WINAPI * MYLOADLIBRARYPROC)(char*);
typedef HINSTANCE (WINAPI * MYGETPROCADDRESSPROC)(HINSTANCE, char*);
typedef HINSTANCE (WINAPI * MSGBOXPROC)(HWND, char*, char*, UINT);

struct tagREMOTEPARAM
{
MYLOADLIBRARYPROC pMyLoadLibrary;
MYGETPROCADDRESSPROC pMyGetProcAddress;
char szUser32[MAX_PATH];
char szMsgBoxA[64];
char szMsg[200];
char szTitle[100];
} REMOTEPARAM;

DWORD WINAPI MyThread(LPVOID pParam)
{
REMOTEPARAM *pParam = reinterpret_cast<REMOTEPARAM*>(pParam);

HINSTANCE hUser32 = pParam->pMyLoadLibrary(pParam->szUser32);
MSGBOXPROC pMsgBox = reinterpret_cast<MSGBOXPROC>(pParam->pMyGetProcAddress(hUser32, pParam->szMsgBoxA));

pMsgBox(NULL, pParam->szMsg, pParam->szTitle, MB_OK);

return 0;
}

void FillParam(REMOTEPARAM *pParam)
{
if(!pParam) return;

HINSTANCE hLib = LoadLibrary("MYWRAP.DLL");
pParam->pMyLoadLibrary = reinterpret_cast<MYLOADLIBRARYPROC>(GetProcAddress(hLib, "MyLoadLibrary"));
pParam->pMyGetProcAddress = reinterpret_cast<MYGETPROCADDRESSPROC>(GetProcAddress(hLib, "MyGetProcAddress"));
lstrcpy(pParam->szUser32, "User32.dll");
lstrcpy(pParam->szMsgBoxA, "MessageBoxA");
lstrcpy(pParam->szMsg, "This is the message text!");
lstrcpy(pParam->szTitle, "Title");
}

////////////////////////////////////////////////////////////////////////////
// 本地代码片断

void Dummy()
{
LPVOID pParamBuff;
LPVOID pCodeBuff;
DWORD dwID;

LoadLibrary("MYWRAP.DLL"); // 确保这个DLL已载入

pParamBuff = VirtualAlloc(...); // 为数据分配空间
pCodeBuff = VirtualAlloc(...); // 为代码分配空间, 要有 PAGE_EXECUTE_READWRITE 属性

// pReceivedParam 为接收到的数据, nParam 为数据长度
// pReceivedCode 为收到的代码, nCode 为代码长度
memcpy(pParamBuff, pReceivedParam, nParam);
memcpy(pCodeBuff, pReceivedCode, nCode);

CreateThread(NULL, NULL, reinterpret_cast<LPTHREAD_START_ROUTINE>(pCodeBuff), pParamBuff, NULL, &dwID);
}


那个DLL就比较简单,直接把 GetProcAddress 和 LoadLibrary 包装一次,可以保证本地和远程的两个函数在内存中的地址一致.

#12


打字真累啊,hehe

#13


老兄厉害,谢谢指教。
分加你了。

#1


@___@ 太强了

不过定义在栈上的是不行的,看看VirtualAlloc这个函数和它的MEM_EXECUTE这个权限。还有这个机器码局限很多啊,不能有大jump,也不能调用api,写的人一定很强,偶像。

#2


有一系列问题需要解决:代码是32位还是64位的,是否特定于CPU(for intel or AMD),是完全重定位的还是需要一些初始化的?... 呵呵,是不是很麻烦呢?
其实也可以简单点, 假设客户端可以正确区分并正确请求相应的代码, 而代码用一个dll封装,那么程序的逻辑就可以简化成从服务器下载一个DLL文件并执行之,这样就可以思路清晰地实现楼主的要求了.(如果要求DLL直接从内存中加载也是可以的,google一下.)

#3


偶像。。关注下

#4


这个太强了。
这个太强了。

#5


谢谢楼上两位指点,看来我还得仔细琢磨下了。
用库也行,就是怕太大了,很多个函数呢。
网络传输的时候太大不行,一个函数就好几百字节呢,用UDP的,拆包也麻烦。

#6


初步考虑下,觉得原因如下:
我把代码拷贝到栈中,所以它的地址应该是SS:EBP,段寄存器是SS,而代码段寄存器是CS,地址应该是CS:EIP,
所以直接把SS:EBP强制转换为CS:EIP是不对的,这就要考虑重定位问题了,啊啊,太麻烦了,再去研究下怎么弄
吧,有结果了把这些东东全部帖上来,希望大家多多指点。

#7


哈哈,弄出来了。
一般情况下,CS是可读可执行不可写、SS是可读可写可执行、DS是可读可写不可执行。
为了防止栈溢出、操作系统会禁止对SS的执行,64位机已经在硬件上做了这个工作;32位机上要在栈上执行代码
只要用VirtualAlloc这个函数分配一段内存,打上可执行标记即可(superarhow老兄说的对啊),64位机没试过。
平常没玩过远程注入啊,其实这个技术已经很成熟了,刚看到别人的例子了。

伪代码如下:

UINT fun (LPVOID m_lpPar)
{
    ::MessageBox(NULL , "垃圾目标,封我号,扣我装备,我Cao你NN。" , "提示" , 0);
    return 0;
}

void test(void)
{
    typedef UINT (*pfun)(LPVOID);
    unsigned char * p = (unsigned char *)fun;
    int i = 0 ;
    while(p[i++] != 0xc3); //计算函数的长度。

    LPVOID m_lpThread = VirtualAlloc(NULL , i , MEM_RESERVE | MEM_COMMIT , PAGE_EXECUTE_READWRITE); //打上可执行标记、可读写

    DWORD m_dwNum = 0;   //函数的内容写进去
    WriteProcessMemory(GetCurrentProcess() , m_lpThread , p , i , &m_dwNum );

    AfxBeginThread((AFX_THREADPROC)m_lpThread, NULL , 0 , 0 , 0 , NULL);
    ......  //OK了,不过只能在发行状态运行哦。
    
    VirtualFree(m_lpThread, i , MEM_DECOMMIT | MEM_RELEASE);
    ......

}

#8


我保证,你接收到的代码,这样100%用不起

"垃圾目标,封我号,扣我装备,我Cao你NN。"
"提示"

在目标进程,这两个字符串 绝对是未定义的
函数 MessageBoxA 也不能保证地址一致

#9


能说明原因吗?
你试过了吗?

如果你没试过,你就不要臆断哦,会误导人的。
可惜这里不能发图,要不我就把运行的过程发上来。

另:
直接调用效果一样,((pfun)m_lpThread)(NULL);

#10


我看错了,老兄。哈哈

发过来的机器码中没有API调用,这个是本地的。
用API和静态数据是会出错。

#11


我不骗你,你的目的说得很明确,是从网络接受到的代码片断。

UINT   fun   (LPVOID   m_lpPar) 

        ::MessageBox(NULL   ,   "垃圾目标,封我号,扣我装备,我Cao你NN。"   ,   "提示"   ,   0); 
        return   0; 


代码里面,那两个字符串并不是保存在这段代码里面的.

如果真要做的话,建议:
1. 保证远程系统和目标系统一致,或者你自己提供一个DLL来wrap LoadLibrayA 和 GetProcAddress 函数
2. 字符串和函数地址请放在一个结构里面
3. 调用的时候,把这个结构的地址传给 thread.

看代码:


////////////////////////////////////////////////////////////////////////////
// 远程代码片断

typedef HINSTANCE (WINAPI * MYLOADLIBRARYPROC)(char*);
typedef HINSTANCE (WINAPI * MYGETPROCADDRESSPROC)(HINSTANCE, char*);
typedef HINSTANCE (WINAPI * MSGBOXPROC)(HWND, char*, char*, UINT);

struct tagREMOTEPARAM
{
MYLOADLIBRARYPROC pMyLoadLibrary;
MYGETPROCADDRESSPROC pMyGetProcAddress;
char szUser32[MAX_PATH];
char szMsgBoxA[64];
char szMsg[200];
char szTitle[100];
} REMOTEPARAM;

DWORD WINAPI MyThread(LPVOID pParam)
{
REMOTEPARAM *pParam = reinterpret_cast<REMOTEPARAM*>(pParam);

HINSTANCE hUser32 = pParam->pMyLoadLibrary(pParam->szUser32);
MSGBOXPROC pMsgBox = reinterpret_cast<MSGBOXPROC>(pParam->pMyGetProcAddress(hUser32, pParam->szMsgBoxA));

pMsgBox(NULL, pParam->szMsg, pParam->szTitle, MB_OK);

return 0;
}

void FillParam(REMOTEPARAM *pParam)
{
if(!pParam) return;

HINSTANCE hLib = LoadLibrary("MYWRAP.DLL");
pParam->pMyLoadLibrary = reinterpret_cast<MYLOADLIBRARYPROC>(GetProcAddress(hLib, "MyLoadLibrary"));
pParam->pMyGetProcAddress = reinterpret_cast<MYGETPROCADDRESSPROC>(GetProcAddress(hLib, "MyGetProcAddress"));
lstrcpy(pParam->szUser32, "User32.dll");
lstrcpy(pParam->szMsgBoxA, "MessageBoxA");
lstrcpy(pParam->szMsg, "This is the message text!");
lstrcpy(pParam->szTitle, "Title");
}

////////////////////////////////////////////////////////////////////////////
// 本地代码片断

void Dummy()
{
LPVOID pParamBuff;
LPVOID pCodeBuff;
DWORD dwID;

LoadLibrary("MYWRAP.DLL"); // 确保这个DLL已载入

pParamBuff = VirtualAlloc(...); // 为数据分配空间
pCodeBuff = VirtualAlloc(...); // 为代码分配空间, 要有 PAGE_EXECUTE_READWRITE 属性

// pReceivedParam 为接收到的数据, nParam 为数据长度
// pReceivedCode 为收到的代码, nCode 为代码长度
memcpy(pParamBuff, pReceivedParam, nParam);
memcpy(pCodeBuff, pReceivedCode, nCode);

CreateThread(NULL, NULL, reinterpret_cast<LPTHREAD_START_ROUTINE>(pCodeBuff), pParamBuff, NULL, &dwID);
}


那个DLL就比较简单,直接把 GetProcAddress 和 LoadLibrary 包装一次,可以保证本地和远程的两个函数在内存中的地址一致.

#12


打字真累啊,hehe

#13


老兄厉害,谢谢指教。
分加你了。