[安全攻防进阶篇] 六.逆向分析之OllyDbg逆向CrackMe01-02及加壳判断 (6)

梦想与她 提交于 2020-08-11 08:25:41

前文作者讲解了OllyDbg和在线沙箱的逆向分析过程,分享了恶意软件如何通过宏脚本发送勒索信息或密码至用户邮箱。这篇文件将带领大家逆向分析两个CrackMe程序,包括逆向分析和源码还原,基础性文章,希望对您有所帮助。技术路上哪有享乐,为了提升安全能力,别抱怨,干就对了,

从2019年7月开始,我来到了一个陌生的专业——网络空间安全。初入安全领域,是非常痛苦和难受的,要学的东西太多、涉及面太广,但好在自己通过分享100篇“网络安全自学”系列文章,艰难前行着。感恩这一年相识、相知、相趣的安全大佬和朋友们,如果写得不好或不足之处,还请大家海涵!

接下来我将开启新的安全系列,叫“安全攻防进阶篇”,也是免费的100篇文章,作者将更加深入的去研究恶意样本分析、逆向分析、内网渗透、网络攻防实战等,也将通过在线笔记和实践操作的形式分享与博友们学习,希望能与您一起进步,加油~

话不多说,让我们开始新的征程吧!您的点赞、评论、收藏将是对我最大的支持,感恩安全路上一路前行,如果有写得不好或侵权的地方,可以联系我删除。基础性文章,希望对您有所帮助,作者目的是与安全人共同进步,也强烈推荐大家去看看钱老师的视频,加油~

作者的github资源:
软件安全:https://github.com/eastmountyxz/Software-Security-Course
其他工具:https://github.com/eastmountyxz/NetworkSecuritySelf-study
Windows-Hacker:https://github.com/eastmountyxz/Windows-Hacker-Exp



声明:本人坚决反对利用教学方法进行犯罪的行为,一切犯罪行为必将受到严惩,绿色网络需要我们共同维护,更推荐大家了解它们背后的原理,更好地进行防护。(参考文献见后)

前文回顾:
[安全攻防进阶篇] 一.什么是逆向分析、逆向分析应用及经典扫雷游戏逆向 (1)
[安全攻防进阶篇] 二.如何学好逆向分析、逆向路线推荐及吕布传游戏逆向案例 (2)
[安全攻防进阶篇] 三.OllyDbg和Cheat Engine工具逆向分析植物大战僵尸游戏 (3)
[安全攻防进阶篇] 四.逆向分析之条件语句和循环语句源码还原及流程控制逆向 (4)
[安全攻防进阶篇] 五.逆向分析之Win32 API获取及加解密目录文件、OllyDbg逆向其原理 (5)







一.OllyDbg基础用法

OllyDbg是一个新的动态追踪工具,将IDA与SoftICE结合起来的思想,Ring 3级调试器,非常容易上手,是当今最为流行的调试解密工具之一。它还支持插件扩展功能,是目前最强大的调试工具之一。

OllyDbg打开如下图所示,包括反汇编窗口、寄存器窗口、信息窗口、数据窗口、堆栈窗口。

  • 反汇编窗口:显示被调试程序的反汇编代码,包括地址、HEX数据、反汇编、注释
  • 寄存器窗口:显示当前所选线程的CPU寄存器内容,点击标签可切换显示寄存器的方式
  • 信息窗口:显示反汇编窗口中选中的第一个命令的参数及跳转目标地址、字符等
  • 数据窗口:显示内存或文件的内容,右键菜单可切换显示方式
  • 堆栈窗口:显示当前线程的堆栈

下图是打开EXE后显示的界面。


下面简单讲解常用的快捷键调试方式。

F2
设置断点,如下图所示的红色位置,程序运行到此处会暂停,再按一次F2键会删除断点。

F9
按下这个键运行程序,如果没有设置相应的点,被调试的程序直接开始运行。

F8
单步步过,每按一次这个按键,将执行反汇编窗口中的一条指令,遇到CALL等子程序不进入其代码。

F7
单步步入,功能通单步步过(F8)类似,区别是遇到CALL等子程序时会进入其中,进入后首先停留在子程序的第一条指令上。如下图进入CALL子程序。

F4
运行到选定位置,即运行到光标所在位置处暂停。

CTRL+F9
执行到返回,按下此键会执行到一个返回指令时暂停,常用于从系统领空返回到我们调试的程序领空。

ALT+F9
执行到用户代码,从系统领空快速返回我们调试的程序领空。


OllyDbg动态分析的基本流程如下:

  • 通常拿到一个软件先试着运行软件,如果有帮助文档查阅帮助文档,熟悉软件的基本用法,接着尝试输入错误的注册码,观察错误提示。
  • 如果没有输入注册码的地方,要考虑是否是读取注册表或Key文件(程序读取一个文件中的内容判断是否注册),这些可以用其他工具来辅助分析。
  • 如果都不是,原程序只是一个功能不全的试用版,那要注册为正式版需要手动写代码完善。
  • 如果需要输入注册码,如上图所示,则调用查壳软件检查程序是否加壳(如PeiD、FI),有壳的需要脱壳之后再调用OllyDbg分析调试,无壳的直接调用工具调试。


二.CrackMe01

题目: 第1题是Acid_burn

1.题目描述

首先打开软件提示如下信息,然后主页面包括三个核心按钮,一个是“序列号+用户名”,一个是退出按钮,另一个是“序列号”。

在这里插入图片描述

主界面如下图所示,我们点击“Serial / Name”按钮。

在这里插入图片描述

显示如下图所示,随机输入用户名和序列号后点击“Check it Baby!”按钮。

在这里插入图片描述

它会提示你输入错误的信息,即“Sorry,The serial is incorect!”

在这里插入图片描述

接着点击“I give Up!!!”退回主界面,点击“Serial”按钮。

在这里插入图片描述
在这里插入图片描述

输入序列号点击“Check it Baby!”,显示如下图所示对话框“Try Again!!”。

在这里插入图片描述



2.逆向分析

第一步,调用PEiD或Exeinfo PE工具进行查壳。
显示结果没有壳,采用Dephi 3.0编写。

在这里插入图片描述

在这里插入图片描述


第二步,通过OD打开软件,模块入口地址是0x0042FD68。

在这里插入图片描述

按下F9运行程序,弹出第一个欢迎界面。

在这里插入图片描述

点击“确定”后会弹出程序的主界面,如下图所示。

在这里插入图片描述


第三步,查找失败提示的关键词“Sorry”。
在弹出的主页面中点击“Serial / Name”按钮,然后随机输入信息后会提示错误信息,这里记住关键词“Sorry”。

在这里插入图片描述

然后,在反汇编窗口右键鼠标,选择“查找”->“所有参考文本字串”。

在这里插入图片描述

弹出如下图所示的对话框,我们看到了非常多的信息,其中有一条是“Good job dude =)”。

在这里插入图片描述


第四步,选中指定字符串右键鼠标,点击“反汇编窗口中跟随”,或者直接双击该行去到指定汇编窗口。
如果字符串较多,可以右键选择“查找文本”,输入“Good”定位字符串的位置。

在这里插入图片描述

接着定位到如下图所示位置。

在这里插入图片描述


第五步,在JNZ跳转处按下F2增加断点,地址为0x0042FB03。

  • JNZ : jump if not zero 结果不为零则转移

在这里插入图片描述


第六步,继续在反汇编窗口右键鼠标,选择“查找”->“所有参考文本字串”,接着定位关键字符串“Try Again”然后在反汇编窗口中跟随。

在这里插入图片描述

找到JGE跳转处按下F2添加断点,地址为0x0042FA5A。

  • JGE: 大于或等于转移指令

在这里插入图片描述

接着回到EXE可执行程序,当我们输入序列号和用户名后,点击“Sorry”提示,并且此时程序自动跳到断点处,如下图所示:

在这里插入图片描述

在这里插入图片描述

此时包括两个断点。

在这里插入图片描述


第七步,按下F8进行单步步过调试,注意CALL函数不进入,一直运行到下一个断点0x0042FB03处。

在这里插入图片描述

在这里插入图片描述


第七步,选中该行右键“二进制”->“用NOP填充”。

在这里插入图片描述

显示如下图所示:

在这里插入图片描述


第八步,继续按下F8单步步过调试,当执行到0x0042FB18位置,会CALL函数然后弹出“Good job dude =)”对话框,成功实现绕过。

在这里插入图片描述


第九步,按下F9运行程序,然后会停到0x0042FA5A断点处,继续按下F9运行程序就会提示成功的对话框。

在这里插入图片描述

在这里插入图片描述


第十步,同样的方法查找第二个对话框的关键信息,然后在0x0042F4D5处增加断点。

在这里插入图片描述

按下F2增加断点。

在这里插入图片描述


第十一步,我们点击“Serial”按钮,然后在弹出的对话框中点击“Check it Baby!”按钮后,会自动定位到0x0042F4D5跳转位置。

在这里插入图片描述


第十二步,右键“二进制”->“用NOP填充”,接着F8单步步过调试。

在这里插入图片描述

最终成功逆向该程序。

在这里插入图片描述


第十三步,保存修改后的可执行文件。
右键选择“复制到可执行文件”->“所有修改”按钮。

在这里插入图片描述

在弹出的窗口中选择“全部复制”。

在这里插入图片描述

在弹出的窗口中选择右键“保存文件”,重命名保存即可。

在这里插入图片描述

最终效果如下图所示,只要点击按钮就提示你成功!

在这里插入图片描述

在这里插入图片描述



3.原理分享及序列号提取

前面我们成功绕过序列号和用户名的判断,下面我们简单还原其原理和获取序列号。先分析第一个对话框。

在这里插入图片描述

首先,我们通过“Good Job”定位关键位置,在0x0042F4D5处添加断点,JNZ跳转前面的CALL函数非常重要,并且该函数是根据EAX和EDX结果执行,所以我们需要在CALL函数处再增加一个断点,从而获取对应的值。

在这里插入图片描述


第二步,按下F9运行程序然后点击对话框会停止在断点处,对应的EDX值为“Hello Dude!”,显然第一个序列号与这个固定的硬编码“Hello Dude!”比较。

  • 第一个序列号:Hello Dude!

在这里插入图片描述


第三步,我们按下F7进入CALL函数,进一步验证该值,发现它CMP比较的正是我们输入的值是否等于“Hello Dude!”,自此判断成功。

在这里插入图片描述


第四步,CALL函数发现输入序列号不正确会JNZ直接跳转到PUSH 0处,然后弹出错误的对话框。

在这里插入图片描述

在这里插入图片描述

输入正确的值会提示正确对话框。

在这里插入图片描述

接着我们继续分析第二个对话框的原理。

在这里插入图片描述


第五步,通过字符串跟随在0x0042FAFE和0x0042FB03位置增加断点,分别是CALL函数的JNZ跳转执行错误对话框。

在这里插入图片描述

在这里插入图片描述


第六步,可以看到EAX存储的值为“CV-6560-CRACKED”时弹出“Good job”的对话框。

在这里插入图片描述

F7进入CALL函数可以看到它判断的过程。

在这里插入图片描述

但如果你只输入序列号,用户名没有验证,也会有错误的提示信息,而且这个序列号真的是固定的吗?

在这里插入图片描述


第七步,接着我们继续往前分析用户名的构成过程,用户名首先要求长度必须大于等于4,否则不执行JGE跳转并弹出错误提示。

  • JGE: 大于或等于转移指令

在这里插入图片描述


第八步,接着分析用户名第一位值,乘以0x29,再乘以2。

在这里插入图片描述

假设结果为16BE,也就是十进制的5822,接着将计算的结果转为字符串;然后和实现准备好的字符串进行拼接,结果为:CW-5658-CRACKED。

在这里插入图片描述

注意,这个序列号是根据用户名动态生成的,变化的。

在这里插入图片描述

下面我们按下F8进行单步步过调试。

在这里插入图片描述

下图是获取第一个值和第二个值的代码。

在这里插入图片描述

核心汇编代码如下,建议大家在0x0042F9C8位置下断点,多调试观察寄存器值变化。

/* 用户名第一位:左移0x3位再减去原来的值 */
0042F9EB  |.  0FB600        MOVZX EAX,BYTE PTR DS:[EAX]
0042F9EE  |.  8BF0          MOV ESI,EAX
0042F9F0  |.  C1E6 03       SHL ESI,3
0042F9F3  |.  2BF0          SUB ESI,EAX

username1 = username[0];
result1 = (username1 << 0x3) - username1; 

/* 用户名第二位:左移4位再加上原来的结果 */
0042FA06  |.  0FB640 01     MOVZX EAX,BYTE PTR DS:[EAX+1]
0042FA0A  |.  C1E0 04       SHL EAX,4
0042FA0D  |.  03F0          ADD ESI,EAX

username2 = username[1];
result1 = (username1 << 0x4) + result1;

/* 用户名第四位:乘以0xB */
0042FA26  |.  0FB640 03     MOVZX EAX,BYTE PTR DS:[EAX+3]
0042FA2A  |.  6BF0 0B       IMUL ESI,EAX,0B

username4 = username[3];
result2 = username4 * 0xB;               

/* 用户名第三位:乘以0xE在加上第四位的结果 */
0042FA3E  |.  0FB640 02     MOVZX EAX,BYTE PTR DS:[EAX+2]
0042FA42  |.  6BC0 0E       IMUL EAX,EAX,0E
0042FA45  |.  03F0          ADD ESI,EAX

username3 = username[2];
result2 = (username3 * 0xE) + result2;

/* 再次计算第一位 乘以0x29再乘以2 */
0042FA8A  |.  0FB600        MOVZX EAX,BYTE PTR DS:[EAX]
0042FA8D  |.  F72D 50174300 IMUL DWORD PTR DS:[431750]
0042FA93  |.  A3 50174300   MOV DWORD PTR DS:[431750],EAX
0042FA98  |.  A1 50174300   MOV EAX,DWORD PTR DS:[431750]
0042FA9D  |.  0105 50174300 ADD DWORD PTR DS:[431750],EAX

result3 = username1 * 0x29 * 2;

        
/* 将result3转为ASCII 并拼接密钥 */
sprintf(key, "%s-%d-%s", key1, result3, key2);

最终运行效果如下图所示:

  • Eastmount
  • CW-5658-CRACKED

在这里插入图片描述

在这里插入图片描述

最后给出鬼手56师傅的C语言代码,非常推荐大家去阅读这位大佬在CSDN和看雪的文章。

#include<stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
    //密码
    char key1[3] = "CW";
    char key2[8] = "CRACKED";
    //输入用户名
    char username[10] = { 0 };
    printf("请输入用户名 长度必须大于等于4:");
    scanf_s("%s", username, 10);
    //判断长度
    if (strlen(username) < 4)
    {
        printf("长度必须大于等于4,请重新输入\n");
    }
    //根据用户名生成密码
    //计算用户名的第一位
    int username1 = username[0];
    int result1 = (username1 << 0x3) - username1;  //左移0x3位再减去原来的值
    //计算用户名第二位
    int username2 = username[1];
    result1 = (username1 << 0x4) + result1;        //左移4位再加上原来的结果
    //计算用户名的第四位
    int username4 = username[3];
    int result2 = username4 * 0xB;                //乘以0xB
    //计算用户名的第三位
    int username3 = username[2];
    result2 = (username3 * 0xE) + result2;        //乘以0xE在加上第四位的结果
    //再次计算第一位
    int result3 = username1 * 0x29 * 2;            //乘以0x29再乘以2
    //将result3转为ASCII 并拼接密钥
    char key[50] = { 0 };
    sprintf(key, "%s-%d-%s", key1, result3, key2);
    //打印key
    printf("密钥为:%s\n", key);
    system("pause");
    return 0;
}


三.CrackMe02

题目: Afkayas.1.Exe

1.题目描述

首先打开软件如下所示,需要输入用户名和序列号然后注册。

在这里插入图片描述

随机输入名字和序列号后点击“OK”按钮后,它会提示你输入错误的信息,即“Try Again”。

在这里插入图片描述



2.逆向分析

第一步,通过PEiD软件分析是无壳,VB编写的。

在这里插入图片描述


第二步,用OD打开可执行文件,定位起始地址是0x00401124。

在这里插入图片描述


第三步,右键“查找”->“所有参考文本字符”。

在这里插入图片描述


第四步,双击“You Get It”去到反汇编窗口位置。

在这里插入图片描述


第五步,在JE跳转处按下F2增加一个断点。

在这里插入图片描述


第六步,然后运行程序会弹出对话框,输入下图所示的用户名和序列号。

在这里插入图片描述


第七步,然后点击“OK”会停在断点0x0040258B位置,右键“二进制”->“用NOP填充”。

在这里插入图片描述


第八步,接着F9运行程序发现成功跳转到“Get it”对话框。

在这里插入图片描述


第九步,右键“复制到可执行文件”->“所有修改”,然后保存文件即可。

在这里插入图片描述

在这里插入图片描述


第十步,当我们再次打开程序时,随机输入字符点击“OK”就会提示成功。

在这里插入图片描述



3.原理分享及序列号提取

第一步,OD重新打开程序然后随机输入用户名和序列号。

在这里插入图片描述


第二步,右键“反汇编窗口中跟随”,然后可以看到JE跳转到了我们的错误提示未知,F2增加断点。

在这里插入图片描述

可以看到正是JE跳转直接让其输入错误信息,说明前面就是对输入用户名和序列号的判断。重点分析全面部分的代码。

在这里插入图片描述


第三步,右键“查找”->“当前模块中的名称”。

在这里插入图片描述

注意到VB字符串比较函数vbaStrCmp,我们增加断点。

在这里插入图片描述

右键“在每个参考上设置断点”进行设置。

在这里插入图片描述


第四步,F9运行并点击“确定”,在断点位置发现下面不远处就有关键字符串“You Get Wrong”。

在这里插入图片描述

但右上角泄露了注册码信息,即为“AKA-877848”。

在这里插入图片描述

简单验证下发现成功。

在这里插入图片描述


第五步,重新运行代码,在0x00402403和0x0040242D增加断点,阅读汇编源码,进行相应还原。

  • 计算用户名的长度
  • 将用户名长度乘以0x17CFB得到结果,如果溢出则跳转
  • 将结果再加上用户名的第一个字符的ASCII
  • 结果转为十进制
  • 结果和AKA进行拼接,得到最后的序列号

在这里插入图片描述

最后同样给出鬼手师傅的还原代码。

#include<stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
    //密码
    char key1[4] = "AKA";
    //输入用户名
    char username[10] = { 0 };
    printf("请输入用户名:");
    scanf_s("%s", username, 10);
    //1\. 取长度
    int iUsernameLen = strlen(username);
    //2\. 将用户名长度乘以0x17CFB 得到结果
    int result = iUsernameLen * 0x17CFB;
    //3\. 将结果再加上用户名的第一个字符的ASCII
    result = result + username[0];
    //4\. 将结果转为十进制 此步骤省略
    //5\. 拼接序列号
    char key[MAX_PATH] = { 0 };
    sprintf(key, "%s-%d", key1, result);
    //打印序列号
    printf("生成的序列号为:%s\n", key);
    system("pause");
    return 0;
}


四.总结

写到这里,这篇文章就介绍完毕,希望对您有所帮助,最后进行简单的总结下。

  • OllyDbg基础用法
  • CrackMe01逆向分析及原理分享
  • CrackMe02逆向分析及原理分享

学安全一年,认识了很多安全大佬和朋友,希望大家一起进步。这篇文章中如果存在一些不足,还请海涵。作者作为网络安全初学者的慢慢成长路吧!希望未来能更透彻撰写相关文章。同时非常感谢参考文献中的安全大佬们的文章分享,深知自己很菜,得努力前行。

有点想家和女神了!月是故乡圆啊~接着加油。



参考文献:
非常推荐鬼手大佬的文章,还有一个是姜晔老师。
[1] 鬼手56大神的CrackMe系列文章
[2] https://www.bilibili.com/video/BV1z7411j7R1?p=2














































易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!