0%

bbvvmm

用ida打开 可以发现程序读入了用户名和密码,然后对用户名和密码进行了加密,如果用户名和密码校验正确,那么就cat flag

首先是用户名,用户名先进行一层加密,后进行sm4加密,最后进行变表base64转换为了一串密文

首先解决base64:

import base64
tab2 = "IJLMNOPKABDEFGHCQRTUVWXSYZbcdefa45789+/6ghjklmnioprstuvqwxz0123y="
tab1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="

s2 = "RVYtG85NQ9OPHU4uQ8AuFM+MHVVrFMJMR8FuF8WJQ8Y="
s1 = ""
for c in s2:
for i in range(len(tab2)):
if(tab2[i] == c):
s1 += tab1[i]
break

print(base64.b64decode(s1))

得到“EF468DBAF985B2509C9E200CF3525AB6”

然后是sm4加密算法,直接在网上找轮子
解密得到“36 32 36 31 36 34 37 32 36 35 37 32 33 31 33 32”

最后
两次转化成ascii
第一次62 61 54 72 65 72 31 32
第二次b a d r e r 1 2

然后就得到了用户名

对于密码,并不清楚这个密码使用了什么加密算法,只清楚这是一个虚拟机,有几百个操作数,好像是对密码进行了某种运算,最后将运算结果保存到内存的某个地方,如果这个内存数据为0,那么密码便正确

直接开ida动态远程调试:
能找到一个类似于栈的结构(一直+4 -4),在内存中找到这个栈对应的位置

通过不断地循环fastcall,看到栈中出现了我刚刚输入的字符串,并且旁边是一堆奇特字符串,而且长度也是六位,于是尝试交上去,成功

getshell的时候:输入密码之后不能直接按回车键,这样会多输入一个 \n,而应该ctrl+d,输入文本中断符,然后拿到flag

WxyVM2

大概是要读这个程序里面的一段汇编代码,这段汇编代码非常长,连ida都无法反编译,但是非常有规律,可以通过写脚本来还原 这是初始化代码,估计是对加密的字符串进行还原的:

.text:00000000004005FE                 mov     cs:dword_694140, 9C06AA99h
.text:0000000000400608 mov cs:dword_694144, 0B4B2B03Fh
.text:0000000000400612 mov cs:dword_694148, 0C51F73CFh
.text:000000000040061C mov cs:dword_69414C, 223520F8h
.text:0000000000400626 mov cs:dword_694150, 0C0C53B9h
.text:0000000000400630 mov cs:dword_694154, 0B59C78EAh
.text:000000000040063A mov cs:dword_694158, 0F7DE2D34h
.text:0000000000400644 mov cs:dword_69415C, 0B27EEE2Ch

类似于下面这样的

.text:000000000048BAF1                 mov     edx, cs:dword_694174
.text:000000000048BAF7 mov eax, cs:dword_694178
.text:000000000048BAFD xor eax, edx
.text:000000000048BAFF mov cs:dword_694178, eax
.text:000000000048B93B                 movzx   eax, cs:byte_694116
.text:000000000048B93C add eax, 7Dh
.text:000000000048B93F mov cs:byte_694116, al
.text:000000000048B95C                 movzx   eax, cs:byte_694100
.text:000000000048B95C xor eax, 6Ah
.text:000000000048B95F mov cs:byte_694100, al
.text:000000000041DD09                 movzx   eax, cs:byte_69410F
.text:000000000041DD09 sub eax, 6Eh
.text:000000000041DD0C mov cs:byte_69410F, al

还有一种 我 mov 我 自 己

.text:000000000040093E                 movzx   eax, cs:byte_694114
.text:0000000000400945 mov cs:byte_694114, al

ida可以修改配置文件,这样就可以反编译了 byte_694100是字符串读入的起始地址 读取18h字节

.text:00000000004005CD                 call    _puts
.text:00000000004005D2 mov esi, offset byte_694100
.text:00000000004005D7 mov edi, offset format ; "%s"
.text:00000000004005DC mov eax, 0
.text:00000000004005E1 call _ssanf

694100~694117 查看汇编代码后发现很多操作都超出了这个内存的范围,而且最后也没有对这个内存进行任何修改,那么就应该直接舍去 先将所有汇编代码dump下来,用脚本筛掉垃圾代码

def get_string(line):
l = []
s = ""
for i in range(len(line)):
if(line[i] == ' ' or line[i] == ',' or i == len(line) - 1):#如果读取到了末尾
if(len(s) != 0):
l.append(s)
s = ""
else:
s = s + line[i]

return l

i = 0
f = open("code.dump", "r")
fout = open("decode.txt", "w")
#fout.write("Below is decoded code.\n(format:) addr [opt] num\n")
while(1):
line = f.readline()
i += 1
if(line == ""):
print("正常退出")
fout.close()
break
tab = get_string(line)#获取到一个汇编指令
if(tab[1] == "mov"):#如果是垃圾指令,就直接跳过
line = f.readline()
i += 1
line = f.readline()
i += 1
line = f.readline()
i += 1

elif(tab[1] == "movzx"):
addr = tab[3]
addr = "0x" + addr[8::]#获取地址信息
addr_num = int(addr, 16)
addr_num -= 6897920 #减去起始地址 得到偏移量
addr = str(addr_num)

line = f.readline()
i += 1

tab = get_string(line)

if(tab[1] != "mov"):#只有当这个指令不是mov的时候才进行判断,因为有一种垃圾指令是自己mov自己
if(tab[1] == "add"):
opt = "+="
elif(tab[1] == "xor"):
opt = "^="
elif(tab[1] == "sub"):
opt = "-="
else:
print("something goes wrong! " + tab[1] + " at :" + str(i))
break

s = tab[3]
# s = s[:-1:]
# s = "0x" + s

if(s[-1] == "h"):
s = s[:-1]
s = "0x" + s

s = str(int(s, 16) % 128)

line = f.readline()
i += 1
fout.write(addr + " " + opt + " " + s + "\n")

else:
print("something goes wrong! " + tab[1] + " at :" + str(i))
break

f.close()
fout.close()

得到类似于如下的数据

23 += 11
3 += 31
5 += 78
23 += 109
2 -= 25
1 -= 48
11 += 123
13 ^= 38
23 ^= 89
12 += 3
11 -= 5
13 += 75
4 ^= 72
5 -= 88
4 -= 107
0 ^= 60
23 ^= 27

再写python进行逆向计算:

def get_string(line):
l = []
s = ""
for i in range(len(line)):
if(line[i] == ' ' ):#如果读取到了末尾
if(len(s) != 0):
l.append(s)
s = ""
else:
s = s + line[i]
if(len(s) != 0):
l.append(s)

return l

enc =[0xC0,0x85,0xF9,0x6C,0xE2,0x14,0xBB,0xe4,0xd,0x59,0x1c,0x23,0x88,0x6e,0x9b,0xca,0xba,0x5c,0x37,0xfff,0x48,0xd8,0x1f,0xab,0xa5]

f = open("decode.txt", "r")
all = []

i = -1
while(1):
line = f.readline()
if(line == ""):
break
i += 1
all.append(line)

while(i >= 0):
line = all[i]
tab = get_string(line)
i -= 1

if(tab[1] == "+="):
enc[int(tab[0])] -= int(tab[2])
elif(tab[1] == "-="):
enc[int(tab[0])] += int(tab[2])
else:
enc[int(tab[0])] ^= int(tab[2])
enc[int(tab[0])] %= 128
ans = ""
for c in enc:
ans = ans + chr(c)
print(ans)

utf-8’ ‘ShinyShot!

这个程序主要是利用了smc(动态代码修改技术)

首先我们向程序里面输入一个数,然后这个程序利用这个数去修改了一些程序的代码

然后输入一个字符串,将字符串base64加密后得到的密文与程序尾所保存的字符串进行比较,如果相同,则flag正确

程序在我们输入了一个数字之后

执行了

*((_BYTE *)TopLevelExceptionFilter + (cout >> 3)) ^= 1 << (cout & 7);

这个指令,TopLevelExceptionFilter的意义暂时不明白,大概就是一个指向某个内存的指针,简记为T, 值为0x401000

程序把*(p + (cout >> 3)) 处的数据异或了一个1 << (cout & 7)

其实1 << (cout & 7)就是将1左移了几位

我们看到后面的

.text:004018DE EB 15             jmp     short loc_4018F5
.text:004018E0                  ; ---------------------------------------------------------------------------
.text:004018E0
.text:004018E0                   loc_4018E0:                  ; CODE XREF: sub_4017AA+126↑j
.text:004018E0 E8 A7 FE FF FF    call    sub_40178C
.text:004018E5                  ; ---------------------------------------------------------------------------
.text:004018E5
.text:004018E5                   loc_4018E5:
.text:004018E5 8D 85 40 FF FF FF lea     eax, [ebp-0C0h]
.text:004018EB 89 44 24 04       mov     [esp+414h+var_410], eax ; char *
.text:004018EF 8D 45 A4          lea     eax, [ebp+Buf]
.text:004018F2 89 04 24          mov     [esp+414h+var_414], eax ; char *
.text:004018F5
.text:004018F5                   loc_4018F5:                  ; CODE XREF: sub_4017AA+134↑j
.text:004018F5 E8 00 FC FF FF    call    sub_4014FA

0x 004018DE jmp 15 的意义是,除开jmp 15,往后数0x15个字节,将程序运行指针设置到此处。

而sub_4014FA是一个需要两个参数的函数,没有参数的传入,函数一定不会有正确答案。

所以我们应当将 jmp 15改为jmp到传入参数的位置

经过计算jmp 5 即可,现在我们的工作是要把0x004018DF处的15修改为5

0x15 = ‭00010101‬

0x5 = 00000101

也就是把第五位的1抹掉就行了,之前那个不明意义的内存修改就是这个用处了。

num = (0x8df<<3) + 0x4 = 18172

后面是一个base64加密,但闲鱼本人并不是密码学选手,太菜了,所以暂时跳过。。

最后将改版的base64加密后的字符串与程序内的进行比较,得到flag

happyVM

第一次做VM题,花了很多时间,最终还是有所收获

首先逆向虚拟机的操作数,操作数无非几个操作:压栈,出栈,寄存器与内存之间的读写,寄存器与寄存器之间的赋值

使用如下代码配合ida python获得所有操作数:

addr = 0x400D40
cnt = 0x78-0x40+1
i = addr
print "********my calc********"
print "********my calc********"
print "********my calc********"
print "********my calc********"
print "********my calc********"
while(i <= addr + cnt -1):
c = int(Byte(i))
if(c in [0x0,0x8,0x9,0xA,0xC,0xD,0xE,0x11,0x13,0x14]):
i += 1
print "opt:" , hex(c) , " data:", hex(Byte(i))
else:
print "opt:" , hex(c)
i += 1

得到如下指令:

0(0x00) : opt: 0x11    data: 0x2d
2(0x01) : opt: 0x0 data: 0x22
4 : opt: 0x5
5 : opt: 0x10
6 : opt: 0x14 data: 0x9
8 : opt: 0x17
9(0x9) : opt: 0x0 data: 0x32
11 : opt: 0x5
12 : opt: 0x3
13 : opt: 0x11 data: 0x16
15 : opt: 0x6
16 : opt: 0x0 data: 0x16
18 : opt: 0x5
19 : opt: 0x11 data: 0x16
21 : opt: 0x17
22(0x16) : opt: 0xe data: 0x1
24 : opt: 0x15
25 : opt: 0x4
26 : opt: 0xf
27 : opt: 0x1
28 : opt: 0x16
29 : opt: 0x2
30 : opt: 0x0 data: 0x0
32 : opt: 0x4
33 : opt: 0x3
34 : opt: 0x5
35 : opt: 0x10
36 : opt: 0x14 data: 0x2b
38 : opt: 0x5
39 : opt: 0x9 data: 0x3
41 : opt: 0x13 data: 0x16
43(0x2b) : opt: 0x5
44 : opt: 0x12

45(0x2d) : opt: 0x15
46 : opt: 0x4
47 : opt: 0x10
48 : opt: 0x14 data: 0x36
50 : opt: 0xa data: 0x1
52 : opt: 0x13 data: 0x2d

54(0x36) : opt: 0x3
55 : opt: 0x4
56 : opt: 0x12

逐行翻译,其中部分压栈与出栈操作可以合并为内存与寄存器,寄存器与寄存器之间的数据交换,翻译得到如下代码:

	simplify:


call 0x2d;//strlen(str)
B = 0x22;
if(A != B) return;//字符串长度为0x22
0x9:
B = 0x32;
push(C);//此时C = 0x22
call(0x16);
C = pop();
B = 0x16;
call(0x16);
return;

0x16:
C--;//倒序处理整个字符串
A = str[C];
A ^= B; //str[C] = str[C] ^ B ,而且B每次加3
str[C] = A;
push(B);
A = 0;
B = C;
if(A == B){//如果这个字符串已经处理完成
goto 0x2b;
}else{
B = pop();
B += 3;
goto 0x16;
}

0x2b:
B = pop();

0x2d:
do{
A = str[C];
if(A == B) break;
C++;
}while(A != B)

A = C;
ret;

最后获取到s2的数据,solve

str = [132 , 131 , 157 , 145 , 129 , 151 , 215 , 190 , 67 , 114 , 97 , 115 , 115 , 12 , 106 , 112 , 115 , 17 , 72 , 44 , 52 , 51 , 49 , 54 , 35 , 52 , 62 , 92 , 35 , 78 , 23 , 17 , 25 , 89 ]
B = 0x16
for i in range(0x21, -1, -1):
str[i] = str[i] ^ B
B += 3

B = 0x32
for i in range(0x21, -1, -1):
str[i] = str[i] ^ B
B += 3

for i in range(0x22):
print(chr(str[i]),end="")

maze 迷宫题,用正确的方式走出迷宫即可,wsad分别控制上下左右

aaa = """11111111111111111111111111111111111111111111111111111111111111111111...............111111111111111...............111
111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111
.111111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111s111111111111111.111111111
1111s111111111111111.11111111111111111111111111111.11111111111111111111111111111.11111111111111111111111111111.11111
111111111111111111111111.11111111111111111111111111111.1111.111111111111111111111111.1t............111111111111111.1
t............111111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.1111111111111
11.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.111111111
111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.11111
1111111111.1111111111111.111111111111111...............111111111111111...............1111111111111111111111111111111
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"""

cnt = 0
map = ""

for c in aaa:
if(c != '\n' and c != ' '):
map = map + c
cnt += 1
if(cnt % 60 == 0):
map = map + '\n'

print(map)

得到如下地图:

111111111111111111111111111111111111111111111111111111111111
11111111...............111111111111111...............1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111s111111111111111.1111111111111s1111111
11111111.11111111111111111111111111111.111111111111111111111
11111111.11111111111111111111111111111.111111111111111111111
11111111.11111111111111111111111111111.1111.1111111111111111
11111111.1t............111111111111111.1t............1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111...............111111111111111...............1111111
111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111111111

走出迷宫即可: wwwwaaaaaaaaaaaaaasssssssssssssssddddddddddddddwwwwwwwaaaaaaaaaaaa

brainfuck是一种小型化程序语言,符合图灵完全思想

字符 含义
> 指针加一
< 指针减一
+ 指针指向的字节的值加一
- 指针指向的字节的值减一
. 输出指针指向的单元内容(ASCⅡ码)
, 输入内容到指针指向的单元(ASCⅡ码)
[ 如果指针指向的单元值为零,向后跳转到对应的]指令的次一指令处
] 如果指针指向的单元值不为零,向前跳转到对应的[指令的次一指令处

,.充当输入输出
+-用于数值操作
<>用于指针操作
[]用于循环(其中执行到”[“时,程序会判断当前指针所指空间是否为0,为0则终止循环,否则继续循环)

用更通俗一点的C语言来描述:

Brainfuck C
> ++ptr;
< –ptr;
+ ++*ptr;
- –*ptr;
. *ptr
, *ptr =getch();
[ while (*ptr) {
] }

当前位置清零
[-] 将当前指针的值归零

之前位置清零
[[-]<] 将当前指针以及之前的指针归零

字符I/O
,. 从键盘读取一个字符并输出到屏幕上。

简单的循环
,[.,] 这是一个连续从键盘读取字符并回显到屏幕上的循环。注意,这里假定0表示输入结束,事实上有些系统并非如此。以-1和”未改变”作为判断依据的程序代码分别是”,+[-.,+]”和”,[.[-],]”。

指针维护
“>,[.>,] “通过移动指针保存所有的输入,供后面的程序使用。

加法
[->+<] 把当前位置的值加到后面的单元中(破坏性的加,它导致左边的单元被归零)。

secend 很简单的一道题,python入门 使用如下网站进行反编译: https://tool.lu/pyc/ 得到

print "Welcome to Processor's Python Classroom Part 2!\n"
print "Now let's start the origin of Python!\n"
print 'Plz Input Your Flag:\n'
enc = raw_input()
len = len(enc)
enc1 = []
enc2 = ''
aaa = 'ioOavquaDb}x2ha4[~ifqZaujQ#'
for i in range(len):
if i % 2 == 0:
enc1.append(chr(ord(enc[i]) + 1))
continue
enc1.append(chr(ord(enc[i]) + 2))

s1 = []
for x in range(3):
for i in range(len):
if (i + x) % 3 == 0:
s1.append(enc1[i])
continue

enc2 = enc2.join(s1)
if enc2 in aaa:
print "You 're Right!"
else:
print "You're Wrong!"
exit(0)

解密代码:

aaa = 'ioOavquaDb}x2ha4[~ifqZaujQ#'
list = list(aaa)
len = len(aaa)

dec1 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
dec2 = ''
cnt = 0
for x in range(3):
for i in range(len):
if (i + x) % 3 == 0:
dec1[i] = list[cnt]
cnt += 1
continue

for i in range(len):
if i % 2 == 0:
dec2 = dec2 + chr(ord(dec1[i]) - 1)
else:
dec2 = dec2 + chr(ord(dec1[i]) - 2)

print(dec2)