现在的电脑基本上都是采取UEFI启动了,之前看过一些资料,大抵都讲道“现在UEFI不需要制作引导分区了,系统会自动从EFI分区下的\EFI\Boot\Bootx64.efi文件启动”,这种说法其实是以偏概全了。导致很多情况下出现了一些奇怪的问题(比如当我准备把操作系统安装到一个移动硬盘上的时候)。

究其错误的原因,从根本上来说是忽略了了主板上NVRAM(非易失性随机访问存储器,主板上自带的一小块配置存储器,不在硬盘上,你把整块硬盘都擦了它还在)中启动配置的存在。

1. 真正的UEFI启动逻辑

def 从EFI文件启动(文件路径):
根据文件路径加载EFI文件
if 开启了SecureBoot功能:
if not SecureBoot校验通过:
启动失败报错退出 # 一般可以在屏幕上看到错误信息
执行EFI # 一般来说也就从这里进入了操作系统或者Grub,所以不会返回

def 从存储设备启动(存储设备):
# 可以是是GPT分区,也可以是MBR分区
for 分区 in 存储设备:
# 分区类型在MBR/GPT的分区表中标注,其本质是个普通FAT文件系统分区
if 分区类型 == EFI系统分区:
# 注意,FAT文件系统路径不区分大小写,且由于出自Windows,所以其路径习惯用反斜杠\而非斜杠/
# 对于非x86_64类型的硬件,默认路径也有差异
if 分区根目录下存在"\EFI\Boot\Bootx64.efi"文件:
从EFI文件启动(分区索引 + 该文件在分区下路径)

def 从EFI启动项启动(启动项):
if 该启动项指向的是一个EFI文件路径:
从EFI文件启动(启动项所指文件路径)
elif 启动项指向的是一个存储设备:
从存储设备启动(启动项所指设备)

def 启动():
根据主板NVRAM中启动项配置,以及扫描的硬件(比如刚插入装机U盘等),生成启动顺序表
if 用户无操控:
for 启动项 in 启动顺序表:
从EFI启动项启动(启动项) # 成功则返回,失败可能会返回,也可能是报错退出
else:
根据启动顺序表选择界面
if 用户选择了一个启动项:
# 这对应了手动从列表中选择一个启动设备
从EFI启动项启动(启动项)
elif 用户手动选择了一个具体的EFI文件:
# 这对应了手动浏览EFI分区并选择某一个具体的文件
从EFI文件启动(文件路径)
这次启动失败了,死机?退出主界面?或者重新尝试?结果因机器而异


硬件扫描和准备
启动()
阅读全文 »

最终效果如图

image-20200804163636576

其实gdb自从8.3版本之后就已经默认支持了彩色显示,并且默认已经启用了,所以查看地址、文件名等符号时都是彩色的。但是!Ubuntu仓库中编译的gdb并没有启用代码高亮功能,运行help set style sources他会告诉你it was not linked against GNU Source Highlight,只有半吊子功能。

image-20200804161721358

想要真正的高亮,还得自己编译。

阅读全文 »

电脑系统为Ubuntu,用有线接入南大的校园网,发现每次开机之后都只有ipv4地址而没有ipv6地址。

  • 同设备Windows无此问题,排除硬件和学校网络的问题。
  • 换了另外一个电脑(也是Ubuntu),问题一样,排除配置错误和偶然问题。

不过,只需要每次开机后关-开一下网络,就会获取到ipv6地址,因此用了一年的笨办法,在crontab中配置开机60秒后自动service network-manager restart

最近发现这个问题原来是因为学校网络的DHCP地址分配速度太慢,导致系统开机向DHCP申请分配地址时,等了很久才只获得了ipv4地址,于是Ubuntu决定不再等了,反正没有ipv6有不是不能用(放屁,众所周知校园网ipv6不需要登陆账号就能直接用)。

新的解决方法说来也简单,只需要告诉Ubuntu别只等到ipv4就认为大功告成,没ipv6就继续等待和重试。

具体步骤如下:

  1. 获取当前连接的UUID

    nmcli connection show

    输出如下

    NAME        UUID                                  TYPE      DEVICE 
    有线连接 1  df43dd98-4443-3f03-9893-f1cd2b39d2a6  ethernet  enp3s0

    可能不止一个,根据名字记住所需要配置的连接的UUID。

  2. 禁止在未获取ipv6地址之前就结束网络连接尝试

    sudo nmcli connection modify <连接的UUID> ipv6.may-fail no
    sudo nmcli connection modify <连接的UUID> ipv4.may-fail no

    原理上,只需要配置ipv6.may-fail就足够,不管我不想试了,反正这样能用。

此后,每次开机系统会等待稍长一些时间才会进入网络已连接的状态,不过就已经直接具有ipv6地址了。

1. 系统与内核的关系

计算机科学语境中,内核即操作系统,kernel = OS,它直接与硬件交互、管理各种资源、调度其他进程。

日常语境中,操作系统是内核加上其他一些工具套件(如Windows的文件浏览器、控制面板、IE)组成的集合kernel ⊂ OS

虽然从用户的角度,那些“系统自带”的套件和普通的用户软件在信任程度、控制权限以及重要性方面都有很大差别,但是从内核的角度来说,这些套件其实和普通的程序其实是平等的,都只是内核调度之下的普通用户态程序。

阅读全文 »

下文中的Python指的是在官方cpython解释器上实现的Python 3.8

1. 一切皆对象

Python之中一切皆对象,更具体些说一切都是指向对象的指针(虽然Python本身并无指针的概念)。

这一点Python远比Java更为激进,Java虽然也有“一切皆对象”的口号,但是至少intdouble等基础类型就不是以对象的形式存在,且对象的方法作为对象的子组件自然也不是对象。但是在Python中则连最基本的整数值是以对象,对象中的方法本身也是另一个对象。可以说是名副其实地做到了一切皆是对象。

2. 连类型也是对象

C++明确区分了类型和对象,前者是一个理念中存在的形式,后者是绑定了特定存储空间的一个内容。

Java中对类型虽然也是都是java.lang.Class类的对象,但是除了反射等少数使用场景,类型和对象之间通常也是泾渭分明的。

而到了Python中,二者却已经到了难分难解的地步了。类型可以像普通对象一样建立、修改、销毁,唯一不同的只是可以产生对象。因此Python中将能产生对象的对象(亦即类型)称为类型对象(定语类型表示其特征,中心语对象表示一切皆对象的本质),其他的对象(寻常所言对象)称为实例对象。

阅读全文 »

下文中的Python指的是在官方cpython解释器上实现的Python 3.8

1. Python也有编译器

一般的观点中常将编程语言分为编译执行解释执行两类,C/C++是最典型的编译执行,而Python被视为最典型的解释执行。

典型以Java为例,二者并非泾渭分明,

graph LR
source(*.java) --javac--> bytecode(*.class)
bytecode --java--> result(程序输出)
  • 第一步中,编译器javac把源文件编译成字节码文件,这一步像是编译型语言,只不过输出不是CPU指令,而是虚拟机指令(所谓跨平台的原因再此)。
  • 第二部中,解释器java(或者说虚拟机JVM)负责执行字节码,产生实际程序效果。

Python其实也和Java一样,存在编译字节码和对字节码进行解释执行两步。只不过,Java的编译部分通常在开发者机器上完成,解释部分在用户机器上运行;而Python中这两步统统运行在用户机器上,编译发生在解释执行的前一刻(即Just In Time编译),编译器嵌入在解释器内部,对开发者和用户透明。通常唯一让使用者察觉到Python字节码存在的现象便是__pycache__文件夹,这个文件夹中缓存了被import的模块的字节码,以便加快执行速度。

阅读全文 »

河西 关中 山西 河北 辽东 山东 川蜀 江南 事件 西晋 280年,晋灭吴,三国结束 成汉 301年,李特起兵 304年,刘渊起兵 前凉 汉赵 慕容鲜卑 汉赵 东晋 311年,永嘉之乱
316年,司马睿即位
后赵 319年,刘曜改汉为赵,石勒自立 后赵 前燕 329年,后赵石勒灭前赵
337年,慕容皝自称为燕王
346年,桓温灭成汉 冉魏 段齐 349年,石虎死
350年,秦齐自立,冉魏诛羯
前秦 351年,冉魏灭后赵
352年,慕容儁灭冉魏称帝
355年,前燕取段齐
376年,前秦苻坚统一北方 后凉 西秦 西燕 383年,淝水之战,前秦分崩 后秦 后燕 394年,后秦灭前秦,后燕灭西燕 北凉 南凉 北魏 南燕 397年,后凉动荡
同年,北魏夺燕冀州地
398年,慕容德自立南燕
西凉 400年,李暠立北凉,后秦灭西秦 北燕 谯蜀 403年,后凉降于后秦
405年,谯纵自立于蜀
407年,赫连勃勃建胡夏
409年,西秦复国,冯跋立北燕
西秦 胡夏 410年,刘裕灭南燕
413年,朱龄石灭谯蜀
414年,西秦灭南凉
417年,刘裕灭后秦而返
北魏 420年,刘裕篡晋
421年,北凉灭西凉
439年,北魏统一北方
阅读全文 »

AFL(american fuzzy lop)最初由Michał Zalewski开发,和libFuzzer等一样是基于覆盖引导(Coverage-guided)的模糊测试工具,它通过记录输入样本的代码覆盖率,从而调整输入样本以提高覆盖率,增加发现漏洞的概率。其工作流程大致如下:

  1. 从源码编译程序时进行插桩,以记录代码覆盖率(Code Coverage)
  2. 选择一些输入文件,作为初始测试集加入输入队列(queue)
  3. 将队列中的文件按一定的策略进行“突变”
  4. 如果经过变异文件更新了覆盖范围,则将其保留添加到队列中
  5. 上述过程会一直循环进行,期间触发了crash的文件会被记录下来

AFL流程

阅读全文 »

被测程序在启用libFuzzer并编译链接后,即成为了一个可接受用户参数的命令行程序,直接执行程序便是启动测试。

1. 命令统一格式

一般格式:

./fuzzer -flag1=val1 -flag2=val2 ... dir1 dir2 ...

flags代表各个控制测试过程的选项参数,可以提供零到任意个,但必须是严格的-flag=value形式,并不是很符合unix命令行习惯:

  • 选项前导用单横线,即使选项是一个词而非单个字符
  • 选项必须要提供对应的值,即使只是一个开关选项如-help,必须要写作-help=1,且选项与值中间只能用等号,不能用空格。

dirs表示语料库目录,它们的内容都会被读取作为初始语料库,但测试过程中生成的新输入只会被保存到第一个目录下。

阅读全文 »