【翻译】Marlin:一种新的随机方法来防御ROP攻击——Marlin: A fine grained randomization approach to defend against ROP

Marlin:一种新的随机方法来防御ROP攻击

【文章为google-translate的直译结果,最近暂时没有时间修改翻译内容。google-translate的翻译结果中有很多明显的错误,遇到类似的问题,请读者结合英文仔细揣摩。】

Abstract:

摘要

Code-reuse attacks, such as return-oriented programming (ROP), bypass defenses against code injection by repurposing existing executable code toward a malicious end. A common feature of these attacks is the reliance on the knowledge of the layout of the executable code. We propose a fine grained randomization based approach that modifies the layout of executable code and hinders code-reuse attack. Our solution, Marlin, randomizes the internal structure of the executable code, thereby denying the attacker the necessary a priori knowledge for constructing the desired sequence of gadget addresses. Our approach can be applied to any ELF binary and every execution of this binary uses a different randomization. Our work shows that such an approach is feasible and significantly increases the level of security against code-reuse based attacks.

诸如返回导向的编程(ROP)之类的代码重用攻击通过将现有的可执行代码重新用于恶意目的,从而绕过了针对代码注入的防御措施。 这些攻击的共同特征是依赖于可执行代码的布局知识。 我们提出了一种基于细粒度随机化的方法,该方法可以修改可执行代码的布局并阻止代码重用攻击。 我们的解决方案Marlin随机化了可执行代码的内部结构,从而使攻击者无法获得必要的先验知识,以构造所需的小工具地址序列。 我们的方法可以应用于任何ELF二进制文件,并且此二进制文件的每次执行都会使用不同的随机化。 我们的工作表明,这种方法是可行的,并且可以大大提高针对基于代码重用的攻击的安全性。

1 Introduction

1引言

The evolution of software exploits, such as buffer overflows and string format vulnerabilities, shows a pattern of an arms race. On one side, stack smashing attacks gave way to heap-based code injection. Defenders countered with canary words, instruction set randomization, base address randomization, and related techniques [9, 10, 28, 1]. Attackers found ways to bypass these defenses [34, 33] and jump to their injected malicious code. Defenders then responded with Write-or-Execute (W ⊕X), which prevents the execution of injected code. To get around W ⊕ X, return-into-libc and return-oriented programming (ROP) [31, 5] attacks were launched that leverage existing code rather than injecting their own. In the former case, a corrupted return address is used to jump to a libc function, such as system. In the latter, the attacker strings together gadgets (small sequences of binary instructions) to perform arbitrary computation.

诸如缓冲区溢出和字符串格式漏洞之类的软件漏洞的演变显示出军备竞赛的模式。一方面,堆栈粉碎攻击让位于基于堆的代码注入。捍卫者反抗金丝雀词,指令集随机化,基址随机化和相关技术[9,10,28,1]。攻击者找到了绕过这些防御措施的方法[34,33]并跳转到注入的恶意代码。防御者然后以Write-or-Execute(W⊕X)进行响应,这阻止了注入代码的执行。为了绕过W x X,发起了利用liblib的返回和面向返回的编程(ROP)[31,5]攻击,这些攻击利用现有代码而不是注入自己的代码。在前一种情况下,损坏的返回地址用于跳转到libc函数(例如system)。在后者中,攻击者将小工具(二进制指令的小序列)串在一起以执行任意计算。

As these attacks rely on knowing the location of code in the executable and libraries, the intuitive solution is to randomize process memory images. In basic address space layout randomization (ASLR), only the start address of the code segment is randomized. However, 32-bit machines provide insufficient entropy, as there are only 2 16 possible starting addresses, making the system vulnerable to brute-force [32]. While upgrading to 64-bit helps, it is not a universal solution.

由于这些攻击依赖于知道可执行文件和库中代码的位置,因此直观的解决方案是将进程内存映像随机化。在基本地址空间布局随机化(ASLR)中,仅代码段的起始地址被随机化。但是,32位机器提供的熵不足,因为只有2 16个可能的起始地址,使系统容易受到暴力攻击[32]。升级到64位虽然有帮助,但这不是通用解决方案。

Specifically, 32-bit (and smaller) architectures will continue to be used as legacy systems and in the area of embedded systems. Furthermore, recent work has demonstrated that an attacker can use information leakage to discover the randomization parameters, thus eliminating the defensive benefits of upgrading [30].

具体而言,32位(及更小的)体系结构将继续用作旧系统以及在嵌入式系统领域。 此外,最近的工作表明,攻击者可以使用信息泄漏来发现随机参数,从而消除了升级带来的防御优势[30]。

Our approach is to revisit the granularity at which randomization is performed. Rather than randomizing only a single parameter, our technique (Marlin) breaks an application binary into blocks of code and shuffles them. This significantly increases the entropy of the system; for instance, an application with 500 code blocks allows for 500! ≈ 2 3767 permutations, making brute-force infeasible. Our approach, which can be applied to any ELF binary without requiring source code, is performed transparently at load time to ensure every execution instance is unique. Finally, by paying a (quite reasonable) performance cost up front, Marlin avoids the overhead of on-going monitoring of critical data, such as return addresses, which other systems impose. We are not the only researchers to have investigated software diversity as ROP attack mitigation. While Section 2.2 offers a detailed comparison, existing approaches suffer from one or more of the following limitations. First, diversification is not done frequently enough. Second, source code or other additional information is required. Third, the granularity of randomization is insufficient, leading large code chunks unrandomized. Fourth, on-going monitoring imposes significant run-time overhead by introducing additional data structures. Marlin provides strong and efficient defense while addressing these limitations.

我们的方法是重新审视执行随机化的粒度。我们的技术(Marlin)不仅将随机的单个参数随机化,而且将应用程序二进制文件分解为代码块并对其进行混洗。这大大增加了系统的熵。例如,具有500个代码块的应用程序允许500个! ≈2 3767排列,使蛮力不可行。我们的方法可以在加载时透明地执行,以确保每个执行实例都是唯一的,该方法可以应用于任何ELF二进制文件而无需源代码。最后,通过预先支付(相当合理的)性能成本,Marlin避免了持续监视关键数据(如其他系统施加的返回地址)的开销。我们不是唯一研究过缓解ROP攻击的软件多样性的研究人员。尽管第2.2节提供了详细的比较,但是现有方法存在以下一个或多个限制。首先,多元化做得不够频繁。其次,需要源代码或其他附加信息。第三,随机化的粒度不足,导致大型代码块无法随机化。第四,正在进行的监视通过引入其他数据结构而增加了可观的运行时开销。马林在解决这些局限性的同时提供了强大而有效的防御。

After surveying code-reuse attacks and defenses in Section 2, we describe our design of Marlin in Section 3. Section 4 discusses our prototype, which consists of an off-line tool to randomize the binary image of an executable. We have constructed working ROP exploits and confirmed that these fail after shuffling the binary image using Marlin. Section 5 shows the results of various evalution experiments. Our evaluation of the time to randomize compiled binaries of the SPEC CPU2006 benchmark suite shows the average performance penalty is reasonable. Section 6 highlights both the merits and limitations of Marlin, and we conclude in Section 7.

在第2节中调查了代码重用的攻击和防御之后,我们在第3节中介绍了Marlin的设计。第4节讨论了我们的原型,该原型由一个离线工具组成,用于随机化可执行文件的二进制映像。 我们已经构建了有效的ROP漏洞利用程序,并确认了使用Marlin对二进制映像进行改组后这些漏洞无法通过。 第5节显示了各种评估实验的结果。 我们对随机化SPEC CPU2006基准套件的编译二进制文件的时间的评估表明,平均性能损失是合理的。 第6节强调了马林鱼的优点和局限性,我们在第7节中总结。

2 Background & Related Work

The focus of our work is on ROP attacks, which are a special case of code reuse attacks that leverage existing code in the application binary to execute arbitrary instructions. In this section, we start with a brief summary of these attack techniques and existing defenses. We then summarize critical factors of code-reuse attacks and define our threat model.

我们的工作重点是ROP攻击,这是一种代码重用攻击的特例,它利用应用程序二进制文件中的现有代码来执行任意指令。 在本节中,我们将简要概述这些攻击技术和现有防御措施。 然后,我们总结了代码重用攻击的关键因素,并定义了我们的威胁模型。

2.1 Return-oriented programming
2.1 面向返回的编程

Return-oriented programming (ROP) is an exploit technique that has evolved from stack-based buffer overflows. In ROP exploits, an attacker crafts a sequence of gadgets that are present in existing code to perform arbitrary computation.

面向返回的编程(ROP)是一种利用技术,它是从基于堆栈的缓冲区溢出演变而来的。 在ROP攻击中,攻击者会制作一系列现有代码中存在的小工具,以执行任意计算。

A gadget is a small sequence of binary code that ends in a ret instruction. By carefully crafting a sequence of addresses on the software stack, an attacker can manipulate the ret instruction semantics to jump to arbitrary addresses that correspond to the beginning of gadgets. Doing so allows the attacker to perform arbitrary computation. These techniques work in both word-aligned architectures like RISC [4] and unaligned CISC architectures [31]. ROP techniques can be used to create rootkits [19], can inject code into Harvard architectures [16], and have been used to perform privilege escalation in Android [12]. Initiating a ROP attack is made even easier by the availability of architecture-independent algorithms to automate gadget creation [15]. Additionally, the same technique of stringing together gadgets has been used to manipulate other instructions, such as jmp and their variants [5, 8, 3].

小工具是一小段二进制代码,以ret指令结尾。 通过精心设计软件堆栈上的地址序列,攻击者可以操纵ret指令语义来跳转到与小工具的开头相对应的任意地址。 这样做允许攻击者执行任意计算。 这些技术在像RISC [4]这样的字对齐体系结构和[31]未对齐的CISC体系结构中都有效。 ROP技术可用于创建rootkit [19],可将代码注入哈佛体系结构[16],并已用于在Android中执行权限提升[12]。 通过使用与体系结构无关的算法来自动创建小工具,可以更轻松地发起ROP攻击[15]。 另外,将小工具串在一起的相同技术已用于操纵其他指令,例如jmp及其变体[5、8、3]。

2.2 Defenses
2.2 防御

Address obfuscation [1], ASLR (e.g., PaX [28]), and Instruction Set Randomization (ISR) aim to defend against code-reuse attacks by introducing randomness into processes’ memory images. They randomize with coarse granularity and are subject to brute force attacks [33, 32], especially on 32-bit architectures. While upgrading to 64-bit increases the randomization, information leakage can allow an attacker to bypass the defense [30]. Furthermore, for some settings (e.g., embedded devices), upgrading to 64-bit is simply not feasible. While [1] suggests randomizing function blocks as a potential technique (which we employ in Marlin), no further implementation, discussion, or evaluation was attempted. Researchers have also considered dynamic monitoring defenses. For instance, DROP [6] dynamically compares the execution of ret instructions with statistically defined normal program behavior. DynIMA [13] combines TPM memory measurement capabilities with dynamic taint analysis to monitor process integrity. Other approaches store sensitive data (e.g., return addresses) in a protected shadow stack [14, 7]. These techniques impose a non-zero performance cost for every checked instruction, yielding non-trivial cumulative overhead. In contrast, Marlin imposes a one-time cost at process start-up and no additional on-going penalty.

地址混淆[1],ASLR(例如PaX [28])和指令集随机化(ISR)旨在通过将随机性引入进程的内存映像来防御代码重用攻击。它们以粗粒度随机化,并且受到蛮力攻击[33,32],尤其是在32位架构上。虽然升级到64位会增加随机性,但信息泄漏可能使攻击者绕过防御[30]。此外,对于某些设置(例如嵌入式设备),升级到64位根本不可行。尽管[1]建议将功能块随机化为一种潜在技术(我们在Marlin中采用了这种技术),但并未尝试进一步的实现,讨论或评估。研究人员还考虑了动态监视防御。例如,DROP [6]动态比较ret指令的执行与统计定义的正常程序行为。 DynIMA [13]将TPM内存测量功能与动态污点分析相结合,以监视过程完整性。其他方法将敏感数据(例如,返回地址)存储在受保护的影子堆栈中[14、7]。这些技术为每个已检查指令施加了非零的性能成本,从而产生了不小的累积开销。相比之下,马林(Marlin)在流程启动时会一次性支付一笔费用,而不会产生额外的持续罚款。

Other approaches introduce randomness at compile time. For instance, compilers can be modified to generate code without ret instructions [25, 22]. These mechanisms, however, fail to handle attacks leveraging jmp instructions; furthermore, if a new type of gadget is proposed, the compiler would have to be modified yet again. Alternatively, app store-based diversification [17] and linkage techniques for performance optimization [23, 24] can be applied to produce unique executables. However, these techniques do not stop an attacker with a known singular target image, do not help legacy systems, and, in the case of the former, rely on centralized control of software deployment. In contrast, proactive obfuscation [29] applies a semantics-preserving transformation to compiled server applications. Marlin is similar to this work in spirit, but the former aimed at diversifying replicas in distributed systems; as such, their threat model and techniques differed from our own.

其他方法在编译时引入了随机性。 例如,可以修改编译器以生成没有ret指令的代码[25,22]。 但是,这些机制无法利用jmp指令处理攻击。 此外,如果提出了一种新型的小工具,则必须再次修改编译器。 或者,基于应用程序商店的多元化[17]和用于性能优化的链接技术[23、24]可以用于生成唯一的可执行文件。 但是,这些技术并不能阻止具有已知单个目标映像的攻击者,也不能帮助遗留系统,并且在前者的情况下,它们依赖于软件部署的集中控制。 相反,主动混淆[29]将保留语义的转换应用于已编译的服务器应用程序。 Marlin在精神上与这项工作相似,但前者旨在使分布式系统中的副本多样化。 因此,他们的威胁模型和技术与我们自己的不同。

Other techniques similar to Marlin have also been proposed to randomize processes. ASLP [21] rewrites the ELF headers and shuffles sections, functions, and variables. As such, ASLP requires relocation information (or recompilation of source code), as well as user input. In contrast, as Marlin randomizes function blocks within the text segment, this additional information is not necessary. Bhatkar et al. [2] associates a pointer with every function and adds a layer of indirection to every function call. Unlike Marlin, the function reordering is not done at load time. ILR [18] randomizes the location of every instruction and uses a process-level virtual machine, which imposes a significant on-going performance cost, to find the called code. Pappas et al. [26] use in-place randomization that probabilistically breaks 80% of the useful gadgets. However, by shuffling the entire memory image, Marlin provides stronger guarantees, probabilistically breaking all sequences. Furthermore, [18] and [26] do not randomize the binary at every execution, which Marlin does. XIFER [11] is very similar to Marlin, except that it provides randomization at the basic block granularity, rather than at the function level. The finer granularity incurs more overhead than Marlin; however, we show that block-level randomization is sufficient to defeat brute force attacks, and the additional granularity is unnecessary.

还提出了类似于Marlin的其他技术来使过程随机化。 ASLP [21]重写了ELF标头,并混洗了部分,函数和变量。这样,ASLP需要重定位信息(或源代码的重新编译)以及用户输入。相反,由于Marlin在文本段内将功能块随机化,因此不需要此附加信息。 Bhatkar等。 [2]将指针与每个函数关联,并为每个函数调用添加一个间接层。与Marlin不同,功能重排序不在加载时完成。 ILR [18]随机化每条指令的位置,并使用一个进程级虚拟机来查找被调用的代码,这会带来很大的持续性能成本。 Pappas等。 [26]使用就地随机化概率破坏了80%的有用小工具。但是,通过改组整个内存映像,Marlin提供了更强的保证,可能会破坏所有序列。此外,[18]和[26]不会在每次执行时随机化二进制文件,而Marlin会这样做。 XIFER [11]与Marlin非常相似,除了它在基本块粒度而不是功能级别上提供随机化。细粒度比Marlin引起更多的开销。但是,我们证明了块级随机化足以击败暴力攻击,并且不需要额外的粒度。

2.3 Enabling factors for code-reuse attacks
2.3导致代码重用攻击的因素

Based on our survey of ROP attacks and defenses, we have identified a number of distinct characteristics and requirements for a successful exploit. We argue that a defensive technique that undermines these invariants will present a robust protection mechanism against these threats. The fundamental assumption and enabling factor for such attacks is as follows:

根据对ROP攻击和防御的调查,我们确定了成功利用漏洞的许多不同特征和要求。 我们认为,破坏这些不变性的防御技术将提供针对这些威胁的强大保护机制。 此类攻击的基本假设和启用因素如下:

The relative offsets of instructions within the application’s code are constant. That is, if an attacker knows any symbol’s address in the application code, then the location of all gadgets and symbols in application’s codebase is deterministic.

应用程序代码中指令的相对偏移量是恒定的。 也就是说,如果攻击者在应用程序代码中知道任何符号的地址,则所有小工具和符号在应用程序代码库中的位置都是确定的。

2.4 Threat Model
2.4 威胁模型

The proposed defense, Marlin, is aimed to protect a vulnerable application against code reuse attacks, such as ROP attacks. This application may have a buffer overflow vulnerability that can be leveraged by an attacker to inject an exploit payload. The system is assumed to be protected using W ⊕ X policy and the attacker can not inject arbitrary executable code in the stack or the heap. The attacker is assumed to have access to the target binary that has not yet undergone Marlin processing. The attacker is also assumed to be aware of the functionality of Marlin. However, the attacker cannot examine the memory dump of the running process and is unaware of how exactly the code is randomized for the currently executing process image. Our approach protects against both remote and local exploits as long as the attacker is not able to examine the memory of the target process.

提议的防御措施Marlin旨在保护易受攻击的应用程序免受代码重用攻击(例如ROP攻击)。 此应用程序可能具有缓冲区溢出漏洞,攻击者可以利用该漏洞注入漏洞利用负载。 假定该系统使用W X策略保护,攻击者无法在堆栈或堆中注入任意可执行代码。 假定攻击者可以访问尚未经过Marlin处理的目标二进制文件。 还假定攻击者知道Marlin的功能。 但是,攻击者无法检查正在运行的进程的内存转储,并且不知道对于当前正在执行的进程映像,如何精确地随机分配代码。 只要攻击者无法检查目标进程的内存,我们的方法就可以防止远程和本地攻击。

3 Marlin

3 马林

Code-reuse attacks make certain assumptions (as discussed in section 2.3) about the address layout of application’s executable code and shared libraries. Marlin’s randomization technique aims at breaking these assumptions by shuffling the code blocks in the binary’s .text section with every execution of this binary. This significantly increases the difficulty of such attacks since the attacker would need to guess the exact permutation being used by the current process image. This shuffling is performed at the granularity of function blocks. Marlin randomizes the target application just before the control is passed over to this application for execution. Thus, every execution of the program results in a different process memory image as illustrated in Figure 2(a). Figure 2(b) illustrates how shuffling the code results in a sequence of gadgets that is not intended by the attacker. We now present Marlin technique in detail.

代码重用攻击对应用程序的可执行代码和共享库的地址布局做出了某些假设(如第2.3节所述)。 马林(Marlin)的随机化技术旨在通过在每次执行此二进制文件时对二进制文件.text节中的代码块进行改组来打破这些假设。 由于攻击者将需要猜测当前过程映像正在使用的确切排列,因此这大大增加了此类攻击的难度。 改组以功能块的粒度执行。 Marlin在将控件传递给该应用程序执行之前,将目标应用程序随机化。 因此,程序的每次执行都会产生不同的过程存储映像,如图2(a)所示。 图2(b)说明了对代码进行改组如何导致一系列小工具,这些小工具不是攻击者想要的。 现在,我们详细介绍Marlin技术。

3.1 Preprocessing phase
3.1 预处理阶段

As mentioned above, Marlin randomizes the application binary at the granularity of function blocks. This requires identifying the function blocks in the application binary. Preprocessing phase parses the ELF binary to extract the function symbols and associated information such as start address of the function and length of the function block. However, traditional binaries are typically stripped binaries and do not contain symbol information. In such cases, we first restore the symbol information using an external tool, Unstrip [27]. Once the symbol information is restored and identified, we proceed on to the next stage of Marlin processing that randomizes the application binary.

如上所述,Marlin以功能块的粒度将应用程序二进制文件随机化。 这需要在应用程序二进制文件中标识功能块。 预处理阶段解析ELF二进制文件,以提取功能符号和相关信息,例如功能的起始地址和功能块的长度。 但是,传统的二进制文件通常是剥离的二进制文件,并且不包含符号信息。 在这种情况下,我们首先使用外部工具Unstrip [27]恢复符号信息。 一旦恢复并识别了符号信息,我们将继续进行Marlin处理的下一阶段,该阶段会将应用程序二进制文件随机化。

3.2 Randomization algorithm
3.2 随机算法

Once the function symbols have been identified, Marlin generates a random permutation of this set of symbols. The resulting permutation determines the order in which the mmap system calls are issued, which changes the order of the mapped symbols in memory. The function blocks are then shuffled around according to this random permutation. Shuffling the code blocks in an application binary changes the relative offsets between instructions that may affect various jump instructions. These jumps may be either absolute jumps or relative jumps. Relative jumps increment or decrement the program counter by a constant value as opposed to absolute jump that directly jump to a fixed address. When the code blocks are randomized, these jumps will no longer point to the desired location and must be ‘fixed’ to point to the proper locations. We achieve this by performing jump patching.

一旦识别出功能符号,Marlin就会生成这组符号的随机排列。 产生的排列决定了发出mmap系统调用的顺序,该顺序更改了内存中映射符号的顺序。 然后,根据此随机排列,功能块会随机排列。 将应用程序二进制文件中的代码块改组会更改指令之间的相对偏移,这可能会影响各种跳转指令。 这些跳跃可能是绝对跳跃,也可能是相对跳跃。 相对跳转相对于直接跳转到固定地址的绝对跳转,将程序计数器增加或减少一个恒定值。 当代码块随机分配时,这些跳转将不再指向所需的位置,而必须“固定”以指向正确的位置。 我们通过执行跳补丁来实现。

The randomization algorithm described in Algorithm 1 involves two stages. In the first stage, the function blocks are shuffled according to a certain random permutation. While shuffling the blocks, padding is added when necessary to ensure that the resulting binary is page aligned. During this shuffling, we keep a record of the original address of the function and also the new address where the function will reside after the binary has been completely randomized. This information is stored in a jump table. Note that this jump patching table is discarded before the application is given control, thus preventing attacker from utilizing this information to derandomize the memory layout.

算法1中描述的随机算法涉及两个阶段。 在第一阶段,根据一定的随机排列对功能块进行混洗。 在对块进行混排时,必要时添加填充以确保生成的二进制文件页面对齐。 在改组过程中,我们会记录函数的原始地址以及二进制文件完全随机化后函数将驻留的新地址的记录。 此信息存储在跳转表中。 请注意,在给应用程序提供控制之前,将丢弃此跳转补丁表,从而防止攻击者利用此信息对存储器布局进行随机化处理。

In the second stage, the actual jump patching is done where the algorithm examines the jump table for every jump that needs to be patched. Whenever a relative jump is encountered, it is the algorithm executes PatchJump() to redirect the jump to the correct address in the binary. PatchJump() method takes the current address of the jump, the address of the destination function, and any offset into the destination function to determine how far away the destination is. Then, it overwrites the original jump with this new offset, so that the jump points to the correct address.

在第二阶段,完成实际的跳转修补,其中算法检查每个需要修补的跳转的跳转表。 每当遇到相对跳转时,算法都会执行PatchJump()将跳转重定向到二进制文件中的正确地址。 PatchJump()方法获取跳转的当前地址,目标函数的地址以及目标函数的任何偏移量,以确定目标距离多远。 然后,它将使用此新偏移量覆盖原始跳转,以便跳转指向正确的地址。

Marlin breaks the basic assumption required by code reuse attacks as mentioned in section 2.3. The run-time shuffling of the code blocks prevents multiple instances of the same program from having the same address layout. Thus, to defeat Marlin, an attacker would need to dynamically construct a new exploit for every instance of every application which is not possible since the randomized layout is not accessible to attacker. We now discuss the security guarantees offered by Marlin.

Marlin打破了第2.3节中提到的代码重用攻击所需的基本假设。 代码块的运行时改组可防止同一程序的多个实例具有相同的地址布局。 因此,要击败Marlin,攻击者将需要为每个应用程序的每个实例动态构建新的利用程序,这是不可能的,因为随机布局无法被攻击者访问。 现在,我们讨论Marlin提供的安全保证。

3.3 Security Evaluation
3.3 安全评估

We now show that our randomization technique significantly increases the brute force effort required to attack the system. In a brute force attack, the attacker will randomly assume a memory layout and craft exploit payload according to that address layout. A failed attempt will usually cause a segmentation fault due to illegal memory access and the crashed process or thread will need to be restarted. We now compute the average number of attempts required by an attacker to succeed. A successful attack is assumed to be equivalent to guessing the correct permutation used for randomization.

现在我们表明,我们的随机化技术显着增加了攻击系统所需的蛮力。 在暴力攻击中,攻击者将随机采用内存布局,并根据该地址布局来利用有效载荷。 失败的尝试通常会由于非法内存访问而导致分段错误,并且崩溃的进程或线程将需要重新启动。 现在,我们计算攻击者成功所需的平均尝试次数。 假设成功的攻击等同于猜测用于随机化的正确排列。

In the discussion that follows, let n denote the number of symbols (excluding forbidden symbols) in an application binary. The total number of possible permutations that can be generated for this application is N = n!. Let P (k) denote the probability that the attack is successful after the k t h k^{th} kth attempt. Let X be a random variable denoting the number of brute force attempts after which the attack is successful for the first time (that is, the attacker guesses the correct permutation). We will now estimate the average value of X. We consider the following two cases.

在下面的讨论中,让n表示应用程序二进制文件中的符号数(不包括禁止的符号)。 可为该应用程序生成的可能排列的总数为N = n!。 令P(k)表示在$ k ^ {th} $次尝试后攻击成功的概率。 令X为一个随机变量,表示暴力攻击尝试的次数,此后第一次攻击成功(即,攻击者猜测正确的排列)。 现在我们将估计X的平均值。我们考虑以下两种情况。

Case 1: A failed attempt crashes the process and causes it to be restarted. In this event, the process will be restarted with a new randomization. The subsequent brute force attempts by an attacker will be independent since he would learn nothing from the past failed attempts. That is, P (k) is constant ( = 1 N 1 \over N N1 ) and independent of k. Let P (k) = p, ∀ k. Then, the average number of attempts before the attack is successful for the first time is

**情况1:**尝试失败会导致进程崩溃并使进程重新启动。 在这种情况下,该过程将以新的随机化重新开始。 攻击者随后的暴力尝试将是独立的,因为他将从过去的失败尝试中学到任何东西。 也就是说,P(k)是常数 ( = 1 N 1 \over N N1 ) 并且独立于k。 令P(k)= p,k。 然后,首次成功攻击之前的平均尝试次数为
E [ X ] = ( p ∗ 1 ) + ( 1 − p ) ∗ ( 1 + E [ X ] ) = 1 p E [ X ] = n ! \begin{aligned}&E[X] = (p * 1) + (1-p)*(1+E[X]) = {1\over p}\\&E[X] = n!\end{aligned} E[X]=(p1)+(1p)(1+E[X])=p1E[X]=n!
Thus, the attacker would have to make an average n! number of attempts to correctly guess the randomized layout and launch successful ROP attack.

因此,攻击者必须平均n! 正确猜测随机布局并成功发起ROP攻击的尝试次数。

Case 2: A failed attempt crashes a thread of the process and causes only that thread to be restarted. In this event, since the process is still executing, the memory layout will remain same. Every failed attempt will eliminate one permutation. The probability that first success is achieved at k th attempt is

**情况2:**失败的尝试会使进程的线程崩溃,并仅导致该线程重新启动。 在这种情况下,由于进程仍在执行,因此内存布局将保持不变。 每次失败的尝试都会消除一个排列。 第k次尝试获得首次成功的概率为
P ( k ) = ( ∏ i = 1 k − 1 N − i N − i + 1 ) ∗ 1 N − k + 1 = 1 N P(k) = (\prod_{i=1}^{k-1}{{N-i} \over {N-i+1}})*{1 \over {N-k+1}}={1 \over N} P(k)=(i=1k1Ni+1Ni)Nk+11=N1
The average number of attempts before first success can be computed as

首次成功之前的平均尝试次数可以计算为
E [ X ] = ∑ x = 1 N x ∗ P ( x ) = ∑ x = 1 N x ∗ 1 N = N + 1 2 E [ X ] = n ! + 1 2 \begin{aligned}&E[X] = \sum_{x=1}^{N}{x * P(x)} = \sum_{x=1}^{N}{x *{ 1 \over N}} = {{N + 1} \over 2} \\&E[X] = {{n! + 1} \over 2}\end{aligned} E[X]=x=1NxP(x)=x=1NxN1=2N+1E[X]=2n!+1
So, the attacker will need an average $ n! \over 2 $ number of brute attempts to correctly guess the randomization and launch successful ROP attack. Given enough time and resources, the attacker can try all possible permutations one after the other and will require at most n! attempts for a successful brute force attack.

因此,攻击者平均需要$ n! 超过2 $尝试正确猜测随机数并发起成功的ROP攻击的粗暴尝试次数。 如果有足够的时间和资源,攻击者可以一个接一个地尝试所有可能的排列,并且最多需要n个! 尝试成功进行暴力攻击。

As an example, to launch a successful ROP attack against an application with 500 symbols that is protected using Marlin, an average 500 ! = 2 3767 500! = 2^{3767} 500!=23767 number of attempts will be required for the first case. This is clearly computationally infeasible. A more extensive evaluation performed using SPEC2006 benchmarks is presented later in Section 5 that demonstrates the effectiveness of our technique.

例如,要对具有500个符号的应用程序发起成功的ROP攻击,并使用Marlin保护这些符号,平均需要500美元! =第2种情况需要2 ^ {3767} $次尝试。 这显然在计算上是不可行的。 使用SPEC2006基准进行的更广泛的评估将在后面的第5节中介绍,这证明了我们技术的有效性。

4 Prototype Implementation

4 原型实现

We have build a prototype implementation of Marlin that can operate on any ELF binary and does not require their source code. As a pre-processing step, we use objdump utility to obtain a disassembly listing of the application binary that contains the program instructions as well as its internal symbols. These listings are then used to generate a set of parameter files. These parameter files contain a list of symbols (functions) present in the binary, as well as their starting addresses and lengths. Another file is created which lists the addresses where the relative jumps inside functions of interest are located. It is important to note that not every function is considered to be a function of interest, since randomizing certain functions, such as start, will render the binary inoperable. These parameter files are used as input in the next phase of processing that performs the shuffling and jump patching operations. Upon completion, the parameter files are deleted and the the new “marlinized” binary is ready to be run like a normal executable binary.

我们已经构建了Marlin的原型实现,该实现可以在任何ELF二进制文件上运行,并且不需要其源代码。作为预处理步骤,我们使用objdump实用程序来获取应用程序二进制文件的反汇编列表,其中包含程序指令及其内部符号。然后,这些清单用于生成一组参数文件。这些参数文件包含二进制文件中存在的符号(功能)列表以及其起始地址和长度。创建另一个文件,该文件列出了相关功能内部相对跳转所在的地址。重要的是要注意,并不是将每个函数都视为感兴趣的函数,因为随机化某些函数(例如start)将使二进制文件不可操作。这些参数文件在执行改组和跳转修补操作的下一阶段处理中用作输入。完成后,参数文件将被删除,新的“ marlinized”二进制文件已准备好像普通可执行二进制文件一样运行。

5 Evaluation

5 评价

We now describe various experiments to evaluate our Marlin prototype. These experiments test three aspects of Marlin. First, we show that Marlin successfully defends against a ROP attack. Second, we study the brute force effort that would be required to circumvent the protection offered by Marlin. Third, we evaluate the processing costs incurred by using Marlin. The experiments were performed on a Linux machine with Intel Core i7 3.40GHz CPU and 8GB RAM. This machine had ASLR and W ⊕ X protection enabled while the experiments were being performed. We used SPEC CPU2006 benchmarksto conduct the various experiments. To launch attacks against Marlin-protected binary, we use ROP gadget 3 [20], an attack tool that automatically creates exploit payload for ROP attacks by searching for gadgets in an application’s executable section.

现在,我们描述各种实验以评估我们的Marlin原型。 这些实验测试了马林鱼的三个方面。 首先,我们证明了Marlin成功防御了ROP攻击。 其次,我们研究了规避马林鱼提供的保护所需的蛮力工作。 第三,我们评估使用Marlin产生的处理成本。 实验是在具有Intel Core i7 3.40GHz CPU和8GB RAM的Linux机器上进行的。 在进行实验时,该机器已启用ASLR和W x X保护。 我们使用SPEC CPU2006基准测试来进行各种实验。 为了对受Marlin保护的二进制文件发起攻击,我们使用ROP小工具3 [20],这是一种攻击工具,可以通过在应用程序的可执行部分中搜索小工具来自动创建ROP攻击的利用有效载荷。

*** These correspond to the average number of attempts for Case 1 in section 3.3.*
The values for Case 2 will be approximately half of the value for Case 1.

** 这些对应于第3.3节中案例1的平均尝试次数。
情况2的值大约是情况1的一半。

*** We were unable to compute factorial for values larger than 3248. The value used in*
these columns is 3248!.

***对于大于3248的值,我们无法计算阶乘。*中使用的值
这些列是3248!。

Effectiveness

效果

We tested the effectiveness of Marlin using a test application that has a buffer overflow vulnerability. This application, ndh rop, was included as a part of the ROPgadget test binaries. We used ROPgadget on this target application and found 162 unique gadgets. These were sufficient to craft a shell code exploit payload. When this exploit payload was provided as an input to the unprotected binary, it gave us a shell. Next, we randomized this application using Marlin technique and tried to attack it using the same input payload. The attack did not succeed and failed to provide us with a shell.

我们使用具有缓冲区溢出漏洞的测试应用程序测试了Marlin的有效性。 此应用程序ndh rop包含在ROPgadget测试二进制文件中。 我们在此目标应用程序上使用了ROPgadget,发现了162个独特的小工具。 这些足以制作Shell代码利用有效载荷。 当此漏洞有效载荷作为未受保护的二进制文件的输入提供时,它给了我们一个外壳。 接下来,我们使用Marlin技术将该应用程序随机化,并尝试使用相同的输入有效负载对其进行攻击。 攻击没有成功,也未能为我们提供弹药。

This highlights the sensitivity of these attacks to slight changes in the address layout. ROP attacks strongly operate under the assumption of a static address layout of executable code. Also, notice that in our threat model, the attacker only has access to the unprotected binary and is not aware of the exact permutation that has been used for randomization. So he can only run ROPgadget on the unprotected test application.

这凸显了这些攻击对地址布局中微小变化的敏感性。 ROP攻击在假定可执行代码的静态地址布局的情况下强烈起作用。 另外,请注意,在我们的威胁模型中,攻击者只能访问不受保护的二进制文件,并且不知道用于随机化的确切排列。 因此,他只能在不受保护的测试应用程序上运行ROPgadget。

Attacks on Marlin

对马林的攻击

In section 3.3, we computed the average number of attempts required to successfully attack a “marlinized” binary. We performed an extensive evaluation of this using SPEC CPU 2006 benchmarks. Table 1 shows the number of brute force attempts and the time it takes to craft one exploit. We noticed that around 80% of these benchmarks have more than 80 symbols (indicating an effort of 80! attempts). We observed an average of 1496 symbols and a median of 502 symbols present in these applications. Thus, the number of brute force attempts in a general case can be approximated to 500 ! ≈ 2 3767 500! ≈ 2^{3767} 500!23767 attempts which is quite significant. Also, on an average, we observed the time to compute one attack payload as 14.3 seconds.

在3.3节中,我们计算了成功攻击“误码”二进制文件所需的平均尝试次数。 我们使用SPEC CPU 2006基准对此进行了广泛的评估。 表1列出了尝试进行暴力破解的次数以及制作一次攻击所需的时间。 我们注意到,大约80%的这些基准测试中有超过80个符号(表示尝试了80次!)。 我们观察到在这些应用程序中平均存在1496个符号,中位值为502个符号。 因此,在一般情况下,暴力破解的尝试次数大约为$ 500! ≈2 ^ {3767} $次尝试,这非常重要。 同样,平均而言,我们观察到计算一个攻击有效载荷的时间为14.3秒。

It is interesting to note that the effectiveness of protection offered by Marlin depends on the modularity of the program.An application that has several function modules will be more secure against brute force attempts when protected with Marlin. If the entire code of an application is organized in few functions, then irrespective of the size of the binary, it will still be quite susceptible to brute force attacks since it would contain large chunks of unrandomized code. Randomizing at finer granularity, for example at the granularity of gadgets or instructions, will solve this issue. However, we believe that randomization breaks the locality principle and the randomized binary may suffer a performance hit. Thus, as a trade off, we chose to randomize at the granularity of function block.

有趣的是,马林提供的保护的有效性取决于程序的模块化。具有多个功能模块的应用程序在受到马林保护时,可以更有效地防止暴力破解。 如果应用程序的整个代码以很少的功能组织,则无论二进制文件的大小如何,由于它将包含大量的非随机代码,因此它仍然很容易受到暴力攻击。 以更细粒度(例如,小工具或说明的粒度)进行随机化将解决此问题。 但是,我们认为随机化会破坏局部性原则,并且随机化的二进制数据可能会遭受性能损失。 因此,作为权衡,我们选择按功能块的粒度进行随机化。

Overhead Analysis

开销分析

We evaluated the efficiency of Marlin by measuring the overhead incurred while loading an application. We use SPEC CPU2006 benchmarks to conduct this performance evaluation. When an application is loaded, Marlin identifies the function blocks and records information about them (such as start address, length) that is used later in jump patching. This computation is independent of the individual randomizations and referred to as preprocessing phase. Next phase involves shuffling the code blocks and patching the jumps. This computation is referred to as startup processing phase.

我们通过测量加载应用程序时产生的开销来评估Marlin的效率。 我们使用SPEC CPU2006基准进行性能评估。 加载应用程序时,Marlin会识别功能块并记录有关功能块的信息(例如起始地址,长度),这些信息将在以后的跳接补丁中使用。 该计算独立于各个随机化,称为预处理阶段。 下一阶段涉及改组代码块并修补跳转。 该计算称为启动处理阶段。

Figure 3 shows the overhead incurred during preprocessing and startup processing phase respectively. The benchmark 483.xalancbmk took significantly longer time to process. This is because it contained 13848 symbols in contrast to a median of 500 symbols by other applications. The average time taken by preprocessing phase was 4.2 seconds, while average time taken by the startup processing phase was 3.3 seconds. It is quite evident from these numbers, that the preprocessing phase is the major contributor to these performance costs.

图3分别显示了预处理和启动处理阶段产生的开销。 基准483.xalancbmk花费了更长的时间来处理。 这是因为它包含13848个符号,而其他应用程序中位数为500个符号。 预处理阶段花费的平均时间为4.2秒,而启动处理阶段花费的平均时间为3.3秒。 从这些数字可以很明显地看出,预处理阶段是这些性能成本的主要贡献者。

Since preprocessing phase is independent of individual randomizations, it can be executed just once per application and the results can be stored in database. The randomization phase, that runs with every execution, can read and process information from this database. This simple optimization can greatly improve efficiency of Marlin. Also, the performance hit due to Marlin is incurred only at the load time of the application. Once the application binary has been randomized, it executes like a normal application binary.

由于预处理阶段与各个随机化无关,因此每个应用程序只能执行一次,并且结果可以存储在数据库中。 每次执行都会运行的随机化阶段可以读取和处理该数据库中的信息。 这种简单的优化可以大大提高Marlin的效率。 同样,仅在应用程序加载时才因Marlin而导致性能下降。 一旦将应用程序二进制文件随机化后,它就会像普通应用程序二进制文件一样执行。

6 Discussion

6讨论

Our proposed solution to defend against code-reuse attacks was to increase the entropy by randomizing the code blocks. One may apply this randomization technique at various levels of granularity - function level, block level or gadget level. The level of granularity to choose is a trade off between security and performance. In our implementation, we implemented the randomization at the function level which is the most coarse granularity amongst the three mentioned above. However, we show that even this coarse level of granularity provides substantial randomization to make brute force attacks infeasible.

我们提出的针对代码重用攻击的解决方案是通过使代码块随机化来增加熵。 可以在各种粒度级别(功能级别,块级别或小工具级别)应用此随机化技术。 选择的粒度级别是安全性和性能之间的权衡。 在我们的实现中,我们在功能级别上实现了随机化,这是上述三个级别中最粗糙的粒度。 但是,我们表明,即使是这种粗糙的粒度级别也可以提供大量的随机性,从而使暴力攻击变得不可行。

Our prototype implementation requires the binary disassembly to contain symbol names, i.e. a non-stripped binary. In practice however, binaries may be stripped and not contain the symbol information. We address this by using external tools such as Unstrip [27] that restore symbol information to a stripped binary. Another approach to process stripped binaries is to randomize at the level of basic blocks since they don’t require symbol information to be identified. Moving forward, we will explore using basic block level instead of function level as the unit of randomization for Marlin.

我们的原型实现要求二进制反汇编包含符号名称,即非剥离的二进制。 但是,实际上,二进制文件可能会被剥离并且不包含符号信息。 我们通过使用外部工具(例如Unstrip [27])来解决此问题,该工具将符号信息还原为剥离的二进制文件。 处理剥离二进制文件的另一种方法是在基本块级别进行随机化,因为它们不需要标识符号信息。 展望未来,我们将探索使用基本块级别而不是功能级别作为Marlin的随机单位。

7 Conclusion

7结论

In this work, we proposed a fine-grained randomization based approach to defend against code reuse attacks. This approach randomizes the application binary with a different randomization for every run. We have implemented a prototype of our approach and demonstrated that it is successful in defeating real ROP attacks crafted using automated attack tools. We have also evaluated the effectiveness of our approach and showed that the brute force effort to attack Marlin is significantly high. Based on the results of our analysis and implementation, we argue that fine-grained randomization is both feasible and practical as a defense against these pernicious code-reuse based attack techniques.

在这项工作中,我们提出了一种基于细粒度随机化的方法来防御代码重用攻击。 这种方法将应用程序二进制文件随机化,每次运行都有不同的随机性。 我们已经实现了该方法的原型,并证明了该方法可以成功击败使用自动攻击工具制作的真实ROP攻击。 我们还评估了该方法的有效性,并证明了攻击Marlin的蛮力很大。 根据我们分析和实现的结果,我们认为细粒度的随机化既可以作为可行的方法,又可以作为对这些有害的基于代码重用的攻击技术的防御。

References

参考文献

  1. Bhatkar, E., Duvarney, D.C., Sekar, R.: Address obfuscation: an efficient approach to combat a broad range of memory error exploits. In: In Proc. of the 12th USENIX Security Symposium. pp. 105–120 (2003)

  2. Bhatkar, S., Sekar, R., DuVarney, D.C.: Efficient techniques for comprehensive protection from memory error exploits. In: Proc. of the 14th conference on USENIX Security Symposium - Volume 14. pp. 17–17. SSYM’05 (2005)

  3. Bletsch, T., Jiang, X., Freeh, V.: Jump-oriented programming: A new class of code-reuse attack. Tech. Rep. TR-2010-8, North Carolina State University (2010)

  4. Buchanan, E., Roemer, R., Shacham, H., Savage, S.: When good instructions go bad: generalizing return-oriented programming to risc. In: Proc. of the 15th ACM conference on Computer and communications security. pp. 27–38 (2008)

  5. Checkoway, S., Davi, L., Dmitrienko, A., Sadeghi, A.R., Shacham, H., Winandy, M.: Return-oriented programming without returns. In: Proc. of the 17th ACM conference on Computer and communications security. pp. 559–572 (2010)

  6. Chen, P., Xiao, H., Shen, X., Yin, X., Mao, B., Xie, L.: DROP: Detecting return-oriented programming malicious code. In: Proc. of the 5th International Conference on Information Systems Security. pp. 163–177 (2009)

  7. Chen, P., Xing, X., Han, H., Mao, B., Xie, L.: Efficient detection of the return-oriented programming malicious code. In: Proc. of the 6th international conference on Information systems security. pp. 140–155 (2010)

  8. Chen, P., Xing, X., Mao, B., Xie, L.: Return-oriented rootkit without returns (on the x86). In: Proc. of the 12th international conference on Information and communications security. pp. 340–354 (2010)

  9. Cowan, C., Beattie, S., Johansen, J., Wagle, P.: Pointguard: Protecting pointers from buffer overflow vulnerabilities. In: In Proc. of the 12th Usenix Security Symposium (2003)

  10. Cowan, C., Pu, C., Maier, D., Hinton, H., Walpole, J., Bakke, P., Beattie, S., Grier, A., Wagle, P., Zhang, Q.: Stackguard: Automatic adaptive detection and prevention of buffer-overflow attacks. In: In Proc. of the 7th USENIX Security Symposium. pp. 63–78 (1998)

  11. Davi, L., Dmitrienko, A., Nürnberger, S., Sadeghi, A.R.: Xifer: A software diversity tool against code-reuse attacks. In: 4th ACM International Workshop on Wireless of the Students, by the Students, for the Students (S3 2012) (Aug 2012)

  12. Davi, L., Dmitrienko, A., Sadeghi, A.R., Winandy, M.: Privilege escalation attacks on android. In: Proc. of the 13th international conference on Information security. pp. 346–360 (2011)

  13. Davi, L., Sadeghi, A.R., Winandy, M.: Dynamic integrity measurement and attestation: towards defense against return-oriented programming attacks. In: Proc. of the 2009 ACM workshop on Scalable trusted computing. pp. 49–54 (2009)

  14. Davi, L., Sadeghi, A.R., Winandy, M.: ROPdefender: a detection tool to defend against return-oriented programming attacks. In: Proc. of the 6th ACM Symposium on Information, Computer and Communications Security. pp. 40–51 (2011)

  15. Dullien, T., Kornau, T., Weinmann, R.P.: A framework for automated architecture independent gadget search. In: Proc. of the 4th USENIX conference on Offensive technologies. WOOT’10 (2010)

  16. Francillon, A., Castelluccia, C.: Code injection attacks on harvard-architecture devices. In: Proc. of the 15th ACM conference on Computer and communications security. pp. 15–26 (2008)

  17. Franz, M.: E unibus pluram: massive-scale software diversity as a defense mechanism. In: Proc. of the 2010 workshop on New security paradigms. pp. 7–16. NSPW’10 (2010)

  18. Hiser, J., Nguyen-Tuong, A., Co, M., Hall, M., Davidson, J.W.: Ilr: Where’d my gadgets go? In: Proc. of the 2012 IEEE Symposium on Security and Privacy. pp.571–585 (2012)

  19. Hund, R., Holz, T., Freiling, F.C.: Return-oriented rootkits: bypassing kernel code integrity protection mechanisms. In: Proc. of the 18th conference on USENIX security symposium. pp. 383–398. SSYM’09 (2009)

  20. Jonathan Salwan: ROPgadget tool. http://shell-storm.org/project/ROPgadget/

  21. Kil, C., Jun, J., Bookholt, C., Xu, J., Ning, P.: Address space layout permutation(aslp): Towards fine-grained randomization of commodity software. In: Proc. of the 22nd Annual Computer Security Applications Conference. pp. 339–348 (2006)

  22. Li, J., Wang, Z., Jiang, X., Grace, M., Bahram, S.: Defeating return-oriented rootkits with ”return-less” kernels. In: Proc. of the 5th European conference on Computer systems. pp. 195–208 (2010)

  23. MSDN Microsoft: /ORDER (Put Functions in Order). http://msdn.microsoft.com/en-us/library/00kh39zz.aspx

  24. MSDN Microsoft: Profile-guided optimizations. http://msdn.microsoft.com/en-us/library/e7k32f4k.aspx

  25. Onarlioglu, K., Bilge, L., Lanzi, A., Balzarotti, D., Kirda, E.: G-free: defeating return-oriented programming through gadget-less binaries. In: Proc. of the 26th Annual Computer Security Applications Conference. pp. 49–58 (2010)

  26. Pappas, V., Polychronakis, M., Keromytis, A.D.: Smashing the gadgets: Hindering return-oriented programming using in-place code randomization. In: IEEE Symposium on Security and Privacy. pp. 601–615 (2012)

  27. Paradyn Project: UNSTRIP. http://paradyn.org/html/tools/unstrip.html (2011)

  28. PaX Team: PaX. http://pax.grsecurity.net/

  29. Roeder, T., Schneider, F.B.: Proactive obfuscation. ACM Trans. Comput. Syst.28, 4:1–4:54 (July 2010)

  30. Roglia, G., Martignoni, L., Paleari, R., Bruschi, D.: Surgically returning to randomized lib©. In: Computer Security Applications Conference, 2009. ACSAC ’09. Annual. pp. 60 –69 (dec 2009)

  31. Shacham, H.: The geometry of innocent flesh on the bone: return-into-libc without function calls (on the x86). In: Proc. of the 14th ACM conference on Computer and communications security. pp. 552–561. ACM (2007)

  32. Shacham, H., Page, M., Pfaff, B., Goh, E.J., Modadugu, N., Boneh, D.: On the effectiveness of address-space randomization. In: Proc. of the 11th ACM conference on Computer and communications security. pp. 298–307 (2004)

  33. Sovarel, A.N., Evans, D., Paul, N.: Where’s the feeb? the effectiveness of instruction set randomization. In: Proc. of the 14th conference on USENIX Security Symposium - Volume 14. pp. 10–10 (2005)

  34. Tyler Durden: Bypassing PaX ASLR protection. Phrack Magazine 59(9) (June 2002)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值