Paul C's Blog

To be funny,to grow up!

0%

这道题是一道已知RC4的密文和密钥,反求明文的伪装成逆向的密码题。

1689514292270

​ 图1

思考

反汇编时,要深入每个关键函数看一看,不能想当然。

IDA给出的伪代码代码大部分时候还是十分正确的,直接看伪代码比反汇编代码正确率要来得高。

干扰项:

1689514736957

在IDA的反汇编界面看代码,会误以为密钥为key

1
key_rodata = b"\xBE\xBA\xFE\xCA\xEF\xBE\xAD\xDE"

也会误认为密文为enc

1689514809165

不管任何一个弄错,都会使得结果错误。

干扰原因:按照C调用约定,在call之前的部分应该是函数的参数部分,但是

1
2
__int64 __usercall RC4_setkey@<rax>(__int64 RC4_key@<r12>, __int64 RC4_keytable@<r13>)
char __usercall memcmp@<al>(unsigned int a1@<edx>, __int64 a2@<r14>)

这里是作者自定义的call行为,call之前的内容并不全是函数的参数。

对于RC4_setkey,其真正的RC4_key是r12,。

1689515709788

而r12早就被赋予了值,其值为val,在got表中。

1
2
3
4
5
6
7
8
9
10
11
12
main         push    rbp
main+1 sub rsp, 200h
main+8 mov ecx, 200h
main+D xor eax, eax
main+F mov rdi, rsp
main+12 repne stosb
main+14 mov ebx, 6
main+19 lea rbp, flag ; "FLAG: "
main+20 lea r12, val
main+27 mov r15, rsp
main+2A lea r14, [r15+80h]
main+31 lea r13, [r15+100h]

对于memcmp,也类似,在其函数内部,再次修改rsi,其真正比较的是dat和r14.

1689516066778

正确的密钥和密文都在got表里

如图1所示,密钥应该为图1所示的val所对应的内容,密文应该为dat对应内容。

1689515029709

密文获取

memcmp里返回dat数组和传入的结果数组的比较情况,真正的密文应该是dat数组。

1
result |= *((_BYTE *)&dat + v3) ^ *(_BYTE *)(a2 + v3);

点击第一处字节后,按下(Alt+L),再点击最后一处字节,就可以选定待导出的密文部分。

1689513948497

Shift+E,导出Array数组,复制粘贴到python代码里。

1689513831064

解密代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 已知的密钥表和加密文本
RC4_key= b"\x31\x09\x81\x19\x19\x14\x45\x11"
cmp_data =bytes([
0x78, 0xCF, 0xC4, 0x85, 0xDC, 0x33, 0x07, 0x4C, 0x93, 0x35,
0xFB, 0x7C, 0x10, 0x8E, 0xBE, 0x93, 0x28, 0xE6, 0x2E, 0x75,
0xDA, 0x5E, 0x85, 0xC5, 0x91, 0x15, 0x75, 0x89, 0x48, 0x0E,
0x29, 0xA4, 0xF9, 0xA6, 0x3A, 0x6E, 0x1F, 0x84, 0xF7, 0x42,
0xB0, 0x93, 0x31, 0xF0, 0x68, 0xC0, 0x43, 0x38, 0x07, 0x32,
0x09, 0x57, 0xDA, 0x32, 0x44, 0xCF, 0xCD, 0x8F, 0xE5, 0xBF,
0xE3, 0xD6, 0xBB, 0x59, 0x9A, 0x6A, 0x84, 0x85, 0xD3, 0x22,
0xA9, 0x8E, 0xB5, 0xEA, 0xBD, 0x57, 0xDE, 0xB1, 0x6C, 0x93,
0xE4, 0x74, 0x70, 0xAC, 0x1A, 0x03, 0xD9, 0x16, 0x9F, 0xBC,
0x97, 0xFB, 0x85, 0xD9, 0xA6, 0x9E, 0xD4, 0xD6, 0x02, 0x59,
0xD5, 0x28, 0xB3, 0x93, 0x16, 0xB6, 0xC4, 0x78, 0xC4, 0xA2,
0x12, 0xD2, 0xEF, 0xB1, 0x54, 0x18, 0xFD, 0x76, 0x51, 0xA3,
0x5E, 0x57, 0xB8, 0x58, 0x4B, 0x1E, 0xE2, 0x41
])

def RC4_setkey(key, keytable):
keytable_length = 256#len(keytable)

# 初始化密钥表
for i in range(keytable_length):
keytable[i] = i

# KSA (密钥调度算法)
j = 0
for i in range(keytable_length):
j = (j + keytable[i] + key[i % len(key)]) % keytable_length
keytable[i], keytable[j] = keytable[j], keytable[i]

return keytable

#zer0pts要求的字符范围
def filter_printable_chars(flag):
filtered_flag = bytearray()
for byte in flag:
if 0x20 <= byte <= 0x7E:
filtered_flag.append(byte)
#else:
#print("ERROR")
#break
return filtered_flag

def gen_keytable(keytable, enc_text):
flag_length = len(enc_text)
v4 = 0
v5 = 0
for v3 in range(flag_length):
v4 = (v4 + 1) % 256
v5 = (keytable[v4] + v5) % 256
keytable[v4], keytable[v5] = keytable[v5], keytable[v4]
#flag[v3] = enc_text[v3] ^ keytable[(keytable[v5] + keytable[v4]) % 256]
return v4,v5

def RC4_decrypt(keytable, enc_text,v4,v5):
flag_length = len(enc_text)
flag = bytearray([0] * flag_length)


for v3 in range(flag_length-1,-1,-1):
flag[v3] = enc_text[v3] ^ keytable[(keytable[v5] + keytable[v4]) % 256]
keytable[v4], keytable[v5] = keytable[v5], keytable[v4]
v5 = (v5-keytable[v4]) % 256
v4 = (v4 - 1) % 256

return flag


# 获得密钥表
RC4_keytable = bytearray([0] * 520)

# 设置密钥表
RC4_setkey(RC4_key, RC4_keytable)
hex_string = ' '.join([f'\\x{byte:02X}' for byte in RC4_keytable])
# 打印密钥表
print("密钥表:\n", hex_string)

#获得解密需要的初始值
v4,v5=gen_keytable(RC4_keytable,cmp_data)
hex_string = ' '.join([f'\\x{byte:02X}' for byte in RC4_keytable])
# 打印密钥表
#print("密钥表:\n", hex_string)
print(v4,v5)

# 解密获取flag
flag = RC4_decrypt(RC4_keytable, cmp_data,v4,v5)
filtered_flag=filter_printable_chars(flag)
# 打印flag
print("Flag:", filtered_flag.decode())

最终结果:

1
Flag: zer0pts{d0n'7_4lw4y5_7ru57_d3c0mp1l3r}

ADVAPI.dll 提供高级系统功能,Advanced,如 注册表、安全性、事件日志、服务管理等 ;

查看API对应的源代码路径:

1
C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um

只运行一个实例

1
2
3
CreateMutexA
FindWindowA
使用共享Section

5.1节的Serial.exe只能运行一个实例。

1688962133346

1688963259744

修改FindWindow后面的判断逻辑即可。一开始改为了75(jnz)

此时可以运行多个,但是运行前需要找到已存在的一个窗口才能继续创建,再次修改程序,把jz改成jmp。

微信

先根据break定位关闭微信的函数CloseWechat,进而找到关键函数sub_405FD0,它控制着返回结果。

1688970540191

关键函数int __stdcall sub_405FD0(DWORD a1, int a2, int a3, int a4)

跟进去后,可以看到一开始检查注册表语言配置和更新配置。

1688968867614

微信整体打开,靠WechatWin.dl里的函数。

1688969199372

打开WechatWin.dll,发现其导入表里存在实例判断函数。

1688969733507

1688969749959

双击进入后,等待库加载完成,需要几分钟,之后x查看两个函数的上下文:

注意这里一定要多等一会,不然调用CreateMutex的地址可能检索不全。

1688970084672

调用FindWindowW函数的上下文十分明显:

1688971265497

对于上图尾行的这个大跳,是一个经典的判断Mutex是否存在的代码段。

Mutex存在,则GetLastError返回 ERROR_ALREADY_EXISTS.,不存在时,返回0.

1688971805972

把jnz StartNewInstance改变成为jmp。

爆破成功。

1688972258464

利用QQProtect.exe提权,获得NT Authority\SYSTEM权限。

漏洞成因:

QQProtect.exe没有使用ASLR保护,篡改函数指针,利用ROP链(Ret Oriented Programming,即修改函数返回指针)实现了任意代码执行。

1.QQProtect.exe+0x40c9f8处,有任意地址写漏洞

待添加自己的截图,网络截图可见:

image0

可以在任意地址写入DWORD(1)。

2.QQProtectEngine.dll+0x3B4F6处,

待添加自己的截图,网络截图可见:

image1

可以完成对任意地址的指针ptr,偏移2个ptr指针类型大小的位置处,赋值为(unsigned int )(ptr+3)。

Affected Products:

  • QQ 9.7.1.28940 ~ 9.7.8.29039
  • TIM 3.4.5.22071 ~ 3.4.7.22084

Affected Components:

  • QQProtect.exe 4.5.0.9424 (in TIM 3.4.5.22071)
  • QQProtect.exe 4.5.0.9426 (in QQ 9.7.1.28940)
  • QQProtectEngine.dll 4.5.0.9424 (in TIM 3.4.5.22071)
  • QQProtectEngine.dll 4.5.0.9426 (in QQ 9.7.1.28940)

POC

POC整体用rust构建,先来解析配置文件里的内容。

1.配置文件解析

针对32位系统使用静态链接的C运行时,构建可执行文件。

1
cargo +stable-i686-pc-windows-msvc build --release --config "build.rustflags = [\"-C\", \"target-feature=+crt-static\"]"

对应的toml文件

1
2
3
4
5
[profile.release]
opt-level = 3

[profile.release.overrides.'cfg(target_os = "windows")']
rustflags = ["-C", "target-feature=+crt-static"]

此时,只需要cargo build --release即可。

开发过程中,要利用一些依赖库,来调用Windows API、服务和命令行参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[dependencies.rhexdump]
version = "0.1.1"

[dependencies.windows]
version = "0.44.0"
features = [
"Win32_Foundation",
"Win32_System_LibraryLoader",
"Win32_System_Threading",
"Win32_System_SystemServices",
"Win32_System_Console"
]

[dependencies.windows-service]
version = "0.5.0"

[dependencies.windows-args]
version = "0.2.0"

#生成动态链接库,即dll文件
[lib]
crate-type = ["cdylib"]

2.定义的src\lib.def文件中的导出符号。

C++名称修饰规则

C++ 的名称修饰规则,也称为名称重整(name mangling)。用于编译器生成符号表,会因平台和编译器有所不同。它将函数和变量的名称、参数类型、返回类型等信息编码成一个唯一的符号。

名称修饰的目的是确保在链接阶段可以正确地解析函数和变量的符号,避免符号冲突和重复定义的问题。 为了实现不同编程语言之间的互操作性,可以使用 extern "C" 来指定使用 C 风格的符号。

导出符号的格式是 ?SymbolName@ClassName@@FunctionSignature=_alias@0

下面的文件规定了导出的类和符号命,并 将所有导出符号重命名为 _die

1
2
3
4
5
6
7
8
9
10
11
12
13
LIBRARY
EXPORTS
?NextSiblingElement@TiXmlNode@@QAEPAVTiXmlElement@@PBD@Z=_die@0
?FirstChildElement@TiXmlNode@@QAEPAVTiXmlElement@@PBD@Z=_die@0
??1TiXmlDocument@@UAE@XZ=_die@0
?RootElement@TiXmlDocument@@QAEPAVTiXmlElement@@XZ=_die@0
?GetText@TiXmlElement@@QBEPBDXZ=_die@0
?Attribute@TiXmlElement@@QBEPBDPBD@Z=_die@0
?Attribute@TiXmlElement@@QBEPBDPBDPAH@Z=_die@0
?LoadXML@TiXmlDocument@@QAE_NPADHW4TiXmlEncoding@@@Z=_die@0
??0TiXmlDocument@@QAE@XZ=_die@0
DllMain

QAE 指示该构造函数是一个非静态成员函数,

  • QB 表示该函数是一个非静态成员函数,并且返回一个指针。
  • EPBD 表示函数的第一个参数是一个 const char* 类型的指针。
  • PAH 表示函数的第二个参数是一个 int* 类型的指针。
  • PAVTiXmlElement 表示返回类型是一个指向TiXmlElement` 类的指针。
  • 0TiXmlDocument@@QAE@XZ中0表示该类的默认构造函数
  • @Z:函数参数的结尾标记 ,XZ 表示该构造函数不接受任何参数。

代码解析

1
CreateProcessAsUserW(token2, None, PWSTR("cmd.exe".encode_utf16().chain(Some(0)).collect::<Vec<u16>>().as_mut_ptr()), None, None, FALSE, PROCESS_CREATION_FLAGS(0), None, None, &si, &mut pi).ok().unwrap();

大数据在医疗、金融、安全、管理等方面都有很多应用,可以用来化解这些领域中出现的风险;而大数据的应用本身又带来一系列安全隐患,需要靠机器学习技术去解决或缓解。

Prompt:本篇论文采用了{}机器学习技术,对大数据的{}{}特征进行建模,处理{分类}任务,达成了{}效果,在{}领域有所应用。

大数据应用

大数据的一些应用,推荐系统、辅助决策、异常检测、关联分析

  1. 数据驱动的决策:大数据分析可以提供大量的实时和历史数据,帮助组织做出更准确的决策。通过对海量数据进行挖掘和分析,可以发现隐藏的关联和趋势,从而指导业务决策和战略规划。
  2. 个性化服务和推荐系统:基于大数据分析的个性化服务和推荐系统可以根据用户的历史行为和偏好,提供定制化的产品和服务。这使得用户能够更好地满足其需求,提升用户体验。
  3. 预测和趋势分析:通过对大数据进行分析,可以预测未来趋势和行业发展方向。这对于市场营销、销售预测、风险管理等领域具有重要意义,可以帮助组织做出战略决策和规划。
  4. 预测和趋势分析:数据挖掘可以通过分析历史数据,发现潜在的趋势和模式,从而进行未来的预测。这在市场预测、销售预测、天气预测等领域具有重要应用。
  5. 客户细分和市场分析:通过对客户行为和偏好的分析,可以将客户划分为不同的细分群体,了解他们的需求和行为模式,并制定针对性的市场策略和营销活动。
  6. 金融欺诈检测:数据挖掘可以帮助金融机构检测异常交易和欺诈行为。通过分析大量的金融交易数据,可以识别潜在的欺诈模式和指标,及时采取相应的措施。
  7. 社交网络分析:数据挖掘可以揭示社交网络中的关系和影响力。通过分析社交媒体数据、用户之间的互动和信息传播,可以了解社交网络中的关键人物、群体动态和舆论趋势。
  8. 生物信息学:数据挖掘在生物信息学中发挥重要作用,例如基因表达数据的分析、蛋白质结构预测、基因组序列分析等,有助于理解生物学的复杂性和进行基因研究。
  9. 物流和供应链优化:通过对物流和供应链数据的挖掘和分析,可以优化物流路径、减少库存成本、提高配送效率,并优化供应链的整体运作。
  10. 医疗诊断和治疗:数据挖掘可以帮助医疗领域进行疾病诊断、药物选择和治疗计划的优化。通过分析医疗数据和患者病历,可以发现潜在的疾病模式和个体化的治疗方案。

大数据在风险管理领域的应用

可靠性方面

基于新能源汽车大数据平台 ,研究电动汽车的锂离子电池可靠性问题, 电池故障诊断与健康状态预估

[1]李放,闵永军,张涌.基于大数据的动力锂电池可靠性关键技术研究综述[J].储能科学与技术,2023,12(06):1981-1994.DOI:10.19799/j.cnki.2095-4239.2023.0316.

医疗

在医疗领域,大数据的应用可以带来以下益处:

  1. 疾病预测和诊断:通过分析大规模的医疗数据,可以发现潜在的疾病风险因素和诊断模式,提前进行疾病预测和诊断,从而提高治疗效果和患者生存率。
  2. 药物研发和治疗方案优化:大数据分析可以加速药物研发过程,帮助科学家识别潜在的药物靶点和候选化合物。此外,通过分析大量的患者数据,可以优化治疗方案,提供更个性化和有效的治疗。

金融

  1. 风险管理和欺诈检测:通过分析大数据,可以识别金融交易中的异常模式和风险指标,帮助金融机构及时发现潜在的欺诈行为,并采取相应措施进行风险管理。
  2. 个人信用评估和贷款决策:利用大数据分析个人的消费行为、信用记录和其他相关数据,可以更准确地评估个人的信用状况,并基于此做出贷款决策和利率定价。

计算机

隐私保护

[1]方滨兴,贾焰,李爱平等.大数据隐私保护技术综述[J].大数据,2016,2(01):1-18.

环境、资源等其他方面

大数据带来的风险

大数据由于需要从网络等多个渠道收集数据,别有用心的攻击者可以进行数据投毒,污染模型,从而导致模型有后门生成。因此,大数据可能带来数据投毒风险。

  1. 数据隐私和安全:大数据涉及大量的个人敏感信息和机密数据,如个人身份信息、医疗记录、财务数据等。如果这些数据未经适当保护,可能会导致隐私泄露和滥用,甚至成为黑客攻击的目标。
  2. 偏见和歧视:大数据分析依赖于大量的历史数据,而这些数据可能反映了现实社会的偏见和歧视。如果不加以适当的处理,分析结果可能具有偏见性,导致不公平的结果和决策。
  3. 数据质量问题:大数据分析对数据的质量和准确性要求很高。如果数据存在错误、缺失或不完整,分析结果可能不准确或误导性,影响决策的可靠性。
  4. 安全漏洞和数据泄露:大数据系统中可能存在安全漏洞和弱点,攻击者可以利用这些漏洞来获取敏感数据或操纵分析结果。此外,数据共享和合作过程中的数据传输也可能面临被窃取或泄露的风险。

AI对大数据风险的解决

社交图谱、差分隐私、安全计算。

金融

计算机

隐私保护

环境、资源等其他方面

RSA

对RSA的攻击:

AES

DES

RC4

流密码 , 一次一个字节地加密消息 ,速度快。

1687250986520

对RC4的攻击:

ECC

多态字符串混淆、

基于哈希的导入解析

运行时常量计算

Zhang R, Che T, Ghahramani Z, et al. Metagan: An adversarial approach to few-shot learning[J]. Advances in neural information processing systems, 2018, 31. pdf

基础知识

1、小样本情况下,人利用经验和先前的知识学习任务,而传统机器学习不使用先前知识容易过拟合。

2.元学习是一种利用先前知识的技术,从相似任务分布上训练提取一种可迁移的模式。

3.半监督小样本学习:有标签的样本是很少,但是无标签的样本也可以获取的到。

问题挑战

半监督小样本分类的元学习

难以从小样本里获得可泛化的决策边界。

解决办法

核心思路:假样本可以帮助小样本分类器学到更陡峭的边界。真假决策边界是如何帮助小样本分类的???

思路来源

小样本学习很像半监督学习。修改GAN在半监督学习领域的应用,将其应用到小样本场景下。

1.两者都没有足够数量的有标签样本。

2.都能从不完美的生成器获益。

创新点

对RN和MAML,叠加MetaGAN,让准确率,上升2到3个百分点。

实验

1.数据集

Omniglot Mini-Imagenet

2.实验内容

在2个数据集上,做3类任务,实验设置有5-way 1-shot,5-way 5-shot,20-way 1-shot,20-way 5-shot。加粗部分在3类任务上都有做。

2.1 监督小样本学习

2.2样本级的半监督小样本学习

允许在任务中有一些未标记的训练样本,来自与标签相同类或者干扰类(是二者择一,还是都选???)。

2.3任务级的半监督小样本学习

更允许无监督学习,即在support Set里和Query Set里都不加标签。

未来研究方向

俞甲子等,《程序员的自我修养——链接、装载与库》,北京:电子工业出版社,2009年6月第2版。

不放过任何一个字节。

利用工具:gcc、readelf、objdump、size,cl

strace ./run跟踪程序的运行

objdump

1
2
3
4
 objdump -j .got -h add.so  看一下.got的段信息.
-s 将段的内容以16进制打印
-d 将所有包含指令的段反汇编
- h 展示区块头的信息

gcc命令

1
2
3
4
-E 预处理、
-S 编译、
-c 汇编、
-o 链接

readelf

1
-hlS 

3.1 目标文件的格式

目标文件:源代码编译后但还未链接的那些中间文件(.o或者.obj)

静态链接库:把很多目标文件捆绑形成一个文件,再加上一些索引,可以简单理解为一个包含很多目标文件的文件包。

1
2
file xxx.so
x86_64,version 1(SYSV)

表明该文件的兼容性,采用了Unix System V的格式规范,即COFF文件。

:Unix最早的可执行文件格式为a.out,无法应对共享库等概念,所以设计了COFF来解决它。

COFF的贡献:目标文件里引入了段机制,不同文件可以有不同数量和不同类型的段(Section节或者Segment段);定义了调试数据格式。

1684805407488

3.2为什么分段

指令和数据分段的好处:

1、给程序指令区域单独设置可读权限,防止程序被改写。

2、利用局部性原理提高缓存的命中率。现代CPU缓存也设计成数据缓存和指令缓存分离。

3、在内存中有多个程序副本时,通过内存共享,节省内存空间。只读数据(指令;图像、文本资源)可以共享;而每个副本进程的数据区域不一样,是进程私有的,属于Private Bytes。

1684807383776

如图所示,Win7的exploer.exe在运行中,此刻占据的总虚存为338 740KB,它的私有数据部分占了51 360 KB,即共享部分数据占了287 380KB,也就是280多MB的空间,极大的节省了内存空间。

3.3 段概述

ELF Header,描述整个文件的文件属性,包括文件是否可执行、是静态链接还是动态链接还是可执行文件(给出入口地址)、目标硬件和目标操作系统,还包括一个段表 Section Table:一个描述各个段在文件中偏移位置及段的属性的数组。
| | |
| ————————- | —————————————————————————————— |
| .bss | 为节省存储空间,只记录未初始化数据预留的空间大小,而不存储其内容,因为其默认全部为0. |
| .data | 初始化的全局变量和局部静态变量 |
| .text、.code | 代码段 |
| .rodata、.rodata1 | 只读数据段,比如字符串常量、全局const变量 |
| .comment | 存放编译器版本信息,比如字符串:”GCC:(GNU) 4.2.0” |
| .note | 额外的编译器信息,程序公司名、发布版本号等 |
| .debug | 调试信息 |
| .line | 调试时的行号表,源代码行号与编译后指令的对应表 |
| .eh_frame | 存储异常处理框架(Exception Handling Frame)的相关信息。包括异常处理函数的调用关系、异常处理函数的地址、堆栈展开信息等。 |
| SHT | 表示 “Section Header Table |

1684811465144

其他段 .got和.plt

.dynamic 动态链接信息
.hash 符号哈希表
.strtab String Table,字符串表
.symtab Symbol Table,符号表
.shstrtab Section Header String table,段名表,集中管理段名称。不存储用户自定义的字符串,而是存储与 ELF 文件结构相关的字符串
.plt\.got (Procedure Linkage Table,过程链接表),是动态链接的跳转表,用于延时加载; 全局入口表 Global Offset Table
.init,.fini 程序初始化与终结代码段

.表示这些段名由系统保留,应用程序自定义段名不能加.,否则容易跟系统保留段名冲突。

一个elf文件中可以有多个.text的段。

一些已遗弃的段名:.sdata、.tdata、.sbss .lit4 .lit8 .reginfo .gptab .liblist .conflict

再看动态链接中的PLT和GOT - 读懂原理与细节

BSS段

Block Started by Symbol Table ,最初是美航符号汇编程序(1950s)的一个伪指令,被IBM保留并引用到Fortran 汇编器,用于定义符号并且为该符号预留给定数量的未初始化内存空间。

因为bss段没有内容,所以它在文件中不占据空间;但是,程序运行时,要占内存空间。

1684810240218

借助size命令,查看代码段和数据段长度。

1684810335918

借助objdump查看各段基本信息,-x查看详细信息。

1684810503471

1
2
VMA Virtual Memory Address 运行地址,PC指针所指,程序真正运行的地址空间
LMA Load Memory Address 加载地址,程序实际存储的地址空间,大部分时候LMA=VMA。

解释:一个初始化的全局变量+一个初始化的局部静态变量,所以.data的Size为8;同理,.bss大小为8;因为两个printf里共有13+4共17个字符(每个字符串尾还有一个\0),所以这里的rodata大小为0x11.

各个段属性的第二行中,CONTENTS表示该段在文件中存在,可以看到BSS段没有CONTENTS,它在ELF文件中不存在内容。

测试

1
static int x1=0;//则x1应该放入.bss段里,因为未初始化的值就是0,放入.bss可以节省空间。

3.5 链接的接口-符号

链接如同拼积木,目标文件B引用A中定义的函数和变量,需要其具备独特的名字。

链接过程中,我们将函数和变量称为符号;其名称称为符号名。

俞甲子等,《程序员的自我修养——链接、装载与库》,北京:电子工业出版社,2009年6月第2版。

1.5 程序的分段和分页

解决三个问题:1.内存隔离。2.空间利用。3.重定位。

本质上是增加了一个中间层,即虚存,这样就可以避免在编写程序时还要考虑详细的地址分配问题了。为了内存隔离,分段;为了细粒度的空间利用,分页。

CPU->虚拟地址—>MMU(Memory Management Unit,内存管理单元)—>物理地址。

2.1 构建过程

预编译:.cpp/.cxx.,.hpp——>.i,.ii,处理所有的”#”预编译命令并递归插入和展开宏命令,删除注释,加入行号和文件名标识,保留#pragma编译器指令。

编译:gcc -S/cc1 hello.c -o hello.s。完成词法分析和语法分析,生成中间代码。

1
2
3
4
5
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/11/include
/usr/local/include
/usr/include/x86_64-linux-gnu
/usr/include

汇编:gcc -c /as hello.s -o hello.o。把汇编符号翻译为机器码。

链接:ld,最终确定各个符号的绝对地址,完成对指令引用的符号地址的校正,需要一大堆文件。

2.2 编译原理概述

词法分析,Scanner—>Tokens,识别记号,同时叫标识符放到符号表(lex)。

语法分析,Parser—>Syntax tree,关于编译器的编译器(yacc)。

语义分析:Semantic Analyzer—>Commented Syntax Tree(注释语法树),只能分析静态语义:声明、类型匹配转换(在编译器可以确定的语义,其他的等待程序运行(除0异常)时才能确定)

中间代码生成:使得编译器可以拆分为前后端。非常接近目标代码,但是跟目标机器与运行时环境无关。不包含数据尺寸、地址和寄存器名字。

目标代码生成与优化:生成器和优化器。

1
LEA,基址比例变址寻址(Load Effective Address),进行地址的计算后将算得的结果放入存储中,不会把结果地址的内存数据实际加载到寄存器里。用于高效地进行一些复杂的地址计算,例如数组索引、数据结构访问等,而无需实际读写内存

2.3 链接器年龄比编译器更长

纸带编程时,一些跳转都是绝对地址,需要人为计算,一旦指令间增删其他指令,所有目标地址都要重新人工计算(重定位,Relocation,每个要被修正的地方叫一个重定位入口,Relocation Entry),一条纸带上的指令很麻烦,多条纸带间更是灾难。

引入符号(对函数和变量位置的助记符),计算符号地址的过程让汇编器自动完成,方便程序的模块化。

程序之间的接口就是这些符号,类似于拼图,我少这一部分,你恰好多这一部分,每个模块放到合适的位置,就是一幅完整的有意义的图。

2.4 静态链接是什么玩意?

详细介绍在第4章。

链接过程包括:地址和空间分配、Symbol Resolution(符号决议:静态链接,绑定:动态链接)和重定位。

库:常用的代码编译成目标文件存放,例如运行时库。

5.2一些历史

DEC(Degital Equipment Corp.)被 康柏电脑 收购, 康柏电脑02年又被惠普(1939)收购。DEC推出的VAX/VMS(Virtual Address eXtension/System) ,是一个最先使用权限管理和容错机制的多任务OS, 提供高性能、可靠性和安全性 。微软开发Win NT时,最初成员来自于DEC的VAX/VMS小组,所以Windows的PE格式来自于DEC的VAX/VMS上的COFF(Common Object File Format)文件格式。

贝尔实验室 (Bell Telephone Laboratories, 1925 ) —> 肖克利半导体实验室(Shockley Semiconductor Laboratory,1955) —> 仙童半导体(Fairchild Semiconductor,1957) —>AMD(1968),Intel(1968) —>Microsoft(操作系统,1975)。

SGI(Silicon Graphics, Inc.,1982) —>NVIDA(正在收购ARM(移动处理器),1993), Netscape (1994年,开源了 Netscape Navigator(打不过微软IE) ,发展成为 Mozilla Firefox 。

Google (创建者为Stanford University博士,PageRank算法: Google搜索引擎的核心, 通过链接关系评估网页的重要性 , 1998 )

思路:链接多的代码块,也可以权重传递和赋予它高的权重值。

  • COFF:VC++产生的目标文件格式。

  • PE:Win平台下的可执行文件格式,和ELF都是基于段的相同结构, 是COFF的一种扩展。对于64位系统,将PE中的32位字段改为64字段,叫做PE32+。

  • 映像文件Image File:因为PE文件在装载时会被映射到进程的虚拟空间中运行,它是进程虚拟空间的映像,所以PE可执行文件又可称为映像文件。

SMP与多核

服务器要的是 并发吞吐 ,要CPU核数、要线程数、要大的L2/L3缓存。

而PC要的是单核频率。

截至2023年5月12日,至强的处理器基准频率基本在4GHz以下, 酷睿i9-13900KS 睿频最高为6GHz(受生产工艺影响)。出于性能需求发展出多处理器,SMP(symmetrical)是其中的常见形式,由于成本高,通过共用缓存,SMP发展成为多核处理器。

段名

段名只具有提示作用,没有实际意义。不同编译器产生的段名不同,VC++用’.code’和’.data’。

使用链接脚本来控制link时,段名含义一般是固定的。

自定义段

1
2
3
4
5
6
7
8
9
10
//GCC,扩展属性,将变量或者函数放入自定义段中
__atribute__((section("name")))
//要放入name段的全局变量或者函数


//VC++中,编译指示:把全局变量放到FOO段里
#pragma data_seg("FOO")
int global =1;
//把编译器指示换回来,此时开始,全局变量和局部变量恢复到放入.data中
#pragama data_seg(".data")

使用VS Command Prompt,编译:

1
2
#编译,不调用链接器,禁用Microsoft C/C++语法扩展,生成标准的C/C++目标文件
cl /c /Za SimpleSection.c

Microsoft C/C++语法扩展,编译器会自动定义__STDC__这个宏。

利用VS目标文件查看工具查看COFF格式或PE格式,详细/简介。

1
dumpbin  [/all,summary] hw2.obj >hw2_section.txt

C runtime library (UCRT)

微软的PE文件格式定义