《加密与解密》第六章 个人阅读笔记 (中)

开始今天的学习吧
请添加图片描述


对称加密算法

对称加密算法是基于这样的假设:双方使用相同的加密方法和解密方法。

算法的安全性依赖于以下两点

  • 加密算法本身足够强:即使攻击者获取到了密文,在没有密钥的情况下,攻击者无法轻易分析出明文
  • 密钥的保密性

比较反直觉的是,将加密算法的保密并不一定能使整个系统更安全。因为这个保密的算法没有经过各位逆向高手,数学专家的验证。验证这个算法的唯一方式就是将这个算法公开,让其他的密码员分析

对称加密算法的使用范围很广,比如简单的移位密码和凯撒密码,或者是IDEA,TEA,AES这些比较“高级”的算法

顺便吐槽一下,大一的离散数学实验和计算机导论实验都让我们编程实现凯撒密码。真的太敷衍了。。。。


RC4流密码

这个算法和MD5 , SHA1相比没有很复杂
可以参考这两篇博客学习:

打算复现 2018 9月安恒杯的NewDriver赛题,这是我找到的安恒杯题目仓库Anheng_cup_month , 但是仓库已经不再维护了。

复现安恒杯的NewDriver题目

参考文章:

ida打开main函数
在这里插入图片描述
这个题目有三个关键调用sub_401160 , sub_401000 , sub_4010E0

sub_401000 , sub_4010E0函数有很明显的rc4特征(废话人家就是rc4算法),我们可以依靠这些特征快速识别rc4:

  • 生成s盒时,两个循环都进行了256次
  • 很多语句会mod256

下图是sub_4010E0函数和《加密与解密》中PRGA的对比,可以发现结构一样,进一步验证了这两个函数是rc4加密函数的猜想
在这里插入图片描述
接着分析sub_401160函数,这段算法的特征也很明显:

  • 将3个字节分成4组,每一组6个二进制位。 将6个二进制位前面填充0,得到8个二进制位,作为替换表的下标
  • byte_402138是替换表,有64个字符
    在这里插入图片描述在这里插入图片描述
    所以这是更改了替换表的base64加密函数

考虑写出注册机,需要手写rc4函数,第一步就是分析在ida中,表示s盒和t盒的变量分别是谁,所以分析sub_401000函数
在这里插入图片描述
-v4表示ia3 表示key的长度

  • v6[i]指的就是a1[v4] , 程序使用0-255初始化a1数组,所以它就是s盒
  • v6是t盒,因为只有t盒需要用到i%keylength 。动态调试发现它的值就是假的flagflag{this_is_not_the_flag_hahaha}

其实我们可以直接用动态调试找到KSA变换后的s盒a1 , 这么做就不用通过t盒手动计算变换后s盒的值了。接下来仿照第三个加密函数,写出脚本

key=[0x66, 0x32, 0xCA, 0xA0, 0xBF, 0x98, 0x2D, 0x76, 0xF1, 0x59, 0x2A, 0x4A, 0xF4, 0x30, 0xAD, 0xD2, 0x1D, 0x02, 0xD8, 0x23, 0x89, 0x5D, 0x83, 0x38, 0x09, 0xF2, 0x74, 0x65, 0x40, 0x19, 0xC6, 0xDD, 0x18, 0xD3, 0x8F, 0x6C, 0x8B, 0xC0, 0xC5, 0x54, 0x2E, 0x81, 0x10, 0xC4, 0x26, 0x56, 0x5F, 0x53, 0x80, 0x43, 0x27, 0x62, 0xEA, 0x3D, 0xE6, 0x00, 0xE7, 0xB7, 0x50, 0x94, 0x90, 0x4C, 0x3F, 0x9D, 0x07, 0xE0, 0xA3, 0x9C, 0x4E, 0x0F, 0x9F, 0xFE, 0x5B, 0x8E, 0xDE, 0x88, 0x72, 0x2F, 0xC1, 0x67, 0x31, 0x70, 0x8D, 0xFD, 0xBE, 0x64, 0xC3, 0xBD, 0x6B, 0x7A, 0xCF, 0x0C, 0x34, 0x1F, 0x6F, 0x01, 0xF0, 0x7C, 0x5E, 0xA4, 0x1E, 0x49, 0x8C, 0x75, 0x1C, 0xE3, 0x20, 0x48, 0x28, 0x79, 0xA5, 0x7F, 0xF5, 0xEC, 0x4F, 0x78, 0x58, 0x11, 0xF7, 0xCD, 0x91, 0x13, 0xFC, 0xB8, 0x2C, 0x04, 0xEE, 0xD5, 0x08, 0x44, 0xA9, 0xE1, 0xB1, 0x42, 0x84, 0x29, 0xA7, 0x47, 0x97, 0x7E, 0xE8, 0xB3, 0x60, 0x0B, 0xF9, 0x4B, 0x3C, 0x77, 0x17, 0x03, 0x82, 0x69, 0x87, 0xD4, 0x95, 0x1A, 0x33, 0x25, 0x6E, 0xCC, 0xD6, 0xBB, 0x99, 0xB0, 0x85, 0x41, 0xB2, 0x0D, 0xDB, 0x35, 0x3B, 0x5C, 0xF8, 0xED, 0x9E, 0xA6, 0x96, 0x39, 0x63, 0x0A, 0x1B, 0x93, 0x21, 0x46, 0x12, 0xD0, 0xB4, 0x22, 0x51, 0xC9, 0x61, 0xD1, 0x2B, 0xAA, 0x45, 0x06, 0x05, 0xCE, 0xFA, 0x92, 0x68, 0xAB, 0x36, 0xDA, 0xC8, 0xE2, 0x37, 0xD9, 0xA2, 0x5A, 0xD7, 0x6A, 0xB5, 0xFF, 0xE9, 0xBA, 0x52, 0x15, 0xF6, 0xBC, 0x9A, 0xB6, 0xEF, 0x6D, 0xCB, 0x4D, 0xAE, 0xE4, 0xA1, 0xAC, 0xEB, 0x0E, 0x71, 0x7B, 0xF3, 0x24, 0xC2, 0xFB, 0x7D, 0x86, 0x55, 0xAF, 0x3A, 0xDF, 0x3E, 0x14, 0xB9, 0x9B, 0x16, 0xDC, 0x73, 0x57, 0xE5, 0xC7, 0x8A, 0xA8, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x74, 0x68, 0x69, 0x73, 0x5F, 0x69, 0x73, 0x5F, 0x6E, 0x6F, 0x74, 0x5F, 0x74, 0x68, 0x65, 0x5F, 0x66, 0x6C, 0x61, 0x67, 0x5F, 0x68, 0x61, 0x68, 0x61, 0x68, 0x61, 0x7D]
key2=[0x20, 0xC3, 0x1A, 0xAE, 0x97, 0x3C, 0x7A, 0x41, 0xDE, 0xF6, 0x78, 0x15, 0xCB, 0x4B, 0x4C, 0xDC, 0x26, 0x55, 0x8B, 0x55, 0xE5, 0xE9, 0x55, 0x75, 0x40, 0x3D, 0x82, 0x13, 0xA5, 0x60, 0x13, 0x3B, 0xF5, 0xD8, 0x19, 0x0E, 0x47, 0xCF, 0x5F, 0x5E, 0xDE, 0x9D, 0x14, 0xBD]
key3=[]
v3=0
v4=0
v6=0
s=""
#这里直接仿写IDA中第三个加密函数逻辑即可
for v3 in range(44):
  v3+=1
  v6=key[v3]
  v4=(v6+v4)%256
  key[v3]=key[v4]
  key[v4]=v6
  key3.append(key[(key[v3]+v6)%256])
for i in range(44):
  key2[i]^=key3[i]
for i in key2:
  s+=chr(i)
print s

我直接使用了其他人的exp,下面是解释:
在这里插入图片描述
这就是下面的代码中,result值的来源:由eax寄存器传递。我们最终需要求出a2[i]的值,也就是经过魔改的base64加密后的值。
在这里插入图片描述
通过上面的python脚本,算出值为ZeptZ3l5UHQra25nd19yYzMrYR5wX2Jtc2P2VF9gYNM9

在在线网站上解密,得到flag
在这里插入图片描述
flag{y0u_know_rc4_and_base64_ha$}


tea算法

这就是tea算法的具体代码

#define DELTA 0x9981abcd

void tea_encrypt(unsigned int* v, unsigned int* key) {
  unsigned int y = v[0], z = v[1], sum = 0;
  for (size_t i = 0; i < 32; i++) { 
    y += ((z<<4)+key[0])^(z+sum) ^((z>>5) + key[1]);
    sum += DELTA; //累加Delta的值
    z += ((y << 4) + key[2]) ^ (sum +y)+((y>>5)+key[3]); //
  }
  v[0] = y;
  v[1] = z;
}

void tea_decrypt(unsigned int* v, unsigned int* key) {
  unsigned int y = v[0], z = v[1], sum = 0;
  sum = DELTA * 32; //32次迭代累加后delta的值
  for (size_t i = 0; i < 32; i++) {
    z -=((y<<4) + key[2])^(y+sum)^((y>>5) + key[3]);
    sum -= DELTA;
    y -= ((z<<4)+key[0])^(z+sum)^((z>>5)+key[1]);
  }
  v[0] = y;
  v[1] = z;
}

tea算法的特点:

  • 密钥长度为128位,分为四组每一组32位参与运算
  • 明文64位,分成32位的两部分参与运算

按照ctf wiki的推荐,我找到了2018 0ctf的Quals milk-tea题目,虽然尝试复现,但我对windows编程了解不够深入,最终没成功分析明白。分析过程我放在后记里了。

这是我找到的放ctf赛题的github仓库CTF Challenges
这是找到的wp 0CTF Quals 2018 Writeup


分析TeaKeyGenMe程序

程序使用MD5加密
在这里插入图片描述
一样通过抓关键特征 + 线上验证的方式分析MD5算法

00401276  |.  8B4C24 2C     mov ecx,dword ptr ss:[esp+0x2C]          ;  低64位的密码
0040127A  |.  8B5424 30     mov edx,dword ptr ss:[esp+0x30]          ;  高64位的密码
0040127E  |.  8D8424 BC0100>lea eax,dword ptr ss:[esp+0x1BC]
00401285  |.  894C24 10     mov dword ptr ss:[esp+0x10],ecx
00401289  |.  50            push eax                                 ;  这里是缓冲区
0040128A  |.  895424 18     mov dword ptr ss:[esp+0x18],edx
0040128E  |.  E8 ED000000   call TEAKeyGe.00401380                   ;  初始化md5
00401293  |.  8B4C24 2C     mov ecx,dword ptr ss:[esp+0x2C]          ;  用户名字符个数
00401297  |.  8D9424 E00200>lea edx,dword ptr ss:[esp+0x2E0]         ;  用户名
0040129E  |.  51            push ecx
0040129F  |.  8D8424 C40100>lea eax,dword ptr ss:[esp+0x1C4]         ;  密码
004012A6  |.  52            push edx
004012A7  |.  50            push eax
004012A8  |.  E8 03010000   call TEAKeyGe.004013B0                   ;  密码的后一个字节放用户名长度*8 , 后9个字节放用户名
004012AD  |.  8D8C24 CC0100>lea ecx,dword ptr ss:[esp+0x1CC]         ;  上一个函数的结果
004012B4  |.  8D9424 040100>lea edx,dword ptr ss:[esp+0x104]         ;  这应该是缓冲区
004012BB  |.  51            push ecx                                 ;  19f5f4
004012BC  |.  52            push edx
004012BD  |.  E8 9E010000   call TEAKeyGe.00401460                   ;  对用户名进行md5加密
004012C2  |.  8B8C24 100100>mov ecx,dword ptr ss:[esp+0x110]

当输入用户名为abcdef,并且调用401460函数后,加密结果如下图
在这里插入图片描述

在这里插入图片描述
继续分析

004012EE  |.  8D5424 28     lea edx,dword ptr ss:[esp+0x28]
004012F2  |.  51            push ecx                                 ;  用户名进行md5加密的结果,并去掉最后32位
004012F3  |.  52            push edx                                 ;  密码+用户名进行MD5加密的前96位, 一共160位
004012F4  |.  894424 44     mov dword ptr ss:[esp+0x44],eax
004012F8  |.  E8 03FDFFFF   call TEAKeyGe.00401000
004012FD  |.  8B4424 30     mov eax,dword ptr ss:[esp+0x30]
{
	00401000  /$  83EC 0C       sub esp,0xC
	00401003  |.  53            push ebx
	00401004  |.  55            push ebp
	00401005  |.  56            push esi
	00401006  |.  8B7424 20     mov esi,dword ptr ss:[esp+0x20]
	0040100A  |.  8B4C24 1C     mov ecx,dword ptr ss:[esp+0x1C]
	0040100E  |.  57            push edi
	0040100F  |.  8B7E 04       mov edi,dword ptr ds:[esi+0x4]
	00401012  |.  33D2          xor edx,edx
	00401014  |.  8B01          mov eax,dword ptr ds:[ecx]               ;  eax为前32位的序列号
	00401016  |.  8B49 04       mov ecx,dword ptr ds:[ecx+0x4]           ;  ecx为后32位的序列号
	00401019  |.  897C24 10     mov dword ptr ss:[esp+0x10],edi          ;  待加密的信息是序列号 密钥为md5后的用户名
	0040101D  |.  8B3E          mov edi,dword ptr ds:[esi]
	0040101F  |.  897C24 24     mov dword ptr ss:[esp+0x24],edi
	00401023  |.  8B7E 0C       mov edi,dword ptr ds:[esi+0xC]
	00401026  |.  8B76 08       mov esi,dword ptr ds:[esi+0x8]
	00401029  |.  897C24 18     mov dword ptr ss:[esp+0x18],edi
	0040102D  |.  897424 14     mov dword ptr ss:[esp+0x14],esi
	00401031  |.  BF 20000000   mov edi,0x20                             ;  edi= 32
	00401036  |>  8B5C24 24     /mov ebx,dword ptr ss:[esp+0x24]
	0040103A  |.  8B6C24 10     |mov ebp,dword ptr ss:[esp+0x10]
	0040103E  |.  8BF1          |mov esi,ecx
	00401040  |.  81EA 4786C861 |sub edx,0x61C88647                      ;  密钥调度常数
	00401046  |.  C1E6 04       |shl esi,0x4
	00401049  |.  03F3          |add esi,ebx
	0040104B  |.  8BD9          |mov ebx,ecx
	0040104D  |.  C1EB 05       |shr ebx,0x5
	00401050  |.  03DD          |add ebx,ebp
	00401052  |.  8B6C24 18     |mov ebp,dword ptr ss:[esp+0x18]         ;  TEAKeyGe.004012FD
	00401056  |.  33F3          |xor esi,ebx
	00401058  |.  8D1C0A        |lea ebx,dword ptr ds:[edx+ecx]
	0040105B  |.  33F3          |xor esi,ebx
	0040105D  |.  8B5C24 14     |mov ebx,dword ptr ss:[esp+0x14]
	00401061  |.  03C6          |add eax,esi
	00401063  |.  8BF0          |mov esi,eax
	00401065  |.  C1E6 04       |shl esi,0x4
	00401068  |.  03F3          |add esi,ebx
	0040106A  |.  8BD8          |mov ebx,eax
	0040106C  |.  C1EB 05       |shr ebx,0x5
	0040106F  |.  03DD          |add ebx,ebp
	00401071  |.  33F3          |xor esi,ebx
	00401073  |.  8D1C02        |lea ebx,dword ptr ds:[edx+eax]
	00401076  |.  33F3          |xor esi,ebx
	00401078  |.  03CE          |add ecx,esi
	0040107A  |.  4F            |dec edi
	0040107B  |.^ 75 B9         \jnz short TEAKeyGe.00401036
	0040107D  |.  8B5424 20     mov edx,dword ptr ss:[esp+0x20]
	00401081  |.  5F            pop edi
	00401082  |.  5E            pop esi
	00401083  |.  5D            pop ebp
	00401084  |.  8902          mov dword ptr ds:[edx],eax
	00401086  |.  894A 04       mov dword ptr ds:[edx+0x4],ecx
	00401089  |.  5B            pop ebx
	0040108A  |.  83C4 0C       add esp,0xC
	0040108D  \.  C3            retn
}

可以从以下几个特征识别401000函数为tea加密函数

  • 00401040 |. 81EA 4786C861 |sub edx,0x61C88647 执行完此处代码,edx的值为0x9e3779b7
  • 循环执行32次 , 每一次循环esi左移4位,ebx右移5位

如果担心出题人魔改tea,那还是仔细分析每一句代码吧 😦
识别出tea算法还不够,需要找到待加密的数据和密钥数据。根据明文是64位,密钥是128位的规则,很容易识别出待加密的数据为用户输入的序列号,密钥为经过md5加密后的用户名

为什么输入的字符有16个,而最终的位数只有64位呢。原因就是程序使用一个字节存下两个字符,4011FE之前的代码保证序列号是十六进制的字符,因此不用考虑放不下的问题
在这里插入图片描述

004012FD  |.  8B4424 30     mov eax,dword ptr ss:[esp+0x30]          ;  tea加密结果
00401301  |.  8B4C24 34     mov ecx,dword ptr ss:[esp+0x34]
00401305  |.  83C4 20       add esp,0x20
00401308  |.  894424 2C     mov dword ptr ss:[esp+0x2C],eax          ;  用户名md5结果
0040130C  |.  894C24 30     mov dword ptr ss:[esp+0x30],ecx
00401310  |.  33C0          xor eax,eax
00401312  |>  8A9404 F40000>/mov dl,byte ptr ss:[esp+eax+0xF4]
00401319  |.  8A5C04 2C     |mov bl,byte ptr ss:[esp+eax+0x2C]
0040131D  |.  32DA          |xor bl,dl                               ;  tea加密结果与用户名MD5结果前64位异或
0040131F  |.  885C04 2C     |mov byte ptr ss:[esp+eax+0x2C],bl
00401323  |.  40            |inc eax
00401324  |.  83F8 08       |cmp eax,0x8
00401327  |.^ 7C E9         \jl short TEAKeyGe.00401312
00401329  |.  8D4424 2C     lea eax,dword ptr ss:[esp+0x2C]
0040132D  |.  8D8C24 FC0000>lea ecx,dword ptr ss:[esp+0xFC]          ;  用户名md5后64位
00401334  |.  50            push eax                                 ; /String2 = 66DBEC8D ???
00401335  |.  51            push ecx                                 ; |String1 = C5F56009 ???
00401336  |.  FF15 00704000 call dword ptr ds:[<&KERNEL32.lstrcmpA>] ; \lstrcmpA

总结一下程序加密的流程:用户输入用户名和序列号。用户名进行MD5哈希,哈希结果作为tea算法的密钥,用户输入的序列号作为tea算法待加密的数据。tea算法得到的结果与MD5哈希值的前64位数据异或,接着与MD5哈希值的后64位比较,如果不相等,则验证失败。

尝试写出注册机。虽然结果是错的,但我找不出问题出在哪里 😭 😭 😭 😭 😭 😭 😭

import hashlib
from ctypes import *

def encrypt(v, k):
    v0 = c_uint32(v[0])
    v1 = c_uint32(v[1])
    sum1 = c_uint32(0)
    delta = 0x9e3779b9
    
    for i in range(32):
        sum1.value += delta
        v0.value += ((v1.value << 4) + k[0]) ^ (v1.value + sum1.value) ^ ((v1.value >> 5) + k[1])
        v1.value += ((v0.value << 4) + k[2]) ^ (v0.value + sum1.value) ^ ((v0.value >> 5) + k[3])
        
    return v0.value, v1.value

def decrypt(v, k):
    v0 = c_uint32(v[0])
    v1 = c_uint32(v[1])
    delta = 0x9e3779b9
    sum1 = c_uint32(delta << 5)
    
    for i in range(32):
        v1.value -= ((v0.value << 4) + k[2]) ^ (v0.value + sum1.value) ^ ((v0.value >> 5) + k[3])
        v0.value -= ((v1.value << 4) + k[0]) ^ (v1.value + sum1.value) ^ ((v1.value >> 5) + k[1])
        sum1.value -= delta
        
    return v0.value, v1.value

name = input("请输入用户名: ")
hash_result = hashlib.md5(name.encode()).digest()
tea_result = [int.from_bytes(hash_result[:4], 'little'), int.from_bytes(hash_result[4:8], 'little')]
key_list = [int.from_bytes(hash_result[i:i+4], 'little') for i in range(8, 24, 4)]

result = decrypt(tea_result, key_list)

print(''.join([f'{x:08x}' for x in result]))

本文结束


后记

照例meme时间

某些网站上,盗录综艺的画质be like:
在这里插入图片描述


在做不出ctf题目时,可以做出如下动作,给自己加油打气
在这里插入图片描述
虽然解决不了问题,但可以让队友看笑话啊

参考资料:《是!尚先生》
在这里插入图片描述


尝试分析mike tea的过程

如果v10数组 的值等于v11数组的值,验证成功。往上翻代码,发现v10就是我们输入的Str , 但是暂时没有找到对Str进行加密的代码
在这里插入图片描述
为了判断Str有没有被加密过,我尝试使用ida进行动态调试,但是报错“请求的操作需要提升”,所以我选择od进行调试
在这里插入图片描述
首先要在od中定位这条代码mov ecx , offset Str 。ida的起始地址为00401000 , OD的起始地址为00A61000 。od上述代码对应地址为004019B0 - 00401000 + 00A61000 = 00A619B0
在这里插入图片描述

在这里插入图片描述
00A6683C存放我们输入milktea coupon的值,以unicode的形式存放。程序调用A611F0函数后,该内存发生了变化。
在这里插入图片描述
继续调试,发现调用ReadFile函数导致该内存改变,找到这段代码
在这里插入图片描述
从FileA文件中读取的数据被放在缓冲区中。那FileA文件里面的数据是从哪里来的呢?好像是管道的数据,那管道的数据从何而来。。。不懂了


扯一下最近的学习情况

  • 破解了两个creakme程序
  • 浅浅学习了栈溢出漏洞
  • 做一题ctf逆向题,里面用到了C#的加密函数,还需要修改PE文件头,识别AES加密函数。被虐后还是滚回来继续看《加密与解密》了:(
  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值