攻防世界re(新1手区)1-12题

目录

Hello, CTF

Insanity

python-trade

re1

game

open-source

simple-unpack

logmein

no-strings-attached

getit

csaw2013reversing2

maze


Hello, CTF

直接运行是个验证字符串是否为flag的程序

攻防世界re(新1手区)1-12题

File 命令查看 显示为32位程序

攻防世界re(新1手区)1-12题

ida打开 Shift+F12 查看字符串变量列表 可以看到一串字符串 猜测为加密后的flag

攻防世界re(新1手区)1-12题

在左边的函数列表找到main 函数,双击进入并按F5 反编译,查看伪代码

攻防世界re(新1手区)1-12题

C 库函数 int sprintf(char *str, const char *format, ...) 发送格式化输出到 str 所指向的字符串。

C 库函数 char *strcat(char *dest, const char *src)src 所指向的字符串追加到 dest 所指向的字符串的结尾。

直接16进制转string就行

攻防世界re(新1手区)1-12题

Insanity

32位的程序但不是PE文件,是ELF文件,将程序在Linux环境下运行

攻防世界re(新1手区)1-12题

查看字符串列表即可

攻防世界re(新1手区)1-12题

python-trade

python反编译 - 在线工具 反编译pyc文件

攻防世界re(新1手区)1-12题

写个逆向解码函数输出即可

#!/usr/bin/env python

# visit https://tool.lu/pyc/ for more information

import base64

def encode(message):

    s = ''

    for i in message:

        x = ord(i) ^ 32

        x = x + 16

        s += chr(x)

   

    return base64.b64encode(s)

def decode(message):

       messages =  base64.b64decode(message)

       s = ''

       for i in messages:

              x = ord(i) - 16

              x = x ^ 32

              s += chr(x)

       return s

correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'

print(decode(correct))

攻防世界re(新1手区)1-12题

re1

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

查看伪代码发现判断flag的逻辑为输入的值与v5变量值比较,v5的值又来自_mm_loadu_si128函数对xmmword_413E34变量的处理 类似于memset(),将xmmword_413E34的值赋值给v5

攻防世界re(新1手区)1-12题

双击xmmword_413E34跟进,可以看到dutctf的字样 还有一段16进制字符串

攻防世界re(新1手区)1-12题

使用R键功能,能够将十进制的数转换为字符串。这里为小端序排列 需要逆序

攻防世界re(新1手区)1-12题

game

攻防世界re(新1手区)1-12题

玩游戏 输入正确的顺序让图标都点亮就行

Ida分析直接看main函数 最后通过判断之后调用的函数

攻防世界re(新1手区)1-12题

双击跟进sub_457AB4()函数

攻防世界re(新1手区)1-12题

再跟进sub_45E940(),通过循环,将a和b数组的值进行xor运算,然后再将数组a的值与0x13 xor运算

攻防世界re(新1手区)1-12题

写脚本

v6 = [0]*56

v3 = [0]*54

v4 = [0]*33

v6[0] = 18

v6[1] = 64

v6[2] = 98

v6[3] = 5

v6[4] = 2

v6[5] = 4

v6[6] = 6

v6[7] = 3

v6[8] = 6

v6[9] = 48

v6[10] = 49

v6[11] = 65

v6[12] = 32

v6[13] = 12

v6[14] = 48

v6[15] = 65

v6[16] = 31

v6[17] = 78

v6[18] = 62

v6[19] = 32

v6[20] = 49

v6[21] = 32

v6[22] = 1

v6[23] = 57

v6[24] = 96

v6[25] = 3

v6[26] = 21

v6[27] = 9

v6[28] = 4

v6[29] = 62

v6[30] = 3

v6[31] = 5

v6[32] = 4

v6[33] = 1

v6[34] = 2

v6[35] = 3

v6[36] = 44

v6[37] = 65

v6[38] = 78

v6[39] = 32

v6[40] = 16

v6[41] = 97

v6[42] = 54

v6[43] = 16

v6[44] = 44

v6[45] = 52

v6[46] = 32

v6[47] = 64

v6[48] = 89

v6[49] = 45

v6[50] = 32

v6[51] = 65

v6[52] = 15

v6[53] = 34

v6[54] = 18

v6[55] = 16

v3[0] = ord('{')

v3[1] = ord(' ')

v3[2] = 18

v3[3] = 98

v3[4] = 119

v3[5] = 108

v3[6] = 65

v3[7] = 41

v3[8] = 124

v3[9] = 80

v3[10] = 125

v3[11] = 38

v3[12] = 124

v3[13] = 111

v3[14] = 74

v3[15] = 49

v3[16] = 83

v3[17] = 108

v3[18] = 94

v3[19] = 108

v3[20] = 84

v3[21] = 6

for i in range(12):

       v4[i] = ord("`S,yhn _uec{"[i])

v4[12] = 127

v4[13] = 119

v4[14] = 96

v4[15] = 48

v4[16] = 107

v4[17] = 71

v4[18] = 92

v4[19] = 29

v4[20] = 81

v4[21] = 107

v4[22] = 90

v4[23] = 85

v4[24] = 64

v4[25] = 12

v4[26] = 43

v4[27] = 76

v4[28] = 86

v4[29] = 13

v4[30] = 114

v4[31] = 1

for i in range(32):

       v3[22+i] = v4[i]

v3.append(ord("u"))

v3.append(ord("~"))

s = ""

for i in range(56):

       v3[i] ^= v6[i]

       v3[i] ^= 0x13

       s += chr(v3[i])

print(s)

攻防世界re(新1手区)1-12题

open-source

分析源码可知

攻防世界re(新1手区)1-12题

写Python脚本得到flag

攻防世界re(新1手区)1-12题

simple-unpack

攻防世界re(新1手区)1-12题

64位的ELF文件使用ida64打开,分析被中断,文件被加壳处理过

攻防世界re(新1手区)1-12题

使用ExeinfoPe进行查壳,发现有upx壳。

攻防世界re(新1手区)1-12题

使用upx -d进行脱壳

攻防世界re(新1手区)1-12题

直接看main函数就行

攻防世界re(新1手区)1-12题

logmein

攻防世界re(新1手区)1-12题

按位校对是否与异或后的值相等

攻防世界re(新1手区)1-12题

写python脚本得到flag

#!/usr/bin python2

#-*-coding=utf-8-*-open

import binascii

v8 = ":\"AL_RT^L*.?+6/46"

v7 = "65626D61726168"

v7=binascii.unhexlify(v7)

v7.decode('utf-8')

#print(v7[-1::-1])

v7 = v7[-1::-1]

v6 = 7

flag = ""

for i in range(0,len(v8)):

       flag += chr( ord(v7[i % v6]) ^ ord(v8[i]) )

print(flag)

攻防世界re(新1手区)1-12题

no-strings-attached

攻防世界re(新1手区)1-12题

直接看main函数

攻防世界re(新1手区)1-12题

逐个查看函数代码,定位到authenticate 看着比较像对flag进行数据处理的。主要是比较 ws 和 s2

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

静态调试

按程序逻辑直接导出数据,写python脚本求解

Shift + E 导出数据

攻防世界re(新1手区)1-12题

wchar_t是C/C++的字符数据类型,是一种扩展的字符存储方式。 在Windows下,wchar_t占2个字节(byte);

在Linux下,wchar_t占4个字节。

wchar_t类型主要用在国际化程序的实现中,但它不等同于Unicode编码。Unicode编码的字符一般以wchar_t类型存储。

这里其实只用到了一个字节框起的部分是无用的 写脚本的时候直接去掉 直接选这个选项可以导出字符

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

为了方便写脚本用这个选项导出

攻防世界re(新1手区)1-12题

#!/usr/bin python3

#-*-coding=utf-8-*-open

a2 = [1,2,3,4,5]

s = [58,54,55,59,128,122,113,120,99,102,115,103,98,101,115,96,107,113,120,106,115,112,100,120,110,112,112,100,112,100,110,123,118,120,106,115,123,128]

flag =''

for i in range(len(s)):

       flag += chr(s[i] - a2[i%5])

print(flag)

攻防世界re(新1手区)1-12题

动态调试

Ollydbg不能调试Linux的ELF可执行文件 可以用ida远程调试或者用gdb

攻防世界re(新1手区)1-12题

在ida查看内存,去查找最后生成的字符串。通过IDA生成的汇编指令,可以看出调用decrypt函数后,生成的字符串保存在EAX寄存器中。这就是为什么要在GDB中查看eax寄存器的值

攻防世界re(新1手区)1-12题

1. gdb 调试

    gdb 554e0986d6db4c19b56cfdb22f13c834

2. 打断点 decrypt

    b decrypt

3. 执行,单步执行

    r

    n

4. 查看地址内容(根据代码分析,数据在 eax)

    数值查看     x/200wd $eax

字符串查看   x/sw $eax  s -> 字符串 w -> 4字节

b 表示下断点,gdb 中可以直接用已知函数名下断点,也可通过*指定地址下断点。

r 表示程序运行,n 表示单步步过,s 表示单步步入

x 指令表示查看寄存器内容

    x/<n/f/u>  <addr>

        <n>:是正整数,表示需要显示的内存单元的个数,即从当前地址向后显示n个内存单元的内

容,

        一个内存单元的大小由第三个参数u定义。

        <f>:表示addr指向的内存内容的输出格式,s对应输出字符串,此处需特别注意输出整型数据的格式:

          x 按十六进制格式显示变量.

          d 按十进制格式显示变量。

          u 按十进制格式显示无符号整型。

          o 按八进制格式显示变量。

          t 按二进制格式显示变量。

          a 按十六进制格式显示变量。

          c 按字符格式显示变量。

          f 按浮点数格式显示变量。

        <u>:就是指以多少个字节作为一个内存单元-unit,默认为4。u还可以用被一些字符表示:

          如b=1 byte, h=2 bytes, w=4 bytes, g=8 bytes.

        <addr>:表示内存地址。

攻防世界re(新1手区)1-12题

x:就是用来查看内存中数值的,后面的200代表查看多少个,wx代表是以word字节查看看,$eax代表的eax寄存器中的值

getit

攻防世界re(新1手区)1-12题

就是对s的值按位 加一或者减一 然后赋值到t中间????????的部分

得到s 和 t 的值

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

#!/usr/bin python3

#-*-coding=utf-8-*-open

s = "c61b68366edeb7bdce3c6820314b7498"

t = "SharifCTF{????????????????????????????????}"

T=list(t)

for i in range(len(s)):

       if ( (i & 1) != 0 ):

              v3 = 1

       else:

              v3 = -1

       T[i+10]=str(chr(ord(s[i])+v3))

flag = "".join(T)

print(flag)

攻防世界re(新1手区)1-12题

csaw2013reversing2

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

动态调试

攻防世界re(新1手区)1-12题

用od调试 为让程序跳转到loc_401096调用sub_401000进行解码,把int3改为nop(0x90),再跳到loc_4010B9进行输出

攻防世界re(新1手区)1-12题

搜索字符串找到IsDebuggerPresent()函数地址

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

把前面的B9改成96 , int3 改成 nop

攻防世界re(新1手区)1-12题

修改完后点击运行即可

攻防世界re(新1手区)1-12题

maze

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

从上往下以此追踪,可以发现这些函数会跳到lable15的位置

攻防世界re(新1手区)1-12题

按R就可以变成字符。输入的应该是'.','0','o','O',并以此来确定上下左右移动

分析这四个函数

前两个是对v10进行减和加 实现的是左右的操作 同时判断是否越界

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

后两个函数也是加减但是对v9进行的,实现的是上下操作 同时判断是否越界

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

看有个wp说明了v9 v10两个参数区别

攻防世界re(新1手区)1-12题

个人感觉这应该是 __int32

Dword类型:DWORD中D表示double,一个word(字)两个字节(两个字节16bit),因此DWORD为四个字节,常表示uint32_t。

查看堆栈变量

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

对lable15分析,发现特殊的字符串,应该就是迷宫的地图

攻防世界re(新1手区)1-12题

攻防世界re(新1手区)1-12题

根据上面四个函数的信息可知是个8x8的迷宫地图

进入判断函数内部

攻防世界re(新1手区)1-12题

a1是一个8*8的数组,并且值由这些组成,a2代表列,a3代表行,a3+1也就是向下走了一个单位

搞明白逻辑之后其实就简单了,画出线路图直接写就行

攻防世界re(新1手区)1-12题

o  0 oo   00  O  000   oooo   ..  OO

左 下 左左 下下 右 下下下 左左左左 上上 右右

#flag 即为

nctf{o0oo00O000oooo..OO}

上一篇:BUUCTF Re [ACTF新生赛2020]Universe_final_answer


下一篇:使用 Packer 创建自定义镜像