无法使代码段仅执行(不可读)

时间:2017-02-21 13:16:06

标签: linux x86 segment

我正在尝试使代码段仅执行(不可读)。

但是在我尝试了手册告诉我的所有内容后,我失败。以下是我为使代码段不可读而采取的措施。

>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 readSegment 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

所以,我想知道,我在测试期间是否有任何错误? 或者这只是英特尔手册中的一个错误?

1 个答案:

答案 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注册表中没有的代码页设置保护密钥。
您仍然可以执行代码但不能阅读它。

取消同步TLB

如果您的应用程序从未触及代码页进行读取,则它们将占用 ITLB 中的某些条目,但不会占用 DTLB 中的某些条目。
如果那时,操作系统将它们映射为仅限主管而不刷新TLB ,则在作为数据访问时会阻止对它们的访问(因为这些页面不存在 DTLB 条目,强迫在内存上行走)但是由于 ITLB ,仍然可以获取代码。

这更多地涉及实践,因为代码跨越多个页面,并且实际上被操作系统读取为数据。

EPT

在虚拟化期间使用扩展数据页将来宾物理地址转换为主机物理地址 虽然它们似乎只是另一个间接层,但它们具有独立的读,写和执行控制位

A paper has been written关于防止内核代码泄漏(以抵消动态面向返回的编程)。