当源代码“素颜”上镜:Go/Python编译器逆向工程线索链
背景
本文主要探讨在编程中遇到的某些疑难杂症,可以通过查看编译生成的低级别语言,来逆向解释上层语言的困惑。在纷繁复杂的代码世界中,源码分析是我们工具箱的一把利器,帮助我们解开代码底层的本质,去探寻真实的计算机实现。
本文以Golang和Python为例,各探寻一个简单的案例,抛出逆向分析的方法。
Python 逆向分析
a = 1
def test1():
print("test1-1", a)
def test2():
print("test2-1", a)
a = 3
print("test2-2", a)
test1()
test2()
看这个简单的案例,不妨停留一下,猜一下test1、test2执行结果是什么?
test1-1 1
test2-1 1
test2-2 3
这应该是大多数人的想法,我们接触Python,直观感受Python是解释执行,一行一行的执行,在执行test2的时候,第一次输出a用的全局变量a=1,第二次由于局部变量优先输出a=3。
下面来揭晓答案:
test1-1 1
Traceback (most recent call last):
File "local_var.py", line 11, in <module>
test2()
File "local_var.py", line 6, in test2
print("test2-1", a)
UnboundLocalError: local variable 'a' referenced before assignment
非常意外! test1的结果毫无疑问,test2执行报错,报错指明a变量在分配之前就被引用了,这是什么问题呢?我们可以借助Python生成的字节码了解Python底层的机制来探寻这个问题。
生成.pyc字节码
生成python字节码指令:
python -m py_compile local_var.py
加载和分析.pyc字节码
import marshal
import dis
import sys
# 加载.pyc文件
with open('local_var.cpython-39.pyc', 'rb') as f:
f.seek(16) # 跳过Python字节码文件的头部信息
code_obj = marshal.load(f)
# 反汇编代码对象
dis.dis(code_obj)
字节码内容分析
Disassembly of <code object test1 at 0x1046895b0, file "local_var.py", line 3>:
4 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('test1-1')
4 LOAD_GLOBAL 1 (a)
6 CALL_FUNCTION 2
8 POP_TOP
10 LOAD_CONST 0 (None)
12 RETURN_VALUE
Disassembly of <code object test2 at 0x1046929d0, file "local_var.py", line 5>:
6 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('test2-1')
4 LOAD_FAST 0 (a)
6 CALL_FUNCTION 2
8 POP_TOP
7 10 LOAD_CONST 2 (3)
12 STORE_FAST 0 (a)
8 14 LOAD_GLOBAL 0 (print)
16 LOAD_CONST 3 ('test2-2')
18 LOAD_FAST 0 (a)
20 CALL_FUNCTION 2
22 POP_TOP
24 LOAD_CONST 0 (None)
26 RETURN_VALUE
阅读字节码,找到并对比test1、test2的代码,我们可以得知:
- 同样加载a变量的数据,
test1用的LOAD_GLOBAL用来加载全局变量,test2用的LOAD_FAST加载局部变量。 - python并非逐行执行,而是通过编译(语法分析、语意分析、构造抽象语法树等)确定语意,确定a变量的来源
而test2中通过编译确定a为局部变量,但定义和赋值在使用之后,于是报错。
Golang逆向分析
性能优化,按照经验将循环中c.b.a这种访问,提前储存,循环中直接访问可以提高效率。但在Golang中做了这个优化,通过Benchmark测试后,发现并没有效果,于是猜测,Golang在编译过程中将这种访问做了存储优化。为了验证这个猜想,编写如下Golang代码:
package main
import (
"testing"
)
type TA struct {
a int
}
type TB struct {
a TA
}
type TC struct {
b TB
}
func getAdd(n int, m int) int {
return n*n*n + m
}
func Benchmark_direct(b *testing.B) {
s := 0
c := TC{b: TB{a: TA{a: 2}}}
for n := 0; n < b.N; n++ {
s = getAdd(c.b.a.a, s)
}
b.Log("Benchmark_direct", s)
}
func Benchmark_cache(b *testing.B) {
s := 0
c := TC{b: TB{a: TA{a: 2}}}
tmp := c.b.a.a
for n := 0; n < b.N; n++ {
s = getAdd(tmp, s)
}
b.Log("Benchmark_cache", s)
}
测试
benchmark测试结果如下
Mac go % go test -bench=. duration_test.go
goos: darwin
goarch: arm64
cpu: Apple M4
Benchmark_direct-10 1000000000 0.2397 ns/op
--- BENCH: Benchmark_direct-10
duration_test.go:27: Benchmark_direct 8
duration_test.go:27: Benchmark_direct 800
duration_test.go:27: Benchmark_direct 80000
duration_test.go:27: Benchmark_direct 8000000
duration_test.go:27: Benchmark_direct 800000000
duration_test.go:27: Benchmark_direct 8000000000
Benchmark_cache-10 1000000000 0.2279 ns/op
--- BENCH: Benchmark_cache-10
duration_test.go:37: Benchmark_cache 8
duration_test.go:37: Benchmark_cache 800
duration_test.go:37: Benchmark_cache 80000
duration_test.go:37: Benchmark_cache 8000000
duration_test.go:37: Benchmark_cache 800000000
duration_test.go:37: Benchmark_cache 8000000000
Benchmark_noop-10 1000000000 0.2276 ns/op
运行结果:
Benchmark_direct、Benchmark_cache中循环一次的执行平均分别为0.2279ns、0.2276ns,两个函数运行时间结果几乎没有差别。
生成代码分析
c.b.a这个操作有缓存,我们从Golang编译后的低级代码找答案,生成指令:
nohup go test -gcflags="-S" duration_test.go > duration_test_src.txt
两个函数diff结果如下图,除了函数名称、代码行号、变量位置外,源代码一致,说明编译器在编译环节所有优化,对a.b.c循环中的访问做了优化,另外还对getAdd函数做了内联处理,更多细节这里不继续展开(可以通过LLM来解析解释后的结果),从而缓存优化问题得到确认。

逆向分析其它用途
从黑盒到白盒
编译结果分析打破了高级语言与机器代码之间的抽象层,使开发者能够以“机器视角”理解代码的真实行为,推动优化从经验驱动转向数据驱动。
逆向调试
在无法通过源码调试时(如生产环境崩溃),直接分析汇编定位问题。
性能分析
结合编译分析、性能剖析(Profiling)和基准测试,构建从源码到落地的全链路优化体系,覆盖开发、测试、部署全生命周期。
源代码加密
通过对编译、可执行文件加载的控制,可以实现对产生的编译结果加密,防止源代码泄露。
当源代码“素颜”上镜:Go/Python编译器逆向工程线索链的更多相关文章
- 4 个快速的 Python 编译器 for 2018
简评:Python 和其他的解释型语言一样经常被吐槽性能不行,所以开发人员为了提升性能创建了不少编译器,本文则选取其中的四个做了基准测试. Python 其实是一种相当快的语言,但它并不像编译型语言那 ...
- Python 编译器与解释器
Python 编译器与解释器 Python的环境我们已经搭建好了,可以开始学习基础知识了.但是,在此之前,还要先说说编译器与解释器相关的内容. 如果这部分内容,让你觉得难以理解或不能完全明白,可以暂时 ...
- 11 个最佳的 Python 编译器和解释器
原作:Archie Mistry 翻译:豌豆花下猫@Python猫 原文:https://morioh.com/p/765b19f066a4 Python 是一门对初学者友好的编程语言,是一种多用途的 ...
- python编译器的安装和pycharm的安装
python编译器的安装 进入官网https://www.python.org/,根据提示安装 安装python编译器 pychram安装 下载地址: https://www.jetbrains.co ...
- 用Python从零开始创建区块链
本文主要内容翻译自Learn Blockchains by Building One 本文原始链接,转载请注明出处. 作者认为最快的学习区块链的方式是自己创建一个,本文就跟随作者用Python来创建一 ...
- python设计模式之责任链模式
python设计模式之责任链模式 开发一个应用时,多数时候我们都能预先知道哪个方法能处理某个特定请求.然而,情况并非总是如此.例如,想想任意一种广播计算机网络,例如最早的以太网实现.在广播计算机网络中 ...
- 使用python爬虫爬取链家潍坊市二手房项目
使用python爬虫爬取链家潍坊市二手房项目 需求分析 需要将潍坊市各县市区页面所展示的二手房信息按要求爬取下来,同时保存到本地. 流程设计 明确目标网站URL( https://wf.lianjia ...
- Numba:高性能Python编译器
一.简介 Numba是一个开源JIT编译器,它将Python和NumPy代码的子集转换为快速机器代码. 二.主要特点 加速Python功能 Numba使用行业标准的LLVM编译器库在运行时将Pytho ...
- 转换器3:手写PHP转Python编译器,词法部分
上周写了<ThinkPhp模板转Flask.Django模板> 一时技痒,自然而然地想搞个大家伙,把整个PHP程序转成Python.不比模板,可以用正则匹配偷懒,这次非写一个Php编译器不 ...
- 详解python编译器和解释器的区别
高级语言不能直接被机器所理解执行,所以都需要一个翻译的阶段,解释型语言用到的是解释器,编译型语言用到的是编译器. 编译型语言通常的执行过程是:源代码——预处理器——编译器——目标代码——链接器——可执 ...
随机推荐
- Luogu P10590 磁力块
P10590 磁力块 有一个很显然的 BFS,对于每一个吸到的新磁力块,遍历序列,把所有它能吸到的磁力块加入一个队列进行扩展.这样时间复杂度是 \(O(n^2)\),不能通过. 考虑影响是否能吸到的两 ...
- 速通提示词工程Prompt Engineering
提示词工程简介 关注提示词开发和优化,帮助用户将大语言模型用于各场景和研究领域. 利用提示工程来提升大语言模型处理复杂任务场景的能力,如问答和算术推理能力. 通过提示工程设计.研发强大的工程技术,实现 ...
- Eclipse WindowBuilder(SWT)插件安装及初次使用记录(萌新)
Eclipse WindowBuilder(SWT)插件安装及初次使用(萌新) 一.插件安装 (有VPN的挂VPN,服务器在外网更新下载比较慢) 1.首先更新到最新版本 点击Help,点击check ...
- AI网络搜索
作为AI应用程序开发人员在了解函数调用(Function Calling)特性调用本地函数时可能注意到列表型参数tools中每一个元素都携带有一个type值.而在大多数函数调用示例程序中,这个type ...
- REST和GraphQL究竟谁才是API设计的终极赢家?
扫描二维码 关注或者微信搜一搜:编程智域 前端至全栈交流与成长 发现1000+提升效率与开发的AI工具和实用程序:https://tools.cmdragon.cn/ 一.核心概念与技术对比 (一)R ...
- Linguistics-English-区分词义 的 常义&反义 结合判断法- "正常的" 三个单词的 Normal/Abnormal -> Regular/Irregular -> Ordinary/Extraordinary
Linguistics-English-区分词义 的 常义&反义 结合判断法 "正常的" 的 三个单词(Normal/Regular/Ordinary)的词义辨析 -> ...
- [题解] CF549F Yura and Developers
洛谷传送门 Codeforces传送门 vjudge传送门 蒟蒻第一次写紫题题解,大佬轻喷. 题目大意: 给定一个数组 \(a\) 和常数 \(k\),求有多少个子区间满足区间和减去区间最大值是 \( ...
- 进阶篇:3.1.1)DFM-注塑件设计
本章目的:设计符合塑料模塑工艺(即注塑)的零件,不再犯简单错误,不必再为反复修改模具而烦恼. 1.基础阅读 进阶篇:2)DFMA的介绍 进阶篇:2.3)DFMA的运用方法(个人方法) 2.注射成型 ...
- Win10正式版如何查看电脑Wifi密码的问题
一些电脑基地的用户,说从Win10正式版计算机中如何提取已经保存的Wi-Fi密码?其实,这个还是很简单的,下面,技术员小编就来分享具体的提取方法. 在 Windows 10 中,查看已保存的 Wi-F ...
- 微软Win11 Build 22000.100更流畅好用了
据深度系统官网得到的最新消息,微软官方发布了Windows 11 Build 22000.100预览版更新.Windows开发团队表示请留意该预览版中的已知问题,从而了解新预览版可能会带来的体验问题. ...