密码学常见加密算法逆向学习
加密算法分为多种形式,一种是单向散列算法,也叫做hash算法,该算法常用于数字签名与完整性检测,常见的散列算法有MD5,SHA,RIPE_MD,HAVAL,N_Hash这几种,另一种则是对称加密算法,对称加密算法加密与解密一般可使用同一个函数进行,算法强度依赖于算法密钥,常见的对称加密算法有,RC4,TEA,IDEA,BlowFish,AES等。
MD5 消息摘要算法
MD5算法是消息摘要算法,也是单项散列算法,其作用是将一个任意长度的消息压缩成固定长度,该算法默认会产生一个128位的消息摘要,常用于验证消息完整性以及数字签名等。
逆向识别方式:
消息摘要初始化时,会用4个变量来辅助计算消息摘要,这些寄存器会被初始化为:
A=>01234567h B=>89abcdefh C=>fedcba98h d=>76543210h
其主要加密代码是这样的,先是初始化这四个变量,然后再更新。
MD5Init(&context); |
可以看到,识别的关键就是,找到这四个关键常数,也就基本上能够确定,目标使用的是MD5算法了。
这四个常数,在内存中也是顺序存储的,很好识别到。
最后调用call计算散列值,并将散列值保存在edx中,数据窗口观察。
源代码是这样的,对比一下,学习识别方法。
总体上反编译对比,可读性很高了已经。
IDA分析一下看看 MD5Init 四个常数没变化。
该算法的变形通常有三个地方,1.改变初始化时用到的四个常数,2.改变填充的方法,3.改变hash变换的处理过程。
ShA 安全散列算法
Sha系列算法,又叫做安全散列算法,其包括 sha-1,sha-256,sha-384,sha-512总共这四种,分别产生160/256/384/512位的散列值,该算法与MD4算法设计原理相同,但安全性更高一些。
以sha-1为例,其会产生160位消息摘要,在对消息处理之前,初始散列值H用5个32位双子进行初始化,可以通过识别这些双字压缩常数来确定是否是该算法。
h0=> 67452301h h1=>efcdab89h h2=>98badcfeh h3=>10325476h h4=>c3d2e1f0h
以下代码,就是标准的sha-1散列初始化标志。
接着是对用户名进行hash处理部分,循环,循环次数取决于用户输入的用户名长度,经过循环后,最后将结果存入eax指向的位置。
首先通过循环的方式,将用户输入的用户名进行sha1_process 处理。
然后调用sha1_hash函数对字符串进行hash运算,此处是对用户名进行循环亦或操作,异或后存入 ss:[esp+eax*1+0x40]
再将亦或后的值,与第二个字符串进行异或操作。
最后执行,亦或操作结束,此处记下004014FF地址,载入IDA分析看看。
下半部分的分析。
剩下的其他算法识别特征如下。
RC4 流密码(对称加密)
RC4算法与1987年诞生,该算法属于对称加密中的流密码算法,广泛用于SSL,WEP等,尽管该算法安全性不太强,但在实际应用中还是可以使用的,据了解,病毒喜欢使用这类算法对自身或对目标文件进行加密,因为该算法简单,不会占用太大的存储空间。
加解密原理: RC4生成一种称为密钥流的伪随机流,RC4由伪随机数生成器(KSA)和异或运算(PRGA)组成,首先RC4会将一个256字节的数组进行初始化,RC4一个字节一个字节地加解密。给定一个密钥,伪随机数生成器接受密钥并产生一个S盒。S盒用来加密数据,而且在加密过程中S盒会变化。
rc4在初始化之前,需要定义一个加密密钥,其源代码如下定义。
在汇编中通常会出现在初始化S盒之前,所以我们需要记下这个密钥。
S盒初始化后长度是256字节,格式如下所示。
跟进401000看看是如何循环填充盒子的,首先循环256次,填充盒子,接着对盒子再次打乱。
调用加密函数后,S盒中的值会再次发生变化。
最后循环取出加密后的值并放入编辑框中。
另一种RC4加密方式,使用vs2013编译代码,载入查看对照源码学习。
|
代码中打开文件,然后加密后写入到文件,可以下readfile回溯到main函数的位置,需要回溯多次才能看到main.
执行 KeyExpansion 密钥填充256数组。
接着就是执行填充 InitSbox 初始化S盒,盒子的初始化就是从1开始向后填充,每次递增,填充长度就是256
填充完成后,通过mov edx, dword ptr ss:[ebp-0x4]
传递S盒首地址,让UpsetSbox
打乱盒子的顺序,进入call 0x01241100
继续分析。
这个地方,我们记下地址,放入IDA中分析一下看看,F5一下。
CRC 循环冗余校验码
该算法全称为CRC32循环冗余校验码,其主要作用适用于校验数据的完整性,CRC32多为32位,利用CRC32多项式的值从04C11DB7h或者EDB88320h中生成一张CRC32码表,该表具有256个元素,然后就可以根据数据表来计算字符串或者文件的CRC32值。
识别的关键点,就是找到初始化时动态生成CRC32码表的地方,找到该表就找到了具体的细节,如下CRC32的常见写法。
|
跟随到生成的位置,在002610E6处下断点,然后运行每点击一次运行,eax中就会存储一个CRC32值,直到循环完成256次为止。
运行完256次以后,生成的码表如下所示。
static const ub4 crctab[256] = { |
TEA 分组加密算法
TEA算法是一种分组加密算法,它的实现非常简单,通常只需要很精短的几行代码,该算法分组长度为64位,密钥长度为128位,其作者推荐使用32次循环加密,即64轮,识别该算法的关键点应该在于,第一是加密循环次数一般为32次,其次识别delta中的压缩常数,该常数由黄金分割点得到。
第一个加密代码格式如下,编译代码载入反汇编器中观察特征。
|
第一步下断点,这里可以下一个scanf断点,然后可以回溯到程序凌空,直接调用,调用加密算法之前会将加密的密钥与待加密数据一同压入传递到加密函数中。
继续跟进加密函数,如下通过分析后得出结论,第一次循环时sub eax, 0x61C88647
中存储的就是我们的压缩常数,第二次循环时此值就会发生变化,其次,经过分析,代码mov ecx, dword ptr ds:[ebx+ecx*4]
ecx中的值始终会以72697479 73656375 30353831 31373031
轮询这变化,当一轮过后继续轮询下一轮,轮询(反向)的就是我们输入的密钥,默认循环32次。
循环结束后,通过push dword ptr ss:[ebp+esi*4-0x1C]
取值并压栈,然后打印出加密后的数值,解密的过程与加密相同。
通过IDA分析一下算法的逻辑,基本上就是异或运算的合集,仔细分析并不难理解。
该加密算法简单容易实现,但是算法容易受到相关密钥攻击,所以一般会配合MD5等算法混合加密使用,如下代码是由一个TEA及MD5算法保护的案例,分析一下学习学习,大体源代码如下,首先使用md5对用户输入的名字进行散列,散列后拷贝到内存,将散列值作为TEA_KEY对用户输入的dwMessage消息进行加密,加密后将结果拷贝到szBuffer中,然后对szBuffer进行异或处理,最后对比用户hash与加密hash是否一致。
首先下断,下一个GetDlgItemTextA
然后输入注册码,注册,回溯到程序领空,然后向下单步,注意call发现,md5压缩常数,可断定采用了md5算法。
继续跟随,分析首先将用户名,等通过md5散列,将得到的值当作密钥密码来使用,并将消息传递到 call 0x00401000
继续进行TEA加密。
最后将加密后的数据,与输入的数据对比,相同则完成注册。
AES 高级加密标准算法
AES 高级加密标准算法,其发展是从1997年开始的,AES其主要是用于替换DES而产生的,该算法具有128位的分组长度,支持192/256位的密钥长度,其算法仅支持128/192/256的密钥长度,分别称作AES-128,AES-192,AES-256。
PEID: https://down.52pojie.cn/?query=peid
首先使用PEID查看,发现其案例中包含有MD5算法的64个常量元素T表,还包括AES的S盒,以及逆S盒,根据特征可知其程序使用了MD5与AES进行加密保护的。
首先下一个GetDlgItemTextA命令,当用户输入数据提交时会断下,回溯一层,首先看到的时代码中使用了MD5算法对用户名进行了散列值计算,此处将产生128位hash散列,并将结果保存在edx指向的内存中暂存。
对比一下源代码中AES的密钥部分,会发现,密钥就是在AES初始化时被读入内存的,这里需要留意。
以下数据窗口中,一共有128位,可判断时AES密钥,具体版本AES-128。
直接跟进 call 0x00401EC0
也就是AES初始化的CALL,看看是如何初始化的。
AES加密循环轮数,与加密过程,根据不同的密钥长度,AES会经历不同的轮数,以AES-128为例,看下表,该算法会经历至少10次轮询对其数组进行变换,最终将轮询结果复制到输出数组中,即得到最终密文。
AES-128 经历10次轮询 / AES-192 经历12次轮询 / AES-256 经历14次轮询
接着是初始化AES加密子密钥生成的循环部分,首先拷贝用于密钥扩展的轮常量Rcon,该轮常量也可以用来识别该算法。
加密轮询主要由4部分 SubBytes(),ShiftRows(),MixColumns(),AddRoundKey() 组成。
subbyte()表示字节代换,其实是一个简单的查表操作,AES定义了一个16*16字节的S盒,该盒中,每个字节元素的高4位作为行标,低4位作为列标,并取出相应的元素作为SubBytes操作的结果。
跟进call,进入到SubByte中,可看到查表操作。
出call后,以下地方需要识别注意。
本案例中的密钥扩展函数中,同时生成了用于解密的子密钥,继续单步向下,可看到 aes_encrypt加密函数。
直接F7 跟进到 call 0x004023A0
程序先对工作模式判断,本例ECB模式,直接继续调用 aes_ecb_encrypt
也就是 call 0x004025F0
继续跟进004025F0首先AES加密过程通常是通过查询4个表(T0,T1,T2,T3)来实现的,加密前首先会运行一次AddRoundKey该函数的作用是,将状态中的元素与轮密钥通过简单异或运算,轮密钥是由用户输入的密钥扩展而来的,同样可以看作一个状态数组。
上方代码运行四次过后,密钥与输入的激活码进行了异或操作,第一次打乱,接着继续向下执行轮函数,本案例中使用了128位算法,故需要执行10轮加密,先来循环9次,同样的此处的四张表地址,也可以作为识别AES算法的依据。
如下是最后一次,最后一次为特殊的处理,总共凑齐10次循环,最后一轮主要执行的是MixColumns操作。
S盒为256个元素的数组,即1个字节(0x00~0xff)可以表示的数量,正向S盒中初始化的元素为。
unsigned char S[256] = { |
解密时逆字节替换就是使用逆S盒进行字节替换,逆S盒为:
unsigned char inv_S[256] = { |
下面是从网上找到的一段AES加密代码,我们编译后研究一下识别方式,其原理同上。
展开代码
|
RSA 大素数算法
RSA算法是一个既能够用于数据加密也能够用于数据签名的算法,其应用非常的广泛,算法安全性上面具有一定的可信度,该算法的安全性依赖于大整数因式分解,也就是说该算法的安全性依赖于密钥的位数,其位数也一直在增加,一般的RSA算法需要使用1024位或者更长的模数才能保障安全性,这个算法具体我也不太懂,只是学习一下如何识别吧。
先来看一下,PEID对其算法的识别情况,提示是大整数,后端源代码如下。
这里如果要是想破解,可以将里面的模数n替换,首先替换成我们自己的n,然后用自己的d来编写注册机。
如果算法中的RSA模n的长度是128位,那么可以使用软件进行因式分解,首先,通过跟踪分析得到n,再将n因式分解,求出私钥d,进而编写出注册机。
如上我们需要关注两点,一个是n一个则是e,提取出来之后使用RSA-Tool工具,分别填入modulus内容,然后分解因式。
n = 0x80C07AFC9D25404D6555B9ACF3567CF1 / e = 0x10001
即可得到 n = pq = A554665CC62120D3 x C75CB54BEDFA30AB
然后通过欧几里得扩展算法,即可得到d,使用RSATools工具,在PrivateExponentd上面输入e,然后点击Calc.D,即可计算出。d = 651A40B9739117EF505DBC33EB8F442D
假设输入用户名lyshark
,其ASCII值 m = 6C79736861726Bh
生成注册码c的加密算法为c = m(d) mod n
6C79736861726B (651A40B9739117EF505DBC33EB8F442D) mod 80C07AFC9D25404D6555B9ACF3567CF1
使用BigIntegerCalculator 大数计算器,填入得到的xyz,计算后即可得到注册码。
可知用户 lyshark
其对应的注册码就是 D729A2A4683F1E03BB5A0067B51B33A
这里密码学中关于公式的相关内容我直接跳过了,什么欧几里得,啥的烂七八糟的,我不太懂也不想懂,会干活就行,管他算法是怎么做出来的,唯一的目的是怎么破了他。
总结: 常见的加密算法就这么几个,当然在加密与解密算法篇还介绍了其他的一些冷门算法知识,我没有全部学下来,只是找了几个重点看了看,如果你对其他冷门算法感兴趣,可以自己去看《加密与解密 - 解密篇》这本书,如果想要更深入的分析可以去看《密码编码学与网络安全》。