easyre-xctf
题目
龟缩起来,保护自己
题解
扔进die里面看一下,UPX加壳程序,脱壳之后拉进IDA,可以找到后半截flag。
在字符串里面找不到前半截,看一眼是不是有其他函数在负责处理,可以找到part1函数。
这里用伪代码只能得到一部分flag,在汇编里面才能看见后面还拼接了_4n。
666
题解
先IDA发现是把输入串通过encode函数加密后与enflag的值对比。
encode函数每三个字符做一轮操作,直接反向即可解出flag,这里注意异或运算符优先级低于加减,要带括号。
enflag="izwhroz\"\"w\"v.K\".Ni"
realflag=""
for i in range (0,18,3):
realflag+=chr((18^ord(enflag[i]))-6)
realflag+=chr((18^ord(enflag[i+1]))+6)
realflag+=chr(18^ord(enflag[i+2])^6)
print(realflag)
easyRE1
题解
IDA一把唆,用flag{}包裹。

Reversing-x64Elf-100
题解
IDA发现关键函数是sub_4006FD。

__int64 __fastcall sub_4006FD(__int64 a1)
{
int i; // [rsp+14h] [rbp-24h]
__int64 v3[4]; // [rsp+18h] [rbp-20h]
v3[0] = (__int64)"Dufhbmf";
v3[1] = (__int64)"pG`imos";
v3[2] = (__int64)"ewUglpt";
for ( i = 0; i <= 11; ++i )
{
if ( *(char *)(v3[i % 3] + 2 * (i / 3)) - *(char *)(i + a1) != 1 )
return 1LL;
}
return 0LL;
}
可以看出函数逻辑是有一个3个元素的字符串数组,然后每个数组进行加减操作,然后与输入的字符作比较,反向编写代码得到flag。
str=["Dufhbmf","pG`imos","ewUglpt"]
flag=""
i=0
print(flag)
for i in range(0,12) :
print(i)
flag+=chr(ord(str[i%3][2*(int(i/3))])-1)
print(flag)
lucknum
题解
IDA直接可以看见flag。

1000Click
题解
打开程序,提示点击1000次或者逆向获得flag。
逆向试了一下,没找到线索,直接上CE。
找到内存,修改为999,然后再点击一次,获得flag。
xxxorrr
题解
IDA逆向,只能看见一个main函数,找不到逻辑,这里学到了一个知识点。
在 ELF (Executable and Linkable Format) 文件中,Initialization Function Table 是一种特殊的数据结构,用于存储在程序启动时(在 main 函数执行之前)需要调用的一系列初始化函数的地址。这个机制允许程序或库执行必要的初始化任务,例如设置全局变量的初始状态、注册回调函数、初始化硬件设备或执行其他任何启动前准备工作。
如果 sub_840 的地址被包含在 ELF 的 Initialization Function Table 中,那么它会在程序的主逻辑开始执行之前被自动调用。这种自动调用是由程序的启动代码(通常是由编译器和链接器自动生成的一部分)负责的,该代码会遍历 Initialization Function Table 中的所有条目,并按照它们出现的顺序调用每个函数。

可以看出会按顺序调用sub_840、sub_84A、sub_8C3、sub_800。
跟一下这几个函数,发现只有84A有一些操作,对s1进行了一轮异或运算,同时可以直接定位到s1的值。
unsigned __int64 sub_84A()
{
int i; // [rsp+Ch] [rbp-14h]
unsigned __int64 v2; // [rsp+18h] [rbp-8h]
v2 = __readfsqword(0x28u);
for ( i = 0; i <= 33; ++i )
s1[i] ^= 2 * i + 65;
return __readfsqword(0x28u) ^ v2;
}

回到main函数,发现注册了sub_916,跟进一下,发现这里是最终判断的函数
unsigned __int64 sub_916()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
v1 = __readfsqword(0x28u);
if ( !strcmp(s1, byte_201060) )
puts("Congratulations!");
else
puts("Wrong!");
return __readfsqword(0x28u) ^ v1;
}
在此之前,main函数里面又进行了一轮异或,是用s1和我们的输入值进行异或,然后进入sub_916,与byte_201060进行比较,可以读到这个串的值。

因此,我们的合理输入,就应该是s1先本身进行一轮异或,再与byte_201060进行一轮异或。
s1="qasxcytgsasxcvrefghnrfghnjedfgbhn"
s2=[ 0x56, 0x4E, 0x57, 0x58, 0x51, 0x51, 0x09, 0x46, 0x17, 0x46,
0x54, 0x5A, 0x59, 0x59, 0x1F, 0x48, 0x32, 0x5B, 0x6B, 0x7C,
0x75, 0x6E, 0x7E, 0x6E, 0x2F, 0x77, 0x4F, 0x7A, 0x71, 0x43,
0x2B, 0x26, 0x89, 0xFE, 0x00]
flag=""
for i in range (0,33):
flag+=chr(ord(s1[i])^(2*i)+65)
flag2=""
for i in range(0,33):
flag2+=chr(ord(flag[i])^s2[i])
print(flag2)
reverse_re3
题目
我从不走回头路,但你能找到我选择的路吗
题解
main函数很简单,直接调用了sub_940()
940点进去,有一些逻辑,并且最后提示flag是输入的md5值。
100、115、119、97对应的字符分别是d s w a,考虑是走迷宫的题。
每一个选项进去都是一个子函数,都使用了dword_202020这个变量,并且步进为15,那么迷宫应该是15个字符为一行,并且可以看出,字符为1的地方是路,那么字符为0就是墙,3是当前位置,4是迷宫出口。
进入dword_202020变量,是一个数组,通过shift+E对变量进行提取。
对提取的数据进行处理,15个字符一行,从3开始走到4
111110000000000
111110311000000
111110001000000
111110001000000
111110001111100
111110000000100
111110000000100
111110000000110
111110000000010
111110000000040
111111111111111
111111111111111
111111111111111
111111111111111
111111111111111
走法:ddsssddddsssdss
110000000000000
110311111000000
110110001000000
110000001000000
110110001111100
110110000000100
110110000000100
110110000011110
110110000010010
110110000010000
110111111010110
110111111111110
110000000000040
111111111111111
111111111111111
走法:dddddsssddddsssaassssddds
000000000000000
031100000000000
000101110000000
000111010000000
000010010000000
011010010000000
001110010000000
000000010000000
000000011110000
000000000010000
000000000010000
000000000010000
000000000011110
000000000000010
000000000000040
走法:ddssddwddssssssdddssssdddss
合理输入:ddsssddddsssdssdddddsssddddsssaassssdddsddssddwddssssssdddssssdddss
计算MD5:aeea66fcac7fa80ed8f79f38ad5bb953
happyctf
题解
这题给了pdb文件,用IDA分析的时候,如果加载了pdb文件,代码会变得非常乱,看不出来什么东西,不加载pdb反而会好一点。

输入一个flag,经过一些函数的处理,然后与目标串进行比较。
看一下前面的几个函数,发现while内部的sub_403B70进行了一些操作。
int __thiscall sub_403B70(void *this, char a2)
{
char v3[65]; // [esp+Fh] [ebp-45h] BYREF
void *v4; // [esp+50h] [ebp-4h]
v4 = this;
v3[0] = a2 ^ 0x14;
sub_406170(v3);
return ++dword_4DD8F8;
}
看起来是把字符进行了一轮和0x14的异或,直接梭,得到flag。
s1="rxusoCqxw{yqK`{KZqag{r`i"
flag=""
for i in range(0,len(s1)):
flag+=chr(ord(s1[i])^0x14)
print(flag)
//flag{Welcome_to_Neusoft}
crypt
题解

输入字符串给Str,然后经过函数sub_140001240处理,在通过异或0x22与byte_14013B000对比。
sub_140001240传入了v9和v4作为参数,v9来自于sub_140001120。
看sub_140001120,像是初始化S盒

看sub_140001240,RC4加密

from Crypto.Cipher import ARC4
Key = "12345678abcdefghijklmnopqrspxyz"
Str = [0x9E, 0xE7, 0x30, 0x5F, 0xA7, 0x01, 0xA6, 0x53, 0x59, 0x1B, 0x0A, 0x20, 0xF1, 0x73, 0xD1, 0x0E, 0xAB, 0x09, 0x84,
0x0E, 0x8D, 0x2B]
a = []
flag = []
for i in range(0, 22):
a.append(Str[i] ^ 34)
flag = ARC4.new(bytes(Key, encoding="utf-8")).decrypt(bytes(a))
for i in range(0, len(Str)):
print(chr(flag[i]), end='')
flag{nice_to_meet_you}
bad_python
题解
给了一个破损的pyc,根据文件名提示应该是python36,补全文件头得到
# Source Generated with Decompyle++
# File: pyre.cpython-36.pyc (Python 3.6)
from ctypes import *
from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes
def encrypt(v, k):
v0 = c_uint32(v[0])
v1 = c_uint32(v[1])
sum1 = c_uint32(0)
delta = 195935983
for i in range(32):
v0.value += (v1.value << 4 ^ v1.value >> 7) + v1.value ^ sum1.value + k[sum1.value & 3]
sum1.value += delta
v1.value += (v0.value << 4 ^ v0.value >> 7) + v0.value ^ sum1.value + k[sum1.value >> 9 & 3]
return (v0.value, v1.value)
if __name__ == '__main__':
flag = input('please input your flag:')
k = [
255,
187,
51,
68]
if len(flag) != 32:
print('wrong!')
exit(-1)
a = []
for i in range(0, 32, 8):
v1 = bytes_to_long(bytes(flag[i:i + 4], 'ascii'))
v2 = bytes_to_long(bytes(flag[i + 4:i + 8], 'ascii'))
a += encrypt([
v1,
v2], k)
enc = [
0xEEC7D402,
0x99E9363F,
0x853BDE61,
558171287,
0x908F94B0,
1715140098,
986348143,
1948615354]
for i in range(8):
if enc[i] != a[i]:
print('wrong!')
exit(-1)
print('flag is flag{%s}' % flag)
TEA加密,解密代码如下
from ctypes import *
# 关键:补全 long_to_bytes/bytes_to_long 的导入
from Crypto.Util.number import bytes_to_long, long_to_bytes
def decrypt(v, k):
"""TEA解密函数(加密的逆过程)"""
v0 = c_uint32(v[0])
v1 = c_uint32(v[1])
delta = 195935983
sum1 = c_uint32(delta * 32) # 32轮后的sum值 = delta*32
for i in range(32):
# 逆序更新v1(加密的反向操作)
v1.value -= (v0.value << 4 ^ v0.value >> 7) + v0.value ^ sum1.value + k[sum1.value >> 9 & 3]
sum1.value -= delta
# 逆序更新v0
v0.value -= (v1.value << 4 ^ v1.value >> 7) + v1.value ^ sum1.value + k[sum1.value & 3]
return (v0.value, v1.value)
def long_to_str(n):
"""32位整数转回4个ASCII字符(bytes_to_long的逆操作)"""
# 转4字节大端字节串(因为bytes_to_long是按大端处理ASCII)
# length=4 保证输出固定4字节,不足补前导0
return long_to_bytes(n, 4).decode('ascii')
# 预设的加密结果
enc = [
0xEEC7D402, 0x99E9363F, 0x853BDE61, 558171287,
0x908F94B0, 1715140098, 986348143, 1948615354]
# 加密密钥
k = [255, 187, 51, 68]
# 分4组解密(每组2个元素)
flag = ""
for i in range(0, 8, 2):
# 取当前组的加密值
v0_enc = enc[i]
v1_enc = enc[i+1]
# 解密
v0_dec, v1_dec = decrypt([v0_enc, v1_enc], k)
# 转回字符串并拼接
flag += long_to_str(v0_dec) + long_to_str(v1_dec)
# 输出结果
print("正确flag:flag{%s}" % flag)
#flag{Th1s_1s_A_Easy_Pyth0n__R3veRse_0}













文章评论