参赛ID:1K0CT
Shift_jmp | 简单花指令
将无条件跳转和loc_117A
patch为nop
重构主函数
简单的异或加密 解题Python脚本如下
1 | key = [83, 88, 65, 120, 83, 54, 106, 100, 56, 100, |
点击就送的逆向题 | C源码的编译
.s
文件是由C源码编译而来的汇编指令 它与.S
文件的区别是它不支持预处理(#define…) gcc编译器可以将其经过链接等再编译为可执行文件 故可以先将.s
文件编译为可执行文件再用IDA分析
clang click.s -o click
Kali虚拟机预装的gcc编译器用以上指令可以直接将.s
文件编译为Linux可执行文件
简单的位移加密 解题Python脚本如下
1 | key = 'Z`J[X^LMNO`PPJPVQRSIUTJ]IMNOZKMM' |
Easymath | z3-solver简单使用
Exinfo查到无壳 64位win程序 IDA64打开
- check函数逻辑简单 不分析(
- checkposition函数逻辑也简单 不分析(
- exchange函数
不理解 26行之前的代码作用 但是并不影响分析(
number在内存空间中每个有效元素之间插入三个0
与代码中的p_number[i] = number_[4 * v10]
对应 最后一个循环的作用就是将字符在table中的位置映射到这些有效元素上
回到主函数 主要的比较逻辑就是经过映射后的position
作为一个5阶方阵与另一个5阶方阵matrix
用特殊的矩阵乘法法则相乘得到的矩阵与单位矩阵相比 以下是Python代码实现 其中n
为矩阵的阶数
1 | for i in range(n): |
除此之外主函数中还能看到为了防止出现多解给出的flag提示(动态调试时发现其为flag中的元素 重构为数组可以将变量名从内存地址变为数组) 可以使用爆破的方式解出flag 也可以使用z3-solver库解出
1 | from z3 import * |
需要注意的是 可以一次性爆破全部不确定的22个元素 但这样会使复杂度提升到29^22(我猜的) 而每一次运算只用到position
中的一行元素 故可以一行一行地爆破 可以将复杂度降低到5*29^5(我猜的)所以选择一行一行爆破(z3学习笔记另写)
luck | 简单爆破
Exinfo查到无壳 64位win程序 IDA64打开
主函数定义了一个unsigned __int8
类型地数组 这个数据类型的大小和char
一样 qmemcpy(cmp_data, "\r\a", 2);
这句实际上只将"\r\a"
中地前两个字符复制到了cmp_data
的末尾 数组名代表首元素地址 所以
cmp_data[0] = ‘\‘
cmp_data[1] = ‘r’
同理
将"o96*#"
添加到了cmp_data
的第37个元素及之后
(其实最简单的方式是动态调试直接查看cmp_data中的数据)
主函数中的加密函数为用递归实现的等差数列求和
不管输入的数字是什么都会输出加密后的密文 根据flag前3个固定的字符爆破flag
1 | key = [13, 7, 29] |
在程序中输入数字得到flag
砍树 | 安卓逆向
主函数的逻辑很简单
就是调用一个函数传入输入的字符串和key 判断返回值 这个函数在ezreeee
库中 导出这个库用IDA分析
在函数列表中检索I0o0I
函数 传入的参数有4个 其中有2个是JNIEnv
和jobject
JNI是为了让JAVA和C/C++互通构建的环境 jxxxxx是为了不与C原有的类型冲突 所以从下面的2个jstring_2unsigchar
函数也可以看出a3
和a4
分别为jeb伪代码中看到的两个参数与加密后的输入对比的是从内存0x14900中复制的35个字节型数据 以下是解密脚本
1 | key = [0, 32, 32, 23, 27, 54, 14, 54, 38, 23, |
听说cpp很难? | C++逆向
Exinfo查到无壳 64位win程序 IDA64打开 伪代码如下
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
分析C++逆向时XXXXXXXoperatorxxx(a, b)
可以看作a
对b
做xxx
操作
其中的主要逻辑是先对be_encoded进行encode
操作 进入查看
需要注意的是主函数中定义的be_encoded
储存的数据长度为1byte 而C++中的双字类型长度为4bytes 所以其中的下标转化到原来的be_encoded
中需要乘上4 text_67
的逻辑很简单
1 | __int64 __fastcall text_67(__int64 be_encoded, char now_char) |
剩下的对比逻辑也很简单 以下是解密脚本
1 | enc = [None] * 33 |
flower-or-tea | 简单花指令和TEA加密
Exinfo查到无壳 32位win程序 IDA32打开 伪代码如下
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
核心逻辑就是输入的数据加密后调换顺序并和v16
比较 这个加密算法(sub_CA10C3
)就是TEA加密 特点是没有数据丢失 可以逆向
需要注意的是传入的第二个参数是主函数中的&v18
打开v18
栈中的位置 发现v19
就是紧挨其的下一个
所以v5 = now_char[1];
这句实际上取的是v19
另外一点需要注意的是数据的上限 unsigned int
类型的数据最多储存4bytes 故每次执行都需要截去高32位
知道这些后就可以写出以下解密脚本
1 | from z3 import * |
rainbow | 控制流平坦化
关于去控制流平坦化参考 https://security.tencent.com/index.php/blog/msg/112
这里用别人的工具脚本去平坦化 去平坦化后的代码如下
由于判断无用块的方法是排除法(排除掉序言块,分发器,预处理块和返回块) 而原程序中退出并不使用return
而是call exit
所以被判定为无用块被删除 故判断长度和flag不正确就退出的块被删去 观察原来的代码 剩下的代码就是加密后的flag和v8~v11
的数据进行对比 解密脚本如下
1 | key = [101, 88, 65, 142, 80, 68, 123, 98, 87, 74, |
mySelf | 简单SMC
Exinfo查到无壳 32位win程序 IDA32打开
VirtualProtect
函数给予一段内存地址(函数)修改自身字节码的权限 基本上是SMC的标志
如果不使用静态分析的方法可以直接动态调试 这样的可操作性高 在SMC的函数下断点 动态调试到断点处 F7步进函数 之后查看汇编代码 将SMC后的函数重新识别为函数再反编译为伪代码分析
1 | unsigned int __cdecl encode(int a1) |
又是一个TEA加密 但是其中用到了一个伪随机数 需要动态调试获得 这个TEA加密和上一题中的不同点是每次被加密的字符顺序不同 同时也要注意每次加密的数据长度是4bytes 而原来传入的是以1byte分割的数组的地址 而在x86程序中双字型数据使用小端序储存 知道了这些之后写出解密脚本
1 | o_key = [0xF0, 0xF9, 0xBD, 0xBD, 0xC4, 0x94, 0x61, 0xE2, 0x25, 0x91, |
小黄鸭 | Python逆向
Exinfo 查到为Python3.7写的程序 可以先用pyinstxtractor将其反汇编生成.pyc
文件 再用uncompyle6进行反编译生成.py
文件
1 | python pyinstxtractor.py duck.exe |
没有进行混淆
1 | # --------duck.py---------- |
简单的ROT13加密 写出脚本解密
1 | key_o = '~h|p4gs`gJdN`thPwR`jDn`te1w`2|RNH' |
浪漫至死不渝 | JS逆向
源码对变量名进行了混淆 去除混淆并对网页元素进行删去 得到主要逻辑
1 | let k={ |
主要是先用栅栏密码对key进行加密 然后对输入逐位字符进行加密 写出解密脚本
1 | key = [125, 130, 131, 122, 117, 110, 123, 125, 130, 131, 122, 117, 110, 123, 99, 99, 99, 99] |
寻找初音未来 | Go语言逆向 | 一坨go史
go语言所有的字符串在内存上都是连续的 所以对一个字符串对应的长度和偏移量非常重要
附件放到虚拟机中打开 发现提示输入初音未来色 上网搜索知道是39C5BB
用IDA打开 发现这个输入通过某些加密算法得到key
再用该key进行RC4加密直接动态调试得到key为CCCCCCCCCCCCCCCCCC
用python内置的RC4解密算法进行解密
1 | from Crypto.Cipher import ARC4 |
AES! AES? | AES算法魔改
Exinfo查到无壳 64位win程序 IDA64打开 伪代码中看到核心加密逻辑是Shiftrow()
和transform()
伪代码如下
1 | void __fastcall ShiftRow(__int64 flag) |
据此写出解密脚本
1 | S = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, |
另外 不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key
ezandroid | 安卓逆向
apk用jeb打开 有两个主要活动如下
1 | package com.example.babyapk_1; |
当MainActivity1中的匹配通过后才会进行MainActivity2的另一部分加密 主要是分奇偶索引进行TEA或异或加密 据此写出解密脚本
1 |
|
是男人就来扎针 | Unity引擎游戏逆向
Unity引擎游戏的控制逻辑一般存放在.\*_Data\Managed\Assembly-CSharp.dll
中 用dnspy打开 主要逻辑如下
1 | using System; |
可以看到游戏前30次每刷新一次分数就会与明文进行异或 最后进行md5加密然后在分数达到100时显示 显然不可能点到100 但是可以先点到30 再用CE改到100 写出解密脚本
1 | import hashlib |
babycode | 不知道什么加密
IDA64打开 伪代码如下 其中两个简单加密算法不展示
1 | __int64 __fastcall main(int a1, char **a2, char **a3) |
核心加密是一个base64的换表 要注意的是其中的key动态调试出来并不正确 因为程序会检测动态调试改变key的初值
1 | .init_array:000055B72E248D80 _init_array segment qword public 'DATA' use64 |
所以用key在(1, 31)
且加密后密文都在表中爆破出key为21 据此写出解密脚本
1 | ![image-20231128164528686](C:\Users\Orink\AppData\Roaming\Typora\typora-user-images\image-20231128164528686.png)def emulator(key:list, time:int): |
yakvm | 虚拟机逆向
main为生成的虚拟机 IDA打开发现是无符号go 用go paraser恢复符号 搜索有关操作码的函数
发现有一个ShowOpcodes函数 但 调试时不进过该函数 交叉引用找到调用它的函数
在判断处下断点 调试时通过改ZF寄存器步进ShowOpcodes函数 得到可读操作码 根据官方文档分析如下
1 | 2:6->2:9 0:OP:type byte |
据此写出解密脚本
1 | key = [137, 108, 159, 114, 185, 90, 174, 68, 160, 81, 179, 41, 186, 89, 168, 78, 229, 121, 149, 106, 147, 103, 156, 114, 133, 98, 146, 116, 181] |