use*_*764 55 c c++ linux native machine-language
我正在尝试调用本机机器语言代码.这是我到目前为止(它得到一个总线错误):
char prog[] = {'\xc3'}; // x86 ret instruction
int main()
{
typedef double (*dfunc)();
dfunc d = (dfunc)(&prog[0]);
(*d)();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它确实正确地调用了函数并且它到达了ret指令.但是当它试图执行ret指令时,它有一个SIGBUS错误.是因为我在一个未清除执行的页面上执行代码或类似的东西?
那么我在这里做错了什么?
Hor*_*man 51
第一个问题可能是存储prog数据的位置不可执行.
至少在Linux上,生成的二进制文件会将全局变量的内容放在"数据"段或此处,这在大多数正常情况下都不可执行.
第二个问题可能是您调用的代码在某种程度上是无效的.在C中调用方法有一定的过程,称为调用约定(例如,您可能使用"cdecl").被调用的函数可能不足以"退回".它可能还需要进行一些堆栈清理等,否则程序会出现意外行为.一旦你遇到第一个问题,这可能是一个问题.
Rud*_*udi 48
您需要调用memprotect以使prog可执行的页面.以下代码确实进行了此调用,并且可以执行prog中的文本.
#include <unistd.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
char prog[] = {
0x55, // push %rbp
0x48, 0x89, 0xe5, // mov %rsp,%rbp
0xf2, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00,
//movsd 0x0(%rip),%xmm0 # c <x+0xc>
0x00,
0x5d, // pop %rbp
0xc3, // retq
};
int main()
{
long pagesize = sysconf(_SC_PAGE_SIZE);
long page_no = (long)prog/pagesize;
int res = mprotect((void*)(page_no*pagesize), (long)page_no+sizeof(prog), PROT_EXEC|PROT_READ|PROT_WRITE);
if(res)
{
fprintf(stderr, "mprotect error:%d\n", res);
return 1;
}
typedef double (*dfunc)(void);
dfunc d = (dfunc)(&prog[0]);
double x = (*d)();
printf("x=%f\n", x);
fflush(stdout);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Ism*_*eno 30
正如大家已经说过的那样,你必须确保它prog[]是可执行的,但是除非你正在编写JIT编译器,否则正确的方法是将符号放在可执行区域中,方法是使用链接描述文件或指定编译器允许的C代码,例如:
const char prog[] __attribute__((section(".text"))) = {...}
Run Code Online (Sandbox Code Playgroud)
Gra*_*ham 28
实际上,所有C编译器都可以通过在代码中嵌入常规汇编语言来实现.当然它是C的非标准扩展,但编译器编写者认识到它通常是必要的.作为非标准扩展,您必须阅读编译器手册并检查如何操作,但GCC"asm"扩展是一种相当标准的方法.
void DoCheck(uint32_t dwSomeValue)
{
uint32_t dwRes;
// Assumes dwSomeValue is not zero.
asm ("bsfl %1,%0"
: "=r" (dwRes)
: "r" (dwSomeValue)
: "cc");
assert(dwRes > 3);
}
Run Code Online (Sandbox Code Playgroud)
由于在汇编程序中很容易将堆栈丢弃,因此编译器通常还允许您识别将用作汇编程序一部分的寄存器.然后,编译器可以确保该函数的其余部分避开这些寄存器.
如果您自己编写汇编代码,则没有充分的理由将汇编程序设置为字节数组.它不只是代码味道 - 我说这是一个真正的错误,只有通过不知道"asm"扩展才会发生,这是将汇编程序嵌入C语言的正确方法.
基本上这已被限制,因为它是对病毒编写者的公开邀请.但是您可以在直接C中使用本机机器码分配和缓冲并设置它 - 这没问题.问题是呼唤它.虽然您可以尝试使用缓冲区的地址设置一个函数指针并调用它,但这种情况极不可能发挥作用,并且很可能会破坏下一版本的编译器,如果您确实设法哄它执行您想要的操作.因此,最好的办法是简单地使用一些内联汇编,设置返回并跳转到自动生成的代码.但是,如果系统能够防范这种情况,你必须找到绕过保护的方法,正如Rudi在他的回答中所描述的那样(但是对于一个特定的系统非常具体).