pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1) 详解

本文详细介绍了在Linux系统中使用mmap进行内存映射时的页对齐操作,通过示例代码解释了如何计算page_offset以确保映射地址对齐。程序演示了当偏移量超过文件大小时的错误处理,并添加额外代码以使总字节数超过一页,便于调试。此外,还展示了如何处理mmap、write和munmap等系统调用的错误情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

发现很多文章都没有把pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1) 详细计算过程写出来,自己专门计算了一遍,附在文末。本例为man mmap中的原样实例程序,加上了一些额外的无关代码及打印输出信息 让总字节数超过一页大小sysconf(_SC_PAGE_SIZE)以方便查看调试信息

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main(int argc, char *argv[])
{
    char *addr;
    int fd;
    struct stat sb;
    off_t offset, pa_offset;
    size_t length;
    ssize_t s;

    if (argc < 3 || argc > 4) {
        fprintf(stderr, "%s file offset [length]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    fd = open(argv[1], O_RDONLY);
    if (fd == -1)
        handle_error("open");

    if (fstat(fd, &sb) == -1)           /* To obtain file size */
        handle_error("fstat");

    offset = atoi(argv[2]);
    pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
        /* offset for mmap() must be page aligned */
     if (offset >= sb.st_size) {
        fprintf(stderr, "offset is past end of file\n");
        exit(EXIT_FAILURE);
    }

    if (argc == 4) {
        length = atoi(argv[3]);
        if (offset + length > sb.st_size)
            length = sb.st_size - offset;
                /* Can't display bytes past end of file */

    } else {    /* No length arg ==> display to end of file */
        length = sb.st_size - offset;
    }

    addr = mmap(NULL, length + offset - pa_offset, PROT_READ,
                MAP_PRIVATE, fd, pa_offset);
    if (addr == MAP_FAILED)
        handle_error("mmap");

    printf("length = %ld\n", length);
	printf("offset = %ld\n", offset);
	printf("pa_offset = %ld\n", pa_offset);
	printf("length + offset - pa_offset = %ld\n", length + offset - pa_offset);
    
    s = write(STDOUT_FILENO, addr + offset - pa_offset, length);
    if (s != length) {
        if (s == -1)
            handle_error("write");

        fprintf(stderr, "partial write");
        exit(EXIT_FAILURE);
    }

    munmap(addr, length + offset - pa_offset);
    close(fd);

    exit(EXIT_SUCCESS);
}

//**额外增加的代码开始
//**加上了一些额外的无关代码及打印输出信息 让总字节数超过一页大小sysconf(_SC_PAGE_SIZE)以方便查看调试信息**开始
/* 
#include "head.h"
struct msgbuff{
    long mtype;
    char mtext[512];
};

void send_msg(int qid, int type, char *m_msg, int size){
    struct msgbuff msg;
    msg.mtype = type;
    strcpy(msg.mtext, m_msg);
    if(msgsnd(qid, (void *)&msg, sizeof(msg.mtext), IPC_NOWAIT) == -1){
        perror("msgsnd");
        exit(1);
    } 
}

void get_msg(int qid, int type){
    while (1){
        struct msgbuff msg;
        bzero(&msg, sizeof(msg));//清空缓冲区,也可用memmet实现
        if (msgrcv(qid, (void *)&msg, sizeof(msg.mtext),type, MSG_NOERROR) == -1) {
            perror("msgrcv");
            exit(1);
        }
        printf("抢到了资源: <%d> <%s>\n", type, msg.mtext);

    }
}

int main(int argc, char **argv){
    int opt, mode = 2, msgtype = 1;
    int msgq_id;
    char mtext[512] = {0};
    //mode == 1-> send
    while((opt = getopt(argc, argv, "st:rm:")) != -1){
        switch(opt){
            case 's':
                mode = 1;
                break;
            case 'r':
                mode = 2;
                break;
            case 't':
                msgtype = atoi(optarg);
                break;
            case 'm':
                strcpy(mtext, optarg);
                break;
            default:
                fprintf(stderr, "Usage: %s -[s|r] -t type -m msg\n", argv[0]);
                exit(1);
        }
    }
    //int msgget(key_t key, int msgflg);
    if((msgq_id = msgget(2021, IPC_CREAT | 0666)) < 0){
        perror("msgget");
        exit(1);
    }
    if (mode == 1){
        send_msg(msgq_id, msgtype, mtext, sizeof(mtext));
    } else {
        get_msg(msgq_id, msgtype);
    }

    return 0;
}
// ./a.out -s -t 1 -m "I'm the 1th!"
// ./a.out -r -t 1.msgq.c
// #include "head.h"
struct msgbuff{
    long mtype;
    char mtext[512];
};

void send_msg(int qid, int type, char *m_msg, int size){
    struct msgbuff msg;
    msg.mtype = type;
    strcpy(msg.mtext, m_msg);
    if(msgsnd(qid, (void *)&msg, sizeof(msg.mtext), IPC_NOWAIT) == -1){
        perror("msgsnd");
        exit(1);
    }
}

void get_msg(int qid, int type){
    while (1){
        struct msgbuff msg;
        bzero(&msg, sizeof(msg));//清空缓冲区,也可用memmet实现
        if (msgrcv(qid, (void *)&msg, sizeof(msg.mtext),type, MSG_NOERROR) == -1) {
            perror("msgrcv");
            exit(1);
        }
        printf("抢到了资源: <%d> <%s>\n", type, msg.mtext);

    }
}

int main(int argc, char **argv){
    int opt, mode = 2, msgtype = 1;
    int msgq_id;
    char mtext[512] = {0};
    //mode == 1-> send
    while((opt = getopt(argc, argv, "st:rm:")) != -1){
        switch(opt){
            case 's':
                mode = 1;
                break;
            case 'r':
                mode = 2;
                break;
            case 't':
                msgtype = atoi(optarg);
                break;
            case 'm':
                strcpy(mtext, optarg);
                break;
            default:
                fprintf(stderr, "Usage: %s -[s|r] -t type -m msg\n", argv[0]);
                exit(1);
        }
    }
    //int msgget(key_t key, int msgflg);
    if((msgq_id = msgget(2021, IPC_CREAT | 0666)) < 0){
        perror("msgget");
        exit(1);
    }
    if (mode == 1){
        send_msg(msgq_id, msgtype, mtext, sizeof(mtext));
    } else {
        get_msg(msgq_id, msgtype);
    }

    return 0;
}
// ./a.out -s -t 1 -m "I'm the 1th!"
// ./a.out -r -t 1.msgq.c#include "head.h"
struct msgbuff{
    long mtype;
    char mtext[512];
};

void send_msg(int qid, int type, char *m_msg, int size){
    struct msgbuff msg;
    msg.mtype = type;
    strcpy(msg.mtext, m_msg);
    if(msgsnd(qid, (void *)&msg, sizeof(msg.mtext), IPC_NOWAIT) == -1){
        perror("msgsnd");
        exit(1);
    } 
}

void get_msg(int qid, int type){
    while (1){
        struct msgbuff msg;
        bzero(&msg, sizeof(msg));//清空缓冲区,也可用memmet实现
        if (msgrcv(qid, (void *)&msg, sizeof(msg.mtext),type, MSG_NOERROR) == -1) {
            perror("msgrcv");
            exit(1);
        }
        printf("抢到了资源: <%d> <%s>\n", type, msg.mtext);

    }
}

int main(int argc, char **argv){
    int opt, mode = 2, msgtype = 1;
    int msgq_id;
    char mtext[512] = {0};
    //mode == 1-> send
    while((opt = getopt(argc, argv, "st:rm:")) != -1){
        switch(opt){
            case 's':
                mode = 1;
                break;
            case 'r':
                mode = 2;
                break;
            case 't':
                msgtype = atoi(optarg);
                break;
            case 'm':
                strcpy(mtext, optarg);
                break;
            default:
                fprintf(stderr, "Usage: %s -[s|r] -t type -m msg\n", argv[0]);
                exit(1);
        }
    }
    //int msgget(key_t key, int msgflg);
    if((msgq_id = msgget(2021, IPC_CREAT | 0666)) < 0){
        perror("msgget");
        exit(1);
    }
    if (mode == 1){
        send_msg(msgq_id, msgtype, mtext, sizeof(mtext));
    } else {
        get_msg(msgq_id, msgtype);
    }

    return 0;
}
// ./a.out -s -t 1 -m "I'm the 1th!"
// ./a.out -r -t 1.msgq.c#include "head.h"
struct msgbuff{
    long mtype;
    char mtext[512];
};

void send_msg(int qid, int type, char *m_msg, int size){
    struct msgbuff msg;
    msg.mtype = type;
    strcpy(msg.mtext, m_msg);
    if(msgsnd(qid, (void *)&msg, sizeof(msg.mtext), IPC_NOWAIT) == -1){
        perror("msgsnd");
        exit(1);
    } 
}

void get_msg(int qid, int type){
    while (1){
        struct msgbuff msg;
        bzero(&msg, sizeof(msg));//清空缓冲区,也可用memmet实现
        if (msgrcv(qid, (void *)&msg, sizeof(msg.mtext),type, MSG_NOERROR) == -1) {
            perror("msgrcv");
            exit(1);
        }
        printf("抢到了资源: <%d> <%s>\n", type, msg.mtext);

    }
}

int main(int argc, char **argv){
    int opt, mode = 2, msgtype = 1;
    int msgq_id;
    char mtext[512] = {0};
    //mode == 1-> send
    while((opt = getopt(argc, argv, "st:rm:")) != -1){
        switch(opt){
            case 's':
                mode = 1;
                break;
            case 'r':
                mode = 2;
                break;
            case 't':
                msgtype = atoi(optarg);
                break;
            case 'm':
                strcpy(mtext, optarg);
                break;
            default:
                fprintf(stderr, "Usage: %s -[s|r] -t type -m msg\n", argv[0]);
                exit(1);
        }
    }
    //int msgget(key_t key, int msgflg);
    if((msgq_id = msgget(2021, IPC_CREAT | 0666)) < 0){
        perror("msgget");
        exit(1);
    }
    if (mode == 1){
        send_msg(msgq_id, msgtype, mtext, sizeof(mtext));
    } else {
        get_msg(msgq_id, msgtype);
    }

    return 0;
}
// ./a.out -s -t 1 -m "I'm the 1th!"
// ./a.out -r -t 1.msgq.c#include "head.h"
struct msgbuff{
    long mtype;
    char mtext[512];
};

void send_msg(int qid, int type, char *m_msg, int size){
    struct msgbuff msg;
    msg.mtype = type;
    strcpy(msg.mtext, m_msg);
    if(msgsnd(qid, (void *)&msg, sizeof(msg.mtext), IPC_NOWAIT) == -1){
        perror("msgsnd");
        exit(1);
    } 
}

void get_msg(int qid, int type){
    while (1){
        struct msgbuff msg;
        bzero(&msg, sizeof(msg));//清空缓冲区,也可用memmet实现
        if (msgrcv(qid, (void *)&msg, sizeof(msg.mtext),type, MSG_NOERROR) == -1) {
            perror("msgrcv");
            exit(1);
        }
        printf("抢到了资源: <%d> <%s>\n", type, msg.mtext);

    }
}

int main(int argc, char **argv){
    int opt, mode = 2, msgtype = 1;
    int msgq_id;
    char mtext[512] = {0};
    //mode == 1-> send
    while((opt = getopt(argc, argv, "st:rm:")) != -1){
        switch(opt){
            case 's':
                mode = 1;
                break;
            case 'r':
                mode = 2;
                break;
            case 't':
                msgtype = atoi(optarg);
                break;
            case 'm':
                strcpy(mtext, optarg);
                break;
            default:
                fprintf(stderr, "Usage: %s -[s|r] -t type -m msg\n", argv[0]);
                exit(1);
        }
    }
    //int msgget(key_t key, int msgflg);
    if((msgq_id = msgget(2021, IPC_CREAT | 0666)) < 0){
        perror("msgget");
        exit(1);
    }
    if (mode == 1){
        send_msg(msgq_id, msgtype, mtext, sizeof(mtext));
    } else {
        get_msg(msgq_id, msgtype);
    }

    return 0;
}
// ./a.out -s -t 1 -m "I'm the 1th!"
// ./a.out -r -t 1.msgq.c#include "head.h"
struct msgbuff{
    long mtype;
    char mtext[512];
};

void send_msg(int qid, int type, char *m_msg, int size){
    struct msgbuff msg;
    msg.mtype = type;
    strcpy(msg.mtext, m_msg);
    if(msgsnd(qid, (void *)&msg, sizeof(msg.mtext), IPC_NOWAIT) == -1){
        perror("msgsnd");
        exit(1);
    } 
}

void get_msg(int qid, int type){
    while (1){
        struct msgbuff msg;
        bzero(&msg, sizeof(msg));//清空缓冲区,也可用memmet实现
        if (msgrcv(qid, (void *)&msg, sizeof(msg.mtext),type, MSG_NOERROR) == -1) {
            perror("msgrcv");
            exit(1);
        }
        printf("抢到了资源: <%d> <%s>\n", type, msg.mtext);

    }
}

int main(int argc, char **argv){
    int opt, mode = 2, msgtype = 1;
    int msgq_id;
    char mtext[512] = {0};
    //mode == 1-> send
    while((opt = getopt(argc, argv, "st:rm:")) != -1){
        switch(opt){
            case 's':
                mode = 1;
                break;
            case 'r':
                mode = 2;
                break;
            case 't':
                msgtype = atoi(optarg);
                break;
            case 'm':
                strcpy(mtext, optarg);
                break;
            default:
                fprintf(stderr, "Usage: %s -[s|r] -t type -m msg\n", argv[0]);
                exit(1);
        }
    }
    //int msgget(key_t key, int msgflg);
    if((msgq_id = msgget(2021, IPC_CREAT | 0666)) < 0){
        perror("msgget");
        exit(1);
    }
    if (mode == 1){
        send_msg(msgq_id, msgtype, mtext, sizeof(mtext));
    } else {
        get_msg(msgq_id, msgtype);
    }

    return 0;
}
// ./a.out -s -t 1 -m "I'm the 1th!"
// ./a.out -r -t 1.msgq.c

*/
//**额外增加的结束
//加上了一些额外的无关代码及打印输出信息 让总字节数超过一页大小sysconf(_SC_PAGE_SIZE)以方便查看调试信息** 结束

编译命令:gcc mmap.c
程序执行格式: ./a.out file offset [length]
格式解析:./a.out +file 文件名 + offset偏移量 +length可选长度

./a.out mmap.c 10000

//执行结果,示例
length = 2266
offset = 10000
pa_offset = 8192

假设文件mmap.c文件大小为12266 ,
length =文件总大小(12266) - 偏移量offset(1000) = 2266

4096(sysconf(_SC_PAGE_SIZE)一页大小)

0001 0000 0000 0000 = 4096(sysconf(_SC_PAGE_SIZE)一页大小)
0000 1111 1111 1111 = (sysconf(_SC_PAGE_SIZE) - 1) =4095
~(0000 1111 1111 1111)
= 1111 0000 0000 0000 = ~ (sysconf(_SC_PAGE_SIZE) - 1) =61440

pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1)
=10000 & 61440
=(0010 0111 0001 0000) & (1111 0000 0000 0000)
=(0010 0000 0000 0000)
=8192 //pa_offset即内存中的页偏移量

&lt;think&gt;好的,用户让我介绍一个名为void setupBreakpoint()的函数,并请求提供完整的ARM64硬件断点Hook代码。我需要先理解用户提供的代码,然后根据ARM64架构的特点来编写相应的hook代码。 首先,分析用户提供的代码。这个函数似乎是在Linux环境下,通过读取文件中的地址,设置内存保护,利用SIGSEGV信号处理来实现断点。但用户现在需要的是ARM64硬件断点的实现,所以原来的方法可能不适用,因为硬件断点通常使用调试寄存器,比如ARM的DBGBCR寄存器。 硬件断点和软件断点的区别在于,硬件断点依赖处理器的调试寄存器,而软件断点通常通过插入断点指令(如BKPT)或修改内存权限来触发异常。硬件断点的优势是不修改目标内存,适用于只读内存或避免检测的场景。 接下来,我需要回忆ARM64架构中硬件断点的相关寄存器和配置方式。ARMv8-A架构提供了调试寄存器DBGBVR和DBGBCR,分别用于设置断点地址和控制参数。每个硬件断点由一对寄存器控制,DBGBVR存放断点地址,DBGBCR配置条件(如执行、读写等)。 编写代码的大致步骤应该是:读取地址文件,配置调试寄存器,设置信号处理函数捕获触发断点后的信号,最后恢复执行。需要注意的是,直接操作调试寄存器可能需要内核权限,或者使用ptrace系统调用来进行进程附加和寄存器设置。 用户可能是在进行逆向工程或调试工作,需要在不修改目标进程代码的情况下设置断点。因此,使用ptrace可能是正确的方法,因为普通用户空间程序无法直接访问调试寄存器,需要借助ptrace来操作。 我需要考虑代码的结构,首先附加到目标进程,然后设置断点地址到DBGBVR,配置DBGBCR启用断点,处理断点触发时的信号,比如SIGTRAP,然后在信号处理函数中处理断点事件,比如修改寄存器或执行其他操作后继续执行。 但用户提供的原代码中使用的是mprotect和SIGSEGV,这可能适用于软件断点。因此,在回答中需要区分软件断点和硬件断点的实现方式,并给出硬件断点的正确方法。 另外,需要注意权限问题,使用ptrace需要足够的权限,或者在调试自己的进程时使用。代码可能需要以root权限运行,或者在Android设备上具有相应的调试权限。 可能的实现步骤: 1. 使用ptrace附加到目标进程。 2. 设置硬件断点寄存器DBGBVR和DBGBCR。 3. 等待断点触发,处理信号。 4. 在信号处理函数中执行自定义逻辑,然后恢复进程。 需要查阅ARM64的文档,确认DBGBCR的位字段,比如启用断点、设置类型(执行、读写)、长度等。例如,DBGBCR的E(Enable)位,PMC(条件)字段等。 另外,硬件断点的数量有限,ARM64通常支持有限数量的硬件断点,比如4个或更多,具体取决于处理器实现。代码中需要处理断点数量的限制。 最后,需要编写完整的示例代码,包括读取地址、附加进程、设置断点、处理信号等步骤,并解释关键部分的作用。 需要确保代码的正确性,比如使用正确的ptrace参数,处理错误情况,以及正确配置调试寄存器。可能还需要处理字节序或地址对齐的问题。 总结:用户需要从软件断点的实现转向硬件断点的ARM64实现,涉及ptrace和调试寄存器的操作,需要详细解释代码逻辑和注意事项。&lt;/think&gt;以下是为ARM64架构编写的硬件断点Hook完整代码及分步解析: ```cpp #include &lt;stdio.h&gt; #include &lt;stdint.h&gt; #include &lt;stdlib.h&gt; #include &lt;signal.h&gt; #include &lt;sys/ptrace.h&gt; #include &lt;sys/user.h&gt; #define DR7_ENABLE_MASK 0x1 #define DR7_LEN_4BYTE (0x2 &lt;&lt; 16) #define DR7_TYPE_EXEC (0x0 &lt;&lt; 20) // 断点处理函数 void signalHandler(int sig, siginfo_t* info, void* context) { ucontext_t* ucontext = (ucontext_t*)context; // 获取触发断点的地址 uintptr_t pc = (uintptr_t)ucontext-&gt;uc_mcontext.pc; printf(&quot;Breakpoint hit at 0x%lx\n&quot;, pc); // 恢复执行(实际应用中需处理寄存器/内存) ptrace(PTRACE_CONT, getpid(), NULL, 0); } void setHardwareBreakpoint(uintptr_t addr) { pid_t pid = getpid(); // 附加到当前进程 if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) { perror(&quot;ptrace attach&quot;); return; } // 等待附加完成 wait(NULL); // 设置调试寄存器 struct user_hwdebug_state debug_state; debug_state.dbg_regs[0].addr = addr; debug_state.dbg_regs[0].ctrl = DR7_ENABLE_MASK | DR7_LEN_4BYTE | DR7_TYPE_EXEC; // 使用PTRACE_SETREGSET设置调试寄存器 struct iovec iov; iov.iov_base = &amp;debug_state; iov.iov_len = sizeof(debug_state); if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &amp;iov) == -1) { perror(&quot;ptrace setregset&quot;); return; } // 分离进程 ptrace(PTRACE_DETACH, pid, NULL, NULL); // 设置信号处理 struct sigaction sa; sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = signalHandler; sigemptyset(&amp;sa.sa_mask); if (sigaction(SIGTRAP, &amp;sa, NULL) == -1) { perror(&quot;sigaction&quot;); return; } printf(&quot;Hardware breakpoint set at 0x%lx\n&quot;, addr); } void setupBreakpoint() { std::ifstream addrFile(&quot;/sdcard/addr.txt&quot;); if (!addrFile.is_open()) { perror(&quot;Failed to open address file&quot;); return; } std::string line; if (std::getline(addrFile, line)) { uintptr_t addr = strtoull(line.c_str(), nullptr, 0); setHardwareBreakpoint(addr); } else { printf(&quot;No address found in file\n&quot;); } addrFile.close(); } ``` 代码关键点解析: 1. **架构依赖特性**: - 使用ARM64的`NT_ARM_HW_WATCH`寄存器组 - `user_hwdebug_state`结构体管理调试寄存器 2. **断点类型配置**: ```cpp #define DR7_ENABLE_MASK 0x1 // 启用断点 #define DR7_LEN_4BYTE (0x2&lt;&lt;16) // 监控4字节范围 #define DR7_TYPE_EXEC (0x0&lt;&lt;20) // 执行时触发 ``` 3. **信号处理流程**: - 捕获`SIGTRAP`信号 - 通过`uc_mcontext.pc`获取触发地址 - 使用`ptrace(PTRACE_CONT)`恢复执行 4. **硬件断点优势**: - 不修改目标内存 - 支持多种触发条件(执行/读写) - 对性能影响较小 注意事项: 1. 需要root权限或`SYS_PTRACE`能力 2. ARM64硬件断点数量有限(通常4-8个) 3. 地址必须按4字节对齐 4. 需要内核版本&gt;=4.10支持完整调试功能 扩展应用场景: - 逆向工程中的指令追踪 - 内存访问监控 - 反调试绕过 - 性能分析 调试寄存器配置参数对照表: | 参数 | 值 | 说明 | |---------------|-------|---------------------| | DR7_ENABLE | 0x1 | 启用断点 | | DR7_LEN_1BYTE | 0x0 | 监控1字节范围 | | DR7_LEN_2BYTE | 0x1 | 监控2字节范围 | | DR7_LEN_4BYTE | 0x2 | 监控4字节范围 | | DR7_LEN_8BYTE | 0x3 | 监控8字节范围 | | DR7_TYPE_EXEC | 0x0 | 执行时触发 | | DR7_TYPE_WRITE| 0x1 | 写入时触发 | | DR7_TYPE_RW | 0x3 | 读写时触发 |
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值