山东大学项目实训树莓派提升计划二期(3)计算机系统原理实验三

前言

本文将介绍与汇编语言相关的实验三。

通常来说,能出的题目无外乎「增删改查」四个部分,CS:APP Data Lab 的则是「增」,即动手写代码,而 Bomb Lab 则是「查」,也就是读代码。我看了一部分 CS:APP 第三章之后发现,书里也是倾向于让学生懂得读汇编代码即可。此外,我还搜索了王爽老师的《汇编语言》实验,他的实验则更多倾向于「增」,与 CS:APP 教材的要求大相径庭。

因此,我会把实验三侧重于「查」,不然难度会比较高。

介绍

本实验共包含 4 道与汇编语言有关的题目,在实验中被称为「顽皮锁」,要求学生给出「钥匙」来进行解锁,如果任何一把钥匙输入错误,程序会自动退出。学生需要通过反汇编、反向工程等技术解码「顽皮锁」,了解其汇编层面的编码逻辑,以此判断 4 把「钥匙」的构成,并通过 C 语言标准输入输出方式解锁「顽皮锁」。

树莓派支持的是 Linux/ARM 平台,但「顽皮锁」实验内容是在 Linux/x86-64 平台上进行编译并完善的,其汇编语言及可执行文件代码可能存在不同,所以本文将主要介绍 C 语言的内容。

文件内容

学生版:

  • LOCK:「顽皮锁」主体可执行文件
  • README:实验指导书实验三部分节选
  • keyhold.c:main 方法所在的文件,能理解整个系统的执行顺序

完整版:

  • keyhold.c:main 方法所在的文件
  • lock.c:4 把「顽皮锁」的 C 语言文件,用于判断「钥匙」是否对上了号
  • lock.h:「顽皮锁」头文件
  • support.c:「顽皮锁」内部具体逻辑的 C 语言实现
  • support.h:「顽皮锁」内部具体逻辑的头文件
  • Makefile:简化编译过程的文件

具体内容

实验题目共有 4 道,分别考察的知识点为:

  • 字符串判断
  • 循环及条件跳转
  • switch 的判断
  • 递归调用

下面是 keyhold.c 文件中包含的 main 方法的函数体,不包含任何「顽皮锁」的具体实现。

int main() {

    printf("噢,这是几个很顽皮的锁,总共需要 4 把钥匙才能打开,\n");
    printf("希望你已经准备好了钥匙,那我们就开始解锁吧。\n");
    printf("(请输入第 1 把钥匙)\n");

    lock_1();

    printf("哇塞,第 1 个锁被打开了,看起来你还不赖嘛,\n");
    printf("要不要试试下一个?\n");
    printf("(请输入第 2 把钥匙)\n");

    lock_2();

    printf("你打开了第 2 把锁,让我们继续~~\n");
    printf("(请输入第 3 把钥匙)\n");

    lock_3();

    printf("这是第 3 把锁,你接近成功了~~\n");
    printf("(请输入第 4 把钥匙)\n");

    lock_4();

    printf("噢天呐,你打开了全部的锁,\n");
    printf("祝贺你,你完成了实验三。\n");

    return 0;
}

实验过程简介

本小节将介绍学生为了完成实验三需要做的事情。

  1. 调试 LOCK 文件

在完成实验三之前,学生已经在实验二中学习了 GDB 和 objdump 工具,本实现需要学生更加巧妙地使用这两个工具来完成实验。

unix> gdb LOCK
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from LOCK...
(No debugging symbols found in LOCK)
(gdb)

当我们调起 GDB 对 LOCK 文件进行运行时,相当于用命令行进行 IDE 中常用的 Debug 活动,具体教程将在实验指导书中给出。

  1. 反汇编 LOCK 文件

因为 LOCK 文件在编译时使用了 GCC 的 -g 参数,带有相应的调试信息,因此可以使用 objdump 对 LOCK 文件进行反编译获得其汇编文件。可以通过下面两个命令来进行反汇编:

unix> objdump -d LOCK >> lock.s
unix> vim lock.s

除了可以使用 vim 编辑器打开 lock.s 汇编文件外,还可以使用 emacs 或 nano,这里举一个在 Linux/x86-64 平台上编译的 lock_4 函数的例子:

00000000000012ee <lock_4>:
    12ee:       f3 0f 1e fa             endbr64
    12f2:       55                      push   %rbp
    12f3:       48 89 e5                mov    %rsp,%rbp
    12f6:       48 83 ec 10             sub    $0x10,%rsp
    12fa:       c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
    1301:       48 8d 45 f8             lea    -0x8(%rbp),%rax
    1305:       48 89 c6                mov    %rax,%rsi
    1308:       48 8d 3d ff 0c 00 00    lea    0xcff(%rip),%rdi        # 200e <_IO_stdin_used+0xe>
    130f:       b8 00 00 00 00          mov    $0x0,%eax
    1314:       e8 77 fd ff ff          callq  1090 <__isoc99_scanf@plt>
    1319:       bf 05 00 00 00          mov    $0x5,%edi
    131e:       e8 85 01 00 00          callq  14a8 <spinning>
    1323:       89 45 fc                mov    %eax,-0x4(%rbp)
    1326:       8b 45 f8                mov    -0x8(%rbp),%eax
    1329:       39 45 fc                cmp    %eax,-0x4(%rbp)
    132c:       74 0a                   je     1338 <lock_4+0x4a>
    132e:       b8 00 00 00 00          mov    $0x0,%eax
    1333:       e8 03 00 00 00          callq  133b <unlock_fail>
    1338:       90                      nop
    1339:       c9                      leaveq
    133a:       c3                      retq

函数的具体含义在此不做解析,这个是第 4 把「顽皮锁」的实验内容。

  1. 读懂汇编语言,输入「钥匙」

当学生反汇编文件后,需要花费一定的时间,并运用自己学习的知识,来读懂这个函数的内容,从而了解「顽皮锁」的内部构造。当了解完成后,进行下述操作可以开始尝试解锁:

unix> ./LOCK
噢,这是几个很顽皮的锁,总共需要 4 把钥匙才能打开,
希望你已经准备好了钥匙,那我们就开始解锁吧。
(请输入第 1 把钥匙)

哇塞,第 1 个锁被打开了,看起来你还不赖嘛,
要不要试试下一个?
(请输入第 2 把钥匙)

嘿嘿,看来你解锁失败了~~
祝你下次好运~~

这里我把输入的信息删去了,「钥匙」正确则会进入下一把锁,错误则程序退出。

实验编写简介

  1. lock 文件
#ifndef CSAPP_EXP2_LOCK_H_
#define CSAPP_EXP2_LOCK_H_

void lock_1();
void lock_2();
void lock_3();
void lock_4();

void unlock_fail();

#endif // CSAPP_EXP2_LOCK_H_

lock 文件包含 4 把「顽皮锁」的判断逻辑,当输入输出判断不对应,即「钥匙不对钥匙孔」时,会在此退出系统。

  1. support 文件
#ifndef CSAPP_EXP2_SUPPORT_H_
#define CSAPP_EXP2_SUPPORT_H_

#include <stdio.h>
#include <stdlib.h>

int arr[4];

char* get_line();

int string_length(const char*);
int strings_not_equal(const char*, const char*);

void read_four_numbers();

int which_to_go(int);
int spinning(int);

#endif // CSAPP_EXP2_SUPPORT_H_

support 文件包含的所有「顽皮锁」背后的核心逻辑函数及实现,通过这个文件可以直接看出「钥匙」的答案。

  1. keyhold

main 方法所在文件,前文已有所涉及。

  1. 汇编层面调试

为了保证实验顺利进行,我需要在汇编层面进行调试,使用的方法与前文相同,尽可能地满足 CS:APP 中所教地内容。

  • 使用全局变量
  • 使用 -O0 关闭 GCC 的汇编层面优化
  • 使用 -masm=att 将汇编代码由 Intel 格式转为 AT&T 格式
  • 使用 -fno-stack-protector 关闭金丝雀值栈保护机制
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值