January 22, 2022

浅谈CTF逆向常见题型--P.Z

前言

这节课,不管你是有了一定基础还是小白,都可以听,同时有一定基础不妨听听别人做题思路,一种题肯定不止一种解法,每个你都是新的脑回路!

什么是逆向,在CTF中,我们的目标就是找到出题人藏起来的flag,通过解密出题的人的正向,以此来得到flag,CTF中目标明确,就是弄清出题人的思路和考点,以此来完成题目。

语言的由来

现在说下简单谈下机器码和汇编和C的关系,简单叙述下,这个十分重要,划重点,汇编和C就是逆向的根,一切都是从汇编开始,就是我们一开始的机器码再到汇编再到C语言。

机器语言

首先,机器码,机器码是什么,其实就是01010101010,一连串的01010010就是一串指令,刚开始的指令,都是有本手册,用计算机的都是科学家,他们就是这样使用计算机的,而我们现在也经常需要看。

就比如你们都应该刷过杂项题,都会看到文件格式,那么就比如我们的一个exe文件,其实就是都是16进制。

image-20220122133320771

汇编语言

接着,汇编语言,一串0101010太麻烦了啊,于是就想着弄点人能懂的,那么现在我们假设0101010是加,那么在汇编语言中 0101010 = add,那么这样表达的语言就叫汇编语言。

机器语言对应的汇编语言

image-20220122133507893

C语言

于是最终,好几个汇编语言构成一个动作,就成了C语言。

诸如此类

image-20220122133635501

工具概览

本节课所用到的工具:

IDA

反汇编软件(转汇编语言到C语言),逆向中主要使用工具

image-20220122135403070

DBG

动调常用工具

image-20220122135650731

DIE

日常查壳

image-20220122135707846

签到类

直接来实战,不多扯,首先讲下签到题,就算你之后的方向不是逆向,听完像一些真签到题在逆向中也能做。

举例题:Buuctf-easyre和2021ZJCTF决赛逆向签到题

Buuctf easyre

0x00 日常查壳

无壳64位

image-20220122141105282

0x01 分析主函数

通过左侧这个框找到main函数

scanf是输入函数,于是来到两个数判断是否相等(那么这题考点其实就是看你数据判断)

image-20220122141211107

0x02 GetFlag!

发现逻辑是判断第一个数是否和第二个数相等

image-20220122140953681

2021 ZJCTF决赛逆向签到题

0x00 日常查壳

无壳64位

image-20220122141355209

0x01 分析主函数

image-20220122154529081

0x02 GetFlag!

那么我们把enc里的数据拿出来,全部减50就可以拿到我们的input

1
2
3
4
enc = [0x76, 0x73, 0x85, 0x75, 0x86, 0x78, 0xAD, 0x6B, 0x97, 0x68, 0x98, 0x67, 0x64, 0x64, 0x62, 0x97, 0x68, 0x98, 0x6B, 0x6B, 0x96, 0x67, 0x62, 0x69, 0x95, 0x96, 0x65, 0x96, 0x6A, 0x69, 0x69, 0x65, 0x66, 0x97, 0x68, 0x98, 0x6A, 0x95, 0x68, 0xAF]

for i in range(0, len(enc)):
print(chr(enc[i] - 50), end = "")

GetFlag!

image-20220122154832273

加壳类

加壳,有压缩壳和加密壳,这节课简单讲讲压缩壳

简单看几眼这张图

image-20220122152655468

Buuctf 新年快乐

0x00 日常查壳

upx壳 32位

image-20220123123642658

有三种脱壳方法

upx软件直接脱

注意原文件会给覆盖

image-20220123123906479

异或类

异或口诀,相同为0,不同为1

例如两个数,5和11,那么如何相同为0,不同为1呢

意为他们的转成二进制

5: 0101

11: 1011

14: 1110

对应位相同为0不同为1

注意异或是可逆的,自己把5 11 14三个数据相同为0不同为1,代一代就会很明白

Buuctf Xor

0x00 日常查壳

无壳64位

image-20220122210828333

0x01 分析主函数

一个异或加密

input[1] ^= input[0]

input[2] ^= einput[1]

input[3] ^= einput[2]

input[32] ^= einput[31]

每一位都是异或了前一位,

那么原来的input[32]怎么获得?就是再异或一遍einput[31],整条从后往前即可

image-20220122151503453

0x02 GetFlag

于是写个从后往前异或的脚本,注意第一位不用

1
2
3
4
5
6
7
ef = "660A6B0C77264F2E4011780D5A3B55117019461F76224D23440E6706680F47324F00"
ef = bytearray.fromhex(ef)

for i in range(len(ef) - 1, 0, -1):
ef[i] ^= ef[i - 1]
for i in range(0, len(ef)):
print(chr(ef[i]), end = "")

GetFlag!

image-20220122212158338

PZCTF Xor

0x00 日常查壳

无壳64位

image-20220123010153514

0x01 分析主函数

就是分组异或

当下标为0 1 2 3什么都不异或

当到4 5 6 7就都异或input[0]

当到8 9 10 11就都异或input[0] input[1]

以此类推

image-20220123010347320

0x02 GetFlag!

思路:分段异或,那么逆回去,也是从尾到头再异或一遍即可

1
2
3
4
5
6
7
8
9
10
11
s = [0x50, 0x5a, 0x43, 0x54, 0x16, 0x2b, 0x11, 0xf, 0x3b, 0x63,0x7e, 0x7e, 0x78, 0x2c, 0x16, 0x3a, 0x71, 0x2e, 0x2e, 0x6d, 0x72, 0x76]

for i in range(len(s) - 1, -1, -1):
# for j in range(int(i / 4) - 1, -1, -1): #不管是先异或 a b c还是 c b a都可以
for j in range(int(i / 4)):
s[i] ^= s[j]

for i in range(len(s)):
print(chr(s[i]), end = "")
# 当一个数异或多个数时 如 a ^ b ^ c ^ d = e
# 那么当他要反过来推回来 不管是正着异或 e ^ b ^ c ^ d 还是倒过来 e ^ d ^ c ^ b 答案都为 a

绿城杯 easy_Re

虽然是RC4加密(下周会讲),但其实也可以当成异或题

绿城杯-Reverse(逆向)-easy_Re 保姆级新手向_哔哩哔哩_bilibili

BugKu Ez_Fibon

0x00 日常查壳

upx壳 64位 直接-d脱掉即可

image-20220122215540631

0x01 分析主函数

重点在与那行 % 64的操作,是没法直接逆回来的,因为不知道减掉了多少个64,所以我们采用爆破法

image-20220122224801239

本来是想给你们介绍三个方法,但是其实爆破法是最好理解,另两个有些场景不太好使用,现在我们掌握好爆破法,很多题都可以使用

0x02 GetFlag

怎么爆破,就是构造每一位,如果我们的输入与加密数据吻合,那么就是正确的输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ef = [0x64, 0x79, 0x6E, 0x76, 0x46, 0x55, 0x7B, 0x6D, 0x40, 0x5E, 0x6D, 0x63, 0x74, 0x51, 0x6D, 0x56, 0x53, 0x7E, 0x77, 0x65, 0x6E, 0x72]

fibon = [0 for i in range(22)]
fibon[0] = 2 #构造斐波那契数列
fibon[1] = 3
for i in range(2, 22):
fibon[i] = fibon[i - 1] + fibon[i - 2]

for i in range(len(ef)):
for f in range(0x40, 0x7F): #构造每一位可能的flag
if ( (i & 1) != 0 ):
tmp = (fibon[i] + i + f) % 64 + 64
else:
tmp = (fibon[i] + i + f) % 64 + 64
if ( tmp == ef[i] ): #如何构造加密后与密文相同
print(chr(f), end = "") #就输出遍历到的值
break

GetFlag!

image-20220123000741566

SMC类

SMC是什么,在程序中就是一段代码需要动态解码

假设我们的数据本来是

ins[] = { 1, 2, 3, 4 },他们分别的指令是add sub mul div(只是假设)

当我们打开ida看,发现ins[] = { 11, 12, 13, 14 },那不就乱了是不是分析不了

但其实出题人在程序的开头有个解码函数将ins里的数据每个 - 10,当程序在运行的时候,数据又回到了 1 2 3 4

这就是SMC

穿山甲 SMC

0x00 日常查壳

image-20220123001042962

0x01 分析主函数

这边即使SMC,还没解码的,可以发现上面for循环就是解码的代码,对401000的地址每个异或7

image-20220123001426418

0x02 SMC

于是我们直接在本地调试,让程序解码,这就是我们说的动调

image-20220123001744950

image-20220123001915246

可以发现还没有恢复,tab转成汇编,按u转为未定义数据,再按p形成结构函数,最后按F5反汇编

image-20220123001935687

于是就恢复了

image-20220123002021472

image-20220123002642746

于是下面显而易见,四个一组四个一组进行操作

image-20220123002949801

0x03 GetFlag!

于是按着操作返回input即可

1
2
3
4
5
6
7
8
v9 = [ 11, 9, 2, 7 ]
v5 = [0x5E, 0x5C, 0x84, 0x66, 0x51, 0x60, 0xF6, 0x63, 0x7A, 0x56, 0xC2, 0x58, 0x72, 0x27, 0x60, 0x63, 0x6A, 0x41, 0xDE, 0x65, 0x2C, 0x18, 0x42, 0x7A]

for i in range(0, len(v5), 4):
print(chr(v5[i] - v9[i % 4]), end = "")
print(chr(v5[i + 1] + v9[(i + 1) % 4]), end = "")
print(chr(int(v5[i + 2] / v9[(i + 2) % 4])), end = "")
print(chr(v5[i + 3] ^ v9[(i + 3) % 4]), end = "")

image-20220123003900822

ACTF新生赛2020-Splendid_MineCraft

两段SMC,更加刺激

文字版WP:

ACTF新生赛2020-Splendid_MineCraft - P.Z’s Blog

视频版WP:

ACTF新生赛2020-Splendid_MineCraft_bilibili

VM类

VM题的核心!操作数和操作码

如jz 0511

jz就是操作码 0511就是操作数

GWCTF 2019-babyvm

GWCTF 2019-babyvm - P.Z’s Blog

2021 深育杯 Press

SangFor(深育杯)-Reverse(逆向) Press Write up_水番正文的博客

DASCTF X SU
🍬
HFCTF2022
🍪

About this Post

This post is written by P.Z, licensed under CC BY-NC 4.0.