2021 Geek re
一年一度的极客大挑战,做完了逆向,题目质量有高有低,一些适合新生,一些又不适合新生,写wp给新生看看,毕竟大家都是这么过来的。
Re0
签到题 ida打开,shift + f12查看字符串就行。
SYC{Welcome_to_Geek_challenge2021}
顺便也将几个常用的快捷键讲讲吧
f5 反编译出类似于c代码的窗口
h键 10进制和16进制互换
d键 将IDA view-RIP解面的单个字节进行转换,可转换为2个,3个,4个,甚至更多个字节。
u键 将多个字节数据,甚至是汇编字节码进行拆分为单个字节的。
c键 重新分析为汇编代码
p键 重新生成函数
/键 在汇编解密也可以看到f5的伪代码,方便在汇编界面调试,前提是代码能f5
r键 转换为字符串类型的样子。
n键 修改变量 函数名称
y键 修改变量类型,使代码更加好看
m键 查看一些api的参数名称
还有些set ip呀,重新定义数组呀之类操作。
还有些好用的ida插件也都说说。
lazyida 可以快速dump出想要类型的数据,word,dword,等等。
findcrypto3 可以用来查看是否有某些常见加密算法的特征值,从而快速定位到关键位置。
keypatch 用来打补丁的,个人用的较少。
bindiff 恢复符号表的好东西,他可以匹配代码的相似度来达到恢复符号表的功能,特别是elf文件动调时,经常遇到符号表不见了的情况,就可以使用bindiff来恢复。
刘壮桌面美化大师
好像是一个桌面控件,弄一下就可以出。但是我感觉是签到题,就直接用jeb去看了看字符串,然后就在里面。
也是一个安卓题常用到的知识点吧,就是字符一般都会在jeb左上角的这个目录下。
Resource/values/strings.xml
当然也可以用apktool来解包。
SYC{We1c0m3_t0_4ndRo1d_ReV3rse!}
买Activity
这个题,还是需要了解点安卓,好像是启动一个活动就可以得到flag,但是本人没具体搞过安卓,所以做的方式可能比较奇怪。
先是用jeb看了看decode函数,发现好像是个自解密,先从native层得到一串字符然后去异或。
所以我们只需要得到native层的函数就行,先解压apk,分析so文件。
函数Java_com_sorrowrain_buyactivity_Decode_stringFromNative里面有两串字符串,后面一堆混淆,解题后推测应该是个栅栏自解密。
s1='CSD!Os!yiyO#|iU`bu1'
s2='Ikxc$dFdOCBq!Oh dtm'
for i in range(len(s1)):
print(chr(ord(s1[i])^16),end='')
print(chr(ord(s2[i]) ^ 16),end='')
#SYC{Th1s_4ct1Vity_iS_R3al1y_Exp0rted!}
Re1
发现就是一个base64+一个异或
#include<stdio.h>
int main()
{
int v9[60];
v9[0] = 21;
v9[1] = 113;
v9[2] = 44;
v9[3] = 4;
v9[4] = 37;
v9[5] = 113;
v9[6] = 40;
v9[7] = 16;
v9[8] = 21;
v9[9] = 44;
v9[10] = 121;
v9[11] = 40;
v9[12] = 34;
v9[13] = 45;
v9[14] = 18;
v9[15] = 38;
v9[16] = 25;
v9[17] = 45;
v9[18] = 6;
v9[19] = 58;
v9[20] = 26;
v9[21] = 20;
v9[22] = 25;
v9[23] = 112;
v9[24] = 24;
v9[25] = 114;
v9[26] = 6;
v9[27] = 57;
v9[28] = 26;
v9[29] = 22;
v9[30] = 121;
v9[31] = 112;
v9[32] = 33;
v9[33] = 7;
v9[34] = 22;
v9[35] = 38;
v9[36] = 25;
v9[37] = 45;
v9[38] = 6;
v9[39] = 58;
v9[40] = 33;
v9[41] = 24;
v9[42] = 14;
v9[43] = 38;
v9[44] = 34;
v9[45] = 114;
v9[46] = 26;
v9[47] = 38;
v9[48] = 35;
v9[49] = 45;
v9[50] = 22;
v9[51] = 114;
v9[52] = 26;
v9[53] = 24;
v9[54] = 10;
v9[55] = 58;
v9[56] = 26;
v9[57] = 24;
v9[58] = 112;
v9[59] = 125;
int i;
for(i=0;i<60;i++)
{
printf("%c",v9[i]^0x40);
}
}
//U1lDe1hPUl9hbmRfYmFzZTY0X2FyZV90aGVfYmFzaXNfb2ZfcmV2ZXJzZX0=
然后base64解密
SYC{XOR_and_base64_are_the_basis_of_reverse}
调试
考点就是ida动调elf文件,和简单的汇编识别能力,需要知道直接调一遍的流程是正确的,需要我们修改流程,而且该题也是个自解密。
但是实际上静态可以做,只需要简单改点汇编,去个花就可以看了。
正常情况我们是无法f5看完整代码的,原因如下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-glJ0ndem-1636989574198)(https://i.loli.net/2021/11/10/6uiHIvt7ec92VzZ.png)]
是在11c9处u键,p键
修改后就可以看伪代码了。
flag:SYC{C0ngr@tuIatlOns_thls_1s_th3_r!gHt_f!ag}
珍惜生命
pyc文件,利用uncompyle6还原为py代码,然后分析py代码。
简单分析后发现考点是z3约束器解方程式。
先用z3求出key
from z3 import *
s = Solver()
key = [BitVec('key[%d]' % i, 16) for i in range(8)]
KEY_cmp = 'Syclover'
key_cmp = [0]*8
key_cmp[0] = (key[1] * key[2] - key[5] * 72 - key[4] * 3 - key[3] ^ key[1] + (key[3] << 2) + key[2] * 6 - key[7] & key[6] - 1000) - 14
key_cmp[1] = (key[5] * 7 + key[3] * 3 + key[2] + key[6] - (key[2] >> 2) - key[1] ^ key[0] + key[7] + (key[4] ^ key[1]) + (key[4] | key[7])) - 801
key_cmp[2] = (key[6] * 5 + key[2] * 6 - key[3] * 7 + key[4] | key[5] + key[4] * 10 + key[0] ^ key[1] * 3 - key[7] + key[0] + key[1]) - 924
key_cmp[3] = key[1] * 3 + key[5] * 9 + key[0] + key[2] * 2 + key[3] * 5 - key[4] * (key[6] ^ key[7]) + 321 - 16
key_cmp[4] = (key[5] * 12 - key[0] ^ key[6] - key[3] * 23 + key[4] * 3 + key[2] * 8 + key[1] - key[7] * 2 + key[6] * 4 + 1324) + 1
key_cmp[5] = key[3] * 54 - key[1] * 3 + key[2] * 3 + key[4] * 11 - key[5] * 2 + key[0] + key[7] * 3 - key[6] - 6298 + 40
key_cmp[6] = key[7] - key[6] * key[3] + key[2] * key[2] - key[4] * 32 + key[5] * (key[0] >> 2) - key[1] * key[1] - 6689 + 41
key_cmp[7] = (key[5] - key[3] * 41 + key[6] * 41 + key[5] ^ (key[4] & key[6] | key[0]) - (key[7] * 24 | key[2]) + key[1] - 589) - 36
for i in range(8):
s.add(key_cmp[i]==ord(KEY_cmp[i]))
print(s.check())
m=s.model()
print(m)
然后异或
key=[0]*8
key[6] = 54
key[1] = 38
key[3] = 99
key[0] = 83
key[7] = 46
key[2] = 121
key[5] = 45
key[4] = 64
key_cmp = ''
key_cmp += chr((key[1] * key[2] - key[5] * 72 - key[4] * 3 - key[3] ^ key[1] + (key[3] << 2) + key[2] * 6 - key[7] & key[6] - 1000) - 14)
key_cmp += chr((key[5] * 7 + key[3] * 3 + key[2] + key[6] - (key[2] >> 2) - key[1] ^ key[0] + key[7] + (key[4] ^ key[1]) + (key[4] | key[7])) - 801)
key_cmp += chr((key[6] * 5 + key[2] * 6 - key[3] * 7 + key[4] | key[5] + key[4] * 10 + key[0] ^ key[1] * 3 - key[7] + key[0] + key[1]) - 924)
key_cmp += chr(key[1] * 3 + key[5] * 9 + key[0] + key[2] * 2 + key[3] * 5 - key[4] * (key[6] ^ key[7]) + 321 - 16)
key_cmp += chr((key[5] * 12 - key[0] ^ key[6] - key[3] * 23 + key[4] * 3 + key[2] * 8 + key[1] - key[7] * 2 + key[6] * 4 + 1324) + 1)
key_cmp += chr(key[3] * 54 - key[1] * 3 + key[2] * 3 + key[4] * 11 - key[5] * 2 + key[0] + key[7] * 3 - key[6] - 6298 + 40)
key_cmp += chr(key[7] - key[6] * key[3] + key[2] * key[2] - key[4] * 32 + key[5] * (key[0] >> 2) - key[1] * key[1] - 6689 + 41)
key_cmp += chr((key[5] - key[3] * 41 + key[6] * 41 + key[5] ^ (key[4] & key[6] | key[0]) - (key[7] * 24 | key[2]) + key[1] - 589) - 36)
print(bytes(key))
flag = [113, 74, 71, 35, 29, 91, 29, 12, 114, 73, 60, 52, 69, 5, 113, 35, 95, 38, 20, 112, 95, 7, 74, 12, 102, 23, 7, 31, 87, 5, 113, 98, 85, 38, 16, 112, 29, 6, 30, 12, 65, 73, 83, 36, 12, 23]
for i in range(46):
print(chr(flag[i] ^ key[((i + 1) % len(key))]),end='')
#W3$c0m3_T0_th3_py_w0r1d_@nd_z3_1s_s0000_g00d!!
new_language
考察net逆向,也就是c#语言,使用dnspy就行,甚至还可以调试。
简单分析后,发现是aes的换盒,也没啥好讲的。实际上就是求box的index。
box=[99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,118,202,130,201,125,250,89,71,240,173,212,162,
175,156,164,114,192,183,253,147,38,54,63,247,204,52,165,229,241,113,216,49,21,4,199,35,195,24,150,5,
154,7,18,128,226,235,39,178,117,9,131,44,26,27,110,90,160,82,59,214,179,41,227,47,132,83,209,0,237,32,
252,177,91,106,203,190,57,74,76,88,207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,
64,143,146,157,56,245,188,182,218,33,16,255,243,210,205,12,19,236,95,151,68,23,196,167,126,61,100,93,
25,115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,219,224,50,58,10,73,6,36,92,194,211,172,98,
145,149,228,121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,180,198,
232,221,116,31,75,189,139,138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,158,225,248,152,17,105,
217,142,148,155,30,135,233,206,85,40,223,140,161,137,13,191,230,66,104,65,153,45,15,176,84,187,22]
enc=[64,249,133,69,146,253,253,207,182,4,157,207,251,4,60,81,59,77,146,77,207,26,38,207,64,77,177,77,64,195,77,253,253]
for i in enc:
print(chr(box.index(i)),end='')
#right!!_y0u_c0mpIete_C#_reVer3e!!
easypyc
文件名称为,easypyc.exe,应该是py打包的exe,对于这种题型我们需要先用pyinstxtractor.py解包exe,然后将生成文件夹中的pyc文件利用uncompyle6还原为py代码。
代码如下
# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)]
# Embedded file name: easypyc.py
whatbox = [
0] * 256
def aaaaaaa(a, b):
k = [
0] * 256
t = 0
for m in range(256):
whatbox[m] = m
k[m] = ord(a[(m % b)])
else:
for i in range(256):
t = (t + whatbox[i] + k[i]) % 256
temp = whatbox[i]
whatbox[i] = whatbox[t]
whatbox[t] = temp
def bbbbbbbbbb(a, b):
q = 0
w = 0
e = 0
for k in range(b):
q = (q + 1) % 256
w = (w + whatbox[q]) % 256
temp = whatbox[q]
whatbox[q] = whatbox[w]
whatbox[w] = temp
e = (whatbox[q] + whatbox[w]) % 256
a[k] = a[k] ^ whatbox[e] ^ 102
print(whatbox[e] ^ 102,end=',')
def ccccccccc(a, b):
for i in range(b):
a[i] ^= a[((i + 1) % b)]
else:
for j in range(1, b):
a[j] ^= a[(j - 1)]
if __name__ == '__main__':
kkkkkkk = 'Geek2021'
tttttt = [117, 62, 240, 152, 195, 117, 103, 74, 240, 151, 173, 162, 17, 75, 141, 165, 136, 117, 113, 33, 98, 151, 174, 4, 48, 25, 254, 101, 185, 127, 131, 87]
ssss = input('Please input your flag:')
inp = [0] * len(ssss)
if len(ssss) != 32:
print('Length Error!!!!')
exit(0)
for i in range(len(ssss)):
inp[i] = ord(ssss[i])
else:
aaaaaaa(kkkkkkk, len(kkkkkkk))
bbbbbbbbbb(inp, 32)
ccccccccc(inp, 32)
for m in range(32):
if tttttt[m] != inp[m]:
raise Exception('sorry your flag is wrong')
print('success!!!!!!')
print('your flag is {}'.format(ssss))
分析下就发现,是个魔改的rc4,然后加了点数组内异或操作,数组异或我就没自己逆了,直接用z3他不香吗。
先用z3解
enc = [117, 62, 240, 152, 195, 117, 103, 74, 240, 151, 173, 162, 17, 75, 141, 165, 136, 117, 113, 33, 98, 151, 174, 4, 48, 25, 254, 101, 185, 127, 131, 87]
def ccccccccc(a, b):
for i in range(b):
a[i] ^= a[((i + 1) % b)]
else:
for j in range(1, b):
a[j] ^= a[(j - 1)]
from z3 import *
s = Solver()
flag = [BitVec('flag[%d]' % i, 8) for i in range(32)]
ccccccccc(flag,32)
for i in range(32):
s.add(flag[i]==enc[i]);
print(s.check())
m=s.model()
print(m)
再随便输入合适长度的flag,调试或打印出rc4异或的值,然后异或。
flag=[0]*32
flag[8] = 104
flag[3] = 210
flag[16] = 135
flag[15] = 175
flag[26] = 59
flag[11] = 143
flag[18] = 87
flag[9] = 210
flag[6] = 87
flag[24] = 38
flag[22] = 181
flag[30] = 93
flag[14] = 105
flag[0] = 34
flag[25] = 18
flag[28] = 71
flag[19] = 83
flag[10] = 181
flag[20] = 3
flag[23] = 140
flag[21] = 64
flag[7] = 69
flag[17] = 170
flag[4] = 186
flag[29] = 155
flag[2] = 28
flag[5] = 225
flag[12] = 128
flag[13] = 51
flag[27] = 220
flag[31] = 161
flag[1] = 87
xor=[113,14,95,169,240,148,36,49,55,179,234,202,229,86,12,202,226,203,36,42,92,18,214,184,121,96,82,187,47,239,98,220]
for i in range(32):
print(chr(xor[i]^flag[i]),end='')
#SYC{Just_a_Eeeeeeasy_Rc4_right?}
Brute_force
go语言逆向,现在ida7.6已经很好的解决了go语言符号表恢复的问题,主要是从main_main开始分析,做这道题主要是考察动调的能力,通过分析可知,使用的命令行传参。
如何命令行传参呢。
开始分析。
接下来分析加密函数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o3wbB5dD-1636989574204)(https://i.loli.net/2021/11/11/zuGAfO7xMqpd8vC.png)]
爆破脚本
import hashlib
table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/_"
list1=['957a3926d4ff16d0d3bac4ed3044537b','d7bf94ada03842f20934c5605728fbc5','5781f597ce91fb5f66057f35846bcb78','ae4b9b5b2468a3d15b6b8690b761e160','4ad50c4af2c5beda7aca217afbac9bd6', 'f40b339be4c5898741b8a0d2a8007bd5']
for j in list1:
for a in range(30,128):
for b in range(30,128):
for c in range(30,128):
for d in range(30,128):
str = chr(a)+chr(b)+chr(c)+chr(d)
flag = hashlib.md5(str.encode('utf-8')).hexdigest()
if flag == j.casefold():
print(str, end='')
#7h3_g0_pr0g@rm_13_slgned
Win32
upx脱壳,后发现经典的win32窗口,如果了解过一些win32知识,做起肯定是简单的,如果没了解过,也没关系,因为这个题因为出题人的小失误,变成了签到题。
对于这种题型,首先需要去找到,消息处理函数,其实我们对窗口做一些操作,比如说关闭窗口,点击窗口,输入东西,都会记录在消息列队里面,有些消息是由系统自己处理,有些就是由出题人自己写的处理方法处理,这些方法就在消息处理函数中。
这个函数就在winmain中的sub_1400011EC,学习下windows窗口的创建就可以知道了。
先创建了个子窗口
然后是0x111
WM_COMMAND
0x0111
选择窗口菜单项或某个控件发送一条消息给它的父窗口或按下一个快捷键时产生此消息
这意味着我们按下试试呢,就会进入这个case语句。
所以这道题解个base64就行。
flag
SYC{y0u_g3t_A_f1ag_by_cyberloafing_auth0r}
wasm
这个题就是一种题型,可以先看看这两篇文章,介绍了这种题怎么出的,和怎么解。
https://www.anquanke.com/post/id/179556
https://blog.csdn.net/TurkeyCock/article/details/83317935
所以需要安装wabt(https://github.com/WebAssembly/wabt),linux下安装就行。
$ mkdir build
$ cd build
$ cmake ..
$ cmake --build .
根据个人情况,先安装camke,第3条指令会报错,需要加上报错语句里面的最后一句话当参数,off什么来着,方可成功。
之后就可以跟着文章做,得到o文件。
分析o文件,发现init_memory导入了很多数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ltQzHHfn-1636989574206)(https://i.loli.net/2021/11/15/VwCXfIqmQ52Fjrz.png)]
然后看main,发现了下面的语句
if ( i32_load(&w2c_memory, v5 + 88, ((unsigned __int64)(unsigned int)v5 + 88) >> 32) != 22 )
i32_store8(&w2c_memory, v4, 0, v3 ^ 0x66);
所以推测就是长度为22的数据,然后和0x66异或,实际上爆破都行,题目文件也给了xor。
enc=[0x35, 0x3F, 0x25, 0x1D, 0x11, 0x07, 0x15, 0x0B, 0x39, 0x2F,
0x15, 0x39, 0x35, 0x56, 0x39, 0x21, 0x09, 0x56, 0x02, 0x47,
0x47, 0x1B]
for i in enc:
print(chr(i^0x66),end='')
#SYC{wasm_Is_S0_Go0d!!}
猜拳
也是一个win32程序,估计加了保护器,所有代码都被加密了。没事,动调就行,在动调的过程中直接f9,还发现了反调试,所以就可以根据反调试来找到main函数了,一种是慢慢调试,找出弹出反调试的messagebox的窗口在哪,第二种就是调试过程中,找到isdebugprevent函数,打个断点,然后断下来。
winmain
仔细分析下,消息处理函数,发现有个函数里面还有switch case,并且有个case在rc4解密,然后调用messagebox,估计就是打印flag了。
然后在7FF6E4761567处直接set ip,然后f8调试,就会弹出一个有flag的窗口了。
SYC{gR3@t1_Y0U_ch3@t_@nd_w!n_gam3}
have a tea
一个elf文件,也是一个类似于加壳的文件,静态看不到代码,动调就行。
动调执行完最下面的jmp指令,就可以看到和elf文件常见的start函数了,开始分析,发现是利用fork创建了个子进程,然后就不能调试了,一种类似于CreateProcess的反调试方法,只不过可以用set ip来强行调试那个关键函数。
set ip 调试sub_5576384552BD函数,发现加密方式就是将字符串变为unsigned int类型,然后两个为一组,先异或,然后进行tea加密,异或的值是前一组加密后的值,第一组是和key的1,2个进行异或。
//try_hardgive_you_cup_tea
//
//SYC{abcdefghijklmnopqrstuvwxyz1234567}
//0x65766967, 0x756F795F, 0x7075635F, 0x6165745F
#include<stdio.h>
void encrypt(unsigned int *flag , unsigned int *key)
{
unsigned int v0,v1,sum=0,i;
unsigned int delta=0x61C88647;
v0=flag[0];
v1=flag[1];
for(i=0;i<32;i++)
{
sum-=delta;
v0+=( (v1<<4)+key[0] ) ^ ( v1+sum ) ^((v1>>5)+key[1]);
v1+=( (v0<<4)+key[2] ) ^ ( v0+sum ) ^((v0>>5)+key[3]);
}
flag[0]=v0;
flag[1]=v1;
}
void decrypt(unsigned int *code , unsigned int *key)
{
unsigned int delta=0x61C88647;
unsigned int v0,v1,sum=0xC6EF3720,i;// sum=0xC6EF3720
v0=code[0];
v1=code[1];
for(i=0;i<32;i++)
{
v1-=( (v0<<4)+key[2] ) ^ (v0+sum) ^ ( (v0>>5)+key[3] );
v0-=( (v1<<4)+key[0] ) ^ (v1+sum) ^ ( (v1>>5)+key[1] );
sum+=delta;
}
code[0]=v0;
code[1]=v1;
}
//0x7b435953, 0x79615379 0x446e6153 0x5f6e6169 0x6e30685a 0x5f614c5f 0x5f6e3179 0x5f616843 0x6e614978 0x0202007d
int main()
{
unsigned int key[6]={0x5F797274,0x64726168,0x65766967, 0x756F795F, 0x7075635F, 0x6165745F};
unsigned int code[10]={0xC9FA3B95, 0x7CFD0735, 0x958C7C9F, 0xC143B59E, 0x61741E89, 0xF47DCDC4, 0xD6E2A1F2, 0x6A38E9AD,
0xC2C16FEB, 0x8C0EE999};
unsigned int flag[10]={0x7b435953, 0x79615379 ,0x446e6153 ,0x5f6e6169 ,0x6e30685a ,0x5f614c5f ,0x5f6e3179 ,0x5f616843 ,0x6e614978 ,0x0202007d};
int i;
for(i=0;i<1;i++)
{
decrypt(&code[0],&key[2]);
code[0]^=key[0];
code[1]^=key[1];
printf("%08x %08x\n",code[0],code[1]);
}
for(i=0;i<10;i++)
{
//printf("%08x:",code[i]);
printf("%c%c%c%c",*((char*)&flag[i]+0),*((char*)&flag[i]+1),*((char*)&flag[i]+2),*((char*)&flag[i]+3));
}
//printf("%08x %08x",code[0],code[1]);
}
//SYC{ySaySanDian_Zh0n_La_y1n_Cha_xIan}