首页
社区
课程
招聘
[翻译][翻译]Windows x64:XOR加密-逆向工程 第二部分
2018-6-1 15:45 4601

[翻译][翻译]Windows x64:XOR加密-逆向工程 第二部分

2018-6-1 15:45
4601

Windows x64:XOR加密-逆向工程 第二部分

前言


前一篇博客中,我们对一个二进制程序进行逆向工程分析,并从中提取了口令;但该口令是以明文形式存放在二进制程序中的。对新手来说这是很好的开始,但在当前的现实环境中你不可能找到这样的二进制程序。实际上,口令通常会经过混淆或加密处理。大多数情况下,口令甚至不会存储在二进制文件中,而是存储在一些远程服务器上,所有的序列号处理和检验过程都会在服务器上完成。


在本文中,我编写了一段简单的C/C++(二者兼容)程序代码。在代码中,我们并没有将任何口令以明文的形式硬编码写入二进制文件中。事实上,主口令是利用XOR(异或)的方法进行加密的。异或是一种最基础的加密方法,它在很多年前就开始投入应用,目前仍然十分有效,而且直到现在还有很多攻击者正在使用。然而,这种方法并不用于实际的加密过程,而是更多地用于对存储文本进行混淆操作。如果想要了解XOR、AND、OR的具体原理,你可以查阅以下链接:https://en.wikipedia.org/wiki/XOR_cipher


以下是我们二进制程序的源码(相关网址:https://github.com/paranoidninja/ScriptDotSh-Reverse-Engineering/tree/master/part_2-2)。你也可以直接下载并运行Git目录下的可执行文件。

#include <stdio.h>
#include <string.h>
void CheckPass(int *XoredPassword) {
    int PArray[10] = {38, 34, 37, 55, 55, 61, 33, 51, 32, 39};
    bool is_equal = true;
    for (int i=0; i<10; i++) {
        if (XoredPassword[i] != PArray[i]) {
            is_equal = false;
            break;
        }
    }
    if (is_equal == true) {
        printf ("[+] Correct Password");
    }
    else {
        printf ("[-] Incorrect Password");
    }
}

int main(int argc, char *argv[]) {    
    if (argc != 2) {
        printf("Help:\n%s <10 character serial key>\n", argv[0]);
    }
    else {
        int stringLength = strlen(argv[1]);
        if ((stringLength > 10) || (stringLength < 10)) {
            printf("[-] Serial key must be of 10 characters. Please recheck your key\n");
        }
        else {
            int XoredDecimal[10] = {};
            int keyStore[10] = {85, 86, 87, 88, 89, 90, 81, 82, 83, 84};

            for (int i=0; i<10; i++) {
                XoredDecimal[i] = ((int)(argv[1][i]))^(keyStore[i]);
            }
            CheckPass(XoredDecimal);
        }
    }
    return 0;
}

我们可以从理解C/C++代码入手,但这会失去乐趣;因为我们在对专有二进制程序进行破解时,往往无法得到源代码。因此,让我们先正常运行一下程序,并查看结果。

可以看到,程序打印显示‘incorrect password’信息。那么,让我们用x64dbg(相关网址:https://x64dbg.com/)加载打开该文件,并对其进行逆向分析。请记住, 我们的目标是找到口令, 而不是仅仅通过修改返回值来打印‘correct password’信息。

反汇编


你可以以如下命令格式,在x64dbg加载二进制程序的过程中,在powershell或cmd中向程序传递其参数和绝对路径:


PS C:\Users\Paranoid Ninja\Desktop> & '.\x64dbg - Shortcut.lnk' 'C:\Users\Paranoid Ninja\Desktop\crackMe_xor.exe' password12

在程序加载之后,需要记住的一点是,在windows系统中调试器不会直接跳转到二进制程序的反汇编代码处,这与linux系统中的情况不同。调试器首先需要加载二进制程序运行所必需的Windows DLL模块。你可以在调试器的日志选项卡中查看加载的DLL模块。



如上图所示,程序调用了ntdll.dll, kernel32.dll, KernelBase.dll和msvcrt.dll。之所以特别提到这些DLL模块,是因为我们当前并不需要对它们进行深入研究。使用‘stepi’命令进行单步步入操作会执行DLL模块的每条语句,并且进入目前我们并不需要了解的模块内部;当前我们只需要对这个二进制程序本身进行逆向分析。因此和上次一样,我们还是先查看字符串引用,并在这个二进制程序中寻找能够设置断点的位置。




下图是字符串引用的情况;我们将在反汇编器中追踪‘Serial key must be…’字符串的位置,并查看所对应的反汇编代码。




如上图所示,我在0x0000000000401681地址处设置了断点。下面介绍一下选择该字符串引用以及在此处设置断点的原因。在之前运行上述代码时,我们发现它进行了一系列的条件检查。例如,第一个条件检查是如果没有输入参数,程序会输出帮助语句;第二个检查是如果输入了长度小于或大于10的密码,程序会打印错误信息,提示需要输入正好10个字符的密码。这意味着,只有在我们提供了正确的参数之后,程序才会继续检查口令,而这就是我们选择这个字符串引用的原因。


如果对0x0000000000401668和0x000000000040166E地址处的代码进行观察你会发现,两处都会进行条件检查,并在随后跳转到0x0000000000401681地址处。如果任意一处的条件检查失败,将会打印‘Serial key error’信息并退出。现在,让我们运行一下二进制程序,来验证我们的猜测是否正确。


开始运行二进制程序之后,你会发现0x00000000004016A9到0x00000000004016EF地址处的代码将某些内容加载到RBP寄存器指向的多个DWORD区域中。



目前还不清楚这些内容是什么,因此我们选择先把它们记录保存下来,以备将来不时之需。这10个字符分别是U, V, W, X, Y, Z, Q, R, S, T。还要注意的是, 你在这些字符的左边所看到的数字,是字符所对应的十六进制数值。比方说,如果将0x55转换为十进制, 你会得到


0x55 = 5 * (16 ^ 1) + 5 * (16 ^ 0) = 80 + 5 = 85

我们都知道, 85 是字符U 的 ascii 值。同样, 如果将所有值转换为 ascii值, 我们就会得到


 Char U V W XQ
 Hex 55565758595A51 52 53 54 
 ASCII 85868788 899081828384

让我们先把这张表格放在一边。记住, 我们的口令是一个10字符的序列, 和这个集合的长度相等。还有在进行异或操作时, 某值与0的异或的结果是该值自身。这就是在进行异或操作时,待加密的字符串长度要与密钥序列长度相同的原因。


在通过使用‘stepi’命令进行一系列的单步步入操作从而返回程序代码之后,我们执行到了0x00000000004016FA地址处。你会发现“jg crackme_xor. 40172F”语句进行了另一次条件判断。通过查看RBX寄存器,你会发现对应位置放置的是数字10。



jg指令的意思是大于则跳转,因此这段代码的功能是,不断循环执行jg指令到0x000000000040172D地址之间的代码段,直至循环变量值递增到10则结束循环。你会发现,0x000000000040172D处的语句跳转返回到了0x00000000004016F6地址处,而在这里程序使用cmp dword ptr指令对值进行比较,若大于10,则直接跳转到0x000000000040172F地址处,否则程序会一直循环,直到寄存器的值等于10.


现在,如果继续使用‘stepi’命令对程序进行单步步入操作,你将会发现在0x0000000000401704地址处,程序把我们输入的口令存储到了rax寄存器中,然后在0x000000000040170F地址处将其放入了eax寄存器中。




在0x0000000000401712地址处,AL寄存器的值(如‘p’)被放入EDX寄存器中,而另一个值在0x 0000000000401715地址处被放入eax寄存器中;这两个值在0x000000000040171E地址处进行异或。



现在,使用‘stepi’命令对语句进行单步步入,你会发现rdx寄存器中存放的‘p’就是我们所输入口令’password12‘中的首字母;而另一个用于异或操作的值(存放在Eax寄存器中),就是大写字母’U’。




接下来,我们逐步执行整个循环过程。当前我们的增量计数器值为1;要记住,这个计数器将会运行10次,这意味着其他9个值也将会被异或。下图展示了下个循环过程会载入的内容。




可以看到,程序将我们所输入口令‘password12’中的‘a’加载到rdx寄存器中,而与‘a’异或的是另一个值’V’。同样,如果运行整个循环,你将会发现所输入的每一个字母都会通过以下方式进行异或,直到在0x00000000004016FA地址处计数器的值增至10。简而言之,与所输入口令进行异或的值正是之前所载入的U, V, W, X, Y, Z, Q, R, S, T。通过将我们的口令与上述密钥序列进行异或操作,我们可以得到




将以上两组二进制数字进行异或,我们得到以下异或二进制结果:


请记住,这只是[j1] 密钥序列,而不是主口令。为了找到口令,我们需要两个值。例如:如果X xor Y = Z,那么Y xor Z = X。因此,我们需要找到两组值,例如X和Y、或Y和Z、或X和Z。现在我们只有[U, V, W, X, Y, Z, Q, R, S, T]这一组密钥集合,这意味着我们还需要找到另一组值才能得到真正的口令。让我们继续分析反汇编代码,看看接下来程序会执行到什么位置。在0x0000000000401736地址处,我们可以看到程序调用了另一个函数,让我们进入函数内部,查看一下其具体功能。

喔太棒了!我们可以看到在0x000000000040156C地址处,另一组字符被加载到寄存器中。把这些字符从十六进制转换成十进制,我们得到以下结果:



让我们先把这个结果放到一边,继续执行代码。在0x 00000000004015BD地址处,程序再次使用cmp指令对值进行比较,并且RBX的值是10。之后程序中再次出现了jg指令;在计数器的值大于10的情况下,该语句跳转到0x00000000004015F2地址处。现在,我们执行到0x00000000004015E2处的代码,程序在该位置比较了‘&’和‘%’的值。我们知道‘&’就是上面所找到的字符,其十六进制值为0x26,ascii码等于38。但是‘%’是什么值,为什么要和‘&’进行比较呢?



‘%’的ascii码是37;向前查找一下,你会发现‘%’是‘P’和’U’进行异或的结果。现在,继续循环执行这段反汇编代码,你会发现程序不会进行下一次的比较;原因就藏在0x00000000004015E2地址处的代码中。在这里,程序对EDX(%)和EAX(&)进行比较,如果不相等则CF位(Carry Flag,进位标志)置1[j1] 。下一条指令在0x00000000004015E4地址处,它将检查上一条指令的值是否是0。je指令在值为0的情况下将发生跳转,但是由于两个值不相等,所以我们将值设为1,因而它不会跳转,并且[j2] 将继续执行下一条指令。


更新【2018-5-29】:je指令是相等则跳转,即ZF位被设置时跳转到指定指令,在本例中该标志位没有被设置。


随后在0x 00000000004015F6地址处,程序会输出‘Incorrect Password’信息,并结束循环退出程序。


因此,我们还不能恢复程序口令。但是,我们已经有了所需的素材。我们拥有了[U, V, W, X, Y, Z, Q, R, S, T]这一组密钥序列,还有用于比较的一组值[26, 22, 25, 37, 37, 3D, 21, 33, 20, 27]。请记住,用于比较的值不是口令,而是实际口令与密钥序列进行异或的结果值。因此,我们需要对这些用于比较的值和密钥序列再次进行异或,才能得到实际的口令。


现在,让我们把以上两组值都转换成十进制格式并且将其异或。下表即为最终结果:



把最后异或得到的十进制值转换成文本,我们得到:


所以,如果我们的推理基本正确的话,口令应该是‘strongpass’。接下来在二进制程序中检验一下:




原文链接:https://scriptdotsh.com/index.php/2018/05/09/ground-zero-part-2-2-reverse-engineering-xor-encryption-windows-x64/

编译:看雪翻译小组 欢歌笑语

校对:看雪翻译小组 木无聊偶


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2018-6-5 19:12 被欢歌笑语编辑 ,原因: 图片尺寸问题
收藏
点赞1
打赏
分享
最新回复 (5)
雪    币: 310
活跃值: (1917)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
niuzuoquan 2018-6-1 16:00
2
0
谢谢楼主分享
雪    币: 1054
活跃值: (165)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
欢歌笑语 2018-6-1 16:36
3
0
不知道为什么,图片会出现模糊的情况,大家如果想看图片可以去原文链接看一下
雪    币: 1784
活跃值: (513)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
木无聊偶 4 2018-6-1 18:06
4
0
欢歌笑语 不知道为什么,图片会出现模糊的情况[em_17],大家如果想看图片可以去原文链接看一下[em_46]
不要从doc文档里往外复制粘贴图片,而是向论坛上传每个单独的图片文件,这样清晰度能高一些
雪    币: 1784
活跃值: (513)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
木无聊偶 4 2018-6-1 18:08
5
0
因为doc文档会对图片进行压缩,降分辨率
雪    币: 6103
活跃值: (1207)
能力值: (RANK:30 )
在线值:
发帖
回帖
粉丝
CCkicker 2018-6-5 17:00
6
0
图片没过来,全是外链,建议图片重新贴过来一下,可以采用截图快捷键的方式,很方便
游客
登录 | 注册 方可回帖
返回