多年来,我没有主动刻意去选择一门编程语言去学习, 无论做什么形式的工作, 首先考虑解决问题的实际效率问题, 逼自己提高工作效率实际上是另一种摆脱舒适区的比较好的方法。一个任务摆在眼前, 为了提高工作效率我会查阅很多资料, 包括: 使用什么编程语言, 各自有什么优缺点; 该使用什么样的架构, 我选择的编程语言有没有成熟的框架, 性能如何等等都会去调研。
在这个过程中首先我可以临时接受一门新的编程语言去学习, 但必须能让我很快上手, 最长学习时间不超过一周即可正常投入到工作中;其次会考虑编程语言的复杂性问题, 这个复杂性包括支持的库是否丰富、是否能够方便跨平台、是否能够方便部署等;最后是该编程语言是否有比较系统的文档和成熟的社区, 方便查阅和解决问题。
首先接触这三门语言跟我第一份正式工作内容有关, 我的第一份工作是做计算机病毒分析, 在逆向分析病毒行为的过程中, 需要在反汇编环境下观察堆、栈和寄存器内容的一举一动, 所以汇编语言属于基本功, 几乎天天打交道。另外在系统编程、嵌入式开发、游戏开发、驱动程序开发、软件加密解密等领域也经常能见到汇编语言的身影。
逆向分析中的汇编语言
随着工作性质的转变, 需要编写病毒的专杀工具来供用户使用, 普通用户当然需要一个界面来操作, 当时能够快速上手画界面并快速进行事件响应的非MFC莫属, MFC是微软基于C++开发的应用程序框架,充分利用了C++的面向对象特性, 并提供了诸如文档/视图架构、事件处理等基于C++封装的功能, MFC通过类封装了Win32 API的许多功能,简化了C++语言调用Windows API的复杂性, 在使用MFC开发病毒专杀工具的过程中, 对C++的特性也逐步熟练。
专杀工具样例
随着病毒的不断升级, 驱动级病毒也越来越普遍, 与病毒的对抗逐步由Ring3环转到Ring0环。编写驱动级病毒对抗程序经常会涉及文件过滤驱动、内核模式钩子等技术, 在Windows驱动开发中, 驱动程序代码主要使用C语言编写,微软的驱动开发工具WDK也是针对C语言的,而内核级编程需要直接调用Windows内核的本机API, 这些API接口也是用C语言编写的, 例如:利用内核钩子和过滤驱动对病毒进行拦截,涉及到编写处理病毒的C语言函数,在反汇编病毒二进制代码后得到的汇编语言代码,仍可以看到病毒实现的关键C语言函数,在使用Windbg进行内核调试时,可以对驱动程序的C语言源代码进行单步追踪,利用C语言可以调用WindowsAPI实现一些检测病毒的关键功能,因此C语言可以说是驱动级对抗程序开发的基石, 熟练掌握C语言在这个阶段必不可少。
文件过滤驱动样例
随着个人手机的不断普及, 大概从2011年开始, Android手机病毒逐步增多, 在逆向Android病毒的过程中, 我学会了jar包的反编译, 同时也学会了重打包, 自然也开始接触到Java语言,但个人对Java并没多大兴趣, 因为当时从心里觉得, 学Java的人实在太多, 高手也很多, 对于我来说没有什么竞争优势, 形成不了技能壁垒, 因此果断的放弃了Java语言的学习。
不过Java语言在大型商务网站, 与政府单位网站进行功能对接的项目中用的还是非常广泛的,毕竟Java生态有非常成熟的框架、整体解决方案和服务支持。
后来又接触到一个跟路由器相关的技术研究任务, 路由器使用的系统为OpenWRT, 并且提供了一个管理界面, 该界面的后端采用Lua语言编写,Lua语言是一门轻量级语言,编译器和解释器极小,可嵌入到其他应用程序。由于Lua语言具备语法灵活自由,支持面向过程和函数式编程,自动内存管理,可扩展性强,执行效率高等优点, 因此在游戏开发、嵌入式系统、Web应用、机器人、物联网和人工智能领域使用较为广泛。OpenWRT系统是一款著名的嵌入式Linux发行版,主要用于无线路由器, 而OpenWRT也支持Lua脚本,其后端主要通过Lua脚本实现, 因此要想完全自主控制路由器的功能, 必须在Lua原脚本代码中进行改造, 熟练掌握Lua语言必不可少。
Openwrt界面
我的第一个爬虫是使用Perl语言完成的, Perl语言的特点是跨平台,处理字符串功能强大,提供了丰富的正则表达式支持, 由于Perl语言采用了类C的语法, 有C语言基础的特别容易上手, 在一些特定领域具有独特的优势,例如:
文本处理 - 过滤日志、解析配置文件等
系统管理 - 系统监控、备份等运维自动化
网络编程 - 网络协议、SOCKET编程
CGI编程 - 用作服务端CGI脚本语言
数据处理 - 文件格式转换、数据分析等
云计算 - 用于部分云平台的自动化脚本
Perl的字符串处理能力与成熟的模块生态系统,使其在文本处理和系统管理方面有着比较广泛的使用, 但缺点也很明显: 语法不是很严谨,可读性较差;运行速度中等,不适合高性能场景,代码可维护性较差,开发活跃度也不及以往。
Perl代码
首次接触Python语言是在2016年, 当时研究的主要项目是跟网络反诈骗相关, Python语言主要有两大优势:
1.上手和开发效率特别高, 语法简洁、库丰富, 在用Python处理Excel数据过程中感觉非常舒适, 所以我更多的是使用Python进行运营方面的工作, 包括: 特定文件内容数据的批量处理, 管理维护类的杂项功能。
2.Python在数据分析、科学计算和人工智能方面有得天独厚的优势,例如用于数据分析的NumPy、Pandas、SciPy、Matplotlib。用于科学计算的SymPy、Scikit-learn、大名鼎鼎的TensorFlow等。用于人工智能的NLTK、OpenCV和Keras等。
尽管Python优势这么明显, 但在学习过程中仍然遇到一些问题, 这其中包括: Python版本及第三方库版本差异带来的问题、pip软件使用问题、代码部署问题, 在解决这些问题上也没少花时间。
Python代码
我正式使用Go语言是在2019年, 因为当时有个项目涉及到三方平台, 包括:Windows、Linux和开发板, 在经过调研后, 发现Go语言的跨平台功能非常强大, 并且学习难度不陡峭, 因为Go语言本身采用C语言开发, 在有C/C++的基础之后, 学习Go语言基本没有什么压力, 在完成这个项目之后, 我也将Go语言作为我的常用语言, 并不是说Go语言有多么优秀, 而是非常适合我的工作性质。Go语言有几个地方让我的工作非常受用。
1.跨平台方便
对于上面涉及到的三个平台, 首先可以统一在我的工作电脑中完成代码编写, 假如我的工作电脑是在MacBook Air上, 那么利用以下语句, 我可以轻易编译出直接在三个平台上运行的可执行程序,例如:
(1).编译在Windows x64上执行的程序:
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o main.exe
(2).编译在Linux x64上执行的程序:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main
(3).编译在树莓派(或者单片机)上执行的程序:
32位:CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -o main
64位:CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o main
(4).编译支持在Apple M1/M2芯片上执行的程序:
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o main
(5).编译支持在macos上执行的程序:
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o main
2.部署简单
我曾经在一个项目中看到用全Python写的代码, 但在线上环境运行过程中, 却使用Docker启动了十几个Python容器, 也看到有些项目在生产环境没有网络环境的情况下, 要上传几百兆的Docker离线容器或一堆依赖包, 但对于Go语言来说, 哪怕机器是个裸机, 我只用上传编译好的一个二进制程序, 假设这个二进制程序名为: main, 我只用以下操作即可部署完成:
chmod +x main (给予可执行权限)
./main &. (后台运行)
全程0依赖包, 0 Docker容器。
3.安全性高
Go语言编译器会将Go的源代码转换成中间字节码, 无法直接在计算机上运行, 这给反编译工具带来很大困难, 因为大部分反编译工具通常只能处理机器码。除此之外,Go编译器还会进行代码优化和混淆,使得反编译难度更大。
我正式接触Rust语言是在2021年, 虽然没有像Go一样在项目中一样使用的非常频繁, 但一直在持续学习和关注其发展。在当年的StackOverflow调查中,Rust成为最受欢迎的语言, Rust作为一门新兴语言, 却有着比较完整的生态系统, rust拥有包管理器Cargo、构建工具rustc等比较成熟的工具链, crates.io上拥有超过5万个包, 有比较成熟的Web框架例如:Actix、Rocket等。最主要的是rust语言受到很多巨头公司的青睐和支持,例如:Microsoft、Amazon和Google。
Rust相比其他编程语言,有着自身一些独特的优势,很值得我们学习,例如:
内存安全问题 - Rust的所有权系统可以保证内存安全,避免野指针错误、缓冲区溢出等问题, 这也是Rust的最大卖点。
性能问题 - Rust的运行时极小,抽象成本低,可编译为原生代码,性能可与C/C++媲美。
范型编程 - Rust有更高级的范性系统, 可以实现轻量级的抽象, 提高代码复用性。
更严格的一致性 - Rust严格的类型系统与显示错误处理, 可以建立更一致、可靠的大型程序。
并发安全 - Rust的Send/Sync系统提供并发安全保证,使并发编程更安全简单。
函数式编程特征 - Rust支持高阶函数、闭包、惰性计算等函数式编程能力。
出众的文档系统 - Rust文档齐全,各语言特性都有详尽的官方文档介绍。
然而, Rust的一些新特性却成为开发人员的入门难点,难点主要有以下几个方面:
所有权(Ownership)系统 - 需要改变编程思维方式,不熟悉会造成代码无法编译。
借用(Borrowing)机制 - 理解引用的作用域和生命周期, 避免dangling pointer。
生命周期(Lifetime) - 了解引用、变量、数据的生命周期关系。
trait对象动态分发 - Rust的trait模型比传统OOP更复杂。
并发模式 - Rust的并发安全保障需要学习新的并发模式。
错误处理 - 了解Result等错误处理方式。
目前越来越多的商业程序和开源项目正逐步使用Rust语言进行重构或编写,比较有代表性的项目如下:
Mozilla - Firefox浏览器中的一些组件,如新引擎Servo使用Rust开发。
Microsoft - Windows、Azure云平台中的一些模块;编译器成分Rust Analyzer。
Amazon - AWS云平台的Firecracker虚拟机管理程序。
Google - Android操作系统内核组件; Fuchsia OS中广泛使用Rust。
Dropbox - 存储服务客户端内部的一些关键网络库采用Rust。
Facebook - 分布式数据库Tornado使用Rust;扩展了Rust在WebAssembly的应用。
Red Hat - Linux操作系统中一些性能敏感的组件用Rust改写。
Nvidia - GPU驱动程序中的服务和工具,以提高安全性。
Cloudflare - 一些网络功能组件,如反向代理、分布式KV存储。
Parity - 密码货币钱包Parity、Polkadot区块链。
Rust带来的内存安全和性能优势,正在被云服务、操作系统、区块链等要求高可靠性的领域所认可和采用。
以自己的发展方向为目标, 工作内容尽量不要偏离自己的发展目标, 否则自己将来的知识很难构成体系, 在这个基础上, 逐步去接触各类编程语言, 用来解决工作中的实际问题, 只有这样,才可能更容易将知识点逐步以点盖面, 建立起自己的知识网络。