【2022春节红包】全部题目WriteUp

查看 105|回复 9
作者:JemmyloveJenny   
Windows初级题
前期准备
先运行一下随便输入一点东西,看看错误提示


2_WrongInput.png (25.59 KB, 下载次数: 0)
下载附件
2022-2-16 20:14 上传

输出是 Error, please try again
用 DetectItEasy 查了一下,是32位程序无壳的,可以直接开始逆。


2_DIE.png (97.22 KB, 下载次数: 0)
下载附件
2022-2-16 20:15 上传

静态分析
拖到IDA里分析一下,主要逻辑都在_main函数里


2_IDA_1.png (147.62 KB, 下载次数: 0)
下载附件
2022-2-16 20:16 上传

从能看到有个循环,循环条件还很怪,地址小于字符串HappyNewYear的地址,跟过去看一下


2_IDA_2.png (186.91 KB, 下载次数: 0)
下载附件
2022-2-16 20:16 上传

给了一个提示,flag的长度是00010111,转成10进制就是23
在前面也能看到if语句判断了v21 != 23
运行程序输入一串长度为23的字符,可以看到输出文字有变化,更加确定了这一点
再来看一眼这个奇怪的循环,v3是一个指针,最开始指向unk_4140B0


2_IDA_3.png (163.11 KB, 下载次数: 0)
下载附件
2022-2-16 20:17 上传

跟过去发现,这个东西在字符串HappyNewYear的前面,而且四个字节一组的样子,数数总共是23个,可以猜测这边是经过加密的flag
由于解密函数不那么简单,也就不再继续静态分析了,上调试器跟着看看
动态分析
我用的是 x96dbg (x32dbg) 用 Ollydbg 或者 IDA直接调试 也一样吧,没啥区别
用x32dbg要注意一点,它停下来的EntryPoint并不是_main函数,而是_start函数


2_dbg_1.png (349.82 KB, 下载次数: 0)
下载附件
2022-2-16 20:17 上传

要在这个地方下个断点,再F7单步跟过去
到了入口点,看一下0x004012F6到0x00401317这一段 便是那个循环了,直接在之后下个断点让他跑完


2_dbg_2.png (350.56 KB, 下载次数: 0)
下载附件
2022-2-16 20:18 上传

果然就出来一个字符串2022HappyNewYear52PoJie,长度刚好是23
用程序验证一下


2_Success.png (26.96 KB, 下载次数: 0)
下载附件
2022-2-16 20:18 上传

成功~这就完事了
Windows中级题
剧透一下
这道题和密码学有关哦~
仿射密码 还有 计算模反元素
在CTF中做了不少密码题,在这里看到我好激动啊啊啊啊!
前期准备
还是运行一下看看,这个是要输入UID和flag的,所以每个人flag还会不一样哦,我就不截图了
一看这个文件大小就不太对劲,根初级题比有点小了,查个壳看看


3_DIE.png (97.27 KB, 下载次数: 0)
下载附件
2022-2-16 20:18 上传

果不其然,是被UPX压缩过了,这个我不知道能不能用工具直接脱
看着是UPX [modified]大概不行吧,我就用ESP定律了
ESP定律脱壳
入口点经典pushad单步一下看到只有ESP寄存器是红色的


3_ESP_1.png (558.8 KB, 下载次数: 0)
下载附件
2022-2-16 20:19 上传

对ESP寄存器 右键-在内存窗口中转到,接着在内存窗口中对这个地址 右键-断点-硬件,访问 几字节都无所谓
接着F9运行,等着popad触发硬件断点


3_ESP_2.png (506.09 KB, 下载次数: 0)
下载附件
2022-2-16 20:19 上传

把硬件断点取消,然后在下面的jmp大跳转上下个断点
断下来之后F7单步,来到OEP
接下来就是要用Scylla插件了,从顶上的插件菜单打开Scylla


3_ESP_3_.png (570.44 KB, 下载次数: 0)
下载附件
2022-2-16 20:36 上传

不解释了,按照图里说的做吧
最后会获得一个xxxxx_dump_SCY.exe这个就是脱壳之后的程序了
静态分析
关于C++的string
这道题使用C++写的
不得不说 我好讨厌C++ 源代码看着都费劲,逆向分析更恶心
C++的源代码中经常会用std::string这个类型,但是逆向的时候好像IDA分析不出来
我没找到std::string的正确结构,在这道题里,我根据代码反推了一个它的结构,也不一定对[捂脸],如果有大佬知道请一定告诉我
struct string
{
    char field_0;
    char field_1[3];
    char *buf;
    int size;
};
可以先添加上这个结构以简化分析流程
看数据流向
会变化的只有 我们输入的UID和key
我们紧盯着它们啥时候被使用就行了


3_IDA_1.png (347.15 KB, 下载次数: 0)
下载附件
2022-2-16 20:22 上传

那个sub_402460其实就是std::string的构造函数,或者是字符串复制函数,并不重要……但这东西耗了我好长时间(从中反推出string的结构)
前几个调用的函数都很简单,简单分析就可以知道,分别返回uid %25,map[uid % 12],还有一个通过map[uid % 12]算出来的值,这个值之后再说(最开始我也没看出来)
看一下最终判断函数,IDA最开始反编译出来的不是这个样子,参数个数比现在多,这边是我修复了std::string这个类型之后才分析出来的
前4个参数实际是结构体的各个部分,合起来就是一个std::string
所以需要手动修改一下函数的定义
再来看参数:
v18其实就是复制后的key
v19的话,伪代码里看不清,但实际上就是v21(其实很明显,v21没被用过,但不应该是多余的嘛)这个v21其实很神奇,是乘法逆元,不知道也没关系
v20来源于v6,也就是uid % 25
现在我们就知道了,key是否正确,是和uid % 25,uid % 12有关的,我们根据最终判断函数来分析一下
分析判断逻辑
主要判断逻辑是在sub_401520里面了,其实逻辑很简单,但是被C++这么一弄就很恶心
再次感到修复类型十分重要,这个函数里用了好多std::string,分清楚就简单了


3_IDA_2.png (329.34 KB, 下载次数: 0)
下载附件
2022-2-16 20:22 上传

变量的定义大概是这样吧
这个函数前面一大串相似的函数调用,它的作用就是在初始化一大串字符串,后面的循环,便是将他们全部拼接到flag后面得到正确的flag内容,也就是flag{Happy_New_Year_52Pojie_2022}
真正的重点在于后面


3_IDA_3.png (350.22 KB, 下载次数: 0)
下载附件
2022-2-16 20:22 上传

我们可以直接在字符串比较函数上下个断点,看看是在比较什么东西,动态调试看一下
动态调试
我输入了自己的UID也就是520012,key随便写了个HappyNewYear
在调用字符串比较函数sub_403ED0的地方0x00401CCE下个断点


3_dbg_1.png (610.03 KB, 下载次数: 0)
下载附件
2022-2-16 20:23 上传

这就能看到比较的两个字符串了,根据调用约定,ecx寄存器里的是第一个参数,栈上的是第二个
对ecx 右键-在内存窗口中转到 就能找到std::string的结构了,根据定义可以看到char* buf指向的地址是0x871199(x86架构 Little Endian顺序读取)


3_dbg_2.png (60.47 KB, 下载次数: 0)
下载附件
2022-2-16 20:23 上传

我们跟过去就可以看到字符串内容是LqjjkDceKcqp,那么久可以认为这是HappyNewYear经过变换后的结果了
第二个参数如法炮制,它的内容就是前面拼接出来的flag{Happy_New_Year_52Pojie_2022}
分析变换方式
我们再仔细捋一捋哦HappyNewYear变成了LqjjkDceKcqp
注意观察,这是一种替换,比如a -> q,yY -> kK,能看出来是替换这一点就行了
更进一步的话,再联系一下,前面求出来2个参数对吧,uid % 25和 通过map[uid % 12]算出来的值
我在CTF里做了不少密码题,一下就想到这是古典密码学的仿射密码了,想不出来也无所谓了
查表逆变换
假如没看出来是仿射密码,只知道是替换的话,这个嘛…也很简单的
输入key的时候直接输入abcdefghijklmnopqrstuvwxyz
继续在这个位置断下来,看看变换完是什么结果呗
abcdefghijklmnopqrstuvwxyz 变换前
qtwzcfiloruxadgjmpsvybehkn 变换后
然后倒着查呗,这多简单呢
仿射密码加密
什么是仿射密码呢?
有两个参数A和B,a-z分别编号0-25
然后加密字符c就是计算c*A + B mod 26再转换成字符
那么解密字符c就是计算(c - B)/A mod 26再转换成字符
这些运算都是在模26的意义下进行的,加减B都好办,那除以A如果不能整除怎么办呢?
这就要引入一个概念,乘法逆元
举个例子吧,3和9在模26下互为乘法逆元,这是什么意思呢?
3 * 9 = 27 = 1 (mod 26),同理还有5 * 21 = 105 = 1 (mod 26),7 * 15 = 105 = 1 (mod 26)等等,在有限域运算下,两者的乘积是1,也就是互为倒数,在有限域中就称作互为乘法逆元。
当我们乘上一个乘法逆元,那么就相当于在有限域中除以了原来的数字,比如:11乘以3就是11 * 3 = 33 = 7 (mod 26),再除以3,可以变为乘以乘法逆元,也就是9,7 * 9 = 63 = 11 (mod 26)又变回来了
我们再实战一下,比如说我的参数是A = 9, B = 12,9的乘法逆元是3
对字母'b'进行加密'b'*9 + 12 = 1*9 + 12 = 21 (mod 26) = 'v',加密完就是'v'
对字母'b'进行解密('b' - 12) / 9 = (1 - 12) * 3 = -33 = 19 (mod 26) = 't',解密完就是't'
我们再对照一下之前断点得到的码表,验证了是完全正确的~
还想再说一下,论坛这道题没有出错,真的很好~密码学这些知识用不好是会出错的
要知道,乘法逆元不是一定存在,只有与26互质的这euler_phi(26) = 12个数字才存在乘法逆元,这道题处理得很好,查了个表,还贴心地帮我们求好了乘法逆元(v21就是求了乘法逆元的结果)
编写注册机
理解了 仿射密码加密 的原理,这个就很简单了,我用C语言写一个
#include
#include
char const flag[] = "flag{Happy_New_Year_52Pojie_2022}";
int map[] = {1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25};
int main()
{
    int uid;
    while (scanf("%d", &uid) != EOF)
    {
        int A = map[uid % 12];
        int B = uid % 25;
        char buf[sizeof(flag)];
        strcpy(buf, flag);
        char *p = buf;
        while (*p)
        {
            if ('a'
输入自己的UID按回车就好了
Android中级题
分析Java代码
安卓题,想用什么用什么吧,GDA, JD-GUI, JADX 这些都可以啊
我是用了GDA看了一下,找到了验证输入的函数


4_GDA.png (113.84 KB, 下载次数: 0)
下载附件
2022-2-16 20:25 上传

核心在于MainActivity的checkSn函数
这是一个Native函数,也就是说在.so动态链接库里面
Java这边还提供了一个线索,flag长度是16
除此以外就没啥了,分析so去吧
定位checkSn函数位置
前期分析
用压缩软件打开apk,发现lib文件夹里只有一个lib52pojie.so文件,我们要分析的就是它了
这个是arm64-v8a架构的库,所以要用 IDA x64 打开
Native函数有两种定义方法,一种是按照类名函数名静态声明并导出,一种是在JNI_OnLoad里调用vm->GetEnv->RegisterNatives动态声明
看一眼它的导出表,发现并没有导出checkSn函数,那么就说明是JNI_OnLoad里动态注册的
那么就是要找到,通过RegisterNatives注册的checkSn函数是哪个呗
我原本还想用IDA静态分析出来的,可是一看那函数指针乱飞的伪代码就放弃了,这东西只能动态调试
我有个root过的Android手机,可以当作调试用的真机,调试比模拟器省事多了
把这个apk安装好,IDA的android_server64也传上去运行,准备调试~
最开始的想法是用IDA的动态调试功能,直接下断点追着分析
但这IDA真机调试好像只能用attach附加到已有进程上,不能直接启动一个app(也可能是我不会,有会的教教我)
我当时绕了不少弯路,整了JDB,DDMS这些乱七八糟的,用他们调试启动app,在JNI_OnLoad前就用IDA附加,好不容易弄完了,但是还有问题,IDA没能把文件里的地址和运行时地址对应起来,也就是说IDA不知道把断点下在哪个地址,根本断不下来,这都白费功夫
用Frida来Hook注册函数
既然动态调试也跟踪不了 那我就在你必然会调用的地方守着嘛~
想法就是,知道会调用RegisterNatives,那么就在那Hook一下传入的参数呗
在网上搜Android Hook相关内容的时候,就看到了有用Frida来Hook那个RegisterNatives的脚本
这个Frida我也早就听说过它的大名,这次就试试看怎么样
安装Frida的过程我就不说了,要有Python环境什么的
然后那个Hook的脚本在这里
,把脚本内容存到文件里,然后命令行输入frida -Uf com.wuaipojie.crackme2022 --no-pause -l script.js就能Hook到真实地址了


4_Frida.png (94.04 KB, 下载次数: 0)
下载附件
2022-2-16 20:24 上传

得到了这样一个结果,确实是通过RegisterNatives注册了checkSn函数,那个0x70c7f7ff74是运行时的地址,而不是IDA里的地址
Frida也是能获取运行时地址的
var base = Module.getBaseAddress("lib52pojie.so")
console.log(base)
这样就能输出lib52pojie.so加载的基址
减一下就知道,验证函数是sub_6F74,同时还可以利用基址计算其他函数的偏移量
动态调试
这个so文件基本上没办法直接静态分析,在汇编中能看到很多的BLR X8这种指令,也就是说调用X8寄存器中地址的这个函数,而X8的计算乱七八糟,IDA的F5直接报废
那么我们接下来要做的就是去除这些干扰,把代码变回正常的样子
我还是用Frida启动这个app,Frida可以获得到lib52pojie.so的基址
然后再用IDA附加到这个进程上,手动计算一下运行时地址下断点
每到一个BLR X8就可以在IDA的寄存器窗口中看到真正的地址,减掉lib52pojie.so的基址之后,就能获得调用的函数在文件中的偏移了,然后把BLR X8改成BL $(address)即可,顺便把计算X8所用到的指令改成nop
修改的话,我是给IDA装了一个插件叫做KeyPatch (KeyStone),修改效果大概如下:


4_IDA_1.png (184.86 KB, 下载次数: 0)
下载附件
2022-2-16 20:25 上传

修改完之后,应该就没啥难点了,IDA的F5又支棱起来了!!!
静态分析
之后就是一般的逆向过程,修复类型,修复参数,重命名函数等等
其中有个用到的字符串类型,可能是C++编译器弄出来的吧(C++真讨厌)
我大概逆向了一下他的结构,不一定对
struct basic_string
{
    int64_t field_0;
    int64_t length;
    int32_t field_10;
    char buf[];
};
修复完的代码大概如下


4_IDA_2.png (219.13 KB, 下载次数: 0)
下载附件
2022-2-16 20:25 上传

可以看到,最后的结果v5是字符串比较的结果,下断点看了一下,这些都是无意义的数据,猜测是一种加密算法的密文比较,之后也证实了,就是变形的sm4
这个cipher变量是怎么看出来的呢…?就是在sub_9C68中用同样的方法修复,可以发现使用了0x357C0那个地方的数据,跳过去一看是sm4的vtable


4_IDA_3.png (244.91 KB, 下载次数: 0)
下载附件
2022-2-16 20:25 上传

这很明显是C++的类的结构,那么sub_9E90,sub_9F50,sub_A048,sub_A080,sub_A4F0这些肯定都是sm4的类函数了
解密密文
用openssl解密(失败)
盲猜sub_9E90,sub_9F50是设置key和iv的,然后sub_A080是加密函数
然后我就从里面下断点得到了key和iv,比较函数获得了正确的密文
key = 0xc099403db00550812ea00fd803dc0e7c
iv = 0x022b26284a337015f04de065e05fc094
cipher = 6e6649305baf80c49b1b063c0500c80346ccfd42b3063ae7312b52a21cd334d8
用openssl解密试了一下,换任何模式都无法解密,加密的密文也都不同
那么可以断定,这道题修改过sm4的默认参数,这个方法行不通
猜测sm4类函数的作用
sub_A048,sub_A080,sub_A4F0的功能还不太确定
其中sub_A048调用的时候传入参数是1,有可能是1代表加密,0代表解密
也有可能是sub_A4F0是解密函数,sub_A080设置加密模式(ECB/CBC)
想想这个sm4的代码大概率不是出题人自己写的,肯定是网上开源的代码
所以直接上Github搜索sm4的C++代码,果然就有
https://github.com/tonyonce2017/SM4
看了一眼,确定解密是单独的函数,那么我们只要拿密文调用解密函数即可
用调试器执行解密函数
我们看一下这一段的汇编


4_IDA_4.png (266.46 KB, 下载次数: 0)
下载附件
2022-2-16 20:25 上传

可以很轻松地 理解这段的含义
那么我们在调用sub_A080加密函数时,稍稍修改一下寄存器的值
把原来的输入内容地址,改成正确的密文的地址,然后修改PC寄存器的值,改成解密函数的地址
处理器会执行PC寄存器的位置,那么就会调用解密函数,还原出正确的明文
看一下解密结果即可
正确的flag就是[WwW.52P0Ji3.cN]
Web中级题
hls加密嘛…肯定是有个key的
我们把里面的文件都提取出来,有好几个加密的.ts,一个script.bundle.js,还有一个drm的请求
尝试直接用返回的drm解密失败了,说明还是得分析javascript
重点在这里


5_Notepad.png (260.38 KB, 下载次数: 0)
下载附件
2022-2-16 20:27 上传

图里解释的很清楚了,drm请求的前后16字节异或,再与请求头中的h异或,即可获得正确的key
我是用cyberchef实现的


5_Cyberchef.png (228.01 KB, 下载次数: 0)
下载附件
2022-2-16 20:27 上传

https://icyberchef.com/#recipe=From_Hex('Auto')XOR(%7B'option':'Hex','string':'7b10311e6e310f0df068d9ede10475a8'%7D,'Standard',true)XOR(%7B'option':'Hex','string':'DA4E5CEAE16FED46EB6F498C9B63D53B'%7D,'Standard',false)To_Hex('Space')&input=MDhBNUU2QzJDMjYxQThBQ0I0RDc5QzQ5QUYxNjBBM0E")
得到正确的key就是a9fb8b364d3f4ae7afd00c28d571aaa9,其实iv也是这个
然后解密就好了嘛 随便怎么弄了,我是用了openssl
openssl aes-128-cbc -d -in live_00003.ts -out live_00003.decrypt.ts -K a9fb8b364d3f4ae7afd00c28d571aaa9 -iv a9fb8b364d3f4ae7afd00c28d571aaa9


5_flag.png (1.25 MB, 下载次数: 0)
下载附件
2022-2-16 20:27 上传

然后在 live_00003.decrypt.ts 中就能看到flag了
flag{like_sub_52tube}
这个flag也有点坑…我把Sub的s看成大写了,然后提交又不知道要不要加flag的大括号
第一天的三次机会就这么全错光了

函数, 下载次数

wsad012348   

看不懂哇
JemmyloveJenny
OP
  


三滑稽甲苯 发表于 2022-2-16 22:17
我分析web时试过直接把所有文件放到服务器上然后浏览器访问,但是怎么调都会报错,这是为什么呢

因为HTTP请求头中的那个h是随机生成的,你请求的时候h和抓包的h不同
再说了…drm那个请求是POST,一般服务器不允许POST静态文件的
Content-Type也不一定对
夜泉   

不错,除了最后web还能看懂,上两个盲区了
amoxuan   

膜拜大佬们
fjqisba   

我尽量下次用Cstring不用C++ string了
三滑稽甲苯   

C++ string不是能直接看到源码吗
h07799486   

我分析web时试过直接把所有文件放到服务器上然后浏览器访问,但是怎么调都会报错,这是为什么呢
hzcdong   

高手~看不懂~
amoxuan   

卡在安卓了
您需要登录后才可以回帖 登录 | 立即注册

返回顶部