在i386架构Linux中,我知道您可以通过将系统调用的标识加载到EAX中并将参数加载到EBX,ECX等中来构建系统调用。
我很担心mprotect的第三个参数在这种情况下会是什么样子;假设我想在二进制文件分配的内存可执行文件中创建一个内存段,那么编码如何用于PROT_EXEC参数(arg 3)?我知道前两个参数是(1)指向已分配块的开始的指针,以及(2)在ASM中相对容易概念化的已分配块的长度(因为它们是内存中的十六进制地址)
如何在Linux上的i386程序集中将mprotect()的第三个参数格式化为中断发出的系统调用?
感谢。
答案 0 :(得分:6)
TL; DR:整数作为第三个参数传递。
现在让我们回答评论中的问题。 如果你打开mman-common.h,它应该位于/ usr / include / asm-generic中,你会发现这些值。
#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位和0来表示。
PROT_READ = 0x1 = 00000001
PROT_WRITE = 0x2 = 00000010
PROT_EXEC = 0x4 = 00000100
选择2的幂是很方便的,因为当你使用二进制OR时,你获得的数字结合了前两个值,所以这两个信息都包含在OR编号中。
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
如果设置了该标志,则该数字为非零,因此内核知道已标记的标志已被传递。
希望这有帮助。