学习逆向也有一段时间了 也是对于这段时间的一个总结
准备
正所谓工欲善其事必先利其器,逆向之前要准备好我们的家伙什儿
IDA
链接:https://pan.baidu.com/s/1tCijS4s_g6KFwV9ycCPVkw
提取码:nd46
反编译工具,还有各种插件实现不同功能,主要应用于静态分析
Ollydbg
吾爱破解专用版就不戳,可能会报毒,一般破解软件会报毒
动态分析神器
爱盘
dnSpy
.NET逆向
jd-dui
.jar文件逆向
exeinfope
软件查壳
linux环境
逆向分析中较多elf文件需要在linux环境下调试分析,可以gdb动态调试elf,也可以配合IDA进行动态调试
.py文件逆向
.py逆向可以通过线上反编译
.py文件在线反编译
正文
01 open-source
题目叫开源,就真开源呗,附件.c文件
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("what?\n");
exit(1);
}
unsigned int first = atoi(argv[1]);
if (first != 0xcafe) {
//first == 0xcafe 跳过
printf("you are wrong, sorry.\n");
exit(2);
}
unsigned int second = atoi(argv[2]);
if (second % 5 == 3 || second % 17 != 8) {
//second % 17 == 8 && second % 5 != 3
printf("ha, you won't get it!\n");
exit(3);
}
if (strcmp("h4cky0u", argv[3])) {
//argv[3] == h4cky0u len == 7
printf("so close, dude!\n");
exit(4);
}
printf("Brr wrrr grr\n");
unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;
printf("Get your key: ");
printf("%x\n", hash);
return 0;
}
代码审计
经过三个判断输出flag(hash值)
int first = 0xcafe;
unsigned int hash = first * 31337 + 8 * 11 + 7 - 1615810207;
printf("%x\n", hash);
c0ffee
02 simple-unpack
查壳,upx壳,还是elf文件,kali命令脱壳
IDA载入
开幕雷击
flag{Upx_1s_n0t_a_d3liv3r_c0mp4ny}
03 logmein
IDA
找到main f5反汇编
4007C0函数输出密码错误
脚本输出flag
eg.大数v7代码中8个字节拆分为8份调用
string a = ":\"AL_RT^L*.?+6/46";
long long v7 = 28537194573619560LL;
for (int i = 0; i < a.size(); i++)
{
cout << (char)(*((char*)&v7 + i % 7) ^ a[i]);
}
RC3-2016-XORISGUD
04 insanity
IDA
生成随机数
先查看strs值
9447{This_is_a_flag}
这就是flag。。。。
hhhh
05 getit
直接IDA开干
f5 反汇编
直接看到文件操作 生成文件名为flag.txt 大胆猜测应该是经过操作在文件中生成flag
分析各种字符串
可知
s = { "c61b68366edeb7bdce3c6820314b7498" };
t = { "SharifCTF{????????????????????????????????}" }
u = { "*******************************************" }
分析一开始的循环
脚本得到处理之后的字符串t
char s[100] = { "c61b68366edeb7bdce3c6820314b7498" };
char t[100] = { "SharifCTF{????????????????????????????????}" }
for (int i = 0; i < s.length(); i++)
{
if (i & 1)
v3 = 1;
else
v3 = -1;
t[i+10] = (char)(s[(signed int)i] + v3);
}
得到新的字符串t
char t[100] = { "SharifCTF{b70c59275fcfa8aebf2d5911223c6589}" };
对于int 数组 p[43] 观察到汇编代码中以十六进制四字节存储 组成p[43]
int p[43] = {30,24,25,32,40,36,28,17,34,39,16,33,19,26,5,3,29,27,31,4,8,15,37,42,14,41,2,23,21,0,10,20,7,11,1,13,6,38,18,35,12,22,9 };
文件操作直接脚本
#include <bits/stdc++.h>
using namespace std;
int main()
{
int v3;
char s[100] = { "c61b68366edeb7bdce3c6820314b7498" };
char b[100] = { "b70c59275fcfa8aebf2d5911223c6589" };
char u[100] = { "*******************************************" };
char t[100] = { "SharifCTF{b70c59275fcfa8aebf2d5911223c6589}" };
int p[43] = {30,24,25,32,40,36,28,17,34,39,16,33,19,26,5,3,29,27,31,4,8,15,37,42,14,41,2,23,21,0,10,20,7,11,1,13,6,38,18,35,12,22,9 };
FILE * stream;
char * filename;
filename = new char;
strcpy(filename, "flag.txt");//直接源目录生成文件
stream = fopen(filename, "w");//打开文件
fprintf(stream, "%s\n", u, strlen(s));//以字符串u初始化文件
for (int i = 0; i < strlen(t); ++i)
{
fseek(stream, p[i], 0);//转移文件指针至一定位置
fputc((char)*(t + p[i]),stream);//修改转移后文件位置的字符
//以下代码将flag再次初始化为u 迷惑操作 注释掉
//fseek(stream, 0LL, 0);
//fprintf(stream, "%s\n", u);
}
fclose(stream);
//remove删掉flag文件 注释掉
//remove(filename);
return 0;
}
奥里给
SharifCTF{b70c59275fcfa8aebf2d5911223c6589}
06 python-trade
.py在线反编译
https://tool.lu/pyc/
简单算法逆向
python脚本
import base64
correct = "XlNkVmtUI1MgXWBZXCFeKY+AaXNt"
flag = ''
correct = base64.b64decode(correct)
for i in correct:
s = i
s -= 16
flag += chr(s ^ 32)
print(flag)
nctf{d3c0mpil1n9_PyC}
07 re1
载入IDA分析
f5反汇编main函数
v3 = strcmp((const char *)&v5, &v9);
重要判断条件 比较v5字符串和v9字符串
双击进入unk_413E90函数显示字符串get flag
v5应与v9相同
_mm_storeu_si128((__m128i *)&v5, _mm_loadu_si128((const __m128i *)&byte_413E34));
作用类似于strcpy,将413E34值赋给v5
但是由于字符串结束判断条件为\0字符 即值为0的字节
而第一个0出现在413E44结尾,故整个判断字符串v5为DUTCTF{We1c0met0DUTCTF}
即为flag
eg. 关于data数据中可以通过快捷键 ‘r’ 将16进制数转换为字符
08 game
打开程序发现是一个关于开灯的游戏
输入不同的数字N可以改变第N排的灯,但是其周围的灯也会受到影响,可以通过分析得到答案(hhh)
既然不是输入key 那么就可以动调出答案
ida先瞅一眼
直接看main函数下面的关键判断,推断457AB4为结果
双击进入查看
发现关键字符串,也可通过Ctrl+T搜索字符串发现
并且可以发现,下面就是flag生成过程
这样静态分析也可以咯
静态
两个异或 上面的一大堆数据相当于两个数组 变量在内存中顺序排列,所以*(&v2+i)就可以遍历v2下面的一系列变量的值 v59同理
脚本
a=[18,64,98,5,2,4,6,3,6,48,49,65,32,12,48,65,31,78,62,32,49,32,1,57,96,3,21,9,4,62,3,5,4,1,2,3,44,65,78,32,16,97,54,16,44,52,32,64,89,45,32,65,15,34,18,16,0]
# v59-v115
b=[123,32,18,98,119,108,65,41,124,80,125,38,124,111,74,49,83,108,94,108,84,6,96,83,44,121,104,110,32,95,117,101,99,123,127,119,96,48,107,71,92,29,81,107,90,85,64,12,43,76,86,13,114,1,117,126,0]
# v2-v58
flag = ''
for i in range(56):
b[i] ^= a[i]
flag += chr(b[i]^0x13)
print(flag)
zsctf{T9is_tOpic_1s_v5ry_int7resting_b6t_others_are_n0t}
动态
祭出神器OD
回顾一下最后判断条件的部分
可以看到在判断条件之前出现字符串CLS 可以通过这个字符串入手找到判断跳转
主线程右键中文搜索引擎智能搜索 进入字符串界面 右键Find查找CLS
双击跟进
IDA和OD的虚拟地址后几位都是一样的
所以将参数"CLS"推进栈后调用的00F581B7函数就是j_system函数
下面一连串jnz判断跳转 最后call 输出flag的函数00F57AB4
直接跳过jnz jmp到00F57AB4
运行
zsctf{T9is_tOpic_1s_v5ry_int7resting_b6t_others_are_n0t}
09 Hello, CTF
载入IDA main函数F5反编译
简单的算法 将输入的字符串转化为16进制码与v13比较
在线转码
CrackMeJustForFun
10 no-strings-attached
elf文件 可能面临linux运行调试
IDA瞅瞅
main函数里面 这个authenticate是主要函数
函数还是比较简单的 虽然有一些wchar_t这样的陌生的数据形式 但是基本能猜到
s2为加密后的字符串 输入字符串和其比较
看看参数 s 和 8A90里面是什么
大概猜到这种类型数据占用4个Byte 真正存储数据的只有第一个Byte 因为其余三个Byte数据要么重复 要么没有价值
先用IDC内置脚本把s串内容搞出来
auto i;
for(i=0;;i++)
{
Message("0x%x,",Byte(0x8048AA8+4*i));
if(!Byte(0x8048AA8+4*i))
break;
}
//shift+F2 打开IDA脚本编辑界面 IDA提供IDA(类C)和python两种语言
s = [0x3a,0x36,0x37,0x3b,0x80,0x7a,0x71,0x78,0x63,0x66,0x73,0x67,0x62,0x65,0x73,0x60,0x6b,0x71,0x78,0x6a,0x73,0x70,0x64,0x78,0x6e,0x70,0x70,0x64,0x70,0x64,0x6e,0x7b,0x76,0x78,0x6a,0x73,0x7b,0x80,0x0,]
再看看decrypt
可以看到算法很简单,但是a2参数只有5个字符,而i越界,两个参数在内存中连续储存,所以a2越界后取到s字符串的值,相当于a2 = a2 + s
脚本
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> s = { 0x3a,0x36,0x37,0x3b,0x80,0x7a,0x71,0x78,0x63,0x66,0x73,0x67,0x62,0x65,0x73,0x60,0x6b,0x71,0x78,0x6a,0x73,0x70,0x64,0x78,0x6e,0x70,0x70,0x64,0x70,0x64,0x6e,0x7b,0x76,0x78,0x6a,0x73,0x7b,0x80,0x0 };
vector<int> a2 = { 0x1,0x2,0x3,0x4,0x5,0x0,0x3a,0x36,0x37,0x3b,0x80,0x7a,0x71,0x78,0x63,0x66,0x73,0x67,0x62,0x65,0x73,0x60,0x6b,0x71,0x78,0x6a,0x73,0x70,0x64,0x78,0x6e,0x70,0x70,0x64,0x70,0x64,0x6e,0x7b,0x76,0x78,0x6a,0x73,0x7b,0x80,0x0 };
int v4, v6, v7, i;
v4 = 0;
v6 = s.size();
v7 = 5;
while (v4 < v6)
{
for (i = 0; i < v7 && v4 < v6; ++i)
s[v4++] -= a2[i];
}
for (i = 0; i < s.size(); i++)
cout << (char)s[i];
}
9447{you_are_an_international_mystery}
11 csaw2013reversing2
程序先进IDA看看
可见开了一段堆空间 sub_40102A函数永返回0
如果是动态debug时会进入循环结束进程
lpMem中存储乱码数据
经过sub_401000函数生成flag
但是没有生成对话框显示flag
不是debug时出现对话框显示flag
程序跑一下看看
输出乱码
动态调试 尝试运行401000函数后跳入对话框显示flag
开始是一个函数 F7步入
在这个函数出现对话框 下断点重新载入F7步入
步入继续运行看到停在MessageBox函数处
单步运行 找到判断条件IsDebuggerPresent()和102A函数
继续单步运行发现下面的跳转已实现 直接跳到MessageBox参数进栈时
所以要让跳转不实现 nop掉该je跳转 而下面的int3中断了程序 也nop掉
继续运行进程会直接结束 所以在函数结束时跳转至原MessageBox处
flag{reversing_is_not_that_hard!}
12 maze
maze是迷宫的意思 所以这道题多少跟走迷宫沾点边
IDA看一看
进入main函数 首先看到输入后的第一个判断条件
长度必须是24而且以nctf{}为格式
下面是一波操作 先往后稍稍 送结果看起
看到一个数组的值不等于35(’#’)的话就跳转到Wrong Flag
从结构来看
- 8 * v10 + SHIWORD(v10)
有点像二维数组用一维数组储存的取值方法
对应题目迷宫 可能出现一个8*8的地图
看下asc_601060的值
一个64位数组 随便写个脚本弄出地图
可以看到要从左上走到#
右下右右下下左下下下右右右右上上左左
a = " ******* * **** * **** * *** *# *** *** *** *********"
index = 0
for i in range(8):
f = ''
for i in range(8):
f += a[index]
index += 1
print(f)
******
* * *
*** * **
** * **
* *# *
** *** *
** *
********
再向上分析这个迷宫是怎么走的
上面有四个分支
有Oo.0四种字符
可以猜测对应上下左右了
再看看对应函数确认一下
Oo.0分别对应 左右上下
nctf{o0oo00O000oooo…OO}