shift + f12 找到一个flag{}
字符串, 定位到关键函数, F5无效, 大概率是有花指令, 读一下汇编
这里连续push两个byte_428C54
很奇怪, nop掉下面那个, 再往上找到函数入口, p设置函数入口, 再F5
LRESULT __stdcall sub_401640(HWND hWndParent, UINT Msg, WPARAM wParam, LPARAM lParam)
{
int v5; // eax
size_t v6; // eax
DWORD v7; // eax
int v8; // eax
int v9; // eax
CHAR *v10; // [esp+0h] [ebp-44Ch]
CHAR v11[256]; // [esp+54h] [ebp-3F8h] BYREF
char v12[7]; // [esp+154h] [ebp-2F8h] BYREF
__int16 v13; // [esp+15Bh] [ebp-2F1h]
char v14; // [esp+15Dh] [ebp-2EFh]
char Str[253]; // [esp+160h] [ebp-2ECh] BYREF
__int16 v16; // [esp+25Dh] [ebp-1EFh]
char v17; // [esp+25Fh] [ebp-1EDh]
CHAR v18[256]; // [esp+260h] [ebp-1ECh] BYREF
CHAR String[4]; // [esp+360h] [ebp-ECh] BYREF
int v20; // [esp+364h] [ebp-E8h]
__int16 v21; // [esp+368h] [ebp-E4h]
CHAR Text[32]; // [esp+36Ch] [ebp-E0h] BYREF
struct tagRECT Rect; // [esp+38Ch] [ebp-C0h] BYREF
CHAR Buffer[100]; // [esp+39Ch] [ebp-B0h] BYREF
HDC hdc; // [esp+400h] [ebp-4Ch]
struct tagPAINTSTRUCT Paint; // [esp+404h] [ebp-48h] BYREF
int v27; // [esp+444h] [ebp-8h]
int v28; // [esp+448h] [ebp-4h]
LoadStringA(hInstance, 0x6Au, Buffer, 100);
if ( Msg > 0x111 )
{
if ( Msg == 517 )
{
if ( strlen((const char *)String1) > 6 )
ExitProcess(0);
if ( strlen((const char *)String1) )
{
memset(v18, 0, sizeof(v18));
v6 = strlen((const char *)String1);
memcpy(v18, String1, v6);
v7 = strlen((const char *)String1);
sub_40101E(String1, v7, v10);
strcpy(Str, "0kk`d1a`55k222k2a776jbfgd`06cjjb");
memset(&Str[33], 0, 0xDCu);
v16 = 0;
v17 = 0;
strcpy(v12, "SS");
*(_DWORD *)&v12[3] = 0;
v13 = 0;
v14 = 0;
v8 = strlen(Str);
sub_401005(v12, (int)Str, v8);
if ( _strcmpi((const char *)String1, Str) )
{
SetWindowTextA(hWndParent, "flag{}");
MessageBoxA(hWndParent, "Are you kidding me?", "^_^", 0);
ExitProcess(0);
}
memcpy(v11, &unk_423030, 0x32u);
v9 = strlen(v11);
sub_401005(v18, (int)v11, v9);
MessageBoxA(hWndParent, v11, 0, 0x32u);
}
++dword_428D54;
}
else
{
if ( Msg != 520 )
return DefWindowProcA(hWndParent, Msg, wParam, lParam);
if ( dword_428D54 == 16 )
{
strcpy(String, "ctf");
v20 = 0;
v21 = 0;
SetWindowTextA(hWndParent, String);
strcpy(Text, "Are you kidding me?");
MessageBoxA(hWndParent, Text, Buffer, 0);
}
++dword_428D54;
}
}
else
{
switch ( Msg )
{
case 0x111u:
v28 = (unsigned __int16)wParam;
v27 = HIWORD(wParam);
if ( (unsigned __int16)wParam == 104 )
{
DialogBoxParamA(hInstance, (LPCSTR)0x67, hWndParent, (DLGPROC)DialogFunc, 0);
}
else
{
if ( (unsigned __int16)wParam != 105 )
return DefWindowProcA(hWndParent, Msg, wParam, lParam);
DestroyWindow(hWndParent);
}
break;
case 2u:
PostQuitMessage(0);
break;
case 0xFu:
hdc = BeginPaint(hWndParent, &Paint);
GetClientRect(hWndParent, &Rect);
v5 = strlen(Buffer);
DrawTextA(hdc, Buffer, v5, &Rect, 1u);
EndPaint(hWndParent, &Paint);
break;
default:
return DefWindowProcA(hWndParent, Msg, wParam, lParam);
}
}
return 0;
}
int __cdecl sub_4013A0(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
DWORD i; // [esp+4Ch] [ebp-24h]
CHAR String2[4]; // [esp+50h] [ebp-20h] BYREF
BYTE v6[16]; // [esp+54h] [ebp-1Ch] BYREF
DWORD pdwDataLen; // [esp+64h] [ebp-Ch] BYREF
HCRYPTHASH phHash; // [esp+68h] [ebp-8h] BYREF
HCRYPTPROV phProv; // [esp+6Ch] [ebp-4h] BYREF
if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
return 0;
if ( CryptCreateHash(phProv, 0x8003u, 0, 0, &phHash) )
{
if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
{
CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);
*lpString1 = 0;
for ( i = 0; i < pdwDataLen; ++i )
{
wsprintfA(String2, "%02X", v6[i]);
lstrcatA(lpString1, String2);
}
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
return 1;
}
else
{
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
return 0;
}
}
else
{
CryptReleaseContext(phProv, 0);
return 0;
}
}
发现string1会经过sub_4013A0
中的hash处理, 查MSDN的文档
https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id?redirectedfrom=MSDN
猜测是经过md5处理, 返回的值与0kk`d1a`55k222k2a776jbfgd`06cjjb ^ SS的结果比较
s = '0kk`d1a`55k222k2a776jbfgd`06cjjb'
key = 'SS'
md5val = ''
for i in range(len(s)):
md5val += chr(ord(s[i]) ^ ord(key[i % 2]))
print(md5val)
查MD5, 得到解密真正flag的key == 123321
unsigned int __cdecl sub_401590(LPCSTR lpString, int a2, unsigned int a3)
{
unsigned int result; // eax
unsigned int i; // [esp+4Ch] [ebp-Ch]
unsigned int v5; // [esp+54h] [ebp-4h]
v5 = lstrlenA(lpString);
for ( i = 0; ; ++i )
{
result = i;
if ( i >= a3 )
break;
*(_BYTE *)(i + a2) ^= lpString[i % v5];
}
return result;
}
shift + e导出unk_423030
数据, 写逆
enc = [
87, 94, 82, 84, 73, 95, 1, 109, 105, 70,
2, 110, 95, 2, 108, 87, 91, 84, 76
]
key = "123321"
flag = ''
for i in range(len(enc)):
flag += chr(enc[i] ^ ord(key[i % 6]))
print(flag)
$ file 51475f91-7b90-41dd-81a3-8b82df4f29d0.bin
51475f91-7b90-41dd-81a3-8b82df4f29d0.bin: firmware 941 v7 TP-LINK Technologies ver. 1.0, version 3.15.36, 4063744 bytes or less, at 0x200 772784 bytes , at 0x100000 2883584 bytes
$ binwalk -e 51475f91-7b90-41dd-81a3-8b82df4f29d0.bin
$ ls _51475f91-7b90-41dd-81a3-8b82df4f29d0.bin.extracted/
120200.squashfs 20400 20400.7z squashfs-root
包含一个squashfs
文件系统镜像, 需要使用firmware-mod-kit
工具包内的unsquashfs_all.sh
解压文件系统镜像
需要在linux里编译, 而且安装过程也比较坑, 嫌麻烦的直接上kali
git clone https://github.com/mirror/firmware-mod-kit.git
sudo apt-get install build-essential zlib1g-dev libz1zma-dev python-magic
cd firmware-mod-kit/src
./configure && make
kali执行
sudo apt update
sudo apt upgrade
sudo apt install firmware-mod-kit
apt安装的软件, 可以用dpkg -L xxx
显示安装路径, 然后用unsquashfs_all.sh
解析文件系统
┌──(kali㉿kali)-[~/Desktop]
└─$ /opt/firmware-mod-kit/trunk/unsquashfs_all.sh 120200.squashfs
Attempting to extract SquashFS 4.X file system...
Skipping squashfs-2.1-r2 (wrong version)...
Skipping squashfs-3.0 (wrong version)...
Skipping squashfs-3.0-lzma-damn-small-variant (wrong version)...
Skipping others/squashfs-2.0-nb4 (wrong version)...
Skipping others/squashfs-2.2-r2-7z (wrong version)...
Skipping others/squashfs-3.0-e2100 (wrong version)...
Skipping others/squashfs-3.2-r2 (wrong version)...
Skipping others/squashfs-3.2-r2-lzma (wrong version)...
Skipping others/squashfs-3.2-r2-lzma/squashfs3.2-r2/squashfs-tools (wrong version)...
Skipping others/squashfs-3.2-r2-hg612-lzma (wrong version)...
Skipping others/squashfs-3.2-r2-wnr1000 (wrong version)...
Skipping others/squashfs-3.2-r2-rtn12 (wrong version)...
Skipping others/squashfs-3.3 (wrong version)...
Skipping others/squashfs-3.3-lzma/squashfs3.3/squashfs-tools (wrong version)...
Skipping others/squashfs-3.3-grml-lzma/squashfs3.3/squashfs-tools (wrong version)...
Skipping others/squashfs-3.4-cisco (wrong version)...
Skipping others/squashfs-3.4-nb4 (wrong version)...
Trying ./src/others/squashfs-4.2-official/unsquashfs... Parallel unsquashfs: Using 4 processors
Trying ./src/others/squashfs-4.2/unsquashfs... Parallel unsquashfs: Using 4 processors
Trying ./src/others/squashfs-4.0-lzma/unsquashfs-lzma... Parallel unsquashfs: Using 4 processors
480 inodes (523 blocks) to write
[====================================================| ] 454/523 86%
created 341 files
created 39 directories
created 70 symlinks
created 0 devices
created 0 fifos
File system sucessfully extracted!
MKFS="./src/others/squashfs-4.0-lzma/mksquashfs-lzma"
提取出文件系统中的内容后, 把各个目录翻一遍, 找到backdoor
文件
直接 kali 脱壳
┌──(kali㉿kali)-[~/Desktop/squashfs-root/tmp]
└─$ upx -d backdoor
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2020
UPX 3.96 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 23rd 2020
File size Ratio Format Name
-------------------- ------ ----------- -----------
54907 <- 19508 35.53% linux/arm backdoor
Unpacked 1 file.
拖进IDA
bool initConnection()
{
char *v0; // r0
char s[512]; // [sp+4h] [bp-208h] BYREF
int v3; // [sp+204h] [bp-8h]
memset(s, 0, sizeof(s));
if ( mainCommSock )
{
close(mainCommSock);
mainCommSock = 0;
}
if ( currentServer )
++currentServer;
else
currentServer = 0;
strcpy(s, (&commServer)[currentServer]);
v3 = 36667;
if ( strchr(s, 58) )
{
v0 = strchr(s, 58);
v3 = atoi(v0 + 1);
*strchr(s, 58) = 0;
}
mainCommSock = socket(2, 1, 0);
return connectTimeout(mainCommSock, s, v3, 30) == 0;
}
.data:0001B108 E4 19 01 00 commServer DCD aEchoByethost51 ; DATA XREF: initConnection+8C↑o
.data:0001B108 ; .text:off_10C2C↑o
.data:0001B108 ; "echo.byethost51.com"
在initConnection
函数中进行链接建立操作, 可以分析出端口和地址
import hashlib
flag = hashlib.md5(b'echo.byethost51.com:36667')
print('flag{' + flag.hexdigest() + '}')
// positive sp value has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Str[50]; // [esp+12h] [ebp-96h] BYREF
char Destination[80]; // [esp+44h] [ebp-64h] BYREF
DWORD flOldProtect; // [esp+94h] [ebp-14h] BYREF
size_t v7; // [esp+98h] [ebp-10h]
int i; // [esp+9Ch] [ebp-Ch]
__main();
puts("please input you flag:");
if ( !VirtualProtect(encrypt, 0xC8u, 4u, &flOldProtect) )
exit(1);
scanf("%40s", Str);
v7 = strlen(Str);
if ( v7 != 24 )
{
puts("Wrong!");
exit(0);
}
strcpy(Destination, Str);
wrong(Str);
omg(Str);
for ( i = 0; i <= 186; ++i )
*((_BYTE *)encrypt + i) ^= 0x41u;
if ( encrypt(Destination) )
finally(Destination);
return 0;
}
char *__cdecl wrong(char *a1)
{
char *result; // eax
int i; // [esp+Ch] [ebp-4h]
for ( i = 0; i <= 23; ++i )
{
result = &a1[i];
if ( (i & 1) != 0 )
a1[i] -= i;
else
a1[i] ^= i;
}
return result;
}
int __cdecl omg(char *str)
{
int testval[24]; // [esp+18h] [ebp-80h] BYREF
int i; // [esp+78h] [ebp-20h]
int success; // [esp+7Ch] [ebp-1Ch]
success = 1;
qmemcpy(testval, &unk_4030C0, sizeof(testval));
for ( i = 0; i <= 23; ++i )
{
if ( str[i] != testval[i] )
success = 0;
}
if ( success == 1 )
return puts("hahahaha_do_you_find_me?");
else
return puts("wrong ~~ But seems a little program");
}
wrong
和omg
函数逻辑很简单, 测试的数组也是硬编码的, 不过逆回去之后会发现是假的flag, 所以关键处理逻辑就是encrypt
函数了
main
函数虽然SP不对, 但是IDA7.7可以直接F5, 大概看一下逻辑也能看懂, 只有encrypt
函数不能反编译, 因为上面的for
循环是做代码修改的, 可以IDAPython修改回去;
也可以动调然后dump出来
拿到dump得到exe, 拖进IDA分析encrypt
int __cdecl start(int a1)
{
int v2[19]; // [esp+1Ch] [ebp-6Ch] BYREF
int v3; // [esp+68h] [ebp-20h]
int i; // [esp+6Ch] [ebp-1Ch]
v3 = 1;
qmemcpy(v2, &unk_403040, sizeof(v2));
for ( i = 0; i <= 18; ++i )
{
if ( (char)(*(_BYTE *)(i + a1) ^ aHahahahaDoYouF[i]) != v2[i] )
{
puts("wrong ~");
v3 = 0;
exit(0);
}
}
puts("come here");
return v3;
}
就是和aHahahahaDoYouF
数组进行xor, 直接REV
#include
#include
using namespace std;
unsigned char testvals[] =
{
14, 0, 0, 0, 13, 0, 0, 0, 9, 0,
0, 0, 6, 0, 0, 0, 19, 0, 0, 0,
5, 0, 0, 0, 88, 0, 0, 0, 86, 0,
0, 0, 62, 0, 0, 0, 6, 0, 0, 0,
12, 0, 0, 0, 60, 0, 0, 0, 31, 0,
0, 0, 87, 0, 0, 0, 20, 0, 0, 0,
107, 0, 0, 0, 87, 0, 0, 0, 89, 0,
0, 0, 13, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 102, 0,
0, 0, 107, 0, 0, 0, 99, 0, 0, 0,
100, 0, 0, 0, 127, 0, 0, 0, 97, 0,
0, 0, 103, 0, 0, 0, 100, 0, 0, 0,
59, 0, 0, 0, 86, 0, 0, 0, 107, 0,
0, 0, 97, 0, 0, 0, 123, 0, 0, 0,
38, 0, 0, 0, 59, 0, 0, 0, 80, 0,
0, 0, 99, 0, 0, 0, 95, 0, 0, 0,
77, 0, 0, 0, 90, 0, 0, 0, 113, 0,
0, 0, 12, 0, 0, 0, 55, 0, 0, 0,
102, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0
};
int main()
{
char xorvals[] = "hahahaha_do_you_find_me?";
string flag = "";
for (int i = 0; i < 19; ++i)
{
flag += xorvals[i] ^ testvals[i * 4];
}
cout << flag << endl;
}
但是只有19位, 还差5位, 在dump出来的程序中再找找发现sub_40159A
函数
int __cdecl sub_40159A(int a1)
{
unsigned int v1; // eax
char v3[9]; // [esp+13h] [ebp-15h] BYREF
int v4; // [esp+1Ch] [ebp-Ch]
strcpy(v3, "%tp&:");
v1 = time(0);
srand(v1);
v4 = rand() % 100;
v3[6] = 0;
*(_WORD *)&v3[7] = 0;
if ( (v3[(unsigned __int8)v3[5]] != *(_BYTE *)((unsigned __int8)v3[5] + a1)) == v4 )
return puts("Really??? Did you find it?OMG!!!");
else
return puts("I hide the last part, you will not succeed!!!");
}
没啥固定逻辑, 剩下5位就摁猜, 因为知道最后一个字符是}
, 然后v3 == "%tp&:"
, 盲猜也是xor, 而且':' ^ '}' == 71
#include
#include
using namespace std;
int main()
{
char xorvals[] = "\%tp&:";
string flag = "flag{d07abccf8a410c";
for (int i = 0; i < 5; ++i)
{
flag += xorvals[i] ^ 71;
}
cout << flag << endl;
}
jadx打开, 看到InviteValidator
package defpackage;
import java.awt.Component;
import javax.swing.JOptionPane;
/* renamed from: InviteValidator reason: default package */
public class InviteValidator {
public static void main(String[] args) {
String response = JOptionPane.showInputDialog((Component) null, "Enter your invitation code:", "Minesweeper Championship 2018", 3);
if (response.equals("[email protected]")) {
JOptionPane.showMessageDialog((Component) null, "Welcome to the Minesweeper Championship 2018!\nPlease enter the following code to the ctfd.flare-on.com website to compete:\n\n" + response, "Success!", -1);
} else {
JOptionPane.showMessageDialog((Component) null, "Incorrect invitation code. Please try again next year.", "Failure", 0);
}
}
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // eax
int v5; // eax
char Str[48]; // [rsp+20h] [rbp-60h] BYREF
char Str1[64]; // [rsp+50h] [rbp-30h] BYREF
char v9[64]; // [rsp+90h] [rbp+10h] BYREF
char v10[64]; // [rsp+D0h] [rbp+50h] BYREF
char Str2[60]; // [rsp+110h] [rbp+90h] BYREF
int v12; // [rsp+14Ch] [rbp+CCh] BYREF
_main();
strcpy(Str2, "EmBmP5Pmn7QcPU4gLYKv5QcMmB3PWHcP5YkPq3=cT6QckkPckoRG");
puts("Hello, please input your flag and I will tell you whether it is right or not.");
scanf("%38s", Str);
if ( strlen(Str) != 38
|| (v3 = strlen(Str), (unsigned int)encode_one(Str, v3, v10, &v12))
|| (v4 = strlen(v10), (unsigned int)encode_two(v10, v4, v9, &v12))
|| (v5 = strlen(v9), (unsigned int)encode_three(v9, v5, Str1, &v12))
|| strcmp(Str1, Str2) )
{
printf("Something wrong. Keep going.");
return 0;
}
else
{
puts("you are right!");
return 0;
}
}
__int64 __fastcall encode_one(char *a1, int a2, char *a3, int *a4)
{
int v5; // esi
int v6; // esi
int v7; // esi
int v8; // [rsp+34h] [rbp-1Ch]
int v9; // [rsp+38h] [rbp-18h]
int v11; // [rsp+48h] [rbp-8h]
int i; // [rsp+4Ch] [rbp-4h]
unsigned __int8 *v13; // [rsp+70h] [rbp+20h]
v13 = (unsigned __int8 *)a1;
if ( !a1 || !a2 )
return 0xFFFFFFFFi64;
v11 = 0;
if ( a2 % 3 )
v11 = 3 - a2 % 3;
v9 = a2 + v11;
v8 = 8 * (a2 + v11) / 6;
for ( i = 0; i < v9; i += 3 )
{
*a3 = alphabet[(char)*v13 >> 2];
if ( a2 + v11 - 3 == i && v11 )
{
if ( v11 == 1 )
{
v5 = (char)cmove_bits(*v13, 6u, 2u);
a3[1] = alphabet[v5 + (char)cmove_bits(v13[1], 0, 4u)];
a3[2] = alphabet[(char)cmove_bits(v13[1], 4u, 2u)];
a3[3] = 61;
}
else if ( v11 == 2 )
{
a3[1] = alphabet[(char)cmove_bits(*v13, 6u, 2u)];
a3[2] = 61;
a3[3] = 61;
}
}
else
{
v6 = (char)cmove_bits(*v13, 6u, 2u);
a3[1] = alphabet[v6 + (char)cmove_bits(v13[1], 0, 4u)];
v7 = (char)cmove_bits(v13[1], 4u, 2u);
a3[2] = alphabet[v7 + (char)cmove_bits(v13[2], 0, 6u)];
a3[3] = alphabet[v13[2] & 0x3F];
}
a3 += 4;
v13 += 3;
}
if ( a4 )
*a4 = v8;
return 0i64;
}
__int64 __fastcall encode_two(const char *a1, int a2, char *a3, int *a4)
{
if ( !a1 || !a2 )
return 0xFFFFFFFFi64;
strncpy(a3, a1 + 26, 0xDui64);
strncpy(a3 + 13, a1, 0xDui64);
strncpy(a3 + 26, a1 + 39, 0xDui64);
strncpy(a3 + 39, a1 + 13, 0xDui64);
return 0i64;
}
__int64 __fastcall encode_three(const char *a1, int a2, char *a3, int *a4)
{
char v5; // [rsp+Fh] [rbp-11h]
int i; // [rsp+14h] [rbp-Ch]
const char *v8; // [rsp+30h] [rbp+10h]
v8 = a1;
if ( !a1 || !a2 )
return 0xFFFFFFFFi64;
for ( i = 0; i < a2; ++i )
{
v5 = *v8;
if ( *v8 <= '@' || v5 > 'Z' )
{
if ( v5 <= '`' || v5 > 'z' )
{
if ( v5 <= '/' || v5 > '9' )
*a3 = v5;
else
*a3 = (v5 - '0' + 3) % 10 + '0';
}
else
{
*a3 = (v5 - 'a' + 3) % 26 + 'a';
}
}
else
{
*a3 = (v5 - 'A' + 3) % 26 + 'A';
}
++a3;
++v8;
}
return 0i64;
}
从后往前逆, encode_three
是数字和字母移位置换3位, encode_two
是每13位数据进行置换, encode_one
是base64(看alphabet猜的)
REV
import base64
testval = 'EmBmP5Pmn7QcPU4gLYKv5QcMmB3PWHcP5YkPq3=cT6QckkPckoRG'
print(len(testval))
testval = list(testval)
for i in range(len(testval)):
tmp = testval[i]
if not str.isalnum(tmp):
continue
if str.isalpha(tmp):
if str.isupper(tmp):
testval[i] = chr(((ord(tmp) - ord('A') - 3) % 26) + ord('A'))
else:
testval[i] = chr(((ord(tmp) - ord('a') - 3) % 26) + ord('a'))
else:
testval[i] = chr(((ord(tmp) - ord('0') - 3) % 10) + ord('0'))
print(''.join(testval))
tempval = [' ' for _ in range(52)]
tempval[13:26] = testval[39:]
tempval[39:] = testval[26:39]
tempval[:13] = testval[13:26]
tempval[26:39] = testval[:13]
print(tempval)
tempval = ''.join(tempval)
print(tempval)
flag = base64.b64decode(tempval.encode('utf-8'))
print(flag.decode('utf-8'))
注意 - 写脚本时踩的坑:
%
的优先级大于+-
;
python3解码base64时, 字符都为unicode编码, 而b64decode函数的参数为byte类型, 所以必须先转码.
还有如果用列表的切片赋值, 会出错, 原因在于浅拷贝, 比如
tempval = testval
testval[13:26] = tempval[39:]
testval[39:] = tempval[26:39]
testval[:13] = tempval[13:26]
testval[26:39] = tempval[:13]