算法与程序设计实验
算法与程序设计实验
要求
1.复现代码,写上自己的注释
2.结果展示
3.将代码进行修改,bug修改,内容提升
第二章
逆序计数
1 | |
寻找最近点对
1 | |
第三章
时序分配
1 | |
最小生成树
kru
1 | |
prim
1 | |
反向删除
1 | |
第四章
哈夫曼编码
1 | |
时隙最优解权值和
1 | |
分段最小二乘法
1 | |
背包问题
1 | |
串匹配
1 | |
要求
1.复现代码,写上自己的注释
2.结果展示
3.将代码进行修改,bug修改,内容提升
1 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 | |
原理
1 | |
操作
结果
这两个堆块实际上指向同一个内存区域
原理
1 | |
操作
结果
chunk1和chunk2是同一个chunk
原理
1 | |
操作
结果
得到两个相同地址的chunk
原理
1 | |
操作
结果
任意地址读写
原理
1 | |
操作
- 1.free 一个fastbin大小的chunk 1
- 2.申请一个largin bin 大小的chunk,此时因为 malloc_consolidate(), chunk1 会被放到 unsorted bin 中
- 再次free chunk1
结果
现在 fastbin 和 unsortedbin 中都放着 p1 的指针,所以我们可以 malloc 两次都到 p1: 0xbba010 0xbba010,任意地址读写
原理
1 | |
操作
fd = goal - 0x18
bk = goal - 0x10
结果
任意地址写
原理
1 | |
操作
构造fake fastbin chunk,free掉这个chunk,再次申请可以拿回这个chunk
前提有一个可控的指针
结果
任意地址写,前提有可控指针
原理
1 | |
操作
结果
在一个大的free堆块中存在一个未被free的堆块
原理
1 | |
操作
结果
任意地址malloc
原理
1 | |
操作
结果
堆块重叠
原理
操作
结果
原理
操作
结果
原理
1 | |
操作
结果
修改任意位置为 一个很大的数
原理
1 | |
操作
结果
栈上地址被覆盖
机器语言是用1和0组成的代码,但机器是识别不了1和0的,更具体的是如何识别的呢?对机器电路进行设计之后,机器能识别高电平还是低电平,刚好与2进制很相似,想输入0就给机器输入低电平,想输入1,就给机器输入高电平,所以就看到了1和0的表示形式
机器语言它是计算机唯一能识别和执行的语言,但它的直观性差,可读性差,比如一串11110000111100001111机器可以快速识别是什么但是我们很难理解,再比如我们想要在屏幕上输出hello world那我们该如何用二进制来表示呢,所以汇编语言就诞生了
汇编语言用助记符来表示机器指令中的操作码和操作数的指令系统,如a = 1,我们不需要去用二进制来理解,我们完全可以利用mov a, 1进行理解,那有没有更简单的方法呢,比如现在要输出hello wrold,还是需要十几行的汇编代码的,所以高级语言就诞生了
高级语言是一种更接近人类的自然语言和数学语言的语言,比如想要a = 1,很直观就是a = 1,在很大程度上减少编程人员的编写量
但是问题来了,机器只懂0和1那怎么才能让高级语言被机器识别,所以就有了编译,将高级语言(源语言)翻译成汇编语言或机器语言(目标语言),编译的根本目的就是把源代码变成目标代码
编译过程主要可以划分为前端与后端,笔者用一张图简述一下

前端把源代码翻译成IR,后端把IR编译成目标平台的机器码,这里笔者在查阅资料的时候发现有些会将生成中间代码放入前端,而有些资料会将生成中间代码放入后端
在词法分析中编译器读入源代码,经过词法分析器识别出Token,比如词法分析器中识别出的Token可以是int, return, {, }等
在语法分析中会把上面的Token串给转换成一个抽象语法树AST,AST树反映了程序的语法结构
在语义分析中需要做的任务是理解语义,语句要做什么,如for是需要去实现循环,if是判断等
在前端完成之后,会生成中间代码,统一优化中间代码,再去将中间代码生成目标代码
前置知识这里笔者简述了一下,具体的可以移步编译原理
gcc这个最经典的编译器提供的是一整套服务,前端和后端耦合在了一起,导致了如果一个新的编程语言出现可能需要设计一个新的IR以及实现这个IR的后端,如果出现了一个新的平台就要实现一个从自己的IR到新平台的后端,针对此类问题就出现了LLVM
不同的前后端使用统一的中间代码,这样一个新的编程语言出现只需要实现一个新的前端,如果出现了一个新的平台只需要实现一个新的后端
LLVM IR有三种表示形式
.ll.bcLLVM Pass 是一个框架设计,是LLVM系统里重要的组成部分,因为LLVM Pass负责LLVM编译器绝大部分的工作,一系列的Pass组合,构建了编译器的转换和优化部分,抽象成结构化的编译器代码。
在实现上,LLVM的核心库中会给你一些 Pass类 去继承。你需要实现它的一些方法。 最后使用LLVM的编译器会把它翻译得到的IR传入Pass里,给你遍历和修改。
LLVM Pass的用处是插桩,机器无关的代码优化,静态分析,代码混淆等
以下内容来自LLVM Pass入门导引
llvm-as:把LLVM IR从人类能看懂的文本格式汇编成二进制格式。注意:此处得到的不是目标平台的机器码。llvm-dis:llvm-as的逆过程,即反汇编。 不过这里的反汇编的对象是LLVM IR的二进制格式,而不是机器码。opt:优化LLVM IR。输出新的LLVM IR。llc:把LLVM IR编译成汇编码。需要用as进一步得到机器码。lli:解释执行LLVM IR。Clang 是 LLVM 的前端,可以用来编译 C,C++,ObjectiveC 等语言。Clang 的功能包括:词法分析、语法分析、语义分析、生成中间中间代码LLVM Intermediate Representation (LLVM IR)。
ubuntu20.04下安装LLVM + Clang如下
sudo apt install clang-12
sudo apt install clang-8
sudo apt install llvm-12
sudo apt install llvm-8
llvm-12安装之后可以使用opt-12,今年的ciscn的LLVM PASS PWN就是opt-12,一般题目都会给出opt的版本。ubuntu20.04应该自带opt-10如果没有的话,sudo apt install clang-10 && sudo apt install llvm-10
上面的做题环境都安装完成之后,先写一个c文件,利用Clang将c文件编译成.ll, .bc等格式看一下是否是如上所说,c文件如下
1 | |
首先是.c->.ll,clang-12 -emit-llvm -S test.c -o test.ll,test.ll(生成的IR文本文件)如下
1 | |
上面的IR很直观,之前提到LLVM PASS的一个用处是优化IR代码,会将上面的可以优化的进行优化
其次是.c->.bc,clang-12 -emit-llvm -c test.c -o test.bc,bc是不可读二进制
然后是.ll -> .bc,llvm-as test.ll -o test.bc,结果和上面的一样
接着是.bc - > .ll,llvm-dis test.bc -o test.ll,同上
最后还有一个.bc -> .s, llc test.bc -o test.s,将字节码的二进制格式文件转换为本地的汇编文件
1 | |
通过前面的知识之后,现在可以尝试编写“hello world”的pass,下面是官方的示例
1 | |
先声明pass本身,然后声明了一个Hello类,它是FunctionPass的子类。稍后将详细描述不同的内置pass子类,但是现在知道FunctionPass一次对一个函数进行操作。
然后声明了LLVM用于标识pass的pass标识符。 这允许LLVM避免使用昂贵的C ++运行时信息,如下
1 | |
然后声明了一个runOnFunction方法,它覆盖了从FunctionPass继承的抽象虚方法。 这是我们应该做的事情,所以我们只用每个函数的名称打印出我们的消息。代码如下
1 | |
接着初始化passID。 LLVM使用ID的地址来标识pass,因此初始化值并不重要。代码如下
1 | |
最后,我们注册我们的类Hello,给它一个命令行参数“hello”,并命名为“Hello World Pass”。 最后两个参数描述了它的行为:如果传递遍历CFG而不修改它,那么第三个参数设置为true; 如果pass是分析pass,例如支配树pass,则提供true作为第四个参数。代码如下
1 | |
如果我们想将通道注册为现有管道的一个步骤,则提供了一些扩展点,例如PassManagerBuilder::EP_EarlyAsPossible在任何优化之前应用我们的通道,或者PassManagerBuilder::EP_FullLinkTimeOptimizationLast 在链接时间优化之后应用它。代码如下
1 | |
现在需要将这个Pass编译成模块,使用如下命令即可
1 | |
现在应该会看到LLVMHello.so这个文件,通过官方文档可知需要使用以下命令
1 | |
这里的 -hello由Hello.cpp中的static RegisterPass<Hello> X参数决定
但是笔者这里报了一个错Error opening 'LLVMHello.so': LLVMHello.so: cannot open shared object file: No such file or directory,这是因为linux无法在默认地址找到LLVMHello.so,解决很简单`sudo cp LLVMHello.so /lib

成功输出test.c所有函数名称
刚刚生成了LLVMHello.so这个pass文件,比赛题和上面也一样,会重写FunctionPass类中的runOnFunction函数,所以我们对上面的示例程序进行逆向分析,看一下虚表位置这样方便比赛的时候确定每个函数的位置

跟进RegisterPass

发现调用了callDefaultCtor进行对象创建,跟进它

给Hello对象分配了0x20个空间,跟进Hello

看到虚表了,直接跟进

runOnFunction函数位于虚表中的最后一个位置,因为runOnFunction函数被我们重写了,所以它指向的是我们自定义的那个函数,比赛题的漏洞基本就是这个,所以在做LLVM Pass pwn的时候定位函数的位置可以从虚表入手
收获很大,从编译过程到LLVM,加固了计算机底层的一些知识,知道了LLVM PASS PWN该怎么入手,以前看到LLVM PASS PWN的时候都不知道怎么运行(XD),这里第一篇就结束了,后面会继续更新
https://zhuanlan.zhihu.com/p/130702001
https://zhuanlan.zhihu.com/p/122522485
借鉴文档:【精选】ret2dlresolve超详细教程(x86&x64)-CSDN博客
在Linux中,程序使用_dl_runtime_resolve(link_map,reloc_offset)来对动态链接的函数进行重定位。
而ret2dlresolve攻击的核心就是控制相应的参数及其对应地址的内容,从而控制解析的函数。
第一次调用一个函数时,先是到plt表,然后jmp到got表

此时got表存的地址是在plt表上

其实也就是jmp got的下一条指令,这里先是push一个数字(该函数在rel.plt上的偏移,reloc_arg,后文会讲到),然后jmp到plt[0] (0x8048380)

在plt[0]处先是push got[1],got[1]就是link_map(链接器的标识信息,后文会讲到),然后jmp到got[2]处,got[2]就是_dl_runtime_resolve函数的地址


所以相当于执行了
1 | |
这个函数会完成符号的解析,即将真实的write函数地址写入其GOT表对应的条目中,随后将控制器交给被解析的函数
1 | |
1 | |
之前一直没掌握SROP技术,以此篇重新学习一下SROP
在目前的pwntools中已经集成了对于srop的攻击。
在汇编代码中看到存在systemcall的时候可以考虑采用该方法进行尝试
下面给出我们将会用到的64位函数及函数调用号和函数原型
| 系统调用 | 调用号 | 函数原型 |
|---|---|---|
| read | 0 | read( int fd, void *buf, size_t count ) |
| write | 1 | write( int fd, const void *buf, size_t count ) |
| sigreturn | 15 | int sigreturn( … ) |
| execve | 59 | execve( const char *filename, char *const argv[], char *const envp[] ) |
###使用sigreturn对read函数调用的寄存器进行部署
接下来就需要注意了,我们进入构造的阶段。我们需要通过sigreturn的调用来实现对read函数调用寄存器的部署。值得高兴的是pwntools中已经有了调用sigreturn的功能,所以在写EXP的时候可以直接使用。再部署之前我们需要之想好在哪几个寄存器中部署什么值,下面列出来一一讲解
| 寄存器和指令 | 存储数据 |
|---|---|
| rax | 系统调用号 |
| rdi | 0 |
| rsi | addr |
| rdx | len |
| rsp | addr |
| rip | syscall_ret |
首先是rax寄存器中一定是存放read函数的系统调用号啦,因为原汇编代码使用的是syscall,这个不多说了
●rdi寄存器作为read函数的一参,0代表标准输入
●rsi寄存器作为read函数的二参,里面存放的是前面通过write函数打印出来的新栈顶的地址,也就是说将接收到的信息写到我们前面通过write函数打印的新栈顶的位置
●rdx作为read函数的三参写0x400个字节
●rsp寄存器需要和rsi保持一致,在写的时候写在rsp指向的位置
●rip寄存器指向syscall_ret,确保在read函数寄存器部署成功之后可以直接调用read函数
1.、请根据课程内容设计一个app的门户框架,需要实现3-4个tab切换效果;本功能要求需要的技术为:activity、xml、fragment
2、在任一tab页中实现列表效果;本功能的实现需要使用 recycleview;
开发工具:as
版本:API 24 Android 7.0
类微信界面主要分为上中下三个部分,其中上下为 top.xml和 bottom.xml 为基础信息显示。
主界面中间部分由4个页面叠加,在进行选择内容时变换界面
其中我选择在聊天界面实现列表效果,采用 recycleview

图片

代码
1 | |
图片

代码
1 | |
通过一个xml文件将标题栏部分和底部选择栏部分添加到一个xml文件里面,再两个文件中间添加一个content部件,将四个fragment当做卡片压入中间主体部分。四个fragment的xml文件类似,故只放一个文件的内容。
第一个聊天界面我设置列表效果,只需要添加一个 recycleview 实现列表效果即可
图片

代码
1 | |
其他3个xml页面设置为介绍界面即可,效果和代码如下所示
图片

代码
1 | |
上文已经在相应的 fragment_lt.xml 文件里面添加了 recycleview,此时再添加一个item.xml页面用于页面显示,只包含一个textview
图片

代码
1 | |
图片

代码
1 | |
首先依次创建4个fragement

会在相应的layout文件夹下生成4个.xml文件

目前的界面只是一个比较简单的界面,需要完成的功能仅有展示和通过点击部件更换中间部分展示的界面,所以要考虑的代码部分分别为以下四个内容:
- 点击监听部分onclick
- 将4个fragment压入content里面的代码部分
- 将四个卡片隐藏起来的代码部分
- 当点击时展示的界面代码部分
创建4个Frangment变量、1个管理对象FragmentManager变量 、4个LinearLayout变量对象
1 | |
新建一个inital函数用以给Fragment页面初始化,在此函数中,将此前定义个4个Fragment变量使用fragmentManager添加到main文件中的中间主体部分的布局中
1 | |
在点击四个部件时需要展示其所代表的界面,故编写新的一个函数showfragment,展示fragment界面
1 | |
而在切换界面时,需要对原先的界面进行隐藏之后再展示所需界面,故编写一个新的函数fragmentHide,将所有的fragment界面都隐藏
1 | |
仅对底部选择栏的四个控件进行监听,并根据监听所得到的结果调用fragment界面
1 | |
注意这里设置了全局监听
因此要修改和覆写onClick函数
修改此处

覆写onClick函数
1 | |
而在最开始的界面自然就是聊天界面,故在最开始的时候就调用聊天的fragment
1 | |
由于点击聊天要实现列表功能,固还需要在 ltfragment 里面实现 recycleview 功能
我们先初始化定义一些变量recyclerView,list,context, myadapter(一个适配器)
1 | |
然后我们开始在onCreateView()函数底下写入适配器需要的一些参数和数据:
1 | |
分析代码
初始化列表内容
1 | |
创建一个LinearLayoutManager对象,并将其赋值给manager变量。然后通过调用setOrientation(LinearLayoutManager.VERTICAL)方法,将布局方向设置为垂直方向。
1 | |
myadapter = new Myadapter(context, list); 创建一个名为myadapter的自定义适配器对象,并传入context和list作为参数进行初始化。
recyclerView.setAdapter(myadapter); 将创建的适配器对象myadapter设置给recyclerView,用于显示数据。recyclerView.setLayoutManager(manager); 将之前创建的布局管理器manager设置给recyclerView,用于控制列表的布局方式。
return view; 返回包含recyclerView的视图对象。
1 | |
1 | |
1 | |




Kylinxin/MyWork: 类微信界面源代码 (github.com)
这是我第一次利用as进行移动开发实现了一个简单的类微信的界面设计,加强了我对as的fragment、基本layout、recycleview的认知,以及对xml文件进行界面编写部分以及对相关的控件有了更深入的了解,能够设计基础UI界面,实现界面跳转功能以及在fragment里面调用recycleview实现列表功能,给我提供了一定的思路进行功能和界面相互连接的代码的编写。在这次的实验下我也对AS这款软件进行了熟悉,对于其提词器的强大有了很深的印象。
——2023.10.13