手把手教你找回加密程序的密码

标签: 数据安全 系统安全 加密程序 密码 | 发表时间:2016-02-06 12:34 | 作者:追影人
出处:http://www.freebuf.com

前不久一朋友说忘了加密程序的密码,里面有很多重要信息,希望我能帮TA找回密码。心想不就是点一下“忘记密码”么,所以爽快答应了,然后就发生了接下来故事。

0×01 前言

当拿到加密文件后,瞬间傻眼。不是联网程序,就是一个孤零零的exe,压根没有“忘记密码”这个选项,双击运行后,弹出那冷冰冰的对话框“please enter password”,于是习惯性地进行了“人工智能弱密码破解”(手动穷举输入密码),一番折腾后,果断放弃了尝试。后背一阵冷汗后,还是硬着头皮上了,谁让咱爽快的答应别人了哩。同时为了挑战一下自己,于是决定将这个程序进行逆向解析,彻底 ”爆”出里面的秘密,谁让咱是屌丝学僧哩,还要指望着修炼技术找工作呢。。。

捕获.JPG

0×02 猜想

程序在用户输入密码后,会立刻判断出密码的对错,所以文件中存在”对比密钥”用于判断密码的正确性。

对比密钥的几种形式:

1.密码的明文;

2.密码的散列值;

3.使用密码和某一特征值生成对比密钥;

4.使用密码和用户待加密的原数据生成对比密钥;

5.………………

0×03 信息收集

第一步:样本设置

table.PNG

第二步:文件静态分析

选取样本1使用UE的检索功能搜索密码“123456”,未找到结果,可证明密码不是以明文形式存储。

选取样本1为标准样本,使用UE的二进制对比功能,对比样本2、3、4与样本1的差异。

27.jpg

28.jpg

29.jpg

将内容为空的加密程序用UE打开,最后一行行号为c9f0h。对比上述样本,可判断加密程序采用文件末尾追加数据的方式存储密文数据,进一步分析后得到数据存储格式。

30.jpg

第三步:IDA静态分析

使用IDA加载样本1,弹出提示框。

31.JPG

点击Ok,程序成功载入,但是函数窗口中只有一个函数,可见程序加了某种壳对IDA逆向分析产生了干扰。

32.jpg

第四步:脱壳

为减少调试中的干扰,进一步理清程序流程,需要进行脱壳处理。使用壳检测神器PEiD判断壳类型,结果如下:

33.jpg

PEiD成功检测出壳名称为PECompact 2.x -> Jeremy Collake ,如果是未知壳, 则需要进行手动脱壳。这里根据壳信息下载对应的脱壳程序对之前设置的四个样本程序进行脱壳处理。

34.jpg

 将脱壳后程序再进行IDA静态分析,函数窗口可获取到所有的函数信息,主程序流程图如下所示

35.JPG

这密密麻麻的分支,让我再次一身冷汗

20110602205245-839259741.jpg

 

第五步:动态调试

使用OD进行动态调试分析,主要分析程序的密码比对流程。

1、将样本1载入OD中,F9直接运行。此时,奇怪的事发生了,程序在弹出密码输入框的同时,OD左下角提示进程已经结束,这意味着程序已经运行结束,怎么密码框还在呢?!!!

36.JPG

由此判断程序在运行时,创建了其他工作进程后结束了自身进程。打开任务管理器,可以看到如下疑似进程在运行。Kill掉这个进程后,密码框消失,可证明该线程为密码框工作线程。

  37.JPG

2、使用文件夹的搜索功能,对全盘进行了搜索,寻找该进程对应的程序存放目录。

1.JPG

打开其对应的文件夹,可以看到有很多类似程序,这些都是测试时记事本生成的中间程序。

2.JPG

3、运行这些程序,均为空白记事本,没有任何内容。经过UE比对确认,这些程序均为笔记本的原始程序,不包含任何数据。

推测:记事本在运行时,先将原始程序释放在temp目录下,然后创建新进程加参数运行释放的程序。

证明:使用OD查看程序调用的函数列表,找到创建进程的相关函数。

4.JPG

这里确定kernel32库函数CreateProcessA,右键选择“查看引用”。

5.JPG

407BAE处调用了该函数创建新进程,在407BAE下断点,运行程序

6.JPG

程序确实是通过加参数的形式运行的,打开cmd,输入程序路径并且加参数运行

7.JPG

程序弹出错误窗口,并不能正常运行弹出密码输入框。

0×04深度分析

获取上述基本信息后,确定了加密程序的数据存储格式和运行加载方式。下面采用OD附加进程的方式直接对运行后新创建的进程进行调试,来梳理密码判断流程。

1、附加程序

双击运行程序,打开OD->文件->附加,双击新进程名称,将OD附加上去,对其进行调试。

9.JPG

由于此时新进程处于密码框输入状态,所以OD会停留在系统函数领空,此时密码框为不可用状态。为了跟踪密码输入后的流程,需从密码输入后跟踪调试,使用Alt+F9程序会自动运行并停留在用户代码段。此时密码输入框处于激活状态,输入正确的密码,点击确定,程序停留在用户代码段。

10.JPG

2、IDA辅助查看程序流程

在获取到密码输入后的关键地址后,使用IDA加载程序,使用F5反编译功能,查看程序的伪代码。

11.JPG

可以看到While循环中第33行为密码输入框,37行调用函数404648进行了密码正确性判断,39行为“Invalid passphrase”密码错误信息。将OD定位到404648函数的调用处,可以看到函数的返回值eax决定了后续分支走向,这个值便是密码正确性判断后产生的结果。

12.JPG

找到密码判断的关键后,进入404648函数,查看返回值的生成过程,确定关键代码。

13.JPG

repe cmps byte ptr [esi],byte ptr [edi]

ESI为12FE78,EDI为3E3D99

14.JPG

程序对两处0×20字节的数据进行比对,而这两个数据正是样本1中key中的前0×20字节的数据。可确定程序在获得输入密码后,经过一系列加密变换后生成0×20字节的key与文件中的密钥进行对比,来判断输入的密码是否正确。

38.JPG

3、加密流程

确定密钥判断关键位置后,继续向上追溯,寻找对比密钥生成过程。经过一番跟踪后,确定函数407481为对比密钥生成函数。

  size_t __usercall sub_407481@(int a1@, void *a2, size_t a3)
{
  int v3; // edi@1
  size_t result; // eax@1
  int v5; // ebx@1
  size_t v6; // ebx@7
  v3 = *(_DWORD *)a1 & 0x3F;
  result = a3 + *(_DWORD *)a1;
  v5 = 64 - v3;
  *(_DWORD *)a1 = result;
  if ( result < a3 )
    ++*(_DWORD *)(a1 + 4);
  if ( v3 && a3 >= v5 )
  {
    memcpy_0((void *)(v3 + a1 + 40), a2, 64 - v3);
    result = sub_404B4C(a1 + 40, a1);
    a3 -= v5;
    a2 = (char *)a2 + v5;
    v3 = 0;
  }
  if ( a3 >= 0x40 )
  {
    v6 = a3 >> 6;
    do
    {
      result = sub_404B4C((int)a2, a1);
      a3 -= 64;
      a2 = (char *)a2 + 64;
      --v6;
    }
    while ( v6 );
  }
  if ( a3 )
    result = (size_t)memcpy_0((void *)(v3 + a1 + 40), a2, a3);
  return result;
}   

用OD在407481函数处下断点

15.JPG

什么!!!!函数在执行时,参数1是明文内容,参数2是明文长度。可见在此之前,程序利用输入的密码对密文进行了解密,然后又将解密出的明文送入函数407481生成比对密钥。

明文是如何解出来的,稍后再分析。先继续分析407481函数如何利用明文生成对比密钥。经调试后,确定函数404B4C为关键的加密函数。

16.JPG

由于该函数非常复杂,所以并不打算对该加密算法进行深入分析,直接将该函数的汇编代码抠出来作为c程序的内嵌代码使用。

17.JPG

404B4C函数的输入分别为eax(待加密的内容,长度为0×40字节),ecx(生成的密钥存放位置),ecx所指向的密钥存放位置为0×28字节,前8个字节存放着原始明文的总长度,后面0×20字节存储着生成密钥,且这0×20字节密钥设有初始值。

18.JPG

407481函数

输入:参数Arg1:原始明文地址

参数Arg2:原始明文长度

输出:  蓝色框中为原始明文长度

红色框中为密钥变换后的结果

绿色框为明文长度除以0×40后剩余的明文内容

19.JPG 

407481过程表示

蓝色框中写入参数Arg2的值

count = Arg2 / 0x40;   //明文长度除以0x40

data=Arg1;

While(count--)

{

Call 404B4C(data); //每次讲明文的0x40字节进行加密计算

data=data+0x40;

}

Call 40B240(Arg2 %0x40 ,data);  //将明文的剩余部分写入绿色框中

 上述过程结束后,程序再次调用了407481函数,参数为原始加密文件中key2密钥,长度为0×10字节。

20.JPG

407481函数运行后将0×10字节的密钥追加在了剩余明文尾部。

21.jpg

随后的call 00407508函数会计算出0×20字节的对比密钥。

经过分析,程序主要利用如下区域的数据进行对比密钥的生成。

22.jpg

蓝色框:原始明文长度+末尾附加的数据长度

红色框:密钥

绿色框:剩余数据

总结对比密钥生成过程:

1.将红色区域初始化,将初始密钥写入

2.每次读取0×40字节的原始数据,使用红色区域的密钥进行加密变换,生成的密钥输出到红色区域;

3.将剩余的原始数据进行填充处理,使其达到0×40长度,然后再进行一次密钥变换,此时生成的密钥便是比对密钥,用于和正确的密钥进行比对。

分析到这里我们发现,对比密钥的生成条件都是可以从文件数据中获取,但是有一个条件现在还不知道,那就是明文数据!!!我们似乎陷入了一个死循环中。。

0×05密文解密

1、猜想:程序在获取到输入密码后,利用输入密码对密文进行解密,用解密后的密文生成对比密钥。

证明:继续回溯跟踪,确定404A6F地址处调用的call 0040854C函数是解密函数,参数1为密文内容,参数2为密文长度,参数3为文件中的key2密钥。

23.JPG

40854C函数在解密过程中还调用了一些未知区域的数据进行解密变换

24.JPG

2、猜想:程序在初始化运行时,生成了这些未知区域的数据。

证明:重新运行程序,断在程序入口点处,查看数据区域41E340处,可见该区域均为0×00。

25.JPG

 在此数据区域设置内存写入断点,F9运行。

26.JPG

确定位置后用IDA反编译,可以清晰观察到程序通过调用408FFB函数向41B300,41BB00,41E340,41EB40,41C700,41CB00六个区域写入数据,每个区域长度为0×100。

  int sub_408FFB()
{
  int v0; // eax@1
  int v1; // ecx@1
  …………………………
  v0 = 1;
  v1 = 0;
  do
  {
    v2 = 283 * (((unsigned int)v0 >> 7) & 1);
    *(int *)((char *)&dword_41CF00 + v1) = v0;
    v1 += 4;
    v0 = v2 ^ 2 * v0;
  }while ( (unsigned int)v1 < 0x28 );
  v28 = 0;
  do
  {
    v3 = v28;
    LOBYTE(v2) = v28;
    v4 = sub_408F52(v1, v2);
    v5 = 2 * (v4 ^ 2 * (v4 ^ 2 * (v4 ^ 2 * v4))) ^ v4;
    v6 = (unsigned __int8)(v5 ^ BYTE1(v5) ^ 0x63);
    v7 = 2 * v6 ^ 283 * (v6 >> 7) | ((v6 | (v6 << 8)) << 8) | 452984832 * (v6 >> 7) ^ ((v6 ^ 2 * v6) << 24);
    ……………………
    v20 = 72448 * v17 ^ 72448 * v18 ^ 72448 * v19 ^ ((v14 ^ 8 * v14) << 8) | 18546688 * v17 ^ 18546688 * v15 ^ 18546688 * (((unsigned int)v14 >> 6) & 1) ^ 18546688 * v18 ^ 18546688 * v19 ^ ((v14 ^ 4 * v16) << 16) | 452984832 * ((unsigned int)v14 >> 7) ^ 452984832 * v17 ^ 452984832 * v18 ^ 452984832 * v19 ^ ((v14 ^ 2 * (v14 ^ 4 * v14)) << 24) | 283 * ((unsigned int)v14 >> 7) ^ 283 * v17 ^ 283 * v15 ^ 283 * (((unsigned int)v14 >> 6) & 1) ^ 283 * v18 ^ 283 * v19 ^ 2 * (v14 ^ 2 * v16);
    ……………………
    dword_41E340[v8] = v21;
    dword_41EB40[v8] = v24;
    dword_41C700[v8] = v1;
    dword_41CB00[v8] = v26;
  }
  while ( v28 < 0x100 );
  dword_41FF58 = 1;
  return 0;
}   

0×06.解密流程

39.JPG

0×07.后记

根据上述分析后,可确定加密程序并非将密码存储在文件中,所以不能根据加密数据逆向推导出原密码,而只能根据上述分析的密码验证流程采用字典攻击进行暴力破解。而这也正是很多加密程序需要使用字典进行暴力破解的原因。程序分析到这里,就该写程序结合字典来“爆”出里面的秘密了。。。。。。

原创作者:追影人,本文属FreeBuf原创奖励计划文章,未经许可禁止转载

相关 [手把 加密 程序] 推荐:

手把手教你找回加密程序的密码

- - FreeBuf.COM | 关注黑客与极客
前不久一朋友说忘了加密程序的密码,里面有很多重要信息,希望我能帮TA找回密码. 心想不就是点一下“忘记密码”么,所以爽快答应了,然后就发生了接下来故事. 当拿到加密文件后,瞬间傻眼. 不是联网程序,就是一个孤零零的exe,压根没有“忘记密码”这个选项,双击运行后,弹出那冷冰冰的对话框“please enter password”,于是习惯性地进行了“人工智能弱密码破解”(手动穷举输入密码),一番折腾后,果断放弃了尝试.

加密锁和云授权

- MArCoRQ - 月光博客
  在中国,加密锁仍然是占主导地位的软件保护方式. 近30年来,计算机硬件不知更新了多少代,软件技术也从上世纪90年代起进入了互联网时代,而加密锁保护方式却一直没有改变,顶多从原先的并口锁进化到USB锁,这是为什么呢.   这是因为,30年来,软件的盗版依然存在,人们却没有找到应对盗版更好的办法. 另外,传统的观念认为,加密锁具有安全强度高、软件授权可随加密锁移动使用优点.

Google升级HTTPS加密

- 请叫我火矞弟 - Solidot
民不拜天又不拜孔子留此膝何为 写道 "Google 修改了启用HTTPS服务的加密方法,以应对未来技术发展后可能造成的解密行为. 这项升级适用于Gmail、Docs和Google+. 现在的HTTPS实现借助于只有域名主人所掌握的私钥生成的session key来加密服务器和客户端之间的流量. 这种方法使得连接可能被所谓“追溯式解密攻击”(retrospective decryption attack)破解.

透明数据加密

- - CSDN博客数据库推荐文章
透明数据加密                                          .                         常见问题解答. 数据在网络上依然是加密状态吗. 任何人只要获得应用程序的授权就能对数据进行解密吗. TDE 与 Oracle 提供的加密方法有何不同. 哪些加密算法可与 TDE 一同使用.

银联加密算法

- - CSDN博客推荐文章
很多人对银联卡的加密算法感兴趣,毕竟分分钟涉及的都是你的钱的安全,但网上很少人却讲银联标准加密算法. 遂写一遍当做是自己的学习笔记,偶尔忘了可以翻翻,同时希望能够帮助到其他人. 首先要认识一下cbc算法和ecb算法. cbc算法是链式的,慢,不可并行处理,但更安全,因为每一次加密都是依赖于上一次的结果,同时这也会导致一次错将导致后面的全部错误.

恐怖分子用单字母加密法加密通信

- Mathack - Solidot
英国航空公司IT雇员Rajib Karim因阴谋发动恐怖袭击而被判30年徒刑. 他利用有两千多年历史的加密方法和孟加拉国伊斯兰激进分子进行通信. 法庭被告知,与Karim联络的孟加拉国激进分子拒绝使用更先进的PGP或TrueCrypt加密技术,而宁愿使用自己发明的Excel换位表单字母替换加密法. 这种方法最早是古希腊人发明,曾被尤利乌斯·恺撒在公元前55年使用,又被称为凯撒密码,它是一种十分简单的加密技术,明文中所有字母在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文.

Android 应用程序

- - CSDN博客推荐文章
Android 应用程序由四个模块构造而成:Activity、Intent 、Content Provider 、Service. 下面简单介绍一下如下模块的含义:. 1、Activity  "活动". 一个Activity就是单独的屏幕,每一个活动都被实现为一个独立的类,并且从活动基类中继承而来,活动类将会显示由视图控件组成的用户接口并对事件作出响应.

Linux程序调试

- - C++博客-首页原创精华区
Linux下的段错误产生的原因及调试方法    原文地址: http://www.upsdn.net/html/2006-11/775.html .    参考地址: http://www.cnblogs.com/khler/archive/2010/09/16/1828349.html .

Cppentry程序开发

- -
最近修改公司线上kafka集群配置然后直接kill掉进程来重启集群发现所有生产者都无法写入数据导致丢了数据,栽了一个大坑,接下来的工作肯定是补坑找原因,就分享一下. 系统环境说明:kafka版本为0.8.1.1,kafka集群配置为10.12.0.23:2181,10.12.0.24:2181,10.12.0.25:2181/kafka,因此在zookeeper中的根路径为:/kafka.

普通程序员、文艺程序员、2B程序员

- 可可 - 宇宙的心弦
希望能引起广大苦逼的正在学或者已经学过c++人的共鸣和会心一笑吧. 如何辨别自己在现实还是虚拟世界.