re-part-6
历经千辛万苦,终于到难度6啦。
easy_Maze
64位ELF。
第一个红框生成迷宫,第二个红框开始走迷宫。跟踪Step_2
,得到:
动态调试得到迷宫:
1 | 1001111 |
从左上角走到右下角,最后flag为UNCTF{ssddwdwdddssaasasaaassddddwdds}
。
ey-or
题目说此文件包含一个标志,并提取。tar.gz
文件。文件比较大,23MB。
任务1:找到print函数是什么?
1 | 调用链: |
输入存在哪儿:
1 | # flag{afffffffffffffffffffffffffffffffff} |
调试了一天半,没做出来,仍需努力。(感觉还是不够耐心,看到代码太多太复杂就没有勇气看了)
0x01 wp复现
佬们说这是Elymas语言。说实话,这个,就算我再看一天,应该也看不出来。(搜索资料的能力太差了。)
其他师傅首先看文件字符串,看到有:
1 | ] == f |
1 | Groovy 编程语言编写的程序,用于读取用户输入的密码并进行加密解密操作。 |
反正我没根据上述程序搜到Elymas语言,直接给chatGPT,它的解释如上所示。整理一下程序思路:
1 | i = 0 |
那么我们就可以:输入一个字符,如果程序结束,则说明输入的字符不对,如果程序继续运行,那么程序的输入就是正确的。
下面是使用subprocess
操控程序的脚本,自己写的。
1 | import time |
总结一下:不要头铁,多找找题目信息。一股脑调试2天,自己真傻逼呀。
libdroid
apk文件,用jeb打开,审计字节码。
发现:
但是getFlag
是在本地实现的,其声明如下:
1 | public static native String getFlag() {} |
于是猜测是不是在apk自带的库中,发现:
这不是flag
,返回的值为v2
函数处理后的,v2
是由v1
得来的,v1
是此函数的参数,并不清楚此函数参数的传值。所以打算使用mumu+jeb3动态调试此程序。
结果此apk不支持调试,所以打算加上调试选项(重签名等操作),最后得到:
对此apk进行动态调试,如下所示:
其中,v7
与v4
分别为:
1 | v7 = ['FE', 'A0', 'AD', '80', '20', '59', 'AB', '12', 'D7', 'C3', '9C', '88', 'FA', '2C', '1D', 'FC', '81', '46', '0D', 'DC', 'E9', 'CE', 'CC', '57', '78', 'F5', '41', '5F', '52', '2', '36', 'D5', '33', '18', '66', '3A', '40', '26', 'E8', '6E', 'B6', 'CD', '72', 'B7', '3C', '1', '66', 'B1', '4F', '99', '23', '63', '95', '77', '34', '61', '69', 'F6', 'A9', '53', '40', '37', '41', '43', '4F', '98', '95', '2C', '7A', '27', '3C', '98', '68', '1A', '88', 'A8', 'B7', '85', 'BB', '15', '4F', '1A', '1', '4D', 'C9', 'C8', '9B', '75', '78', '57', '7F', '98', '0D', 'D8', '51', 'A8', '22', 'B9', '5E', '59', '4D', '71', '4F', '1A', '81', 'A9', 'BF', '7', '29', 'ED', 'FD', '83'] |
其中,phoneHome
函数如下所示:
这是一个静态函数,保存在liblibdroid.so
中,里面第18行、19行、20行调用的别的函数,是静态链接到bytecode
上的。
下面需要调研一下静态链接是怎么连接到apk文件中的。
没咋调研出来,准备直接猜上面函数的各种操作,如下所示:
1 | from tqdm import tqdm |
上述脚本一直跑不出来,看到其他人的wp所示:https://blog.csdn.net/qq_41071646/article/details/90232128
思路很正确,感觉是我脚本写的有问题,明天再看看。改完之后的,脚本有点小问题:
1 | from tqdm import tqdm |
leaked-license-64(未做出)
解压后,有两个文件:admin@nsa.gov.us_license
、Leaked_Lisence.dll
。admin@nsa.gov.us_license
的值为7e43ecf0b4e27dacfb5e613437b17acb46e8deab2c70510dc71844b492a691ec
,Leaked_Lisence.dll
是一个32位dll。
思路:将7e43ecf0b4e27dacfb5e613437b17acb46e8deab2c70510dc71844b492a691ec
输入到某个函数(dll中存储)中,出flag
。其中有一个verify
函数,是Leaked_Lisence.dll
唯一的导出函数。
感觉这个题,我的思路就是一点一点分析,或者就是直接调用dll函数。
1 | arg_0 char* 长度为16 |
写了个程序调试此dll,如下:
1 |
|
上述脚本一直在控制台上输出奇怪的字符。
此问题就是动态加载dll,每次调试都要对dll中的verify函数重新组织成函数,所以想静态调试。
使用pexports.exe将dll变为lib(但是函数的可执行代码并没有放到lib中),之后修改上述脚本:
1 |
|
此脚本一直报堆栈错误,说调用的genlisence-dll.dll
有bug(其实是没有bug的,只是静态链接的原因而已)。最后脚本如下:
1 |
|
最后始终没有静态链接,就这么调试吧,唉。
折磨,直接看wp吧。好多代码,看着心烦。
0x01 wp
全是转的外网的博客,外网的博客现在也没了。中文的有:https://www.cnblogs.com/DirWang/p/11469346.html
思路就是构造一个消息结构,之后dll调用。感觉之后还得仔细调试。不能太浮躁!!
思绪良久,打算总结一下这道题的答案。dll中verify函数的具体参数为两个magic数据结构,magic数据结构如下所示:
1 | struct magic { |
算法本身的逻辑如下:
1 | def verify(ID, licence): |
下一道下一道!!
pingpong
apk文件。程序逻辑如下:
1 | def main: |
我们想要tt=0
,这样的话就要先ping,再pong,循环往复1000000/2
次,才可以。
两个函数ping
、pong
保存在pp库中:
pp
库使用了OLLVM保护。如下所示:
原来的那个脚本失效了。所以得手撕OLLVM。
0x00 手撕OLLVM的尝试
1 | pong 分析 |
1 | ping 分析 |
1 | ping |
R0 | R1 | R2 | R3 | R4 | R5 | R6 | R7 | var_1C | var_2C+4 | var_2C | var_20 | var_24 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1<<1e | 0x12ABCDA2 | 0 | num | 3 | num | 1 | p | 1<<1e | 0 | 0 | 1<<1e | ||
0xC40FA35D | |||||||||||||
1 | 1/0 | 0xD175216E | 0x9EE75616 | 0x3CD203AF | |||||||||
2 | 0xE5925FB | 0xF133E736/0xE5925FB | var_18 | ||||||||||
0x65982633 | |||||||||||||
0x72FA0C29 | 1 | num | |||||||||||
0xE5D890A5 | |||||||||||||
3 | 0x18C23607 | 0x8B06AA34/0x18C23607 | |||||||||||
0xA | 0xE5D890A5 | 0xA*1 | num-1 | ||||||||||
… | |||||||||||||
0xA | 0xE5D890A5 | 0xA**num | 0 | ||||||||||
4 | 0x18C23607 | 0x8B06AA34/0x18C23607 | |||||||||||
5(var_1C%2=1) | 1 | 0xF4B872E4/0xF10822ED | 0xFFFFFFF0^var_1C | var_1C | num | 1 | |||||||
6(var_1C%3!=0) | 0/1 | 0xCDB35A57/0xC5866837 | 0xC5866837 | ||||||||||
end | [var_2C*(var_2C+4)]%0xA+var_1C | 0xA | 0xA**num | 0xC5866837 | |||||||||
6(var_1C%3=0) | 0/1 | 0xCDB35A57/0xC5866837 | 0xC5866837 | ||||||||||
1+var_1C | 0xC5866837 | p+1 | |||||||||||
end | [var_2C*(var_2C+4)]%0xA+var_1C | 0xA | 0xA**num | 0xC5866837 | |||||||||
5(var_1C%2=0) | 1 | 0xF4B872E4/0xF10822ED | 0xFFFFFFF0^var_1C | var_1C | num | ||||||||
var_1C+1 | 0xF10822ED | p+1 | |||||||||||
2 | 0xE5925FB | 0xF133E736/0xE5925FB | var_18 | ||||||||||
-1 | 0x12ABCDA2 | var_24 | 0 | var_20*var_24 | 5 | 2 | var_2C+4 | var_2C+(var_2C+4) | var_24 | 0 |
1 | label_B: |
1 | var_1C = p |
撕不出来啊,看wp。
0x01 WP
其实不难看出,只要用模拟执行,模拟出ping/pong循环往复的过程,就可以。
wp中使用了frida进行模拟执行。
远古时候曾经用过frida:https://wd-2711.tech/2022/11/15/android-app-100-wp/
1 | import frida, sys |
上述脚本一直找不到libpp.so
,搜索发现是模拟器一般是x86 或者x86-64架构,只会加载对应lib/x86 和lib/x86-64下面的so文件,真机才会加载lib/arm*的so文件,所以想让app加载arm 可以真机,或者arm架构的模拟器
。也就是说,frida只会搜索x86下的文件夹,而armabi下的文件夹无法搜索。所以,我首先把armabi下的libpp.so复制到x86文件夹下,发现不行。
然后,我并没有找到arm架构的模拟器。所以就用了自己的本机,但是本机没有root,所以运行不了frida-server
,root风险太高,所以就打算用frida-gadget(非root)的方法,但是最后还是不能正常调试,显示Failed to attach: unable to connect to remote frida-server: closed
。
就这样吧,下一道,wp为BCTF{MagicNum4500009}
。
childRE
没有pdb文件的PE64。
动调了一下,发现可能有反调试。验证了一下没有hh,只是调试器加参数的时候加错了。
程序逻辑如下:
1 | 1. 输入长度为31的字符串 |
容易得到outputstring=private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
,使用newbing,可以得到其原始值为?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
,之后只要构成满二叉树,按层遍历即可。满二叉树如下所示:
相应脚本如下:
1 | #!/usr/bin/env python |
MyDriver2-397
发现是一个sys驱动文件。通常用于存储设备驱动程序。sys文件不能直接运行,需要通过注册表配置和加载驱动程序的命令来安装和启动。
尝试动调一下此程序。
0x00 环境部署
首先部署windbg双机调试环境,见tool-use。
其次,安装驱动文件sys,见tool-use,具体而言:关闭win7的强制签名bcdedit /set testsigning on
,之后使用OsrLoader安装驱动。
之后,打开win7虚拟机进入调试模式,之后立即打开windbg,命令行为:
1 | windbg.exe -b -k com:pipe,port=\\.\pipe\com_1,resets=0 |
然后在sys文件上打断点,如bp MyDriver2+0xxx
。运行windbg进入桌面,之后使用OsrLoader打开驱动。
0x01 苦逼的调试过程
参考链接:https://estongsy.wordpress.com/2012/07/17/debugging-a-driver-sys-file-using-windbg/
1 | windbg.exe -b -k com:pipe,port=\\.\pipe\com_1,resets=0 |
1 | nt!NtCreateFile: |
1 | v5 = 0xfffffa80`1a282150 |
1 | bp MyDriver2+0x11F6 // v9 <- v5 <- 修改前的CreateFile |
1 | v9 = fffffa80`1ac088b0 |
1 | bp MyDriver2+0x12AC // return |
1 | nt!NtCreateFile: |
这个call qword ptr [rax+2444C790h]
很奇怪啊,难不成调用nt!NtCreateFile
的时候rax
都是固定的?
1 | bp fffff800`041b107c // 在nt!NtCreateFile上打断点 |
调试后发现完全不一样啦。
发现第一行jmp到了sub_114D0函数。如下所示:
1 | bp MyDriver2+0x1552 // 调用wcsstr的时候 |
发现wcsstr(*(*(a3 + 16) + 8i64)
指向\??\MountPointManager
。SubStr
指向P_giveMe_flag_233.txt
。那我想,运行此驱动,然后立即打开P_giveMe_flag_233.txt
,不就得了。但是先别停,这样不稳,继续调下去才稳。
1 | r rax=1 |
查看此时r12对应的值,如下所示:
写入文件的值为:the flag is A_simple_Inline_hook_Drv
。Flag为RCTF{A_simple_Inline_hook_Drv}
。
0x02 题目总结
题目逻辑如下:钩取CreateFile
函数,如果调用此函数的文件是P_giveMe_flag_233.txt
,那么就把flag写入到文件中,否则直接使系统崩溃。
Junk_Instruction
西湖论剑预选赛的题目,既然是预选赛,看来应该很简单。看来re还是任重道远。但是庆幸的是,今天终于可以re啦!
是一个32位的GUI程序,输入字符串并且判断是否正确,不正确输出error。没有符号表。有很多的垃圾指令。
0x00 探索
猜测是MFC程序。查看之前做过的题,链接为:https://wd-2711.tech/2022/11/23/re-part-4/
。其中的MFC逆向-200,还有就是:https://wd-2711.tech/2022/12/04/mfc-study/
。
使用xspy,程序输出:
1 | message map=0x004BF724(Junk_Instruction.exe+0x17f724) |
补充:xspy 输出的 OnMsg 与 OnCommand 有一些区别。OnMsg 表示程序收到了一个普通的消息,例如 WM_PAINT、WM_LBUTTONDOWN 等,这些消息通常是由系统或用户的操作产生的。OnCommand 表示程序收到了一个命令消息,例如 ID_FILE_OPEN、ID_EDIT_COPY 等,这些消息通常是由菜单或工具栏上的按钮产生的。
所以,猜测OnCommand
是面板中的check
按钮。定位到0x400000+0x002420=0x402420
。发现是一个SEH(异常处理函数)。
相当于:
而:
相当于:
还有把call $+5
改为push 下一条指令地址
。
最后,可以写脚本:
1 | #!/usr/bin/env python |
结果:
patch后的程序如下:https://github.com/WD-2711/re_files/tree/main/%E6%94%BB%E9%98%B2%E4%B8%96%E7%95%8C/Junk_Instruction
icekey
64位程序。.NET
是由微软开发的一个软件框架,用于开发和运行各种类型的应用程序,包括桌面应用程序、Web应用程序、移动应用程序、服务端应用程序等。.NET
框架提供了一个通用的基础设施,让开发人员可以使用多种编程语言(如C#
、VB.NET
、F#
等)来编写应用程序,并利用.NET
框架提供的库和工具来进行开发、测试、调试和部署。
ILSpy是.net
反编译工具。dnSpy也是一个.net
反编译工具。找到一个main函数:
1 | internal unsafe static int main() |
可以看到,程序中还提供了解密字段。那么很简单,我们只需要动态调试,并在if(0<num)
时将array字段改为3ACF8D62AAA0B630C4AF43AF327CE129D46F0FEB98D9040F713BE65502A5107A
即可,最终得到:5acb06231724c8c369bae711166dbe85
。flag为flag{5acb06231724c8c369bae711166dbe85}
。
xx
64位程序。程序逻辑挺清晰的。输入字符串,进行某些操作,与另一些字符串作比较。输入长度为19。
其中使用了tea加密(find_crypt与newbing都这么说。),但是仔细瞅了瞅,应该使用了XXTEA(主要是看移位)。
0x00 某些逻辑
其主要操作如下:
1 | v20[3]=v20[0]^v20[3] |
其逆如下:
1 | v20[23]=v20[0]^v20[1]^v20[2]^v20[3]^v20[4]^v20[5]^v20[6]^v20[23] |
其逆如下:
1 | v19[21]=v20[23] |
对于XXTEA,其加密数据为:输入(19字节)+0x0013000000
(注意顺序),密钥为:输入的前4位(4个字节)+0x00*12
。delta为默认值,即0x9e3779b9
。
最终脚本如下:
1 | #!/usr/bin/env python |
1 |
|
easyvm
64位的ELF,看题目名称是个虚拟机。运行如下:
打开后要求输入32位的flag。
重点是红框部分,其中应该有关于验证的具体逻辑。其中,sub_400c1e
函数如下:
它是对上上图第一个红框中的v3
进行值的分配。off_4010a8
是好多函数的偏移。经过分析,上上图第2个红框是sub_400806
函数,很明显是一个虚拟机switch ... case ...
函数。
分析之后,总结其流程如下(伪代码):
1 | *(_QWORD *)(a1 + 24) = arr2 |
经过究极简化,例如:
1 | *(_QWORD *)(a1 + 24) ----> arr2 |
最终可以得到:
1 | i = 0 |
可以写脚本:
1 | #!/usr/bin/env python |
seven
是sys驱动文件!
留言
- 文章链接: https://wd-2711.tech/
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明出处!