RE

0x1 Check_1n

在B站上看到一个大佬的C语言项目,他在gihub开源了代码,感觉挺有意思的。偷偷拿来魔改了一下当签到题,拿到题目后,如果是按照常规思路走,那就是先找到开机密码:

IDA打开搜索关键字符串就可以拿到开机密码:HelloWorld

然后,里面有个虚假的flag,base64接出来是Why don't you try the magic brick game,明示让玩打砖块游戏。

进入游戏,没过一会就当场去世以后,得到flag,如下图所示:

当然有些师傅直接看到了base58编码的flag,直接拿去解密速度就更快了。

0x2 Chelly's ldentity

输入 input 拷贝到 instr,并依次进行将 instr 传入三个函数

第一个函数用于限制输入长度为16字符。

第二个函数是加密函数。

首先生成了向量v10,紧接着用一个循环遍历输入,向量中元素小于instr中元素时累加向量的值,之后instr元素与这个值异或

加密函数中向量生成方式如下:

从2至len(此处为128),若满足if判断则加入向量中。其中if判断是一个判断是否为质数的函数,也就是说,向量是生成len之内的质数

简言之,加密函数将输入的每个字符对一个值进行异或,这个值是向量v10中低于该字符的元素之和。

第三个函数是一个check输入经过加密后是否正确的函数。

与一个数组进行判等。

因此,只需要生成一个128内的质数数组,用判等数组进行爆破即可。#生成质数数组

def is_prime_num(num):

#判断num是否为质数

for i in range(2, num):

if num % i == 0:

return False

return True

def create_table(n):

#生成n之内的质数列表

table = []

for num in range(2, n):

if is_prime_num(num):

table.append(num)

return table

def de_anwser(_key):

#根据key,爆破

table = create_table(128)

flag = ''

for k in _key:

for ch in range(128):

count = 0

i = 0

while table[i] < ch:

count += table[i]

i += 1

tmp = ch ^ count

if tmp == k:

flag += chr(ch)

return flag

key = [438,1176,1089,377,377,1600,924,377,1610,924,637,639,376,566,836,830 ]

flag = de_anwser(key)

print(flag) #flag{Che11y_1s_EG0IST}

0x3 BabyDriver

题目思路来源:参考2016 HCTF Reverse题目seven

一看源码,你就知道这是一个打着驱动幌子的简单迷宫,师傅们可能卡在了驱动对应的键盘码这块,我将上下左右设置为为IKJL,键盘过滤驱动捕获点击以后达成条件会输出成功,但不知道为啥卸载驱动以后老是蓝屏。。。

进到驱动程序的入口点DriverEntry里面,可以看到Driverunload,和分发函数,主题人想用KdDisableDebugger:

因为是键盘过滤驱动,主要看下IRP读操作的回调函数,也就是CompletionRoutine的位置:

很快在函数内找到迷宫的地图和行走逻辑,与普通迷宫不同的是,是由键盘过滤驱动获取键盘扫描码来控制上下左右:

根据键盘过滤驱动获得的key值为,我们可以知道IKJL为上下左右:

然后在虚拟机里加载下驱动,键盘依次敲击LKKKLLKLKKKLLLKKKLLLLLL,在有节奏感的敲击后,就可以获得success!

然后我虚拟机就蓝了。。。。

0x4 WannaReverse

题目思路来源:WannaCry的加密原理

这道题目被apeng师傅非预期了,膜就完事,首先讲下我认为的常规做法,我们拿到四个文件,主要需要逆向WannaReverse.exe这个文件,libcrypto-1_1.dll是openssl库用来提供rsa的相关加密函数,clickme.exe是仿照wannacry病毒的界面程序(自带换壁纸功能),flag.txt.Encry是被WannaReverse.exe加密的flag。

题目加密流程和WannaCry的加密原理差不多,也是RSA2048 + AES随机密钥,但是为了大大降低题目难度,也不在加密算法这里留坑了,直接把私钥给了,在clickme.exe里点Decrypt按钮就有私钥(模仿WannaRen直接给密钥)

main函数功能如下图所示:

然后在encfile函数里进行了关键的操作,最后将文件头+加密的AES密钥+AES加密的文件写入flag.txt.Encry文件:

标准的流程就这些,动调一下应该可以直接梳理清除,

解题脚本:#coding:utf-8

from Crypto.Cipher import AES

from binascii import b2a_hex, a2b_hex

import rsa

import base64

def aes_decrypt(text,key):

key = key.encode('utf-8')

mode = AES.MODE_ECB

cryptor = AES.new(key, mode)

plain_text = cryptor.decrypt(a2b_hex(text))

return plain_text.replace('\x00','')

def decrypt(crypt_text): # 用私钥解密

with open('rsa_private_key.pem', 'r') as privatefile:

p = privatefile.read()

privkey = rsa.PrivateKey.load_pkcs1(p)

lase_text = rsa.decrypt(crypt_text, privkey)

return lase_text

if name == 'main':

with open("flag.txt.Encry",'rb') as f:

res = f.read()[0xd:0x165]

print res

res = base64.b64decode(res)

key = decrypt(res)

with open("flag.txt.Encry",'rb') as f:

res = f.read()[0x165:]

print (res.encode('hex'))

print (aes_decrypt(res.encode('hex'),key))

但是这里必须贴一下我失误的伪随机用法,apeng师傅的非预期解法,学习到了:

师傅的脚本:from Crypto.Cipher import AES

from base64 import *

def gen_key(seed):

k = b""

for i in range(32):

seed = seed * 214013 + 2531011

k+=bytes([0x30+(((seed>>16) & 0x7fff)%10)])

return k

t = 1590310000

# t = 1589530000

while True:

key = gen_key(t)

# print(key)

aes = AES.new(key, mode=AES.MODE_ECB)

cipher = b"\\\xbc\xea\x89\xba+\x18\'y?\x13\n\x8a\x97\xb4\x9b\xcdx\x9b\xd85\x92\x05EL\"\xa5i7\xebn+\x0e\xbd\x84\x0f\x91a8\xf6\xf1\xba\x99\x19Ar\x07\x91\xf0&h\x06a&\\ 5\xdd\xcf\xfcwWT\x81\xf2\xf2\xe4\xaf\xbf\xa2\x1d)\xael\x08;v\x1bf\xb8\xfer\xcb\xd6\x94\xc3\xd5j\xe7\x0cz(\xdc\xbc\xac\x80"

if aes.decrypt(cipher).endswith(b"\x00\x00\x00"):

print(t)

print(aes.decrypt(cipher).decode("utf-16"))

exit()

t-=1

if t%10000 == 0:

print(t)

我这样的勒索病毒,就是在白给。。。。

0x5 EzMachine

如果没有限制的话,会存在多解的情况,因此后来更新了题目描述,flag的组成仅包括:字母、大括号、大括号回头和下划线。

首先打开IDA,来到main函数,发现有混淆干扰了反汇编,手动patch为nop

然后创建函数并查看伪代码,发现是VM结构,下面开始分析。

off_4448F4处保存各个opcode对应函数的地址,4449A0处为虚拟机代码起始地址,dword_445BD8为ip,off_4427FC处保存有四个寄存器的地址,dword_445BAC为栈的起始地址,dword_445BC8为esp,opcode中有对栈的操作、跳转操作、取输入的操作以及加减乘除异或五种运算,分析各个opcode的作用后,可以写脚本生成伪汇编代码:opcode = {0x00:'nop',0x01:'mov',0x02:'pushi',0x03:'pushr',0x04:'pop',0x05:'print',0x06:'add',0x07:'sub',0x08:'mul',0x09:'div',0x0A:'xor',0x0B:'jmp',0x0C:'cmp',0x0D:'je',0x0E:'jn',0x0F:'jg',0x10:'jl',0x11:'input',0x12:'clrstr',0x13:'LoadStack',0x14:'LoadString',0xFF:'quit'}

operand = {'nop':0,'mov':2,'pushi':1,'pushr':1,'pop':1,'print':0,'add':2,'sub':2,'mul':2,'div':2,'xor':2,'jmp':1,'cmp':2,'je':1,'jn':1,'jg':1,'jl':1,'input':0,'clrstr':0,'LoadStack':2,'LoadString':2,'quit':0}

code = [

0x01, 3,3,

0x05, 0x00, 0x00,

0x11, 0x00, 0x00,

0x01, 1,17,

0x0C, 0,1,

0x0D, 10, 0x00,

0x01, 3,1,

0x05, 0x00, 0x00,

0xFF, 0x00, 0x00,

0x01, 2, 0,

0x01, 0, 17,

0x0C, 0, 2,

0x0D, 43, 0x00,

0x14, 0, 2,

0x01, 1, 97,

0x0C, 0, 1,

0x10, 26, 0x00,

0x01, 1, 122,

0x0C, 0, 1,

0x0F, 26, 0x00,

0x01, 1, 71,

0x0A, 0, 1,

0x01, 1, 1,

0x06, 0, 1,

0x0B, 36, 0x00,

0x01, 1, 65,

0x0C, 0, 1,

0x10, 36, 0x00,

0x01, 1, 90,

0x0C, 0, 1,

0x0F, 36, 0x00,

0x01, 1, 75,

0x0A, 0, 1,

0x01, 1, 1,

0x07, 0, 1,

0x01, 1, 16,

0x09, 0, 1,

0x03, 1, 0x00,

0x03, 0, 0x00,

0x01, 1, 1,

0x06, 2, 1,

0x0B, 11, 0x00,

0x02, 0x7, 0x00,

0x02, 0xD, 0x00,

0x02, 0x0, 0x00,

0x02, 0x5, 0x00,

0x02, 0x1, 0x00,

0x02, 0xC, 0x00,

0x02, 0x1, 0x00,

0x02, 0x0, 0x00,

0x02, 0x0, 0x00,

0x02, 0xD, 0x00,

0x02, 0x5, 0x00,

0x02, 0xF, 0x00,

0x02, 0x0, 0x00,

0x02, 0x9, 0x00,

0x02, 0x5, 0x00,

0x02, 0xF, 0x00,

0x02, 0x3, 0x00,

0x02, 0x0, 0x00,

0x02, 0x2, 0x00,

0x02, 0x5, 0x00,

0x02, 0x3, 0x00,

0x02, 0x3, 0x00,

0x02, 0x1, 0x00,

0x02, 0x7, 0x00,

0x02, 0x7, 0x00,

0x02, 0xB, 0x00,

0x02, 0x2, 0x00,

0x02, 0x1, 0x00,

0x02, 0x2, 0x00,

0x02, 0x7, 0x00,

0x02, 0x2, 0x00,

0x02, 0xC, 0x00,

0x02, 0x2, 0x00,

0x02, 0x2, 0x00,

0x01, 2, 1,

0x13, 1, 2,

0x04, 0, 0x00,

0x0C, 0, 1,

0x0E, 91, 0x00,

0x01, 1, 34,

0x0C, 2, 1,

0x0D, 89, 0x00,

0x01, 1, 1,

0x06, 2, 1,

0x0B, 78, 0x00,

0x01, 3, 0,

0x05, 0x00, 0x00,

0xFF, 0x00, 0x00,

0x01, 3, 1,

0x05, 0x00, 0x00,

0xFF, 0x00, 0x00,

]

for i in range(0,len(code),3):

if not code[i] in opcode.keys():

print('unknow')

continue

op = opcode[code[i]]

print(op, end = ' ')

if operand[op] != 0:

print(code[i+1:i+1+operand[op]])

else:

print()

得到伪汇编代码:mov [3, 3]

print

input

mov [1, 17] //flag长度

cmp [0, 1]

je [10]

mov [3, 1]

print

quit

mov [2, 0]

mov [0, 17]

cmp [0, 2]

je [43]

LoadString [0, 2]

mov [1, 97]

cmp [0, 1]

jl [26] //if input[i] < 'a': jmp

mov [1, 122]

cmp [0, 1]

jg [26] //if input[i] > 'z': jmp

zmov [1, 71]

xor [0, 1]

mov [1, 1]

add [0, 1]

jmp [36]

mov [1, 65]

cmp [0, 1]

jl [36] //if input[i] < 'A': jmp

mov [1, 90]

cmp [0, 1]

jg [36] //if input[i] > 'Z': jmp

mov [1, 75]

xor [0, 1]

mov [1, 1]

sub [0, 1]

mov [1, 16]

div [0, 1]

pushr [1]

pushr [0]

mov [1, 1]

add [2, 1]

jmp [11]

pushi [7]

pushi [13]

pushi [0]

pushi [5]

pushi [1]

pushi [12]

pushi [1]

pushi [0]

pushi [0]

pushi [13]

pushi [5]

pushi [15]

pushi [0]

pushi [9]

pushi [5]

pushi [15]

pushi [3]

pushi [0]

pushi [2]

pushi [5]

pushi [3]

pushi [3]

pushi [1]

pushi [7]

pushi [7]

pushi [11]

pushi [2]

pushi [1]

pushi [2]

pushi [7]

pushi [2]

pushi [12]

pushi [2]

pushi [2]

mov [2, 1]

LoadStack [1, 2]

pop [0]

cmp [0, 1]

jn [91]

mov [1, 34]

cmp [2, 1]

je [89]

mov [1, 1]

add [2, 1]

jmp [78]

mov [3, 0]

print

quit

mov [3, 1]

print

quit

伪代码中,类似于mov、add、sub等指令后面的操作数为寄存器编号,例如 mov [1,2]代表将2号寄存器的内容放入1号寄存器。接下来分析伪汇编代码,发现是对输入的值进行处理,小写字母异或71后+1,大写字母异或75后-1,非字母不处理,处理得到的结果除以16,得到的商和余数分别进栈,并在最后部分比较,因此可以写出exp计算flag:array = [0x7,0xd,0x0,0x5,0x1,0xc,0x1,0x0,0x0,0xd,0x5,0xf,0x0,0x9,0x5,0xf,0x3,0x0,0x2,0x5,0x3,0x3,0x1,0x7,0x7,0xb,0x2,0x1,0x2,0x7,0x2,0xc,0x2,0x2,]

array = array[::-1]

for i in range(0, len(array), 2):

c = array[i] + array[i+1]*16

tmp = (c-1) ^ 71

if tmp >= ord('a') and tmp <= ord('z'):

print(chr(tmp), end = "")

continue

tmp = (c+1) ^ 75

if tmp >= ord('A') and tmp <= ord('Z'):

print(chr(tmp), end = "")

continue

print(chr(c), end = "")

flag{Such_A_EZVM}

0x6 DbgIsFun

首先检查tls回调,出现了smc,解密方法为第i位与i异或,解密后创建了运行该函数的线程

main函数的开头可以看到安装了seh函数

安装后进行输入,然后对输入的长度减了一个0x1C,并随后触发int3断点来到seh函数。

SEH中的判断如图所示。在main函数中对flag长度做的sub操作会影响EFlags的值,当输入长度等于0x1C时,ZFlag置0,SEH函数由此判断输入的长度是否正确,如果错误会将程序的流程引向一段假的flag解密函数。长度正确则对标志位置1。

dword_41A8E0这个标志位将在子线程的函数中被循环检查,静态下该函数还未解密,所以需要进行动态调试等待代码段解密。解密后来到子线程函数:

当dword_41A8E0处的标志位被置1后,函数跳出Sleep死循环开始运行,首先会取41A8DE起始的前0x8C个字节计算字节码之和,如果该段代码被patch或者存在断点,字节码之和就会改变,从而影响后续的计算结果。

计算完字节码之和后与所输入字符串进行异或,再往后就是将异或后的结果进行RC4加密并校验,密钥为GKCTF

如图所示,校验时的数据存放在代码段,因此调试时的反汇编结果可能出现错误,需要手动矫正。从4014AA开始即为校验数据,将这段数据进行RC4解密,密钥GKCTF,再与前面计算得到的字节码之和进行异或即可得到flagfrom Crypto.Cipher import ARC4

key = b"GKCTF"

hexstr = "2DD40FD054EE75D0E03096E1798AE0FE183A27E72F86C9FE6643A775"

newstr = b""

for i in range(0, len(hexstr), 2):

newstr += int(hexstr[i:i+2],16).to_bytes(1,'little')

rc4 = ARC4.new(key)

flag = rc4.decrypt(newstr)

for i in flag:

print(chr(i^0xC9), end = "")

flag{5tay4wayFr0m8reakp0int}

温柔正确的人总是难以生存,因为这世界既不温柔,也不正确

Java ist reverse_GKCTF 2020 Reverse Writeup相关推荐

  1. [BUGKU][CTF][Reverse][2020] Reverse writeup 1-7 暂时肝不动了

    Reverse 入门逆向 步骤: ida main函数 按R Reverse signin 关键字: 知识点:Android逆向分析.(常用工具:安卓模拟器.JEB.Cyberchef.Android ...

  2. Java字符串反转函数reverse()

    Java字符串反转函数reverse() package test1;public class TestReverse {public static void main(String[] args) ...

  3. 用java语言实现2020年1月1号到2022年7月3号之间的间隔天数

    用java语言实现2020年1月1号到2022年7月3号之间的间隔天数 1.思路 1.算出2020年1月1号到2021年12月31号之间的间隔天数sumYear 2.算出2022年1月1号到2022年 ...

  4. # java学习笔记 2020 2/8(十二)慕课网 构造方法

    java学习笔记 2020 2/8(十二)慕课网 构造方法 构造方法是一种特殊的方法,它是一个与类同名的方法. 对象的创建就是通过构造方法来完成,其功能主要是完成对象的初始化. 当类实例化一个对象时会 ...

  5. java 中1%3c1%3c1_祥云杯2020 部分WriteUp

    祥云杯 Web ★sign 1%09||%09ls%09/ 1%09||%09ca\t%09in\dex.p\hp时有个闪 1%09||find%09/%09-name%09`echo%09ZmxhK ...

  6. Java for LeetCode 206 Reverse Linked List

    Reverse a singly linked list. 解题思路: 用Stack实现,JAVA实现如下: public ListNode reverseList(ListNode head) {i ...

  7. 2019年java安装步骤_win10的JAVA(jdk)2020年最新版安装教程心得

    想学安卓反向解析,前期工作就把我折腾得够呛,java安装教程大多18年之前的,和现在的有一些不同,导致我按照网上的教程折腾了好几天都没安装成功,今天突然成功了,有点开心,迫不及待把自己的一些小经验分享 ...

  8. java reverse_Java Integer类reverse()方法与示例

    java reverse 整数类reverse()方法 (Integer class reverse() method) reverse() method is available in java.l ...

  9. java 自行车_JAVA 2020款山地车盘点

    最近,笔者为大家带来了不少有关顶尖山地车型的文章,不少的观众老爷直呼看看就好,那今天我们就来看看入门山地车.JAVA作为近几年迅速崛起的国产品牌,推出的一系列产品中带着鲜明的"JAVA&qu ...

  10. Java我的2020年终盘点

    这段时间一直在忙着工作上的事情和新书的筹备.但都2021年了,怎么都应该写一点,算不上总结回顾,只能算闲聊胡扯. 2020年TIOBE's对于各种编程语言排名情况的总结是:"Python i ...

最新文章

  1. Java的Scanner类的next与nextLine用法区别
  2. Virtual Box 安装过程(卸载Vmware后)
  3. php注册界面模板,WeUI注册页面
  4. 推荐高质量的公众号,值得跟进学习!
  5. 解决:java.lang.NoSuchMethodException: gentle.entity.User.<init>()
  6. 13寸笔记本电脑尺寸_如何判断行李箱的尺寸?标准行李箱尺寸对照表(13~32寸)
  7. jeecg开源社区第12期架构学习班开始报名了
  8. shell 脚本中 while 只执行一次
  9. Failed building wheel for scandir 解决方案
  10. HIve内置函数(functions)使用和解析
  11. window上安装fasttext
  12. 使用angrutils生成控制流图出错的解决过程
  13. python代码实现蜡笔小新
  14. 用Java输出高频词_编程高阶用法--开发者高频词汇
  15. php配置设置时区,php如何设置时区
  16. druid之本机批量摄取
  17. Pois-son blending
  18. leetcode276周赛记录(待补充)
  19. 五金制品厂物料产品编码方案(1)
  20. mongodb 之 模糊查询

热门文章

  1. Vue中Class和Style几种v-bind绑定的用法-详解案例
  2. Affine set 和 convex set 的定义
  3. 8、javascript数组
  4. cidaemon.exe进程
  5. 百度、太一云、趣链、星合、达令共议什么是真区块链【区块链打假】
  6. 关联规则分析(频繁项集查找方法为apriori方法的Fk-1*Fk-1)
  7. 《Python机器学习及实践:从零开始通往Kaggle竞赛之路》第1章 简介篇 学习笔记(一)1.1机器学习综述、1.2Python编程库、1.3Python环境配置总结
  8. DropdownMenu 下拉菜单的使用
  9. LeetCode刷题系列之数组--0704.二分查找(JAVA版本)
  10. Python中,使用正则表达式时,遇到的re.error:unbalanced parenthesis问题记录