【问题标题】:Cannot Make Code Segment Execute-Only (Not Readable)无法使代码段只执行(不可读)
【发布时间】:2017-07-11 03:19:06
【问题描述】:

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

但是在我尝试了手册告诉我的所有操作之后,我失败了。这是我为使代码段不可读所做的操作。

>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] 中插入一个新的 Segment,并且我将代码段类型设置为 8,即 1000B,这意味着根据“Segment Types”的“Execute-Only”上面发布的链接:

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));

之后,我将 %CS 更改为 0x17(00010111B,表示 LDT 中的条目 2)并使用 ljmp

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

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

【问题讨论】:

  • 没错,你不能在 IA 中这样做。

标签: linux x86 segment


【解决方案1】:

在执行(unsigned int)a[0] 时,您仍在通过DS 访问代码。

只写段不存在(如果存在,设置DS 只写是个坏主意)。

如果你做的一切都正确mov eax, [cs:...](NASM 语法)会失败(但mov eax, [ds:...] 不会)。


快速浏览一下英特尔手册后,仅执行页面不应该存在(至少直接执行),因此使用 mprotectPROT_EXEC 可能会受到限制(代码仍然可读)。
不过值得一试。

有三种方法可以解决这个问题。
但是,如果没有操作系统的帮助,这些都无法实现,因此它们更具理论性而非实用性。

保护键

如果 CPU 支持它们(参见 Intel 手册 3 的第 4.6.2 节),它们会在读取代码和数据的方式上引入不对称性。

读取数据受密钥保护。 然而,获取是不是

线性地址的保护密钥如何控制对地址的访问取决于线性地址的模式:

  • 线性地址的保护只控制对该地址的数据访问。它不会以任何方式影响从地址中获取指令。

因此,可以为您的应用程序在其PKRU 寄存器中没有的代码页设置保护密钥。
您仍然可以执行代码,但不能阅读。

不同步 TLB

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

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

EPT

在虚拟化期间使用扩展数据页将客户物理地址转换为主机物理地址。
尽管它们似乎只是另一个间接级别,但它们具有独立的读取、写入和执行控制位

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

【讨论】:

  • 试过mov eax, [cs:...] 并且故障段按预期显示。没错,段描述符中的设置应该只影响使用相应段寄存器的内存访问。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2016-09-15
  • 1970-01-01
  • 2011-10-20
  • 2016-05-31
  • 2018-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多