2

在 i386 架构 Linux 中,我知道您可以通过将系统调用的标识加载到 EAX 并将参数加载到 EBX、ECX 等来构建系统调用。

在这种情况下,我对 mprotect 的第三个参数的样子感到困惑;假设我想在二进制文件分配的内存可执行文件中创建一个内存段,PROT_EXEC 参数(arg 3)的编码如何工作?我知道前两个参数是(1)指向分配块开始的指针,以及(2)分配块的长度,这在 ASM 中相对容易概念化(因为它们是内存中的十六进制地址) .

如何将 mprotect() 的第三个参数格式化为 Linux 上 i386 程序集中发出的中断系统调用?

谢谢。

4

1 回答 1

6

TL;DR:一个整数作为第三个参数传递。

现在让我们回答评论中的问题。如果您打开应该位于 /usr/include/asm-generic 中的 mman-common.h,您将找到这些值。

#define PROT_READ       0x1             /* page can be read */
#define PROT_WRITE      0x2             /* page can be written */
#define PROT_EXEC       0x4             /* page can be executed */
#define PROT_SEM        0x8             /* page may be used for atomic ops */

就在编译之前,预处理器用上面的数字替换你的参数。所以如果你有这个电话:

mprotect(myaddress, 256, PROT_READ | PROT_WRITE);

它将被以下代码替换:

mprotect(myaddress, 256, 0x1 | 0x2);

现在看看不同参数可以采用的值:它们不是随机选择的,它们是 2 的幂,因此在二进制表示法中,它们仅由一个 1 数字和零表示。

PROT_READ  = 0x1 =   00000001
PROT_WRITE = 0x2 =   00000010
PROT_EXEC  = 0x4 =   00000100

选择 2 的幂很方便,因为当您使用二进制 OR 时,您获得的数字结合了之前的两个值,因此这两个信息都包含在 OR-ed 数字中。

PROT_WRITE | PROT_EXEC = 
    00000010
  | 00000100
=   00000110

现在回到我们的电话:

如果你打过电话mprotect(myaddress, 256, PROT_READ | PROT_WRITE),会发生的事情是那PROT_READ | PROT_WRITE会被合并成0x1 | 0x2,也就是0x3

现在在内核方面,假设PROT_READ | PROT_WRITE是由用户编写的。内核收到参数0x3,并想检查是否最初写入了 PROT_READ 。一种方法是这样写:

if (PROT_READ & userValue) { }

它之所以有效,是因为 userValue 包含 PROT_READ 和 PROT_WRITE 的组合版本,以二进制形式:

PROT_READ & userValue = 
    00000001
&&  00000011
 =  00000001

如果设置了标志,则此数字非零,因此内核知道已通过标志。

希望这可以帮助。

于 2013-05-21T21:39:26.917 回答