二进制作业第二周
RE
droids0(pico2019):
这是一个安卓逆向的入门题(根本没有逆向,老样子在帮你部署环境),根据hint去安装了Android Studio
资料:
https://blog.csdn.net/weixin_43734793/article/details/124966390
https://blog.csdn.net/andylao62/article/details/23456881
安装的一点点事情:
1.电脑要有JDK5或之后的版本
2.安装后的初始化出现网络问题,即无法访问插件网址,这里因为电脑有某lash,进行了手动的代理设置
正在疯狂解压↑
感觉和Jetbrain家风格好像(难道是一家的?)
怎么好多东西都是第一次使用要在线下载
现在就直接运行一遍
根据提示了解到flag会在log中输出,直接运行就行:
flag: picoCTF{a.moose.once.bit.my.sister}
虽然不是很懂,特别是连java都不会,但是好歹环境没问题
正解到这里就结束了
现在记录一点折腾的过程
听说java中间会解释出某种叫做字节码的东西
假装看懂↑
不管如何,我们凭借着c++(学姬算鸡)的底子,了解到怎么也得是先找到main函数入口:
然后虽然看不懂字节码,但是硬看…(主要是现在还没有去搞java逆向的工具…待会儿折腾…):
你发现了吗…再往下看,凭借我小学800词的英语底子我看到了这个:
看来是某个类方法,但是属实是不会看这字节码,也不了解java apk的变量结构(那就学):
经过学习某个叫做smail的东西,我们可以知道是调用了某个静态函数(不知道是全局函数还是静态成员方法—java有全局函数吗?),那么进一步分析语法去找参数吧
这东西为啥是空的…到此为止…字节码分析失败,以后还是得去看看java逆向,光有字节码不行
盲猜v0寄存器里存的就是flag的主体,跪求哪位椰叶如果知道这个做法有没有搞头,评论踢我一脚…
到此为止吧,至少运行跑出来了…
Check_Your_Luck
很显然是求解线性方程组…但是我手头没有代码(线代老师饶了我吧QWQ)
我们使用正则(我太懒了):
1 | patt: |
处理后,丢入到在线计算线性方程组中(因为我没有写过求解线性方程组的代码—回头补上):
现在直接组装起flag即可…或者运行一遍…
flag{4544_123_677_1754_777}注意要换成NSSCTF{4544_123_677_1754_777}
正则真是个好东西啊,怎么各位做题都不用捏?(还是说椰叶们用了但WP里没写)
结束…
help
代码都一看就懂,会生成一个map地图,动调提取出来,然后肉眼写吧(路径是wasd四个方向也一眼就看出来了)
处理脚本:
1 | //BFS实现 |
然后就手动对着16x16的地图走一遍吧,懒得再整代码之类的了(太麻烦了吧)—更新:代码已整(见结尾部分↓)
整理出来某段该死的路径(眼睛快瞎了):
wwdddwwwaaawwwwwwwwwddddssssdddssdsssssssdddwwwwddsssd
在线md5出来:a8622109e2fb1296e06d5eed6f78f954
记得外面是NSSCTF{} nnd试了半天才知道是这个格式才对,就不能说清楚
flag: NSSCTF{a8622109e2fb1296e06d5eed6f78f954}
更新:走迷宫代码:
1 |
|
结束…
easyapp
附件还挺花…没有zip后缀…
加上然后解压出来个apk…继续整吧,不会java是真的麻烦…
又是smail…看来得学学这玩意…
第一部分—encoder类
总之就是分析代码…首先发现main中对输入的字符串进行了encode方法的调用,也就是所我们先要去查看encoder类:
这里有个key的field(别问我这是什么…我没学过java…貌似是某种数据段)
而且赋初值为0x75bcd15,继续分析发现后续代码有一个逻辑上的映射变换
这里是一个循环↑分析:
53行:循环边界,判断v1是否大于0,是的话跳出
55行:将p1的第v2个字符赋值给v3
58行:获取key(p0是Encoder类)给v4
60行:v3和v4异或(xor)
61行:字面意思
65行:append到字符串(v0?)
67行:v2自增,准备进行下一次循环
也就是说这里的字符串对key进行了异或映射
第二部分—main继续分析
为什么有两个MainActivity.smail…
所以这里对key进行了重新赋值…我实在看不懂这东西的运行顺序…看WP看的这么个意思…
总之就是要把p2和这个key进行异或运算然后就能出flag,p2就是这个(这个一看就懂):
那么我们另外写脚本吧,就用python吧…
1 | p2 = "\u68ff\u68e2\u68e2\u68f2\u68e5\u68f7\u68ca\u68d0\u68c1\u68da\u68e8\u68e8\u68f5\u68e2\u68cc" |
这里因为对py不熟悉所以还是看了题解QWQ
NSSCTF{apkYYDS}
结束…(相当于把大佬的WP抄了一遍…)
asm1
应该没有比我更闲的人了…这东西我还逆向出伪代码…
1 | asm1: |
接下来伪代码分析:
1 | short &k = [ebp+0x8] //写了个引用代表这个内存了哈哈哈 |
貌似是动态flag,我这里是0x6fa,所以结果就是0x6fa-0x12==0x6e8
结束…
asm2
也是看代码,这个题就是一个循环:
写解密代码:
1 |
|
运行:
结束…
asm3
这道题试试用c调用汇编函数,首先在win下编译失败…
ubuntu下编译环境的搭建
然后转到ubuntu,在编译时报了一个错:
百度后了解到是在x86_64直接编译32位程序报错,需要安装multilib库:
真就是配环境配了半天…又得更新包索引…
代码修改
运行结果
有一说一,没懂明白全为0的寄存器如何运行sub指令,不知道是我看的不对还是咋的
代码就是多了几个AL,AH寄存器而已(但是我不会算…字节位置有点没弄明白)
x86平台(即32位平台)的EAX占32位(4字节),EAX的低16位是AX,AX的低8位是AL,AX的高8位是AH
但是算起来内存没弄明白…
暂时结束…
reverse_cipher
这题简单,直接拖IDA看,逆向分析逻辑就行,就是最后那个}字符不知道咋的IDA逆向出v11=v5…v5前面都没有初始化…不过不影响…手动putchar(str[23]);就行
直接在IDA逆向出的代码中加注释:
1 | int __cdecl main(int argc, const char **argv, const char **envp) { |
写解密脚本(c/c++代码叫脚本?):
1 |
|
运行结果:
结束…
PWN
ciscn_2019_n_8
先看看是啥文件—32位的,用IDA32打开
尝试IDA动态调试,莫名其妙不能动弹?难道是开了保护?(开保护是不能调试吗?)
额,nb…火力全开…
还是静态分析吧…
这里其实最没弄明白的其实是那个LL,不确定val作为数组每个元素应该填充多长…但是这题是<二进制>赛项…
所以理论上这里应该是重点要关注的地方…
那么尽管IDA反编译出来的伪代码有点玄学…但是发现有一个重要的指针类型转换的步骤:
1 | if ( *(_QWORD *)&var[13] == 17LL ) |
QWORD
这个东西是4字节…众所周知,一个字是2字节,那么QWORD是4个字,也就是8字节…对上了,就是LL(long long)
那么就往进塞数据呗(这里我仍然想吐槽python的类型转换)
用python写exp:
1 | from pwn import * |
运行结果(第二次的截图,省略了ls了…doge)
结束…
mrctf2020_easyoverflow
这题就是个字符串溢出
首先main中v5被赋值为一个字符串,我叫他fake_flag0,然后有一个对v4的gets()
再看下面判断调用了check,并传递了v5这个字符数组
参数和另一个字符串比较,去看看:
把这个字符串复制出来,我叫他fake_flag1
然后发现 main中v4有0x30个字节(48个)—(-0x40)-(-0x70)==0x30
所以把这段内存覆盖了就行,exp如下:
运行结果:
结束…
jarvisoj_level2
这道题最大的收获就是把x86的堆栈看了一遍,还有脑内调试
有关X86 32位汇编利用堆栈传递函数参数的过程:
https://blog.csdn.net/weixin_62320071/article/details/129475981
https://cloud.tencent.com/developer/article/2123636
https://zhuanlan.zhihu.com/p/290689333?ivk_sa=1024320u&utm_id=0
题解:
这道题是典型的栈溢出,IDA反编译后的代码非常简单,有system函数的地址,而且我们可以通过字符串查找找到存在/bin/bash这个字符串(可获取地址)
那么我们就直接让vulnerable_function返回的时候跳转到system函数去getshell就可以
具体是由于read可输入的长度大于缓冲区的长度,所以存在溢出,那么只需要合理构造payload即可进行getshell
(但是我做这题很吃力,x86汇编属实不会,主要是对函数栈帧掌握不清楚)
exp:
1 | from pwn import * |
勉强结束…
jarvisoj_level2_x64
这道题学习到的知识点
参阅:
https://zhuanlan.zhihu.com/p/502718676
https://stackoverflow.com/questions/23367624/intel-64-rsi-and-rdi-registers
https://www.cnblogs.com/nemuzuki/p/17218722.html
x86_ 64函数传参使用到的寄存器
重点是rdi,rsi,rdx,rcx这几个
分析题目
1.参数问题
这道题也是栈溢出,不过是x86_64的程序,所以我们重点注意的应该是去考虑x86_64和x86的堆栈(或者说函数调用时)有什么区别,而且重点要关注函数传参有什么不同
经过学习,了解到在x86中,各个参数依次从右向左入栈(CDECL?),然后再压栈eip和ebp;
而在x86_64中,如果子函数的参数数量<=6个,那么就会使用到6个特殊的寄存器,也就是上面的rdi,rsi,rdx,rcx,r8,r9这6个寄存器.然后如果还有参数,则像32位一样压栈.
那么也就是说,64位程序调用函数时,会先把参数从寄存器中pop出来,也就是pop rdi; ret指令
对应到本题中调用system()函数只需要传递一个字符串,也就是system执行的命令,所以只需要多考虑一个rdi(第一个入参对应的寄存器)的问题
2.题目分析
分析好关键后,接下来开始审题
这次做题思路比较清晰,而且看了看雪上的某篇IDA使用教程,这回在IDAview视图中认真分析了一下汇编,后面记录一下(虽然和这道题的题解关系不大,但是有助于理解x86_64函数传参)
题目和x86版本的基本一致,仍然是标准的栈溢出:buf数组长度为0x80,然而read()可以输入0x200,满足溢出
同样,去找system()的地址和/bin/bash的地址
shift+F12打开字符串,可以看到存在/bin/bash—0x600A90
发现有_system函数(注意不是extern的那个system)—0x4004C0
接下来就是区别,需要找到pop rdi; ret指令的地址,以便调用
这里需要使用到Linux下的ROPgadget工具查找(第一次用这个,看了一下使用方法)
找到地址为0x4006b3
3.payload
覆盖模板: [函数局部变量(buf数组)]+[rbp]+[pop rdi; ret指令的地址]+[’/bin/bash’的地址]+[system()函数的地址(实际上是那个_system())]
python代码:
1 | from pwn import * |
运行结果:
题解到此结束…
题目IDAview分析记录
main()汇编分析
main中有两个变量var_10和var_4
首先是调用main时,开头的push rbp;mov rbp,rsp指令创建函数栈帧,然后sub rsp,10h给局部变量(形参?)分配空间(10h应该是16字节给两个变量,一个int argc和一个char *argv[]的数组—数组形参实际上是一个指针,所以也是8字节)
然后接下来的两个mov就能看出来x86_64的传参了,第一个是edi,第二个是rsi,逐个从寄存器中复制到内存
然后eax置0
此时该调用函数了,因为vulnerable_function没有形参,所以没有对edi这些寄存器进行处理,直接调用
vulnerable_function()汇编分析
同样在开头push rbp;mov rbp,rsp创建函数栈帧
接下来是对rsp进行add,但是这里我不理解为什么是add而不是sub(),总之就是对buf数组进行处理分配
下一行就是为_system函数的调用做准备,处理实参:
mov edi, offset command ; "echo Input:"中,offset表示获取一个标号,command标号代表的内存中存有这个字符串,将其赋值给edi用于_system的第一个参数(只有这一个参数)
然后调用_system()
返回来后,继续为_read()的调用做准备:
先放上来_read()的描述(注意参数的位置):
其实这个函数就是unistd.h(UNIX std)中声明的read()函数,用于文件读写
第一个参数是文件描述符(0代表标准输入stdin,1代表标准输出stdout,2表示标准错误输出stderr)
第二个参数是输入的目标缓冲区
第三个参数是指定要读取的最大字节数
ssize_t read(int fd, void *buf, size_t nbytes)
lea指令全称是 “Load Effective Address”,意为加载有效地址,就是将指定的内存地址计算出来并存放在目标寄存器中.这里这条指令将 rbp+buf
地址的计算结果保存在 rax
中,准备赋值给rsi
将200h赋值给edx作为第3个参数(nbytes)
将刚刚计算出来的eax赋值给rsi作为第二个参数(buf)
将0h赋值给edi作为第一个参数(fd)
然后调用_read(),这道题的切入点就是这里
后面的部分没看…留作以后…
结束…
ciscn_2019_ne_5
这道题就是绕了一下,利用strcat()进行了栈溢出,同时不同的步骤需要从不同的函数进行处理利用,要进行顺序分析
分析如下:
代码首先需要输入正确的密码,然后下面是一个switch的循环(注意反编译显示循环使用一个跳转函数实现)
Addlog中允许输入0x80(128)长度的src
print中有_system()函数可以利用
getflag中有strcpy,同时局部变量(反编译问题)共有0x48,0x80>0x48,满足溢出
ROPgadget工具查找’sh’字符串的地址(此题没有/bin/sh)
先走1选项,输入足够长的字符串准备溢出,然后走4选项进行strcpy溢出,跳转至system()即可
payload:
1 | from pwn import * |
运行结果:
这题看了半天需要填充多少字节,唉,看错了
结束…