我正在尝试使代码段仅执行(不可读)。
但是在我尝试了手册告诉我的所有内容后,我失败。以下是我为使代码段不可读而采取的措施。
>uname -a
Linux Emmet-VM 3.19.0-25-generic #26~14.04.1-Ubuntu SMP Fri Jul 24 21:18:00 UTC 2015 i686 i686 i686 GNU/Linux
>lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.3 LTS
Release: 14.04
Codename: trusty
首先,我在“英特尔(R)64和IA-32架构软件开发人员手册(组合卷1,2A,2B,2C,2D,3A,3B,3C和3D)中找到了这个< / EM>“: Set read-enable bit to enable read和Segment Types。(抱歉,我仍然不允许在我的帖子中嵌入图片,因此请改为链接)
所以,我想如果我改变%CS,并让它指向一个读取使能位设置为0的段描述符,我应该使代码段不可读。
然后,我使用下面的代码将新的段插入到LDT.entry [2]中,并且我将代码段类型设置为8,即1000B,这意味着根据“段类型”执行“仅执行”上面发布的链接:
typedef struct user_desc UserDesc;
UserDesc *seg = (UserDesc*)malloc(sizeof(UserDesc));
seg->entry_number = 0x2;
seg->base_addr = 0x00000000;
seg->limit = 0xffffffff;
seg->seg_32bit = 0x1;
seg->contents = 0x02;
seg->read_exec_only = 0x1;
seg->limit_in_pages = 0x1;
seg->seg_not_present = 0x0;
seg->useable = 0x0;
int ret = modify_ldt(1, (void*)seg, sizeof(UserDesc));
之后,我用ljmp
将%CS更改为0x17(00010111B,表示LDT中的条目2)。
asm("ljmp $0x17, $reload_cs\n"
"reload_cs:");
但即便如此,我仍然可以读取代码段中的字节代码:
void foo() {printf("foo\n");}
void test(){
char* a = (char*)foo;
printf("0x%x\n", (unsigned int)a[0]);// This prints 0x55
}
如果代码段不可读,则上面的代码会抛出segment fault
错误。但它成功打印0x55
。
所以,我想知道,我在测试期间是否有任何错误? 或者这只是英特尔手册中的一个错误?
答案 0 :(得分:2)
执行DS
时,您仍在通过(unsigned int)a[0]
访问代码。
只写段不存在(如果它们存在,那么设置DS
只写一个不错的主意)。
如果你做的一切都正确mov eax, [cs:...]
(NASM语法)将失败(但mov eax, [ds:...]
赢了<。p>。
快速浏览一下英特尔手册后,不应该只执行页面(至少是直接),因此使用mprotect PROT_EXEC
可能会限制使用(代码仍然可读)。
值得一试。
有三种方法可以解决这个问题 但是,如果没有操作系统的帮助,这些都无法实现,因此它们更具理论性而非实用性。
如果CPU支持它们(参见英特尔手册3的第4.6.2节),它们会引入代码和数据读取方式的不对称性。
读取数据受密钥保护。 然而,提取不:
线性地址的保护键如何控制对地址的访问取决于线性地址的模式:
- 线性地址的保护仅控制对地址的数据访问。它不会以任何方式影响从地址获取的指令。
因此,可以为您的应用程序在其PKRU
注册表中没有的代码页设置保护密钥。
您仍然可以执行代码但不能阅读它。
如果您的应用程序从未触及代码页进行读取,则它们将占用 ITLB 中的某些条目,但不会占用 DTLB 中的某些条目。
如果那时,操作系统将它们映射为仅限主管而不刷新TLB ,则在作为数据访问时会阻止对它们的访问(因为这些页面不存在 DTLB 条目,强迫在内存上行走)但是由于 ITLB ,仍然可以获取代码。
这更多地涉及实践,因为代码跨越多个页面,并且实际上被操作系统读取为数据。
在虚拟化期间使用扩展数据页将来宾物理地址转换为主机物理地址 虽然它们似乎只是另一个间接层,但它们具有独立的读,写和执行控制位。
A paper has been written关于防止内核代码泄漏(以抵消动态面向返回的编程)。