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

前言

粗略浏览了第六章,虽然书中详细给出各种加密算法的原理,但是对于没有密码学基础的我来说,还是难以理解:(

慢慢看吧
请添加图片描述
(冲浪发现的视频,作者见左上角水印)


单向散列算法(哈希算法)

哈希算法具有以下的特征:

  1. 往函数输入相同的值时,函数的输出相同
  2. 如果往函数中输入不同的值,得到的结果(通常)差别很大。
  3. 算法不可逆,意味着没有办法从算法的输出逆推得到输入

在这里插入图片描述
(来源:cs106b Slides17 hash1)

由于哈希算法的不可逆性,服务端通常使用哈希算法+盐值的方式存放密码,大大增加了解密者得出原密码的难度。

之前看过这种存放方式: md5(md5(password)+salt)

md5算法

一些资料

说实话,我目前能看懂书中介绍的MD5计算流程,但是要我分析代码时能反应出这是md5算法。。。。不太行

分析MD5KeyGenMe.exe程序

尝试运行程序
在这里插入图片描述
用exeinfo pe查壳,使用Krypto ANALyzer插件分析程序使用的加密算法。
在这里插入图片描述

在这里插入图片描述
发现是md5加密,使用OD进一步分析程序。
GetDlgItemTexr函数下断点,Name输入123456, Serial Number输入abcdef

0040114A  |.  68 C9000000   push 0xC9                                ; /Count = C9 (201.)
0040114F  |.  A4            movs byte ptr es:[edi],byte ptr ds:[esi] ; |
00401150  |.  8BB424 E00300>mov esi,dword ptr ss:[esp+0x3E0]         ; |
00401157  |.  894424 21     mov dword ptr ss:[esp+0x21],eax          ; |
0040115B  |.  51            push ecx                                 ; |Buffer = 0019F64C
0040115C  |.  66:894424 29  mov word ptr ss:[esp+0x29],ax            ; |
00401161  |.  68 E8030000   push 0x3E8                               ; |ControlID = 3E8 (1000.)
00401166  |.  56            push esi                                 ; |hWnd = 000808B8 ('MD5 *KeyGenMe*',class='#32770')
00401167  |.  885C24 20     mov byte ptr ss:[esp+0x20],bl            ; |
0040116B  |.  884424 33     mov byte ptr ss:[esp+0x33],al            ; |
0040116F  |.  FFD5          call ebp                                 ; \GetDlgItemTextA
// 获取用户名
00401171  |.  8BF8          mov edi,eax
00401173  |.  3BFB          cmp edi,ebx
00401175  |.  0F84 0E010000 je MD5KeyGe.00401289
0040117B  |.  8D5424 60     lea edx,dword ptr ss:[esp+0x60]
0040117F  |.  68 C9000000   push 0xC9                                ; /Count = C9 (201.)
00401184  |.  52            push edx                                 ; |Buffer = NULL
00401185  |.  68 E9030000   push 0x3E9                               ; |ControlID = 3E9 (1001.)
0040118A  |.  56            push esi                                 ; |hWnd = 000808B8 ('MD5 *KeyGenMe*',class='#32770')
0040118B  |.  FFD5          call ebp                                 ; \GetDlgItemTextA
// 获取序列号
0040118D  |.  83F8 13       cmp eax,0x13
00401190  |.  0F85 F3000000 jnz MD5KeyGe.00401289
// 如果序列号的长度不等于19,跳转

我将序列号改为1234567891234567891 , 满足序列号长度为19的要求,重新运行程序。

00401196  |.  8A4C24 64     mov cl,byte ptr ss:[esp+0x64]
0040119A  |.  B0 2D         mov al,0x2D
0040119C  |.  3AC8          cmp cl,al
0040119E  |.  0F85 E5000000 jnz MD5KeyGe.00401289
004011A4  |.  384424 69     cmp byte ptr ss:[esp+0x69],al
004011A8  |.  0F85 DB000000 jnz MD5KeyGe.00401289
004011AE  |.  384424 6E     cmp byte ptr ss:[esp+0x6E],al
004011B2  |.  0F85 D1000000 jnz MD5KeyGe.00401289

右键[esp+0x64]->数据窗口中跟随->内存地址,发现[esp+0x64]指的是序列号的第五个字节。
以此类推,这段代码验证了序列号第五,第十,第十五个字节是不是-,如果不是,验证失败。

我将序列号改为1234-6789-1234-6789,重新运行,继续分析程序

004011B8  |.  8B4C24 65     mov ecx,dword ptr ss:[esp+0x65]          ;  序列号第六个字节
004011BC  |.  8B4424 60     mov eax,dword ptr ss:[esp+0x60]          ;  序列号第一个字节
004011C0  |.  8B5424 6A     mov edx,dword ptr ss:[esp+0x6A]          ;  序列号第十一个字节
004011C4  |.  894C24 14     mov dword ptr ss:[esp+0x14],ecx
004011C8  |.  894424 10     mov dword ptr ss:[esp+0x10],eax
004011CC  |.  8B4424 6F     mov eax,dword ptr ss:[esp+0x6F]
004011D0  |.  8D8C24 280100>lea ecx,dword ptr ss:[esp+0x128]         ;  md5的框架
004011D7  |.  895424 18     mov dword ptr ss:[esp+0x18],edx
004011DB  |.  51            push ecx
004011DC  |.  894424 20     mov dword ptr ss:[esp+0x20],eax
004011E0  |.  E8 CB000000   call MD5KeyGe.004012B0
{
// 初始化MD5
    004012B0  /$  8B4424 04     mov eax,dword ptr ss:[esp+0x4]
    004012B4  |.  33C9          xor ecx,ecx
	004012B6  |.  8948 14       mov dword ptr ds:[eax+0x14],ecx
	004012B9  |.  8948 10       mov dword ptr ds:[eax+0x10],ecx
	004012BC  |.  C700 01234567 mov dword ptr ds:[eax],0x67452301
	004012C2  |.  C740 04 89ABC>mov dword ptr ds:[eax+0x4],0xEFCDAB89
	004012C9  |.  C740 08 FEDCB>mov dword ptr ds:[eax+0x8],0x98BADCFE
	004012D0  |.  C740 0C 76543>mov dword ptr ds:[eax+0xC],0x10325476
	004012D7  \.  C3            retn
}

很明显,004012B0函数用于初始化md5,因为出现了四个md5标志常数。
继续分析: 下面的A指的是md5初始化的常数01234567h

004011E5  |.  8D9424 4C0200>lea edx,dword ptr ss:[esp+0x24C]
004011EC  |.  57            push edi                                 ;  name的长度
004011ED  |.  8D8424 300100>lea eax,dword ptr ss:[esp+0x130]
004011F4  |.  52            push edx                                 ;  name的地址
004011F5      50            push eax                                 ;  A的地址
004011F6  |.  E8 E5000000   call MD5KeyGe.004012E0

猜测004012E0函数作用
猜测得到的数据是用户名+www.pediy.com进行MD5的结果,验证一下
在数据窗口跟踪寄存器的值,正好eax是第一个被我们分析的寄存器
在这里插入图片描述
在这里插入图片描述

可以发现用户名和www.pediy字符串连一起了。具体函数函数代码不分析了,因为看不懂:(

004011FB  |.  83C4 10       add esp,0x10
004011FE  |.  8D4C24 24     lea ecx,dword ptr ss:[esp+0x24]
00401202  |.  51            push ecx                                 ; /String = NULL
00401203  |.  FF15 04604000 call dword ptr ds:[<&KERNEL32.lstrlenA>] ; \lstrlenA
00401209  |.  50            push eax                                
 ;  www.pendiy.com的长度
0040120A  |.  8D5424 28     lea edx,dword ptr ss:[esp+0x28]         
 ;  www.pendiy.com
0040120E  |.  8D8424 2C0100>lea eax,dword ptr ss:[esp+0x12C]
00401215  |.  52            push edx                                
 ;  www.pendiy.com
00401216  |.  50            push eax                                
 ;  A的地址
00401217  |.  E8 C4000000   call MD5KeyGe.004012E0

0040121C  |.  8D8C24 340100>lea ecx,dword ptr ss:[esp+0x134]
;算法的结果存放位置
00401223  |.  8D9424 8C0100>lea edx,dword ptr ss:[esp+0x18C]
0040122A  |.  51            push ecx
0040122B  |.  52            push edx
0040122C  |.  E8 5F010000   call MD5KeyGe.00401390

猜测401390函数作用
发现ecx指向的位置有128位的二进制数改变了,因此00401390应该就是MD5加密算法
在这里插入图片描述
(由00 00 00 变成上图的结果)
在这里插入图片描述

00401231  |.  83C4 14       add esp,0x14
00401234  |.  33C0          xor eax,eax
00401236  |>  8A8C04 800100>/mov cl,byte ptr ss:[esp+eax+0x180]
0040123D  |.  83E1 1F       |and ecx,0x1F
//0x1F的二进制表示为11111,一个二进制数与1等于它本身,所以
//and ecx,0x1F的运算结果为ecx%32

00401240  |.  40            |inc eax
00401241  |.  83F8 10       |cmp eax,0x10
00401244  |.  8A540C 3C     |mov dl,byte ptr ss:[esp+ecx+0x3C]
00401248  |.  889404 0F0300>|mov byte ptr ss:[esp+eax+0x30F],dl  结果放入缓冲区
0040124F  |.^ 7C E5         \jl short MD5KeyGe.00401236
00401251  |.  8D8424 100300>lea eax,dword ptr ss:[esp+0x310]
00401258  |.  8D4C24 10     lea ecx,dword ptr ss:[esp+0x10]
0040125C  |.  50            push eax                                 ; /String2 = ""
0040125D  |.  51            push ecx                                 ; |String1 = "#Eg壂惋簶vT2?
0040125E  |.  FF15 00604000 call dword ptr ds:[<&KERNEL32.lstrcmpA>] ; \lstrcmpA

观察[esp+0x3C+ecx]处的数据 ,结合ecx的取值为0-31,发现这是一张替换表,ecx表示下标。
在这里插入图片描述
最后将输入的序列号(不含-)与上面代码计算出的序列号进行比较
在这里插入图片描述


程序加密原理讲明白了,接下来要写出注册机
重新梳理程序加密逻辑:程序在用户名字符串的末尾连接上www.pediy.com字符串,进行MD5加密。遍历加密结果,将每一个字节%32,将其作为替换表的下标,找到对应的字符。最后将加密结果与输入的序列号(不含-)进行比较


写出注册机

我首先尝试在cpp中使用openssl第三方库,但是用一天时间没成功导入,于是选择python下的hashlib库调用MD5函数

官方文档如下:hashlib — Secure hashes and message digests

import hashlib

table = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
name = input("请输入字符串")
md5_result = hashlib.md5()
md5_result.update(name.encode())
md5_result.update(b"www.pediy.com")
print("MD5结果",md5_result.hexdigest())

for i in range(0,len(md5_result.hexdigest()) , 2):
    index = int(md5_result.hexdigest()[i:i+2],16) % 32
    if i == 8 or i == 16 or i == 24:
        print('-',end='')
    print(table[index],end = '')

# 请输入字符串123456
# MD5结果 c52f2de7041dc12e26829e219867b035
# 7HF9-6X3G-84Y3-S9JP

md5结束

终于写完MD5了,休息一下
在这里插入图片描述
使用该图片已征求本人同意:)

sha1算法

复习一下MD5的消息处理函数
F ( X , Y , Z ) = ( X & Y ) ∣ ( ( X ˜ ) & Z ) G ( X , Y , Z ) = ( X & Z ) ∣ ( Y & ( Z ˜ ) ) H ( X , Y , Z ) = X ⊕ Y ⊕ Z I ( X , Y , Z ) = Y ⊕ ( X ∣ (   Z ) ) F(X,Y,Z) = (X\&Y)|((\~X)\&Z)\\ G(X,Y,Z) = (X\&Z) |(Y\&(\~Z))\\ H(X,Y,Z) = X\oplus Y\oplus Z\\ I(X,Y,Z) =Y\oplus (X|(~Z)) F(X,Y,Z)=(X&Y)((X˜)&Z)G(X,Y,Z)=(X&Z)(Y&(Z˜))H(X,Y,Z)=XYZI(X,Y,Z)=Y(X( Z))

这是sha1的处理函数
f ( x ) = { ( B & C ) ∣ ( ( B ˜ ) & D )       0 ≤ t ≤ 19 B ⊕ C ⊕ D       20 ≤ t ≤ 39 ( B & C ) ∣ ( B & D ) ∣ ( C & D )       40 ≤ t ≤ 59 B ⊕ C ⊕ D       60 ≤ t ≤ 79 f(x)= \left\{ \begin{array}{cc}{(B\&C)|((\~B)\&D)~~~~~0\leq t \leq19}\\ {B\oplus C\oplus D~~~~~20\leq t \leq 39}\\ {(B\&C)|(B\&D)|(C\&D)~~~~~40\leq t \leq 59}\\ {B\oplus C\oplus D~~~~~ 60\leq t \leq 79}\end{array} \right. f(x)= (B&C)((B˜)&D)     0t19BCD     20t39(B&C)(B&D)(C&D)     40t59BCD     60t79

md5初始化数据是4个双字,产生的消息摘要为128位,而sha1初始化数据为5个双字,产生的消息摘要为160位。
将这些信息记下来,就能快速判断程序使用哪种加密函数

分析sha1KeyGenMe.exe

查壳,发现程序使用了sha1加密
在这里插入图片描述

运行程序
在这里插入图片描述
根据Wrong Serial字符串在OD中定位到弹出窗口的代码,往上翻,找到程序获取用户输入的API函数,下断点后运行程序。
在这里插入图片描述

004014EE  |.  83F8 14       cmp eax,0x14    比较获取序列号的长度,如果不是20,验证失败
004014F1  |.  0F85 F3000000 jnz SHA1KeyG.004015EA

先把序列号改成12345678901234567890, 满足长度要求,重新运行程序
接着分析代码

004014F7  |.  8D8424 600300>lea eax,dword ptr ss:[esp+0x360]
004014FE  |.  50            push eax
004014FF  |.  E8 FCFAFFFF   call SHA1KeyG.00401000                   ;  初始化sha1
00401504  |.  83C4 04       add esp,0x4
00401507  |.  33FF          xor edi,edi
00401509  |.  3BF3          cmp esi,ebx
0040150B  |.  7E 1E         jle short SHA1KeyG.0040152B
0040150D  |>  0FBE8C3C D001>/movsx ecx,byte ptr ss:[esp+edi+0x1D0]   ;  name
00401515  |.  8D9424 600300>|lea edx,dword ptr ss:[esp+0x360]
0040151C  |.  51            |push ecx
0040151D  |.  52            |push edx
0040151E  |.  E8 1DFBFFFF   |call SHA1KeyG.00401040                  ;  每调用一次这个函数[esp+0X360]的值就会改变
00401523  |.  83C4 08       |add esp,0x8
00401526  |.  47            |inc edi
00401527  |.  3BFE          |cmp edi,esi
00401529  |.^ 7C E2         \jl short SHA1KeyG.0040150D
0040152B  |>  8D8424 080100>lea eax,dword ptr ss:[esp+0x108]         ;  缓冲区
00401532  |.  8D8C24 600300>lea ecx,dword ptr ss:[esp+0x360]
00401539  |.  50            push eax
0040153A  |.  51            push ecx
0040153B  |.  E8 60FDFFFF   call SHA1KeyG.004012A0                   ;  调用sha1

我们不需要通过分析每一个函数的具体汇编代码来理解函数作用,比如上面的代码,只要我们注意到[esp + 0x108]是缓冲区,提前跟踪数据窗口,就能知道004012A0函数是sha1加密函数
在这里插入图片描述在线网站验证一下上面的数据是不是用户名sha1的结果
在这里插入图片描述

继续分析加密代码

0040152B  |> \8D8424 080100>lea eax,dword ptr ss:[esp+0x108]         ;  缓冲区
00401532  |.  8D8C24 600300>lea ecx,dword ptr ss:[esp+0x360]
00401539  |.  50            push eax
0040153A  |.  51            push ecx
0040153B  |.  E8 60FDFFFF   call SHA1KeyG.004012A0                   ;  用户名sha1
00401540  |.  83C4 08       add esp,0x8
00401543  |.  33C0          xor eax,eax
00401545  |>  8A5404 34     /mov dl,byte ptr ss:[esp+eax+0x34]       ;  50 45 44 49 59 20 46 6F 72 75 6D 00 第一个字节. . . . .
00401549  |.  8A8C04 080100>|mov cl,byte ptr ss:[esp+eax+0x108]      ;  sha1结果
00401550  |.  32D1          |xor dl,cl
00401552  |.  885404 40     |mov byte ptr ss:[esp+eax+0x40],dl       ;  区域1正好在PEDIY Forum结束的后两个字节
00401556  |.  40            |inc eax
00401557  |.  83F8 11       |cmp eax,0x11
0040155A  |.^ 7C E9         \jl short SHA1KeyG.00401545
0040155C  |.  83F8 14       cmp eax,0x14
0040155F  |.  7D 1B         jge short SHA1KeyG.0040157C
00401561  |.  8D4C24 28     lea ecx,dword ptr ss:[esp+0x28]          ;  pediy.com
00401565  |.  83E9 11       sub ecx,0x11
00401568  |>  8A1401        /mov dl,byte ptr ds:[ecx+eax]            ;  三次循环 70 65 64
0040156B  |.  329404 080100>|xor dl,byte ptr ss:[esp+eax+0x108]      ;  从加密后的第十八个字节开始 到第二十个字节结束
00401572  |.  40            |inc eax
00401573  |.  83F8 14       |cmp eax,0x14
00401576  |.  885404 3F     |mov byte ptr ss:[esp+eax+0x3F],dl       ;  区域2连接在区域1之后,总共改变三字节
0040157A  |.^ 7C EC         \jl short SHA1KeyG.00401568
0040157C  |>  8B1D A4504000 mov ebx,dword ptr ds:[<&USER32.wsprintfA>;  user32.wsprintfA
00401582  |.  33F6          xor esi,esi
00401584  |.  8D7C24 10     lea edi,dword ptr ss:[esp+0x10]
00401588  |>  8A4434 4A     /mov al,byte ptr ss:[esp+esi+0x4A]       ;  第十一个字节开始 (下标为十)
0040158C  |.  8A4C34 40     |mov cl,byte ptr ss:[esp+esi+0x40]       ;  从第一个字节(下标为0)开始
00401590  |.  32C8          |xor cl,al
00401592  |.  8AC1          |mov al,cl
00401594  |.  884C34 40     |mov byte ptr ss:[esp+esi+0x40],cl       ;  改变区域1,从第一个字节开始
00401598  |.  25 FF000000   |and eax,0xFF                            ;  eax % 0x100
0040159D  |.  50            |push eax
0040159E  |.  68 4C604000   |push SHA1KeyG.0040604C                  ;  ASCII "%02X"
004015A3  |.  57            |push edi                                ;  user32.GetDlgItemTextA
004015A4  |.  FFD3          |call ebx
004015A6  |.  83C4 0C       |add esp,0xC
004015A9  |.  46            |inc esi
004015AA  |.  83C7 02       |add edi,0x2
004015AD  |.  83FE 0A       |cmp esi,0xA                             ;  循环十一次
004015B0  |.^ 7C D6         \jl short SHA1KeyG.00401588

尝试写出注册机

import hashlib

name = input("请输入字符串")
data = [0x50, 0x45, 0x44, 0x49, 0x59, 0x20, 0x46, 0x6F, 0x72, 0x75, 0x6D, 0x00]
sha1_result = hashlib.sha1()
sha1_result.update(name.encode())
print("sha1结果是", sha1_result.hexdigest())
for i in range(0 , 0x11):
    tmp = data[i] ^ int(sha1_result.hexdigest()[2*i:2*i+2], 16)
    data.append(tmp)

data.append(0x70^int(sha1_result.hexdigest()[34:36],16))
data.append(0x65^int(sha1_result.hexdigest()[36:38],16))
data.append(0x64^int(sha1_result.hexdigest()[38:40],16))
data.append(0)

for i  in range(11):
    data[12 + i] = data[12 + i] ^ data[22 + i]

result = ''
for i in range(12, 22):
    result += hex(data[i]).replace('0x','').upper().zfill(2)

print("序列号为",result)

本文结束,接下来的内容是一些废话


后记

我在文章开头说没有密码学基础,但是我上过学校的网络安全知识导论,里面虽然有讲到这些算法,但是老师没有讲明白。实在不清楚导论课有什么意义,可能起了造型上的作用?

照例的meme时间

这ctf比赛,我有四不打:

  • 没有逆向和pwn的比赛我不打,因为它善。出题人花了那么长的时间出水题,我一不小心夸夸夸全做出来了,对不起出题人的辛苦付出
  • 有逆向和pwn的比赛我不打,因为它难。长时间分析可执行程序,难免头昏眼花,影响接下来的学习效率
  • 线上比赛我不打,线上比赛监管力度不高,通常比赛还没结束,flag就漫天飞了,没什么含金量
  • 线下比赛我也不打,因为我是社恐,在一堆参赛选手中进行比赛,容易紧张,发挥不出正常水平

在这里插入图片描述

最近的学习内容

  • 入坑二进制安全了

  • 在做s.6081的lab1,有一个bug花费了我一小时,结果发现是头文件顺序写反了
    在这里插入图片描述

  • 在写注册机时,本来想在cpp下使用openssl第三方库。

参考博客

结果花了一天,还是没有成功导入openssl。。。 解决了这个bug,就有其他bug冒出来:(

  • 30
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值