Post

2024年终总结

2024年终总结

相比于前两年的忙碌与焦虑,2024年对我来说像是gap year。这一年没有值得称道的大收获,但有从许许多多小的经历汇聚起来的新感悟。

1-3月 - “劫后余生”

1月的前10天,我在生理和心理都极度紧张的情况下完成了大论文盲审的提交以及毕设答辩。紧接着,为了尽量避免延毕,我把另外一个才做了30%的工作包装一下投稿到了随手找的一个C会,并计划在寒假期间继续完善。随着1月14日上午终于收到迟来的ICSE ‘24 SRC录用通知,我悬着快1年的心终于可以放下。虽然第二篇投稿最终因为novelty的原因没有中(1个accept,1个weak,但小会议没有rebuttal),我还是基本能够按时毕业了。收到录用通知后还是不敢怠慢,因为是第一次加上单独投稿,我对于后续的流程都格外上心。尤其是为了准备camera ready,我多准备了一点实验,还对照review写了一个feedback,实际上都不影响最终的录用。

回到学校后,我赶紧确定了毕业系统里的进度,在3月15日顺利拿到了双证。然后整个3月主要就忙了毕业和准备出国开会两件事。期间去电群,派出所办了n次手续,去了武汉和南京分别办申根签证,然后就是和学校里的各个待过的小团体告别。我对于毕业这件事情本身没有很强烈的感触,但对于待了7年的交大还是很有感情的。我记得和室友在最后一晚说到,我们就像交大的一个细胞,7年换一次:我们结束了在交大的生命,交大则每年都在更新血液。在毕业典礼结束后,我还安排和对象的父母双方见面,我将其视作我从担任辅导员以来锻炼的人情世故的最后考验,并下定决心在接下来的几年里不再把心思放到“琢磨人”这件事情上。

4-5月 - 告别Master

虽然身份上已经告别了硕士,即将开始好几个月的gap year,但我的心里仍然很难对于过去3年中的许多经历释怀。随着清明节祭拜外婆和奶奶,我再次从长辈那里接受了死亡教育和家族教育,开始以更加坦然的心态面对生老病死这些人生大事。4月的下半月,我前往里斯本开会,并随后在西班牙开启了10天的毕业旅行。这段旅程让我的思绪更加放空,切割掉过去的窝心回忆。5月份仍然延续着这个节奏,先后去了新疆,泰山,张家界,希望自己能够在前往香港之前多看看祖国的大好河山,不忘乡土情怀。

这两个月的间歇我也顺利完成了去香港的学生/工作签证事宜,并尝试申请了两次暑期开源计划(GSoC/OSPP)。虽然最终因为调研不够和proposal不过硬没有申请上,但也是增加了自己的知识面。希望在读博期间能够完成一次高质量的开源活动,并保持开源贡献。我认为这是介于onsite实习和组内research之间的成长模式,既能面向工业界需求锻炼自己的技术栈,也能发现一些高质量的软件安全问题。

6-8月 - 初来香港

最开始制定RA计划时,组里的学长指定的是IoT相关的二进制分析。但来了之后,导师把3个月RA的目标定为了升级组内项目的LLVM版本(3.6->13)。较大的版本跨度,不符合软件工程规范的代码,以及缺乏成熟的技术路线和相关经验,让这个任务对于我一个人来说是非常有挑战的。即使是有组里最资深的学长来指导我做,很多时候的细节我还是拿不定主意。同时因为缺乏长期计划和阶段性目标,导致这个任务的正反馈很低,让我一度认为其非常无聊繁琐。当然,我能理解导师给我安排这样一个任务主要就是为了锻炼组内需要的工程能力并熟悉组内项目,俗称“get your hands dirty”。

我们的预期是项目能够在新版本的LLVM上能够正常工作,但当时甚至没有配套的回归测试来保障升级的可靠性,所以最后降级为了通过编译以及保证分析过程中IR的合法性。此外,项目的编码风格不统一,没有按照LLVM推荐的coding style,所以升级这些代码需要重新理解逻辑。项目的很多代码也涉及到了非常基础的程序分析理论(例如reducible graph),或者包含了前人设计的,但没有文档的heuristics,这也增加了我理解代码的难度。最后,项目本身也在积极地开发之中,我还需要定期合并最新代码,并配合其他同学设计“热迁移”的方案。总之,我抱着“摸着石头过河”的心态,最终也算跌跌撞撞完成了所有CMake编译模块的单独升级,但仍存在一定的链接问题需要处理。

在完成这个任务的中后期,我还经历了一件非常“乌龙”的事情。组内毕业的大师兄回访实验室的时候知道了我在做这个,随即问我为什么要进行升级,但我只能从很细节的角度给出一些原因,始终触及不到他站在商业/全局角度看待这个项目的逻辑:升级能给项目带来什么?性能提升?适配更多基于新LLVM的项目?更易于维护组内的另一个工作SIRO Translator?这对我来说简直是灵魂拷问:难道下游软件版本越新就越好吗?我项目都做了一大半了,反而要我自己来调研项目的必要性?我不禁反思,我当时愿意接这个项目的时候有多少是出于学习的心态,有多少是为了完成服从性测试。但不管怎么说,这教会了我一个道理:在投入大量精力做一件事情之前能够尽可能搞懂这件事情的意义,不然会在不断的自我怀疑中内耗,并大概率导致失败

为了全面地回答这个问题,我做了关于LLVM changelog的调研,同时和SIRO的作者学长进行了交流:现代的LLVM为了现代高性能的指令集架构设计了更多的数据类型(vector),并修改了指针的类型(typed pointer -> opaque (void) pointer),还存在一些小规模的API变动。但这些升级对于提高静态分析性能来说似乎都没有太多本质的帮助。况且我们组已经在整体的技术路线上采取debug binary -> lifting -> 3.6 LLVM IR的技术路线,升级LLVM版本也不会带来太多的兼容性的提高。经过以上的努力,随着2个半月的RA合同到期,我还是放弃了继续升级。

在RA期间,我也浅浅地调研了升级的research value,包括static analyzer testing,semantic level for (ML)IR in static analysis

9-12月 - 开启PhD生活

刚入学,因为多次的实践都意识到了回归测试的重要性,组里便发动了所有学生为项目添加回归测试框架(又是不折不扣的labor work)。新生负责根据数据集标注ground truth,老生负责分析测试集的质量以及修复bug。由于我们的项目目前已经转型为二进制静态分析工具,所以找了一个CGC使用的数据集cb-multios。数据集包含了人工仿照真实软件场景编写的规模较小(100-1k LoC)的项目,我们需要标注出每个项目中被人为注入bug的静态数据流,尤其是最终的sink函数。

不同于Juliet Test Suite,我遇到的一些bug是非常规的,难以归类到某一CWE类型;或者遇到的bug其实是作者仿写了知名的CVE,其实相当于进行漏洞复现了。此外,为了对接修复bug的学长,很多时候需要拿出PoC才能快速定位静态分析工具失败的原因,这也提高了我分析漏洞的经验。

因为需要case by case地去看,很多时候的进度就是一周修复1-2个bug。终于我们在11月完成了这个任务,随后我便开始把重心转移到Plankton lifter上来。我首先调研了导师最关心的在C++ binary上构建callgraph的任务,对应到lifter阶段就是如何恢复虚函数表以及对应的class hierarchy。对于有debug info的情况,这是一个纯工程的问题;对于没有debug info,甚至没有symbol的情况,则不存在一个通用的方案。目前主流的两种思路分别是通过各种heuristic来找虚函数表,缺点是不能适配各种C++ ABI和指令集;通过object数据流分析找到virtual pointer以及对应的虚函数表初始化的位置,缺点是受到程序优化的影响较大。此类研究因为CFI的热度而流行过一段时间(2014-2020),但却没有一个集大成式的工作/文章来总结,能称得上是一个open problem。结合组里的研究方向来说:如何支持不同语言的虚函数表?如何分辨虚函数表和跳转表?如何处理相对虚函数表(relative vtable)?我认为都是值得探索的方向。

为了让我们尽快上手lifter的代码,学长还提出了两个good first issue:PIC代码中switch case的恢复和C++ try-catch的恢复。在有debug symbol的情况下,这也不会是太难的问题。我先是对C++ try-catch的恢复进行了一次文献调研,发现这也是一个结合控制流和类型恢复的topic,并且主要的工作还是来自于rewriting领域。在一次和导师的沟通后,我决定将C++ lifting作为自己接下来一段时间的工作重心,并根据几类OOP特征的lift写了一篇literature review,同时回顾了binary lifter这个领域从2013年以来到现在的几乎所有主流lifter的实现。有意思的是,我还申请了IDA Pro的学生license,希望能在接下来的一年熟练使用这些SOTA工具。

在最后的2个月,我还接手了关于CBTransform模块的维护工作。这个模块负责对用于静态分析之前的IR进行优化,使得其更好地适配静态分析器的工作。例如使得代码更紧凑,含有更少的类型转换,从而提高分析器工作效率;lower掉静态分析器不支持的指令类型。虽然写Pass本身是很容易的事情,但深刻理解LLVM中的几个经典Pass(SimplifyCFG,InstCombine)还是能够提高功力的。在这个过程中还发现一些有趣的问题:例如lifter产生的verified IR,在经过CBTransform之后,由于对predecessor的处理不当,仍然会产生malformed IR;例如lifter先从cfg中恢复出switchinst,但在静态分析时却又被lower为if-else结构。这些工程问题普遍反应了我们对于什么是最适合静态分析的IR以及如何更好地配合lifter和静态分析器仍然存在不统一的看法。尤其是前者,我认为从lifter的角度出发,结合最近很火的MLIR/Clang IR,将会是一个有趣的研究方向。

最后聊聊课程,这个学期我完成了3门课程(ML,概率统计,密码学)和其他所有的杂课。选择machine learning是因为我对于其他的core课程实在没兴趣,但binary disassemble/decompilation这个领域还是有很多ML相关的工作的,尤其是2024年出现了7篇顶会。虽然说是ML,但也介绍了许多DL和LLM的内容,总体来说更像一次次讲座。选修概率统计原本是因为和ML内容高度重合,而且学院要去选一门系外课程。没想到的是,这门来自商学院的概率统计课是他们的必修课,属于PQE内容,而且导师还是科班数学系出身。这门课属实让我回到了当时数学二专的感觉,狠狠被拷打了一个学期。每周的前2天我几乎都要花在这门课的作业上,workload比ML还大。密码学我则是直接waive掉了。杂课没啥好说的,就是除了规定的讲座课外我还听了几次小范围的讲座(Zhiqiang Lin,Darko,Ziyang Li),感觉通过作者视角来读论文也是一种高效的方式。

结语

作为一名一年级博士生,我显然离自己博士期间的目标还有很远,既然已经找到了方向,也有组里学长的支持,我应该在学长毕业之前尽快交接好组内的项目和research工作,让自己成长为在一个小方向能够独当一面的PhD。寒假期间,我会继续完成之前提高的lifter的小任务,同时完成Usenix Security ‘25的AE review工作。

如果说这半年的博士生活有什么感触,第一个是要学会圈内经营,圈外低调。以前我没有融入任何学术的圈子,从而总想着提高自己的表面影响力。但现在,我可能只要能把自己的能力证明给组内的导师/学长就足够了。另一个就是所有优秀的博士生都应当有成熟的科学哲学素养:如何提高学术品位,快速选择并切入一个领域,找到值得研究的问题,这我认为在博士期间需要提升的软技能。

最后,给2025年的自己一点期望:能够完成自己的第一篇顶会工作并录用,能够review二篇顶会文章,能够在LLVM/Binary相关的开源项目贡献1k+ LoC,能够参加一次国际学术会议,能够定下自己的PQE主题。

This post is licensed under CC BY 4.0 by the author.