当前位置: 首页 > news >正文

Reverse-WP记录9

前言

之前写的,一直没发,留个记录吧,万一哪天记录掉了起码在csdn有个念想

1.easyre1

32位无壳elf文件

shift+F12进入字符串,发现一串数字,双击进入

进入main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{__isoc99_scanf("%s", flag);enkey();reduce();check();return 0;
}

enkey函数,把flag的每一项和key的对应项异或,因为这里的0x804A0A0代表地址,正好是key的地址

int enkey()
{int i; // [esp+Ch] [ebp-4h]for ( i = 0; i <= 31; ++i )*(_BYTE *)(i + 134520992) ^= *(_BYTE *)(i + 134520896);return 0;
}

reduce函数,这里是把flag的每一项的ascll码减一,因为这里的0x804A0A0就是flag的地址

int reduce()
{int i; // [esp+Ch] [ebp-Ch]for ( i = 0; i <= 30; ++i )--*(_BYTE *)(i + 134520992);putchar(10);return 0;


check函数,验证加密后的flag

int check()
{if ( !strcmp(flag, "d^XSAozQPU^WOBU[VQOATZSE@AZZVOF") )return puts("you are right");elsereturn puts("no no no");
}

然后根据逻辑写出解密脚本

enc = 'd^XSAozQPU^WOBU[VQOATZSE@AZZVOF'
key = '5055045045055045055045055045055'
flag=''
for i,j in zip(enc,key):i = ord(i) + 1flag += chr(i ^ ord(j))
print(flag)

flag{PolarDNbecomesbiggerandstronger}

2.BabyRE

64位无壳exe文件

ida进入main函数,c++写的程序,if判断flag

int __cdecl main(int argc, const char **argv, const char **envp)
{std::ostream *v3; // raxchar v5[48]; // [rsp+20h] [rbp-20h] BYREF_main();endoce();std::string::basic_string(v5);std::operator>><char>(refptr__ZSt3cin);if ( (unsigned __int8)std::operator==<char>(v5, &flag[abi:cxx11]) )v3 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Ok");elsev3 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Err");refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v3);std::string::~string(v5);return 0;
}

endoce函数是把flag每一位加2

__int64 endoce(void)
{__int64 result; // rax__int64 v1; // [rsp+20h] [rbp-20h] BYREF__int64 v2; // [rsp+28h] [rbp-18h] BYREF_BYTE *v3; // [rsp+30h] [rbp-10h]void *v4; // [rsp+38h] [rbp-8h]v4 = &flag[abi:cxx11];v2 = std::string::begin(&flag[abi:cxx11]);v1 = std::string::end(&flag[abi:cxx11]);while ( 1 ){result = __gnu_cxx::operator!=<char *,std::string>(&v2, &v1);if ( !(_BYTE)result )break;v3 = (_BYTE *)__gnu_cxx::__normal_iterator<char *,std::string>::operator*(&v2);*v3 += 2;__gnu_cxx::__normal_iterator<char *,std::string>::operator++(&v2);}return result;
}

但是看不出来flag被初始化成了什么,也不知道flag有几位,这里选择动调,在这里下断点,等运行到这里F7步入,因为在这之前没有看到对flag的初始化,然后随便输入

size函数计算flag的长度,size对应地址加8就是flag的长度

__int64 __fastcall std::string::size(__int64 a1)
{return *(a1 + 8);
}

运行到这里时,双击a2

长度为0x10即16位

重新开启动调,输入16个1,运行到这里可以查看v4的值,运行到下一步可以看到v5的值

这里的逻辑显然是比较v4和v5的值,v3是长度,类似C语言的strncmp(v4,v5,v3),由于到这一步我们的输入已经过了加密步骤,所以不需要对flag解密

flag{cufhiexdpolivnqr}

3.C^

查壳,无壳,发现32位

int __cdecl main(int argc, const char **argv, const char **envp)
{int v4; // [esp-Ah] [ebp-60h]int v5; // [esp-6h] [ebp-5Ch]_BYTE v6[48]; // [esp-2h] [ebp-58h] BYREFint v7; // [esp+2Eh] [ebp-28h]size_t v8; // [esp+32h] [ebp-24h]int v9; // [esp+36h] [ebp-20h]int v10; // [esp+3Ah] [ebp-1Ch]int *p_argc; // [esp+4Ah] [ebp-Ch]p_argc = &argc;init();*(_DWORD *)&v6[2] = 0;v7 = 0;memset(&v6[4], 0, 4 * (((&v6[2] - &v6[4] + 50) & 0xFFFFFFFC) >> 2));v10 = 0;v9 = 0;puts("Please enter flag\n");((void (__stdcall *)(const char *, _BYTE *, int, int, _DWORD))__isoc99_scanf)("%s", &v6[2], v4, v5, *(_DWORD *)v6);v8 = strlen(&v6[2]);fun1(&v6[2], v8);if ( check(&v6[2], v8) )puts("RIGHT");elseprintf("Try again");return 0;
}

通过代码审计可知

  • 调用函数
  • 调用 fun1(&v6[2], v8); 对输入字符串进行某种变换。
  • 调用 check(&v6[2], v8); 检查变换后的字符串是否符合预期。
  • 输出结果
  • 如果 check 返回非零值,输出 "RIGHT";否则输出 "Try again"

然后点进去

fun1函数对v6加密,异或1

int __cdecl fun1(int a1, int a2)
{int i; // [esp+Ch] [ebp-4h]for ( i = 0; i < a2; ++i )*(i + a1) ^= 1u;return 0;
}

check函数验证s与加密后的v6是否相等

int __cdecl check(int a1, int a2)
{char s[10]; // [esp+8h] [ebp-20h] BYREF__int16 v4; // [esp+12h] [ebp-16h]int v5; // [esp+14h] [ebp-14h]int v6; // [esp+18h] [ebp-10h]int i; // [esp+1Ch] [ebp-Ch]strcpy(s, "shfiu777");s[9] = 0;v4 = 0;v5 = 0;v6 = 0;if ( strlen(s) != a2 )return 0;for ( i = 0; i < a2; ++i ){if ( *(i + a1) != s[i] )return 0;}return 1;
}

然后直接写脚本解密即可

def decrypt(processed_flag):# 逆向 fun1 操作(每个字符与 1 异或)original_flag = ''.join(chr(ord(c) ^ 1) for c in processed_flag)return original_flagdef encrypt(original_flag):# 正向 fun1 操作(每个字符与 1 异或)processed_flag = ''.join(chr(ord(c) ^ 1) for c in original_flag)return processed_flagdef check(processed_flag):# 预期值为 "shfiu777"expected_value = "shfiu777"return processed_flag == expected_value# 主程序
if __name__ == "__main__":# 根据 check 函数的逻辑,processed_flag 必须等于 "shfiu777"processed_flag = "shfiu777"# 解密original_flag = decrypt(processed_flag)print("Original flag:", original_flag)# 验证解密结果reconstructed_flag = encrypt(original_flag)if check(reconstructed_flag):print("Verification successful!")else:print("Verification failed!")

flag{f9239748ca798af5d838ac8699bb5d3d}

4.crc

打开ida

int __fastcall main(int argc, const char **argv, const char **envp)
{const char *v3; // raxconst char *v4; // raxconst char *v5; // raxconst char *v6; // raxconst char *v7; // raxconst char *v8; // raxchar v10[16]; // [rsp+0h] [rbp-30h] BYREFchar v11[8]; // [rsp+10h] [rbp-20h] BYREF__int64 v12; // [rsp+18h] [rbp-18h]int v13; // [rsp+20h] [rbp-10h]unsigned __int64 v14; // [rsp+28h] [rbp-8h]v14 = __readfsqword(0x28u);*(_QWORD *)v11 = 0LL;v12 = 0LL;v13 = 0;scanf("%s", v11);strmncpy(v11, 0, 4, v10);v3 = (const char *)magic(v10);if ( !strcmp(v3, "d1f4eb9a") ){strmncpy(v11, 4, 1, v10);v4 = (const char *)magic(v10);if ( !strcmp(v4, "15d54739") ){strmncpy(v11, 5, 4, v10);v5 = (const char *)magic(v10);if ( !strcmp(v5, "540bbb08") ){strmncpy(v11, 9, 2, v10);v6 = (const char *)magic(v10);if ( !strcmp(v6, "3fcbd242") ){strmncpy(v11, 11, 4, v10);v7 = (const char *)magic(v10);if ( !strcmp(v7, "2479c623") ){strmncpy(v11, 15, 1, v10);v8 = (const char *)magic(v10);if ( !strcmp(v8, "fcb6e20c") )printf("Very nice!");}}}}}return 0;
}

分析可知是一系列的递归计算,这时候还看不出什么

打开strmncpy函数,是从输入的内容中进行字段选择的过程

char *__fastcall strmncpy(const char *a1, int a2, int a3, char *a4)
{char *v4; // raxchar *v5; // rdxchar *result; // raxint i; // [rsp+24h] [rbp-14h]const char *v9; // [rsp+28h] [rbp-10h]v9 = &a1[a2];for ( i = 0; i < a3; ++i ){v4 = a4++;v5 = (char *)v9++;*v4 = *v5;}result = a4;*a4 = 0;return result;
}

再打开magic函数,是CRC32计算

再回到main函数进行综合分析,这几个8位十六进制数其实就是从flag中取出1-4个字段进行crc计算后的结果

然后写了个通用的爆破脚本

import binascii
import stringprint('-------------Start Crack CRC-------------')def crack_crc_1(target):comment = ''chars = string.printablefor crc_value in target:for char1 in chars:char_crc = binascii.crc32(char1.encode())  # 获取遍历字符的CRC32值calc_crc = char_crc & 0xffffffff  # 将获取到的字符的CRC32值与0xffffffff进行与运算if calc_crc == crc_value:  # 将每个字符的CRC32值与每个文件的CRC32值进行匹配comment += char1print('{}'.format(comment))def crack_crc_2(target):comment = ''chars = string.printablefor crc_value in target:for char1 in chars:for char2 in chars:res_char = char1 + char2  # 获取遍历的任意2Byte字符char_crc = binascii.crc32(res_char.encode())  # 获取遍历字符的CRC32值calc_crc = char_crc & 0xffffffff  # 将获取到的字符的CRC32值与0xffffffff进行与运算if calc_crc == crc_value:  # 将获取字符的CRC32值与每个文件的CRC32值进行匹配comment += res_charprint('{}'.format(comment))def crack_crc_3(target):comment = ''chars = string.printablefor crc_value in target:for char1 in chars:for char2 in chars:for char3 in chars:res_char = char1 + char2 + char3  # 获取遍历的任意3Byte字符char_crc = binascii.crc32(res_char.encode())  # 获取遍历字符的CRC32值calc_crc = char_crc & 0xffffffff  # 将遍历的字符的CRC32值与0xffffffff进行与运算if calc_crc == crc_value:  # 将获取字符的CRC32值与每个文件的CRC32值进行匹配comment += res_charprint('{}'.format(comment))def crack_crc_4(target):comment = ''chars = string.printablefor crc_value in target:for char1 in chars:for char2 in chars:for char3 in chars:for char4 in chars:res_char = char1 + char2 + char3 + char4  # 获取遍历的任意4Byte字符char_crc = binascii.crc32(res_char.encode())  # 获取遍历字符的CRC32值calc_crc = char_crc & 0xffffffff  # 将遍历的字符的CRC32值与0xffffffff进行与运算if calc_crc == crc_value:  # 将获取字符的CRC32值与每个文件的CRC32值进行匹配comment += res_charprint('{}'.format(comment))if __name__ == '__main__':crack_crc_4([0xd1f4eb9a])crack_crc_1([0x15d54739])crack_crc_4([0x540bbb08])crack_crc_2([0x3fcbd242])crack_crc_4([0x2479c623])crack_crc_1([0xfcb6e20c])print('-----------CRC Crack Completed-----------')

然后大概爆破了有1分钟吧,然后就出来答案了

flag{ezrebyzhsh}

5.JunkCode

一道花指令的题,IDA打开后通过内存可以大概做出题目,但按照题目的本意应该是要进行花指令去除的

发现反编译不了,然后在后面看到了爆红的字段,然后就看到了这个花指令

z就是判断条件为1就跳转,但00411AC4为花指令,后面的内容无法被正常识别和解析,导致反编译失败

具体的操作方法是:选中内存地址00411AC4,按U键进行undefine

选中00411AC5处按C键MakeCode,再将00411AC4处的字节内容改为0x90

再选中00411AC5处按C键MakeCode

最后到函数头位置按P构建函数

再进行反编译

和刚开始看内存时的猜测一样,就是进行一次异或

写出解密脚本

a = 'dnceyhwli]amfg]kq]dwll{'
b = ''
for i in range(0,len(a)):b += chr(ord(a[i]) ^ 2)
print(b)

6.[HGAME 2023 week3]patchme

打开ida

从旁边看其他函数,发现有一个SMC自解密的函数,先利用IDC脚本还原看一下逻辑

for i in range(0x00000000000014C6,0x00000000000014C6+961):patch_byte(i,get_wide_byte(i)^0x66)
print("yes")

然后F5选择强制分析,但是可能canary赋值的时候会出错,先U转未定义然后选择分析就能识别正常

下面这里就是输出flag的地方,直接在函数在canary赋值完后直接jmp到flag操作的段上

但是这个init_array会执行两个函数,第一个不影响,第二个会调用SMC自解密的函数会影响我们原本解密的函数

这里有两个干扰,一个是exit退出直接nop掉,还有一个是异或干扰也直接nop掉

或者直接在这个函数前面直接调用我们的flag那个函数就可以完成操作,这里call open我们直接call flag

但是需要下面去call一个退出,不然会出现段错误

解密脚本

flag=[0xFA, 0x28, 0x8A, 0x80, 0x99, 0xD9, 0x16, 0x54, 0x63, 0xB5, 0x53, 0x49, 0x09, 0x05, 0x85, 0x58, 0x97, 0x90, 0x66, 0xDC, 0xA0, 0xF3, 0x8C, 0xCE, 0xBD, 0x4C, 0xF4, 0x54, 0xE8, 0xF3, 0x5C, 0x4C, 0x31, 0x83, 0x67, 0x16, 0x99, 0xE4, 0x44, 0xD1,0xAC, 0x6B, 0x61, 0xDA, 0xD0, 0xBB, 0x55]
key=[0x92, 0x4F, 0xEB, 0xED, 0xFC, 0xA2, 0x4F, 0x3B, 0x16, 0xEA, 0x67, 0x3B, 0x6C, 0x5A, 0xE4, 0x07, 0xE7, 0xD0, 0x12, 0xBF, 0xC8, 0xAC, 0xE1, 0xAF, 0xCE, 0x38, 0x91, 0x26, 0xB7, 0xC3, 0x2E, 0x13, 0x43, 0xE6, 0x11, 0x73, 0xEB, 0x97, 0x21, 0x8E, 0xC1, 0x0A, 0x54, 0xAE, 0xB5, 0xC9, 0x28]
for i in range(len(flag)):print(chr(flag[i]^key[i]),end="")

7.猜猜我在哪

拖入IDA,main函数伪代码如下:

可以看到这里对输入字符串的变换是随机的,会生成0-4的随机数,使用这个随机数作为key进行变换,最后与程序中存储的字符串进行比较判断flag是否正确。

encrypt函数伪代码如下:

这里实现了一个类似凯撒密码的加密,使用key对输入的字符串进行偏移,只会处理大写字母和小写字母(可参考ASCII码表了解其判断大小写字母的原理),其他字符不被偏移。逆运算则是减去key。

了解了加密原理之后,获取到加密后文本,则可尝试对其解密,观察main函数中第27行,其中用于比较的全局变量IDA给予其名为src,双击观察到其内容为khb i0dj lv qrw khuh.

编写一个脚本尝试对其进行如上面凯撒密码的解密

参考脚本:

flag = list("khb iodj lv qrw khuh.")for i in range(len(flag)):ord_ = ord(flag[i])if ord_ >= 0x60 and ord_ <=0x7a:flag[i] = chr(ord_ - 3)print("".join(flag))

由于已知传进来的字符串只有小写字母,就只判断了小写字母,第6行减去的值为试出来的key,当其值为3时可观察到输出结果内存在有意义的单词

输出结果:

he_ flag is not here.

可以发现这里的脚本写的并不完美,本应在26个英文字母之间循环的字符出现了一个下划线,需要手动修复一下

这里加密后的字符为b,减去偏移3后为y(abcdefghijklmnopqrstuvwxyz字母表中进行循环,b减1之后则到字母表的首项,再从字母表的尾项继续减掉剩下的2,得到字母y)

最终明文为:

hey flag is not here.

输入到程序中:

由于key是随机的,需要多试几次才能得到key值为3,最后会提示You are get it!

flag为flag{hey flag is not here.}

8.c2

可根据运行提示,结合IDA的反编译代码分析。运行如下图:

拖入ida中

分析一波

首先,对用户输入字符串进行了逐字节与10异或。随后,对异或后的字符串每字节ASCII码-3,得到最终变换的字符串。最后,将变换后的字符串与程序中检查的字符串比较,判断用户输入的flag是否正确。

检查字符串如图所示:

然后写出解密脚本

str1 = list("hefklijcda")
for i in range(len(str1)):str1[i] = chr(ord(str1[i]) + 3)str1[i] = chr(ord(str1[i]) ^ 10)print("".join(str1))

9.语言不通禁止入内

打开是反汇编代码,可用放ai里,也可以手搓

这里数据太长了就不放进ai里了,直接全局搜索main函数

放进vscode进行寻找

发现这里将字符串传给了processString函数,找到该函数:

该函数对传入的字符串逐字节进行了与6异或后减1的操作

提取出来字符串

str =  [0x7E, 0x60, 0x62, 0x64, 0x69, 0x75, 0x60, 0x64, 0x63, 0x64, 0x72, 0x72, 0x60, 0x68, 0x65, 0x6B, 0x7C]
flag = ""
for i in range(len(str)):flag += chr((str[i] ^ 6) - 1)
print(flag)

10.左右为难

一看就是迷宫题,简单分析一下

可以看到,16个字符为一行,把地图拿出来划分好,碰到$为成功,@为目前所在位置,碰到0失败

0000000000000000
0@00000111000000
0100111101011100
0100100001010100
0100111001110110
0100001000000010
01111110000000$0
0000000000000000

然后可以手搓数一下路径,或者拿bfs或者dfs算法优先算一下搞个脚本

from collections import deque# 定义迷宫
maze = ["0000000000000000","0@00000111000000","0100111101011100","0100100001010100","0100111001110110","0100001000000010","01111110000000$0","0000000000000000"
]# 调整方向优先级:下(s) > 右(d) > 上(w) > 左(a)
directions = {'s': (1, 0),'d': (0, 1),'w': (-1, 0),'a': (0, -1)
}def bfs(maze):rows, cols = len(maze), len(maze[0])# 找到起点 @ 和终点 $for r in range(rows):for c in range(cols):if maze[r][c] == '@':start = (r, c)if maze[r][c] == '$':end = (r, c)# 初始化队列和访问集合queue = deque([(start, "")])  # 队列元素为 (当前位置, 当前路径)visited = set([start])        # 已访问的格子while queue:(r, c), path = queue.popleft()# 如果到达终点,返回路径if (r, c) == end:return path# 尝试四个方向(按优先级顺序)for move, (dr, dc) in directions.items():nr, nc = r + dr, c + dc# 检查是否越界或访问过if 0 <= nr < rows and 0 <= nc < cols and (nr, nc) not in visited:if maze[nr][nc] == '1' or maze[nr][nc] == '$':  # 只能走 1 或 $visited.add((nr, nc))queue.append(((nr, nc), path + move))# 如果队列为空且未找到路径return "No path found"# 调用 BFS 并输出结果
result = bfs(maze)
print("迷宫路径:", result)

sssssdddddwwaawwdddwddsssddwwddssdss

然后md5加密一下

f787a0b786068936636c1e10e246a297

11. android2

拖进jadx中,看到加密密文

定位关键函数encode

但是发现这个key值不太对,所以有问题

然后可以看见key已经修改了

然后写个脚本

enc="棿棢棢棲棥棷棊棐棁棚棨棨棵棢棌"
print(hex(ord(enc[0])))
key=987654321
flag=""
for i in range(len(enc)):flag+=(chr(ord(enc[i])^key & 0xFFFF))
print(flag)

12. [FSCTF 2023]ezcode

打开附件,看样子还是python字节码

让ai帮我们给转换成一下python代码然后看看代码运行逻辑是咋样的

def func1(key, message):s_box = func2(key)crypt = str(func3(message, s_box))return cryptdef func2(key):s_box = list(range(256))j = 0for i in range(256):j = (j + s_box[i] + ord(key[i % len(key)])) % 256s_box[i], s_box[j] = s_box[j], s_box[i]return s_boxdef func3(plain, box):res = []y = 'FSCTF'i = j = 0for s in plain:i = (i + 1) % 256j = (j + box[i]) % 256box[i], box[j] = box[j], box[i]t = (box[i] + box[j]) % 256k = box[t]res.append(chr(ord(s) ^ k ^ ord(y[i % len(y)])))cipher = ''.join(res)return cipherdef encode(inputs):s = 'vwxrstuopq34567ABCDEFGHIJyz012PQRSTKLMNOZabcdUVWXYefghijklmn89+/'bin_str = []for i in inputs:x = str(bin(ord(i))).replace('0b', '')bin_str.append('{:0>8}'.format(x))outputs = ''nums = 0while bin_str:temp_list = bin_str[:3]if len(temp_list) != 3:nums = 3 - len(temp_list)while len(temp_list) < 3:temp_list.append('00000000')temp_str = ''.join(temp_list)temp_str_list = []for i in range(0, 4):temp_str_list.append(int(temp_str[i*6:(i+1)*6], 2))if nums:temp_str_list = temp_str_list[:4-nums]for i in temp_str_list:outputs += s[i]bin_str = bin_str[3:]outputs += '=' * numsreturn outputs# Main program
print('Please input your flag:')
m = input()
k = 'XFFTnT'
c = func1(k, m)
outputs = encode(c)if outputs == 'ADkopgjJFP+28RYgXUxU2Oej':print('Success!!')
else:print('think again')

然后来分析一下这个代码

4个函数

函数1

def func1(key, message):s_box = func2(key)         # 用 key 生成 s_box(类似RC4的KSA)crypt = str(func3(message, s_box))  # 用 s_box 对 message 加密(类似RC4的PRGA)return crypt

函数2 (有点像rsa)

def func2(key):s_box = list(range(256))   # 初始化 s_box = [0, 1, 2, ..., 255]j = 0for i in range(256):j = (j + s_box[i] + ord(key[i % len(key)])) % 256  # 用 key 打乱 s_boxs_box[i], s_box[j] = s_box[j], s_box[i]  # 交换 s_box[i] 和 s_box[j]return s_box

函数3 (PRGA + XOR加密)

def func3(plain, box):res = []y = 'FSCTF'       # 额外的XOR密钥i = j = 0for s in plain:i = (i + 1) % 256j = (j + box[i]) % 256box[i], box[j] = box[j], box[i]  # 更新 s_boxt = (box[i] + box[j]) % 256k = box[t]                       # 生成密钥流字节 k# 加密:s ^ k ^ y[i % len(y)]res.append(chr(ord(s) ^ k ^ ord(y[i % len(y)])))cipher = ''.join(res)return cipher

函数4 (自定义base64编码)

def encode(inputs):s = 'vwxrstuopq34567ABCDEFGHIJyz012PQRSTKLMNOZabcdUVWXYefghijklmn89+/'bin_str = []for i in inputs:x = bin(ord(i))[2:].zfill(8)  # 转8位二进制bin_str.append(x)outputs = ''nums = 0while bin_str:temp_list = bin_str[:3]  # 每次取3字节(24位)if len(temp_list) != 3:nums = 3 - len(temp_list)  # 补0的字节数temp_list += ['00000000'] * nums  # 补全3字节temp_str = ''.join(temp_list)  # 24位二进制temp_str_list = []for i in range(4):  # 分成4个6位组chunk = temp_str[i*6 : (i+1)*6]temp_str_list.append(int(chunk, 2))  # 转成0-63的数字if nums:  # 如果有补0,去掉最后`nums`个字符temp_str_list = temp_str_list[:4 - nums]for num in temp_str_list:outputs += s[num]  # 查表映射bin_str = bin_str[3:]  # 处理下一组outputs += '=' * nums  # 补'='return outputs

整个逻辑已经分析的很清楚了,只要把encode 得到 func1 的输出 b'=.\x07#M...'。逆向 func3:重新计算 s_box = func2('XFFTnT')。用 cipher_byte ^ k ^ y_byte 恢复 plain即可

def func2(key):s_box = list(range(256))j = 0for i in range(256):j = (j + s_box[i] + ord(key[i % len(key)])) % 256s_box[i], s_box[j] = s_box[j], s_box[i]return s_boxdef decrypt_func3(cipher, box):res = []y = 'FSCTF'i = j = 0for s in cipher:i = (i + 1) % 256j = (j + box[i]) % 256box[i], box[j] = box[j], box[i]t = (box[i] + box[j]) % 256k = box[t]# 解密:plain_byte = cipher_byte ^ k ^ y_byteplain_byte = ord(s) ^ k ^ ord(y[i % len(y)])res.append(chr(plain_byte))return ''.join(res)# 1. 计算 s_box
key = 'XFFTnT'
s_box = func2(key)# 2. 解密 cipher
cipher = '=.\x07#M\xd8Q\xef\x9d\xf2\x0ct\xc2\xd0\xadv|\xb7'
plain = decrypt_func3(cipher, s_box.copy())  # 避免修改原 s_box
print("Decrypted plaintext:", plain)

13.MYapk

挺有意思的,显示的数字是经过运算得出的,在内存中不存在这个数,导致了gg修改器修改不了这个数字

// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`,
// then press Enter. You can now see whitespace characters in your code.import java.util.Timer;
import java.util.TimerTask;public class Main {private Timer timer;private TimerTask timerTask;private Boolean started = false;private int success = 0;String getit(String input) {int i;byte[] message;int messageLength;int messageLength2;int f;int g;int[] T = new int[64];for (int i2 = 0; i2 < 64; i2++) {T[i2] = (int) (Math.abs(Math.sin(i2 + 1)) * 4.294967296E9d);}byte[] message2 = input.getBytes();int d = message2.length;int numBlocks = ((d + 8) >>> 6) + 1;int totalLength = numBlocks << 6;byte[] paddedMessage = new byte[totalLength];System.arraycopy(message2, 0, paddedMessage, 0, d);paddedMessage[d] = Byte.MIN_VALUE;long messageBits = d * 8;int i3 = 0;while (true) {i = 8;if (i3 >= 8) {break;}paddedMessage[(totalLength - 8) + i3] = (byte) (messageBits >>> (i3 * 8));i3++;}int[] state = {-1732584194, -271733879, 271733878, 1732584193};int i4 = 0;while (i4 < numBlocks) {int[] block = new int[16];for (int j = 0; j < 16; j++) {int index = (i4 << 6) + (j << 2);block[j] = (paddedMessage[index] & 255) | ((paddedMessage[index + 1] & 255) << i) | ((paddedMessage[index + 2] & 255) << 16) | ((paddedMessage[index + 3] & 255) << 24);}int a = state[0];int b = state[1];int c = state[2];int j2 = 0;int d2 = state[3];while (j2 < 64) {if (j2 < 16) {message = message2;messageLength = d;messageLength2 = d2;f = ((~b) & messageLength2) | (b & c);g = j2;} else {message = message2;messageLength = d;messageLength2 = d2;if (j2 < 32) {f = (messageLength2 & b) | ((~messageLength2) & c);g = ((j2 * 5) + 1) % 16;} else if (j2 < 48) {f = (b ^ c) ^ messageLength2;g = ((j2 * 3) + 5) % 16;} else {f = ((~messageLength2) | b) ^ c;g = (j2 * 7) % 16;}}int temp = messageLength2;int d3 = c;c = b;b += Integer.rotateLeft(a + f + block[g] + T[j2], 7);a = temp;j2++;d2 = d3;message2 = message;d = messageLength;T = T;block = block;}int messageLength3 = d;int messageLength4 = d2;state[0] = state[0] + a;state[1] = state[1] + b;state[2] = state[2] + c;state[3] = state[3] + messageLength4;i4++;d = messageLength3;T = T;i = 8;}byte[] hash = new byte[16];for (int i5 = 0; i5 < 4; i5++) {hash[i5 * 4] = (byte) (state[i5] & 255);hash[(i5 * 4) + 1] = (byte) ((state[i5] >>> 8) & 255);hash[(i5 * 4) + 2] = (byte) ((state[i5] >>> 16) & 255);hash[(i5 * 4) + 3] = (byte) ((state[i5] >>> 24) & 255);}StringBuilder sb = new StringBuilder();for (int i6 = 0; i6 < hash.length; i6++) {sb.append(String.format("%02x", Integer.valueOf(hash[i6] & 255)));}return sb.toString();}public static void main(String[] args) {Main ma=new Main();System.out.println( ma.getit("66.666s"));// Press Shift+F10 or click the green arrow button in the gutter to run the code.}
}

d1be6793ed8569ee43950ef21f991095

采用frida来做

setImmediate(function() { //prevent timeoutconsole.log("[*] Starting script");// //com.moible.r15.main
//     Java.perform(function () {
//       // var FridaActivity2 = Java.use(
//       //   "com.moible.r15.main");
//       // console.log("static_bool_var:", FridaActivity2.static_bool_var.value);
//       // FridaActivity2.setStatic_bool_var();  //调用静态函数
//       // console.log("static_bool_var:", FridaActivity2.static_bool_var.value);//       //调用非静态函数
//       Java.choose("com.moible.r15.main", {
//         onMatch : function(instance) {
//           console.log("bool_var:", instance.bool_var.value);
//           instance.setBool_var();
//           console.log("bool_var:", instance.bool_var.value);
//         }, onComplete : function() {
//         }
//       })
//     });Java.perform(function() {// 获取目标类的引用var main = Java.use('com.moible.r15.main');var mains=main.$new();// 调用目标函数var result = mains.getit("66.666s");// 在这里处理返回值console.log("Result: " + result);
});})
//frida -U -l exp.js -f com.moible.r15  com.moible.r15.main
//getText

NSSCTF{d1be6793ed8569ee43950ef21f991095}

调用静态函数可以直接调用,不用implementation再去定义。调用非静态函数需要用choose去搜索实例,再从结果实例里调用非静态函数,无需手动触发了。

package com.github.androiddemo.Activity;import android.content.Intent;public class FridaActivity2 extends BaseFridaActivity {private static boolean static_bool_var = false;private boolean bool_var = false;private static void setStatic_bool_var() {static_bool_var = true;}private void setBool_var() {this.bool_var = true;}
}
function call_var() {Java.perform(function () {var FridaActivity2 = Java.use("com.github.androiddemo.Activity.FridaActivity2");console.log("static_bool_var:", FridaActivity2.static_bool_var.value);FridaActivity2.setStatic_bool_var();  //调用静态函数console.log("static_bool_var:", FridaActivity2.static_bool_var.value);//调用非静态函数Java.choose("com.github.androiddemo.Activity.FridaActivity2", {onMatch : function(instance) {console.log("bool_var:", instance.bool_var.value);instance.setBool_var();console.log("bool_var:", instance.bool_var.value);}, onComplete : function() {}})});
}

最后的脚本:

setImmediate(function() { //prevent timeoutconsole.log("[*] Starting script");
// //com.moible.r15.main
//     Java.perform(function () {
//       // var FridaActivity2 = Java.use(
//       //   "com.moible.r15.main");
//       // console.log("static_bool_var:", FridaActivity2.static_bool_var.value);
//       // FridaActivity2.setStatic_bool_var();  //调用静态函数
//       // console.log("static_bool_var:", FridaActivity2.static_bool_var.value);//       //调用非静态函数
//       Java.choose("com.moible.r15.main", {
//         onMatch : function(instance) {
//           console.log("bool_var:", instance.bool_var.value);
//           instance.setBool_var();
//           console.log("bool_var:", instance.bool_var.value);
//         }, onComplete : function() {
//         }
//       })
//     });Java.perform(function() {// 获取目标类的引用var main = Java.use('com.moible.r15.main');var mains=main.$new();// 调用目标函数var result = mains.getit("66.666s");// 在这里处理返回值console.log("Result: " + result);
});
})
//frida -U -l exp.js -f com.moible.r15  com.moible.r15.main
//getText

计算过程解析

  1. 输入字符串"66.666s"(即计时器显示的值)
  2. 哈希算法getit() 函数实现了一个类似 MD5 的自定义哈希算法:
    • 初始化 4 个状态变量(类似 MD5 的 A/B/C/D)
    • 对输入进行 填充(Padding)和 分块(Block Processing)
    • 进行 64 轮位运算(涉及 ANDORXORNOT循环左移
    • 最终生成 16 字节(128 位)哈希值,并转为 32 位十六进制字符串
  1. 计算步骤
    • 输入"66.666s"(字节形式:[0x36, 0x36, 0x2e, 0x36, 0x36, 0x36, 0x73]
    • 填充
      • 在末尾追加 0x8010000000
      • 填充 0x00 直到总长度为 56 mod 64(MD5 标准填充)
      • 最后 8 字节存储输入长度(7 * 8 = 56,即 0x38
    • 分块处理
      • 分成 512-bit(64 字节) 的块
      • 每块再分成 16 个 32-bit 整数
    • 64 轮位运算
      • 每轮使用不同的 非线性函数F, G, H, I 类似 MD5)
      • 每轮使用不同的 位移量常数(基于 sin 函数)
    • 最终哈希
      • 4 个状态变量 A, B, C, D 拼接成 16 字节
      • 转为 小端序十六进制 字符串
  1. 最终结果
    • 计算 getit("66.666s") 得到 1a74ee530fafa690dcddd0ce38260755

NSSCTF{1a74ee530fafa690dcddd0ce38260755}

14. [MoeCTF 2021]clothes

1.查壳,发现是ASPack壳

发现需要用工具脱壳,当然也可以用onlydebug进行手动脱壳然后dump下来

这里直接用工具进行脱壳就行

打开ida

发现这里的逻辑异常清晰,是一个加密文一和密文二的异或就行

enc1=[0x1E, 0x2A, 0x4E, 0x24, 0x23, 0x0F, 0x28, 0x39, 0x71, 0x3C, 0x4F, 0x4C, 0x6E, 0x35, 0x22, 0x3E, 0x08, 0x02, 0x31, 0x7D, 0x2C, 0x36, 0x16, 0x04, 0x22, 0x1A, 0x53, 0x07, 0x73, 0x38, 0x00, 0x00]
enc2=[0x73, 0x45, 0x2B, 0x47, 0x57, 0x69, 0x53, 0x0D, 0x44, 0x4C, 0x2E, 0x2F, 0x05, 0x6A, 0x13, 0x4D, 0x57, 0x31, 0x4B, 0x22, 0x58, 0x06, 0x49, 0x71, 0x4C, 0x6A, 0x32, 0x64, 0x18, 0x45, 0x00, 0x00]
flag=""
for i in range(30):flag+=chr(enc1[i]^enc2[i])
print(flag)

15. [MTCTF 2021 final]pyc

发现是pyc字节码题目,这里直接用uncompyle6或者用pycdc.exe进行反编译即可

import hashlib
s = input()
if len(s) != 72:print('wrong')
a1 = set()
a2 = set()
a3 = set()
a4 = [0x9E3779B9L,0x9E3779B9L]
for d in '012345678':a3.add(s.count(d))
for i in range(0, len(s), 9):for l in range(0, 15, 2):a2.add(sum((lambda .0: Unsupported opcode: GEN_START
pass# WARNING: Decompyle incomplete
)((lambda .0: [ int(v) for v in .0 ])(str(a4[1] ^ 0xE4172600000000L ^ 0xCD70877AL)[l:l + 3]))))if int(s[i:i + 9]) >= a4[0]:passelse:a4[0] = int(s[i:i + 9])a1.add(s[i:i + 9])if len(a1) == 8 and len(a2) == 1 and len(a3) == 1 and s.count('9') == 0:print(f'''flag{{{hashlib.md5(s.encode('ascii')).hexdigest()}}}''')return NoneNone(print)return None

进行分析

import hashlib
s = input()
if len(s) != 72:print('wrong')
a1 = set()
a2 = set()
a3 = set()
a4 = [0x9E3779B9,0x9E3779B9]
for d in '012345678':a3.add(s.count(d))for i in range(0, len(s), 9): # 8组for l in range(0, 15, 2):a2.add(sum(int(s[i+j:i+j+1]) for j in ([ int(v) for v in (str(a4[1] ^ 0xE4172600000000 ^ 0xCD70877A)[l:l + 3])])))# 642 201 174 480 063 345 528 867# 三行 三列 两对角线"""|0|1|2||3|4|5||6|7|8|"""print(a2)if int(s[i:i + 9]) >= a4[0]: # 每一组都要比前一组小passelse:a4[0] = int(s[i:i + 9])a1.add(s[i:i + 9])if len(a1) == 8 and len(a2) == 1 and len(a3) == 1 and s.count('9') == 0:# '0'~'8'出现次数一样 没有'9' 求出的sum和相同 9个字符一组 每组9个字符都一样print(f'''flag{{{hashlib.md5(s.encode('ascii')).hexdigest()}}}''')# 分析发现是3x3幻方 用0~8来填  刚好3x3也只有8个

结合set a1,a2,a3 以及一些sum的check 可以分析出这是8个3x3幻方
网上找到

-1过后降序排列md5即可

然后输入

723048561705246381561048723507642183381246705327840165183642507165840327


 

http://www.xdnf.cn/news/212041.html

相关文章:

  • 51la统计坏了吗?用悟空统计保障运营决策安全详解
  • 斯坦福课程 MSE 318/CME 338: Large-Scale Numerical Optimization
  • Linux权限拓展
  • Headers池技术在Python爬虫反反爬中的应用
  • Kotlin 常见问题
  • 简单音频比较
  • 数据库day-08
  • C#中winform窗体如何捕获键盘按键事件
  • 深度学习篇---模型权重变化与维度分析
  • 阿里云 OpenManus 实战:高效AI协作体系
  • “情况说明“以后,Unity XR 开发者如何选择?
  • HTTP(超文本传输协议)全面总结
  • 蓝桥杯 10. 凯撒加密
  • [C]基础14.字符函数和字符串函数
  • 网络原理—应用层和数据链路层
  • 指针(5)
  • Spring Boot 集成 ActiveMQ 实现异步消息通信(一)
  • 跨平台项目部署全攻略:Windows后端+Mac前端在服务器的协同实战
  • Arduion 第一天,变量的详细解析
  • 三格电子——四路CAN转4G网关使用中的常见问题
  • 【深度学习新浪潮】ISP芯片算法技术简介及关键技术分析
  • 深度解析 MyBatis`@TableField(typeHandler = JacksonTypeHandler.class)`:优雅处理复杂数据存储
  • 深入理解二分查找
  • AI防摔倒检测系统
  • 实验七:基于89C51和DS18B20的温度采集与显示
  • 【从滚动条缺失到布局体系:前端布局问题的系统性思考】
  • pytorch 一些常用语法
  • 图漾官网Sample_V1版本C++语言完整参考例子---单相机版本
  • 企业办公协同平台安全一体化生态入住技术架构与接口标准分析报告
  • ubnuntu使用conda进行虚拟环境迁移,复制,克隆