本次比赛的各个Re题目都对各种公开密码算法的运用考察比较多,还是挺有意思的~

Reverse

badblock

main函数打开发现是C++写的,充斥大量无用代码
于是转头执行,发现接收输入后回弹err...
搜索字符串发现有三处引用
分别是两个anti_debug和一个puts_wrong
anti_debug分别通过ptrace和getpid()的方法,不必多说,调试的时候直接绕过即可

puts_wrong则是在一个vtable中,很明显是一个类的成员函数
于是根据vt指针找到sub_5607AA5F4DD0
做了一些初始化以后、包括对input和data的赋值
调用了这个函数

return (*((unsigned int (__fastcall **)(Class *))obj->this + 13))(obj) == 1;

this+13即vt[12],点进去看一下即可发现是main_loop

循环处理code,显然是个VM
此处一共有2个类,根据代码逐步复原结构即可

然后处理VM的各个handler

.data.rel.ro:00005607AA7F7A10 vt              dq offset plus          ; DATA XREF: Imp+16↑o
.data.rel.ro:00005607AA7F7A18                 dq offset sub
.data.rel.ro:00005607AA7F7A20                 dq offset cmp
.data.rel.ro:00005607AA7F7A28                 dq offset jmp_2
.data.rel.ro:00005607AA7F7A30                 dq offset mov
.data.rel.ro:00005607AA7F7A38                 dq offset save_ip
.data.rel.ro:00005607AA7F7A40                 dq offset load_ip
.data.rel.ro:00005607AA7F7A48                 dq offset load_short
.data.rel.ro:00005607AA7F7A50                 dq offset wrong
.data.rel.ro:00005607AA7F7A58                 dq offset load_input
.data.rel.ro:00005607AA7F7A60                 dq offset load_data
.data.rel.ro:00005607AA7F7A68                 dq offset xor
.data.rel.ro:00005607AA7F7A70                 dq offset get_final_flag
.data.rel.ro:00005607AA7F7A78                 dq offset main_loop

跑一下log可以发现是将输入和一个数异或后与另一个数比较的算法,dump出来发现不可见,于是回头调试发现在接受输入后的几行指令后的sub_5607AA5F4D50中对输入进行了一个前后字符异或的处理

我对模拟器进行了Patch功能-即输入不对时也会继续向后执行,和Dump功能的添加–计算并保存flag

code = [8, 0, 20, 8, 1, 0, 8, 2, 1, 8, 7, 9, 8, 8, 0, 8, 9, 0, 1, 9, 8, 1, 8, 2, 3, 7, 8, 516, 65532, 0, 5, 3, 9, 3, 1, 0, 260, 10, 0, 5, 4, 1, 1, 4, 3, 1, 4, 4, 10, 5, 1, 12, 5, 4, 11, 6, 1, 1, 1, 2, 3, 6, 5, 260, 65525, 0, 516, 1, 0, 255, 0, 0, 9, 0, 0, 255, 0, 0, 0, 0, ]
# data = [46, 0, 38, 0, 45, 0, 41, 0, 77,0,103, 0, 5, 0, 68, 0, 26, 0, 14,0,127, 0, 127, 0, 125, 0, 101, 0, 119,0,36, 0, 26, 0, 93, 0, 51, 0, 81, 0]
mem = [0 for i in range(50)]
input = [0 for i in range(50)]
dump = ""
n = 0
flag = 0
while (n < 100):# patch checkif (n == 21):flag = 1# print("Patched:%d"%flag)c = code[3 * n] & 0xfff = code[3 * n] >> 8h1 = code[3 * n + 1]h2 = code[3 * n + 2]if (f != 0 and f != flag):n += 1print("pass")continueif (False):passelif (c == 1):mem[h1] += mem[h2]elif (c == 2):mem[h1] -= mem[h2]elif (c == 3):# dump flagif (n == 20):dump += chr(mem[4] ^ mem[6])print("cmp:%d-%d" % (mem[h1], mem[h2]), end='\t')if (mem[h1] != mem[h2]):flag = 2else:flag = 1print(flag)elif (c == 4):n += h1n &= 0xffprint("jmp")elif (c == 5):mem[h1] = mem[h2]elif (c == 6):mem[h1] = nelif (c == 7):n = mem[h1]elif (c == 8):mem[h1] = h2elif (c == 9):print("failed")breakelif (c == 10):print("Load input[%d] to %d" % (mem[h2], h1))mem[h1] = input[mem[h2]]elif (c == 11):mem[h1] = data[mem[h2] * 2]elif (c == 12):mem[h1] ^= mem[h2]elif (c == 255):passelse:print("Error:%d" % c)breakprint("[%d]: %d-%d-%d" % (n, c, h1, h2), end=' ')print(mem)n += 1
print(dump)
input = [(ord(i)) for i in dump]
for i in range(4):for j in range(len(input) - 2, -1, -1):input[j + 1] ^= input[j]
print(input)
print("".join([chr(i) for i in input]))

flag{Y0u_ar3_S0co0L}

Ctopia

一个很简单的游戏
随手翻翻resource目录下发现有个secret/secret.key
于是字符串搜索,发现get_secret函数,再向上看发现是Final_decrypt函数
程序跑起来以后修改IP寄存器使其执行这段代码,发现show_text显示的是乱码,于是往下看发现其他修改Key的操作

于是重新动态调试,并依次执行这几个write_key
最后执行Final_decrypt即可

happy

IDA打开发现函数极少,搜索一下字符串也无结果,显然加壳
跑起来以后gcore pid得到进程内存,IDA打开再搜索内存即可找到主函数

附加失败,估计是有trace_me反调试
于是修改ELF标志位,使其从Shared改成Execute,即可主动由IDA启动程序

动态调试后获得解密出的key

key{hAppysad}

接着对key和input进行处理,加密后直接与结果比较
根据sbox搜索可知是DES加密

c = [39, 66, 172, 166, 75, 144, 164, 125, 71, 64, 204, 69, 127, 161, 44, 188, 131, 82, 94, 81, 96, 249, 238, 79, 61, 104, 221, 222, 232, 116, 250, 26, 83, 34, 91, 19, 199, 229, 122, 94, 88, 128, 176, 101, 153, 241, 91, 79, 238, 80, 235, 215, 187, 38, 81, 149]
from Cryptodome.Cipher import DES
des = DES.new(b"hAppysad", DES.MODE_ECB)
print(des.decrypt(bytes(c)))

解密即可得到flag

b’flag{If_u_kn0w_bas364_aNd_d3S_u_Wil1_be_happY}hh12345678’

CaR

反编译发现字符串py2exe等信息,显然是python打包出的程序
在github上找到unpy2exe,得到pyc,然后uncompyle6反编译获得经过变量混淆的python源码

代码量不大,算法也看起来不复杂所以反混淆不算太难
很容易看出来类里用一个固定字符串的md5然后经过一堆乱七八糟的运算做key来进行RC4加密

由于RC4的特性,因此算法无需修改直接拿过来就能做解密
只不过注意输入内容添加了若干0和一些无用md5后做了b64_encode,,因此稍加处理进行反向变换即可

    def decode(self, string):self.result = ''string = base64.b64decode(string[16:])self.docrypt(string)return self.result[10+16:]

然后是chng的变换算法
这里比较蛋疼的是perm被随机处理了,还好大小只有4个元素,懒得去看shuffle的代码了,直接做全排列暴力破解即可

def de_chng(st):def permutations(arr, position, end, l):if position == end:# print arrl.append([i for i in arr])else:for index in range(position, end):arr[index], arr[position] = arr[position], arr[index]permutations(arr, position+1, end, l)arr[index], arr[position] = arr[position], arr[index]r = []W = 4perm = range(W)permutations(perm, 0, 4, r)# print(r)results = []for perm in r:s = stfor i in xrange(100):res = [0 for i in range(len(s))]for x in xrange(0, len(s), W):for y in xrange(W):res[x+perm[y]] = s[x+y]s = "".join(res)s = s[-1:] + s[:-1]a = s[:len(s)//2]b = s[len(s)//2:]s = "".join([a[i] + b[i] for i in range(len(a))])s = s[-1:] + s[:-1]results.append(s)return results

最后随便拿服务器的一个输入过来做逆向处理即可

    rc = l1l11l11l1l1l('sdfgowormznsjx9ooxxx')st = "0036dbd8313ed055NJD5H1Ufzl75UaPQgZ9+L0KOk9UZhvE1QB3ovGWDtopy1BCira230hzQIi8VgYgbncMtfow1hc/8tP6qUrewpq8s"d = rc.decode(st)print dd = de_chng(d)for i in d:print i

得到flag

flag{573fa30f270e63d7fb82c778932e24ba}…

re4

解压出来是一个C#程序+dll+14个enc
C#直接反编译发现加壳了,用de4dot可以脱下
然后查看button2_Click方法

比较清晰的代码
读取文件后分块,分块的标准是\x00\x01
根据块的长度来判断调用那个Encrypt方法
EncryptEx中很简单可以看出是RC4,key是"abcdef123456"的md5
Encrypt中有点少见,点进去可以看到SM2的字样,刚开始直接忽略了,但是可以注意到ecc_curve的对象,所以应该是ECC椭圆加密算法。但是ECC加密是非对称的,程序中只能拿到公钥,于是该密钥对肯定是公开的。搜索了一下发现是国密SM2的建议密钥对。

找了一下python的实现发现结果不同,于是乖乖找C#的写法,调用程序提供的库来解密。

另外由于国密中规定密文分为三段:EcPoint+Cipher+hash,而出题人仅根据明文长度来截取密文保存,导致当块长度小于130(EcPoint的长度)时会导致密文丢失

将14个文件全部解密后发现特征是jpg图片的,但由于上述缺陷导致部分内容缺失,需要手动补全。另一方面,复制来的RC4返回值是string,顺手用了encode()方法,默认是UTF-8编码,这个过程中使得一些字符变异了。例如jpeg中的Marker\xff\xdb变成了\xC3\xBF\xC3\x9B

>>> chr(0xdb)
'Û'
>>> b"\xc3\x9b".decode()
'Û'

可以看到是同一个字符,编码不同导致的

这一点折腾了我半天orz

最后恢复出的数据看起来仍然是不完整的,猜测是结尾没有\x00\x01导致最后一个块被丢失,直接加上结尾标志FFD9后只能显示很少的图片

附上脚本
C#的SM2相关库来自SM2 c#实现和Cipher类补充,前者缺少Cipher类,所以需要后者补充。不过理论上来说单独后者应该就够了。

C#-完成SM2解密
usage: exe filename

 class Program{public static byte[] Decrypt(byte[] privateKey, byte[] encryptedData){if (null == privateKey || privateKey.Length == 0){return null;}if (encryptedData == null || encryptedData.Length == 0){return null;}String data = Encoding.Default.GetString(Hex.Encode(encryptedData));byte[] c1Bytes = Hex.Decode(Encoding.Default.GetBytes(data.Substring(0, 130)));int c2Len = encryptedData.Length - 97;byte[] c2 = Hex.Decode(Encoding.Default.GetBytes(data.Substring(130, 2 * c2Len)));byte[] c3 = Hex.Decode(Encoding.Default.GetBytes(data.Substring(130 + 2 * c2Len, 64)));SM2 sm2 = SM2.Instance;BigInteger userD = new BigInteger(1, privateKey);ECPoint c1 = sm2.ecc_curve.DecodePoint(c1Bytes);Cipher cipher = new Cipher();cipher.Init_dec(userD, c1);cipher.Decrypt(c2);cipher.Dofinal(c3);return c2;}static void Main(string[] args){String filename = args[0];int num = 0;// 国密规范正式私钥  String prik = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94";// 国密规范正式公钥  String pubk = "04F6E0C3345AE42B51E06BF50B98834988D54EBC7460FE135A48171BC0629EAE205EEDE253A530608178A98F1E19BB737302813BA39ED3FA3C51639D7A20C7391A";BinaryReader binaryReader = new BinaryReader(new FileStream("F:\\ctf\\pcb\\re4\\9d61d305d4362e8796737a5b52fbbf1b_secret\\en"+filename, FileMode.Open));byte[] array1 = new byte[200000];byte[] array2 = new byte[2];byte[] array3 = new byte[200000];while (binaryReader.BaseStream.Position < binaryReader.BaseStream.Length){binaryReader.Read(array2, 0, 2);array3[num++] = array2[0];array3[num++] = array2[1];}//cipherText = Encoding.UTF8.GetString(array3).Substring(0, num);//System.Console.Out.WriteLine("加密: ");//String cipherText = SM2Utils.Encrypt(Hex.Decode(pubk), sourceData);//System.Console.Out.WriteLine(cipherText);//System.Console.Out.WriteLine("解密: ");if (num < 130){//长度不足array1 = Enumerable.Repeat((byte)0xaa, num).ToArray();// + Enumerable.Repeat((byte)0x01, 1).ToArray();array1[num - 2] = 0;array1[num - 1] = 1;}else{array1 = Decrypt(Hex.Decode(prik), Hex.Decode(array3.Take(num).ToArray()));}//System.Console.Out.WriteLine(plainText);//Console.ReadLine();BinaryWriter binaryWriter = new BinaryWriter(new FileStream(filename, FileMode.Create));binaryWriter.Write(array1, 0, array1.Length);binaryWriter.Close();}}

python调用脚本修改后放在最后了

后记

在群里跳了一下招来出题人沟通了一下XD
确实从几率来说只少最后一个块的话应该不会影响太大的,至少不应该只有头部那么一点儿图片
于是回头继续研究图片结构,找到了两个问题

  1. 修复图片
    如前所述,由于程序缺陷导致块长度小于130时会导致密文丢失。目前我是使用0xaa来填充的,除了头部以外还有中间的16字节第五块。
    研究了一下JEPG格式

    头部块基本是固定的,影响不大,可以直接复制填充
    观察其他JPG图片格式可以看出来一般情况下第二个块是FF DB块(DQT段),但DQT段的标准是每个qtable结构体长度为0x41字节,当某个结构体结束后为FF字节则表示段结束
    而这个文件显然需填充的部分到下一个FF之间不足0x41字节,说明这个缺失段不是DQT段(否则会造成DQT段碰不到结束标志进而整个文件解析错误)
    于是暂且忽略它(后来跟出题人要来原始图片看到是EXIF附加信息段,果然不影响图片显示)
    另一个问题是第五块的16字节,即FF C4块(DHT段),这个段描述了哈夫曼编码的相关信息,非常重要。相关解析刚开始复制了别的图片A的dht段,变成乱码。后来又复制了别的图片B的dht段,解析成功,出现了可辨识的图片。后与原图对照相同,猜测该值为固定的几种。

  2. UTF-8编码
    之前fix编码问题的时候是先将en1-14全部解密后连接在一起,保存成文件,然后再整个文件扫描C2和C3并尝试解码,这样虽然能确保修复UTF-8的编码问题,但是会带来一个新的问题:JPG图片中本身可能具有能被UTF-8编码的字节组
    搜索了一下发现果然有orz
    当时以为是C#程序的编码问题,调试了一下发现是我python脚本的问题OTZ在py中fix一下即可修复

python脚本-包装、处理RC4和调用C#导出程序

from hashlib import md5
import osdef RC4(s):key_box = md5(b"abcdef123456").hexdigest()string_lenth = len(s)result = ''l = list(range(256))rand_num = []for i in range(256):rand_num.append(ord(key_box[i % len(key_box)]))x = 0for i in range(256):x = (x + l[i] + rand_num[i]) % 256y = l[i]l[i] = l[x]l[x] = yy = 0x = 0for i in range(string_lenth):y = (y + 1) % 256x = (x + l[y]) % 256tmp = l[y]l[y] = l[x]l[x] = tmpresult += chr((s[i]) ^ l[(l[y] + l[x]) % 256])return resultoutput = b""
#en0 - en14
for i in range(15):print(i)with open("./re4/9d61d305d4362e8796737a5b52fbbf1b_secret/en%d"%i, "rb") as f:data = f.read()if(len(data)%4==2):# string to bytes(ascii)de_data = RC4(data)tmp = b""for i in de_data:tmp += bytes((ord(i),))output += tmp# print(tmp)else:passcommand = r"F:/ctf/pcb/re4/9d61d305d4362e8796737a5b52fbbf1b_secret/pcb_re4.exe %d"%(i)#, data.decode())print(command)os.system(command)with open("%d"%i, "rb") as ff:data = ff.read()output += datawith open("re4_decode.jpg", "wb") as f:f.write(output)

将之前所述的两个地方修复以后,可以得到这样的图片

很显然,仍然有一些问题
检查了一下无果后跟出题人要了原图,做了二进制比较以后发现有数据缺失
一阵debug后反应过来是SM2的算法问题
Encrypt加密了X个字节的内容后,返回了ECPoint+Cipher+Hash的HEX_String,并写入X个字节
也就是说解密的时候只能得到近一半的密文,三处图片数据缺失对应5、12、13三个段的缺失,因此本题最多完成到如上程度了

这样这个题目应该说完全吃透了=-=感谢出题人的题目,还是学到不少东西的~

181202 逆向-2018鹏城杯相关推荐

  1. 高薪寻人 | 2018 “神策杯”高校算法大师赛 6 强诞生,【招人】进行时……

    近日,2018 "神策杯"高校算法大师赛在北京落下帷幕. 此次比赛共提交近 3000 份优秀作品,近 600 支队伍激情参与,神策数据专家团队通过作品评估.成果展示.现场路演等层层 ...

  2. 2018 “神策杯”高校算法大师赛 6 强诞生,【招人】进行时……

    近日,2018 "神策杯"高校算法大师赛在北京落下帷幕. 此次比赛共提交近 3000 份优秀作品,近 600 支队伍激情参与,神策数据专家团队通过作品评估.成果展示.现场路演等层层 ...

  3. 阿里云天池 - 2018之江杯全球人工智能大赛 之零样本图像目标识别 参赛总结

    9月份报名做了 2018之江杯全球人工智能大赛 之零样本图像目标识别 的这道题 -     题目链接-https://tianchi.aliyun.com/competition/entrance/2 ...

  4. 2018 蓝桥杯省赛 B 组模拟赛(一)--封印之门

    题目链接:https://nanti.jisuanke.com/t/A1594 蒜头君被暗黑军团包围在一座岛上,所有通往近卫军团的路都有暗黑军团把手.幸运的是,小岛上有一扇上古之神打造的封印之门,可以 ...

  5. 全球变暖--2018蓝桥杯省赛

    全球变暖–2018蓝桥杯省赛 题目描述 你有一张某海域 NxN 像素的照片,"."表示海洋."#"表示陆地,如下所示: - .##- .##- -##. -## ...

  6. 181124 逆向-2018“柏鹭杯”厦大邀请赛初赛(Re1、2)

    总体来说逆向的题目质量挺高~感觉学到了不少东西=-= 就是第三题放题时间有点晚233没有公网的情况下做题难度确实比较大 欢迎各位师傅指出疏漏错误和交流~ Re1 JAVA层 用JEB查看反编译代码,J ...

  7. 2018蓝桥杯 题解

    一. 标题:分数 1/1 + 1/2 + 1/4 + 1/8 + 1/16 + .... 每项是前一项的一半,如果一共有20项, 求这个和是多少,结果用分数表示出来. 类似: 3/2 当然,这只是加了 ...

  8. 2018蓝桥杯 航班时间和乘积尾零

    法一答案均转自https://blog.csdn.net/zhanw15/article/details/79845250 标题:乘积尾零 如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾 ...

  9. 2018华迪杯计算机设计大赛,华迪杯第14届中国大学生计算机设计大赛四川省赛在吉利学院成功举办_四川在线...

    5月15日至16日,由四川省教育厅.中国大学生计算机设计大赛四川省赛组委会主办,四川省高等院校计算机基础教育研究会.吉利学院承办的2021年四川省大学生计算机设计大赛暨"华迪杯"第 ...

最新文章

  1. jquery 的3D Carousel插件参数说明
  2. 二维数组练习--矩阵的加法和乘法
  3. PHP微信登錄(網頁授權)之後的獲取用戶的信息
  4. 请求时的编码问题 Use body.encode(‘utf-8‘) if you want to send it encoded in UTF-8
  5. Hibernate第一次课(1)
  6. jenkins 发送邮件模板
  7. enablePullDownRefresh的使用
  8. VGGnet论文解读及代码实现
  9. 微擎写Android接口json,【微擎教程】getLocation需要在app.json中声明permission字段
  10. Jenkins控制台中文输出乱码解决方法
  11. JS实践与写博客-序
  12. 少样本学习系列(一)【Metrics-Based Methods】
  13. win10 系统下获取系统版本号为6.2的问题
  14. js双击事件条件触发_js页面触发chargeRequest事件和Nginx获取日志信息
  15. 【行空板教程】手写输入法
  16. 腾讯云服务器申请自助退款流程(图文教程)
  17. DB2 数据库的备份与还原
  18. 前端leader找我谈心:我是如何从刚毕业的前端菜鸟一步步成长为前端工程师的?
  19. Intriguing properties of neural networks——L-BFGS attack
  20. 《用户至上:用户研究方法与实践(原书第2版)》一1.1 什么是用户体验

热门文章

  1. Maven deploy配置方法
  2. eBay卖家用WorldFirst将PayPal美元提现国内银行教程!
  3. STM32软件仿真卡住
  4. 图片放大无失真算法 opengles实现
  5. 吐血整理-周志华演讲合集
  6. 【软件安装】SPSS22.0安装
  7. 304、bootstrap 之 图片样式
  8. 100句勉励自己的人生格言
  9. 最难初等平面几何题系列及其解法的一个介绍性材料
  10. MFC 在其他的类中引用Dlg类方法