C++ 在数据区域中执行代码需要做什么(段保护)
我在linux平台上工作,使用g++编写上述程序,将函数从代码区复制到数据区。如何更改数据段的保护以允许我执行复制的功能 代码如下:C++ 在数据区域中执行代码需要做什么(段保护),c++,c,assembly,linux-kernel,C++,C,Assembly,Linux Kernel,我在linux平台上工作,使用g++编写上述程序,将函数从代码区复制到数据区。如何更改数据段的保护以允许我执行复制的功能 代码如下: #include <stdio.h> #include <stdint.h> #include <string.h> #define Return asm volatile("pop %rbp; retq; retq; retq; retq; retq;") int64_t funcEnd=0xc35dc3c3c3c3c35d
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#define Return asm volatile("pop %rbp; retq; retq; retq; retq; retq;")
int64_t funcEnd=0xc35dc3c3c3c3c35d;
constexpr int maxCode=0x800;
int8_t code[maxCode];
void testCode(void){
int a=8,b=7;
a+=b*a;
Return;
}
typedef void (*action)(void);
int main(int argc, char **argv)
{
action a=&testCode;
testCode();
int8_t *p0=(int8_t*)a,*p=p0,*p1=p0+maxCode;
for(;p!=p1;p++)
if ( (*(int64_t*)p)==funcEnd ) break;
if(p!=p1){
p+=sizeof(int64_t);
printf("found\n");
memcpy(&code,(void*)a,p-(int8_t*)a);
((action)&code)();
}
printf("returning 0\n");
return 0;
}
#包括
#包括
#包括
#定义返回asm volatile(“pop%rbp;retq;retq;retq;retq;retq;”)
int64_t funcEnd=0xC35DC3C35D;
constexpr int maxCode=0x800;
int8_t代码[maxCode];
void测试代码(void){
INTA=8,b=7;
a+=b*a;
返回;
}
类型定义无效(*行动)(无效);
int main(int argc,字符**argv)
{
动作a=&testCode;
testCode();
int8_t*p0=(int8_t*)a,*p=p0,*p1=p0+maxCode;
对于(;p!=p1;p++)
如果(*(int64_t*)p)=funcEnd)中断;
如果(p!=p1){
p+=sizeof(int64_t);
printf(“找到的”\n);
memcpy(&code,(void*)a,p-(int8_*)a);
((行动)及守则)();
}
printf(“返回0\n”);
返回0;
}
这取决于您是在静态(构建时)还是在动态(运行时)执行此操作
构建时间
您需要告诉GCC将blob放在可执行的部分中。我们使用\uuuuuuuuuuuuuuu属性((section))
,并在创建节时指定节的属性
运行时
TL;DR:跳到我的答案的末尾,在那里我使用mmap
尽管其他人可能会质疑为什么您希望在运行时允许这样的操作,但请记住,这正是带有JIT编译器的VM(例如Java VM、.NET CLR等)在发出本机代码时所做的
您需要更改尝试执行的内存的内存保护。我们通过mprotect(addr,PROT\u EXEC)
来实现这一点。请注意,addr
必须与平台的页面大小对齐。在x86上,页面大小为4K。我们用它来保证这种一致性
(两者的)示例:
SELinux冲击
请注意,这会将可执行代码放在堆上,这可能有点危险。CentOS 7机器上的SELinux实际上拒绝了mprotect
呼叫:
SELinux正在阻止/home/jreinhart/so/a.out对进程使用execheap访问。
*****插件允许执行堆(53.1置信度)建议********************
如果您不认为/home/jreinhart/so/a.out应该需要映射可写和可执行的堆内存。
然后你需要报告一个bug。这是一个潜在的危险通道。
因此,我必须临时sudo setEnforced 0
,才能让它正常工作
但是,我不知道为什么,因为查看/proc/[pid]/maps
,页面清楚地标记为仅可执行,而不是SELinux指出的“可写和可执行”。如果我将memcpy
移动到mprotect
之后,我的进程就会出错,因为我正试图写入不可写内存。所以看来SELinux有点过于热心了
改用mmap
与mprotect
ing堆的一个区域(使用aligned\u alloc
分配)不同,使用mmap
更简单。这也避免了SELinux的任何问题,因为我们不尝试在堆上执行
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h> /* mmap() */
static uint8_t code[] = {
0xB8,0x2A,0x00,0x00,0x00, /* mov eax,0x2a */
0xC3, /* ret */
};
int main(void)
{
void *p = mmap(NULL, sizeof(code), PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (p==MAP_FAILED) {
fprintf(stderr, "mmap() failed\n");
return 2;
}
memcpy(p, code, sizeof(code));
int (*func)(void) = p;
printf("(dynamic) code returned %d\n", func());
pause();
return 0;
}
你需要<代码> McReule>代码>。我需要导入C++中用其他语言编译的函数作为数据。我在哪里找到McPuthman McEnter(它显示出来)C或C++?选择一个。我会选择
mmap
,因为在我看来,它是上述解决方案中最不“黑客”和可移植的。@tangrs我完全同意。使用mmap
和mprotect
正确管理您的页面权限是一件明智的事情。@JonathonReinhart我刚刚阅读了非常详细的评论。谢谢与此同时,我已经到达mmap并做了一些检查,但在执行时能够分配内存和复制,我得到了段错误。我会检查你的代码,必要时再回来。@JonathonReinhart它工作了,但没有在代码中使用“printf”。如果我使用对“printf”的调用,则调用的addres会被损坏,因此必须执行一些不同的操作。@user3723779请注意,调用
指令分支到一个相对地址。也就是说,调用
指令中的立即数告诉CPU要跳转多少字节(来自下一条指令)<代码>EIP
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h> /* mmap() */
static uint8_t code[] = {
0xB8,0x2A,0x00,0x00,0x00, /* mov eax,0x2a */
0xC3, /* ret */
};
int main(void)
{
void *p = mmap(NULL, sizeof(code), PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (p==MAP_FAILED) {
fprintf(stderr, "mmap() failed\n");
return 2;
}
memcpy(p, code, sizeof(code));
int (*func)(void) = p;
printf("(dynamic) code returned %d\n", func());
pause();
return 0;
}
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h> /* mmap(), mprotect() */
static uint8_t code[] = {
0xB8,0x2A,0x00,0x00,0x00, /* mov eax,0x2a */
0xC3, /* ret */
};
int main(void)
{
const size_t len = sizeof(code);
/* mmap a region for our code */
void *p = mmap(NULL, len, PROT_READ|PROT_WRITE, /* No PROT_EXEC */
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (p==MAP_FAILED) {
fprintf(stderr, "mmap() failed\n");
return 2;
}
/* Copy it in (still not executable) */
memcpy(p, code, len);
/* Now make it execute-only */
if (mprotect(p, len, PROT_EXEC) < 0) {
fprintf(stderr, "mprotect failed to mark exec-only\n");
return 2;
}
/* Go! */
int (*func)(void) = p;
printf("(dynamic) code returned %d\n", func());
pause();
return 0;
}