解密虚拟内存0x400000以下的地方

一. 前言

  最近看CSAPP时,对以前没有仔细注意的一处知识盲区产生了兴趣,所以进行了深入研究,并写下此文一记录。

二. 问题

  二话不说直接上图。下图是CSAPP第七章的虚拟内存分析图。书中提到

在X86-64位Linux系统中,代码段总是从地址0x400000处开始,后面是数据段。堆在数据段之后,通过调用malloc向上增长…

在这里插入图片描述
  但是0X400000以下呢?为什么没有提到呢?翻遍全书,都没有一处提到0至0X400000处的空间是做什么用的。实际运行以下cat /proc/self/maps,发现结果的确如此

在这里插入图片描述

经过大量的搜索、查阅文献,最终找到了答案,下面就整理一下,由于Linux和Windows存在不同的情况,因此分开整理。

三. Linux

  先来看看这个0X400000的地址在哪儿规定的。实际上在源码/usr/lib/ldscripts/elf_x86_64.x中可以找到如下定义

PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); \
    . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;

  同时,对于X86-64位Linux系统而言,空出低端内存最重要的作用是为了保证空指针可以触发访问缺失页的异常SIGSEGV,而不是访问了不该访问的资源从而导致了奇奇怪怪的问题。可能你回好奇,为什么这里不直接从0x0000001开始做数据段呢?理论上说一个0就够用了呀?
  这可以归结为以下几点原因:

  • 考虑到多级页表的分配,省一点点不如多省一点,反正64位空间足够用,不再乎几页
  • 默认页大小一般是4KB,但是实际会存在很多大页机制,而大页的大小默认是4MB

  再追问一个问题,是否能少分配一点呢?答案是当然可以,只要大于65536即可。65536的规定来自于mmap_min_addr,可以通过/proc/sys/vm/mmap_min_addr查看。

四. Windows

  事情到了windows这里一下就变得不一样了。下图摘自《程序员的自我修养:链接、装载和库》。由图可见,windows操作系统的内存分配十分之奔放,相对于Linux,有着以下特色

  • 有多个栈空间供于多线程使用,栈的分布不像Linux一样死板,所以0x400000以下的地方很可能是在使用的
  • HeapCreate管理的堆不一定向上增长,而是甚至可能向下增长
  • 堆最大的大小应如heap5所示,大约为1.5GB-1.7GB

在这里插入图片描述

总结

  总结一下,对于Linux而言,0X400000以下的空间默认不映射,从而起到保护程序安全的作用。对于windows而言,程序安全交由操作系统保证,因此最大限度利用资源,地址可以低到0X400000以下。

参考文献

[1] CSAPP
[2] 程序员的自我修养:链接、装载和库
[3] /usr/lib/ldscripts/elf_x86_64.x
[4] https://wiki.debian.org/mmap_min_addr

  • 16
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
以下代码是使用Java实现SM4算法进行加密和解密的示例代码: ```java import org.bouncycastle.crypto.engines.SM4Engine; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.paddings.PKCS7Padding; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.encoders.Hex; import java.nio.charset.StandardCharsets; import java.security.Security; public class SM4Example { public static void main(String[] args) throws Exception { // 加载Bouncy Castle库 Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); // 待加密的明文 String plaintext = "Hello world!"; byte[] plainBytes = plaintext.getBytes(StandardCharsets.UTF_8); // 128位密钥 String keyHex = "0123456789abcdeffedcba9876543210"; byte[] keyBytes = Hex.decode(keyHex); // 128位初始向量 String ivHex = "0123456789abcdef0123456789abcdef"; byte[] ivBytes = Hex.decode(ivHex); // 加密 CBCBlockCipher cipher = new CBCBlockCipher(new SM4Engine()); PKCS7Padding padding = new PKCS7Padding(); cipher.init(true, new ParametersWithIV(new KeyParameter(keyBytes), ivBytes)); byte[] ciphertext = new byte[cipher.getOutputSize(plainBytes.length)]; int len = cipher.processBytes(plainBytes, 0, plainBytes.length, ciphertext, 0); len += cipher.doFinal(ciphertext, len); System.out.println("Ciphertext: " + Hex.toHexString(ciphertext)); // 解密 cipher.init(false, new ParametersWithIV(new KeyParameter(keyBytes), ivBytes)); byte[] decrypted = new byte[cipher.getOutputSize(len)]; int decryptedLen = cipher.processBytes(ciphertext, 0, len, decrypted, 0); decryptedLen += cipher.doFinal(decrypted, decryptedLen); String decryptedText = new String(decrypted, 0, decryptedLen, StandardCharsets.UTF_8); System.out.println("Decrypted text: " + decryptedText); } } ``` 在解密输出时少一位的情况下,可以考虑在解密时使用Padding方式进行明确,以确保解密输出的长度正确。 例如,在上面的示例代码中,我们可以使用 `PKCS7Padding` 填充方式来进行解密: ```java cipher.init(false, new ParametersWithIV(new KeyParameter(keyBytes), ivBytes)); byte[] decrypted = new byte[cipher.getOutputSize(len)]; int decryptedLen = cipher.processBytes(ciphertext, 0, len, decrypted, 0); decryptedLen += cipher.doFinal(decrypted, decryptedLen); decryptedLen -= new PKCS7Padding().padCount(decrypted); ``` 在解密输出时,使用 `padCount()` 方法获取 Padding 的长度,并将其从解密输出的长度中减去,以得到正确的明文长度。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ch_ty

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值