C:在不编写太多程序集的情况下,执行手动MMAP文本段 问题:
我想在内存中映射一个新的(单页)文本段,并从一个C程序中执行它,而不用编写太多的汇编代码。 为此,我复制了程序本身的一个C函数,并设置了一个标志,这样当我跳转到它时,函数中的另一个路径将被调用。 你可能会认为这很愚蠢,但这和我们做的事情是一样的,例如,C:在不编写太多程序集的情况下,执行手动MMAP文本段 问题:,c,assembly,mmap,C,Assembly,Mmap,我想在内存中映射一个新的(单页)文本段,并从一个C程序中执行它,而不用编写太多的汇编代码。 为此,我复制了程序本身的一个C函数,并设置了一个标志,这样当我跳转到它时,函数中的另一个路径将被调用。 你可能会认为这很愚蠢,但这和我们做的事情是一样的,例如,fork()a进程 为什么? 我在Android上实现了一个最小的捕获和重放机制。 目前,我正在使用一种不太优雅的方式来重播真正的Android应用程序,这需要禁用ASLR,除了安全漏洞(目前可以忽略)之外,它还给我们带来了一些其他问题。 因此,从
fork()
a进程
为什么?
我在Android上实现了一个最小的捕获和重放机制。
目前,我正在使用一种不太优雅的方式来重播真正的Android应用程序,这需要禁用ASLR,除了安全漏洞(目前可以忽略)之外,它还给我们带来了一些其他问题。
因此,从一个纯C进程开始,我想进行一个转换,并重放一个Android函数。如果我使用下面的方法,它会比当前的方法好得多,当前的方法会介入第一个被调用的Android函数,并且属于主线程,并且。。呜呜呜呜
方法:
在C程序中,我映射一个文本段,它基本上是程序本身的C函数的副本。
为此,我调用一个名为entrypoint
的函数一次,获取程序计数器,设置一个标志并返回。
然后,我在内存中映射一个页面(entrypoint函数的代码大小不应大于此值),比如在0xabc000
,然后从我的PC开始在其中复制一个页面。
这意味着页面abc
应包含从pc到入口点函数(基本上从pc开始的页面)末尾的文本/代码副本
我将页面abc
的权限设置为可执行,然后跳转到它
我得到了一个分割冲突。但我想知道,为什么
示例代码:
额外信息
C库与我的应用程序静态链接。
我在一个具有arm架构的Android设备上运行这个。
不幸的是,操作系统没有提供任何关于分段的信息,因为这是一个与libc静态链接的纯C进程。那里没有与Android相关的东西。甚至连llog都没有
假设
装配操作正确(用于获取和跳转到PC)。
同时检查mmap和mprotect调用结果,我通过打印proc/self/maps
文件进行验证
随意的想法:
由于我与libc静态链接,并且为了调试目的,我正在对
entrypoint
执行一些printf
,可能是函数指针与原始代码段(我从中复制的entrypoint
的代码段)的开头有一些固定偏移,因此,当我试图从不同的偏移量执行时,会发生中断?您可以使用函数指针来完成此操作
(我坦率地承认,这可能违反了……的许多部分)
首先,typedef
是函数指针类型,因为如果使用typedef
,处理函数指针要容易得多:
typedef ( *randomAddressFunc_t )( void );
// define a function pointer
randomAddressFunc_t JUMP;
// assign an address to the function pointer:
JUMP = ( randomAddressFunc_t ) newtextseg;
// call it
JUMP();
应该尝试执行
newtexseg
指向的任何二进制位,因为您提到了在x86上进行检查,下面是一个对我有用的示例:
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
void printcaller()
{
printf("caller = %p\n", __builtin_return_address(0));
}
void entrypoint(void (*callback)())
{
callback();
}
int main()
{
unsigned char* block = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
printf("entrypoint = %p, block = %p\n", entrypoint, block);
memcpy(block, entrypoint, 1024);
printf("entrypoint():");
entrypoint(printcaller);
printf("block():");
((void (*)())block)(printcaller);
return 0;
}
注意,我必须显式地传递回调地址,因为一个简单的函数调用使用相对偏移量,该偏移量由于复制而中断。访问其他任何内容(例如全局数据,包括字符串文字)也可能会出现此问题
正如@EOF在其评论中提到的:
您只能从有效页面进行复制。如果起始地址不是
页面对齐,并且从中memcyp(…,…,page_SIZE)的页面是
在mmap()ed区域的最后一部分,您将得到memcpy()的segfault
本身
事实上,正是出于这个原因,我不得不使用1024
传递函数指针的备用版本:
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
struct functions
{
void* (*getcaller)();
int (*printf)(const char*, ...);
};
void* getcaller()
{
return __builtin_return_address(0);
}
void entrypoint(const char* fmt, const struct functions* functions)
{
functions->printf(fmt, functions->getcaller());
}
int main()
{
struct functions functions;
unsigned char* block = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
printf("entrypoint = %p, block = %p\n", entrypoint, block);
memcpy(block, entrypoint, 1024);
functions.printf = printf;
functions.getcaller = getcaller;
entrypoint("entrypoint(): %p\n", &functions);
((void (*)())block)("block(): %p\n", &functions);
return 0;
}
#包括
#包括
#包括
结构函数
{
void*(*getcaller)();
int(*printf)(常量字符*,…);
};
void*getcaller()
{
返回-内置返回地址(0);
}
void入口点(常量字符*fmt,常量结构函数*functions)
{
函数->printf(fmt,函数->getcaller());
}
int main()
{
结构功能;
unsigned char*block=mmap(NULL,4096,PROT_READ | PROT_WRITE | PROT_EXEC,MAP_PRIVATE | MAP_ANONYMOUS,-1,0);
printf(“入口点=%p,块=%p\n”,入口点,块);
memcpy(块,入口点,1024);
functions.printf=printf;
functions.getcaller=getcaller;
入口点(“入口点():%p\n”,函数(&F);
((void(*)()())块)((块():%p\n,&函数);
返回0;
}
您至少应该编译为独立于位置的代码。要获取PC,只需使用函数名即可。如果你得到一个segfault,最起码的要求是你使用一个调试器并告诉我们错误在哪里。@Jester很遗憾,我在Android设备上运行这个程序,操作系统没有为SEGV
生成任何事件。甚至没有创建<代码>墓碑,这是Android上进程崩溃时发生的情况。我想我必须通过适当的调试将其测试到x86,然后在arm上传输。您只能从有效页面进行复制。如果起始地址没有页面对齐,并且您memcpy(…,…,page_SIZE)
所在的页面是mmap()
ed区域的最后一部分,您将得到memcpy()
本身的segfult。可以通过使用函数指针,也可以通过设置程序计数器(如我的方法)它失败了。谢谢你花时间回答。似乎我不会尝试x86,因为我正在尝试使用远程gdb
服务器。我没有得到memcpy
错误,所以我正在尝试调查它。您的方法跳回原始代码,而在我的例子中,我想跳转到“独立”的代码块!我可以不使用数据段,但我需要访问库调用!如果代码指针被破坏,我唯一的希望就是asm
调用不会被破坏
$ ./a.out
entrypoint = 0x40064a, block = 0x7eff7cbd1000
entrypoint():caller = 0x400661
block():caller = 0x7eff7cbd1017
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
struct functions
{
void* (*getcaller)();
int (*printf)(const char*, ...);
};
void* getcaller()
{
return __builtin_return_address(0);
}
void entrypoint(const char* fmt, const struct functions* functions)
{
functions->printf(fmt, functions->getcaller());
}
int main()
{
struct functions functions;
unsigned char* block = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
printf("entrypoint = %p, block = %p\n", entrypoint, block);
memcpy(block, entrypoint, 1024);
functions.printf = printf;
functions.getcaller = getcaller;
entrypoint("entrypoint(): %p\n", &functions);
((void (*)())block)("block(): %p\n", &functions);
return 0;
}