记录BUU上刷的Reverse题
[羊城杯 2020]Bytecode (py字节码)
题目中给出了挺长的字节码,在解这道题的时候,需要多参考查看官方dis反汇编器的文档。
可以使用以下命令,时不时反汇编一下逆向出的代码,进行对比。
1 | python3 -m dis fileName.py |
题目给出的字节码:
1 | 4 0 LOAD_CONST 0 (3) # LOAD_CONST 压栈操作 |
本人逆向出来的,或许有些许出入,但逻辑基本正确……
1 | import sys |
首先是开头的五个字符的爆破代码:
1 | import string |
然后是中间的异或运算,我还是使用的爆破:
1 | import string |
最后只需要解z3就可以了:
1 | from z3 import * |
最后把flag拼一下:
GWHT{fc2a8bb7f347a6f8a05c5c69f3aeff73}
[ACTF新生赛2020]fungame (溢出)
ida32打开看看
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
sub_401340函数,动调y1的值即可(后来发现y1的数组直接告诉我们了,shift+e提取即可)
shift+e提取数据
1 | y1 = [0x23, 0x61, 0x3e, 0x69, 0x54, 0x41, 0x18, 0x4d, 0x6e, 0x3b, 0x65, 0x53, 0x30, 0x79, 0x45, 0x5b] |
–更新–
以为到这边就结束了,发现输入的flag不对,于是查看下面一个函数
1 | int __cdecl sub_4013BA(char *Source) |
Destination的大小只有12,但是我们传过来的参数大小为16位,所以肯定会溢出,超出的4个字符,作为地址,调用隐藏函数sub_40233D,交叉引用参数x,可以找到函数sub_40233D
base64解码,注意,函数sub_40233D还输出了x,即函数的地址,所以我们需要在flag中添加上
且,ida中使用的是小端序,添加地址需要注意。
1 | from Crypto.Util.number import * |
给我整蒙了……
[未完FlareOn2]very_success
放在linux中,file一下
die查一下:
用ida32打开
查看sub_401000函数,调整一下堆栈平衡,alt+k,调整最前面的堆栈平衡。
反汇编成功的伪代码
1 | BOOL __usercall sub_401000@<eax>(int a1@<ebp>) |
这题的逻辑看的我头晕
境界还不够,先放一放
[SCTF2019]Strange apk (apk动态释放文件)
用jeb进行返汇编。
上次做apk逆向,告诉我assets目录下面存放着很重要的东西,在这边只看到了一个data文件,很奇怪。
发现了几个奇怪的函数
按x查看“_”函数的调用
函数的意思就是把这个asset目录下的data文件和字符串”syclover” 进行异或,我们用脚本来还原。
这个脚本网上很多都是还原成2000KB+的文件,和源文件对应不上,反汇编不起来。我重新找了个脚本,但是需要很长时间跑出来。
1 | s = "syclover" |
查看datas头文件可以发现,跑出来datas文件是apk格式的文件,后缀改成apk,然后再用jeb打开。
首先查看s文件
1 | string = "c2N0ZntXM2xjMG1l" |
base64解码一下
再去分析t文件
syclover在进行md5加密之后,作为key与data进行了encode
将这串字符中的“8”去掉
1 | string = "~8t808_8A8n848r808i8d8-8w808r8l8d8}8".replace("8","") |
sctf{W3lc0me~t0_An4r0id-w0rld}
还是先用正常一点的方法来看这道题:
首先是寻找app的入口点,打开Manifest文件,入口点是myappllicatin.t,但是我们只能看到sctf.hello这个包。所以可能是app动态释放文件
当我们运行上面的脚本之后,再次查看Mainfest文件,可以看到入口点和包对应上了。
较好解决方案应该是用映射大师,“frida dex dump(python)将app运行时候的dex给dump下来再进行分析。”–引用大佬的话
尝试了许久,发现不太支持,先放一放……
[RoarCTF2019]polyre (OLLVM控制流程平坦化+CRC)
ida64拖进去看看
定位一下:
对于这个程序,我没有一点话要说……
–更新–
参考:
https://security.tencent.com/index.php/blog/msg/112
https://blog.csdn.net/liuxiaohuai_/article/details/114369681
查阅博客后,得知该程序添加了控制流平坦化,目的是为了模糊基本块之间的前后关系,以及增大分析的难度,我们需要使用deflat.py执行控制流平坦化脚本命令,且使用其脚本需要安装angr库
输入命令:
1 | python3 deflat.py attachment 0x400620 |
关于这行命令,最重要的就是后面的地址的选取,其实是函数的起始地址
还是感谢大佬的好心提示,脚本运行需要修改一些东西,关于这边存放的是am_graph模块的存放地址
复原成功之后,下面是ida反汇编伪代码:
1 | __int64 __fastcall main(int a1, char **a2, char **a3) |
从大佬那边荡过来的简化代码:
1 | _int64 __fastcall main(__int64 a1, char **a2, char **a3) |
具体的逻辑:
1 | v12 = 0 |
从别处得知是CRC32加密
大致流程:输入 48位,分成 6 组,将每组 8 字节转化为 long 类型的值,对每组进行加密,先判断正负,然后将值乘 2,随后根据正负异或 0xB0004B7679FA26B3,循环 64 次,最后与unk_402170进行比较。
需要注意的是ida的小端序,代码来源
1 | origin = [0xbc8ff26d43536296, |
可以对照看一下CRC64加密算法的C实现
https://blog.csdn.net/l1028386804/article/details/50748724
1 | /* |
[GKCTF 2021]QQQQT (base58)
用die扫一下:
用网上下载了一个EnigmaVBUnpacker
解包之后再次扫描已经没有壳了
但是程序也打不开了……
放到xdbg32中,发现可以字符串
1 | string = "56fkoP8KhwCf3v7CEz" |
flag{12t4tww3r5e77}
[NPUCTF2020]你好sao啊(base64换表解密)
可以看出,中间的RxEncode是一个base64换表解密
将只需要将v15,v16,v17,v18从后往前拼成字节
可以看到这个表中,数字5和6换成了{和},只需要把最后base64加密中的5和6替换即可。
1 | import base64 |
[UTCTF2020]babymips (mips+异或运算)
用ida打开,函数很清晰:
shift+e提取数据unk_4015F4,并将前84个赋值给v7
跟进sub_401164函数:
a2,是上面数据提取的v7,这样的话,这题静态分析也可以做,动调也可以做。
1 | arr =[98, 108, 127, 118, 122, 123, 102, 115, 118, 80, 82, 125, 64, 84, 85, 121, 64, 73, 71, 77, 116, 25, 123, 106, 66, 10, 79, 82, 125, 105, 79, 83, 12, 100, 16, 15, 30, 74, 103, 3, 124, 103, 2, 106, 49, 103, 97, 55, 122, 98, 44, 44, 15, 110, 23, 0, 22, 15, 22, 10, 109, 98, 115, 37, 57, 118, 46, 28, 99, 120, 43, 116, 50, 22, 32, 34, 68, 25, 0, 0, 0, 0, 0, 78] |
[WUSTCTF2020]funnyre (花指令+异或运算)
ida64打开,一整块的汇编都在报红,需要手动恢复main函数
mian函数调好之后发现有错误,跟进查看返汇编
经典花指令,会出现好几次,需要一个一个手修。
nop掉jz和jnz,然后对比其他汇编的硬编码就能知道错误在哪里了
修复成功之后,分析一下main函数,输入字符串为38位,除去头5位与最后一位,其他字符串,经过很多次运算之后,与unk_4025C0数组的32位进行对比
对unk_4025C0数组进行数据提取:
注意,中间还存在了一个取反运算
三百多个数据,一个一个输入,累死了=^=,幸好解出来了:
1 | arr = [ 0xD9, 0x2C, 0x27, 0xD6, 0xD8, 0x2A, 0xDA, 0x2D, 0xD7, 0x2C, 0xDC, 0xE1, 0xDB, 0x2C, 0xD9, 0xDD, 0x27, 0x2D, 0x2A, 0xDC, 0xDB, 0x2C, 0xE1, 0x29, 0xDA, 0xDA, 0x2C, 0xDA, 0x2A, 0xD9, 0x29, 0x2A] |
[QCTF2018]Xman-babymips (mips+位移运算)
用ida32打开,虽然是mips架构的程序,但是生成的伪代码还是很清晰的:
1 | int __fastcall main(int a1, char **a2, char **a3) |
跟进sub_4007F0函数
1 | int __fastcall sub_4007F0(const char *a1) |
先进行一个异或,然后五位后面的进行一个移位或运算
最后对比,shift+e提取数据
1 | string1 = [81,124,106,123,103] |
其中,因为要考虑到溢出情况,所以%0x100,当然ascii码的范围在128以内,所以求余0x80也可以。
[羊城杯 2020]login (pyinstaller解包+pyc返汇编+z3)
现在就是需要根据struct.pyc文件头来修复login.pyc的文件头
大佬使用010editor,那我也使用010editor
struct.pyc:
login.pyc:
对比一下,我们复制struct.pyc的E3之前的数据复制到login就可以成功修复文件了
uncompyle6不支持反汇编python 3.9版本的pyc文件
我使用的是pycdc,使用方法在这里
1 | ./pycdc /home/ks/桌面/pyinstaller/login.pyc |
就能看到py代码了
1 | from z3 import * |
1 | sat |
1 | arr = [ 10,24,119,7,104,43,28,91,108,52,88,74,88,33] |
[MRCTF2020]PixelShooter(apk逆向/C#逆向)
这次又换了一个安卓逆向工具:jeb
听大佬说安卓Unity游戏核心逻辑一般位于assets\bin\Data\Managed\Assembly-CSharp.dll中
进入之后,搜索字符串就出来了
如果说是unity逆向,那么dnspy可以逆向出来啊,大佬能逆出来,为啥我逆不出来??
[FlareOn5]Ultimate Minesweeper (.NET逆向)
dotnet题,用dnSpy64打开
动调了许久,找不出门路,于是借鉴了大佬Hk_Mayfly的文章
首先是找到判断的主要函数处
跟进SquareRevealedCallback函数,可以发现前面的if语句中输出了结束信息以及程序结束的函数,那么后面的getkey函数就是输出我们flag的函数
1 | // Token: 0x0600000D RID: 13 RVA: 0x000023E4 File Offset: 0x000005E4 |
我们将SquareRevealedCallback函数中的if语句注释掉,就不会弹出失败信息了,右键,编辑方法。
接着是将文件保存,ctrl+shift+s,选择一个目录保存
然后打开文件,找到关键点
后面一次点击这3个点,就可以成功输出flag
flag{Ch3aters_Alw4ys_W1n@flare-on.com}
[网鼎杯 2020 青龙组]jocker [混淆+动调]
又遇到了混淆题……
一眼就看出来前面的是假把戏,只有最后的encrypt才是真正的加密函数,还进行了混淆。
上idc代码:
1 |
|
在运行idc代码之后,修改一下函数,再跳过for循环,应用补丁,删除ida数据库,重新加载后,呈现出来的mian函数就是这样:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
再稍微修改一下encrypt函数,以及函数的范围
1 | int __cdecl encrypt(char *a1) |
还有finally函数:
1 | int __cdecl finally(char *a1) |
分析一下encrypt函数
input与ahh异或之后需要等于v2,动调一下v2
1 | v2 = [0xe,0xd,0x9,0x6,0x13,0x5,0x58, 0x56, 0x3e, 0x06,0xc, 0x3c, 0x1f, 0x57, 0x14, 0x6b, 0x57, 0x59, 0xd] |
encrypt函数只能求出前19位,还有最后5位是在finally函数中
v3字符串是”%tp&:”,我们唯一可以确定的是最后一位是”}”,可以算出同一加密的key是多少
1 | v2 = [0xe,0xd,0x9,0x6,0x13,0x5,0x58, 0x56, 0x3e, 0x06,0xc, 0x3c, 0x1f, 0x57, 0x14, 0x6b, 0x57, 0x59, 0xd] |
[GWCTF 2019]re3 (混淆+动调+AES)
findcrypt查看加密
再查看字符串,跟进成功的字符串
发现已被混淆
跟进此处,发现这是个函数,下面还调用了
查看反汇编窗口:
这些数据其实是代码,经过异或之后,变成代码执行
shift+F2 调用idc
1 |
|
选中修改后的数据,按c,force,转换成代码,
xuanzsub_402219函数,修改函数的范围
tab键查看反汇编
我们再修改一下main函数的值
注意,修改完成之后,需要patch一下源程序,然后把ida数据库文件删除后,再用ida重新打开。而且在调试的时候,需要跳过中间的混淆函数
反汇编函数:
1 | void __fastcall __noreturn main(int a1, char **a2, char **a3) |
从后往前分析:
跟进最后的sub_402219函数,
1 | __int64 __fastcall sub_402219(__int64 a1) |
sub_40196E是AES加密,sub_400A71是生成轮密钥,最后对比的byte_6030A0的值可以动调出来:
1 | byte_6030A0 = 0xBC0AADC0147C5ECCE0B140BC9C51D52B46B2B9434DE5324BAD7FB4B39CDB4B5B |
现在我们需要知道作为轮密钥的unk_603170的值
分析sub_402219上面的sub_40207B函数
sub_401CF9函数,将base64的表传入,赋值给v2
最下面的一个函数sub_401CF9,传入参数unk_603170和v2
动调unk_603170的值。sub_402219中,unk_603170作为二参传入sub_400A71函数中
分析其反汇编,因为参数传递是从右往左传递,通过ida动调,就能得知unk_603170
shift+e数据提取
1 | unk_603170 = 0xcb8d493521b47a4cc1ae7e62229266ce |
- Base64表经过sub_40207B函数两次加密,传入unk_603170
- 使用unk_603170作为轮回密钥,进行AES加密
- 结果与byte_6030A0比较
1 | from Crypto.Cipher import AES |
[RCTF2019]DontEatMe
迷宫题:
[红帽杯2019] xx
上来先用findcrypt插件识别一下加密算法
先是判断输入字符串长度是否等于19
动调后可以看出,xxtea加密上面的代码,就是把输入字符串第四个后面的所有元素赋值为0,并把这四个字符作为二参,作为密钥,进入到xxtea加密算法中
1 | 四参:"qwertyuiopasdfghjklzxcvbnm1234567890"字符串地址 |
然后是对xxtea加密后的算法进行混淆
然后是进行异或操作:
最后是进行对比
正向:
—->输入字符串(19位)
—->取前面输入的前4位作为xxtea算法密钥
—->xxtea算法加密
—->加密字符串后进行打乱置换
—->对打乱之后的数组进行异或
—->对比16进制数组
逆向:
—->得到最后对比的16进制数组
—->异或
—->恢复置换
—->xxtea解密
—->得到输入字符串
因为是小端序,最后对比的16进制数组应该是:
1 | data =[0xCE,0xBC,0x40,0x6B,0x7C,0x3A,0x95,0xC0,0xEF,0x9B,0x20,0x20,0x91,0xF7,0x02,0x35,0x23,0x18,0x02,0xC8,0xE7,0x56,0x56,0xFA] |
动调了一下异或,发现第一次和第二次都没有异或,第三次才异或,而且越往后,内层异或次数越多。i在外层递增遍历数组v20,i/3的值越大,内层v20[j]与v20[i]的异或次数i/3越多。下面是c的正向伪代码:
1 | for ( i = 1; i < 24; ++i ) |
下面是逆向的python代码:
1 | for i in range(len(data)-1,-1,-1): |
数组置换代码:
1 | table = {0:2,1:0,2:3,3:1,4:6,5:4,6:7,7:5,8:10,9:8,10:11,11:9,12:14,13:12,14:15,15:13,16:18,17:16,18:19,19:17,20:22,21:20,22:23,23:21} |
1 | data1 = [0xbc, 0xa5, 0xce, 0x40, 0xf4, 0xb2, 0xb2, 0xe7, 0xa9, 0x12, 0x9d, 0x12, 0xae, 0x10, 0xc8, 0x5b, 0x3d, 0xd7, 0x6, 0x1d, 0xdc, 0x70, 0xf8, 0xdc] |
1 | import struct |
[虎符2022 未完] fpbe
查看官方wp,发现使用的是另一款基于java的反汇编器:Ghidra
插件也下载好了,就是不会安装……没有在file下面找到extension
[GUET CTF2019] encrypt RC4/elf动调/base64分块
写在前面:关于elf的动调
把ida目录下dbgsrv的两个linux_server拖到linux需要调试的文件的文件夹中
运行相关格式的linux_server
返回ida中,选择remote linux debugger,F9,选择相应配置
在程序开始的地方打上断点,按F9,就可以动调了(操作与其他动调工具一致)
这道题是关于RC4的加密算法
算法共有三个步骤
- S列表和T向量的初始值,同时建立一个临时变量T,将密钥的值循环赋值到T数组中
- S的初始置换
- 生成密码流
查看main函数
题目中给出了密钥v10数组,并作为二参进入了函数sub_4006B6
跟进sub_4006B6函数,其融合了RC4加密算法的第一二步骤
将v9(以下统称S)数组中元素的值按升序置为0-255,题目中没有明显的赋值T向量,但效果一致
接下来就是swap(),打乱S数组了
展示一下打乱前和打乱后的S数组(大可不必)
打乱前:
打乱后:
跟进sub_4007DB函数,与RC4有所出入的是生成的 k(v9[(v7 + v8)])直接与明文(s)异或了
正向:s –> k
k ^ 明文 ==> 密文
逆向:密文 ^ k ==> 明文
所以我们只需要求等式右边的k值,与密文异或,就能求出明文了(很多wp上来就是求数组S,说实话没啥用,因为k就是通过S生成的,S则是通过递增数组和密钥T数组生成的,当然也是可以求出来的)
动调elf文件,跟进sub_4007D8函数,光标放在22行,查看反汇编界面,rdx是异或之前的数,在其处下断点,一直F9,将k数组统计出来
1 | k = [0x10,0x59,0x9C,0x92,0x06,0x22,0xCF,0xA5,0x72,0x1E,0x45,0x6A,0x06,0xCB,0x08,0xC3,0xE4,0x49,0x5A,0x63,0x0C,0xDF,0xF6,0x5F,0x08,0x28,0xBD,0xE2,0x10,0x15,0x1F,0x6E,0xAA,0x5A,0xCA,0xEC,0x80,0xAF,0x9B,0x16,0xBB,0x3D,0x13,0x2F,0x6A,0xA4,0xC7,0x2E,0xBC,0x4B,0x60,0x9A,0xAF,0xE9,0xCE,0xDA,0x67,0x39,0xBA,0x3B,0x85,0xEB,0xD2,0x6B,0xAB,0x06,0x6B,0x10,0x57,0x2C,0x88,0x70,0xF7,0x4F,0xAA,0x7F,0x12,0x47,0xD6,0xDE,0x74,0xB2,0x1D,0xA4,0xD7,0x76,0x9A,0xE0] |
现在我们需要求的是密文,我们跟进下面的函数
这边很眼熟,在base64加密中看见过,将3位字符,转换成4位字符
正向:rc4加密后的字符每4位转换成3位
逆向:最后对比的byte_602080字符,每3位转换成4位
shift+e数据提取(漏了一位,记得手动添上)
1 | data = [0x5a, 0x60, 0x54, 0x7A, 0x7A, 0x54, 0x72, 0x44,0x7C, 0x66, 0x51, 0x50, 0x5B, 0x5F, 0x56, 0x56,0x4C, 0x7C, 0x79, 0x6E, 0x65, 0x55, 0x52, 0x79,0x55, 0x6D, 0x46, 0x6B, 0x6C, 0x56, 0x4A, 0x67,0x4C, 0x61, 0x73, 0x4A, 0x72, 0x6F, 0x5A, 0x70,0x48, 0x52, 0x78, 0x49, 0x55, 0x6C, 0x48, 0x5C,0x76, 0x5A, 0x45, 0x3D] |
太强了……改天得研究一下这个脚本是怎么写的
[HDCTF] MFC (Win消息发送+DES加密)
刚开始用的火绒剑,感觉没啥用
去下了个spy类工具,用来查看windows的窗口,消息和进程等等
这边用的是大佬推荐的CometAssistant,彗星小助手
还去下载了一个spy++ (但是看着有点膈应人)
关于xspy的使用很简单,只需要把这个放大镜拖动放到需要嗅探的窗口中就可以了
突破口在这边,当然也涉及我的知识盲区了:向窗口发送信息
这边直接把大佬博客里的代码贴过来:
1 | #include <stdio.h> |
分析一下:
首先Win32 API的使用需要导入Windows.h的库
FindWindowA:检索其类名和窗口名与指定字符串匹配的顶级窗口的句柄。此函数不搜索子窗口。此函数不执行区分大小写的搜索。返回值是一个窗口句柄。一参是窗口的类名,可为空。二参是窗口的标题名称。
SendMessage:将指定的消息发送到一个或多个窗口。SendMessage函数调用指定窗口的窗口过程,直到窗口过程处理完消息后才返回。
贴上详细解释,长话短说就是一参是接受窗口的句柄,二参是指定被发送的消息,三四参数是附加消息指定信息。
这个窗口的窗口名称为“Flag就在控件中”,我们就去寻找这个窗口的句柄,并赋值为h。
找到句柄之后我们向其发送一个数字:0x464(至于为什么要发送0x464,还得让我琢磨琢磨……)
当发送完数字之后,窗口变了
DES是一个常见的分组加密形式,需要一个密钥+明文==>明文
貌似这个名字就是明文了
1 | crypto: 944c8d100f82f0c18b682f63e4dbaa207a2f1e72581c2f1b |
flag{thIs_Is_real_kEy_hahaaa}
[CISCN2018]2ex (base64换表)
啊这……
放到ida中,这个字符很起疑心:
长度竟然为64?base64换表走起:
1 | import base64 |
CTFtools也成,得把表中的转义字符\给去掉才行
(太棒了,水了一题)
[安洵杯 2019]crackMe (SM4+base64魔改+hook)
搞不懂为什么我运行不起来,修复了半天,去装了个win7
运行之后,应该是这样的界面:
猜测这道题的考点是hook
正向:
——–>base64表大小写转换
——–>SM4加密(key = “where_are_you_now?”)
——–>SM4加密后,密码相邻位互换
——–>base64表,先进行大小写转换,再进行(a1+24)%64转换[相当于把字母表向左移动了24位]
——–>base64解密
逆向
——–>base64表转换(大小写与移位)
——–>加密字符转换(相邻位置互换)
——–>base64换表解密
——–>SM4解密(需要pysm4的包)
这道base64是用”!”,来替换’=’ ,先来转换base64的表:
1 | base = list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") |
再把需要转换的字符两两转换,换表base64解码
1 | import base64 |
然后把生成的两个16进制数:需要解密的数hex_key以及轮回秘钥hex_s,放到SM4解密函数decode()中解密,将生成的Int类型数字,转换成16进制,再转换成bytes类型
1 | import binascii |
参考链接:http://81.69.232.121/%E5%AE%89%E6%B4%B5%E6%9D%AF-2019crackme-%E6%88%91%E4%BA%BA%E5%82%BB%E4%BA%86/
[WUSTCTF2020]level4 (idc or 算法)
没想到这题竟然有三种解法,之前写这篇的时候,没有深入的研究:
ida打开:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
跟进type1:
1 | __int64 __fastcall type1(char *a1) |
跟进type2:
1 | int __fastcall type2(char *a1) |
两个递归调用,可以确定type1是中序遍历二叉树,type2是后序遍历二叉树,我们需要做的就是通过type1,type2,我们可以尝试还原出二叉树的构造。
方法一:
手算,如果二叉树太大,耗时耗力。
方法二:
可以猜测出flag应为前序遍历,使用idc脚本编写
跟进type2,查看反汇编代码,发现只需要调整代码块的位置,就使type2函数变成前序遍历
1 | static main() |
在计算机中,汇编指令和数据其实都是一码事,都是二进制代码,为了方便使用的是16进制数,一个字节8位,使用的是两个十六进制数。Byte是根据给出的虚拟地址,取一个字节,PatchByte则是用二参的字节去patch一参的地址。
idc代码就是把address1-address2处的字节与address2-address3的字节交换
可以在这边输入6,可以看到指令的字节
选中代码,右键分析,强制执行之后:
改变前:
改变后:
别的大佬的博客用的都是keypatch插件,我是直接把对应地址的call指令重新patch了
最后,把patch完的程序,apply patches to应用
出来了:
方法三:
上手C++代码
1 | #include <iostream> |
感谢师傅送我的代码!!
[CFI-CTF 2018]IntroToPE (.NET逆向)
打开看一下:
查个壳,这让我有点害怕,第一次遇到带红的
1 | Big sec. 1 .text , Explore and analyze .NET assemblies with .NET Reflector v10 - www.red-gate.com |
还去下载了.net Reflector 和 .NET Generic unpacker
我们直接用dnSpy打开,
找到对应的函数
热泪盈眶
[GXYCTF2019]simple CPP (z3+反调试+动调)
这题折磨的我没有脾气……
先查个壳,C++逆向,据我所知,c++的题目,一般都得调试……
瞧瞧这是人看的题目么
main函数中,还出现了反调试函数,调试会崩,需要打补丁。我用ida调试了很久,太菜,无果……
题目的意思是将输入的字符转换成16进制字符串,假设输入abc,则字符串为616263
我们需要求出v20数组
用x,y,z,w分别代表v20[0]~v20[3],还是耐住性子写了一遍本题的位运算
1 | v20[3] = w |
在Z3中,只有BitVecs可以进行位运算
1 | # -*- coding:utf-8 -*- |
然而我的代码和大佬的代码运行结果只有一个变量不一致……
1 | y = 0xc00020130082c0c |
在动调中发现了这些字符
下面的代码是大佬的代码,我实在没有搞懂lis中0x8c之后的十六进制数是哪里来的……
1 | flag = '' |
We1l_D0näeéb’ _ólgebra_am_i
第二部分的解,比赛的时候已经给出e!P0or_a
所以flag{We1l_D0ne!P0or_algebra_am_i}
参考链接:https://blog.csdn.net/mcmuyanga/article/details/113628506
[FlareOn1]Bob Doge (.net逆向+dnspy逆向)
先查个壳,发现没有壳,c++编写
运行一下,发现是个安装的程序
安装成功之后,会在安装目录下,生成一个.exe文件,我们再扫个壳,这边就能看出来是C#编写的.net程序了
(麻了,我还以为要逆向安装程序,给我ida给我看呆了)
点击decode之后变成了doge,哈哈哈
我们放到dnSpy中进行反编译
找到加密函数之后,text3就是flag
断点打在加密函数最后的text3处,F5运行到这边,flag就出来了
(我跟个憨憨一样用逆向算法算了半天还没算出来……)
[SWPU2019]ReverseMe (od动调plus)
首先是查壳:
因为是c++编写的,ida静态分析难度较大,所以选择od动调分析:
快过来熟悉一下od的操作!!!
首先通过中文搜索引擎跳转到提示性语句:
可以确定的是call语句的下一条cmp指令,与0x20,也就是32,决定是否跳转,也就是0x20是长度
我们输入一个符合条件的数值,继续向下调试。我这边输入了32个1
可以看到,在执行lea语句后,esi被赋值成”SWPU_2019_CTF”
这边是与输入字符进行异或,ord(1) ^ ord(S) = ord(‘b’)
可以在循环之外下断点,然后F9快速跳到断点出,再往下看,下面则是把数据依次压入栈:
再往下看,遇到一个函数,我们进入一个函数
F7跟进,我们遇到了xor,从0xae58b8地址读取数据赋给ecx,进行异或
选中下面的框框,我们选择在数据窗口中跟随地址
就能看到是什么数据进行对比了
1 | string2 = [0x86,0x0C,0x3E,0xCA,0x98,0xD7,0xAE,0x19,0xE2,0x77,0x6B,0xA6,0x6A,0xA1,0x77,0xB0,0x69,0x91,0x37,0x05,0x7A,0xF9,0x7B,0x30,0x43,0x5A,0x4B,0x10,0x86,0x7D,0xD4,0x28] |
跳出这个函数之后,接着往下看:
开始了第三次异或,和第二次一样,我们只需要找这个地址的数据就行了
继续跟随数据窗口,跳转如下,我们找到了需要最终对比的数据了:
1 | final = [0xB3,0x37,0x0F,0xF8,0xBC,0xBC,0xAE,0x5D,0xBA,0x5A,0x4D,0x86,0x44,0x97,0x62,0xD3,0x4F,0xBA,0x24,0x16,0x0B,0x9F,0x72,0x1A,0x65,0x68,0x6D,0x26,0xBA,0x6B,0xC8,0x67] |
最终脚本如下
1 | # 正 final[i] = input[i]^string1[i%13]^string2[i] |
flag{Y0uaretheB3st!#@_VirtualCC}
参考:
https://blog.nowcoder.net/n/aa8086cbf8c54e3b8dd49c52e2fcefe0?from=nowcoder_improve
[SCTF2019]babyre (花指令+三维迷宫+暗藏base64+???)
ida打开后一瞅,我就知道事情不简单
发现了海量的花指令
第一处:
修改之后
第二处,下面的jrcxz也需要nop掉(细节上出问题后面就得重修……)
第三处:
第四处:
E13处,in指令第二个字节C7是mov指令,就需要去把第一个字节给nop掉了
第五处:
所以说,一共 是五处花指令?
下面是转换main函数,快捷键P
这边的话,先d键,转换为数据,再c键,force转换为代码
(注意,需要把所有的花指令修完之后,再去转换main函数和C22函数,重复了n次,每次都再C22处JUMPOUT)
选中798到C21,按P键转换成函数,还原main函数
这边也类似,选中到c22,按住P键,就可以把刚刚的loc_C22声明成函数,之后就可以F5反汇编了
以下就是成功反汇编出的主函数:
1 | __int64 __fastcall main(int a1, char **a2, char **a3) |
可以看出这是个迷宫题,而且看起来方向键很多,是三维迷宫,长度为125 = 5 * 5 * 5
1 | maze = '**************.****.**s..*..******.****.***********..***..**..#*..***..***.********************.**..*******..**...*..*.*.**.*' |
得出:
1 | input1 = 'ddwwxssxaxwwaasasyywwdd' |
第二个挑战,就是每输入四个字符,就将其转换成3个字符:
下面附上大佬的爆破代码:
1 | #include <stdio.h> |
每个都带进入算了一遍,得知:
1 | input2 = "c2N0Zl85MTAy" |
麻了,每四位转换成3位,就是唯一不同的地方是,缺4补上@,就是base64加密啊……
最后进入第三个challenge,参考的是这位大佬
1 | __int64 __fastcall sub_FFA(char *a1) |
跟进这个函数
因为v2 初始等于 4 , v4 初始等于 0 ,上面的代码就可以转换成:
v10[n+4] = v10[n] ^ function(v10[a+1] ^ v10[a+2] ^ v10[a+3])
根据交换律
==>v10[n] = v10[n+4] ^ function(v10[a+1] ^ v10[a+2] ^ v10[a+3])
这样就可以通过后四位逆推前一位(心态已经崩了)
1 | import libnum |
运行结果:
所以最后的flag为:
sctf{ddwwxxssxaxwwaasasyywwdd-c2N0Zl85MTAy(fl4g_is_s0_ug1y!)}
Ecquation (js fuck加密+z3)
题目中提示这个是Js的fuck加密,可以在网站上一个一个的解密,当然也可以用脚本跑
在网上找了一份关于此的解密脚本
1 | <script> |
解出来之后:
1 | l[40]+l[35]+l[34]-l[0]-l[15]-l[37]+l[7]+l[6]-l[26]+l[20]+l[19]+l[8]-l[17]-l[14]-l[38]+l[1]-l[9]+l[22]+l[41]+l[3]-l[29]-l[36]-l[25]+l[5]+l[32]-l[16]+l[12]-l[24]+l[30]+l[39]+l[10]+l[2]+l[27]+l[28]+l[21]+l[33]-l[18]+l[4]==861&&l[31]+l[26]+l[11]-l[33]+l[27]-l[3]+l[12]+l[30]+l[1]+l[32]-l[16]+l[7]+l[10]-l[25]+l[38]-l[41]-l[14]-l[19]+l[29]+l[36]-l[9]-l[28]-l[6]-l[0]-l[22]-l[18]+l[20]-l[37]+l[4]-l[24]+l[34]-l[21]-l[39]-l[23]-l[8]-l[40]+l[15]-l[35]==-448&&l[26]+l[14]+l[15]+l[9]+l[13]+l[30]-l[11]+l[18]+l[23]+l[7]+l[3]+l[12]+l[25]-l[24]-l[39]-l[35]-l[20]+l[40]-l[8]+l[10]-l[5]-l[33]-l[31]+l[32]+l[19]+l[21]-l[6]+l[1]+l[16]+l[17]+l[29]+l[22]-l[4]-l[36]+l[41]+l[38]+l[2]+l[0]==1244&&l[5]+l[22]+l[15]+l[2]-l[28]-l[10]-l[3]-l[13]-l[18]+l[30]-l[9]+l[32]+l[19]+l[34]+l[23]-l[17]+l[16]-l[7]+l[24]-l[39]+l[8]-l[12]-l[40]-l[25]+l[37]-l[35]+l[11]-l[14]+l[20]-l[27]+l[4]-l[33]-l[21]+l[31]-l[6]+l[1]+l[38]-l[29]==-39&&l[41]-l[29]+l[23]-l[4]+l[20]-l[33]+l[35]+l[3]-l[19]-l[21]+l[11]+l[26]-l[24]-l[17]+l[37]+l[1]+l[16]-l[0]-l[13]+l[7]+l[10]+l[14]+l[22]+l[39]-l[40]+l[34]-l[38]+l[32]+l[25]-l[2]+l[15]+l[6]+l[28]-l[8]-l[5]-l[31]-l[30]-l[27]==485&&l[13]+l[19]+l[21]-l[2]-l[33]-l[0]+l[39]+l[31]-l[23]-l[41]+l[38]-l[29]+l[36]+l[24]-l[20]-l[9]-l[32]+l[37]-l[35]+l[40]+l[7]-l[26]+l[15]-l[10]-l[6]-l[16]-l[4]-l[5]-l[30]-l[14]-l[22]-l[25]-l[34]-l[17]-l[11]-l[27]+l[1]-l[28]==-1068&&l[32]+l[0]+l[9]+l[14]+l[11]+l[18]-l[13]+l[24]-l[2]-l[15]+l[19]-l[21]+l[1]+l[39]-l[8]-l[3]+l[33]+l[6]-l[5]-l[35]-l[28]+l[25]-l[41]+l[22]-l[17]+l[10]+l[40]+l[34]+l[27]-l[20]+l[23]+l[31]-l[16]+l[7]+l[12]-l[30]+l[29]-l[4]==939&&l[19]+l[11]+l[20]-l[16]+l[40]+l[25]+l[1]-l[31]+l[28]-l[23]+l[14]-l[9]-l[27]+l[35]+l[39]-l[37]-l[8]-l[22]+l[5]-l[6]+l[0]-l[32]+l[24]+l[33]+l[29]+l[38]+l[15]-l[2]+l[30]+l[7]+l[12]-l[3]-l[17]+l[34]+l[41]-l[4]-l[13]-l[26]==413&&l[22]+l[4]-l[9]+l[34]+l[35]+l[17]+l[3]-l[24]+l[38]-l[5]-l[41]-l[31]-l[0]-l[25]+l[33]+l[15]-l[1]-l[10]+l[16]-l[29]-l[12]+l[26]-l[39]-l[21]-l[18]-l[6]-l[40]-l[13]+l[8]+l[37]+l[19]+l[14]+l[32]+l[28]-l[11]+l[23]+l[36]+l[7]==117&&l[32]+l[16]+l[3]+l[11]+l[34]-l[31]+l[14]+l[25]+l[1]-l[30]-l[33]-l[40]-l[4]-l[29]+l[18]-l[27]+l[13]-l[19]-l[12]+l[23]-l[39]-l[41]-l[8]+l[22]-l[5]-l[38]-l[9]-l[37]+l[17]-l[36]+l[24]-l[21]+l[2]-l[26]+l[20]-l[7]+l[35]-l[0]==-313&&l[40]-l[1]+l[5]+l[7]+l[33]+l[29]+l[12]+l[38]-l[31]+l[2]+l[14]-l[35]-l[8]-l[24]-l[39]-l[9]-l[28]+l[23]-l[17]-l[22]-l[26]+l[32]-l[11]+l[4]-l[36]+l[10]+l[20]-l[18]-l[16]+l[6]-l[0]+l[3]-l[30]+l[37]-l[19]+l[21]+l[25]-l[15]==-42&&l[21]+l[26]-l[17]-l[25]+l[27]-l[22]-l[39]-l[23]-l[15]-l[20]-l[32]+l[12]+l[3]-l[6]+l[28]+l[31]+l[13]-l[16]-l[37]-l[30]-l[5]+l[41]+l[29]+l[36]+l[1]+l[11]+l[24]+l[18]-l[40]+l[19]-l[35]+l[2]-l[38]+l[14]-l[9]+l[4]+l[0]-l[33]==289&&l[29]+l[31]+l[32]-l[17]-l[7]+l[34]+l[2]+l[14]+l[23]-l[4]+l[3]+l[35]-l[33]-l[9]-l[20]-l[37]+l[24]-l[27]+l[36]+l[15]-l[18]-l[0]+l[12]+l[11]-l[38]+l[6]+l[22]+l[39]-l[25]-l[10]-l[19]-l[1]+l[13]-l[41]+l[30]-l[16]+l[28]-l[26]==-117&&l[5]+l[37]-l[39]+l[0]-l[27]+l[12]+l[41]-l[22]+l[8]-l[16]-l[38]+l[9]+l[15]-l[35]-l[29]+l[18]+l[6]-l[25]-l[28]+l[36]+l[34]+l[32]-l[14]-l[1]+l[20]+l[40]-l[19]-l[4]-l[7]+l[26]+l[30]-l[10]+l[13]-l[21]+l[2]-l[23]-l[3]-l[33]==-252&&l[29]+l[10]-l[41]-l[9]+l[12]-l[28]+l[11]+l[40]-l[27]-l[8]+l[32]-l[25]-l[23]+l[39]-l[1]-l[36]-l[15]+l[33]-l[20]+l[18]+l[22]-l[3]+l[6]-l[34]-l[21]+l[19]+l[26]+l[13]-l[4]+l[7]-l[37]+l[38]-l[2]-l[30]-l[0]-l[35]+l[5]+l[17]==-183&&l[6]-l[8]-l[20]+l[34]-l[33]-l[25]-l[4]+l[3]+l[17]-l[13]-l[15]-l[40]+l[1]-l[30]-l[14]-l[28]-l[35]+l[38]-l[22]+l[2]+l[24]-l[29]+l[5]+l[9]+l[37]+l[23]-l[18]+l[19]-l[21]+l[11]+l[36]+l[41]-l[7]-l[32]+l[10]+l[26]-l[0]+l[31]==188&&l[3]+l[6]-l[41]+l[10]+l[39]+l[37]+l[1]+l[8]+l[21]+l[24]+l[29]+l[12]+l[27]-l[38]+l[11]+l[23]+l[28]+l[33]-l[31]+l[14]-l[5]+l[32]-l[17]+l[40]-l[34]+l[20]-l[22]-l[16]+l[19]+l[2]-l[36]-l[7]+l[18]+l[15]+l[26]-l[0]-l[4]+l[35]==1036&&l[28]-l[33]+l[2]+l[37]-l[12]-l[9]-l[39]+l[16]-l[32]+l[8]-l[36]+l[31]+l[10]-l[4]+l[21]-l[25]+l[18]+l[24]-l[0]+l[29]-l[26]+l[35]-l[22]-l[41]-l[6]+l[15]+l[19]+l[40]+l[7]+l[34]+l[17]-l[3]-l[13]+l[5]+l[23]+l[11]-l[27]+l[1]==328&&l[22]-l[32]+l[17]-l[9]+l[20]-l[18]-l[34]+l[23]+l[36]-l[35]-l[38]+l[27]+l[4]-l[5]-l[41]+l[29]+l[33]+l[0]-l[37]+l[28]-l[40]-l[11]-l[12]+l[7]+l[1]+l[2]-l[26]-l[16]-l[8]+l[24]-l[25]+l[3]-l[6]-l[19]-l[39]-l[14]-l[31]+l[10]==-196&&l[11]+l[13]+l[14]-l[15]-l[29]-l[2]+l[7]+l[20]+l[30]-l[36]-l[33]-l[19]+l[31]+l[0]-l[39]-l[4]-l[6]+l[38]+l[35]-l[28]+l[34]-l[9]-l[23]-l[26]+l[37]-l[8]-l[27]+l[5]-l[41]+l[3]+l[17]+l[40]-l[10]+l[25]+l[12]-l[24]+l[18]+l[32]==7&&l[34]-l[37]-l[40]+l[4]-l[22]-l[31]-l[6]+l[38]+l[13]-l[28]+l[8]+l[30]-l[20]-l[7]-l[32]+l[26]+l[1]-l[18]+l[5]+l[35]-l[24]-l[41]+l[9]-l[0]-l[2]-l[15]-l[10]+l[12]-l[36]+l[33]-l[16]-l[14]-l[25]-l[29]-l[21]+l[27]+l[3]-l[17]==-945&&l[12]-l[30]-l[8]+l[20]-l[2]-l[36]-l[25]-l[0]-l[19]-l[28]-l[7]-l[11]-l[33]+l[4]-l[23]+l[10]-l[41]+l[39]-l[32]+l[27]+l[18]+l[15]+l[34]+l[13]-l[40]+l[29]-l[6]+l[37]-l[14]-l[16]+l[38]-l[26]+l[17]+l[31]-l[22]-l[35]+l[5]-l[1]==-480&&l[36]-l[11]-l[34]+l[8]+l[0]+l[15]+l[28]-l[39]-l[32]-l[2]-l[27]+l[22]+l[16]-l[30]-l[3]+l[31]-l[26]+l[20]+l[17]-l[29]-l[18]+l[19]-l[10]+l[6]-l[5]-l[38]-l[25]-l[24]+l[4]+l[23]+l[9]+l[14]+l[21]-l[37]+l[13]-l[41]-l[12]+l[35]==-213&&l[19]-l[36]-l[12]+l[33]-l[27]-l[37]-l[25]+l[38]+l[16]-l[18]+l[22]-l[39]+l[13]-l[7]-l[31]-l[26]+l[15]-l[10]-l[9]-l[2]-l[30]-l[11]+l[41]-l[4]+l[24]+l[34]+l[5]+l[17]+l[14]+l[6]+l[8]-l[21]-l[23]+l[32]-l[1]-l[29]-l[0]+l[3]==-386&&l[0]+l[7]-l[28]-l[38]+l[19]+l[31]-l[5]+l[24]-l[3]+l[33]-l[12]-l[29]+l[32]+l[1]-l[34]-l[9]-l[25]+l[26]-l[8]+l[4]-l[10]+l[40]-l[15]-l[11]-l[27]+l[36]+l[14]+l[41]-l[35]-l[13]-l[17]-l[21]-l[18]+l[39]-l[2]+l[20]-l[23]-l[22]==-349&&l[10]+l[22]+l[21]-l[0]+l[15]-l[6]+l[20]-l[29]-l[30]-l[33]+l[19]+l[23]-l[28]+l[41]-l[27]-l[12]-l[37]-l[32]+l[34]-l[36]+l[3]+l[1]-l[13]+l[18]+l[14]+l[9]+l[7]-l[39]+l[8]+l[2]-l[31]-l[5]-l[40]+l[38]-l[26]-l[4]+l[16]-l[25]==98&&l[28]+l[38]+l[20]+l[0]-l[5]-l[34]-l[41]+l[22]-l[26]+l[11]+l[29]+l[31]-l[3]-l[16]+l[23]+l[17]-l[18]+l[9]-l[4]-l[12]-l[19]-l[40]-l[27]+l[33]+l[8]-l[37]+l[2]+l[15]-l[24]-l[39]+l[10]+l[35]-l[1]+l[30]-l[36]-l[25]-l[14]-l[32]==-412&&l[1]-l[24]-l[29]+l[39]+l[41]+l[0]+l[9]-l[19]+l[6]-l[37]-l[22]+l[32]+l[21]+l[28]+l[36]+l[4]-l[17]+l[20]-l[13]-l[35]-l[5]+l[33]-l[27]-l[30]+l[40]+l[25]-l[18]+l[34]-l[3]-l[10]-l[16]-l[23]-l[38]+l[8]-l[14]-l[11]-l[7]+l[12]==-95&&l[2]-l[24]+l[31]+l[0]+l[9]-l[6]+l[7]-l[1]-l[22]+l[8]-l[23]+l[40]+l[20]-l[38]-l[11]-l[14]+l[18]-l[36]+l[15]-l[4]-l[41]-l[12]-l[34]+l[32]-l[35]+l[17]-l[21]-l[10]-l[29]+l[39]-l[16]+l[27]+l[26]-l[3]-l[5]+l[13]+l[25]-l[28]==-379&&l[19]-l[17]+l[31]+l[14]+l[6]-l[12]+l[16]-l[8]+l[27]-l[13]+l[41]+l[2]-l[7]+l[32]+l[1]+l[25]-l[9]+l[37]+l[34]-l[18]-l[40]-l[11]-l[10]+l[38]+l[21]+l[3]-l[0]+l[24]+l[15]+l[23]-l[20]+l[26]+l[22]-l[4]-l[28]-l[5]+l[39]+l[35]==861&&l[35]+l[36]-l[16]-l[26]-l[31]+l[0]+l[21]-l[13]+l[14]+l[39]+l[7]+l[4]+l[34]+l[38]+l[17]+l[22]+l[32]+l[5]+l[15]+l[8]-l[29]+l[40]+l[24]+l[6]+l[30]-l[2]+l[25]+l[23]+l[1]+l[12]+l[9]-l[10]-l[3]-l[19]+l[20]-l[37]-l[33]-l[18]==1169&&l[13]+l[0]-l[25]-l[32]-l[21]-l[34]-l[14]-l[9]-l[8]-l[15]-l[16]+l[38]-l[35]-l[30]-l[40]-l[12]+l[3]-l[19]+l[4]-l[41]+l[2]-l[36]+l[37]+l[17]-l[1]+l[26]-l[39]-l[10]-l[33]+l[5]-l[27]-l[23]-l[24]-l[7]+l[31]-l[28]-l[18]+l[6]==-1236&&l[20]+l[27]-l[29]-l[25]-l[3]+l[28]-l[32]-l[11]+l[10]+l[31]+l[16]+l[21]-l[7]+l[4]-l[24]-l[35]+l[26]+l[12]-l[37]+l[6]+l[23]+l[41]-l[39]-l[38]+l[40]-l[36]+l[8]-l[9]-l[5]-l[1]-l[13]-l[14]+l[19]+l[0]-l[34]-l[15]+l[17]+l[22]==-114&&l[12]-l[28]-l[13]-l[23]-l[33]+l[18]+l[10]+l[11]+l[2]-l[36]+l[41]-l[16]+l[39]+l[34]+l[32]+l[37]-l[38]+l[20]+l[6]+l[7]+l[31]+l[5]+l[22]-l[4]-l[15]-l[24]+l[17]-l[3]+l[1]-l[35]-l[9]+l[30]+l[25]-l[0]-l[8]-l[14]+l[26]+l[21]==659&&l[21]-l[3]+l[7]-l[27]+l[0]-l[32]-l[24]-l[37]+l[4]-l[22]+l[20]-l[5]-l[30]-l[31]-l[1]+l[15]+l[41]+l[12]+l[40]+l[38]-l[17]-l[39]+l[19]-l[13]+l[23]+l[18]-l[2]+l[6]-l[33]-l[9]+l[28]+l[8]-l[16]-l[10]-l[14]+l[34]+l[35]-l[11]==-430&&l[11]-l[23]-l[9]-l[19]+l[17]+l[38]-l[36]-l[22]-l[10]+l[27]-l[14]-l[4]+l[5]+l[31]+l[2]+l[0]-l[16]-l[8]-l[28]+l[3]+l[40]+l[25]-l[33]+l[13]-l[32]-l[35]+l[26]-l[20]-l[41]-l[30]-l[12]-l[7]+l[37]-l[39]+l[15]+l[18]-l[29]-l[21]==-513&&l[32]+l[19]+l[4]-l[13]-l[17]-l[30]+l[5]-l[33]-l[37]-l[15]-l[18]+l[7]+l[25]-l[14]+l[35]+l[40]+l[16]+l[1]+l[2]+l[26]-l[3]-l[39]-l[22]+l[23]-l[36]-l[27]-l[9]+l[6]-l[41]-l[0]-l[31]-l[20]+l[12]-l[8]+l[29]-l[11]-l[34]+l[21]==-502&&l[30]-l[31]-l[36]+l[3]+l[9]-l[40]-l[33]+l[25]+l[39]-l[26]+l[23]-l[0]-l[29]-l[32]-l[4]+l[37]+l[28]+l[21]+l[17]+l[2]+l[24]+l[6]+l[5]+l[8]+l[16]+l[27]+l[19]+l[12]+l[20]+l[41]-l[22]+l[15]-l[11]+l[34]-l[18]-l[38]+l[1]-l[14]==853&&l[38]-l[10]+l[16]+l[8]+l[21]-l[25]+l[36]-l[30]+l[31]-l[3]+l[5]-l[15]+l[23]-l[28]+l[7]+l[12]-l[29]+l[22]-l[0]-l[37]-l[14]-l[11]+l[32]+l[33]-l[9]+l[39]+l[41]-l[19]-l[1]+l[18]-l[4]-l[6]+l[13]+l[20]-l[2]-l[35]-l[26]+l[27]==-28&&l[11]+l[18]-l[26]+l[15]-l[14]-l[33]+l[7]-l[23]-l[25]+l[0]-l[6]-l[21]-l[16]+l[17]-l[19]-l[28]-l[38]-l[37]+l[9]+l[20]-l[8]-l[3]+l[22]-l[35]-l[10]-l[31]-l[2]+l[41]-l[1]-l[4]+l[24]-l[34]+l[39]+l[40]+l[32]-l[5]+l[36]-l[27]==-529&&l[38]+l[8]+l[36]+l[35]-l[23]-l[34]+l[13]-l[4]-l[27]-l[24]+l[26]+l[31]-l[30]-l[5]-l[40]+l[28]-l[11]-l[2]-l[39]+l[15]+l[10]-l[17]+l[3]+l[19]+l[22]+l[33]+l[0]+l[37]+l[16]-l[9]-l[32]+l[25]-l[21]-l[12]+l[6]-l[41]+l[20]-l[18]==-12&&l[6]-l[30]-l[20]-l[27]-l[14]-l[39]+l[41]-l[33]-l[0]+l[25]-l[32]-l[3]+l[26]-l[12]+l[8]-l[35]-l[24]+l[15]+l[9]-l[4]+l[13]+l[36]+l[34]+l[1]-l[28]-l[21]+l[18]+l[23]+l[29]-l[10]-l[38]+l[22]+l[37]+l[5]+l[19]+l[7]+l[16]-l[31]==+(!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+[+!+[]]) |
可以看到等式最后有个数字没有解出来
我们放到网站中解就行了
是个81,关于等式,我们使用z3
1 | from z3 import * |
把求出来的手动转换成数组,然后输出
用的是之前的方法做的,感觉不太简洁,看网上还有更好的方法
https://www.codetd.com/article/10613214
BUUCTF–[V&N2020 公开赛]CSRe
这题首先是去混淆,然后是逆向.NET文件
exeinfo PE可以得知这是个有混淆的文件.NET文件
使用de4dot文件去混淆
然后用dnSpy逆向.net
str解出来后是1415
text解出来之后是turn
flag{1415turn}
[ACTF新生赛2020]SoulLike (elf暴力/pwntools)
用ida打开
sub_83A太大了,且篇都是xor,mov,add三个指令,
除了最后的几个字符
参考链接:
https://blog.css8.cn/post/1439298.html
原来是配置的问题,需要修改ida /ctg目录下的hexrays.cfg文件中的MAX_FUNCSIZE=64,改成1024
就是将输入的flag,经过3000行的异或后,与最后的几个进行对比
因为最后有提示错误的位数,所以可以试着用爆破的方法……
这边放上大佬整理的关于pwntools的使用方法:
https://seamiloak.github.io/2020/10/16/pwntools%E4%BD%BF%E7%94%A8/
https://blog.csdn.net/hanqdi_/article/details/107164199
1 | from pwn import * |
太强了!!
参考链接:
http://bubuko.com/infodetail-3671012.html
[羊城杯 2020]easyre (base64/凯撒)
还是很庆幸自己能独立做出来这一题的
第一个加密是base64加密,
第二个加密是base64加密后的变换,
第三次加密是所有字符向后+3
主要是第二次的加密,不知道到底是怎样转换的,
放到x64dbg中动调了一下
输入时带入了字符串
1 | string = "abcdefghijklmnopqrstuvwxyz0123456789012" |
一次加密后:
1 | string = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5MTI=" |
二次加密后:
1 | string = "R1dnd4eXowMTIYWJjZGVmZ2hpazNDU2Nzg5MTI=mtsbW5vcHFyc3" |
当时看到加密函数之后人傻了,仔细观察发现只是固定字符段调换了位置,就写出了下面的解法:
1 | #a3-->a1 |
三次加密后:
1 | string = "U4gqg7hArzPWLBZMmCJYpC5ksdcQGX5Qcj8PWL=pwveZ8yfKIbf6" |
整体还是不难的
1 | import base64 |
findKey (花指令+正逆向+MD5)
先查个壳
打开运行之后:
发现有花指令:
push了两个一样的指令,需要把下面一个去掉才行,nop一次就好
(大佬z1r0说,下面调用了一个系统的strlen函数,在call函数前,应该会push参数到栈中,而突然跳转本来就很奇怪……)
最后选中所有红色的代码,按一下p键,就反汇编成功了
注意,选中的代码一定要包含所有的红色代码,否则会有函数不显示
开始分析代码:(题目是findkey,而且也没有的输入……)
进一下sub_40101E函数,发现关键词8003u,是熟悉的味道:md5
string1经过md5加密之后,与v15异或,需要等于Str的值
经过if判断之后,string1再与v14异或,得到flag
整个程序可以写成以下的伪代码:
1 | Str = "0kk`d1a`55k222k2a776jbfgd`06cjjb" |
逆向思路:
–>Str^v15–>string1(md5加密后)
–>md5解密还原string1
–>string1与v14异或
–>解出flag
1 | Str = "0kk`d1a`55k222k2a776jbfgd`06cjjb" |
得出string1的值为“123321”
v14的值:
1 | v14 = [0x57,0x5E,0x52,0x54,0x49,0x5F,0x1,0x6D,0x69,0x46,0x2,0x6E,0x5F,0x2,0x6C,0x57,0x5B,0x54,0x4C] |
string1与v15异或:
1 | v14 = [0x57,0x5E,0x52,0x54,0x49,0x5F,0x1,0x6D,0x69,0x46,0x2,0x6E,0x5F,0x2,0x6C,0x57,0x5B,0x54,0x4C] |
flag{n0_Zu0_n0_die}
[GUET-CTF2019]number_game (动调/二叉树)
ida打开函数,是这个样子:
4006D6判断输入字符类型,不满足直接输出wrong
400758这块把我看迷糊了,很像二叉树的前序遍历
后面的400807也是一个打乱顺序的函数
用户输入字符–>两个函数打乱–>满足数独情况
满足数独的情况是
[0,4,2,1,4,2,1,4,3,0]
下午直接找了大佬z1r0来研究这一题。
首先我们想到的是pwndbg来动态调试这一题,当然调试出来了,但是我们带入的是值,不是下标,没有很好的解决这一题,然后我们选择了爆破,只是我们的方法不太好,也就爆破个几个小时就出来了……
1 | from pwn import * |
https://www.cnblogs.com/th1r7een/articles/14532420.html
这题最好还是正向来做……
首先是sub_400758函数构建了一颗二叉树
1 | def test(index,lenth): |
中序遍历后的顺序为:[7,3,8,1,9,4,0,5,2,6]
flag{1134240024}
发现IDA还可以动调!!
需要查看IDA权威指南,IDA动调的话,先放一防,先刷题先……
firmware (路由器逆向/binwalk)
关于固件分析的文章:
https://www.freebuf.com/articles/ics-articles/262454.html
关于路由器逆向的文章:
https://blog.csdn.net/QQ1084283172/article/details/66971242
通常固件文件以bin/zip/LZMA/arj等文件压缩类型封装。
题目给了一个bin文件,
用linux的file命令查看一下,查看是否是二进制镜像文件
通常用binwalk对其进行提取,当然也可以再用binwalk查看一下bin文件的细节信息
用binwalk对bin文件进行提取(e–extract)
1 | binwalk -e firmware.bin |
可以看到提取出了这些文件
我们再通过hexdump或者file确认一下,是否是squashsf文件系统,其地址00包含squashsf的头部特征(hsqs)
squashsf后缀文件需要进一步解压,从而获得固件的根文件
可能是binwalk没有安装完全,导致squashfs文件系统损坏
binwalk安装传送门:
https://blog.csdn.net/QQ1084283172/article/details/65441110
摸索了很久,发现firmware-mod-kit可以成功解压squashfs文件系统,
https://blog.csdn.net/sjt670994562/article/details/98071249
这边放上github的firmware-mod-kit的链接
1 | git clone https://github.com/mirror/firmware-mod-kit |
炸了……搞了很久还是报错kali和ubuntu都试过了……
再放一放……
–更新–
1 | sudo apt install firmware-mod-kit |
直接用apt安装即可(感谢大佬z1r0的指导!!)默认路径在opt下,在binwalk提取bin文件后的目录下输入下面的指令就行了
1 | /opt/firmware-mod-kit/trunk/unsquashfs_all.sh ./120200.squashfs |
进入squashfs-root目录下的tmp文件夹
file一下,elf文件,ARM类型,但是无法执行
发现有upx壳
1 | upx -d backdoor |
脱壳之后放在ida里面反汇编
1 | echo.byethost51.com:36667 |
flag{33a422c45d551ac6e4756f59812a954b}
[WMCTF2020]easy_re (perl特性)
查壳后发现没壳,用x64dbg打开
搜索script,并添加断点
向下F8,flag就出来了
WMCTF{I_WAnt_dynam1c_F1ag}
据说是因为perl语言解压call有字符串script,
参考链接:https://blog.csdn.net/Palmer9/article/details/107727922
crackMe (od动调)
用ida32打开后
最外层是一个死循环,要想跳出死循环,需要满足后面的if嵌套,
可以发现if嵌套上面的sub_401090是用来给byte_416050赋值的,还把welcomebeijing这个用户名作为了参数传递到了其中
接着跟进sub_401830,说了一大串之后,v17作为二参进入了sub_401470
要使得v14,也就是sub_40147中的a3等于43924,需要满足a2满足所有的if条件,也就是a2=”dbappsec”,即v17=”dbappsec”,猜测v17是输入的flag经过变换后对比的值
返回sub_401830函数,关于v17的值的代码块也就只有这边了
接下来,我们需要动调,把与byte_416050异或的v16数组求出来
通过明显的地方,可以推算出ida和x32dbg的地址差
光标确定到某一参数,按住tab键可以查看其汇编代码,在伪代码窗口的定义部分可以看到这些参数关于ebp的偏移
加上偏移量就能快速定位到需要执行的函数了
在ida中定位while大循环,然后再dbg中定位,在循环开始和xor处打断点,多按几次F9,记录一下edx低位的值
正向:
user输入–>byte_406050–>byte_406050^v16[v5-1]–>v17
逆向:
v17–>byte_406050^v17–>v16[v5-1]
byte_406050数组,是user传入之后经过一系列变换生成的,我们需要动调的就是byte_406050的值,然后在与已知的v17异或,就是v16的值了,也就是题目要求的flag
我动调之后的结果是[0x7e,0x98,0xc9,0x95,0x10,0x6d,0xf3,0x67]
先放一放……
—-更新—-
在xor的上下有两个反调试指令,所以应该把je改成jmp,动调就行了
调试结果:
1 | v16 = [0x2a,0xd7,0x92,0xe9,0x53,0xe2,0xc4,0xcd] |
代码:
1 | v16 = [0x2a,0xd7,0x92,0xe9,0x53,0xe2,0xc4,0xcd] |
关于反调试的可以看看雪大神的这篇文章:
https://bbs.pediy.com/thread-225740.htm
参考文章:
https://www.songbingjia.com/nginx/show-209658.html
https://www.cnblogs.com/pppyyyzzz/p/12546844.html
特殊的base64 (base64换表)
经典的base64换表,“mTy……”是换表之后的加密的字符串,字符串搜索之后可以看到换的base64表。
1 | import base64 |
[网鼎杯 2020 青龙组]singal (vm题/ponce插件)
传送门:https://www.52pojie.cn/thread-1176826-1-1.html
查壳,发现没有壳
这个qmemcpy的意思是把unk_403040的456个字符复制到v4中
这个题就是首先是需要去摸清楚指令的调用顺序
进行观察,可以发现,v4 与 a1[v10 + 1]比较 是判断条件,a1是输入的opcode, 而v4来自v5,v5由flag进行运算得到: flag—>v5—->v4 所以我们要得到v4,然后推v5,再得到flag
1 | #include <stdio.h> |
通过case7对比的数组v4来求出v5,即执行的顺序,然后通过v5来求出flag反向推出flag,执行顺序由后往前。
学到一招,可以把多行的数据转换成数组
https://www.52pojie.cn/thread-1187493-1-1.html
很好奇还有关于ida插件ponce的使用来解决这一道题
–待更新–
[GWCTF 2019]xxor (xor plus)
当初发现这个题目不是一般的难,就跳过了,这次正好来总结一下。
下载后发现是个linux文件,先放linux运行一下:
放到ida里面看一下
首先是循环输入六次,然后进入一个循环,然后sub_400686这个函数一看就是加密函数了,一参是我们输入的值,二参是一个固定数组。
2 dup(2) ,就是2出现两次的意思,上次个maze题也出现过
那这样二参我们就知道了:{2,2,3,4}
注意一下第二个循环,j循环了三次
后面的的sub_400770则是比较函数了
我们跟进:
我这边正好熟悉一下z3的用法:
1 | from z3 import * |
也就是六个数字,循环三次,两两计算
v5初始等于0,那逆向就是0x40*1166789954 = 74674557056,原来每次递增,那么最后就递减
别的如果是加,那么就都减去,次序再调换一下
(貌似python对位移运算不够友善,以后做这种计算的题目还是用c吧)
1 | #include <stdio.h> |
令我疑惑的是,为什么会有下面的操作?
1 | for (int i = 0;i < 6 ;i ++) |
思考了一下,发现不加后面的&0xff结果也一样
[WUSTCTF2020]level4(二叉树遍历)
先放linux里面运行了一下
1 | 2f0t02T{hcsiI_SwA__r7Ee} |
ida打开
只能理解为顺序错了,两串字符串的内容是一致的。
两个函数都是递归函数,Type1函数putchar()在中间,Type2函数putchar()在后面,而且上面的提示语句(Data Structure)已经告诉我们是关于数据结构的东西。我们便可以想到是树的前中后遍历,那么Type1就是中序遍历,Type2函数就是后序遍历,我们需要的就是Type3就是前序遍历,开始手动绘制……
wctf2020{This_IS_A_7reE}
[ACTF新生赛2020]Universe_final_answer (z3计算)
查个壳,然后用ida64打开
跟进这个sub_860函数,就能看到一堆的方程
在网上学习了一下python中z3的基本用法
1 | from z3 import * |
下面开始着手编写对应脚本
1 | from z3 import * |
解出来之后,还需要转化一下,且注意顺序,v2是a1[0],v6与v5的次序是对调的。
1 | [v1 = 48, |
1 | lis = [ 70,48,117,82,84,121,95,55,119] |
F0uRTy_7w@
所以说sub_860的值出来了,进入一个sub_C50()函数判断,我们只需要用Linux虚拟机运行一下,将我们求得的字符串带入输入到其中,使得条件成立,flag就出来了。
flag{F0uRTy_7w@_42}
[UTCTF2020]basic-re (水题)
用ida64打开看看
flag{str1ngs_1s_y0ur_fr13nd}
[Zer0pts2020]easy strcmp (binascii+函数变址)
查壳后用ida64打开
这题貌似没有我想的那么简单
1 | __int64 __fastcall main(int a1, char **a2, char **a3) |
这边的mian函数只是一个比较的函数
参考资料:https://www.cnblogs.com/Mayfly-nymph/p/13154909.html
但是人没写怎么查找到加密输入符号的函数
这边放上z1r0的解析:
https://www.zhihu.com/people/xu-xu-xuxu-94
在机智聪明,成熟老练的z1r0大佬的指导下,我逐渐摸清楚为两个函数是如何变幻的,并且还学到了很多东西,我总结一下:
在sub_795这个函数中,可以看到:
qword_201090这个全局变量(在.bss表中,所以是全局变量)保存的是strcmp的地址
off_201028=sub_6EA是什么意思?我们跟进一下
首先理解一下offset是什么意思:
https://blog.csdn.net/deniece1/article/details/102934346
offset为属性操作符,表示应该把其后面的符号地址的值,不是内容作为操作数,所以off_201028应该存放的是strcmp的地址
所以,off_201028 = sub_6EA,相当于把sub_6EA的地址赋值给了strcmp!!!再推理一下,全局变量qword_201090保存的是strcmp的地址,所以如果要执行strcmp函数,我们需要用qword_201090来实现。
z1r0大佬说, .plt段存放的一般是puts/printf函数,.bss存放的是全局变量,init放置的是程序初始化的代码块,.text存放代码段。
因此,在main函数中的strcmp函数,其实调用的是sub_6EA这个函数
我们跟进sub_6EA这个函数:
第一个for循环是用来测定输入字符串的长度,v4除以8再+1,相当于组数。然后第二个for循环,每组减去qword_201060这个数组,我们跟进
程序后面的qword_201090,我们推理过了,就相当于于是strcmp,用来于外面的”zer0pts{*CENSORED*}”相比较
所以我们应该是加上qword_201060的值才对,上代码:
1 | import binascii |
binascii.b2a_hex()函数运行之后的结果为b’4445524f534e4543’,为byte类型的字符串,所以需要转换一下,hex(x,16)
奇怪啊,为什么要把每一段字符串逆一下?([::-1])
如果说这个是因为ida的小段存储机制,所以要逆一下,那后面求完之后的y为什么要逆一下?奇怪??
b’flag{l3ts_m4k3_4_DETOUR_t0d4y}’
[FlareOn3]Challenge1 (base64换表)
先查个壳,再用ida32打开
输入的字符串经过sub_401260函数的转换,变成了str2,然后与str1进行比较
可以看到这边是很熟悉的base64算法,我们点进去byte_413000看看
一个很经典的base64换标操作,下面是代码:
1 | import base64 |
flag{sh00ting_phish_in_a_barrel@flare-on.com}
[ACTF新生赛2020]Oruga(迷宫plus)
oruga是什么文件?不管了,先插个壳看看
用ida64打开看看
前面五个字符告诉我们了,继续跟进sub_78A看看
再到byte_201020看看
到这就傻了,琢磨了半天……
https://www.cxybb.com/article/weixin_43876357/107525490
原来是迷宫啊,但是这个迷宫怎么用上面的查看的……
原来下面的这个意思是代表这4个0x00的意思,那这样就能把ida中的这一组数据转换成列表了,这边我搬运的大佬z1r0的代码
1 | mz = [0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x23, 0x23, 0x23,0x00, 0x00, 0x00, 0x23, 0x23, 0x00, 0x00, 0x00, 0x4F, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x4F, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x4C, 0x00, 0x4F, 0x4F, 0x00, 0x4F, 0x4F, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x4C, 0x00, 0x4F, 0x4F, 0x00, 0x4F, 0x4F, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x4C, 0x4C, 0x00, 0x4F, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, |
还有三个问题我没有解决:
- 地图大小如何确定
- 游戏规则如何建立(碰到障碍物才能停下再继续操作)
- 障碍物如何显示?
16*16 = 256,所以可以确定地图的大小
第二层while循环中就限制了移动的范围,一是在地图的边缘,而是碰到障碍物
而障碍物貌似就是第二层循环中的后面的限制条件,可以把实体化,打印出来……
flag{MEWEMEWJMEWJM} 吧
[BJDCTF2020]BJD hamburger competition(unity逆向+sha1+mod5)
我惊了……
查个壳子看看,没有壳,用ida64打开,貌似查壳+ida不是正确的打开方式
看看z1r0的博客(下面的这句话才是最装逼的,哈哈哈),用到的是dnSPY这个软件:
‘https://www.zhihu.com/people/xu-xu-xuxu-94
成功下载,并打开
https://blog.csdn.net/weixin_47158947/article/details/107818896
主要看第二点,反编译这个Assembly-CSharp.dll这个文件,直接修改unity的C#源代码
然后放到dnspy中……
顺利找到一个BJDCTF的字眼
踩坑了,我都不知道哈希函数基本是不可逆的
看z1r0的博客才知道文中的Sha1只是判断第一个字符,我们通过这个获得了密文,接着将1001这个密文进行MOD5加密
可以看到,哈希值是下面这个,(我看z1r0的博客,会有两个值……)
不管了,继续看
作者的意图是获取mod5加密的前20个字符
1 | string = 'b8c37e33defde51cf91e1e03e51657da' |
flag{B8C37E33DEFDE51CF91E}
[WUSTCTF2020]Cr0ssfun(水题)
先查个壳,用ida64打开
可以看到v4是我们输入的flag,check函数是比较函数,我们跟进
继续跟进……
1 | _BOOL8 __fastcall iven_1s_educated(_BYTE *a1) |
继续跟进……
1 | _BOOL8 __fastcall iven_1s_brave(_BYTE *a1) |
这iven也太自恋了吧……下面是代码
1 | a1 = [0]*40 |
wctf2020{cpp*@nd_r3verse*@re_fun}
[FlareOn6]Overlong(od动调)
差壳,啥意思?要用ide试试?
我也没看出来个啥,运行了一下,到时候上ida直接查找这句话。
打开ida32查看,
1 | int __stdcall start(int a1, int a2, int a3, int a4) |
unk_402008,中有很多数字,但最后弹出的框中,只有28个数字……
写了很多代码之后,解不出来……
https://blog.csdn.net/P_Black_K/article/details/122342264
原来是要去用od去动调这个28啊……
我用的是吾爱破解的od,打开后可以看到push 0x1c,而这个数字正好是28,我们做的就是去修改这个0x1c。
出来了!第一次用od动调程序!!
flag{I_a_M_t_h_e_e_n_C_o_D_i_n_g@flare-on.com}
[GWCTF 2019]xxor (xor plus++)
先查个壳,用IDA64打开
1 | // positive sp value has been detected, the output may be wrong! |
我们跟进main
1 | __int64 __fastcall main(int a1, char **a2, char **a3) |
输入六次,
先是循环三次,可以看出sub_400686是一个加密的函数
而sub_400770是一个对比的函数
1 | __int64 __fastcall sub_400770(_DWORD *a1) |
加密之后的v7数组
1 | v7 = [3746099070,550153460,3774025685,1548802262,2652626477,2230518816] |
这边可以看到,dword_601060数组为[2,2,3,4]
然后看看加密算法
1 | __int64 __fastcall sub_400686(unsigned int *a1, _DWORD *a2) |
好!到这边就没了,炸了!
参考链接:
https://blog.csdn.net/HardDebugger/article/details/108625220
https://www.programminghunter.com/article/3800202454/
1 | from Crypto.Util.number import long_to_bytes |
分析一下这个代码:
ctypes是python的外部函数库,提供与C兼容的数据类型,并允许调用DLL或共享库中的函数(我暂时不需要了解这个)
我们这边就探讨这个c_uint 这是个无符号数,对数组中的类型进行限制,限制为无符号数,最后还需要进行long_to_bytes的转换??太强了,先放这边搁着,先下一题看看
[MRCTF2020]Xor
没有壳子,直接用ida32打开。
啥情况?
我就先用跳转,跳转到401095这个地址
进入sub_401020之后,我按了一下tab,貌似就可以反汇编了,我返回主函数看一下,成功反汇编!
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
byte_4212C0来存放字符串,然后在后面的for语句中,与i异或之后,与byte_41EA08比较,如下所示,我们开始编写代码
奇怪了,暴力和正常的异或都没用啊,这肯定是错的……
1 | string = 'SAWB~FXZ:J:`tQJ"N@ bpdd}8g' |
发现了暗门
那是什么暗门,这个这个函数包含了我们刚刚看过的主函数
算了,看看吧……
麻了,原来串个字符的前面有个M,没注意到,脚本编写没有问题的
1 | string = 'MSAWB~FXZ:J:`tQJ"N@ bpdd}8g' |
27 MRCTF{@_R3@1ly_E2_R3verse!}
[MRCTF2020]hello_world_go
没看到壳子,用ida64打开
???
下一题
[WUSTCTF2020]level3(base64换表)
用64位的IDA打开
一个base64有问题的题目
这个是
本程序里面的,我特地找了之前做过的reverse_3,纯正的base64题目,中的码表,用来做了个对比
这边是少了个=号,base62倒是可以解出来,结果却都是数字
像我之前做过的一个换码表的题目了,我正好反过来看一看
[ACTF新生赛2020]usualCrypt这道题
刚刚试了一下,换表的话,长度相等才行……新表的明显短了,这怎样才好??
我懂了,题目中的表的次序换了
在table上按x,果真有引用这个的函数
1 | __int64 O_OLookAtYou() |
这就简单了,换表之后,就能解码了,下面就是代码:
1 | import base64 |
b’wctf2020{Base64_is_the_start_of_reverse}’
[FlareOn4]IgniteMe()
先查个壳,发现没有壳,用32位IDA打开
1 | void __noreturn start() |
一眼看出if语句是用来判断的,我们跟进这个sub_401050()函数
1 | int sub_401050() |
判断数组我命名成为model,转换后的数组我命名成my_flag,而中间的转换数组,我命名成为my_arr
1 | int sub_4010F0() |
发现在判断函数的上面的sub_4010F0()函数,已经开辟了my_arr数组了,
可以推理出my_arr里面的元素都是0,总数没细看,再回到判断函数。
v4 =的这个函数,我给它命名为my_rol,我属实没看懂这个是什么意思
1 | __int16 sub_401000() |
我假设就是向左移动一位的意思,为1,073,971,200
那也就是v4 = 1,073,971,200,i = v1 - 1,那么这个v1我猜是40
看了人家的脚本之后,v4=4??
这是我没想到的,差别大了……
1 | model = [0x0D,0x26,0x49,0x45,0x2A,0x17,0x78,0x44,0x2B,0x6C,0x5D,0x5E,0x45,0x12,0x2F,0x17, |
R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com
网上还有一个爆破版本,不想动脑子了,球球了,我用python实现一下。
https://blog.csdn.net/Palmer9/article/details/105253318
1 | model = [0x0D,0x26,0x49,0x45,0x2A,0x17,0x78,0x44,0x2B,0x6C,0x5D,0x5E,0x45,0x12,0x2F,0x17, |
2.9日补充:
最近比赛挺多,压力都很大,特地过来复习一下od动调:
关于od调试常用的快捷键:
https://mambainveins.gitee.io/2020/09/02/2020-09-02-GDB_common_instructions/
F7步进,也就是遇到一个函数会进去,就是步入call的意思。
F8步过,也就是遇到一个函数,执行完之后,不会进入call.
F9,直接执行
我们需要做的就是动态调试v4的值,v4在sub_401050这个函数中
然后值等于sub_401000()函数的返回值
首先我们是搜索字符串,定位提示性语句
F8一直步过,到输入字符串的位置停下
我们随便输入一串字符串,回车就能一直运行
现在到达401050这个位置,我们F7步进
然后F7一直步过
到401000下面,我们就能看到v4的值了
al = 4
Youngter-drive(windows多线程+index逆向)
Exeinfo:
有壳,咱们脱壳先,对比一下,没有了貌似
继续,咱们用IDA32打开
下一步
1 | int __cdecl main_0(int argc, const char **argv, const char **envp) |
首先是sub_4110FF()函数,我们跟进
1 | ((void (*)(void))sub_4110FF)(); |
输入语句,将输出的flag给Source,
1 | ::hObject = CreateMutexW(0, 0, 0); |
CreateMutexW函数,创建或者打开一个命名或未命名的互斥对象
1 | j_strcpy(Destination, Source); |
将source赋值给destination
1 | hObject = CreateThread(0, 0, StartAddress, 0, 0, 0); |
创建线程 CreateThread(0, 0, StartAddress, 0, 0, 0),通常起始地址是程序代码中定义的函数名称
后面的CloseHandle真实关闭前面线程对象的句柄
我们跟进StartAddress
1 | void __stdcall __noreturn StartAddress_0(int a1) |
这边的sleep(0x64u)要当心,这个函数对字符串的加密是隔一个字符加密
1 | // 已检测到正的sp值, 输出可能错误! |
https://www.cnblogs.com/Mayfly-nymph/p/12573658.html
第二个线程执行的函数:
1 | void __stdcall __noreturn sub_411B10(int a1) |
有两个线程,别人博客上面,还得确认哪一个线程先执行,说是第二个先执行
也就是偶数不变,奇数会变。
下面是代码:
1 | # -*- coding:utf-8 -*- |
不用看了,我甚至连线程的工作原理都不知道,先去看看python的多线程机制
https://www.runoob.com/python/python-multithreading.html
创建线程类,生成线程对象,将需要运行的函数放入run()这个方法中,而且每运行一次都需要获得锁,来保证线程的同步性,在运行完成之后需要释放锁,还可以把线程放入线程列表中,使其具有优先级,使其可以交替执行
现在可以理解了。。
flag{ThisisthreadofwindowshahaIsES}
z1r0的博客上说,最后差了一位,确实
传入的参数a2,是29,而我们要求的flag为30位
该爆破是就爆破:
1 | flag = 'ThisisthreadofwindowshahaIsES' |
flag{ThisisthreadofwindowshahaIsESE}
[WUSTCTF2020]level2(upx脱壳)
查壳后发现有壳,upx工具走起
打开之后,没看到正确的主函数
在string窗口中,找到了,试试呗
好家伙,还真是
flag{Just_upx_-d}
去看看z1r0的博客吧,z1r0老师傅专程详解了汇编代码,我得好好看看
相册(apk病毒+base64)
有意思……
看了一段时间就麻了
看看z1r0的博客中,用的是导航中的搜索文本功能,搜索的是email
然后直接跟进sendMailjavamail
这个软件的操作和ida一样啊,x键就能看到引用的地方,跟进之后
跟进后面的的MAILSERVER
嗯,base64加密……
然后是解压apk,找到.so文件,我都不知道so文件是干嘛的
百度了一下,so文件对于linux系统来说,就像dll文件相对于windows系统一样,一般来讲,so文件称为共享库
https://blog.csdn.net/wangquan1992/article/details/113770115
我搞了半天也不知道so文件怎么打开
什么叫strings看一下,哈哈哈
1 | import base64 |
我的心态是这样的:
[HDCTF2019]Maze (easy 迷宫)
有壳,万能脱壳FFI,没有壳了应该
maze是迷宫的意思,之前做过一个迷宫的题目,被虐了
上来啥也别干,直接查看strings窗口
找到了!
用python 算了一下是70个字符,我猜是7*10
1 | maze = "*******+********* ****** **** ******* **F****** **************" |
这边是输入14个字符,看我上面的图案,还真是
接下来就是寻找规则了
22333233224441?
ssaaasaassdddw?
好家伙,第二个就对了,笑死了
flag{ssaaasaassdddw}
[WUSTCTF2020]level1
先查壳
没有壳,然后放到ida64中打开
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
很清晰,output文本文件是输出之后的文件
直接上代码:
1 | lists = [198,232,816,200,1536,300,6144,984,51200,570,92160,\ |
需要注意的是
- i&1 == 1代表是奇数,i&1==0代表是偶数,
1 | str[0] 没有输出 |
而对于我们output文件,我们看到的第一个数字198其实是在第二个,所以,可以理解为什么是 int(lists[i] >> (i+1))
- i & 1 == 0 代表i是偶数 i & 1 == 1代表i是奇数
最终结果:ctf2020{d9-dE6-20c}
[MRCTF2020]Transform(index 逆向)
首先是查壳,没有壳,我们用ida64打开
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
可以看到,第一个for循环是把输入的字符放到了str中,str[dword_40F040[i]]把值给了byte_414040,byte_414040再与dword_40F040[i]进行异或
下面的for 循环是将dword_40F040[i]与byte_40F0E0做比较
参考链接:https://www.cnblogs.com/Mayfly-nymph/p/12806027.html
总体来说
就是将输入的flag使用dword数组作为索引,打乱顺序
再将打乱后的flag数组,与dword数组异或,得到byte
1 | dword = [0x9, 0x0A, 0x0F, 0x17, 0x7, 0x18, 0x0C, 0x6, 0x1, 0x10,\ |
flag{Tr4nsp0sltiON_Clph3r_1s_3z}
原来关于index转换的题目还能这么做,创建一个全为0的flag列表,然后正序的推理……
[ACTF新生赛2020]usualCrypt
查个壳,没有壳,用IDA32打开
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
分析一下:
首先是输入将flag放入v8数组中,然后进入了sub_401080这个函数,猜测应该是加密函数,我们继续跟进。
1 | int __cdecl sub_401080(int a1, int a2, int a3) |
一开始是一个sub_401000函数
1 | int sub_401000() |
这个函数的意思是将base表中的
我们进入这个byte_40E0AA查看,貌似不是普通的base64加密函数
我们已知:
base:ABCDEFGHIJ
byte_40E0AA:KLMNOPQRS…………
查看跳出sub_401000,查看后面代码,可以看出是base64加密
我们再继续往后看
1 | return sub_401030(a3); |
最后有一个sub_401030函数,我们继续跟进python
1 | int __cdecl sub_401030(const char *a1) |
可以看出这个函数的意思就是把加密后的结果大小写字母转换,
看最后的比较函数,
1 | while ( *(v5 + v3) == byte_40E0E4[v3] ) // 匹配函数 |
这么多应该都是比较函数,我是这么理解的:
如果v5这个字符串与byte_40E0E4这个字符串相等,则会一直执行while循环对比,直到v3=strlen(v5)+1,然后进入LABEL_6比较,相等则v3-1=strlen(v5),就输出正确的提示信息;
如果v5与这个字符串有一个不相等,则直接会退出while循环,这时
然后跟进byte_40E0E4,这个时候长度一定不相等,就输出错误的提示信息。
最后与加密之后对比的字符串为:
1 | string = "zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9" |
有个疑问哈,这个z和后面的是分离的,所以怎么分辨那些块是那个字符串,还是可以共用??如果是aMxh3……字符串这他对应的内容是多少,是和string一致,还是比string少一个z……
在询问大佬z1r0之后,得知,A 键可以使字符串连接起来
所以,base64对应的字符串:
1 | base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" |
我们理一下,
- 先把这个string大小写先转换
- 修改base64转换表
- 加密结果通过转换表得到正常加密的结果
- base64解密
上代码:
1 | import base64 |
flag{bAse64_h2s_a_Surprise}
[2019红帽杯]easyRE(.fini)
查壳之后没有壳,直接用ida64打开,我一般是直接按Tab键看伪代码的
1 | __int64 sub_4009C6() |
当我发现这个sub_400E44是base64之后,n键改个名字:my_base64
上面有一堆混淆人的伪代码,咱们直接看重点就行,
1 | memset(v18, 0, 65); |
为v18初始化
1 | sub_4406E0(0, v18, 64LL); |
结合上下文可以知道这个sub_44065E0是赋值给v18的
1 | if ( sub_424BA0(v18) == '\'' ) |
然后就是将v18base64加密十次,与off_6cc090比较,我们直接把off_6cc090反向解密10次就行
1 | string = 'Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1\ |
炸了,给我跳出一个网址。在查看z1r0的博客后,有些不理解的地方,不知道他关于真正flag函数是怎么找到的,在询问z1r0之后,z1r0贴心地给了我提示,我总结一下:
1 | v10 = my_base64(v9); |
这个off_6cc090就是我们上面要比对的字符串,我们跟进之后:
我们可以先对off_6CC090这个字符串按x,就会弹出下面的框框,可以看到
- 引用这个字符串所在的函数(也就是上面10个base64加密的坑人的那个……)
- 引用类型r,
- 文本(应该是汇编指令)
(看来我得好好的去看看IDA权威指南了,一脸蒙蔽……)
然后看颜色就可以知道,下面的byte_6CC0A0,处在同一个段中,我们跟进,然后切换伪代码视图:
我们继续按x,然后就可以看到引用了,我们继续跟进:
地址为.fini_array:00000000006CBEE8
进程终止代码的一部分。程序正常退出时,系统将安排执行这边的代码!!!
所以这个函数很重要的!!!我们直接点击 sub_400D35这个函数跟进,然后按下tab:
出来了!!
1 | if ( (v1 ^ byte_6CC0A0[0]) == 'f' && (HIBYTE(v4) ^ byte_6CC0A3) == 'g' ) |
瞧瞧这第20行的地方,一个f,一个g,很有希望!!
看上面一行,v4=v1,那就很好理解了:
直接把大佬的原话搬出来:
https://www.zhihu.com/people/xu-xu-xuxu-94
后面我看不懂z1r0博客中exp的十六进制数从哪里来的,我就去寻找资料
https://www.i4k.xyz/article/Palmer9/103940709
说找到这串不随机的数,怎么找的,我就用IDA跳转地址的功能,G就可以了
手动输入了一下,发现还真有
用刚学的知识分析一下:
r代表读取交叉引用,说明sub_400D35函数引用了这串字符,我怎么没看到……
跟进去发现,就是这个byte_6CC0A0……(百度一下眼瞎怎么治)
直接上z1r0的代码,不解释了,看懂就行:
1 | v4 = '' |
看这个代码就清晰多了,key里面的是那些特殊字符的16进制形式,我们需要求出v4,使得v4的前四位能与key中的前四位进行异或操作时,等于flag,所以我们进行了key1[i]与ord(str1[i])的异或操作,将其还原回来,嗯,可以理解……
1 | v4 = '' |
然后就是让key1中的每个字符,与v4[i%4]进行异或操作,为什么要求余4?因为v4一共就四位。
1 | flag = '' |
flag{Act1ve_Defen5e_Test}
这也真“easy”,真不错……
[GUET-CTF2019]re (暴力+次序)
先查壳,发现有upx壳子,然后我们用upx脱个壳
然后放到IDA 64里面查看
1 | __int64 __fastcall sub_400E28(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6) |
不要慌张,我们直接看correct的上一行,根据我的推测,从键盘输入的字符应该是放到了v11中,我们直接看sub_4009AE这个函数
我们需要求的就是a1这个数组了
然后我们直接上代码:
1 | lists1 = [1629056,6771600,3682944,10431000,3977328, 5138336,7532250,\ |
flag{e65421110b0a3099a1c039337},提交之后竟然错了。。。
知错就改,z1r0中的博客说a[6]不存在,z1r0然后用暴力枚举,把a[6]给爆破出来了,上代码:
1 | lists1 = [1629056,6771600,3682944,10431000,3977328, 5138336,7532250,\ |
奇怪啊,z1r0说,结果为第二个,为什么还是错的??
返回ADI中,我看到了:
还得注意次序!!flag{e165421110ba03099a1c039337}
[SUCTF 2019] SignIn(rsa plus)
RSA绝对是当今应用最为广泛的公钥加密算法
这是我第二次碰到rsa算法了,这次不能放过它了
这里我参考了b站油管rsa的介绍
看看z1r0的博客,发现一篇文章也说的很好
https://blog.csdn.net/dbs1215/article/details/48953589
- 什么是rsa?
rsa是一种加密和解密使用不同密码的方式,因此公钥密码通常车给非对称密码
- rsa加密
密文=明文^E mod N
常说的公钥为E和N的组合,我们就用(E,N)来表示公钥
- rsa解密
明文=密文^D mod N
对密文进行D次方后除以N的余数就是明文,这就是RSA解密过程,知道D和N就能进行
- 一般题目的解题方法
一般的题目中会给出我们E和N,这两个是属于公钥,我们可以先把N放到因式分解工具找出P和Q,然后通过rsatool求出d——需要把N,E,Q,P都放到rsatool中并且调试对应的配置,最后是脚本的编写,具体我会放在后面
1 | 因式分解工具:http://www.factordb.com/index.php |
先查看题目,记得是没有壳的
记住rsa的特性,一个非常长的数字N,还有一个很短的数字E
分析一下题目,v8用来接受输入的字符,然后v8,v9进入了一个sub_96A的函数,我们跟进查看
1 | #include <stdio.h> |
以上参考z1r0的代码,该函数的功能是将v8的字符串转换为16进制放入v9,然后就是下面的两个函数,gmpz_init_set_str和gmpz_powm
是GNU的高精度算法??
1 | __gmpz_init_set_str(v7, "ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16LL); |
将ad939ff59~~这个字符串以16进制写入v7
将1034~~这个数字以10进制写入v4
将65537这个字符串以10进制写入v5
__gmpz_pown(v6,v6,v5,v4),意思是计算v6的v5次方,并对v4取模,写入v6,现在我终于看明白了,v6相当于明文,v5相当于E,v4相当于N,
rsa的加密算法:明文^E%N
__gmpz_cmp是一个比较函数,看名字就知道了
rsa加密之后与ad939ff59~~这个函数作比较
我们先用因式分解工具来算出P和Q
1 | P:282164587459512124844245113950593348271 |
然后用rsatool求出d
记住要先选择number base,即进制,然后再输入数据。
D = 91646299298871237857836940212608056141193465208586711901499120163393577626813
上代码:
1 | import gmpy2 |
https://blog.csdn.net/weixin_60553183/article/details/122084511
我得理一下,gmpy2是干嘛的。
下面是关于gmpy2的常见函数的使用
https://blog.csdn.net/weixin_43790779/article/details/108473984
powmod是求大整数x的y次幂m取余,
b‘suctf{Pwn*@*hundred_years}’
xor (异或)
首先用exeinfoPE判断一下
是64位的,没有加壳,然后用IDA 64位打开,找到main函数
然后就是分析主函数,输入v6,让v6和global作比较,由题目得知,如果如数v6的长度没有33,则直接跳转到LABLE_12的位置,输出Failed
然后就是分析伪代码
下面是脚本的编写:
1 | lists = ['f',0x0A,'k',0x0C,'w','&','O','.','@',0x11,\ |
将伪代码中的单个字符保持不动,连起来的字符分开,十六进制的在前面填上0x进制符号,并删除后面的h,然后放入列表lists
将列表中的int类型转换位char类型,用isinstance( x, type)方法来比较
将列表中的元素转换为字符串,如果这时输出,那么十六进制的那一块将转换为空格
然后将字符串的字符一次进行异或运算,最终输出得到flag
hello word(apk逆向)
写在后面的话:
apk反汇编推荐用jadx-gui和GDAE
–正文–
参考资料:https://www.zhihu.com/question/29370382
- 使用apktool
cmd+D,打开dos,找到apk的目录,然后键入
1 | apktool d 名字.apk |
即可。
解出以上资源
1 | CD 进入 |
但是我们需要的是.dex文件,所以此时应该再运行
1 | apktool d -s -f 名字.apk |
-s 不反编译dex文件,而是将其保留
-f 如果目标文件夹存在,则删除后重新反编译
d 反编译apk文件
将修改完的apk重新打包
1 | apktool b 目录名 -o newtest.apk |
-b build
-o 新的文件名
- 使用dex2jar工具
参考链接:https://www.jianshu.com/p/dbe579f6cc84
将apktool反汇编所得文件的classes.dex复制到dex2jar.bat所在目录
我的存放地址是:C:\Users\Lenovo\Desktop\dex2jar-2.0
然后在命令行中键入
1 | d2j-dex2jar classes.dex |
生成了一个class-dex2jar.jar文件,就是反编译后的java源码文件
- 使用jd-gui.exe运行生成的反编译的Java源码
然后用jd-gui.exe运行生成的class-dex2jar.jar文件,就可以看到源码了
reverse_3 (base64)
1 | if ( !strncmp(Dest, Str2, v6) ) |
strncmp()函数,str2与Dest作比较,最多比较前v6个字符串长度
结果为0,则两个字符串相等
1 | strncpy(Dest, v5, 40u); |
strncpy()函数,v5复制到Dest字符数组中,长度为40u
1 | memset(str,'$',7) |
memset()函数,将符号‘$’复制到str字符数组中,长度为7
根据大佬写的知乎,发现这个是base64的编码,好家伙,python两行搞完。
1 | import base64 |
Simple_Rev (ADI小端存储)
今天吃了个大亏
ADA里面的字符存储是小端存储
小端存储就是低地址存放到底位上,高地址存放到高位上。
以下是小端存储模式下的0x123456的存储,明显看到次序是颠倒的
所以,遇到这种直接把字符需要小心,先转换一下次序再说
首先我们测一下这个exe文件是否有壳
说句实话,我看不出来,貌似有一个压缩壳,然后是需要64位的Ada使用
打开之后看一下伪代码
主要是进Decry()函数看一下,进去之后就是新世界
这边就是主要算法了
这种的意思是字母大写转换为小写,key由大写转换为小写
通过循环上面的得知,
key = ‘adsfkndcls’ text = ‘killshadow’
看一下算法主题,就是一个while()循环,输入字符,如果碰到换行符直接退出。
1 | key = 'adsfkndcls' |
就是要把题目给理解透彻了才行
题目的意思是如果不是字母(大写小写皆可),就直接退出,如果是字母,就执行下面的转换,转换的里面还挺复杂的,所以直接就暴力枚举。
判断是不是字母还有点小绕
[GXYCTF2019]luck_guy (ADI小端存储)
1 | f1 = "GXY{do_not_" |
memset参考文档http://c.biancheng.net/view/231.html
memset()是初始化内存的万能函数,初始化之后,再向内存空间中存放需要的值,一般来说使用“0”初始化内存单元,通常给数组或结构体进行初始化,其实相当于结束标志‘\0’,最后面的参数是长度,一般专业一点会写sizeof(XX)
1 | int main() |
所以基础很重要啊!!!
首先我们来分析一下题目,
进入patch_me函数
再进入get_flag函数
1 | v7 = __readfsqword(40u); |
产生一个随机数,并进入switch结构,我们可以看出,按照4、5、1的顺序,就能求出flag,记得要注意ADI的小端优先
代码如下:
1 | flag = 'GXY{do_not_' |
flag{do_not_hate_me}
刮开有奖 (ASCII排序+base64)
首先查壳,分析一下是32位的程序,没有壳,所以用ida32打开
DialogFunc是一个函数,咱们跟进去
1 | if ( strlen(String) == 8 ) |
查看定义的字符的汇编代码
可以发现其实就是一连串的连续空间,就是一个字符数组
然后进入一个函数,看参数,第二个是0,第三个是10,既然看不懂,就直接把它改一改,放到vscode里面运行一下,因为我们知道它的参数了
1 | # include <stdio.h> |
不怎么接触c语言,昨天才知道,*(a+i)其实相当于数组a[i],a是基准地址,i是偏移量,后面我就知道了,这个函数的形参的第一个其实就是字符数组,之前定义的。
运行完成后,其实就是对定义的字符进行了排序,有ASCII从小到大排序
再看这边,String就是我们输入的数字
用v18这个数组来存储,然后把v18放置在函数中,第二个参数是长度
1 | v2 = a2 / 3; |
用z1r0的话就是盲猜是base64解码
这边会有移动,是base64的最明显的特征,把这个函数取名为base_64,后面还有一个base64的编码
这边就很简单了,注意,str1[0]的值是排序完成之后的值
ZJSECaNH3ng
3CEHJNSZagn
str1[0] = 3, string[0] = U
str4 = J, string[1] = J
string [3] = P
1 | a = 'Z' |
最后结合起来string,即flag{UJWP1jMp}
[BJDCTF2020]JustRE (水题)
既然程序看不懂,就找找字符列表中,有没有能看懂的,发现最后有个可疑字符
咱们点进去
内存中的数字直接按住tab是不能显示伪代码的,得点击函数才行
然后就是读一下程序
我认为上面的19999是鼠标点击的次数,点击这么多次后,然后就会弹出flag
sprintf函数,共有三个参数,第二个是格式,第一个是需要赋值的字符串,后面两个是被格式化的变量,所以flag{1999902069a45792d233ac}
简单注册器(apk逆向)
写在后面的话:
apk反汇编推荐用jadx-gui或GDAE
首先用apkide打开apk文件
打开apkIDE,然后把apk文件拖到右边(不是直接将apk拖入apkIDE)
等它自动转换后,找到输出目录
直接复制到地址栏跳转,找到MainActivity.class文件
- 最后将这个文件用jd-gui打开
so easy
然后就是阅读程序
1 | package com.example.flag; |
字符数组转换一下,然后再反向一下
1 | string = "dd2940c04462b4dd7c450528835cca15" |
flag{“59acc538825054c7de4b26440c0999dd”}
findit(apk逆向)
写在后面的话:
apk反汇编推荐用jadx-gui或GDAE
–正文–
好家伙,做一个安卓题目就新换一个工具,这次用的是安卓杀手,
z1r0的博客里说,{ 的ascii码是123,十六进制是0x7b,
1 | texts = [0x70,0x76,0x6b,0x71,0x7b,0x6d,0x31,0x36,0x34,0x36, 0x37, 0x35,\ |
pvkq{m164675262033l4m49lnp7p9mnk28k75}
说实话结果很像了,
然后就是凯撒加密,偏移量为10,解密之后就是:
flag{c164675262033b4c49bdf7f9cda28a75}
pyre(%的逆推)
在线反汇编pyr
然后分析一下代码
1 | #!/usr/bin/env python |
最后输出的code已经给出了
首先是异或一下,从最后一个元素开始
for i in range(l-2,-1,-1),倒数第二个元素和最后一个元素异或,以此类推
然后就是关于%~~公式的逆向了
关于z1r0的分析,就是以上这样,反过来就是(input[i] - i)%128
1 | code = [ |
[ACTF新生赛2020]easyre(index的推导)
参考链接:https://www.cnblogs.com/Mayfly-nymph/p/12664201.html
先是查壳,然后万能脱壳工具脱一下,然后放到ADI中看看
好家伙,7.5版本和7.0版本是打开是不一样的
1 | 1 int __cdecl main(int argc, const char **argv, const char **envp) |
这多清晰。。。
感谢z1r0的指导!
所以第50行相当于,v4[i] != byte_402000[v16[i] - 1]
以下是python脚本
1 | key = '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(\'&%$# !"' #'一定要加\ |
仔细想了一下,用公式推一下就好理解了:
1 | v4[i] == key [v16[i] - 1] |
v16这个数组就相当于是flag,我们需要做的就是逆着推一下。
flag{U9X_1S_W6@T?}
rsa (非对称解密)
非常6+1的重要!!
参考链接:
https://blog.csdn.net/ao52426055/article/details/110221943
https://blog.csdn.net/kevin66654/article/details/54087647
首先直接把两个文件的后缀改成.txt的
可以看到pub是个公钥解析,我们放到随便一个RSA公钥分解的网站中分解,解除模数和指数
key长度: | 256 |
---|---|
模数N: | 8693448229604811919066606200349480058890565601720302561721665405 8378322103517 |
指数e: | 65537 (0x10001) |
然后分解N
[http://factordb.com/index.php?query=8693448229604811919066606200349480058890565601720302561721665405%208378322103517](http://factordb.com/index.php?query=8693448229604811919066606200349480058890565601720302561721665405 8378322103517)
P:285960468890451637935629440372639283459
q: 304008741604601924494328155975272418463
得知p、q、r、e之后,直接用rsatool2解出d
81176168860169991027846870170527607562179635470395365333547868786951080991441
算出来之后
通过这五个值,e、n、p、q、d来计算最后的值
记得要安装一下rsa库
dos直接键入: pip3 install rsa
上代码:
1 | import rsa |
b’flag{decrypt_256}\n’
[ACTF新生赛2020]rome(ASCII暴力破解)
1 | int func() |
在检查没有壳之后,直接IDA打开,然后进入这个函数,其实就是输入一段字符之后,进行一系列的转换,根据z1r0的知乎得知,可以运用暴力破解(太棒了)!
1 | string = 'Qsw3sj_lz4_Ujw@l' |
字符已经给出
循环这个这个字符串,用变量k来保存j的值,j来循环ASCII码,如果j经过转换之后,与对应的string相等,那就是flag了。
CrackRTF1 (散列算法+Win32API+文件头)
这题对于我这种re新手,确实属于难度的天花板了……
首先是查壳,发现没有壳。
1 | int __cdecl main_0(int argc, const char **argv, const char **envp) |
又触碰盲区了,再参考一下大佬z1r0的博客:
https://www.zhihu.com/people/xu-xu-xuxu-94
这边先是输入第一个password,
1 | scanf("%s", Destination); |
然后就要当心了
1 | v7 = atoi(Destination); |
根据z1r0所说,atoi在pawnh中很常见,用于将字符串转换为整形
1 | if ( v7 < 100000 ) |
所以我们确定了第一个password的位数是6位,并且不小于100000
1 | strcat(Destination, "@DBApp"); |
然后是与“@DBApp”连接
1 | strcat(Destination, "@DBApp"); |
进入了一个奇奇怪怪的函数,不要害怕
1 | if ( CryptCreateHash(phProv, 0x8004u, 0, 0, &phHash) ) |
直接进大本营干这个函数,第二个参数是属于加密的类型,跟进去看
发现,08004是sha1哈希算法,就把main函数的里面的改成sha1(改名很重要)
1 | sha1(Destination, v3, String1); |
v3是长度,String1就是加密之后的数值了,然后与”6E32D0943418C2C33385BC35A1470250DD8923A9”作比较,python里面需要小写才行
然后就是编写算法
1 | import hashlib |
解出来之后就是123321@DBApp
在往下看,输入第二个密码,既然知道是第二个密码了,就提前把str改成second_password
1 | printf("pls input the first passwd(2): "); |
长度也是6位
1 | strcat(second_password, Destination); |
把第一个密码的值加到第二个密码后面
1 | sub_401019(second_password, v4, String1); |
又进入了一个奇奇怪怪的函数,和上面一个差不多,加密之后与”27019e688a4e62a649fd99cadaafdb4e”进行比较
1 | if ( CryptCreateHash(phProv, 0x8003u, 0, 0, &phHash) ) |
这边的编码参数是0x8003,这个是MD5的哈希算法,看似可以直接暴力,但是,题目中并没有像第一个password一样,告诉我们类型以及范围,如果要暴力,就需要把ASCII全部循环一遍,所以还是想想别的办法。
发现这边还有函数,参数还是第二个密码
进去直接好家伙,一堆win32的API,在官网上面找找到底是什么意思。
官方API链接:https://docs.microsoft.com/en-us/windows/win32/api/
先是FindResourceA 函数,粗略的看一遍之后,说的第一个参数是啥啥句柄,第二个参数是资源名称,第三个是资源类型,返回的是指定信息块的句柄
再看SizeofResource,返回值为资源中的字节数
同样的,LoadResource,没见过,最好都看看
返回值为资源关联的数据的句柄。
再看LockResource
返回值为指向资源第一个字节的指针
createFileA,用来创建,或者打开文件
最重要的来了,关于WriteFileA的
还有它的形参:
1 | sub_401005(lpString, lpBuffer, nNumberOfBytesToWrite); |
这个函数把我们的总password放入第一个形参,lpBuffer是一个指针,
1 | v5 = lstrlenA(lpString); |
现在就是这关键的地方了
a2也就是我们的lpBuffer,经过异或之后,成为我们需要生成文件,即rtf的头,才能传入下面WriteFile中,根据z1r0的博客所说,rtf的头一般为{\rtf1\ansi,
而AAA这个文件我们可以用Resource Hacker打开,只要查看开头6个就行了
然后就是匹配算法
1 | rtf = '{\\rtf1' |
结果为:~!3a@0
程序的意思是输入第二个密码之后,生成一个rtf文件,最后输出的rtf中,就是最终的答案
如你所见,flag{N0_M0re_Free_Bugs}
login (JS逆向/凯撒)
第一次做网页的逆向题目,有点激动,F12进入源代码查看
哪里不会搜哪里,返回一个element对象??
好吧,只是键入最基本的按钮值而已
主要是下面的rotFlag
1 | <script type="text/javascript"> |
charCodeAt(),返回第一个位置的Unicode编码,
再看看replace()方法,如果找到第一个参数的字符串,就被第二个参数替换
fromCharCode()相当于python里面的chr()
参考链接:https://www.zhihu.com/people/xu-xu-xuxu-94
后面不太懂了,根据z1r0的知乎中写到,flag.replace(/[a-zA-Z]/g,function(c){XXX}),是让flag中的a-z,A-Z字符,被后面的function(c)所替换。
后面的算法就是rot13算法了: