网络通信流量分析
第一道题比较简单就是一道魔改的RC4加密,只是题目附件有点混淆给了三个文件其中还有一个pcap文件有点奇怪,刚开始做的时候不知道这个有什么用,后面才发现原来key在里面
先对其查壳,两个都没壳直接用IDA打开先分析cli文件
找到main函数
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
unsigned int v3; // ebx
char *v4; // rax
socklen_t addr_len; // [rsp+0h] [rbp-50h] BYREF
int optval; // [rsp+4h] [rbp-4Ch] BYREF
int fd; // [rsp+8h] [rbp-48h]
int v8; // [rsp+Ch] [rbp-44h]
struct sockaddr addr; // [rsp+10h] [rbp-40h] BYREF
struct sockaddr v10; // [rsp+20h] [rbp-30h] BYREF
unsigned __int64 v11; // [rsp+38h] [rbp-18h]
v11 = __readfsqword(0x28u);
addr_len = 16;
optval = 1;
fd = socket(2, 1, 0);
if ( fd < 0 )
{
perror("Socket creation failed");
exit(1);
}
addr.sa_family = 2;
*(_WORD *)addr.sa_data = htons(0x22B8u);
*(_DWORD *)&addr.sa_data[2] = 0;
if ( setsockopt(fd, 1, 2, &optval, 4u) == -1 )
{
perror("setsockopt faild");
close(fd);
exit(1);
}
if ( bind(fd, &addr, 0x10u) < 0 )
{
perror("Bind failed");
exit(1);
}
if ( listen(fd, 5) < 0 )
{
perror("listen failed");
close(fd);
exit(1);
}
puts("Server is listening on port 8888...");
while ( 1 )
{
while ( 1 )
{
v8 = accept(fd, &v10, &addr_len);
if ( v8 >= 0 )
break;
perror("accept failed");
}
v3 = ntohs(*(uint16_t *)v10.sa_data);
v4 = inet_ntoa(*(struct in_addr *)&v10.sa_data[2]);
printf("Connection from %s:%d\n", v4, v3);
if ( !fork() )
{
close(fd);
sub_1825((unsigned int)v8);
exit(0);
}
close(v8);
}
}
审计可得这是一个需要通过密码认证的服务的客户端程序,既然有认证肯定有认证程序,然后在sub_1329函数中找到了经典RC4算法
unsigned __int64 __fastcall sub_1329(const char *a1, __int64 a2, unsigned __int64 a3)
{
char v3; // bl
char v6; // [rsp+27h] [rbp-139h]
char v7; // [rsp+27h] [rbp-139h]
unsigned __int64 i; // [rsp+28h] [rbp-138h]
unsigned __int64 j; // [rsp+28h] [rbp-138h]
__int64 v10; // [rsp+28h] [rbp-138h]
__int64 v11; // [rsp+30h] [rbp-130h]
__int64 v12; // [rsp+30h] [rbp-130h]
unsigned __int64 k; // [rsp+38h] [rbp-128h]
char v14[264]; // [rsp+40h] [rbp-120h] BYREF
unsigned __int64 v15; // [rsp+148h] [rbp-18h]
v15 = __readfsqword(0x28u);
LOBYTE(v11) = 0;
for ( i = 0LL; i <= 0xFF; ++i )
v14[i] = i;
for ( j = 0LL; j <= 0xFF; ++j )
{
v3 = v14[j] + v11;
v11 = (unsigned __int8)(v3 + a1[j % strlen(a1)]);
v6 = v14[j];
v14[j] = v14[v11];
v14[v11] = v6;
}
LOBYTE(v12) = 0;
LOBYTE(v10) = 0;
for ( k = 0LL; k < a3; ++k )
{
v10 = (unsigned __int8)(v10 + 1);
v12 = (unsigned __int8)(v14[v10] + v12);
v7 = v14[v10];
v14[v10] = v14[v12];
v14[v12] = v7;
*(_BYTE *)(a2 + k) ^= v14[(unsigned __int8)(v14[v10] + v14[v12])];
}
return __readfsqword(0x28u) ^ v15;
}
审计后发现没有加密后的数据与密钥key,程序中也没找到,这时候想到了另一个程序,也用IDA打开看看
找到main函数
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
unsigned int v3; // ebx
char *v4; // rax
socklen_t addr_len; // [rsp+0h] [rbp-50h] BYREF
int optval; // [rsp+4h] [rbp-4Ch] BYREF
int fd; // [rsp+8h] [rbp-48h]
int v8; // [rsp+Ch] [rbp-44h]
struct sockaddr addr; // [rsp+10h] [rbp-40h] BYREF
struct sockaddr v10; // [rsp+20h] [rbp-30h] BYREF
unsigned __int64 v11; // [rsp+38h] [rbp-18h]
v11 = __readfsqword(0x28u);
addr_len = 16;
optval = 1;
fd = socket(2, 1, 0);
if ( fd < 0 )
{
perror("Socket creation failed");
exit(1);
}
addr.sa_family = 2;
*(_WORD *)addr.sa_data = htons(0x22B8u);
*(_DWORD *)&addr.sa_data[2] = 0;
if ( setsockopt(fd, 1, 2, &optval, 4u) == -1 )
{
perror("setsockopt faild");
close(fd);
exit(1);
}
if ( bind(fd, &addr, 0x10u) < 0 )
{
perror("Bind failed");
exit(1);
}
if ( listen(fd, 5) < 0 )
{
perror("listen failed");
close(fd);
exit(1);
}
puts("Server is listening on port 8888...");
while ( 1 )
{
while ( 1 )
{
v8 = accept(fd, &v10, &addr_len);
if ( v8 >= 0 )
break;
perror("accept failed");
}
v3 = ntohs(*(uint16_t *)v10.sa_data);
v4 = inet_ntoa(*(struct in_addr *)&v10.sa_data[2]);
printf("Connection from %s:%d\n", v4, v3);
if ( !fork() )
{
close(fd);
sub_1825((unsigned int)v8);
exit(0);
}
close(v8);
}
}
审计可得这是一个基础的TCP服务器程序,典型的多进程网络服务器架构,每个客户端连接由一个独立的子进程处理
主要步骤
- 创建socket
- 设置端口8888
- 绑定地址
- 监听连接
- 循环接受客户端连接
- 创建子进程处理请求
这两个附件一个客服端一个服务器相互对应想必数据就应该在这个服务器程序中,然后在sub_1825函数中找到了加密后的数据并在sub_1552函数中找到了魔改的RC4
这是服务器端处理客户端请求的函数
unsigned __int64 __fastcall sub_1825(int a1)
{
__int64 v2; // [rsp+18h] [rbp-58h] BYREF
__int64 buf[4]; // [rsp+20h] [rbp-50h] BYREF
__int64 v4[4]; // [rsp+40h] [rbp-30h] BYREF
__int16 v5; // [rsp+60h] [rbp-10h]
unsigned __int64 v6; // [rsp+68h] [rbp-8h]
v6 = __readfsqword(0x28u);
memset(buf, 0, sizeof(buf));
v2 = 0LL;
v4[0] = 0xC632A2F05BD9371DLL;
v4[1] = 0x3AA73E7E508CA730LL;
v4[2] = 0x1C6B85816B58C0BALL;
v4[3] = 0x9742C18A7C80F54CLL;
v5 = -14448;
recv(a1, buf, 0x20uLL, 0);
*((_BYTE *)buf + strcspn((const char *)buf, "\n")) = 0;
if ( (unsigned int)sub_14A4(buf) )
{
send(a1, "right\n", 6uLL, 0);
sub_1552(buf, v4, 34LL);
recv(a1, &v2, 8uLL, 0);
if ( (_BYTE)v2 == 10 )
send(a1, v4, 0x22uLL, 0);
}
else
{
send(a1, "wrong\n", 6uLL, 0);
}
close(a1);
return __readfsqword(0x28u) ^ v6;
}
这是一个验证程序
unsigned __int64 __fastcall sub_1552(const char *a1, __int64 a2, unsigned __int64 a3)
{
char v3; // bl
char v6; // [rsp+27h] [rbp-139h]
char v7; // [rsp+27h] [rbp-139h]
unsigned __int64 i; // [rsp+28h] [rbp-138h]
unsigned __int64 j; // [rsp+28h] [rbp-138h]
__int64 v10; // [rsp+28h] [rbp-138h]
__int64 v11; // [rsp+30h] [rbp-130h]
__int64 v12; // [rsp+30h] [rbp-130h]
unsigned __int64 k; // [rsp+38h] [rbp-128h]
char v14[264]; // [rsp+40h] [rbp-120h] BYREF
unsigned __int64 v15; // [rsp+148h] [rbp-18h]
v15 = __readfsqword(0x28u);
LOBYTE(v11) = 0;
for ( i = 0LL; i <= 0xFF; ++i )
v14[i] = i;
for ( j = 0LL; j <= 0xFF; ++j )
{
v3 = v14[j] + v11;
v11 = (unsigned __int8)(v3 + a1[j % strlen(a1)]);
v6 = v14[j];
v14[j] = v14[v11];
v14[v11] = v6;
}
LOBYTE(v12) = 0;
LOBYTE(v10) = 0;
for ( k = 0LL; k < a3; ++k )
{
v10 = (unsigned __int8)(v10 + 1);
v12 = (unsigned __int8)(v14[v10] + v12);
v7 = v14[v10];
v14[v10] = v14[v12];
v14[v12] = v7;
*(_BYTE *)(a2 + k) ^= v14[(unsigned __int8)(v14[v10] + v14[v12])] ^ 0x25;
}
return __readfsqword(0x28u) ^ v15;
}
但要解密RC4要密钥在这个程序中也没找到,想必就在那个pcap文件中了,打开在冒红的地方找到了
然后只要写个解密脚本即可
def rc4_decrypt(password, encrypted_data):
# 初始化S盒
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + ord(password[i % len(password)])) & 0xFF
S[i], S[j] = S[j], S[i]
i = j = 0
result = bytearray()
for byte in encrypted_data:
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
# 解密:异或0x25和密钥流
key_byte = S[(S[i] + S[j]) & 0xFF] ^ 0x25 # 魔改处
result.append(byte ^ key_byte)
return bytes(result)
if __name__ == "__main__":
# 密码
password = "WangDingCUPKEY!!"
# 题目中的加密数据
encrypted_data = [
0xC632A2F05BD9371D,
0x3AA73E7E508CA730,
0x1C6B85816B58C0BA,
0x9742C18A7C80F54C,
0xC790 # -14448的补码
]
# 转换为bytes
encrypted_bytes = b''
for data in encrypted_data[:-1]: # 处理前4个8字节数据
encrypted_bytes += data.to_bytes(8, 'little')
encrypted_bytes += encrypted_data[-1].to_bytes(2, 'little') # 处理最后2字节
# 解密
decrypted = rc4_decrypt(password, encrypted_bytes)
print(decrypted.decode())
python逆向分析
很抽象的一道题,感觉就是一道misc题
给了一个文件夹与一个bin文件还挺大的,刚开始分析的时候,踩了挺多坑,文件夹中有一个跟文件夹相同名字的一个程序是用python写还是2.7版本的,运行这个程序也要2.7的python环境会得到这个程序是干嘛的,当时测了很多遍才明白这是干什么的。
先对OWO文件解包分析主要的pyc文件进行反编译
# uncompyle6 version 3.9.2
# Python bytecode version base 2.7 (62211)
# Decompiled from: Python 3.12.3 (tags/v3.12.3:f6650f9, Apr 9 2024, 14:05:25) [MSC v.1938 64 bit (AMD64)]
# Embedded file name: /mnt/d/Work/网鼎杯-出题/Reverse-compress/src/obf-src/dist/obf/OwO.py
from pytransform import pyarmor_runtime
pyarmor_runtime()
__pyarmor__(__name__, __file__, b'PYARMOR\x00\x00\x02\x07\x00\x03\xf3\r\n\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00@\x00\x00\x00\xc6\x03\x00\x00\x00\x00\x00\x18\x18e\xc4H\xb5-4\x1a\xb3\xdb\x12\x0f!\xc3\xbe[\x00\x00\x00\x00\x00\x00\x00\x00\xec\xc7\x9b>ih\x96\xb1*\xf1\xc9\xac\x12\xf7\xde\xd7\xd9]\xe3\xc7\x1ec#\xa3\x0f\rHs\xc7\x95\xe5\x13\x08\xd0\xcf2\x95\x82\xc5\x12]\x18\xb4~*\r9\xda{P,\\\xdd\xc0\xa1W\xca\x9aa\xe6\xaa\x81\xd2>\xd0S[6*<t\xedI>k>\x8e>\xf2\x0c\x93H\x8f\xcc\x03\xde\xf0@\xb2\xf6\xa2\x1f\x10>r\x13\xa5\x12\t\xefO\xe1\x97V\x99\xfa\xa6*\xc0\xb3a\xa3\',\xd8m\x1e\xcf\xcc\xd7%\xc2\x12]\x0b\x12\x01}J\x93\xdb01\x95\xae\xf1\x8f\xe9*\xb8\x1e\xb7\x86\xe8y\x80#\xf7t\x04;\xe9\xefUm\xd7\x9f\x97\x9e\xc2Y\xe1=\xf5\x19;\xc7&\xee\x8b\x00y\x08\x85hJ\xd6\xbc}\x86\x85\xff\xcdt\xa8qU\xe9i=\x80\x9e\x1e\xb0\xb1Z\x91\xab\xabo\xfd\x01\'\x893\x10\xbc\xc9\xf1\x84h\x08+^\xdc\x1bI\xbe\xb5\x88\xae\xfb\x17r\xa8\xe49B=c\x01\x8fC`\xbc\x15!p\x81\x13\xcf\x83\x8a\x02\xa8;<\xa11\xf6\xf2\x84\xc5\x03^\x10\xb3\xf42\xa9\xcc~\xe3\xc0\t\xd5\x1e\xf8\xc8\x9e\xae\xaf\x9c\x99\xa9m\x9a\x9e\xff*\x9e\xc4v`&\x94M\xb7\xa9\nY\x1a\xb4t\xf6\x1b]\x054\x7fG \x8a\xfan\xbd0\xea\xc1\x8b]\x95\xcb7\x86N\xe3\xc4a\xad\xa3\xa7\x0b\xc1\xfd\xd9\xf0\x16\x8bf\xe1(\xbf\x88\xa6\xcci\xaf\x0fE\x1f\x1d\xa8\xbd\xdb\xf2\x1aT\x13K\x98i%\xf4\xbd]\x0c\xa1$\xb4\t\x8d@7\xf9\x88\xd7\xd9L,\xe3\x87\x11\x13\xda\x12\xd1(\x8f\x8d(\x97\x95\x90Q\xff\xbe\xd0Dg\x14\x17y\x8a\xdd\x05\x85D\x0f\xd5\xeb\xdfev\t\xe2\xe5\x8b\t&J\xbbI)\x9e\xfb_\xe2\xed<\xef5\xaf\xb0\xf4\xdb\x9e\x14T\xd8J\xbb\xf5\xbd\xd0f\xda\xa20_"\xd0\n\x81\xaa\xa8\x8a\xd2\xb0\xe0b+\xace~6\xd8\xbb\xa8\xc0\x9bP\xa9\xa3C\xe5d\xe8\xfb\x9d,\x983\x05\xd8\r\xbd\x12G\x113L\xfc\xe6o\xfb\xb4\x8ct\xa8\xd2>\x16D2\xee\x10nN-Q.Sg\xd8\xec\xd4Nv~s\xbe8\xb2\x01\xe6\xc5\xcb\x90\xa3Gi!\x1f\x88?\xbe\xc0}\xfcd\nx\x05\xf4\xcdyuh\x99\x19\xd7\x88P\x8d\xd0\xe6_*\xd9\x83#\x03\x8d\xa9u2\xa4aB\x19$\x12\xab:-\xc0\xa97v`\x82Z\xd1\x999\xb1X\xb1GdVh{D\x012\xc5\x0c\x1d\xcc\xe4\xd4O\x1dl<\xb4IT\x12\x8bQ\x86\x8e\x9f\x14\x7f/\xb1\xd0\xfe\x96$f-\xbb\x9c\x83\xc6J\xa1\xdd\xbd\xcf\xa7ixu\xf8\xfd\xaf\xc4\x08}\x85d\x1b\x13\xbb~ \x87\xec?\x04\'\x16\xb0{\xd2@\x17\xda\xb2\xe0|\xc3\xdc\xd8\xdb\xa8d+^X@\x80\xce\x7f\xb0\tj\x80\xb4\x88\xf0\x81\x82\x95,)z\x17\xeaW>e\x8a\x8d\x06\x10G\xe2}#\xf3\xbc?Y\xfb\x18\xf0\xe0Iq\xf1t\xaa\xa0\x89\x01bb\x0ef7t\xff4\xd8\xa9[\x1d\x81GK\x08bY\x81\x95>>\x9b\r\xb9J\xea\xd3sX\x0c\xa0\x12 \xf20\x8bN9r\x1b$N\x1e\xe0?o\xbaR^:\x87m\xb8\xb3P7\xf9\xf5\xa9\x8c\xd7\xe7>\x85\n\xa2\xdd\x17\xb8\x81\x9aH,"\xa1*\xa2\xe8#\xb9\xf25\xb3g]*/s}%KD\xa1\x8cI"Y\x87\x9a\xf3z\x8f+\xc6\x9bEF\xb9\xa9\x1bG\x1d\x8fHK\x10H1\xbfK\xb7\xden\xc9\x0b\xcb\x92s\xc0\xc1mz\xcc\x019\xc35w\xd8\x92R\xe3"6o!x\x9d\x9e\xf4\xad\xeeH_+\x98\xeaO\xa0\xb9\xcd\x98\xe2\x81\x10\x1e\x03\x80\xf0\x12]#g\x9ak\xce\x97K3\xe4b-2\xa0\x82w,\xd56\xe6p\x8f\x8f\xa8XL\xbcvQ9\x11\xae\x17\xc4\x06\xdf\x85\xc2\x06\x9b\xfd\xe8\x96\xecG\x01\x84\x00\t\xf9\xf8J\xb4\x01%\xc1\x94\xf5\xde\xff\xb1\xa8_\xbc\xac\xc6\x1c%;sd(\x1a\xc4\x96?\xac\x0f#\xe1\xc9n\xc9', 2)
这是一个使用PyArmor加密的Python代码,里面还用了 pytransform我就在包里面找到了它的pyc文件并进行了反编译
import os, platform, sys, struct
from ctypes import cdll, c_char, c_char_p, c_int, c_void_p, pythonapi, py_object, PYFUNCTYPE, CFUNCTYPE
from fnmatch import fnmatch
plat_path = 'platforms'
plat_table = (
(
'windows', ('windows', 'cygwin-*')),
(
'darwin', ('darwin', 'ios')),
(
'linux', ('linux*', )),
(
'freebsd', ('freebsd*', 'openbsd*')),
(
'poky', ('poky', )))
arch_table = (
(
'x86', ('i?86', )),
(
'x86_64', ('x64', 'x86_64', 'amd64', 'intel')),
(
'arm', ('armv5', )),
(
'armv6', ('armv6l', )),
(
'armv7', ('armv7l', )),
(
'ppc64', ('ppc64le', )),
(
'mips32', ('mips', )),
(
'aarch32', ('aarch32', )),
(
'aarch64', ('aarch64', 'arm64')))
HT_HARDDISK, HT_IFMAC, HT_IPV4, HT_IPV6, HT_DOMAIN = range(5)
_pytransform = None
class PytransformError(Exception):
pass
def dllmethod(func):
def wrap(*args, **kwargs):
return func(*args, **kwargs)
return wrap
@dllmethod
def version_info():
global _pytransform
prototype = PYFUNCTYPE(py_object)
dlfunc = prototype(('version_info', _pytransform))
return dlfunc()
@dllmethod
def init_pytransform():
major, minor = sys.version_info[0:2]
prototype = PYFUNCTYPE(c_int, c_int, c_int, c_void_p)
init_module = prototype(('init_module', _pytransform))
ret = init_module(major, minor, pythonapi._handle)
if ret & 61440 == 4096:
raise PytransformError('Initialize python wrapper failed (%d)' % (ret & 4095))
return ret
@dllmethod
def init_runtime():
prototype = PYFUNCTYPE(c_int, c_int, c_int, c_int, c_int)
_init_runtime = prototype(('init_runtime', _pytransform))
return _init_runtime(0, 0, 0, 0)
@dllmethod
def encrypt_code_object(pubkey, co, flags, suffix=''):
_pytransform.set_option(6, suffix.encode())
prototype = PYFUNCTYPE(py_object, py_object, py_object, c_int)
dlfunc = prototype(('encrypt_code_object', _pytransform))
return dlfunc(pubkey, co, flags)
@dllmethod
def generate_license_file(filename, priname, rcode, start=-1, count=1):
prototype = PYFUNCTYPE(c_int, c_char_p, c_char_p, c_char_p, c_int, c_int)
dlfunc = prototype(('generate_project_license_files', _pytransform))
if sys.version_info[0] == 3:
return dlfunc(filename.encode(), priname.encode(), rcode.encode(), start, count)
return dlfunc(filename, priname, rcode, start, count)
@dllmethod
def generate_license_key(prikey, keysize, rcode):
prototype = PYFUNCTYPE(py_object, c_char_p, c_int, c_char_p)
dlfunc = prototype(('generate_license_key', _pytransform))
if sys.version_info[0] == 2:
return dlfunc(prikey, keysize, rcode)
return dlfunc(prikey, keysize, rcode.encode())
@dllmethod
def get_registration_code():
prototype = PYFUNCTYPE(py_object)
dlfunc = prototype(('get_registration_code', _pytransform))
return dlfunc()
@dllmethod
def get_expired_days():
prototype = PYFUNCTYPE(py_object)
dlfunc = prototype(('get_expired_days', _pytransform))
return dlfunc()
@dllmethod
def clean_obj(obj, kind):
prototype = PYFUNCTYPE(c_int, py_object, c_int)
dlfunc = prototype(('clean_obj', _pytransform))
return dlfunc(obj, kind)
def clean_str(*args):
tdict = {'str': 0,
'bytearray': 1,
'unicode': 2}
for obj in args:
k = tdict.get(type(obj).__name__)
if k is None:
raise RuntimeError('Can not clean object: %s' % obj)
clean_obj(obj, k)
return
def get_hd_info(hdtype, name=None):
if hdtype not in range(HT_DOMAIN + 1):
raise RuntimeError('Invalid parameter hdtype: %s' % hdtype)
size = 256
t_buf = c_char * size
buf = t_buf()
cname = c_char_p(0 if name is None else name.encode('utf-8') if hasattr('name', 'encode') else name)
if _pytransform.get_hd_info(hdtype, buf, size, cname) == -1:
raise PytransformError('Get hardware information failed')
return buf.value.decode()
def show_hd_info():
return _pytransform.show_hd_info()
def assert_armored(*names):
prototype = PYFUNCTYPE(py_object, py_object)
dlfunc = prototype(('assert_armored', _pytransform))
def wrapper(func):
def wrap_execute(*args, **kwargs):
dlfunc(names)
return func(*args, **kwargs)
return wrap_execute
return wrapper
def get_license_info():
info = {'ISSUER': None,
'EXPIRED': None,
'HARDDISK': None,
'IFMAC': None,
'IFIPV4': None,
'DOMAIN': None,
'DATA': None,
'CODE': None}
rcode = get_registration_code().decode()
if rcode.startswith('*VERSION:'):
index = rcode.find('\n')
info['ISSUER'] = rcode[9:index].split('.')[0].replace('-sn-1.txt', '')
rcode = rcode[index + 1:]
index = 0
if rcode.startswith('*TIME:'):
from time import ctime
index = rcode.find('\n')
info['EXPIRED'] = ctime(float(rcode[6:index]))
index += 1
if rcode[index:].startswith('*FLAGS:'):
index += len('*FLAGS:') + 1
info['FLAGS'] = ord(rcode[index - 1])
prev = None
start = index
for k in [3, 4, 5, 6, 23, 8]:
index = rcode.find('*%s:' % k)
if index > -1:
if prev is not None:
info[prev] = rcode[start:index]
prev = k
start = index + len(k) + 2
info['CODE'] = rcode[start:]
i = info['CODE'].find(';')
if i > 0:
info['DATA'] = info['CODE'][i + 1:]
info['CODE'] = info['CODE'][:i]
return info
def get_license_code():
return get_license_info()['CODE']
def get_user_data():
return get_license_info()['DATA']
def _match_features(patterns, s):
for pat in patterns:
if fnmatch(s, pat):
return True
def _gnu_get_libc_version():
try:
prototype = CFUNCTYPE(c_char_p)
ver = prototype(('gnu_get_libc_version', cdll.LoadLibrary('')))()
return ver.decode().split('.')
except Exception:
pass
def format_platform(platid=None):
if platid:
return os.path.normpath(platid)
plat = platform.system().lower()
mach = platform.machine().lower()
for alias, platlist in plat_table:
if _match_features(platlist, plat):
plat = alias
break
if plat == 'linux':
cname, cver = platform.libc_ver()
if cname == 'musl':
plat = 'musl'
elif cname == 'libc':
plat = 'android'
elif cname == 'glibc':
v = _gnu_get_libc_version()
if v and len(v) >= 2 and int(v[0]) * 100 + int(v[1]) < 214:
plat = 'centos6'
for alias, archlist in arch_table:
if _match_features(archlist, mach):
mach = alias
break
if plat == 'windows' and mach == 'x86_64':
bitness = struct.calcsize(('P').encode()) * 8
if bitness == 32:
mach = 'x86'
return os.path.join(plat, mach)
def _load_library(path=None, is_runtime=0, platid=None, suffix='', advanced=0):
path = os.path.dirname(__file__) if path is None else os.path.normpath(path)
plat = platform.system().lower()
name = '_pytransform' + suffix
if plat == 'linux':
filename = os.path.abspath(os.path.join(path, name + '.so'))
elif plat == 'darwin':
filename = os.path.join(path, name + '.dylib')
elif plat == 'windows':
filename = os.path.join(path, name + '.dll')
elif plat == 'freebsd':
filename = os.path.join(path, name + '.so')
else:
raise PytransformError('Platform %s not supported' % plat)
if platid is not None and os.path.isfile(platid):
filename = platid
elif platid is not None or not os.path.exists(filename) or not is_runtime:
libpath = platid if platid is not None and os.path.isabs(platid) else os.path.join(path, plat_path, format_platform(platid))
filename = os.path.join(libpath, os.path.basename(filename))
if not os.path.exists(filename):
raise PytransformError('Could not find "%s"' % filename)
try:
m = cdll.LoadLibrary(filename)
except Exception as e:
if sys.flags.debug:
print 'Load %s failed:\n%s' % (filename, e)
raise
if not os.path.abspath('.') == os.path.abspath(path):
m.set_option(1, path.encode() if sys.version_info[0] == 3 else path)
m.set_option(2, sys.byteorder.encode())
if sys.flags.debug:
m.set_option(3, c_char_p(1))
m.set_option(4, c_char_p(not is_runtime))
m.set_option(5, c_char_p(not advanced))
if suffix:
m.set_option(6, suffix.encode())
return m
def pyarmor_init(path=None, is_runtime=0, platid=None, suffix='', advanced=0):
global _pytransform
_pytransform = _load_library(path, is_runtime, platid, suffix, advanced)
return init_pytransform()
def pyarmor_runtime(path=None, suffix='', advanced=0):
try:
pyarmor_init(path, is_runtime=1, suffix=suffix, advanced=advanced)
init_runtime()
except Exception as e:
if sys.flags.debug or hasattr(sys, '_catch_pyarmor'):
raise
sys.stderr.write('%s\n' % str(e))
sys.exit(1)
def generate_capsule(licfile):
prikey, pubkey, prolic = _generate_project_capsule()
capkey, newkey = _generate_pytransform_key(licfile, pubkey)
return (prikey, pubkey, capkey, newkey, prolic)
@dllmethod
def _generate_project_capsule():
prototype = PYFUNCTYPE(py_object)
dlfunc = prototype(('generate_project_capsule', _pytransform))
return dlfunc()
@dllmethod
def _generate_pytransform_key(licfile, pubkey):
prototype = PYFUNCTYPE(py_object, c_char_p, py_object)
dlfunc = prototype(('generate_pytransform_key', _pytransform))
return dlfunc(licfile.encode() if sys.version_info[0] == 3 else licfile, pubkey)
@dllmethod
def encrypt_project_files(proname, filelist, mode=0):
prototype = PYFUNCTYPE(c_int, c_char_p, py_object, c_int)
dlfunc = prototype(('encrypt_project_files', _pytransform))
return dlfunc(proname.encode(), filelist, mode)
def generate_project_capsule(licfile):
prikey, pubkey, prolic = _generate_project_capsule()
capkey = _encode_capsule_key_file(licfile)
return (prikey, pubkey, capkey, prolic)
@dllmethod
def _encode_capsule_key_file(licfile):
prototype = PYFUNCTYPE(py_object, c_char_p, c_char_p)
dlfunc = prototype(('encode_capsule_key_file', _pytransform))
return dlfunc(licfile.encode(), None)
@dllmethod
def encrypt_files(key, filelist, mode=0):
t_key = c_char * 32
prototype = PYFUNCTYPE(c_int, t_key, py_object, c_int)
dlfunc = prototype(('encrypt_files', _pytransform))
return dlfunc(t_key(*key), filelist, mode)
@dllmethod
def generate_module_key(pubname, key):
t_key = c_char * 32
prototype = PYFUNCTYPE(py_object, c_char_p, t_key, c_char_p)
dlfunc = prototype(('generate_module_key', _pytransform))
return dlfunc(pubname.encode(), t_key(*key), None)
@dllmethod
def old_init_runtime(systrace=0, sysprofile=1, threadtrace=0, threadprofile=1):
"""Only for old version, before PyArmor 3"""
pyarmor_init(is_runtime=1)
prototype = PYFUNCTYPE(c_int, c_int, c_int, c_int, c_int)
_init_runtime = prototype(('init_runtime', _pytransform))
return _init_runtime(systrace, sysprofile, threadtrace, threadprofile)
@dllmethod
def import_module(modname, filename):
"""Only for old version, before PyArmor 3"""
prototype = PYFUNCTYPE(py_object, c_char_p, c_char_p)
_import_module = prototype(('import_module', _pytransform))
return _import_module(modname.encode(), filename.encode())
@dllmethod
def exec_file(filename):
"""Only for old version, before PyArmor 3"""
prototype = PYFUNCTYPE(c_int, c_char_p)
_exec_file = prototype(('exec_file', _pytransform))
return _exec_file(filename.encode())
其中pyarmor_runtime()函数是PyArmor的运行时代码,但还是不明白它是按照什么加密的,所以我就用这个程序一直测,写txt文件,给它加密,然后写了个脚本用来查看bin文件
先看看怎么用的
写txt文件
得到bin文件来查看
with open("X:/XS/2.bin", 'rb') as f:
data = f.read()
for i, b in enumerate(data):
if i % 16 == 0:
print(f'\n{i:04x}: ', end='')
print(f'{b:02x} ', end='')
得到
用不同的字符测试几次,才发现这是哈夫曼编码
解码
from pathlib import Path
def h(a: str, b: str):
#哈夫曼解码
d = Path(a).read_bytes()
l = d[1]
lens = d[2:2 + l]
c = {}
p = 2 + l
x = 0
for i, n in enumerate(lens):
for _ in range(n):
code = bin(x)[2:].rjust(i + 1, '0')
c[code] = d[p]
p += 1
x += 1
x <<= 1
bits = bin(int.from_bytes(d[p:], 'big'))[2:].zfill(len(d[p:]) * 8)
r = bytearray()
i = 0
while i < len(bits):
for j in range(1, l + 1):
t = bits[i:i + j]
if t in c:
r.append(c[t])
i += j
break
Path(b).write_bytes(bytes(r))
if __name__ == "__main__":
h("X:/XS/output.bin", "out.bin")
得到了解码后的bin文件,查看一下数据结构
解密前:
解密后:
发现是WAV文件,改一下后缀,然后用工具分析一下
类似于0101这种的,对WAV进行解码改为二进制格式的采点取64(32有点小了,这里可以都试试)
from ctypes import c_short
from pathlib import Path
def w(a: str, b: str):
d = Path(a).read_bytes()
s = [c_short(int.from_bytes(d[i:i + 2], 'little')).value
for i in range(44, len(d), 2)]
k = [sum(abs(x) for x in s[i:i + 64]) // 64
for i in range(0, len(s), 64)]
r = ''.join('1' if x > 5000 else '0' for x in k)
Path(b).write_text(r)
if __name__ == "__main__":
w("out.wav", "out.bin")
得到out.bin文件
到这一步只要把01换成1,同时10换成0,就可解码出PNG文件(至于为什么,因为出题人脑洞大)
from pathlib import Path
def b(a: str, b: str):
d = Path(a).read_text().strip()
r = ""
i = 0
while i < len(d) - 1:
if d[i:i + 2] == "01":
r += "1"
i += 2
elif d[i:i + 2] == "10":
r += "0"
i += 2
else:
r += d[i]
i += 1
if i < len(d):
r += d[i]
if len(r) % 8:
r = r.ljust((len(r) + 7) // 8 * 8, '0')
Path(b).write_bytes(bytes(int(r[i:i + 8], 2) for i in range(0, len(r), 8)))
if __name__ == "__main__":
b("out.bin", "out.png")
得到一个二维码扫码即可得到flag