害,惨痛的参赛经历们
太难了太难了,持续更新
1.ViQinere
(安恒五月赛和BJDCTF一起办的)
这个题目其实就是普通的逆向,也没什么花里胡哨的,逻辑也很清楚,为什么我写脚本的时候这么菜啊呜呜呜
64位拖进ida
然后看看sub_1209()
然后是exp
a1='zyxwvutsrqponmlkjihgfedcba' #byte_4060
b='TaQini' #byte_4180
flag=''
key='FQD{GfjuJ5UbLrWjZjpvErXkiAZzlvO0xTa!cwnLLAsy3B0iEvEy}'
def sub_1209(a):
x=ord(a)
if 96<x<=122:
x=x-97
return x
elif 65<x<=90:
x=(x-65)^(-0x80) #后来看别的师傅也可以x=(x-65)^0xffffff80-0x100000000
return x
else:
return x
def resub(a):
if a<0:
a=a^(-0x80)
a=a+65
return chr(a)
else:
a=a+97
return chr(a)
v5=0
for i in range(len(key)):
if 96<ord(key[i])<=122:
v2=sub_1209(b[v5&5])
v5+=1
for j in range(26):
if ord(key[i])==ord(a1[(v2&0x7f+j)%26]):
flag+=resub(j)
elif 64 <ord(key[i])<=90:
v1=sub_1209(b[v5&5])
v5+=1
for k in range(26):#这里下面是+128提到了上面所以是26,所以最后逆的时候-128
if ord(key[i])+32==ord(a1[(k+v1&0x7f)%26]):
flag+=resub(k-128)
else:
flag+=key[i]
print(flag)
exp我真的想了好久好久怎么去实现,明明逻辑很简单,也挺好逆的,太菜了太菜了唉,这个比赛对我来说真的太难了唉
这题有个小点
就是我写resub的时候
print((-127)^(-0x80)+65)#输出64,然而我想让他输出66
俺才知道原来py中异或的优先级没有±高,果然是一个fw
这个应该算是这次re签到了,签了好久,嗯
2.blink
出处同上(和上一题一样应该都是100分的题目),这题我当时没做出来,赛后复现了一下,发现自己的一些问题,没遇到类似的题目就会很迷茫,也懒得去想
比如这题打开之后是满屏blingbling的星星(大雾),ida打开也没发现什么特别的,然后就不知道该怎么入手了
这个题还蛮好玩的,目前看到了师傅们的两种解法都蛮有意思的,这里就不写了
记录一下学到的点
方法一:把星星都打印下来,是一个二维码
这个要用的python的PIL库
学习链接
这里有个奇怪的小点我一开始from PIL import Image没有成功,但是直接import PIL却成功了emmm然后试了试import PIL.Image成功了,据说卸了重装会好?
方法二:patch掉jle条件,然后运行直接显示全部星星
参考链接
有点想放一下这个题目
记录一下要学的一个:ida下的patch用法(花指令可太烦liao)
3.MiscVm
(同上)
这题冷静分析其实没有那么难,得冷静下来
先大体看一下主函数
先看最后一个关键判断,通过我们的opcode来进行变换,看起来很复杂,但是仔细研究的话
比如这个
这一步变换前面的三步其实是等价的,相当于只++v9和++v10
同理还有很多,这样我们可以简化我们的opcode
3和6是相反操作,这样36在一起的我们可以删去,还有一些超过12的还有别的可以化简的,我们的opcode最后得出
opcode=[4,1,7,9,10,9,4,1,7,9,10,9,4,1,7,
9,10,9,4,1,7,9,10,9,4,1,7,9,10,9,4,1,7,9,10,
9,4,1,7,9,10,9,4,1,7,9,10,9] #opcode
可以看出,只在1,4,7,9,10进行了变化
据此写出这一段的exp
#coding=utf-8
v15=[0x42,0x4a,0x44,0x7b,0x33,0x370,0x46,0xd4,0x3c,0x610,0x4f,0xc8,0x6c,0x320,
0x1e,0x190,0x6f,0x630,0x46,0x190,0x3b,0x610,0x1d,0xc4,0x3e,0x660,0x4b,0xd0,
0x6c,0x310,0x46,0x188,0x33,0x370,0x4c,0xcc,0x7d] #off_203020
#print(len(v15))
opcode=[4,1,7,9,10,9,4,1,7,9,10,9,4,1,7,
9,10,9,4,1,7,9,10,9,4,1,7,9,10,9,4,1,7,9,10,
9,4,1,7,9,10,9,4,1,7,9,10,9] #opcode
#print(len(opcode))
v9=4
v13=9999
for a in opcode:
if a==1:
v15[v9]//=16
v9+=1
#elif a==2:
#v15[v9]*=v9
#v15[v9]-=128
#elif a==3:
#v15[v9]//=10
#v9-=1
elif a==4:
v15[v9]^=0xa
v9+=1
#elif a==5:
#elif a==6:
#v9+=1
#v15[v9]*=10
elif a==7:
v15[v9] -= 128
v15[v9] = ~v15[v9]
#elif a==8:
#v15[v9]+=1
#v15[v9]-=v13
#v13=v13%1234-2019
#v9+=1
elif a==9:
v9+=1
elif a==10:
v15[v9]//=4
#elif a==11:
#elif a==12:
#v9-=1
#v15[v9]+=32
#v15[v9]^=0x22
else:
continue
#print(v15)
然后继续分析上面的加密
sub_1551()给了两组数据,进行替换,
这里我卡了挺久的,主要是因为可以理解原算法但是不是很清楚应该怎么逆(还是太蠢了)
exp如下
#sub_1551
v11=[1,2,3,4,19,31,25,14,23,33,13,9,24,6,26,34,17,10,8,29,12,15,22,11,
18,16,32,28,21,36,20,7,5,27,30,35,37]
v48=[1,2,3,4,31,29,7,35,14,21,9,16,27,18,25,10,20,15,17,22,28,26,36,
33,32,5,8,12,23,34,13,30,24,11,19,6,37]
temp=[0]*37
flag1=[0]*37
for i in range(37):
temp[v48[i]-1]=v15[i]
for j in range(37):
flag1[v11[j]-1]=temp[j]
print(flag1)
然后是将除了BJD{}剩余的前16位与后16位替换
#sub_19E2
for i in range(4,20):
pass1=flag1[i+16]
flag1[i+16]=flag1[i]
flag1[i]=pass1
flag=''
for i in flag1:
flag+=chr(i)
print(flag)
顺便这题找off_203020的数据时,因为眼神不好,看错好几个,俺的idapy和idc还不能熟练运用
然后关于case x==4 那里
我后来看到了别的师傅的wp
可以直接化简成v15[v9]^=0xA
然后我后来去试了一下,第二个v4算出来的结果都是0,第二个我的理解是16*所以左移了4位,所以lobyte(v15[v9])都是0了,导致最后结果都是0,所以可以直接化简,不知道对不对,如果有幸被师傅看到了,希望得到一些指点
下一个小坑:机器码和opcode结合的vm逆向
4.安恒四月赛第三题
差点坑掉了这题,找了有一会的题目。。。俺也不记得当初放在那里了
当初并不会做,后来赛后看了同学的wp,同学是手逆做的(真的tql)
后来看了别的师傅的wp才知道sm4算法,所以在这里写一下
学习链接
SM4密码算法是一个分组算法,其算法设计简沽,结构有特点,安全高效。数据分组长度为128比特,密钥长度为128 比特。加密算法与密钥扩展算法均采取32轮迭代结构。SM4密码算法以字节(8位)和字节(32位)作为单位进行数据处理。SM4密码算法是对合运算,因此解密算法与加密算法的结构相同,只是轮密钥的使用顺序相反,解密轮密钥是加密轮密钥的逆序。
然后在github上找了一下
sm4解密参考
用法如下(转载自上述连接)
1. encrypt和decrypt
>>> from pysm4 import encrypt, decrypt
# 明文
>>> clear_num = 0x0123456789abcdeffedcba9876543210
# 密钥
>>> mk = 0x0123456789abcdeffedcba9876543210
# 加密
>>> cipher_num = encrypt(clear_num, mk)
>>> hex(cipher_num)[2:].replace('L', '')
'681edf34d206965e86b3e94f536e4246'
# 解密
>>> clear_num == decrypt(cipher_num, mk)
True
2. encrypt_ecb和decrypt_ecb
>>> from pysm4 import encrypt_ecb, decrypt_ecb
# 明文
>>> plain_text = 'pysm4是国密SM4算法的Python实现'
# 密钥
>>> key = 'hello, world!' # 密钥长度小于等于16字节
# 加密
>>> cipher_text = encrypt_ecb(plain_text, key)
>>> cipher_text
'ng3L4ldgvsZciAgx3LhplDvIzrd0+GXiNqNmd1VW0YOlwo+ojtpownOCbnxbq/3y'
# 解密
>>> plain_text == decrypt_ecb(cipher_text, key)
True
3. encrypt_cbc和decrypt_cbc
>>> from pysm4 import encrypt_cbc, decrypt_cbc
# 明文
>>> plain_text = 'pysm4是国密SM4算法的Python实现'
# 密钥
>>> key = 'hello, world!' # 密钥 长度小于等于16字节
# 初始化向量
>>> iv = '11111111' # 初始化向量 长度小于等于16字节
# 加密
>>> cipher_text = encrypt_cbc(plain_text, key, iv)
'cTsdKRSH2FqIJf22NHMjX5ZFHghR4ZtJ10wbNwj2//bJSElBXVeMtFycjdlVKP15'
# 解密
>>> plain_text == decrypt_cbc(cipher_text, key, iv)
True
然后在虚拟机里安装好之后
5.GKCTF chellys_identity
小声说all alone with you真的好听,pp真的好看
32位拖进ida,很多函数,很多套娃,索性这题比较好定位主函数
看一下二次判断里面
给出了一组数组,最后拿来比较的
然后看看加密那里
这里我一开始看有点看晕了,因为for循环那里吧三句话都看成for循环的子句了,后来才发现只有第一句(认真点啊)
生成质数那里代码还是比较明显的(说不定数据结构试验就考了)
加密函数那一段大大概就是,生成的数中,若比我输入的这个数小,就把它们都加起来,然后异或我输入的那个数,然后依次16位,在于我给定的数组相比较
exp
def primenum(n):
a=[]
for i in range(2,n+1):
for j in range(2,i):
if i%j==0:
break
else:
a.append(i)
return a
flag=''
v9=[438,1176,1089,377,377,1600,924,377,1610,924,637,639,376,566,836,830]
b=[]
b=primenum(128)
print(b)
for i in v9:
for j in range(30,127):
v6=0
k=0
for k in b:
if k<j:
v6+=k
else:
break
v8=j^v6
if v8==i:
flag+=chr(j)
print(flag)
6.tree
出自2020网鼎杯朱雀组re
这是一道二叉树的题目,没怎么做过与数据结构有关系的题目摸一摸,就当是复习数据结构实验了,hehe
32位拖进ida
主函数简单好定位,查看chkflag
emmm大概就是先判断长度,然后把我们输入的当中的数变成二进制的,比如第一个0对应0000,1对应0001,底下有一个如果不想等就不变
查看parse
这个两个函数一起看的意思呢,就是我们的输入的先进行01换算,然后呢顺着这个树找到根节点,根节点输出呢与最后那一个相同就好了
先看,parse的参数是root,当我们跟进root,查看他在内存当中的地址
.bss:00406040 _root dd ? ; DATA XREF: init(void)+1D7↑w
.bss:00406040 ; _main+43↑r
后面还有一个函数调用了它,跟进这个函数,f5。。反汇编之后的代码很长,很难看(对我来说很难了呜呜呜)硬解应该是能解出来的
od动调看看吧,字符串定位,下断,查看
然后跟进0x406530看看二叉树长啥样子
我们根节点是0x406530,跟着这个,可以开始画图了,怎么画呢,想到我们之前的函数,我们的v3分别左边+12,右边+16,这两个就是我们的左右子女结点,然后一路顺着,就找到对应的字母,如果一开始还是比较模糊,od跟进下面那个函数动调差不多能确定前几个
我的图如下(到这里为止自我感觉都很良好)
然后可以开始倒着推编写脚本了(呵呵我感觉都不用写脚本可以直接倒着推了)
#flag{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
v2='zvzjyvosgnzkbjjjypjbjdvmsjjyvsjx'
a={'y':'0000','b':'00010','q':'00011','g':'0010','f':'0011','j':'010','w':'01100',
'p':'01101','x':'011100','d':'0111010','i':'0111011','k':'01111','s':'100',
'z':'1010','n':'1011','c':'11000','t':'110010','e':'110011','h':'1101','o':'11100',
'l':'1110100','u':'11101010','r':'111010110','a':'111010111','m':'111011','v':'1111'}
b=list(a.keys())
c=list(a.values())
x=''
for i in v2:
for j in range(26):
if i==b[j]:
x+=c[j]
print(x)
flag=''
for i in range(0,len(x),4):
flag+=hex(int(x[i:i+4],2)) #二进制转十六进制,先转十进制再转十六
print(flag.replace('0x',''))
打印出来结果后按照他给的排就好了
呵呵,憨憨做完这道题目之后都是自我感觉良好的,做完之后去看了师傅们的wp
果然是一个废物,这里记录一下师傅们的写法
师傅的方法
重新回到ida,可以发现一个outtree函数(我我我我我哦我。。)
这标准的树。。连我这个没怎么好好听数据结构课的都(我有罪我忏悔)
然后神奇的地方来了,可以把原来调用parse的地方patch成outtree
然后再运行就能直接输出结构了
还有一种方法,利用idapython(呵呵我就是fw)
参考链接师傅的方法2
def traverse_leaf(pnode):
if pnode != 0:
if Dword(pnode + 12) == 0 and Dword(pnode + 16) == 0:
print(chr(Byte(pnode)))
print("".join(a))
lujing.append([chr(Byte(pnode)), “”.join(a)])
a.append(‘0’)
traverse_leaf(Dword(pnode + 12))
a.append(‘1’)
traverse_leaf(Dword(pnode + 16))
if pnode != 0X0406530:
a.pop()
traverse_leaf(0X0406530)
print(lujing)
这个脚本我还没研究懂,等我看透。。