日常练习2
mango

2023/11/3

点击就送

得到一个.s文件,是gcc汇编后得到的文件。在kali

1
2
gcc -c 1.s       #生成.o
gcc 1.o # 生成a.out

再把a.out拖到ida,简单难度的逆向

幸运数字

爆破0-999找前四位符合SYC{的

砍树

先用jadx看看主逻辑,大概就是砍树,然后验证flag。但是我砍了777+它给我退出了(怒

问题就出在这个哭脸的返回值上。要使它为1!

用ida打开相应的so层一探究竟

Alt+F搜索

进入之后反汇编看看

大概就是text经过处理后和dest相等

简单的异或

1
2
3
4
5
6
7
8
9
10
dest = [0, 32, 32, 23, 27, 54, 14, 54, 38, 23,
4, 42, 41, 7, 38, 21, 82, 51, 45, 15,
58, 39, 17, 6, 51, 7, 70, 23, 61, 10,
60, 56, 46, 34, 24]
S = "Sycloverforerver"
S = list(S)
text = []
for i in range(34):
text.append(chr(dest[i] ^ ord(S[i % 7])))
print(''.join(text))

这里一开始想复杂了,看到char给人改成小端了,然后就没出来flag。理解不够到位

doublegame(未)

2023//11/5

wordy

打开ida发现只反汇编了一小段程序,很多灰色数据部分。然后考虑花指令。这里学习了一下使用自动patch

1
2
3
4
5
6
7
8
startaddr=0x1135
endaddr=0x3100

for i in range(startaddr,endaddr):
if get_wide_byte(i)==0xEB:
if get_wide_byte(i+1)==0xFF:
patch_byte(i,0x90)
print("[+] Addr {} is patched".format(hex(i)))

然后P一下就都蓝了。翻找一下可以找到flag

help

迷宫题

有个CreateMap()函数,在它后面的跳转点打个断点,动态调试

进入CreateMap()找map

在Export data 中提取出来,编辑成16个一行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1111111111111111
1000001111110111
1011101111110111
1011101100010111
1011101101010111
1011100001010111
1011111101010111
1011111100010111
1011111110110111
1011111110110111
1000011000010001
1111011110110101
1111011110110101
1000011110110100
1011111110000111
1011111111111111

在check()函数可以发现步法,以及初始位置(1,15)

那么接下来就只需要找路了,如果能搞个dfs搜索就好了,,

1
2
3
4
import hashlib
md=hashlib.md5()
md.update('wwdddwwwaaawwwwwwwwwddddssssdddssdsssssssdddwwwwddsssd'.encode('utf-8'))
print(md.hexdigest())

2023/11/6

teaaaa

x找调用函数

比较特别的是修改了key的值,然后是一个简单改动的xtea,复制然后把+改-就差不多了

2023/11/7

小黄鸭

先是查了下壳发现里面有很多文件

然后扔到binwalk看看是真有很多

尝试binwalk -e解开发现全是zlib,,

然后递归解开 binwalk -eW,发现里面有一些pyc,所以应该是py打包成的exe

用工具拆开:

反编译里面的1.py

比较简单。逻辑是输入一个flag,如果里面的内容是字母就rot13,然后ord+2

如果不是字母就直接ord+1

所以逆过程就是先ord-1,把符号数字找出来,剩下字母的再-1,然后解开rot13

最后把他们拼在一起

安全编程

是鹏城杯2023的题,当时看到是rust以及这个题目感觉很难orz

之前看过一点rust的内容,给我的感觉就是很安全()没学完

ida打开之后非常地复杂。从字符串摸到了main函数,很复杂

标了一下字符串。然后在kali运行了一下,是个猜数游戏

逻辑应该是程序自动生成一个1-10的数字,用户要输入一个1-10的数字,猜对100次修复encflag输出flag。

这里学习了一下ida的远程linux调试,感觉这样不用gdb了捏

步骤是把要运行的elf放到kali的ida文件夹里,然后启动对应的server,然后在ida打开调试

这道题的话,因为是rust所以代码比较难读,但是程序逻辑比较简单。所以可以在程序的每个跳转点处打断点,然后观察执行顺序,找出来需要的变量和常量

这里主要是修改了guess_number,改成99,再猜1次就好了。程序把生成的猜数答案放在了eax里(返回值)所以照着猜就好了

终于学会了远程调试,开熏

2023/11/12

啊啊啊好几天没做题了。。猛肝了一堆实验。。

z3test(未)

好像解出了一个错的矩阵。。。

Let’s Go(未

看不懂

ez_chal

打开之后发现函数名什么的都没加载出来,看wp得知是被混淆了(很好奇是怎样混淆的,混淆函数应该也在这里面吧)

然后依照动作标一下名字。

看到加密的形式是一个魔改的xtea

密文已经给出了:

这里的encode没有被混淆,是64位的整数(动态调试里发现的)

下面验证flag的时候把encode转换为了_DWORD *双字型,32位无符号整数,所以把这个切割一下。

这个题目里面这个encode是以小端序的方式存储的

所以顺序应该是C1……DC…………

在动调里找到的key:

里面的常量,函数名,delta都被混淆了,都要在动态调试里把真实的值找出来

delta=0x9E3778B9

然后就写脚本

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 0x9E3778B9, sum = delta * num_rounds;
for (int i = 0; i < num_rounds; i++) {
v1 -= v0 ^ ((key[(sum >> 11) & 3]) + sum) ^ (v0 + ((v0 >> 5) ^ (16 * v0)));
sum -= delta;
v0 -= v1 ^ ((key[sum & 3]) + sum) ^ (v1 + ((v1 >> 5) ^ (16 * v1)));
}
v[0] = v0;
v[1] = v1;
}
int main() {
uint32_t key[4] = {0};
uint32_t encode[] = {
0xC19EA29C, 0xDC091F87, 0x91F6E33B, 0xF69A5C7A,
0x93529F20, 0x8A5B94E1, 0xF91D069B, 0x23B0E340
};
unsigned int rounds = 64;
char firstkey[40] = {0};
memcpy(firstkey, "NewStar!NewStar!", strlen("NewStar!NewStar!"));
for (int i = 0; i < strlen(firstkey); i += 4) {
key[i / 4] = *(uint32_t*)(firstkey + i);
printf("%u\n", key[i / 4]);
}
for (int i = 0; i < 8; i += 2) {
decipher(rounds, &encode[i], key);
}
printf("Decrypted data is: %s \n", (char*)encode);
return 0;
}

真的,写脚本的时候得看清楚了,这少一点那少一点的。

STL

STL是C++的一个标准库

阅读逻辑比较简单

但是没搞明白这里的逆过程

官方是这样写的,但是没看懂

于是看了别的办法发现可以爆破。爆破也是个好办法呢!

然后学到了不止char存在小端序,int64也有。就是这些大于16位的就会存在小端序。

八个位是一个字节

十六个位,两个字节,一个字

A9 A是高字节,9是低字节

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
from Crypto.Util.number import long_to_bytes
encode=[0x2882D802120E,0x28529A05954,0x486088C03,0xC0FB3B55754,0xC2B9B7F8651,
0xAE83FB054C,0x29ABF6DDCB15,0x10E261FC807,0x2A82FE86D707,0xE0CB79A5706,
0x330560890D06]
enc_max=max(encode)
enc_min=min(encode)
# print(hex(enc_max>>15))
# print(hex(enc_min>>15))
hex_list=[0]
flag=''
for v in encode:
for v7 in range(0x90000,0x70000000):
if((v7<<15)^v7)==v:
print(long_to_bytes(v7),hex(v),hex(v7)[2:].zfill(8))
tmp=hex(v7)[2:].zfill(8) # 不足8位补0填充为8位
#print(tmp)
for i in range(len(tmp)-1,0,-2): # 小端序
hex_list.append(int(tmp[i-1:i+1],16)) # 16进制
break
print(hex_list)
for i in range(len(hex_list)-2,0,-1):
hex_list[i]^=hex_list[i+1]
for i in hex_list[::-1]:
flag+=chr(i)
print(flag)

这里有些注意点

  1. long_to_byte是把长整型转换为字节码,字节码在密码学算法中用得比较多,这里是用来看足不足一个字节,因为后面要调整顺序。

  2. zfill 是补0

  3. hex(v7)会返回一个十六进制字符串 ,所以需要[2:]去掉0x

2023/11/13

浪漫至死不渝

做的时候和做完都感觉自己是一条狗被创飞了

主要是阅读js代码,找到第一组加密函数,是栅栏,把一个明文加密成了一个类似key的字符串在第二组加密中使用

第二组就是简单的加减异或

1
2
3
4
5
6
7
8
9
10
11
12
13
encode=[125, 130, 131, 122, 117, 110, 123, 125, 130, 131, 122, 117, 110, 123, 99, 99, 99, 99]
Text1="5201314WXHN"
Text1=list(Text1)

for j in range(18):
if j<14:
encode[j]-=10
encode[j]^=ord(Text1[j%7])
else:
encode[j] -= 99
encode[j] ^= ord(Text1[j-7])
print(''.join(chr(code) for code in encode))
# FJIAXUEFJIAXUEWXHN

听说Cpp很难

感觉有点稀里糊涂做的

可以直接找到密文

这是主要的加密逻辑

在动态调试里可以发现v18的值为0xA,在text_67 中其实key是多少都无所谓

写一下

1
2
3
4
5
6
7
8
9
10
11
12
v19 = [77, 95, 61, 123, 55, 104, 115, 87, 39, 104, 81, 89,
127, 38, 107, 89, 115, 87, 85, 91, 89, 111, 106, 89,
39,87, 114, 87, 79, 87, 120, 120, 125]
print(len(v19))

# v+=10 v=v^10-8
for i in range(len(v19)):
v19[i]=(v19[i]+10)^10
v19[i]-=10

print(''.join(chr(code) for code in v19))
# SYC…Anma1nG_y0u_maKe_it_1alaIa~~ƒ

这里把v19的数组小小改动了一下,不知道那两个负数是什么情况

2023/11/14

flower or tea

去花指令+xtea

全是E8,nop

注意如果有特别多的栈指针不平衡,可以大批量还原成数据重新生成函数

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
uint32_t enc[]={
0x9AF9464B,0xC417B89E,0xB217A713,0xC93BA9E8,0x94F3E44E,0xB5CC2AB5,0x4451E42C,0x7A8A289A, 0x53C8D008,0x6E117B49,0x9BFFD794,0x5EFF2DF9,0x17E72531, 0xDFBD9979,0x8F871B3A,0x73E8C5AC,0xB28670A6,0x5AF6A369,0x2CF7DA24,0x347B66AF,0xB9C84D60,0x911E912F,0xBD5A2F9B,0xCB96733A,0xC59968BE,0xA00013E9,0xC12F4EA4,0xDE863A10,0xA0C4D594, 0x4380983C,0x7E2F7648, 0xE54DDC89,0x3F27A690,0xB58D3199,0x604AE517,0x9C903984,0xF4E04481,0x3CF4EDFF,0,0
};
void decrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 0x31415927, sum = delta * num_rounds;
for (i = 0; i < num_rounds; i++) {
sum -= delta;
v0 -= (key[sum & 3] + sum) ^ (v1 + ((v1 >> 5) ^ (16 * v1)));
v1 -= sum ^ (key[(sum >> 11) & 3] + sum) ^ (v0 + ((v0 >> 5) ^ (16 * v0)));
}
v[0] = v0;
v[1] = v1;
}
int main(){
uint32_t key[4]={32,27,39,44};
unsigned int r=54;
char flag[40]={0};
for(int i=0;i<40;i+=2){
decrypt(r,&enc[i],key);
// printf("%d",i);
}
// printf("%s\n",(char*)enc);
for(int i=0;i<20;i++){
flag[i]=(char)enc[2*i];
flag[39-i]=(char)enc[2*i+1];
}
for(int i=0;i<40;i++){
printf("%c",(char)enc[i]);
}
printf("\n");
for(int i=0;i<40;i++){
printf("%c",flag[i]);
}
return 0;
}

tea的数据类型基本就是模板,不要尝试用ida中反汇编出来的。。。

2023/11/15

myself

一个smc,但没找着修改代码的函数在哪

在x32dbg里面把加密函数拉出来了,是个xtea,解密:

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
void decrypt(unsigned int num_rounds, uint32_t v[2]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 0x61C88647, sum = -delta * num_rounds;
for (i = 0; i < num_rounds; i++) {
v1 -= ((v0 >> 5) + 4) ^ (16 * v0 + 3) ^ (sum + v0);
v0 -= ((v1 >> 5) + 2) ^ (16 * v1 + 2) ^ (sum + v1);
sum += delta;
}
v[0] = v0;
v[1] = v1;
}
int main(){
unsigned int r=32;
uint32_t enc[] =
{
0xBDBDF9F0, 0xE26194C4, 0x80799125, 0x1F0FC219,
0xEB6A1815, 0x84F572C5, 0x40CC3A85, 0xD2A32ABB
};
for(int i=0;i<8;i+=2){
decrypt(r,&enc[i]);
}
// for(int i=0;i<8;i++){
// printf("%c",(char*)enc[i]);
// }
printf("%s",(char*)enc);
return 0;
}

然后密文是以char存储的,八个一组加密。存储形式要是搞不清楚可以动调的时候看看

2023/11/18

鹏城杯BabyRe

好久之前的比赛题,当时没做出来,看了大佬的wp复习了好多以前的知识

加密流程很清晰

flag分成4组,12个一组丢进去32轮加密

比赛的时候被这个移位迷惑了,当时不知道这只是ida反汇编出来的一些函数,实际上加密函数里没有。被困在这弄了很久。

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
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
void decrypt(unsigned char* data) {
unsigned int* _data = (unsigned int*) data;
unsigned int rand_num[32][6];
srand(0xDEADC0DE);
for (int _ = 0; _ < 32; _++) {
for (int i = 0; i < 6; i++) {
rand_num[_][i] = rand();
}
}
for (int _ = 31; _ >= 0; _--) {
unsigned int a = _data[0] >> 7;
unsigned int b = rand_num[_][4] + a;
unsigned int c = (_data[0] >> 15) ^ (_data[0] << 10) | 3;
_data[2] -= b + (rand_num[_][5] ^ c);

a = _data[2] >> 7;
b = rand_num[_][2] + a;
c = (_data[2] >> 15) ^ (_data[2] << 10) | 3;
_data[1] -= b + (rand_num[_][3] ^ c);

a = _data[1] >> 7;
b = rand_num[_][0] + a;
c = (_data[1] >> 15) ^ (_data[1] << 10) | 3;
_data[0] -= b + (rand_num[_][1] ^ c);

for (int i = 0; i < 12; i++) {
data[i] = 167 * (data[i] - 66 & 0xFF);
}
}


}
int main() {
unsigned char enc[] = {
0x48, 0x4D, 0x3B, 0xA0, 0x27, 0x31, 0x28, 0x54, 0x6D, 0xF1,
0x21, 0x35, 0x18, 0x73, 0x6A, 0x4C, 0x71, 0x3B, 0xBD, 0x98,
0xB6, 0x5A, 0x77, 0x2D, 0x0B, 0x2B, 0xCB, 0x9B, 0xE4, 0x8A,
0x4C, 0xA9, 0x5C, 0x4F, 0x1B, 0xF1, 0x98, 0x3D, 0x30, 0x59,
0x3F, 0x14, 0xFC, 0x7A, 0xF4, 0x64, 0x02, 0x2B, 0x00
};
decrypt((unsigned char*)enc);
decrypt((unsigned char*)enc + 12);
decrypt((unsigned char*)enc + 24);
decrypt((unsigned char*)enc + 36);
for (int i = 0; i < 48; i++) {
printf("%c", enc[i]);
}
return 0;
}

这里面有个要注意的地方:

对于data[i]=23*(data[i]+66)

逆过程为data[i]167*(data[i]-66&0xff)

因为加密中运算的时候把类型强制转换成了unsigned _int8 所以是0-255内的,要&0xff。这个167是怎么回事呢?是23*x(mod 255)的乘法逆元。这样子就可以不打表算了。捡起来一点密码学知识…

回顾起来发现题目真的不难,就是做题的时候被一些细节问题绊住了,还是不够熟练。

由 Hexo 驱动 & 主题 Keep
访客数 访问量