CrackMe

MFC的逆向分析CracckMe,写题的时候在网上搜到了逆向分析CrackMe 这篇文章,通过OD+IDA的方式定位到关键代码处,然后进行解密的操作。

知识点: MD5 Sha1 Hashcat Wincrpt

定位关键函数

OD来定位关键代码和提取数据,因为这个程序有反调试,而OD之中有插件,能够很好的反反调试而提取相关的数据。

IDA能够很好的反编译相应的代码,搞清楚函数的逻辑。

搜索MessageBoxA,因为当输入flag和key之后,输入错误会弹出弹窗,而MessageBoxA 有可能是表示弹窗的函数位置

image-20220426200327902

(Ctrl+G)跟踪表达式【TextA/W】 就找到引用这个函数的地址,看到这个地址之后,发现它的下方就是“Wrong!!!“字符串,就找到对应输出Wrong的这个函数的位置,

image-20220423131904713

定位到输出wrong!!!的代码处

image-20220423132053957

并且在它的下面就是输出successful的代码处

image-20220423132133014

在ida之中能更加容易的分析代码,所以通过这两个输出wrong和Success的地址,在ida之中定位到这两个函数,并且通过他们向上就找(交叉引用的方法)到对输入的key和flag进行验证的函数。

关键函数的位置:0x4C31E0

image-20220423140534625

分析关键函数逻辑

这里将key分成了两部分进行加密,前4个字节使用MD5加密,后4个字节使用Sha1加密,key的md5加密的结果会作为AES加密的密钥,flag的加密是AES加密

image-20220426201914088

4个加密函数

这里有4个加密的函数,在每个函数的里面都是调用Wincrypt的函数来进行加密的操作,但当时我做的时候完全没有注意到这个点,都是通过加密之后的长度来判断用的是什么加密(所以把key解密出来了),不然也不至于最后一步没有解出来。

所以自己以后对于这种情况,要查明这里面调用的函数中引用的参数表示什么意思,有什么作用!!!!!

image-20220425204459582

Algid 的数值:将输入的8个字节的key分成两个部分,前4个字节用md5加密,后4个字节用Sha1加密

image-20220425205856887

image-20220425205920813

所以第一个是对输入的flag进行md5的hash加密

image-20220423135349916

提取加密结果的数据(OD)

加密结果的对比的函数

image-20220423175915409

三个对比加密数据的汇编代码处

image-20220423142415880

image-20220423142438993

image-20220423142505141

通过ida之中三个对输入的加密结果判断的函数的地址,在od之中对应的地址下断点,动调调试找到对比的数据(密文)

md5解密

第一组(md5) =》 找个在线网址解密

数据地址009AF460

image-20220423143845071

密文:

1
2
009AF460  9F 77 C2 A4 AC 5C 0A 67  焪陇琝.g
009AF468 13 21 BB E1 E9 97 2A F6 !会闂*?

密文:9F77C2A4AC5C0A671321BBE1E9972AF6 明文:NocT

image-20220426202804265

SHA1解密

第二组(SHA1)=》(使用HashCat解密) Hashcat使用手册

长度是:20

image-20220423155222571

密文:

1
2
3
010FF59C  D5 9F 8E 94 B0 E1 DE 6E  諢帞搬辬
010FF5A4 32 95 18 A0 C4 44 AA 94 2?犇D獢
010FF5AC DE 7C 8D 44 迀岲..

密文:d59f8e94b0e1de6e329518a0c444aa94de7c8d44 明文:uRne

所以key的值就是: NocTuRne

image-20220423165125938

最后得到的key: NocTuRne

Wincrpt解密AES

第三组(AES)

长度是 0x30

image-20220423165627288

密文:

1
2
3
4
5
6
003AFC20  5B 9C EE B2 3B B7 D7 34  [滎?纷4
003AFC28 F3 1B 75 14 C6 B2 1F E8 ?u撇?
003AFC30 DE 33 44 74 75 1B 47 6A ?DtuGj
003AFC38 D4 37 51 88 FC 67 E6 60 ?Q堻g鎌
003AFC40 DA 0D 58 07 81 43 53 EA ?X丆S?
003AFC48 7B 52 85 6C 86 65 AF B4 {R卨唀
1
0x5B,0x9C,0xEE,0xB2,0x3B,0xB7,0xD7,0x34,0xF3,0x1B,0x75,0x14,0xC6,0xB2,0x1F,0xE8,0xDE,0x33,0x44,0x74,0x75,0x1B,0x47,0x6A,0xD4,0x37,0x51,0x88,0xFC,0x67,0xE6,0x60,0xDA,0x0D,0x58,0x07,0x81,0x43,0x53,0xEA,0x7B,0x52,0x85,0x6C,0x86,0x65,0xAF,0xB4

AES的密钥:

1
0x5c,0x53,0xa4,0xa4,0x1d,0x52,0x43,0x7a,0x9f,0xa1,0xe9,0xc2,0x6c,0xa5,0x90,0x90

我实在是没有想到啊,最后这一步居然是AES解密

Wincrpt

最后一步直接使用Wincrpt中的CryptEncrypt对应的CryptDecrypt这个函数来进行解密

image-20220426133237250

CryptDecrypt的讲解

在调用CryptEncrypt之前,应用程序必须通过调用CryptCreateHash函数获取散列对象的 句柄。加密完成后,可以使用CryptGetHashParam函数获取哈希值,也可以使用 CryptSignHash函数对哈希进行签名 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BOOL CryptEncrypt(
[in] HCRYPTKEY hKey,
[in] HCRYPTHASH hHash,
[in] BOOL Final,
[in] DWORD dwFlags,
[in, out] BYTE *pbData,
[in, out] DWORD *pdwDataLen,
[in] DWORD dwBufLen
);
[in] hKey: 用于解密的密钥句柄。
[in] hHash: 哈希对象的句柄。如果要同时解密和散列数据,则在此参数中传递散列对象的句柄。散列值用解密后的明文更新。此选项在同时解密 和验证签名时很有用。如果不进行散列,则此参数必须为零。
[in] Final: 一个布尔值,指定这是否是正在解密的系列中的最后一部分。如果这是最后一个或唯一一个块,则此值为TRUE 。如果这不是最后一 个块,则此值为FALSE。
[in] dwFlags: 标志值
[in, out] pbData: 指向包含要加密的明文的缓冲区的指针。此缓冲区中的明文被此函数创建的密文覆盖。
[in, out] pdwDataLen: 一个指向DWORD值的指针,该值在输入时包含pbData缓冲区中明文的长度(以字节为单位)。退出时,此DWORD包含 写入pbData缓冲区的密文的长度(以字节为单位)。
[in] dwBufLen: 指定输入pbData缓冲区的总大小(以字节为单位) 。

CryptDecrypt的讲解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
BOOL CryptDecrypt(
[in] HCRYPTKEY hKey,
[in] HCRYPTHASH hHash,
[in] BOOL Final,
[in] DWORD dwFlags,
[in, out] BYTE *pbData,
[in, out] DWORD *pdwDataLen
);
hkey: 用于解密的密钥句柄,此密钥指定要使用的解密算法
[in] hHash: 哈希对象的句柄。如果要同时解密和散列数据,则在此参数中传递散列对象的句柄。散列值用解密后的明文更新。此选项在同时解密 和验证签名时很有用。如果不进行散列,则此参数必须为零。
[in] Final: 一个布尔值,指定这是否是正在解密的系列中的最后一部分。如果这是最后一个或唯一一个块,则此值为TRUE 。如果这不是最后一 个块,则此值为FALSE。
[in] dwFlags: 标志值(见下图)
[in, out] pbData: 指向包含要解密的数据的缓冲区的指针。解密完成后,明文被放回同一个缓冲区。
[in, out] pdwDataLen: 指向指示pbData缓冲区长度的DWORD值的指针。在调用此函数之前,调用应用程序将DWORD值设置为要解密的字节 数。返回时,DWORD值包含解密后的明文的字节数。
image-20220426132654399

解密脚本

这里调用了CryptEncrypt进行AES的加密的操作,所以这里我们直接使用CryptDecrypt来解密就可以了,可以直接从网上去找用它来解密的脚本,也可以直接把加密的函数脱出来,将里面CryptEncrypt函数修改成CryptDecrypt函数就可以了(两个脚本的逻辑都是一样的)

第一种脚本:

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
#include <tchar.h>
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#include <conio.h>
#include <iostream>

using namespace std;


int main(void)
{
BOOL v6; // [esp+4h] [ebp-18h]
HCRYPTKEY phKey; // [esp+Ch] [ebp-10h] BYREF
HCRYPTPROV phProv; // [esp+10h] [ebp-Ch] BYREF
HCRYPTHASH phHash; // [esp+14h] [ebp-8h] BYREF

phProv = 0;
phHash = 0;
phKey = 0;
BYTE a3[] = {0x5B,0x9C,0xEE,0xB2,0x3B,0xB7,0xD7,0x34,0xF3,0x1B,0x75,0x14,0xC6,0xB2,0x1F,0xE8,0xDE,0x33,0x44,0x74,0x75,0x1B,0x47,0x6A,0xD4,0x37,0x51,0x88,0xFC,0x67,0xE6,0x60,0xDA,0x0D,0x58,0x07,0x81,0x43,0x53,0xEA,0x7B,0x52,0x85,0x6C,0x86,0x65,0xAF,0xB4};
BYTE pbData[] ={0x5c,0x53,0xa4,0xa4,0x1d,0x52,0x43,0x7a,0x9f,0xa1,0xe9,0xc2,0x6c,0xa5,0x90,0x90};
DWORD dwDataLen = 0x10;
DWORD *pdwDataLen ;
*pdwDataLen = 0x20;
v6 = CryptAcquireContextA(&phProv, 0, 0, 0x18u, 0xF0000000);
if ( v6 )
{
v6 = CryptCreateHash(phProv, 0x8003u, 0, 0, &phHash);
if ( v6 )
{
v6 = CryptHashData(phHash, pbData, dwDataLen, 0);
if ( v6 )
{
v6 = CryptDeriveKey(phProv, 0x660Eu, phHash, 1u, &phKey);
if ( v6 )
v6 = CryptDecrypt(phKey, 0, 1, 0, a3, pdwDataLen);
}
}
}
cout<<a3<<endl;
if ( phKey )
CryptDestroyKey(phKey);
if ( phHash )
CryptDestroyHash(phHash);
if ( phProv )
CryptReleaseContext(phProv, 0);
return 0;
}

第二种脚本:

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
#include <windows.h>
#include <windef.h>
#include <wincrypt.h>
#include<stdio.h>
#include <iostream>

using namespace std;

BOOL AesDecrypt()
{
BOOL bRet = TRUE;
HCRYPTPROV hCryptProv = NULL;
HCRYPTHASH hCryptHash = NULL;
HCRYPTKEY hCryptKey = NULL;

BYTE pPassword[] = { 0x5c,0x53,0xa4,0xa4,0x1d,0x52,0x43,0x7a,0x9f,0xa1,0xe9,0xc2,0x6c,0xa5,0x90,0x90 };
DWORD dwPasswordLength = 0x10;
BYTE pData[] = { 0x5B, 0x9C, 0xEE, 0xB2, 0x3B, 0xB7, 0xD7, 0x34, 0xF3, 0x1B, 0x75, 0x14, 0xC6, 0xB2, 0x1F, 0xE8, 0xDE, 0x33, 0x44, 0x74, 0x75, 0x1B, 0x47, 0x6A, 0xD4, 0x37, 0x51, 0x88, 0xFC, 0x67, 0xE6, 0x60, 0xDA, 0x0D, 0x58, 0x07, 0x81, 0x43, 0x53, 0xEA, 0x7B, 0x52, 0x85, 0x6C, 0x86, 0x65, 0xAF, 0xB4 };
DWORD dwDataLength = 0x20;
DWORD dwBufferLength = 0x108;
do
{
// 获取CSP句柄
bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
if (FALSE == bRet)
{

break;
}

// 创建HASH对象
bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash);
if (FALSE == bRet)
{

break;
}

// 对密钥进行HASH计算
bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0);
if (FALSE == bRet)
{

break;
}

// 使用HASH来生成密钥
bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey);
if (FALSE == bRet)
{

break;
}

// 解密数据
bRet = ::CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength);
if (FALSE == bRet)
{

break;
}

} while (FALSE);
cout<<pData<<endl;

// 关闭释放
if (hCryptKey)
{
::CryptDestroyKey(hCryptKey);
}
if (hCryptHash)
{
::CryptDestroyHash(hCryptHash);
}
if (hCryptProv)
{
::CryptReleaseContext(hCryptProv, 0);
}

return bRet;
}


int main()
{
AesDecrypt();
return 0;

}

最后解得的结果是:

image-20220426195628830

最后的flag :DASCTF{H@sh_a^d_Aes_6y_W1nCrypt}

fakePica

先用PKID查壳,太久没做APK的题目,当时做的时候都忘了进行查壳了,以后一定要注意,不要直接就jeb里面拖

可以看到这是一个梆梆加固

image-20220426205532076

分析APK加固的方法

这里讲述的是通过反编译代码分析是梆梆加固的过程

先用jeb打开文件,在文件的主函数之中并没有找到MainActivity,但是发现引入了有关AppAplication的包,就可以猜测加了壳

再用APKTool得到程序的相关的文件,查看lib文件

image-20220426210811471

这两个so文件就是梆梆加固的so文件(通过下面lib文件和对应的加固方法的字典就能知道),可以用python写一个脚本来辨认

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
markNameMap.put("libchaosvmp.so", "娜迦");
markNameMap.put("libddog.so", "娜迦");
markNameMap.put("libfdog.so", "娜迦");
markNameMap.put("libedog.so", "娜迦企业版");
markNameMap.put("libexec.so", "爱加密");
markNameMap.put("libexecmain.so", "爱加密");
markNameMap.put("ijiami.dat", "爱加密");
markNameMap.put("ijiami.ajm", "爱加密企业版");
markNameMap.put("libsecexe.so", "梆梆免费版");
markNameMap.put("libsecmain.so", "梆梆免费版");
markNameMap.put("libSecShell.so", "梆梆免费版");
markNameMap.put("libDexHelper.so", "梆梆企业版");
markNameMap.put("libDexHelper-x86.so", "梆梆企业版");
markNameMap.put("libprotectClass.so", "360");
markNameMap.put("libjiagu.so", "360");
markNameMap.put("libjiagu_art.so", "360");
markNameMap.put("libjiagu_x86.so", "360");
markNameMap.put("libegis.so", "通付盾");
markNameMap.put("libNSaferOnly.so", "通付盾");
markNameMap.put("libnqshield.so", "网秦");
markNameMap.put("libbaiduprotect.so", "百度");
markNameMap.put("aliprotect.dat", "阿里聚安全");
markNameMap.put("libsgmain.so", "阿里聚安全");
markNameMap.put("libsgsecuritybody.so", "阿里聚安全");
markNameMap.put("libmobisec.so", "阿里聚安全");
markNameMap.put("libtup.so", "腾讯");
markNameMap.put("libexec.so", "腾讯");
markNameMap.put("libshell.so", "腾讯");
markNameMap.put("mix.dex", "腾讯");
markNameMap.put("lib/armeabi/mix.dex", "腾讯");
markNameMap.put("lib/armeabi/mixz.dex", "腾讯");
markNameMap.put("libtosprotection.armeabi.so", "腾讯御安全");
markNameMap.put("libtosprotection.armeabi-v7a.so", "腾讯御安全");
markNameMap.put("libtosprotection.x86.so", "腾讯御安全");
markNameMap.put("libnesec.so", "网易易盾");
markNameMap.put("libAPKProtect.so", "APKProtect");
markNameMap.put("libkwscmm.so", "几维安全");
markNameMap.put("libkwscr.so", "几维安全");
markNameMap.put("libkwslinker.so", "几维安全");
markNameMap.put("libx3g.so", "顶像科技");
markNameMap.put("libapssec.so", "盛大");
markNameMap.put("librsprotect.so", "瑞星");

blackdex-APK脱壳

使用blackdex软件进行脱壳

判断32位还是64位的APK文件

首先判断一个APK是32位还是64位的

32位和64的架构

image-20220426213154347

查看lib文件夹

这里是armeabi-v7a 就说明是32位的

image-20220426213235800

所以运行32位的blackdex(将对应位数的blackdex的apk文件放入模拟器之中),将需要脱壳的apk文件也拖入模拟器之中,运行脱壳的Apk文件,在这个程序之中选中需要脱壳的这个软件,就可以直接脱壳了,它脱壳完成之后将脱壳得到的dex文件存入下面这个文件目录之中

image-20220426213832469

在虚拟器的文件管理之中打开该文件夹,并且选中该文件夹(这个文件夹之中有4个dex文件),再打开共享文件夹,在共享文件夹的地方选中图中选项

image-20220426215804244

共享文件打开的地方:(高级功能之中能够对电脑端的共享文件夹进行重新的设置)

这样达到的效果就是电脑端的该文件夹和模拟器端的该文件夹是同步的。

image-20220426215837100

image-20220426215849767

分析脱壳得到的dex文件

将这些dex文件拖进jeb之中分析:

dex文件:

  • dex文件是Android系统的可执行文件,包含应用程序的全部操作指令以及运行时数据。

  • 当java程序编译成class后,还需要使用dex工具将所有的class文件整合到一个dex文件,目的是其中各个类能够共享数据,在一定程度上降低了冗余,同时也是文件结构更加经凑,dex文件是传统jar文件大小的50%左右。

  • dex文件的作用是记录整个工程(通常是一个Android工程)的所有类文件的信息。

逐个打开每个dex文件,在cookie_8836564.dex文件之中找到MainActivity函数

AES解密

打开这个mainActivity这个类就能很直观的看到用的是AES加密,并且是直接调用的Java的接口,所以就是标准的AES解密

image-20220427092404661

AES的 key = “picapicapicapica”

AES的 IV向量 IV = “0102030405060708”

第一组的密文

1
2
3
4
5
6
7
8
-114, 0x5F, -37, 0x7F, -110, 0x71, 41, 74, 40, 73, 19, 0x7C, -57, -88, 39, -116, -16, -75, -3, -45, -73, -6, -104, -6, -78, 0x79, 110, 74, -90, -47, -28, -28

0x8e,0x5f,0xdb,0x7f,0x92,0x71,0x29,0x4a,0x28,0x49,0x13,0x7c,0xc7,0xa8,0x27,0x8c,0xf0,0xb5,0xfd,0xd3,0xb7,0xfa,0x98,0xfa,0xb2,0x79,0x6e,0x4a,0xa6,0xd1,0xe4,0xe4

8e5fdb7f9271294a2849137cc7a8278cf0b5fdd3b7fa98fab2796e4aa6d1e4e4

明文: picacomic@gmail.com

image-20220427003406872

第二组的密文:

1
2
3
4
5
-40, 26, 0x5F, -49, -40, -123, 72, -90, -100, -41, 0x7A, -4, 25, -101, -58, 0x74

0xd8,0x1a,0x5f,0xcf,0xd8,0x85,0x48,0xa6,0x9c,0xd7,0x7a,0xfc,0x19,0x9b,0xc6,0x74

明文: picacomic

image-20220427003510166

image-20220427003744579

image-20220427003747472

flag{picacomic@gmail.compicacomic}

奇怪的交易

当时用ida打开这个文件的时候就发现这个文件里面怎么提到了python的东西,当时还以为是在c语言之中运行python文件,结果发现是用python来打包elf文件,自己平时知识点的积累还是太少了

Lu1u师傅

使用pyinstxtractor解包就得到了对应的pyc文件

image-20220427135100720

pyc文件以及其中的pyz文件解包

这个地方用pyinstxtractor解包源文件的时候要使用python3.10的环境运行这个pyinstxtractor.py文件。这样pyz文件解包的文件才能够在PYZ-00.pyz_extracted文件夹之中获得

安装和使用Miniconda3 这个软件的作用是用来管理环境的(这里用来在本机上配置python3.10环境)

下载的地址

安装和卸载的地址

使用软件

如何判断这个文件打包的时候使用的是python版本的是3.10的呢?

答:随便使用一个版本的pyinstxtractor来解包文件,通过它的依赖文件就可以进行判断

image-20220502181556493

这道题目的python版本是3.10,它的magic的num可以通过struct.pyc文件的前4个字节获得,或者通过PYZ-00.pyz文件的5到8字节数据能够得到

image-20220502181645404

用这个magic number将pyc文件补全(因为在打包文件的时候常常会将pyc文件的magic number去掉,我们在恢复的时候需要自己去补全,补全之后就能够进行反编译了,但是这里反编译因为是python3.10,所以不能使用python-uncompyle6得到,要使用pycdc来进行反编译

对奇怪的交易.pyc 文件头4个字节进行重新设置

python3.10版本的pyc文件的文件头是

image-20220427173936449

设置完之后,用在线pyc反编译软件可以反编译,也可以使用pycdc进行反编译 ,再用pycdas得到字节码

对照着字节码对下面反编译的结果进行修复

  • RSA低指数加密

  • 从cpu包中引入函数encrypt()加密

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
# Source Generated with Decompyle++
# File: 奇怪的交易.pyc (Python 3.10)

from cup import *
if __name__ == '__main__':
flag = input('\xe8\xaf\xb7\xe8\xbe\x93\xe5\x85\xa5flag')
pub_key = [ 0x649EE967E7916A825CC9FD3320BEABF263BEAC68C080F52824A0F521EDB6B78577EC52BF1C9E78F4BB71192F9A23F1A17AA76E5979E4D953329D3CA65FB4A71DA57412B59DFD6AEDF0191C5555D3E5F582B81B5E6B23163E9889204A81AFFDF119FE25C92F4ED59BD3285BCD7AAE14824240D2E33C5A97848F4EB7AAC203DE6330D2B4D8FF61691544FBECD120F99A157B3D2F58FA51B2887A9D06CA383C44D071314A12B17928B96F03A06E959A5AFEFA0183664F52CD32B9FC72A04B45913FCB2D5D2D3A415A14F611CF1EAC2D6C785142A8E9CC41B67A6CD85001B06EDB8CA767D367E56E0AE651491BF8A8C17A38A1835DB9E4A9292B1D86D5776C98CC25L,
0x647327833ACFEF1F9C83E74E171FC300FA347D4A6769476C33DA82C95120ACB38B62B33D429206FE6E9BB0BB7AB748A1036971BEA36EC47130B749C1C9FF6FE03D0F7D9FC5346EB0E575BDFA6C530AA57CD676894FC080D2DD049AB59625F4B9C78BCFD95CDCD2793E440E26E189D251121CB6EB177FEDB596409034E8B0C5BBD9BD9342235DBB226C9170EFE347FF0FD2CFF9A1F7B647CC83E4D8F005FD7125A89251C768AFE70BDD54B88116814D5030F499BCAC4673CCCC342FB4B6AC58EA5A64546DC25912B6C430529F6A7F449FD96536DE269D1A1B015A4AC6B6E46EE19DCE8143726A6503E290E4BAE6BD78319B5878981F6CFFDB3B818209341FD68BL]
m = libnum.s2n(flag)
c = str(pow(m, pub_key[1], pub_key[0])) // RSA的公钥 pub_key[1] 模的对象pub_key[0]
array0 = []
array1 = [0xd28ed952,0x57c844df,0xd91ba938,0xf9f3bd2d,0x8ef8e43d,0x24d0a6d4,0x57e35037,0x57b9a2ea,0x3c5f16c0,0xd7821910,0x3b3d098c,0x1a22518d,0xc83555b7,0xe8dff468,0xbdbdffd,0xc5b84feb,0xd9f837c6,0x248c0bef,0x8efa4edd,0x5cc7851,0x8b4b608c,0x57b785f2,0xc0b62792,0x22c8fc3e,0xaab1c22d,0xbdb9c266,0x528335db,0xae9f9816,0xd1f40b3c,0x8206ddc3,0xc4e0badc,0xe407bd26,0x8ae5685,0x8016c6a5,0xaf4ab9d3,0x1e35204a,0x3b483e49,0x85082a0b,0xca0bc95a,0xa7be567c,0x41eb42c8,0x6aad143c,0xdfefb591,0x93346b38,0x4547158e,0x289465d1,0xaee1a7a2,0x80e574ae,0xf154f55f,0x7e755cdc,0xfcbda653,0x8e902444,0xca742e12,0xb8424071,0xb4b15ec2,0x943bfa09,0xbc97cd93,0x4ca0c180,0x2f9e8e58,0x8b58328f,0xf9822360,0xd1fd15ee,0x40398f89,0x559e65ca,0xa2d6c17e,0x59d616ed,0x1dd8eeb5,0xbd8c105,0x8e014807,0x347aaa04,0x7573b596,0xbfc6ee25,0x4540463f,0x2168859c,0x6a405498,0x9d93febe,0x4c9a89ad,0x2f041932,0x47454f1e,0xf2174a07,0x62a2dc3,0x952bfe83,0xf730ac4c,0x24cf49f1,0x3a4e5490,0x6a2f4289,0x8379d23a,0xead737ee,0xe41555fb,0x27500d24,0x99f3b244,0x5d1878b8,0x842c31a4,0x46e33b42,0xa14f3e4,0xa5ce044c,0x4ee902b9,0x31276ee4,0x1854ae7c,0xe0178482,0xf412bbbc,0x59f173e1,0x9ae5225,0xde740b00,0xb7cb64fd,0xebcadb1f,0x8eae2326,0x933c216c,0xd7d1f649,0x1cb99f66,0xa448ac16,0xbc082807,0x4b2a6481,0x7afa8587,0x8474a61d,0x60c272b,0xbc5654d1,0x669749b9,0x40470389,0xa8546b9,0xd3c5280f,0x5d597033,0x73920388,0x595d57ad,0xc59872f8,0x317471ac,0xe51502a2,0x38cc9816,0x4c81d679,0x79e45563,0xebe6a798,0xe09575cd,0xaddf4157,0xc4770191,0x1cbf464d,0x675e4574,0xdac71054,0x99807e43,0xa88d74b1,0xcb77e028,0x5b67a7bb,0xeeebc3b6,0xe7e680e5,0x10450af8,0x12eccbeb,0xc4b10cdc,0x91776399,0x1a32a98,0x63576ed2,0x6796abb9,0x2cc32a20,0x8e364d8f,0xa0985a77,0x218d8f16,0x837d6dc3]
i = 0
// 我觉得按常理来说是将数据的每四个字节为一组,
while i < len(c):
tem = 0
for ii in c[i:i+4]:
tem = (tem<<8)+ord(ii) //将输入的元素的4个数按照大序端的方式排成一个数
array0.append(tem) // 每四个字节的数据一起加密
i += 4
if not i < len(c): // 判断字节的长度
temArray = [ 54,54,54,54]
strLen = len(array0)
res = encrypt(strLen, array0, temArray)
if array0 == array1:
print('You are right!')
input('')
quit()
else:
print('Why not drink a cup of tea and have a rest?')
continue
array0是输入的数据
strLen是输入数据的长度
temArray应该是密钥
array1是密文

但是因为这个反编译的代码不太正确,所以使用pycdas得到它的字节码: 字节码

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
[Code]
File Name: 奇怪的交易.py
Object Name: <module>
Arg Count: 0
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 0
Stack Size: 6
Flags: 0x00000040 (CO_NOFREE)
[Names]
'cup'
'__name__'
'input'
'flag'
'pub_key'
'libnum'
's2n'
'm'
'str'
'pow'
'c'
'\xe1\x98\xa1'
'\xe1\x98\x99'
'i'
'len'
'\xe1\x98\x9e'
'ii'
'ord'
'append'
'\xe1\x98\x9d'
'\xe1\x98\xa0'
'encrypt'
'res'
'print'
'quit'
[Var Names]
[Free Vars]
[Cell Vars]
[Constants]
0
(
'*'
)
'__main__'
True
'\xe8\xaf\xb7\xe8\xbe\x93\xe5\x85\xa5flag'
0x649EE967E7916A825CC9FD3320BEABF263BEAC68C080F52824A0F521EDB6B78577EC52BF1C9E78F4BB71192F9A23F1A17AA76E5979E4D953329D3CA65FB4A71DA57412B59DFD6AEDF0191C5555D3E5F582B81B5E6B23163E9889204A81AFFDF119FE25C92F4ED59BD3285BCD7AAE14824240D2E33C5A97848F4EB7AAC203DE6330D2B4D8FF61691544FBECD120F99A157B3D2F58FA51B2887A9D06CA383C44D071314A12B17928B96F03A06E959A5AFEFA0183664F52CD32B9FC72A04B45913FCB2D5D2D3A415A14F611CF1EAC2D6C785142A8E9CC41B67A6CD85001B06EDB8CA767D367E56E0AE651491BF8A8C17A38A1835DB9E4A9292B1D86D5776C98CC25L
0x647327833ACFEF1F9C83E74E171FC300FA347D4A6769476C33DA82C95120ACB38B62B33D429206FE6E9BB0BB7AB748A1036971BEA36EC47130B749C1C9FF6FE03D0F7D9FC5346EB0E575BDFA6C530AA57CD676894FC080D2DD049AB59625F4B9C78BCFD95CDCD2793E440E26E189D251121CB6EB177FEDB596409034E8B0C5BBD9BD9342235DBB226C9170EFE347FF0FD2CFF9A1F7B647CC83E4D8F005FD7125A89251C768AFE70BDD54B88116814D5030F499BCAC4673CCCC342FB4B6AC58EA5A64546DC25912B6C430529F6A7F449FD96536DE269D1A1B015A4AC6B6E46EE19DCE8143726A6503E290E4BAE6BD78319B5878981F6CFFDB3B818209341FD68BL
1
(
0xd28ed952,0x57c844df,0xd91ba938,0xf9f3bd2d,0x8ef8e43d,0x24d0a6d4,0x57e35037,0x57b9a2ea,0x3c5f16c0,0xd7821910,0x3b3d098c,0x1a22518d,0xc83555b7,0xe8dff468,0xbdbdffd,0xc5b84feb,0xd9f837c6,0x248c0bef,0x8efa4edd,0x5cc7851,0x8b4b608c,0x57b785f2,0xc0b62792,0x22c8fc3e,0xaab1c22d,0xbdb9c266,0x528335db,0xae9f9816,0xd1f40b3c,0x8206ddc3,0xc4e0badc,0xe407bd26,0x8ae5685,0x8016c6a5,0xaf4ab9d3,0x1e35204a,0x3b483e49,0x85082a0b,0xca0bc95a,0xa7be567c,0x41eb42c8,0x6aad143c,0xdfefb591,0x93346b38,0x4547158e,0x289465d1,0xaee1a7a2,0x80e574ae,0xf154f55f,0x7e755cdc,0xfcbda653,0x8e902444,0xca742e12,0xb8424071,0xb4b15ec2,0x943bfa09,0xbc97cd93,0x4ca0c180,0x2f9e8e58,0x8b58328f,0xf9822360,0xd1fd15ee,0x40398f89,0x559e65ca,0xa2d6c17e,0x59d616ed,0x1dd8eeb5,0xbd8c105,0x8e014807,0x347aaa04,0x7573b596,0xbfc6ee25,0x4540463f,0x2168859c,0x6a405498,0x9d93febe,0x4c9a89ad,0x2f041932,0x47454f1e,0xf2174a07,0x62a2dc3,0x952bfe83,0xf730ac4c,0x24cf49f1,0x3a4e5490,0x6a2f4289,0x8379d23a,0xead737ee,0xe41555fb,0x27500d24,0x99f3b244,0x5d1878b8,0x842c31a4,0x46e33b42,0xa14f3e4,0xa5ce044c,0x4ee902b9,0x31276ee4,0x1854ae7c,0xe0178482,0xf412bbbc,0x59f173e1,0x9ae5225,0xde740b00,0xb7cb64fd,0xebcadb1f,0x8eae2326,0x933c216c,0xd7d1f649,0x1cb99f66,0xa448ac16,0xbc082807,0x4b2a6481,0x7afa8587,0x8474a61d,0x60c272b,0xbc5654d1,0x669749b9,0x40470389,0xa8546b9,0xd3c5280f,0x5d597033,0x73920388,0x595d57ad,0xc59872f8,0x317471ac,0xe51502a2,0x38cc9816,0x4c81d679,0x79e45563,0xebe6a798,0xe09575cd,0xaddf4157,0xc4770191,0x1cbf464d,0x675e4574,0xdac71054,0x99807e43,0xa88d74b1,0xcb77e028,0x5b67a7bb,0xeeebc3b6,0xe7e680e5,0x10450af8,0x12eccbeb,0xc4b10cdc,0x91776399,0x1a32a98,0x63576ed2,0x6796abb9,0x2cc32a20,0x8e364d8f,0xa0985a77,0x218d8f16,0x837d6dc3,

)
4
8
(
54
54
54
54
)
'You are right!'
''
'Why not drink a cup of tea and have a rest?'
None
[Disassembly]
0 LOAD_CONST 0: 0
2 LOAD_CONST 1: ('*',)
4 IMPORT_NAME 0: cup
6 IMPORT_STAR
8 LOAD_NAME 1: __name__
10 LOAD_CONST 2: '__main__'
12 COMPARE_OP 2 (==)
14 POP_JUMP_IF_FALSE 119 (to 238)
16 NOP
18 LOAD_NAME 2: input
20 LOAD_CONST 4: '\xe8\xaf\xb7\xe8\xbe\x93\xe5\x85\xa5flag'
22 CALL_FUNCTION 1
24 STORE_NAME 3: flag
26 LOAD_CONST 5: 0x649EE967E7916A825CC9FD3320BEABF263BEAC68C080F52824A0F521EDB6B78577EC52BF1C9E78F4BB71192F9A23F1A17AA76E5979E4D953329D3CA65FB4A71DA57412B59DFD6AEDF0191C5555D3E5F582B81B5E6B23163E9889204A81AFFDF119FE25C92F4ED59BD3285BCD7AAE14824240D2E33C5A97848F4EB7AAC203DE6330D2B4D8FF61691544FBECD120F99A157B3D2F58FA51B2887A9D06CA383C44D071314A12B17928B96F03A06E959A5AFEFA0183664F52CD32B9FC72A04B45913FCB2D5D2D3A415A14F611CF1EAC2D6C785142A8E9CC41B67A6CD85001B06EDB8CA767D367E56E0AE651491BF8A8C17A38A1835DB9E4A9292B1D86D5776C98CC25L
28 LOAD_CONST 6: 0x647327833ACFEF1F9C83E74E171FC300FA347D4A6769476C33DA82C95120ACB38B62B33D429206FE6E9BB0BB7AB748A1036971BEA36EC47130B749C1C9FF6FE03D0F7D9FC5346EB0E575BDFA6C530AA57CD676894FC080D2DD049AB59625F4B9C78BCFD95CDCD2793E440E26E189D251121CB6EB177FEDB596409034E8B0C5BBD9BD9342235DBB226C9170EFE347FF0FD2CFF9A1F7B647CC83E4D8F005FD7125A89251C768AFE70BDD54B88116814D5030F499BCAC4673CCCC342FB4B6AC58EA5A64546DC25912B6C430529F6A7F449FD96536DE269D1A1B015A4AC6B6E46EE19DCE8143726A6503E290E4BAE6BD78319B5878981F6CFFDB3B818209341FD68BL
30 BUILD_LIST 2
32 STORE_NAME 4: pub_key
34 LOAD_NAME 5: libnum
36 LOAD_METHOD 6: s2n
38 LOAD_NAME 3: flag
40 CALL_METHOD 1
42 STORE_NAME 7: m
44 LOAD_NAME 8: str
46 LOAD_NAME 9: pow
48 LOAD_NAME 7: m
50 LOAD_NAME 4: pub_key
52 LOAD_CONST 7: 1
54 BINARY_SUBSCR
56 LOAD_NAME 4: pub_key
58 LOAD_CONST 0: 0
60 BINARY_SUBSCR
62 CALL_FUNCTION 3
64 CALL_FUNCTION 1
66 STORE_NAME 10: c
68 BUILD_LIST 0
70 STORE_NAME 11: store
72 BUILD_LIST 0
74 LOAD_CONST 8: (0xD28ED952L, 1472742623, 0xD91BA938L, 0xF9F3BD2DL, 0x8EF8E43DL, 617653972, 1474514999, 1471783658, 1012864704, 0xD7821910L, 993855884, 438456717, 0xC83555B7L, 0xE8DFF468L, 198959101, 0xC5B84FEBL, 0xD9F837C6L, 613157871, 0x8EFA4EDDL, 97286225, 0x8B4B608CL, 1471645170, 0xC0B62792L, 583597118, 0xAAB1C22DL, 0xBDB9C266L, 1384330715, 0xAE9F9816L, 0xD1F40B3CL, 0x8206DDC3L, 0xC4E0BADCL, 0xE407BD26L, 145643141, 0x8016C6A5L, 0xAF4AB9D3L, 506798154, 994590281, 0x85082A0BL, 0xCA0BC95AL, 0xA7BE567CL, 1105937096, 1789727804, 0xDFEFB591L, 0x93346B38L, 1162286478, 680814033, 0xAEE1A7A2L, 0x80E574AEL, 0xF154F55FL, 2121620700, 0xFCBDA653L, 0x8E902444L, 0xCA742E12L, 0xB8424071L, 0xB4B15EC2L, 0x943BFA09L, 0xBC97CD93L, 1285603712, 798920280, 0x8B58328FL, 0xF9822360L, 0xD1FD15EEL, 1077514121, 1436444106, 0xA2D6C17EL, 1507202797, 500756149, 198754565, 0x8E014807L, 880454148, 1970517398, 0xBFC6EE25L, 1161840191, 560498076, 1782600856, 0x9D93FEBEL, 1285196205, 788797746, 1195724574, 0xF2174A07L, 103427523, 0x952BFE83L, 0xF730AC4CL, 617564657, 978211984, 1781482121, 0x8379D23AL, 0xEAD737EEL, 0xE41555FBL, 659557668, 0x99F3B244L, 1561884856, 0x842C31A4L, 1189296962, 169145316, 0xA5CE044CL, 1323893433, 824667876, 408202876, 0xE0178482L, 0xF412BBBCL, 1508996065, 162419237, 0xDE740B00L, 0xB7CB64FDL, 0xEBCADB1FL, 0x8EAE2326L, 0x933C216CL, 0xD7D1F649L, 481927014, 0xA448AC16L, 0xBC082807L, 1261069441, 2063238535, 0x8474A61DL, 101459755, 0xBC5654D1L, 1721190841, 1078395785, 176506553, 0xD3C5280FL, 1566142515, 1938949000, 1499289517, 0xC59872F8L, 829714860, 0xE51502A2L, 952932374, 1283577465, 2045007203, 0xEBE6A798L, 0xE09575CDL, 0xADDF4157L, 0xC4770191L, 482297421, 1734231412, 0xDAC71054L, 0x99807E43L, 0xA88D74B1L, 0xCB77E028L, 1533519803, 0xEEEBC3B6L, 0xE7E680E5L, 272960248, 317508587, 0xC4B10CDCL, 0x91776399L, 27470488, 1666674386, 1737927609, 750987808, 0x8E364D8FL, 0xA0985A77L, 562925334, 0x837D6DC3L)
76 LIST_EXTEND 1
78 STORE_NAME 12: ᘙ
80 LOAD_CONST 0: 0
82 STORE_NAME 13: i
84 LOAD_NAME 13: i
86 LOAD_NAME 14: len while i<len(c):
88 LOAD_NAME 10: c
90 CALL_FUNCTION 1
92 COMPARE_OP 0 (<)
94 POP_JUMP_IF_FALSE 84 (to 168) // 当不成立的时候就错误的跳转
96 LOAD_CONST 0: 0
98 STORE_NAME 15: index index = 0
100 LOAD_NAME 10: c
102 LOAD_NAME 13: i
104 LOAD_NAME 13: i
106 LOAD_CONST 9: 4
108 BINARY_ADD // i+4
110 BUILD_SLICE 2 c[i:i+4]
112 BINARY_SUBSCR // 读取其中的值
114 GET_ITER // 开始迭代
116 FOR_ITER 10 (to 138)
118 STORE_NAME 16: ii
120 LOAD_NAME 15: index
122 LOAD_CONST 10: 8
124 BINARY_LSHIFT // index<<8
126 LOAD_NAME 17: ord
128 LOAD_NAME 16: ii
130 CALL_FUNCTION 1
132 BINARY_ADD (index<<8)+ord(ii)
134 STORE_NAME 15: index index = (index<<8)+ord(ii)
136 JUMP_ABSOLUTE 58
138 LOAD_NAME 11: store
140 LOAD_METHOD 18: append
142 LOAD_NAME 15: index
144 CALL_METHOD 1 store.append(index)
146 POP_TOP
148 LOAD_NAME 13: i
150 LOAD_CONST 9: 4
152 INPLACE_ADD // i = i+4
154 STORE_NAME 13: i
156 LOAD_NAME 13: i
158 LOAD_NAME 14: len
160 LOAD_NAME 10: c
162 CALL_FUNCTION 1
164 COMPARE_OP 0 (<) i<len(c)
166 POP_JUMP_IF_TRUE 48 (to 96) if not i<len(c):
168 BUILD_LIST 0
170 LOAD_CONST 11: (54, 54, 54, 54)
172 LIST_EXTEND 1
174 STORE_NAME 19: ᘝ
176 LOAD_NAME 14: len
178 LOAD_NAME 11: store
180 CALL_FUNCTION 1
182 STORE_NAME 20: ᘠ
184 LOAD_NAME 21: encrypt
186 LOAD_NAME 20: ᘠ
188 LOAD_NAME 11: store
190 LOAD_NAME 19: ᘝ
192 CALL_FUNCTION 3
194 STORE_NAME 22: res
196 LOAD_NAME 11: store
198 LOAD_NAME 12: ᘙ
200 COMPARE_OP 2 (==)
202 POP_JUMP_IF_FALSE 114 (to 228)
204 LOAD_NAME 23: print
206 LOAD_CONST 12: 'You are right!'
208 CALL_FUNCTION 1
210 POP_TOP
212 LOAD_NAME 2: input
214 LOAD_CONST 13: ''
216 CALL_FUNCTION 1
218 POP_TOP
220 LOAD_NAME 24: quit
222 CALL_FUNCTION 0
224 POP_TOP
226 JUMP_FORWARD 4 (to 236)
228 LOAD_NAME 23: print
230 LOAD_CONST 14: 'Why not drink a cup of tea and have a rest?'
232 CALL_FUNCTION 1
234 POP_TOP
236 JUMP_ABSOLUTE 9
238 LOAD_CONST 15: None
240 RETURN_VALUE

pyz文件解密

送上面修复的反编译的结果之中我们能够看到,这里引用了一个函数,这个函数是重cup这个包中导入的

这个包的获得是在PYZ-00.pyz_extracted文件夹:

一般一个稍微大一点的项目都会分成多个py文件,甚至会依赖其他模块,这些被依赖的文件解析后都会放入PYZ-00.pyz_extracted文件夹之中,这个文件之中相当于放的是核心代码,这个文件之中放入的就是PYZ-00.pyz这个文件解压之后得到的文件,注意这个文件想要用pyinstxtractor.py正常得到,我们需要在这个pyc相对应的python环境之中运行该脚本(上面已经介绍了怎么管理多个python环境)

这里的pyz文件进行了加密,因为pyinstaller可以将文件pyc进行一定的压缩加密,以防止被逆向

pyz文件加密解密

pyz文件加密的密钥和过程的文件

通过pyinstxtractor获得了部分没有加密的pyc文件,其中就有archive.pyc文件(得到加密过程)和crypto_key文件(得到具体key参数)

image-20220504152929032

archive.pyc文件:(反编译)

image-20220504153248511

加密的关键代码

image-20220504153401888

crypto_key文件:(反编译)

1
key = '0000000000000tea'

image-20220504153121767

因为在源加密代码之中加密函数是从cup这个包之中引入的,所以我们在那个解压缩之后得到的文件夹之中找到对应的对应的包的加密文件

image-20220504154026910

pyz文件解密脚本

解密该文件:

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
import tinyaes
import zlib

CRYPT_BLOCK_SIZE = 16

# key obtained from pyimod00_crypto_key
key = bytes('0000000000000tea', 'utf-8')

inf = open('cup.pyc.encrypted', 'rb') # encrypted file input
outf = open('cup.pyc', 'wb') # output file

# Initialization vector
iv = inf.read(CRYPT_BLOCK_SIZE)

cipher = tinyaes.AES(key, iv)

# Decrypt and decompress
plaintext = zlib.decompress(cipher.CTR_xcrypt_buffer(inf.read()))

# Write pyc header
# The header below is for Python 3.8
outf.write(b'\x6f\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0')

# Write decrypted data
outf.write(plaintext)

inf.close()
outf.close()

各个python版本打包的pyc文件的文件头:

1
2
3
4
5
6
7
8
9
10
11
12
Python 2.7: \x03\xf3\x0d\x0a\0\0\0\0
Python 3.0: \x3b\x0c\x0d\x0a\0\0\0\0
Python 3.1: \x4f\x0c\x0d\x0a\0\0\0\0
Python 3.2: \x6c\x0c\x0d\x0a\0\0\0\0
Python 3.3: \x9e\x0c\x0d\x0a\0\0\0\0\0\0\0\0
Python 3.4: \xee\x0c\x0d\x0a\0\0\0\0\0\0\0\0
Python 3.5: \x17\x0d\x0d\x0a\0\0\0\0\0\0\0\0
Python 3.6: \x33\x0d\x0d\x0a\0\0\0\0\0\0\0\0
Python 3.7: \x42\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0
Python 3.8: \x55\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0
Python 3.9: \x61\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0
Python 3.10: \x6f\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0

xxtea解密

得到的加密函数encrypt()的pyc文件,所以对这个文件反编译就能够知道函数逻辑

可以看到这里加密的方法是xxtea,这里反编译得到的代码还是有点问题,但是我们可以明显的知道这个地方用的都是xxtea加密

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
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.10

import libnum
from ctypes import *

def MX(z, y, total, key, p, e):
temp1 = (z.value >> 5 ^ y.value << 2) + (y.value >> 3 ^ z.value << 4)
temp2 = (total.value ^ y.value) + (key[p & 3 ^ e.value] ^ z.value)
return c_uint32(temp1 ^ temp2)


def encrypt(n, v, key):
DELTA = 0x9E3779B9L
rounds = 6 + 52 // n
total = c_uint32(0)
z = c_uint32(v[n - 1])
e = c_uint32(0)
while rounds > 0:
total.value += DELTA
e.value = total.value >> 2 & 3
y = c_uint32(v[0])
v[n - 1] = c_uint32(v[n - 1] + MX(z, y, total, key, n - 1, e).value).value
z.value = v[n - 1]
rounds -= 1
if not rounds > 0:
return v

所以xxtea解密:

因为加密的数据是按照大序端存放的,并且给得到的是经过rsa加密的数字的字符串

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
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9E3779B9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}


int main()
{
uint32_t v[]= {0xd28ed952,0x57c844df,0xd91ba938,0xf9f3bd2d,0x8ef8e43d,0x24d0a6d4,0x57e35037,0x57b9a2ea,0x3c5f16c0,0xd7821910,0x3b3d098c,0x1a22518d,0xc83555b7,0xe8dff468,0xbdbdffd,0xc5b84feb,0xd9f837c6,0x248c0bef,0x8efa4edd,0x5cc7851,0x8b4b608c,0x57b785f2,0xc0b62792,0x22c8fc3e,0xaab1c22d,0xbdb9c266,0x528335db,0xae9f9816,0xd1f40b3c,0x8206ddc3,0xc4e0badc,0xe407bd26,0x8ae5685,0x8016c6a5,0xaf4ab9d3,0x1e35204a,0x3b483e49,0x85082a0b,0xca0bc95a,0xa7be567c,0x41eb42c8,0x6aad143c,0xdfefb591,0x93346b38,0x4547158e,0x289465d1,0xaee1a7a2,0x80e574ae,0xf154f55f,0x7e755cdc,0xfcbda653,0x8e902444,0xca742e12,0xb8424071,0xb4b15ec2,0x943bfa09,0xbc97cd93,0x4ca0c180,0x2f9e8e58,0x8b58328f,0xf9822360,0xd1fd15ee,0x40398f89,0x559e65ca,0xa2d6c17e,0x59d616ed,0x1dd8eeb5,0xbd8c105,0x8e014807,0x347aaa04,0x7573b596,0xbfc6ee25,0x4540463f,0x2168859c,0x6a405498,0x9d93febe,0x4c9a89ad,0x2f041932,0x47454f1e,0xf2174a07,0x62a2dc3,0x952bfe83,0xf730ac4c,0x24cf49f1,0x3a4e5490,0x6a2f4289,0x8379d23a,0xead737ee,0xe41555fb,0x27500d24,0x99f3b244,0x5d1878b8,0x842c31a4,0x46e33b42,0xa14f3e4,0xa5ce044c,0x4ee902b9,0x31276ee4,0x1854ae7c,0xe0178482,0xf412bbbc,0x59f173e1,0x9ae5225,0xde740b00,0xb7cb64fd,0xebcadb1f,0x8eae2326,0x933c216c,0xd7d1f649,0x1cb99f66,0xa448ac16,0xbc082807,0x4b2a6481,0x7afa8587,0x8474a61d,0x60c272b,0xbc5654d1,0x669749b9,0x40470389,0xa8546b9,0xd3c5280f,0x5d597033,0x73920388,0x595d57ad,0xc59872f8,0x317471ac,0xe51502a2,0x38cc9816,0x4c81d679,0x79e45563,0xebe6a798,0xe09575cd,0xaddf4157,0xc4770191,0x1cbf464d,0x675e4574,0xdac71054,0x99807e43,0xa88d74b1,0xcb77e028,0x5b67a7bb,0xeeebc3b6,0xe7e680e5,0x10450af8,0x12eccbeb,0xc4b10cdc,0x91776399,0x1a32a98,0x63576ed2,0x6796abb9,0x2cc32a20,0x8e364d8f,0xa0985a77,0x218d8f16,0x837d6dc3,0x0};
uint32_t const k[4]= {54,54,54,54};
int n= 155; //n的绝对值表示v的长度,取正表示加密,取负表示解密
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
// printf("加密前原始数据:%s\n",(char*)v);
// btea(v, n, k);
// printf("加密后的数据:%u %u %u\n",v[0],v[1],v[3]);
btea(v, -n, k);
// printf("解密后的数据:%s\n",(char*)v);
for (int i=0;i<155;i++)
{
for(int j=0;j<4;j++)
{
printf("%c",((v[i]>>24)&0xff));
v[i] = v[i]<<8; // 将这个数左移8位
}
}
return 0;
}

xxtea解密结果:(RSA的密文)

1
10610336534759505889607399322387179316771488492347274741918862678692508953185876570981227584004676580623553664818853686933004290078153620168054665086468417541382824708104480882577200529822968531743002301934310349005341104696887943182074473298650903541494918266823037984054778903666406545980557074219162536057146090758158128189406073809226361445046225524917089434897957301396534515964547462425719205819342172669899546965221084098690893672595962129879041507903210851706793788311452973769358455761907303633956322972510500253009083922781934406731633755418753858930476576720874219359466503538931371444470303193503733920039

RSA低解密指数攻击

RSA解密,在题目之中提供了n和公钥

素数分解的网站 http://www.factordb.com/index.php(这道题用不上)

1
2
3
e = 0x647327833ACFEF1F9C83E74E171FC300FA347D4A6769476C33DA82C95120ACB38B62B33D429206FE6E9BB0BB7AB748A1036971BEA36EC47130B749C1C9FF6FE03D0F7D9FC5346EB0E575BDFA6C530AA57CD676894FC080D2DD049AB59625F4B9C78BCFD95CDCD2793E440E26E189D251121CB6EB177FEDB596409034E8B0C5BBD9BD9342235DBB226C9170EFE347FF0FD2CFF9A1F7B647CC83E4D8F005FD7125A89251C768AFE70BDD54B88116814D5030F499BCAC4673CCCC342FB4B6AC58EA5A64546DC25912B6C430529F6A7F449FD96536DE269D1A1B015A4AC6B6E46EE19DCE8143726A6503E290E4BAE6BD78319B5878981F6CFFDB3B818209341FD68B
n =
0x649EE967E7916A825CC9FD3320BEABF263BEAC68C080F52824A0F521EDB6B78577EC52BF1C9E78F4BB71192F9A23F1A17AA76E5979E4D953329D3CA65FB4A71DA57412B59DFD6AEDF0191C5555D3E5F582B81B5E6B23163E9889204A81AFFDF119FE25C92F4ED59BD3285BCD7AAE14824240D2E33C5A97848F4EB7AAC203DE6330D2B4D8FF61691544FBECD120F99A157B3D2F58FA51B2887A9D06CA383C44D071314A12B17928B96F03A06E959A5AFEFA0183664F52CD32B9FC72A04B45913FCB2D5D2D3A415A14F611CF1EAC2D6C785142A8E9CC41B67A6CD85001B06EDB8CA767D367E56E0AE651491BF8A8C17A38A1835DB9E4A9292B1D86D5776C98CC25

这里的RSA是低解密指数攻击

在RSA中d也称为解密指数,当d比较小的时候,e也就显得特别大了。
适用情况:e过大或过小(一般e过大时使用)

使用工具:工具rsa-wiener-attack

使用工具rsa-wiener-attack解密

rsa-wiener-attack下载地址

利用这个工具是为了得到d,然后再使用RSA解密运算 pow(c,d,n) 得到最终的密文

RSA解密脚本:

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
from Crypto.PublicKey import RSA
import ContinuedFractions, Arithmetic
from Crypto.Util.number import long_to_bytes

def wiener_hack(e, n):
# firstly git clone https://github.com/pablocelayes/rsa-wiener-attack.git !
frac = ContinuedFractions.rational_to_contfrac(e, n)
convergents = ContinuedFractions.convergents_from_contfrac(frac)
for (k, d) in convergents:
if k != 0 and (e * d - 1) % k == 0:
phi = (e * d - 1) // k
s = n - phi + 1
discr = s * s - 4 * n
if (discr >= 0):
t = Arithmetic.is_perfect_square(discr)
if t != -1 and (s + t) % 2 == 0:
print("First: Hacked d.")
return d
return False
n = 0x649EE967E7916A825CC9FD3320BEABF263BEAC68C080F52824A0F521EDB6B78577EC52BF1C9E78F4BB71192F9A23F1A17AA76E5979E4D953329D3CA65FB4A71DA57412B59DFD6AEDF0191C5555D3E5F582B81B5E6B23163E9889204A81AFFDF119FE25C92F4ED59BD3285BCD7AAE14824240D2E33C5A97848F4EB7AAC203DE6330D2B4D8FF61691544FBECD120F99A157B3D2F58FA51B2887A9D06CA383C44D071314A12B17928B96F03A06E959A5AFEFA0183664F52CD32B9FC72A04B45913FCB2D5D2D3A415A14F611CF1EAC2D6C785142A8E9CC41B67A6CD85001B06EDB8CA767D367E56E0AE651491BF8A8C17A38A1835DB9E4A9292B1D86D5776C98CC25
e = 0x647327833ACFEF1F9C83E74E171FC300FA347D4A6769476C33DA82C95120ACB38B62B33D429206FE6E9BB0BB7AB748A1036971BEA36EC47130B749C1C9FF6FE03D0F7D9FC5346EB0E575BDFA6C530AA57CD676894FC080D2DD049AB59625F4B9C78BCFD95CDCD2793E440E26E189D251121CB6EB177FEDB596409034E8B0C5BBD9BD9342235DBB226C9170EFE347FF0FD2CFF9A1F7B647CC83E4D8F005FD7125A89251C768AFE70BDD54B88116814D5030F499BCAC4673CCCC342FB4B6AC58EA5A64546DC25912B6C430529F6A7F449FD96536DE269D1A1B015A4AC6B6E46EE19DCE8143726A6503E290E4BAE6BD78319B5878981F6CFFDB3B818209341FD68B
c = 10610336534759505889607399322387179316771488492347274741918862678692508953185876570981227584004676580623553664818853686933004290078153620168054665086468417541382824708104480882577200529822968531743002301934310349005341104696887943182074473298650903541494918266823037984054778903666406545980557074219162536057146090758158128189406073809226361445046225524917089434897957301396534515964547462425719205819342172669899546965221084098690893672595962129879041507903210851706793788311452973769358455761907303633956322972510500253009083922781934406731633755418753858930476576720874219359466503538931371444470303193503733920039

d = wiener_hack(e, n)
m = pow(c,d,n)
print(long_to_bytes(m))
b'flag{You_Need_Some_Tea}'

所以最后的flag是 flag{You_Need_Some_Tea}