编辑
2025-02-11
记录知识
0

cache的结构介绍

image.png

根据上面的图可以知道,我们需要留意如下信息:

  • offset
  • line
  • index
  • way
  • set
  • tag

offset

对于cache而言,offset代表了cache的便宜,假设offset占用了4位,则我们知道cache line大小是16 Byte

line

和offset对应,offset作为PA的低四位,则cache line共计大小是16Byte

index

索引是指的有多少个cache line,index作为索引组合起来可以计算为一个way,假设index占用8位,则一个way占用256个cache line,则16*256=4096 Byte大小作为一个way。

way

我们计算了offset和index的乘积也就是4096Byte,way这里指的是路,有多少个路就是代表整个cache总大小多少个4096 Byte,假设cache的总大小是16KB,那么我们16/4=4,这里就是四路cache
结合来看,那么一个总cache大小是16KB的情况下,假设way是4路,则每一路是4KB,如果cache line是16Byte,则我们知道index就是256个

set

根据上面的计算,我们再把每个way上index相同的cache line称之为一个set,也就是一组。那么按照上面的例子,同一个index的组一共有8个,因为我们有8个way

tag

对于PA物理内存上,将除掉offset和index的位剩余的为作为tag标记,用于判断cache line存放的数据是否和处理器想要的一致。

编辑
2025-02-10
记录知识
0

atexit

之前同事问了一个问题,在调试程序的时候,想要在程序退出的时候进行一些清理工作,但是不太清楚这个程序内应该怎样添加好。当时没有回答出来这个问题,最近在翻阅的时候找到了这个标准库函数atexit,本文介绍简单介绍这个函数功能。留作记忆

示例代码

#include <stdlib.h> #include <stdio.h> void atexit_func() { printf("atexit_func is called\n"); } void main() { atexit(atexit_func); return ; }

此代码通过atexit注册了一个退出回调函数atexit_func,此函数打印了一句话。

运行代码

为了能够证明这个atexit早就存在并可以使用,这里以c89来进行编译构建如下:

gcc test.c -std=c89 -o test

然后运行如下

# ./test atexit_func is called

总结

至此可以发现,此函数可以用作程序的退出回收工作,我们在调试操作系统上一些社区特别大型的c程序的时候,不是很方便在社区代码中添加回收逻辑的时候,这个atexit可以做到这点。

编辑
2025-01-22
记录知识
0

根据防破解之-完整性校验我们拿到了固定的摘要,我们需要针对这个摘要进行加密,否则其他人可以修改#这个摘要信息,将已经破解的文件的摘要放在指定节上。

一、什么是非对称加密

了解非对称加密的时候,我们需要先知道什么是对称加密,对称加密指的加密和解密时使用的密钥都是同一个,是“对称”的。图个网络图片例子如下:

image.png

这里我们虽然发现对称加密能够做到加密策略,但是如果对称密钥泄露,那加密也就被破解,也就是如何把密钥安全地传递给对方,这里非对称加密就出现了。

对于非对称加密,它有两个密钥,一个叫公钥,一个叫私钥。两个密钥是不同的,“不对称”,公钥可以公开给任何人使用,而私钥必须严格保密。

公钥和私钥有单向性,虽然都可以用来加密解密,但公钥加密后只能用私钥解密,反过来,私钥加密后也只能用公钥解密。

这样,非对称加密可以解决密钥被泄露的问题,也就是我们的所有的内容通过私钥加密,而我发布的公钥仅仅用来解密我私钥加密的内容。因为我私钥拿在手上,不会释放,所以没有办法破解密文

二、什么是RSA

RSA是比较著名的非对称加密,它的安全性基于“整数分解”的数学难题,使用两个超大素数的乘积作为生成密钥的材料,想要从公钥推算出私钥是非常困难的,有兴趣了解rsa加密的可以查看此文章:RSA

我们可以实践如下:

2.1 生成公/私钥

为了更安全,这里选择2048长度

openssl genrsa -out rsa_private_key.pem 2048 openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

2.2 使用公钥加密

我们使用文件plaintext.bin作为待加密文件,ciphertext.bin是密文文件

openssl rsautl -encrypt -pubin -inkey rsa_public_key.pem -in plaintext.bin -out ciphertext.bin

2.3 使用私钥解密

openssl rsautl -decrypt -inkey rsa_private_key.pem -in ciphertext.bin -out out_plaintext.bin

此时我们可以发现"plaintext.bin"和"out_plaintext.bin"是完全相等的。

至此,我们可以在sha256的基础上,通过非对称加密将摘要信息进行加密,这样对方无法破解我们的摘要信息。从而保证了摘要信息被篡改的风险。

编辑
2025-01-22
记录知识
0

之前我们讲到了elf文件,通过解析elf文件,我们知道了对于关键文件需要保护哪些内容,这里主要针对是关键性文件的完整性校验的了解。

一、完整性计算

根据防破解之-elf文件格式我们知道了数据来源,为了实现关键文件防篡改,我们需要对这些内容进行完整性计算,针对此,我们应该满足下面三点:

  • 数据正向计算容易,逆向计算几乎不可能
  • 数据计算结果长度固定
  • 数据计算不易碰撞

根据上面的要求,结合当前已知的数据结构,我们可以选择hash,并且是单向hash。而常用的单向hash有哪些呢,如下:

  • md5
  • sha
  • sm3

对于此,我们可以如下假设,先定义一个数据源来自于.text

objcopy --dump-section .text=text.bin libhelloworld.so

这里我们提取了text.bin,我们先使用md5进行提取摘要

# md5sum text.bin 2d662e596919c294d7e3f274d75549b6 text.bin

使用sha256进行提取摘要

# openssl dgst -sha256 text.bin SHA256(text.bin)= 6aaf37f9ef03aa1f06dab8083784a4b50acca524f9cf7476acc52bd23dc118f2

使用sm3进行提取摘要 通过openssl

# openssl dgst -sm3 text.bin SM3(text.bin)= d0e43ea849949decc4cf8deb557f4cb46f0f560563dc2f0a63f6cbfa5e27de18

至此,基于三种算法的完整性计算方案已经演示

二、md5的碰撞

我们可以知道,md5默认是256bit的摘要提取,但是根据当前的技术状态,md5是能够存在碰撞的,虽然是2^128的概率,如下是碰撞例子:

数据1:

STR1=d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70

数据2

STR2=d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70

此时我们做一下文本转换

echo $STR1 | xxd -r -p > str1 echo $STR2 | xxd -r -p > str2

此时我们对比一下即可

# md5sum str1 str2 79054025255fb1a26e4bc422aef54eb4 str1 79054025255fb1a26e4bc422aef54eb4 str2

可以看到,上面STR1和STR2进行碰撞了

我们通过hexdump进行转换16进制如下:

hexdump -C str1 > 1 hexdump -C str2 > 2

此时我们对比即可两个str的不同

image.png

参考:https://www.mscs.dal.ca/~selinger/md5collision/

故,根据此信息,md5存在碰撞问题,我们可以选择sha或sm3,对于sha,我们通常情况下选择更通用的sha256。

三、最终选择

根据上面我们可以知道,如果我们不在意md5的碰撞问题,那么我们可以选择md5,如果比较在意碰撞,那么我们可以选择更通用的sha256算法

当然sha还提供了其他的信息摘要算法,如下:

# openssl help Message Digest commands (see the `dgst' command for more details) blake2b512 blake2s256 gost md4 md5 rmd160 sha1 sha224 sha256 sha3-224 sha3-256 sha3-384 sha3-512 sha384 sha512 sha512-224 sha512-256 shake128 shake256 sm3

这些摘要算法我就不一一演示,对于当前方案,我们只需要知道选择了sha256。后续如果有需求使用sm3,会在重构的时候使用sm3。

对于sha256的原理,我也不了解,需要时间沉淀,这里提供文档,点击即可阅读,有兴趣的可以了解一下

image.png

关于演示,这里贴出一个网页,也可以了解一下:

https://sha256algorithm.com/

编辑
2025-01-22
记录知识
0

为了让系统的二进制文件避免被破解,我们先需要针对elf文件进行了解,本文以防破解为目的,来简单了解一下elf的组成,如果需要详细了解的,建议阅读《elf.pdf》和《elf-64-gen.pdf》,文件点击即可阅读。理解此文章需要一点elf知识,建议可以先搜索引擎简单了解一下。

一、ELF的结构

这里以网上的图片为例,ELF结构如下:

image.png 可以发现,这里列举了必要的ELF内容,这里解释如下:

ELF Header: ELF的头结构 .text: 程序的代码段 .rodata: 程序的只读数据区 .data: 程序的已初始化的数据区 .bss: 程序的未初始化数据(初始化为0算未初始化) .symtab: 链接符号表 .strtab: 字符串表 Section Header:节的头表

而实际上,我以一个helloworld.c的so编译一个libhelloworld.so为例,查看一下elf的结构:

抛开elf头和节头,剩下的段如下:

# readelf -S libhelloworld.so 节头: [号] 名称 类型 地址 偏移量 大小 全体大小 旗标 链接 信息 对齐 [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .note.gnu.build-i NOTE 00000000000001c8 000001c8 0000000000000024 0000000000000000 A 0 0 4 [ 2] .gnu.hash GNU_HASH 00000000000001f0 000001f0 0000000000000024 0000000000000000 A 3 0 8 [ 3] .dynsym DYNSYM 0000000000000218 00000218 00000000000000d8 0000000000000018 A 4 3 8 [ 4] .dynstr STRTAB 00000000000002f0 000002f0 000000000000007b 0000000000000000 A 0 0 1 [ 5] .gnu.version VERSYM 000000000000036c 0000036c 0000000000000012 0000000000000002 A 3 0 2 [ 6] .gnu.version_r VERNEED 0000000000000380 00000380 0000000000000020 0000000000000000 A 4 1 8 [ 7] .rela.dyn RELA 00000000000003a0 000003a0 00000000000000a8 0000000000000018 A 3 0 8 [ 8] .rela.plt RELA 0000000000000448 00000448 0000000000000048 0000000000000018 AI 3 19 8 [ 9] .init PROGBITS 0000000000000490 00000490 0000000000000014 0000000000000000 AX 0 0 4 [10] .plt PROGBITS 00000000000004b0 000004b0 0000000000000050 0000000000000010 AX 0 0 16 [11] .text PROGBITS 0000000000000500 00000500 00000000000000fc 0000000000000000 AX 0 0 8 [12] .fini PROGBITS 00000000000005fc 000005fc 0000000000000010 0000000000000000 AX 0 0 4 [13] .eh_frame_hdr PROGBITS 000000000000060c 0000060c 0000000000000034 0000000000000000 A 0 0 4 [14] .eh_frame PROGBITS 0000000000000640 00000640 000000000000009c 0000000000000000 A 0 0 8 [15] .init_array INIT_ARRAY 0000000000010df0 00000df0 0000000000000008 0000000000000008 WA 0 0 8 [16] .fini_array FINI_ARRAY 0000000000010df8 00000df8 0000000000000008 0000000000000008 WA 0 0 8 [17] .dynamic DYNAMIC 0000000000010e00 00000e00 00000000000001c0 0000000000000010 WA 4 0 8 [18] .got PROGBITS 0000000000010fc0 00000fc0 0000000000000028 0000000000000008 WA 0 0 8 [19] .got.plt PROGBITS 0000000000010fe8 00000fe8 0000000000000030 0000000000000008 WA 0 0 8 [20] .data PROGBITS 0000000000011018 00001018 0000000000000016 0000000000000000 WA 0 0 8 [21] .bss NOBITS 0000000000011030 0000102e 0000000000000008 0000000000000000 WA 0 0 4 [22] .comment PROGBITS 0000000000000000 0000102e 000000000000002a 0000000000000001 MS 0 0 1 [23] .symtab SYMTAB 0000000000000000 00001058 0000000000000690 0000000000000018 24 64 8 [24] .strtab STRTAB 0000000000000000 000016e8 0000000000000236 0000000000000000 0 0 1 [25] .shstrtab STRTAB 0000000000000000 0000191e 00000000000000e5 0000000000000000 0 0 1

其他的节的解释,可以查看官方文档。这里不做解释了。

二、关注的节

我们带着问题来找答案:目前需要对一个elf文件进行保护,那我们保护什么呢?答案应该如下:

  1. 代码的实现
  2. 程序的数据

我们不需要关心的是

  1. 无需加载到内存中的节
  2. 参与动态链接的节
  3. 参与异常处理的节
  4. 特殊或用于标识的节

这里我们可以知道,下面节是我们无需关心的:

.rela.dyn .rela.plt .plt .got .got.plt .eh_frame_hdr .eh_frame .shstrtab .*gnu*

需要关心的是:

.init .init_array .fini .fini_array .text .rodata .data .bss

但是这里我们注意的是32位和64位的.init* 和.fini* 目前没有由程序参与,故可以排除掉,则我们关心如下节即可:

.text .rodata .data .bss

2.1 .text

我们知道.text是二进制的代码段,我们可以通过命令查看如下,以libhelloworld.so为例

# objdump -d -j .text libhelloworld.so libhelloworld.so: 文件格式 elf64-littleaarch64 Disassembly of section .text: 0000000000000500 <hello_world@@Base-0xd4>: 500: 90000080 adrp x0, 10000 <hello_world@@Base+0xfa2c> 504: f947ec00 ldr x0, [x0, #4056] 508: b4000040 cbz x0, 510 <puts@plt+0x20> 50c: 17fffff5 b 4e0 <__gmon_start__@plt> 510: d65f03c0 ret 514: d503201f nop 518: b0000080 adrp x0, 11000 <hello_world@@Base+0x10a2c> 51c: 9100c000 add x0, x0, #0x30 520: b0000081 adrp x1, 11000 <hello_world@@Base+0x10a2c> 524: 9100c021 add x1, x1, #0x30 528: eb00003f cmp x1, x0 52c: 540000c0 b.eq 544 <puts@plt+0x54> // b.none 530: 90000081 adrp x1, 10000 <hello_world@@Base+0xfa2c> 534: f947e421 ldr x1, [x1, #4040] 538: b4000061 cbz x1, 544 <puts@plt+0x54> 53c: aa0103f0 mov x16, x1 540: d61f0200 br x16 544: d65f03c0 ret 548: b0000080 adrp x0, 11000 <hello_world@@Base+0x10a2c> 54c: 9100c000 add x0, x0, #0x30 550: b0000081 adrp x1, 11000 <hello_world@@Base+0x10a2c> 554: 9100c021 add x1, x1, #0x30 558: cb000021 sub x1, x1, x0 55c: d37ffc22 lsr x2, x1, #63 560: 8b810c41 add x1, x2, x1, asr #3 564: eb8107ff cmp xzr, x1, asr #1 568: 9341fc21 asr x1, x1, #1 56c: 540000c0 b.eq 584 <puts@plt+0x94> // b.none 570: 90000082 adrp x2, 10000 <hello_world@@Base+0xfa2c> 574: f947f042 ldr x2, [x2, #4064] 578: b4000062 cbz x2, 584 <puts@plt+0x94> 57c: aa0203f0 mov x16, x2 580: d61f0200 br x16 584: d65f03c0 ret 588: a9be7bfd stp x29, x30, [sp, #-32]! 58c: 910003fd mov x29, sp 590: f9000bf3 str x19, [sp, #16] 594: b0000093 adrp x19, 11000 <hello_world@@Base+0x10a2c> 598: 3940c260 ldrb w0, [x19, #48] 59c: 35000140 cbnz w0, 5c4 <puts@plt+0xd4> 5a0: 90000080 adrp x0, 10000 <hello_world@@Base+0xfa2c> 5a4: f947e800 ldr x0, [x0, #4048] 5a8: b4000080 cbz x0, 5b8 <puts@plt+0xc8> 5ac: b0000080 adrp x0, 11000 <hello_world@@Base+0x10a2c> 5b0: f9400c00 ldr x0, [x0, #24] 5b4: 97ffffc7 bl 4d0 <__cxa_finalize@plt> 5b8: 97ffffd8 bl 518 <puts@plt+0x28> 5bc: 52800020 mov w0, #0x1 // #1 5c0: 3900c260 strb w0, [x19, #48] 5c4: f9400bf3 ldr x19, [sp, #16] 5c8: a8c27bfd ldp x29, x30, [sp], #32 5cc: d65f03c0 ret 5d0: 17ffffde b 548 <puts@plt+0x58> 00000000000005d4 <hello_world@@Base>: 5d4: a9be7bfd stp x29, x30, [sp, #-32]! 5d8: 910003fd mov x29, sp 5dc: 52800020 mov w0, #0x1 // #1 5e0: b9001fe0 str w0, [sp, #28] 5e4: b0000080 adrp x0, 11000 <hello_world@@Base+0x10a2c> 5e8: 91008000 add x0, x0, #0x20 5ec: 97ffffc1 bl 4f0 <puts@plt> 5f0: d503201f nop 5f4: a8c27bfd ldp x29, x30, [sp], #32 5f8: d65f03c0 ret

2.2 .data

我们知道.data是二进制的已初始化数据段,我们可以通过命令查看如下

# readelf -p .data libhelloworld.so String dump of section '.data': [ 8] Hello, World!

2.3 rodata

我们知道.rodata是二进制的只读数据段,我们需要添加一个变量让其存放在rodata,如下:

const char k[]="Hello, Kylin!";

此时我们查看如下

# readelf -p .rodata libhelloworld.so String dump of section '.rodata': [ 0] Hello, Kylin!

2.4 .bss

我们知道.bss是二进制的未初始化数据段,我们需要添加一个变量让其存放在.bss,如下:

char bss[64] = "";

此时我们查看如下

# objdump -d -j .bss libhelloworld.so libhelloworld.so: 文件格式 elf64-littleaarch64 Disassembly of section .bss: 0000000000011038 <completed.9189>: ... 0000000000011040 <bss>: ...

三、如何保护

我们抓住了elf的重点,就是

可加载文件的在运行时加载的text/data/rodata/bss段 如果我们下载hexedit工具,我们只需要对libhelloworld.so进行修改,就能左右代码的执行,数据的信息等等。为了简单易懂,我以修改rodata为例示例修改elf文件。

3.1 制造篡改

假设程序如下:

#include <stdio.h> void hello_world() { const char k[]="Hello, Kylin!"; printf("%s\n", k); } # LD_LIBRARY_PATH=./ ./test-helloworld Hello, Kylin!

已知rodata的内容是"Hello, Kylin!",我需要通过hexedit修改为"Hello, World!"如下:

hexedit libhelloworld.so

我们知道Hello, World!和Hello, Kylin!的二进制如下

# echo "Hello, World!" | xxd -g 1 00000000: 48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 0a Hello, World!. # echo "Hello, Kylin!" | xxd -g 1 00000000: 48 65 6c 6c 6f 2c 20 4b 79 6c 69 6e 21 0a Hello, Kylin!.

这里我们打算把Kylin更换成World,我们使用hexedit编辑即可

hexedit libhelloworld.so

这里我们将4b 79 6c 69 6e修改成57 6f 72 6c 64即可。此时我们再运行程序

# LD_LIBRARY_PATH=./ ./test-helloworld Hello, World!

3.2 开展保护

为了针对elf程序的修改行为,我们计划是新增一个名字为.encrypt的加密节,里面存放上述关键节的hash加密。关于hash的选择后面文章会提,这里提供添加节的方法如下:

objcopy --add-section .encrypt=sha256_text.bin libhelloworld.so libhelloworld_en.so

此时,这个新的so会多出现一个节,如下:

readelf -S libhelloworld_en.so [23] .encrypt PROGBITS 0000000000000000 00001058 0000000000000100 0000000000000000 0 0 1

至此,我们根据elf的基本组成原理,为了安全性考量,新增了一个由程序自定义行为(PROGBITS)的节.encrypt来实现对核心文件防篡改的基本需求