攻防世界 Reverse 新手区 wp

it2024-08-11  36

学习逆向也有一段时间了 也是对于这段时间的一个总结

准备

正所谓工欲善其事必先利其器,逆向之前要准备好我们的家伙什儿

IDA

链接:https://pan.baidu.com/s/1tCijS4s_g6KFwV9ycCPVkw 提取码:nd46 反编译工具,还有各种插件实现不同功能,主要应用于静态分析

Ollydbg

吾爱破解专用版就不戳,可能会报毒,一般破解软件会报毒 动态分析神器 爱盘

dnSpy

.NET逆向

jd-dui

.jar文件逆向

exeinfope

软件查壳

linux环境

逆向分析中较多elf文件需要在linux环境下调试分析,可以gdb动态调试elf,也可以配合IDA进行动态调试

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}

最新回复(0)