re学习记录

kali

image-20250122203829491

win10

image-20250122203910929

RE

若显示未安装crypto库,使用pip install pycryptodome 来安装crypto库

​ && 与 同真为真,有假即假 and

​ || 或 有真即真,全假才假 or

​ ! 非 not

IDA的快捷键

1.shift+E

换进制

image-20250119223941168

按H 换成16进制

按R 换成R 键(切换显示格式)

  • 如果当前数据是数值形式,按 R 键可以切换显示格式。
  • 如果数值对应 ASCII 字符,按 R 键会将其显示为字符形式。
  • 例如:
    • 原始数据:0x48
    • R 键后:'H'

A 键(转换为 ASCII 字符串)

  • 选中一段数据(通常是连续的字节),然后按 A 键。
  • IDA 会将这些字节解释为 ASCII 字符串,并将其显示为字符串形式。
  • 例如:
    • 原始数据:48 65 6C 6C 6F 00(十六进制)。
    • A 键后:"Hello"

Decimal 16进制

Octal 8 进制

Char 将ASCII转换为字符

单击字符串后点击H 将字符串转换为16进制 按R 转换为字符串

按 D 字符串准换为ASCII

按 A ASCII 转换为字符串

image-20250120224621745

CTRL+F9 走到该函数返回的地址

若要在汇编代码页面要修改汇编代码 右键 Assemble 就可以修改

或者

Edit patch program change byte

image-20250320093950845

一.RE中常见加密解密

1.AES

1.识别特征

image-20241226143347233

image-20241226143828419

0dc7b70d94a867f5582b84363b7d8223

密钥拓展 原始密钥一般比较短 比如16字节 而算法如果进行10轮运算的话需要16*(10+1)字节长度的密钥 需要对原始密钥进行拓展

字节代换(SubBytes)根据查表把一个字节换位另一个字节

image-20241226144149206

行移位 (ShiftRows) 将数据矩阵的每一行循环移位一定长度

image-20241226144208827

列混合 ( MixColumns)将数据矩阵乘以一个固定的矩阵 增加混淆程度

image-20241226144218233

轮密钥加(AddRoundKey) 将数据矩阵和密钥矩阵进行异或操作

image-20241226144227914

2.解密脚本积累

需要干什么

①在题目中找到密钥,赋值给脚本中的key变量

②找到密文,赋值给脚本中的cipher变量

③运行即可获得明文

from Crypto.Cipher import AES
 
def AES_dec(cipher, key):
    key = key.encode()
    aes = AES.new(key, AES.MODE_ECB)  # 创建一个AES解密对象,需要传入密钥和加密模式(这里是ECB模式)
    return aes.decrypt(cipher).decode()  # 返回解密结果
 
if __name__ == '__main__':
    key = '1234567890123456'        
    # 注意解密过程中密文的格式需要为byte类型  字节数组类型
    # 本题获取到的是十六进制文本,处理成字符串后使用bytes.fromhex()函数转为对应的字节数组(如果是其它形式,用相应的函数来转bytes)      hex 16进制
    cipher = bytes.fromhex('F3498AED82CE44E2357C23F5DCF897A43B6A7BFEE0467C591E301CBC38F99913')
    res = AES_dec(cipher, key)
    print(res)

2.base64换表

1.识别特征

看到base64或者类似base64标准表的

标准的base64加解密所用表是【ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/】刚好64个字符

像这样:image-20241226144700114

2.解题脚本积累
需要

①在题目中找到新的base64表(换完之后的),赋值给s2

②将密文赋值给en_text

③运行后即可获得明文

import base64 #导入base64模块用于解密
s1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' #标准表
s2 = 'qvEJAfHmUYjBac+u8Ph5n9Od17FrICL/X0gVtM4Qk6T2z3wNSsyoebilxWKGZpRD' #base64换表
en_text = '5Mc58bPHLiAx7J8ocJIlaVUxaJvMcoYMaoPMaOfg15c475tscHfM/8==' #密文
 
map = str.maketrans(s2, s1) #用str类中的maketrans建立映射,注意第一个参数是需要映射的字符串,第二个参数是映射的目标
map_text = en_text.translate(map) #映射实现替换密文,替换前是base64换表加密,替换后则是base64标准表加密
print(map_text) #可以先看看标准表加密的原base64密文
print(base64.b64decode(map_text)) #直接使用提供的base64解密函数解密

3.base58换表

1.特征识别

base58标准表123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

2.解题脚本积累

① 将新表赋值给s2

② 将密文赋值给en_text

③运行

import base58  # 导入base58模块用于解密
s1 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'  # 标准表
s2 = 'ABCDEFGHJKLMNPQRSTUVWXYZ123456789abcdefghijkmnopqrstuvwxyz'  # base58换表
en_text = 'BQ3SDnTj7vaKkMvur'  # 密文
 
# 用str类中的maketrans建立映射,注意第一个参数是需要映射的字符串,第二个参数是映射的目标
map = str.maketrans(s2, s1)
map_text = en_text.translate(map)  # 映射实现替换密文,替换前是base58换表加密,替换后则是base58标准表加密
print(map_text)  # 可以先看看标准表加密的原base58密文
print(base58.b58decode(map_text).decode())  # 直接使用提供的base58解密函数解密

4.base64魔改

1.例题分析

image-20241226145601509

change_table()函数

对标准表修改,需要写脚本获得变表

table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
arr = list(table)
for i in range(0, 16):
    arr[4 * i], arr[4 * i + 2] = arr[4 * i + 2], arr[4 * i]
    arr[4 * i + 1], arr[4 * i + 3] = arr[4*i + 3], arr[4 * i + 1]
for i in arr:
    print(i,end='') #CDABGHEFKLIJOPMNSTQRWXUVabYZefcdijghmnklqropuvstyzwx23016745+/89

image-20241226145820706

在base加密的过程中,对每次计算的索引格外加了key中的内容,这样一来不仅表不是标准表,索引的规则也被修改了

image-20241226145908912

对base索引的这个流程中,减去题目中每次加密过程加上的key,让索引变成原来不加key的正常的索引

table = "CDABGHEFKLIJOPMNSTQRWXUVabYZefcdijghmnklqropuvstyzwx23016745+/89"  # base64变表
cipher = "TqK1YUSaQryEMHaLMnWhYU+Fe0WPenqhRXahfkV6WE2fa3iRW197Za62eEaD"  # 密文
_index = []
key = [1, 2, 3, 4]
for i in range(len(cipher)):
    tmp = table.index(cipher[i]) - key[i % 4]  # 减去加密时加上的key
    if tmp >= 0:
        _index.append(tmp)
    else:  # 因为减去key会导致索引变成负数,+64保证在正常索引范围
        _index.append(tmp + 64)
       

最后再按照正常base64加密的逻辑,每四个索引为一组,合并成一个单独的整数(通过对每个值左移相应的位数(a 左移 18 位,b 左移 12 位,c 左移 6 位),然后使用按位或运算符(|)结合这些结果,得到一个 24 位的整数

根据得到的 24 位整数,通过右移操作从中提取出三个字节:

对于 j=0,提取高位字节(8-15位)。
对于 j=1,提取中间字节(16-23位)。
对于 j=2,提取低位字节(24-31位)。

使用 & 0xff 来确保获得的字节只保留最低的 8 位

for i in range(0, len(_index), 4):
    a = _index[i]
    b = _index[i + 1]
    c = _index[i + 2]
    d = _index[i + 3]
    sum = a << 18 | b << 12 | c << 6 | d
    for j in range(3):
        print(chr((sum >> ((2 - j) * 8)) & 0xff), end="")
2.解密脚本积累

变表

table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
arr = list(table)
for i in range(0, 16):
    arr[4 * i], arr[4 * i + 2] = arr[4 * i + 2], arr[4 * i]
    arr[4 * i + 1], arr[4 * i + 3] = arr[4*i + 3], arr[4 * i + 1]
for i in arr:
    print(i,end='') #CDABGHEFKLIJOPMNSTQRWXUVabYZefcdijghmnklqropuvstyzwx23016745+/89

魔改base

注:上文分析时展示脚本只适用于明文长度为3的倍数的情况,明文长度非3的倍数,则密文一定会出现'='字符,因此必须格外处理'='字符的情况

处理方法是去掉密文右边的'=',并添加一些未处理的判断


table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"  # base64变表   换成上文换后的base64表
cipher = "YWJjZGVmZw=="  # 密文
cipher = cipher.rstrip('=') #去除密文多余的'='
_index = []
key = [1, 2, 3, 4]
for i in range(len(cipher)):
    tmp = table.index(cipher[i])
    _index.append(tmp)
print(_index)
for i in range(0, len(_index), 4):
    a = _index[i]
    b = _index[i + 1]
    c = _index[i + 2] if i + 2 < len(_index) else 0  # 添加范围检查,为未处理部分设为0
    d = _index[i + 3] if i + 3 < len(_index) else 0
    sum = a << 18 | b << 12 | c << 6 | d
    for j in range(3):
        if i * 6 + j * 8 < len(cipher) * 8:  # 检查是否超出原始编码长度
            print(chr((sum >> ((2 - j) * 8)) & 0xff), end="")

5.z3

安装z3库

**pip install z3-solver**

1.识别特征

出现多组限制方程

如:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char flag[32]; // [rsp+20h] [rbp-60h] BYREF
  int v5; // [rsp+40h] [rbp-40h]
  int v6; // [rsp+44h] [rbp-3Ch]
  int v7; // [rsp+48h] [rbp-38h]
  int v8; // [rsp+4Ch] [rbp-34h]
  int v9; // [rsp+50h] [rbp-30h]
  int v10; // [rsp+54h] [rbp-2Ch]
  int v11; // [rsp+58h] [rbp-28h]
  int v12; // [rsp+5Ch] [rbp-24h]
  int v13; // [rsp+60h] [rbp-20h]
  int v14; // [rsp+64h] [rbp-1Ch]
  int v15; // [rsp+68h] [rbp-18h]
  int v16; // [rsp+6Ch] [rbp-14h]
  int v17; // [rsp+70h] [rbp-10h]
  int v18; // [rsp+74h] [rbp-Ch]
  int v19; // [rsp+78h] [rbp-8h]
  int v20; // [rsp+7Ch] [rbp-4h]

  _main(argc, argv, envp);
  printf("Please input your flag: ");
  scanf("%s", flag);
  v20 = flag[0];
  v19 = flag[1];
  v18 = flag[2];
  v17 = flag[3];
  v16 = flag[4];
  v15 = flag[5];
  v14 = flag[6];
  v13 = flag[7];
  v12 = flag[8];
  v11 = flag[9];
  v10 = flag[10];
  v9 = flag[11];
  v8 = flag[12];
  v7 = flag[13];
  v6 = flag[14];
  v5 = flag[15];
  if ( 7 * flag[0] == 546                       // flag[0]=78
    && 2 * v19 == 166                           // v19=83
    && 6 * v18 + v17 + 7 * v15 == 1055
    && 2 * v7 + v12 + 7 * v15 + v17 + 4 * v19 + 4 * v16 + 6 * v13 + 8 * v5 == 3107
    && 4 * v16 == 336                           // v16=84
    && 2 * v19 + 7 * v15 == 656                 // v15=70
    && 2 * v7 + 3 * v9 + 3 * v14 + 6 * v13 + v12 + 5 * v11 + 16 * v10 + 6 * v8 + 8 * v5 == 5749
    && 6 * v13 == 606                           // v13=101
    && 5 * v6 + v12 == 652                      // v12=52
    && 5 * v11 + 16 * v10 + 6 * v8 == 3213
    && 2 * v7 + 3 * v9 + 24 * v10 + 5 * v11 + 3 * v14 + 6 * v13 + v12 + 6 * v8 + 8 * v5 == 6717
    && 3 * v9 == 285                            // v9=95
    && 2 * v12 + 3 * v14 + 6 * v13 + 8 * v10 + 6 * v8 + 2 * v7 + 5 * v6 + 8 * v5 == 4573
    && 5 * v6 == 600                            // v6=120
    && v17 + 6 * v18 + 4 * v16 + 7 * v15 + 2 * v7 == 1615
    && v12 + 7 * v15 + 2 * v19 + 6 * v13 + 8 * v5 == 2314 )// v5=125
  {
    puts("Success");
    system("pause");
  }
  else
  {
    puts("Wrong");
    system("pause");
  }
  return 0;
}




2.解题脚本积累

定义变量 遍历i即0到23 str(i)将i当做字符串,然后把s和字符串连接起来,比如s0,s1.........s23

x = [Int('s' + str(i)) for i in range(0,24)]
from z3 import *
 
#创建未知数变量
v = [Int(f'v{i}')    for i in range(0, 16)]   
 
#创建解释器对象
solver = Solver()#创建一个求解器对象
 
#添加约束方程
solver.add(v[0] * 7 == 546)
solver.add(v[1] * 2 == 166)
solver.add(v[2] * 6 + v[3] * 1 + v[5] * 7 == 1055)
solver.add(v[1] * 4 + v[3] + v[4] * 4 + v[5] * 7 + v[7] * 6 + v[8] * 1 + v[13] * 2 + v[15] * 8 == 3107)
solver.add(v[4] * 4 == 336)
solver.add(v[1] * 2 + v[5] * 7 == 656)
solver.add(v[6] * 3 + v[7] * 6 + v[8] + v[9] * 5 + v[10] * 16 + v[11] * 3 + v[12] * 6 + v[13] * 2 + v[15] * 8 == 5749)
solver.add(v[7] * 6 == 606)
solver.add(v[8] + v[14] * 5 == 652)
solver.add(v[9] * 5 + v[10] * 16 + v[12] * 6 == 3213)
solver.add(v[6] * 3 + v[7] * 6 + v[8] + v[9] * 5 + v[10] * 24 + v[11] * 3 + v[12] * 6 + v[13] * 2 + v[15] * 8 == 6717)
solver.add(v[11] * 3 == 285)
solver.add(v[6] * 3 + v[7] * 6 + v[8] * 2 + v[10] * 8 + v[12] * 6 + v[13] * 2 + v[14] * 5 + v[15] * 8 == 4573)
solver.add(v[14] * 5 == 600)
solver.add(v[2] * 6 + v[3] * 1 + v[4] * 4 + v[5] * 7 + v[13] * 2 == 1615)
solver.add(v[1] * 2 + v[5] * 7 + v[7] * 6 + v[8] * 1 + v[15] * 8 == 2314)
 
#求解并转化为字符输出,得到flag
if solver.check() == sat: #check()方法用来判断是否有解,sat(即satisify)表示满足有解
    ans = solver.model() #model()方法得到解
    for i in v:
        print(chr(ans[i].as_long()), end='')
#一般不会无解,如果无解八成是未知数变量的类型不符合,或约束方程添加错误
else:
    print("no ans!")

这个是v1,v2变量

如果是多个变量如v , w, x, y ,z 使用这个

from z3 import *
s = Solver()
v , w, x, y ,z = Ints(‘v w x y z’)
s.add(v * 23 + w * -32 + x * 98 + y * 55 + z * 90 == 333322)
s.add(v * 123 + w * -322 + x * 68 + y * 67 + z * 32 == 707724)
s.add(v * 266 + w * -34 + x * 43 + y * 8 + z * 32 == 1272529)
s.add(v * 343 + w * -352 + x * 58 + y * 65 + z * 5 == 1672457)
s.add(v * 231 + w * -321 + x * 938 + y * 555 + z * 970 == 3372367)
flag=[]
if s.check() == sat:
ans=s.model()
flag.append(ans[v])
flag.append(ans[w])
flag.append(ans[x])
flag.append(ans[y])
flag.append(ans[z])
for c in flag:
print(c,end=“_”)
脚本解析

1.创建未知数变量

v , w, x, y ,z = Ints(‘v w x y z’)或

v = [Int(f'v{i}') for i in range(0, 16)]

【for i in range(0, 16)】的含义是产生值为0到15的i

【f'v{i}'】表示格式化字符串,结果显然为['v0', 'v1', 'v2' ... 'v15']

这里最后还有一步操作很关键,【Int()】是z3中用于将变量转换为特定类型的一个函数,转换后的结果显然为[v0, v1, v2 ... v15],这步转换是不可或缺的,只有转为z3中的特定整数类型变量后,它们才能作为之后的约束方程中的变量来使用
2.创建解释器对象

就是实例化

solver = Solver()

3.添加约束方程

直接调用Solver类的成员函数add()将题目给出的所有约束方程添加

solver.add(v[0] * 7 == 546)
solver.add(v[1] * 2 == 166)
solver.add(v[2] * 6 + v[3] * 1 + v[5] * 7 == 1055)
solver.add(v[1] * 4 + v[3] + v[4] * 4 + v[5] * 7 + v[7] * 6 + v[8] * 1 + v[13] * 2 + v[15] * 8 == 3107)
solver.add(v[4] * 4 == 336)
solver.add(v[1] * 2 + v[5] * 7 == 656)
solver.add(v[6] * 3 + v[7] * 6 + v[8] + v[9] * 5 + v[10] * 16 + v[11] * 3 + v[12] * 6 + v[13] * 2 + v[15] * 8 == 5749)
solver.add(v[7] * 6 == 606)
solver.add(v[8] + v[14] * 5 == 652)
solver.add(v[9] * 5 + v[10] * 16 + v[12] * 6 == 3213)
solver.add(v[6] * 3 + v[7] * 6 + v[8] + v[9] * 5 + v[10] * 24 + v[11] * 3 + v[12] * 6 + v[13] * 2 + v[15] * 8 == 6717)
solver.add(v[11] * 3 == 285)
solver.add(v[6] * 3 + v[7] * 6 + v[8] * 2 + v[10] * 8 + v[12] * 6 + v[13] * 2 + v[14] * 5 + v[15] * 8 == 4573)
solver.add(v[14] * 5 == 600)
solver.add(v[2] * 6 + v[3] * 1 + v[4] * 4 + v[5] * 7 + v[13] * 2 == 1615)
solver.add(v[1] * 2 + v[5] * 7 + v[7] * 6 + v[8] * 1 + v[15] * 8 == 2314)

4.求解并转化为字符输出,得到flag

使用成员函数check()获取返回值,判断是否等于sat(sat是z3模块中的一个常量,代表方程组有解)

有解情况下使用成员函数model()获取解,该函数会返回一个列表,列表中的解以键值对的形式存储,例如本题的解形式如下:

接下来要做的是将解转化为字符,但由于该列表中的元素是z3中的特殊类型,需要先转换为python中的整数类型才能使用chr()函数转为对应字符

另外需要注意的是该列表求出的解不是按未知参数的名字大小排序的,如果直接按列表中的解的顺序转为字符输出,flag显然不对

解决办法是循环访问v列表中的[v0, v1, v2 ... v15],将其作为索引去访问ans列表,即此处的【ans[i]】,然后使用as_long()函数将解的类型转为python中的int类型,最后使用chr()函数转为对应字符

        if solver.check() == sat: #check()方法用来判断是否有解,sat(即satisify)表示满足有解
            ans = solver.model() #model()方法得到解
            for i in v:
                print(chr(ans[i].as_long()), end='')
        #一般不会无解,如果无解八成是未知数变量的类型不符合,或约束方程添加错误
        else:
            print("no ans!")
出现多个逻辑联系符

image-20241226204451621

​ 脚本批量处理

import re
 
def replace_func(match):
    shift = 2 #shift是指第一个未知数和0的差,例如:如果题目中第一个未知数是v2(如果是v3),那么shift就设置成2(就设置成3)
    index = int(match.group(1)) - shift
    return str(f'v[{index}]')  # 返回字符串'v[a后数字-1]',用其替换匹配到的an
 
 
if __name__ == '__main__':
    s1 = ""  # 定义包含an的字符串
 
    s1 = re.sub(r'v(2[0-9]|1[0-9]|[1-9])', replace_func, s1)
    # sub函数参数, pattern、repl、string分别表示:正则表达式匹配规则、替换后结果(可以是函数也可以是常量)、要被查找替换的原始字符串
    s1 = re.sub('!', '=', s1) #有些题目给的条件的方程是用'||'关系运算符连接的不等式方程,需要用这一行代码将'!'替换成'='变成等式方程
    res = s1.split('| | ')
    print(res)

将整串条件复制过来放进s1中之后,把多余的换行和空格删除掉,形成一连串的由关系运算符连接的条件,至于这里为什么会有一行s1+='xxx' 这个方程没有和其它方程一起连接,所以单独给它拼接进s1里

image-20241226204643575

image-20241226204657199

根据条件中具体的关系运算符,到底是"&&"还是"||"来使用split()函数将每个方程分隔开形成列表

在这里你需要仔细注意一下方程中的"||"中间有没有空格,如果有那你用的split()函数也得加上空格,即s1.split('| |'),因为你要让split()函数正确地找到分隔符
得到一串字符

把这一整个列表复制下来,赋值给下面这个脚本的fc列表,直接运行就能出结果

from z3 import *
 
def solver_eng(fc):
    # 创建解释器对象
    solver = Solver()
    # 添加约束方程
    for i in range(len(fc)):
        solver.add(eval(fc[i])) #eval函数会将字符串形式的方程转换为z3模块能解析的方程
 
    # 求解并转化为字符输出,得到flag
    if solver.check() == sat:  # check()方法用来判断是否有解,sat(即satisify)表示满足有解
        ans = solver.model()  # model()方法得到解
        for i in v:
            print(chr(ans[i].as_long()), end='')
    # 一般不会无解,如果无解八成是未知数变量的类型不符合,或约束方程添加错误
    else:
        print("no ans!")
 
 
if __name__ == '__main__':
    # 设置方程,请用脚本将条件中的所有方程处理成列表,然后赋值给fc列表(这样你就不用一个一个方程慢慢去复制了)
    fc = []
    # 创建未知数变量
    v = [Int(f'v{i}') for i in range(0, len(fc))]
 
    solver_eng(fc)
例题分析
NSSCTF---login

使用 python pyinstxtractor.py login.exe

image-20241226200808807

找到新文件夹

找到struct和login

image-20241226200840622

image-20241226200901437

插入方法 编辑——插入字节

根据struct的文件头更改login的文件头并修改后缀为.pyc 或.PYC

使用命令 uncompyle6 login.pyc > out999.py 反编译pyc文件

获得源码

# uncompyle6 version 3.9.2
# Python bytecode version base 3.6 (3379)
# Decompiled from: Python 3.7.8 (tags/v3.7.8:4b47a5b6ba, Jun 28 2020, 08:53:46) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: login.py
# Compiled at: 1995-09-28 00:18:56
# Size of source mod 2**32: 257 bytes
import sys
input1 = input("input something:")
if len(input1) != 14:
    print("Wrong length!")
    sys.exit()
else:
    code = []
    for i in range(13):
        code.append(ord(input1[i]) ^ ord(input1[i + 1]))

    code.append(ord(input1[13]))
    a1 = code[2]
    a2 = code[1]
    a3 = code[0]
    a4 = code[3]
    a5 = code[4]
    a6 = code[5]
    a7 = code[6]
    a8 = code[7]
    a9 = code[9]
    a10 = code[8]
    a11 = code[10]
    a12 = code[11]
    a13 = code[12]
    a14 = code[13]
    if (a1 * 88 + a2 * 67 + a3 * 65 - a4 * 5 + a5 * 43 + a6 * 89 + a7 * 25 + a8 * 13 - a9 * 36 + a10 * 15 + a11 * 11 + a12 * 47 - a13 * 60 + a14 * 29 == 22748) & (a1 * 89 + a2 * 7 + a3 * 12 - a4 * 25 + a5 * 41 + a6 * 23 + a7 * 20 - a8 * 66 + a9 * 31 + a10 * 8 + a11 * 2 - a12 * 41 - a13 * 39 + a14 * 17 == 7258) & (a1 * 28 + a2 * 35 + a3 * 16 - a4 * 65 + a5 * 53 + a6 * 39 + a7 * 27 + a8 * 15 - a9 * 33 + a10 * 13 + a11 * 101 + a12 * 90 - a13 * 34 + a14 * 23 == 26190) & (a1 * 23 + a2 * 34 + a3 * 35 - a4 * 59 + a5 * 49 + a6 * 81 + a7 * 25 + (a8 << 7) - a9 * 32 + a10 * 75 + a11 * 81 + a12 * 47 - a13 * 60 + a14 * 29 == 37136) & (a1 * 38 + a2 * 97 + a3 * 35 - a4 * 52 + a5 * 42 + a6 * 79 + a7 * 90 + a8 * 23 - a9 * 36 + a10 * 57 + a11 * 81 + a12 * 42 - a13 * 62 - a14 * 11 == 27915) & (a1 * 22 + a2 * 27 + a3 * 35 - a4 * 45 + a5 * 47 + a6 * 49 + a7 * 29 + a8 * 18 - a9 * 26 + a10 * 35 + a11 * 41 + a12 * 40 - a13 * 61 + a14 * 28 == 17298) & (a1 * 12 + a2 * 45 + a3 * 35 - a4 * 9 - a5 * 42 + a6 * 86 + a7 * 23 + a8 * 85 - a9 * 47 + a10 * 34 + a11 * 76 + a12 * 43 - a13 * 44 + a14 * 65 == 19875) & (a1 * 79 + a2 * 62 + a3 * 35 - a4 * 85 + a5 * 33 + a6 * 79 + a7 * 86 + a8 * 14 - a9 * 30 + a10 * 25 + a11 * 11 + a12 * 57 - a13 * 50 - a14 * 9 == 22784) & (a1 * 8 + a2 * 6 + a3 * 64 - a4 * 85 + a5 * 73 + a6 * 29 + a7 * 2 + a8 * 23 - a9 * 36 + a10 * 5 + a11 * 2 + a12 * 47 - a13 * 64 + a14 * 27 == 9710) & (a1 * 67 - a2 * 68 + a3 * 68 - a4 * 51 - a5 * 43 + a6 * 81 + a7 * 22 - a8 * 12 - a9 * 38 + a10 * 75 + a11 * 41 + a12 * 27 - a13 * 52 + a14 * 31 == 13376) & (a1 * 85 + a2 * 63 + a3 * 5 - a4 * 51 + a5 * 44 + a6 * 36 + a7 * 28 + a8 * 15 - a9 * 6 + a10 * 45 + a11 * 31 + a12 * 7 - a13 * 67 + a14 * 78 == 24065) & (a1 * 47 + a2 * 64 + a3 * 66 - a4 * 5 + a5 * 43 + a6 * 112 + a7 * 25 + a8 * 13 - a9 * 35 + a10 * 95 + a11 * 21 + a12 * 43 - a13 * 61 + a14 * 20 == 27687) & (a1 * 89 + a2 * 67 + a3 * 85 - a4 * 25 + a5 * 49 + a6 * 89 + a7 * 23 + a8 * 56 - a9 * 92 + a10 * 14 + a11 * 89 + a12 * 47 - a13 * 61 - a14 * 29 == 29250) & (a1 * 95 + a2 * 34 + a3 * 62 - a4 * 9 - a5 * 43 + a6 * 83 + a7 * 25 + a8 * 12 - a9 * 36 + a10 * 16 + a11 * 51 + a12 * 47 - a13 * 60 - a14 * 24 == 15317):
        print("flag is GWHT{md5(your_input)}")
        print("Congratulations and have fun!")
    else:
        print("Sorry,plz try again...")

# okay decompiling login.pyc

套脚本

from z3 import *

def solve_equations():
    # 创建Z3求解器
    solver = Solver()

    # 创建变量
    a1 = Int('a2')
    a2 = Int('a1')
    a3 = Int('a0')
    a4 = Int('a3')
    a5 = Int('a4')
    a6 = Int('a5')
    a7 = Int('a6')
    a8 = Int('a7')
    a9 = Int('a9')
    a10 = Int('a8')
    a11 = Int('a10')
    a12 = Int('a11')
    a13 = Int('a12')
    a14 = Int('a13')

    # 添加方程
    solver.add((((a1 * 88 + a2 * 67 + a3 * 65 - a4 * 5) + a5 * 43 + a6 * 89 + a7 * 25 + a8 * 13 - a9 * 36) + a10 * 15 + a11 * 11 + a12 * 47 - a13 * 60) + a14 * 29 == 22748)
    solver.add(((((a1 * 89 + a2 * 7 + a3 * 12 - a4 * 25) + a5 * 41 + a6 * 23 + a7 * 20 - a8 * 66) + a9 * 31 + a10 * 8 + a11 * 2 - a12 * 41 - a13 * 39) + a14 * 17 == 7258))
    solver.add(((((a1 * 28 + a2 * 35 + a3 * 16 - a4 * 65) + a5 * 53 + a6 * 39 + a7 * 27 + a8 * 15 - a9 * 33) + a10 * 13 + a11 * 101 + a12 * 90 - a13 * 34) + a14 * 23 == 26190))
    solver.add(((((a1 * 23 + a2 * 34 + a3 * 35 - a4 * 59) + a5 * 49 + a6 * 81 + a7 * 25 + (a8 * 128) - a9 * 32) + a10 * 75 + a11 * 81 + a12 * 47 - a13 * 60) + a14 * 29 == 37136))
    solver.add((((a1 * 38 + a2 * 97 + a3 * 35 - a4 * 52) + a5 * 42 + a6 * 79 + a7 * 90 + a8 * 23 - a9 * 36) + a10 * 57 + a11 * 81 + a12 * 42 - a13 * 62 - a14 * 11 == 27915))
    solver.add(((((a1 * 22 + a2 * 27 + a3 * 35 - a4 * 45) + a5 * 47 + a6 * 49 + a7 * 29 + a8 * 18 - a9 * 26) + a10 * 35 + a11 * 41 + a12 * 40 - a13 * 61) + a14 * 28 == 17298))
    solver.add(((((a1 * 12 + a2 * 45 + a3 * 35 - a4 * 9 - a5 * 42) + a6 * 86 + a7 * 23 + a8 * 85 - a9 * 47) + a10 * 34 + a11 * 76 + a12 * 43 - a13 * 44) + a14 * 65 == 19875))
    solver.add((((a1 * 79 + a2 * 62 + a3 * 35 - a4 * 85) + a5 * 33 + a6 * 79 + a7 * 86 + a8 * 14 - a9 * 30) + a10 * 25 + a11 * 11 + a12 * 57 - a13 * 50 - a14 * 9 == 22784))
    solver.add(((((a1 * 8 + a2 * 6 + a3 * 64 - a4 * 85) + a5 * 73 + a6 * 29 + a7 * 2 + a8 * 23 - a9 * 36) + a10 * 5 + a11 * 2 + a12 * 47 - a13 * 64) + a14 * 27 == 9710))
    solver.add((((((a1 * 67 - a2 * 68) + a3 * 68 - a4 * 51 - a5 * 43) + a6 * 81 + a7 * 22 - a8 * 12 - a9 * 38) + a10 * 75 + a11 * 41 + a12 * 27 - a13 * 52) + a14 * 31 == 13376))
    solver.add(((((a1 * 85 + a2 * 63 + a3 * 5 - a4 * 51) + a5 * 44 + a6 * 36 + a7 * 28 + a8 * 15 - a9 * 6) + a10 * 45 + a11 * 31 + a12 * 7 - a13 * 67) + a14 * 78 == 24065))
    solver.add(((((a1 * 47 + a2 * 64 + a3 * 66 - a4 * 5) + a5 * 43 + a6 * 112 + a7 * 25 + a8 * 13 - a9 * 35) + a10 * 95 + a11 * 21 + a12 * 43 - a13 * 61) + a14 * 20 == 27687))
    solver.add((((a1 * 89 + a2 * 67 + a3 * 85 - a4 * 25) + a5 * 49 + a6 * 89 + a7 * 23 + a8 * 56 - a9 * 92) + a10 * 14 + a11 * 89 + a12 * 47 - a13 * 61 - a14 * 29 == 29250))
    solver.add((((a1 * 95 + a2 * 34 + a3 * 62 - a4 * 9 - a5 * 43) + a6 * 83 + a7 * 25 + a8 * 12 - a9 * 36) + a10 * 16 + a11 * 51 + a12 * 47 - a13 * 60 - a14 * 24 == 15317))

    # 求解方程组
    print(solver.check())
    print(solver.model())

solve_equations()

这段代码中的这行代码((((a1 * 23 + a2 * 34 + a3 * 35 - a4 * 59) + a5 * 49 + a6 * 81 + a7 * 25 + (a8 << 7) - a9 * 32) + a10 * 75 + a11 * 81 + a12 * 47 - a13 * 60) + a14 * 29 == 37136),其中(a8 << 7)是不允许被使用的

需要把(a8 << 7)改为(a8 * 128),这两个所造成的效果是一样的,这个是原码和补码问题

运行得到

sat
[a13 = 33,
a3 = 7,
a4 = 104,
a10 = 88,
a12 = 88,
a1 = 24,
a7 = 91,
a9 = 52,
a6 = 28,
a5 = 43,
a0 = 10,
a8 = 108,
a2 = 119,
a11 = 74]

整理的[10, 24, 119, 7, 104, 43, 28, 91, 108, 52, 88, 74, 88, 33]

根据 code.append(ord(input1[i]) ^ ord(input1[i + 1]))

然后异或

key = [10, 24, 119, 7, 104, 43, 28, 91, 108, 52, 88, 74, 88, 33]
for i in range(12, -1, -1):
    key[i] ^= key[i+1]
for i in key:
    print(chr(i), end="")

得到U_G07_th3_k3y!

MD5加密就是flag

6.RC4

1.识别特征

长度为256的数组的创建

RC4是一种对称加密,通过密钥key和s盒生成密钥流,明文逐字节异或s盒,同时s盒也会发生变化

加密和解密使用相同的函数和密钥k

RC4的一些关键变量

1 S-Box也就是S盒,一个长度为256的数组,每个单元长度为一个字节

2 密钥K,密钥的长度与 明文长度 和 密钥流长度 无直接关系

3 临时向量k也是256字节,每个单元也是一个字节,如果密钥长度为256字节, 就直接把密钥的值赋给k,否则,轮转地将密钥的每个字节赋给k

#RC4加密
def rc4(key, ciphertext):
    # 初始化S盒
    sbox = list(range(256))
    j = 0
    for i in range(256):
        j = (j + sbox[i] + key[i % len(key)]) % 256
        sbox[i], sbox[j] = sbox[j], sbox[i]
 
    # 生成密钥流
    i = 0
    j = 0
    keystream = []
    for _ in range(len(ciphertext)):
        i = (i + 1) % 256
        j = (j + sbox[i]) % 256
        sbox[i], sbox[j] = sbox[j], sbox[i]
        k = sbox[(sbox[i] + sbox[j]) % 256]
        keystream.append(k)
    # print(keystream)
 
    # 解密密文
    plaintext = []
    for i in range(len(ciphertext)):
        m = ciphertext[i] ^ keystream[i]
        plaintext.append(m)
    print(plaintext)
 
    # 将明文转换为字符串
    return ''.join([chr(p) for p in plaintext])

# 测试
key = b"Flag{This_a_Flag}"
ciphertext =[0xE8,0xD8,0xBD,0x91,0x87,0x1A,0x01,0x0E,0x56,0x0F
	,0x53,0xF4,0x88,0x96,0x82,0xF9,0x61,0x42,0x0A,0xF2,0xAB
	,0x08,0xFE,0xD7,0xAC,0xFD,0x5E,0x00]
# for i in ciphertext:
#     print(chr(i),end="")
plaintext = rc4(key, ciphertext)
2.解密脚本积累

需要干什么

①将密文处理成列表形式,赋值给data变量/ciphertext

②将密钥处理成字符串形式,赋值给key变量

③运行后获得明文

ida在阿济格0x01/0x02/0x0*等16进制的数据转化位字符串时会将0去掉,导致长度不对等,此时需要手动调试还原。

def rc4_init(s_box, key, key_len):  # rc4初始化函数,产生s_box
    k = [0] * 256
    i = j = 0
    for i in range(256):
        s_box[i] = i
        k[i] = key[i % key_len]
    for i in range(256):
        j = (j + s_box[i] + ord(k[i])) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
def rc4_crypt(s_box, data, data_len, key, key_len):  # rc4算法,由于异或运算的对合性,RC4加密解密使用同一套算法,加解密都是它
    rc4_init(s_box, key, key_len)
    i = j = 0
    for k in range(data_len):
        i = (i + 1) % 256
        j = (j + s_box[i]) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
        t = (s_box[i] + s_box[j]) % 256
        data[k] ^= s_box[t]
 
if __name__ == '__main__':
    s_box = [0] * 257  # 定义存放s_box数据的列表
 
    # 此处的data即要解密的密文,需要定义成列表形式,其中的元素可以是十六进制或十进制数
    # 如果题目给出的是字符串,需要你自己先把数据处理成列表形式再套用脚本
    data = [235, 13, 97, 41, 191, 155, 5, 34, 243, 50, 40, 151, 227, 134,
            77, 45, 90, 42, 163, 85, 170, 213, 180, 108, 139, 81, 177]  
    #key一定要字符串
    key = "wanyuanshenwande"
 
    rc4_crypt(s_box, data, len(data), key, len(key))
    for i in data:
        print(chr(i), end='')

盒初始化

  • 第1个实现rc4 函数中,key[i % len(key)] 也是直接使用的,但这里 key 是一个字节字符串(bytes),因此可以直接用于索引。
  • 第2个实现rc4_init 函数中,key[i % key_len] 是直接使用的,假设 key 是一个字节列表或字节数组。

问题:如果 key 是一个字节字符串(如 b"Flag{This_a_Flag}"),那么在第2个实现中,key[i % key_len] 返回的是字节对象,而不是整数。应该使用 key[i % key_len] 直接作为整数,或者使用 ord(key[i % key_len]) 来获取字节的整数值。

  1. 密钥流生成与解密

第1个实现rc4 函数生成了一个独立的 keystream 列表,然后通过 ciphertext[i] ^ keystream[i] 进行解密,最终返回

解密后的 plaintext

第2个实现rc4_crypt 函数直接在 data 上进行原地修改,通过 data[k] ^= s_box[t] 进行解密。

问题:第一个实现中,data 是直接修改的,这可能会导致问题,尤其是在多次调用 rc4_crypt 时,data 的内容会被不断修改。此外,data 中的值可能会超出可打印字符的范围(0-127),导致 chr(i) 抛出 ValueError

C语言版

#include <stdio.h>
#include <string.h>
void swap(int* x, int* y) {
    int tmp = *x;
    *x = *y;
    *y = tmp;
}
void rc4_init(int s_box[], char key[], int key_len) {
    int k[256] = {0};
    int i = 0, j = 0;
    for (int i = 0; i < 256; i++) {
        s_box[i] = i;
        k[i] = key[i % key_len];
    }
    for (int i = 0; i < 256; i++) {
        j = (j + s_box[i] + k[i]) % 256;
        swap(&s_box[i], &s_box[j]);
    }
}
void rc4_crypt(int s_box[], int data[], int data_len, char key[], int key_len) {
    rc4_init(s_box, key, key_len);
    int i = 0, j = 0;
    for (int k = 0; k < data_len; k++) {
        i = (i + 1) % 256;
        j = (j + s_box[i]) % 256;
        swap(&s_box[i], &s_box[j]);
        int tmp = (s_box[i] + s_box[j]) % 256;
        data[k] ^= s_box[tmp];
    }
}
int main() {
    int s_box[257] = {0};
    int data[] = {235, 13, 97, 41, 191, 155, 5, 34, 243, 50, 40, 151, 227, 134, 77, 45, 90, 42, 163, 85, 170, 213, 180, 108, 139, 81, 177};
    char key[] = "wanyuanshenwande";
    rc4_crypt(s_box, data, sizeof(data)/ sizeof(data[0]), key, strlen(key));
    for(int i = 0; i < sizeof(data)/ sizeof(data[0]); i++){
        printf("%c", data[i]);
    }
    return 0;
}
3.RC4加密过程

image-20241226215025167

RC4初始化

进入rc4_init函数

第一步:初始化S-Box

具体过程如下图所示,其实就是从0~255填充满大小为256的数组

image-20250307165146193

1 初始化一个256字节的数组用来存放S盒

2 填充T盒,不满256字节就填充到256字节

第二步:KSA过程
初始化密钥

上面我们有了最初的S-Box,那么对于KSA的核心作用呢,实际上是通过密钥来置乱初始的向量,这个初始向量是一个固定值,从0~255来填充满S-Box。

image-20250307165242076

image-20250307165301092

3 交换 s[i]与s[j] i 从0开始一直到255下标结束. j是 s[i]与T[i]组合得出的下标。
这样S盒就被打乱了

image-20241226215157313

第三步:PRGA过程

这个过程是整个RC4算法的核心,通过这个过程生成我们需要的PRNG序列

image-20250307165336387

image-20250307165351088

RC4加密

RC4加密其实就是遍历数据,将数据与sbox进行异或加密,而在此之前还需要交换一次sbox的数据

交换完之后 再把s[i] + s[j]的组合当做下标再去异或.

image-20241226215205307

加密脚本

	for(dn=0;dn<datalen;dn++)
	{
		//i确保S-box的每个元素都得到处理,j保证S-box的搅乱是随机的。 1
		i=(i+1)%256;                                             
		j=(j+rc4.s_box[i])%256;
		
		//交换 s_box[i] 和 s_box[j]								2
		tmp=rc4.s_box[i];                             
		rc4.s_box[i] = rc4.s_box[j];
		rc4.s_box[j] = tmp;
		
		//交换完之后 再把s[i] + s[j]的组合当做下标再去异或.		3
		t = (rc4.s_box[i] + rc4.s_box[j]) % 256;
		data[dn] ^= rc4.s_box[t];
	 } 

魔改rc4,如修改了加密轮数为128轮,解密时所有相关轮数也需要修改成128轮;

在初始化s盒时,做了很细微的修改,

image-20241226215407712

魔改RC4

image-20250101213921657

正确

image-20250101214001462

错误

image-20250101214020071

7.凯撒密码

解密脚本积累
def caesarDecode(en_text, rot, rot_upper, rot_lower, rot_diggit):
    tmp=''
    for i in en_text:
        if(i.isupper() and rot_upper): #i是大写字母而且设置了要移大写字母
            tmp += chr((ord(i) - 65 - rot) % 26 + 65)
        elif(i.islower() and rot_lower):
            tmp += chr((ord(i) - 97 - rot) % 26 + 97)
        elif(i.isdigit() and rot_diggit):
            tmp += chr((ord(i) - 48 - rot) % 10 + 48) #10个数字字符所以%10
        else:
            tmp += i
    return tmp
 
if __name__=='__main__':
    en_text = "EmBmP5Pmn7QcPU4gLYKv5QcMmB3PWHcP5YkPq3=cT6QckkPckoRG" #设置密文
    rot = 3 #设置位移数, 如果是左移设置成负数即可   正数为右移
    rot_upper = True #移大写字母
    rot_lower = True #移小写字母
    rot_diggit = True #移数字字符
    #一般只会移这三类,如果题目特殊,可以自行添加,在caesarDecode()函数中添加对应的分支判断即可
 
    en_text = caesarDecode(en_text, rot, rot_upper, rot_lower, rot_diggit)
    print(en_text)

8.16进制与字符串互换

解密脚本积累

#16进制转字符串
hex_data = '68656c6c6f2c206b6571696e6721'
print(bytes.fromhex(hex_data).decode())
 
#字符串转16进制
str = 'hello, keqing!'
print(str.encode().hex())

9.MD5加密(一般在线网站即可,but...留一手)

加密脚本积累

①直接将要加密的内容赋值给flag变量

②如果要加密成大写字母形式,使用upper()函数

import hashlib
flag = 'ddsssddddsssdssdddddsssddddsssaassssdddsddssddwddssssssdddssssdddss'
md5 = hashlib.md5() ## 创建md5对象
md5.update(flag.encode()) #更新md5对象的信息,必须传入bytes类型的数据(b'xxx'格式)
print(md5.hexdigest()) #获取加密后的内容    
print(md5.hexdigest().upper()) #如果要大写形式,加上upper()      小写就去掉.upper

10.TEA

https://blog.csdn.net/liKeQing1027520/article/details/141287289

1.特征识别

delta和sum 将密文分成两部分

2.解题脚本积累

需要做什么

①根据题目具体的tea魔改,修改对应的delta值、sum初始值、三行经典特征加密代码

②分析题目,找到密钥(由4个32位无符号整数组成)

③找到密文,密文出现的方式因题目迥异,但可以肯定的是单次调用tea解密时只传入2个32位无符号整数

④按脚本main函数中的注释提示,处理好密钥和密文,输出部分的固定不动(除非明文要的不是字符,那你具体分析),运行即可获得明文

#include <stdint.h>
#include <stdio.h>
void tea_enc(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1];  // v0、v1分别是明文的左、右半部分
    uint32_t sum = 0;               // sum用作加密过程中的一个累加变量
    uint32_t delta = 0xd33b470;     //作为sum每次累加的变化值,题目中往往会修改此值
    for (int i = 0; i < 32; i++) {  // tea加密进行32轮
        //以下3行是核心加密过程,题目中往往会对部分细节做出修改(但由于异或的对称性质,根本不需要记,写解密函数时照抄就行了)
        sum += delta;
        v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
        v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
    }
    // v0和v1只是加密的临时变量,因此加密后的内容要还给v数组
    v[0] = v0;
    v[1] = v1;
}
void tea_dec(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1];  // v0、v1分别是密文的左、右半部分
    uint32_t delta = 0xd33b470;     //作为sum每次累加的变化值,题目中往往会修改此值
    uint32_t sum = 32 * delta;      //此处需要分析32轮加密结束后sum的值与delta的变化, 以此处加密为例子,32轮每次sum+=delta,因此最后sum=32*delta
    for (int i = 0; i < 32; i++) {  // tea加密进行32轮
        //根据加密时的顺序颠倒下面3行的顺序,将加法改为减法(异或部分都是整体,不用管),就是逆向解密过程
        v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
        v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
        sum -= delta;
    }
    // 因此解密后的内容要还给v数组
    v[0] = v0;
    v[1] = v1;
}
 
int main() {
    // k为加解密密钥,4个32位无符号整数,密钥长度为128位
    uint32_t k[4] = {1, 2, 3, 4};
    // v为要加解密的数据,两个32位无符号整数
    //但是稍微难一点点的都不会直接加密两个uint32_t,除非签到题。像这里的例子就是给了32个uint8_t
    //(常见的题目还有给几个uint32_t的,其实无非是拆开写成0x17、0x65...和连着写成0x1765的区别)
    //在后面的循环里每次传两组,每组4个组成uint32_t用于tea算法
    int8_t input[33] = {0x17, 0x65, 0x54, 0x89, 0xed, 0x65, 0x46, 0x32, 0x3d, 0x58, 0xa9, 0xfd, 0xe2, 0x5e, 0x61, 0x97,
                        0xe4, 0x60, 0xf1, 0x91, 0x73, 0xe9, 0xe9, 0xa2, 0x59, 0xcb, 0x9a, 0x99, 0xec, 0xb1, 0xe1, 0x7d};
 
    for (int i = 0; i < 32; i += 8) {
        //每组4个组成uint32_t用于tea算法,tea算法每次加解密操作的v一定是两个uint_32,至于怎么传入两个uint_32,题目有各种呈现方式,需要做题者自行分析
        uint32_t v[2] = {*(uint32_t*)&input[i], *(uint32_t*)&input[i + 4]};
        tea_dec(v, k);
        
        // tea输出字符的固定算法,外层循环两次是因为明文分为左半和右半两个uint32_t
        // 内层循环4次是因为一个字符占1个字节即8位,每次&0xff可以摘下最后1字节打印出对应ASCII字符,然后>>8准备下一字节
        for (int j = 0; j < 2; j++) {
            for (int k = 0; k < 4; k++) {
                printf("%c", v[j] & 0xff);
                v[j] >>= 8;
            }
        }
    }
    return 0;
}

原题改解密脚本

__int64 __fastcall sub_1400010B4(unsigned int *a1, long long *a2)
{
  int v2; // ebx
  long long v3; // r11d
  int v4; // edi
  int v5; // esi
  int v6; // ebp
  unsigned int v7; // r9d
  __int64 v8; // rdx
  unsigned int v9; // r10d
  __int64 result; // rax

  v2 = *a2;
  v3 = 0;
  v4 = a2[1];
  v5 = a2[2];
  v6 = a2[3];
  v7 = *a1;
  v8 = 32;
  v9 = a1[1];
  for(int i = 0 ; i < 32 ; i ++ )
  {
  	v3-=0x543210DD;
  }
  do
  {
    result = v3 + v7;
	v9 -= result ^ (v5 + 16 * v7) ^ (v6 + (v7 >> 5));
    v7 -= (v3 + v9) ^ (v2 + 16 * v9) ^ (v4 + (v9 >> 5));
    v3 += 1412567261;
    --v8;
  }
  while ( v8 );
  *a1 = v7;
  a1[1] = v9;
  return result;
}

int main ()
{
	long long a2[4]={0x12345678,0x23456789,0x34567890,0x45678901};
	unsigned int Buf2[8]={0};
	Buf2[0] = 0x2E63829D;
	Buf2[1] = 0xC14E400F;
	Buf2[2] = 0x9B39BFB9;
	Buf2[3] = 0x5A1F8B14;
	Buf2[4] = 0x61886DDE;
	Buf2[5] = 0x6565C6CF;
	Buf2[6] = 0x9F064F64;
	Buf2[7] = 0x236A43F6;
	for(int i = 0 ; i < 8 ; i+=2 )
	{
		sub_1400010B4(Buf2+i,a2);
	}
	for(int i = 0 ; i < 8 ; i++ )
	{
		unsigned int tmp = Buf2[i];
		for(int j = 0 ; j< 4 ; j++ )
		{
			printf("%c",tmp%0x100);
			tmp/=0x100;
		}
	}
	printf("k}");
}

11.异或XOR

脚本积累

flag=[35, 21, 37, 83, 8, 26, 89, 56, 18, 106, 57, 49, 39, 91, 11, 19, 19, 8, 92, 51, 11, 53, 97, 1, 81, 31, 16, 92]
key='''Flag{This_a_Flag}'''
res=[]
for i in range(len(flag)):
    res+=[flag[i]^ord(key[i%len(key)])]
print(res)
for i in res:
    print(chr(i),end="")

12.upx改壳

在010editor中将 0 1 !前改成upx

即55 50 58

image-20250117204448908

image-20250117204501324

13.SM4

解密脚本积累

from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
import base64
 
class SM4Utils:
    secret_key = b''
 
    def __init__(self, secret_key):
        self.secret_key = secret_key
 
    # 加密方法
    def encryptData_ECB(self, plain_text):
        # 创建 SM4对象
        crypt_sm4 = CryptSM4()
        # 设置key
        crypt_sm4.set_key(self.secret_key, SM4_ENCRYPT)
        # 调用加密方法加密(十六进制的bytes类型)
        encrypt_value = crypt_sm4.crypt_ecb(plain_text)
        # 用base64.b64encode转码(编码后的bytes)
        cipher_text = base64.b64encode(encrypt_value)
        # 返回加密后的字符串
        return cipher_text.decode('utf-8', 'ignore')
 
    def decryptData_ECB(self, cipher_text):
        crypt_sm4 = CryptSM4()
        crypt_sm4.set_key(self.secret_key, SM4_DECRYPT)
        # 将转入参数base64.b64decode解码成十六进制的bytes类型
        byt_cipher_text = base64.b64decode(cipher_text)
        # 调用加密方法解密,解密后为bytes类型
        decrypt_value = crypt_sm4.crypt_ecb(byt_cipher_text)
        return decrypt_value.decode('utf-8', 'ignore')
 
    def encryptData_CBC(self, iv, key, plain_text):
        # 创建 SM4对象
        crypt_sm4 = CryptSM4()
        # 设置key
        crypt_sm4.set_key(key, SM4_ENCRYPT)
        # 调用加密方法加密(十六进制的bytes类型)
        encrypt_value = crypt_sm4.crypt_cbc(iv, plain_text)
        # 用base64.b64encode转码(编码后的bytes)
        cipher_text = base64.b64encode(encrypt_value)
        # 返回加密后的字符串
        return cipher_text.decode('utf-8', 'ignore')
 
    def decryptData_CBC(self, iv, key, cipher_text):
        crypt_sm4 = CryptSM4()
        # secret_key = b"K0qBoObS8kmfjemG"
        crypt_sm4.set_key(self.secret_key, SM4_DECRYPT)
        # 将转入参数base64.b64decode解码成十六进制的bytes类型
        byt_cipher_text = base64.b64decode(cipher_text)
        # 调用加密方法解密,解密后为bytes类型
        decrypt_value = crypt_sm4.crypt_cbc(iv, byt_cipher_text)
        return decrypt_value.decode('utf-8', 'ignore')
 
 
if __name__ == '__main__':
    key = b'JeF8U9wHFOMfs2Y8'
    SM4_Utils = SM4Utils(key)
 
    plain_text = b'kYEI25N30vqgSURbd5vEmz/yHt1SMH5YtoRKdXvuPtHrbuuaeOYgeyb1p0fgaq4D'
    iv = b'UISwD9fW6cFh9SNS' #在cbc模式下才需要设置初始化向量iv,ecb模式没有这个值,分析题目用的是什么模式再决定要调用的解密方法
    print(SM4_Utils.decryptData_CBC(iv, key, plain_text))

14.迷宫

贴大佬链接https://blog.csdn.net/liKeQing1027520/article/details/138583198?spm=1001.2014.3001.5501

一,将数据处理成二维列表
一)题目提供的是字符组成的迷宫

img

(二)题目提供的是整数(通常是0和1)组成的迷宫

img

三)脚本一:处理迷宫为二维列表

#str为ida中使用快捷键[shift+e]提取到的数据, 如果提取的是string literal则加上引号视作字符串,如果是C array(decimal)则加上中括号视作列表
str = "字符串"/[一维列表] 
s = 0 #s用作索引访问str, 供下面tmp列表取值
 
#分析题目后设置迷宫的行列
row =  #设置二维迷宫行数
col =  #设置二维迷宫列数
 
maze = []
for i in range(row):
    tmp = []
    for j in range(col):
        tmp.append(str[s])
        s+=1
    maze.append(tmp) #凑一行添加一行到迷宫中
print(maze)

二,调整下面这个脚本的参数获得路径

遇到二维四向迷宫(有时候分析ida反汇编可能是一维数组,但实际一定是一维数组当作二维用,所以需要上面的脚本来处理成二维列表)

需要调整的参数有:①二维列表迷宫,②起点、终点与障碍特征(若题目给出的数据的起点终点无特征, 手动添加特征即可, 障碍通常是1但有的题目也有可能是0或其它字符如'#')

一)脚本二:获得迷宫路径

from collections import deque
 
#设置二维四向迷宫, 如果题目是多个小迷宫问题, 拆分多次调用脚本获取路径即可
maze = 二维列表迷宫
path_len = 0x7fffffff#如果题目未给出终点坐标,则一定会指定路径的长度,在此处修改路径长度,否则请保留path_len的极大值
 
#进行BFS寻找路径
def bfs(start, end, barrier):
    directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 定义四个方向的移动
    for i in range(len(maze)):#获取起点和终点在列表中的索引
        for j in range(len(maze[i])):
            if(maze[i][j] == start):
                start = (i, j)
            if(maze[i][j] == end):
                end = (i, j)
    #以下均是bfs算法套路
    queue = deque()
    queue.append((start, [start]))  # (当前位置, 路径)
    visited = set()
    visited.add(start)
    while queue:
        position, path = queue.popleft()
        if position == end:
            return path
        elif len(path)==path_len:
            return path
        for d in directions:
            next_position = (position[0] + d[0], position[1] + d[1])
            if 0 <= next_position[0] < len(maze) and 0 <= next_position[1] < len(maze[0]) and \
               maze[next_position[0]][next_position[1]] != barrier and next_position not in visited:
                queue.append((next_position, path + [next_position]))
                visited.add(next_position)
    return None
 
#执行BFS搜索并打印结果
if __name__ == '__main__':
    #maze[起点x坐标][起点y坐标] = 'S' #如果题目给了起点终点的坐标,在这里直接给起点和终点添加特征
    #maze[终点x坐标][终点y坐标] = 'E' 
    
    path = bfs('S', 'E', 1) #bfs函数传入参数代表起点、终点、障碍的特征(若题目给出的数据无特征, 手动添加特征即可, 通常障碍是1也有可能是0或其它字符如'#')
    print("移动路径坐标:", path)
    print("移动路径方位:{", end='')
    for i in range(1 ,len(path)):
        x1, y1, x2, y2 = path[i - 1][0], path[i - 1][1], path[i][0], path[i][1]
        if(x1 > x2):#上
            print("w", end='')
        elif(x1 < x2):#下
            print("s", end='')
        elif(y1 > y2):#左
            print("a", end='')
        elif(y1 < y2):#右
            print("d", end='')
    print('}')

动态调试

CSDN_1733453818694

汇编窗口 寄存器窗口

信息窗口

数据窗口 堆栈窗口

rand随机数

通常通过获取出题的时间戳进行爆破

posted @ 2025-05-30 10:15  ethan——1231  阅读(57)  评论(0)    收藏  举报