HECTF2021 Reverse wp

前言

昨天结束的比赛,体验不错,Reverse拿了6个一血,也把Reverse的题目AK了,记录下。
请添加图片描述

文章目录

hard

直接拖进IDA进行分析,先查找字符串

请添加图片描述
直接发现flag:

HECTF{HElLo_RRRRe}

Baby_upx

根据题目提示,估计是加了upx壳,查壳:

请添加图片描述
果然加了upx壳,首先尝试用工具脱壳,发现脱壳失败,直接使用esp定律,手动dump下来,拖进ida分析:
请添加图片描述
先是检验了输入的格式前面是不是HECTF,然后将HECTF这五个字符与括号里的值与进行异或加密,最后与v4中的值进行比较,这里采用了rand()函数,但是没设置种子,所以是伪随机,直接写脚本解:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <Windows.h>
using namespace std;

VOID GetFlag()
{
	unsigned int v4[] = {
		0x7D2A2B17,0x72A211A,0x1010197D,0x40B1E15,0x42429
	};

	char key[] = "HECTF";
	char *res = (char*)v4;
	char flag[30] = { 0 };
	memset(flag, 0, 30);
	unsigned char middle = 0;
	int index = 0;
	for (int i = 0; i <20; i++) {
		
		
		index = (i ^ (rand() + 10086)) % 5;
		flag[i] = res[i] ^ key[index];
	}

	printf("%s\r\n", flag);


}
int main(int argc, char *argv[], char *envp[])
{
	GetFlag();
    system("pause");
	return 0;
}    

结果:
请添加图片描述
套上HECTF,得到flag:

HECTF{Thi5_iiS5_UUPX_LalAC}

Baby_pp

拖进IDA,发现了一些奇怪字符:

请添加图片描述
百度一下,怀疑是用Pyinstaller打包的,那么就是py程序

请添加图片描述
尝试使用pyinstxtractor和uncompyle6进行反编译,得到源码:

import random
ens = '742641edefb6770733ab5932325106b3a5fa75222791d09e451161c46f15504402b32737362443d4df7d136145cd970b54116669c230'
def encode(s, nuum):
    step = len(s) // nuum
    ens = ''
    for i in range(step):
        print(s[i::step])
        ens += s[i::step]
    else:
        return ens


def main():
    random.seed(10085)
    u_input = input(': ')
    t = ''
    for i in u_input:
		t += '%02x' % (ord(i) ^ random.randint(0, 127))
    else:
        eni = encode(t, 6)
        if eni == ens:
            print('Success!')
        else:
            print('Failed!')
    print(t)
if __name__ == '__main__':
    main()  

发现是先将字符串进行异或加密,当然这里种子设置的是固定的,所以rand()也是伪随机,然后打乱字符串的顺序,直接写脚本反向求解一下:

import random
import libnum
ens = '742641edefb6770733ab5932325106b3a5fa75222791d09e451161c46f15504402b32737362443d4df7d136145cd970b54116669c230'
def decode(eni,num):
    random.seed(10085)
    step=len(eni)//6
    dec=['1']*108
    total=0
    for i in range(step):
        for j in range(num):
            dec[j*step+i]=eni[total]
            total+=1

    dec=''.join(dec)
    print(dec)
    flag=''
    for i in range(0,len(dec),2):
        s=dec[i:i+2]
        middle=int(s,16)
        flag+=chr(middle^random.randint(0, 127))

    print(flag)
if __name__ == '__main__':
    #6是通过加密过程算出来的 加密的时候是每6位为一组
    decode(ens,6)

结果:
请添加图片描述
得到一串接近flag的flag,要求我们解密里面的字符串,观察发现里面的字符串只有01248五个数字组成,直接百度:
请添加图片描述
发现是云影密码,直接拿来用,最终脚本:

import random
import libnum
ens = '742641edefb6770733ab5932325106b3a5fa75222791d09e451161c46f15504402b32737362443d4df7d136145cd970b54116669c230'
def decode(eni,num):
    random.seed(10085)
    step=len(eni)//6
    dec=['1']*len(eni)
    total=0
    for i in range(step):
        for j in range(num):
            dec[j*step+i]=eni[total]
            total+=1

    dec=''.join(dec)
    print(dec)
    flag=''
    for i in range(0,len(dec),2):
        s=dec[i:i+2]
        middle=int(s,16)
        flag+=chr(middle^random.randint(0, 127))

    print(flag)
#云影密码 只有01248这5个数字
def decode_01248(c):
    dic = [chr(i) for i in range(ord("A"), ord("Z") + 1)]
    flag = []
    c2 = [i for i in c.split("0")]
    for i in c2:
        c3 = 0
        for j in i:
            c3 += int(j)
        flag.append(dic[c3 - 1])

    return ''.join(flag)

if __name__ == '__main__':
    #6是通过加密过程算出来的 加密的时候是每6位为一组
    decode(ens,6)
    eni="80410840840842108808881088408084210842"
    print("HECTF{"+decode_01248(eni)+"}")

得到flag:

HECTF{HELLOPYTHON}

baby-maze

题目标题提示,是关于迷宫的题目,拖进ida分析,找到main函数:

请添加图片描述
发现无法f5,怀疑加了花指令:
请添加图片描述
往下看发现确实加了花指令,这里就是jnz和jz两个构成绝对跳转,nop掉一个然后一个改成jmp,手动去一下花指令,这里贴两张修改后的图:
请添加图片描述
请添加图片描述
修改完后,f5查看伪代码:
请添加图片描述
发现可疑数据,和类似上下左右走路的判断,往下看.
请添加图片描述
会从v6里面取值,怀疑v6存储的就是地图,v6是一个数组,那么就是一个三维的地图,根据上面知道总共有6层,然后是通过dword_4033E4去取某层地图,说明控制的是Z坐标,dword_4033E0啥都没有操作,只是简单的加,说明控制的是x坐标,dword_4033E8乘了一个数,说明控制的是y坐标,继续往下看:
请添加图片描述
可以发现地图中值为2的就是终点,值为1就是路,值为0就是墙,那么就可以写脚本打印地图了:

maze1=[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
maze2=[0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00]
maze3=[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x01, 0x01, 0x00, 0x00]
maze4=[0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00,
  0x00, 0x00]
maze5=[0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]
maze6=[ 0x02, 0x00, 0x00]


for i in range(len(maze1)):
    if(maze1[i]==1):
        print("O",end="")
    else:
        print("X",end="")

    if((i+1)%6==0):
        print("")

print("------------------------")

for i in range(len(maze2)):
    if (maze2[i] == 1):
        print("O", end="")
    else:
        print("X", end="")

    if ((i + 1) % 5 == 0):
        print("")

print("------------------------")

for i in range(len(maze3)):
    if (maze3[i] == 1):
        print("O", end="")
    else:
        print("X", end="")

    if ((i + 1) % 4 == 0):
        print("")

print("------------------------")

for i in range(len(maze4)):
    if (maze4[i] == 1):
        print("O", end="")
    else:
        print("X", end="")

    if ((i + 1) % 3 == 0):
        print("")

print("------------------------")

for i in range(len(maze5)):
    if (maze5[i] == 1):
        print("O", end="")
    else:
        print("X", end="")

    if ((i + 1) % 2 == 0):
        print("")

print("------------------------")

for i in range(len(maze6)):
    if (maze6[i] == 1):
        print("O", end="")
    elif(maze6[i] == 2):
        print("*", end="")
    else:
        print("X", end="")

结果:
请添加图片描述
这里X代表不能走O代表能走,*代表终点。然后根据前面的swtich case来手动走一下迷宫:
请添加图片描述
得到flag:

HECTF{o....^oo0~0o^^o.^.O^.o^}

baby_and

发现是apk,使用jadx看一下:

请添加图片描述
发现将输入的值进行base64解码三次,然后发送给别的函数进行处理:
请添加图片描述
判断解密后的长度是不是4,如果是4再将值取名为key,然后送到别的函数进行处理:
请添加图片描述
先获取前面传过来的key,然后读取了JFIF.jpg的二进制,将图片的二进制与key进行异或运算,得到新的byte然后将这个byte转化一下,显示成图片,这里怀疑JFIF.jpg这张图片是经过加密的,这里就是一个解密操作,解密成正常的图片,将apk的后缀改成.zip,将JFIF.jpg提取出来,发现:
请添加图片描述
发现文件头格式不正确,所以我们得将这张图片进行解密,先通过jpg的文件头和文件尾,算出key,然后解密整张图片,写脚本:

with open("JFIF.jpg","rb") as f:
    imageBytes=f.read()
#jpg文件头 文件尾
res=[0xFF,0xD8,0xFF,0xD9]
key=[1]*4
bytes_list=list(imageBytes)

key[0]=res[0]^bytes_list[0]
key[1]=res[1]^bytes_list[1]
key[2]=res[2]^bytes_list[-2]
key[3]=res[3]^bytes_list[-1]

for i in key:
    print(chr(i),end="")
print("")

print(bytes_list)
for i in range(len(imageBytes)):
    bytes_list[i]=key[i%4]^bytes_list[i]

imageBytes=bytes(bytes_list)

#HECTF{He1l0_Android^_^}
with open("newJFIF.jpg","wb") as f:
    f.write(imageBytes)

得到一张图片:
请添加图片描述
得到flag:

HECTF{He1l0_Android^_^}

baby_anti

拖进IDA,找到main函数:

请添加图片描述
发现main函数无法f5,往下看发现加了花指令,跟baby_maze加的花指令类似,手动去下花指令,贴两张修改后的图:
请添加图片描述

请添加图片描述
f5查看伪代码:

请添加图片描述
先是规定了输入的长度,然后判断了下flag的格式,然后将我们的输入作了转化,且规定了输入的字符只能是数组和abcdef,往下分析:
请添加图片描述
经过动态调试发现,取出第i+1个转化后的值当作高位,取出第i个转化后的值当作低位合成一个byte类型的值,最后与v14进行比较,而v14的值前面赋值了:
请添加图片描述
然后写脚本得到flag了,先是通过v14得到转化后的值,再通过判断转化后的值是否小于0xA来判断属于哪个区间,由于求余的关系,求余前面的值不确定,采用爆破的方式获取,最终脚本:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <Windows.h>
using namespace std;
VOID GetFlag_Anti()
{
	char key[] = "Hel10_aN7i_Re";
	unsigned char res[26] = { 0 };
	int total = 0;
	for (int i = 0; i < 13; i++) {
		printf("%x\r\n", key[i]);
		res[total++] = key[i] >> 4;
		res[total++] = key[i] & 0xF;
		
	}
	char flag[30] = { 0 };
	memset(flag, 0, 30);
	for (int i = 0; i < 26; i++) {
		if (res[i] >= 10) {
			for (int j = 97; j <= 102; j++) {
				if ((j - i - 37) % 6 + 10 == res[i]) {
					flag[i] = j;
					break;
				}
				
			}
			

		}
		else {
			for (int j = 48; j <= 57; j++) {
				if ((j + i + 52) % 10 == res[i]) {
					flag[i] = j;
					break;
				}
				
			}
		}
	}

	printf("HECTF{%s}\r\n", flag);

	return;


}
int main(int argc, char *argv[], char *envp[])
{
	
	GetFlag_Anti();
	
	
	system("pause");
	return 0;

}

结果:
请添加图片描述
得到flag:

HECTF{47422b74515e480b70805c3920}

crypt

拖进IDA分析,找到main函数

请添加图片描述
先是检验了flag的格式,然后通过“-”字符取到括号里的每一段内容,这里只有当v5为偶数时才会进行加密,
往下看:
请添加图片描述
将加密后的结果值再进行加密,然后和一串字符串进行比较,这串字符看着像base64,查看一下sub_400A72:
请添加图片描述
发现就是base64的特征,查看一下base64的表:
请添加图片描述
发现表换掉了,那么就是将base64换表,我们解密一下:

import base64

string1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
s="7id81DpecpDeES8frwU4Qa=="
print(len(s))

a=base64.b64decode(s.translate(str.maketrans(string1,string2)))

a=list(a)

for i in a:
    #print(chr(i))
    print(hex(i),end=",")

得到:

0x84,0x80,0xe2,0x6e,0x73,0xc4,0x8,0xf9,0xc4,0xa3,0x68,0x85,0x45,0x6e,0x1e,0xd0

这串就是前面一次加密后的正确的结果值,再回头看一下:
请添加图片描述
这里是当v5为偶数时进行加密,而且是将&v7[v5-2]作为参数传递进去的,而这个v7数组进入加密之前就是我们输入的值,分析下sub_4008A4:
请添加图片描述
可以发现是两两为一组进行加密的,v5就是前一个值,v6就是后一个值,进行加密之后传出去,通过dowrd_602108和dword_60210c进行赋值。这里是累加上去,那么我们逆向的时候就是将上面base64解密得到的结果值,累减过去还原他,写脚本:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <Windows.h>
using namespace std;
VOID GetFlag_Crypt()
{
	unsigned char a3[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/";

	unsigned char encode[] = {
		0x84,0x80,0xe2,0x6e,0x73,0xc4,0x8,0xf9,0xc4,0xa3,0x68,0x85,0x45,0x6e,0x1e,0xd0
	};
	
	char input[] = "1234-5678-abcd-efgh";
	unsigned int dEncode[] = {
		0x6EE28084,0xF908C473,0x8568A3C4,0xD01E6E45
	};

	unsigned int flag[4] = { 0 };

	unsigned int v6 = 0;
	unsigned int v5 = 0;
	unsigned int middle = 0;
	unsigned int v7 = 0;
	int v8 = 0;
	//将v7的值还原的最后的情况
	for (int i = 0; i < 32; i++) {
		v7 -= 0x779EFF7A;
	}
	unsigned int end = v7;

	for (int i = 0; i < 2; i++) {
		v7 = end;
		v6=dEncode[i*2+1];
		v5=dEncode[i*2];
		for (int j = 0; j < 32; j++) {
			
			
			v8 = *(DWORD*)(4 * ((v7 >> 11) & 3) + a3);
			middle = (v5 + ((v5 >> 5) ^ (v5 * 16))) ^ (v7+v8);
			v6 = v6 - middle;

			v7 += 0x779EFF7A;
			middle = (v6 + ((v6 >> 5) ^ (16 * v6))) ^ (*(DWORD *)(4 * (v7 & 3) + a3) + v7);
			v5 = v5 - middle;
			
		
		}
		
		
		flag[i * 2 + 1] = v6;
		flag[i * 2] = v5;
	}

	char *Cflag = (char*)flag;
	printf("HECTF{");
	for (int i = 0; i <16; i++) {

		printf("%c", Cflag[i]);
		if ((i + 1) % 4 == 0 && i!=15) {
			printf("-");
		}
	}
	printf("}");
	

}
int main(int argc, char *argv[], char *envp[])
{
	GetFlag_Crypt();
	system("pause");
	return 0;

}

结果:
请添加图片描述
发现不对劲,是乱码,怀疑是脚本写错了,经过排查,没有问题,所以去利用ida远程调试了下加密函数,可能存在猫腻:
请添加图片描述
发现return后跳到了一个奇怪的地方,f5查看一下:
请添加图片描述
发现这里会调用一个函数,跟进去看下:
请添加图片描述
发现会将我们前面的加密的结果再进行一次异或加密,这里也是固定了随机数种子,所以是伪随机,且是linux下c产生的随机数,因为我本机环境是windows懒得去虚拟机下写c,直接手动提取到随机产生的值:

0x3e6,0x103

因此原来的脚本需要改下,要先将结果值进行异或,再进行还原,最终脚本:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <Windows.h>
using namespace std;
VOID GetFlag_Crypt()
{
	unsigned char a3[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/";

	unsigned char encode[] = {
		0x84,0x80,0xe2,0x6e,0x73,0xc4,0x8,0xf9,0xc4,0xa3,0x68,0x85,0x45,0x6e,0x1e,0xd0
	};
	
	char input[] = "1234-5678-abcd-efgh";
	unsigned int dEncode[] = {
		0x6EE28084,0xF908C473,0x8568A3C4,0xD01E6E45
	};

	unsigned int flag[4] = { 0 };

	unsigned int v6 = 0;
	unsigned int v5 = 0;
	unsigned int middle = 0;
	unsigned int v7 = 0;
	int v8 = 0;
	for (int i = 0; i < 32; i++) {
		v7 -= 0x779EFF7A;
	}
	unsigned int end = v7;

	for (int i = 0; i < 2; i++) {
		v7 = end;
		//去linux下拿到随机数
		//srand(0x2710)
		v6 = dEncode[i * 2 + 1] ^ 0x3e6;
		v5 = dEncode[i * 2] ^ 0x103;
		for (int j = 0; j < 32; j++) {
			
			
			v8 = *(DWORD*)(4 * ((v7 >> 11) & 3) + a3);
			middle = (v5 + ((v5 >> 5) ^ (v5 * 16))) ^ (v7+v8);
			v6 = v6 - middle;

			v7 += 0x779EFF7A;
			middle = (v6 + ((v6 >> 5) ^ (16 * v6))) ^ (*(DWORD *)(4 * (v7 & 3) + a3) + v7);
			v5 = v5 - middle;
			
		
		}
		
		
		flag[i * 2 + 1] = v6;
		flag[i * 2] = v5;
	}

	char *Cflag = (char*)flag;
	printf("HECTF{");
	for (int i = 0; i <16; i++) {

		printf("%c", Cflag[i]);
		if ((i + 1) % 4 == 0 && i!=15) {
			printf("-");
		}
	}
	printf("}");
	

}
int main(int argc, char *argv[], char *envp[])
{
	
	
	GetFlag_Crypt();
	
	
	system("pause");
	return 0;

}

得到flag:

HECTF{1024-2048-abcd-hhhh}

flag

拖进IDA分析

请添加图片描述
发现没有任何符号,然后查看字符串
请添加图片描述
发现有很多.go字符,怀疑是golang写的,通过查资料,发现有一款IDA插件,IDAGolangHelper,可以进行符号识别,使用一下:
请添加图片描述
发现符号都识别出来了,然后也找到了主函数:
请添加图片描述
分析一下:
请添加图片描述
验证了下flag的格式,然后将输入的flag的每一个字符通过0扩展成四个字节存入.
请添加图片描述
下面就是加密函数加密很多次,不过这个加密类似前面crypt的加密,可以从后面反推回去:
请添加图片描述
验证函数就是这个main_flag,进去看一下:
请添加图片描述
就是将加密后的结果值,与上面那些值进行比较。我们再回去看一下:
请添加图片描述
这个v29算的有点麻烦,经过ida远程调试发现最后的v30等于v28+1,当v28=29时,v30=0,然后这里一些DWORD1,DWORD2等不认识的转化:

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
经过ida远程调试发现了取值关系,这里还需要一个v55的值,v55的值在最开头赋值了:
请添加图片描述
因此将验证函数的结果值作为加密后的值进行反推回去,写脚本:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <Windows.h>
using namespace std;
VOID GetFlag_GoLang()
{
	
	unsigned int res[] = {
		0x67185A11,0xE98651DC,0x354A7497,0x104A33B2,0x4B0D80A6,0x7B7DAD7A,
		0xEEAB3824,0xFC48315E,0x31AAF1C5,0x577EE036,0x65938F6C,0xDB52C493,
		0xA20EACE0,0xCC54C27D,0x54ECC3C1,0xF7F51FA6,0xBF187DA8,0x4F723FB1,
		0x0F936A37,0x466E0D5D,0x42BBA543,0x707D816C,0xEA694ED4,0xA4C6D06F,
		0x266E4F35,0x3246607F,0xD91B6BFD,0x7933B9A4,0x1185EED8,0xE2E4347D
	};
	

	

	int v22=0;
	ULONG64 v21 = 0;
	ULONG64 v26 = 0;
	ULONG64 v25 = 0;
	ULONG64 v30 = 0;
	ULONG64 v29 = 0;

	DWORD dv55[4] = {
		0x64B71598,0xD5FB3BED,0x1185971C,0x6F61074
	};

	
	//还原v22最后的值
	for (int i = 0; i < 31; i++) {
		v22 += 0xEAE5AD5;
	}
	
	unsigned int flag[30] = { 0 };
	unsigned int middle = 0;
	unsigned int v23 = 0;
	
	for (int i = 0; i < 32; i++) {
        //反推回去还原
		for (int j = 29; j >= 0; j--) {
			if (j == 29) {
				res[0] -= (res[29] + v22 + 0xEAE5AD5) ^ (dv55[2] + 16 * res[29]) ^ (dv55[3] + (res[29] >> 5));
				res[29] = res[29]-((v22 + res[0] + 0xEAE5AD5) ^ (dv55[0] + 16 * res[0]) ^ (dv55[1] + (res[0] >> 5)));
			}
			else {
				res[j+1]-= (res[j] + v22 + 0xEAE5AD5) ^ (dv55[2] + 16 * res[j]) ^ (dv55[3] + (res[j] >> 5));
				res[j]= res[j] - ((v22 + res[j+1] + 0xEAE5AD5) ^ (dv55[0] + 16 * res[j+1]) ^ (dv55[1] + (res[j+1] >> 5)));
			}


		}


		v22 -= 0xEAE5AD5;


	}

	for (int i = 0; i < 30; i++) {
		printf("%c", res[i]);
	}
	
}
int main(int argc, char *argv[], char *envp[])
{
	
	
	GetFlag_GoLang();
	
	system("pause");
	return 0;

}

结果:
请添加图片描述
得到flag:

HECTF{Lets_g0_t0_dr1nk_tea!!!}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值