Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C:在不编写太多程序集的情况下,执行手动MMAP文本段 问题:_C_Assembly_Mmap - Fatal编程技术网

C:在不编写太多程序集的情况下,执行手动MMAP文本段 问题:

C:在不编写太多程序集的情况下,执行手动MMAP文本段 问题:,c,assembly,mmap,C,Assembly,Mmap,我想在内存中映射一个新的(单页)文本段,并从一个C程序中执行它,而不用编写太多的汇编代码。 为此,我复制了程序本身的一个C函数,并设置了一个标志,这样当我跳转到它时,函数中的另一个路径将被调用。 你可能会认为这很愚蠢,但这和我们做的事情是一样的,例如,fork()a进程 为什么? 我在Android上实现了一个最小的捕获和重放机制。 目前,我正在使用一种不太优雅的方式来重播真正的Android应用程序,这需要禁用ASLR,除了安全漏洞(目前可以忽略)之外,它还给我们带来了一些其他问题。 因此,从

我想在内存中映射一个新的(单页)文本段,并从一个C程序中执行它,而不用编写太多的汇编代码。 为此,我复制了程序本身的一个C函数,并设置了一个标志,这样当我跳转到它时,函数中的另一个路径将被调用。 你可能会认为这很愚蠢,但这和我们做的事情是一样的,例如,
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;
}