天草逆向教程笔记-全-

天草逆向教程笔记(全)

天草中级班 - P1:第01课 - 白嫖无双 🛡️

在本节课中,我们将学习如何对一个使用“穿山甲”3.x版本加壳的程序进行手动脱壳。课程将涵盖标准保护判断、绕过反调试检测、定位原始入口点以及最终修复与优化脱壳后文件的全过程。


判断保护模式

首先,我们需要判断目标程序使用的是标准保护模式还是最小保护模式。

以下是判断方法:

  • 使用辅助工具进行检查。将工具指向目标程序。
  • 观察工具提示。若显示为“标准保护”或“最小保护”,则说明是标准模式。
  • 也可通过查看加壳软件本身的选项来确认其提供的几种保护类型。

确认程序为标准保护模式后,我们开始进行动态分析。


载入程序与初步分析

上一节我们确认了保护模式,本节中我们来看看如何在调试器中载入程序。

使用OD载入目标程序。载入后,观察进程列表。

  • 如果OD中只显示一个主进程,说明这是一个单进程程序。
  • 我们直接在程序入口点附近下断点,然后运行程序。

程序中断后,观察堆栈。堆栈中出现的特定返回地址通常是即将发生关键跳转的标志。找到第二或第三个这样的返回地址,就可能接近核心代码。


处理反调试与定位OEP

上一节我们尝试了直接下断点,但遇到了反调试。本节我们来看看如何绕过它。

直接下断点可能导致调试异常,例如提示“debug program was unable to process exception”,这表明程序检测到了OD。

以下是尝试的几种方法:

  1. 第一种方法:在疑似关键跳转处下断点。尝试后,同样触发反调试,方法无效。
  2. 第二种方法:采用国外高手常用的“二次断点法”。在程序处理导入地址表(IAT)的代码段附近下断点。具体位置是一个处理IAT的CALL之后。
  3. 执行流程
    • 先删除之前下的所有硬件断点。
    • 让程序运行,直到它处理完IAT。在这个时机下断点。
    • 中断后,需要将之前为了跳过反调试而修改的代码恢复原状,否则程序会检测到代码被修改并触发保护。
    • 恢复代码后,再次下断点,即可成功中断在理想位置。

成功绕过反调试后,继续单步跟踪。注意观察CALL指令。

  • 计算器类的CALL通常与寄存器ECXEDX有关。
  • 如果看到CALL ECXCALL EDX,可以尝试跟进。
  • 通过单步执行,判断哪个CALL会跳转到程序的原始入口点。进入那个CALL后,即可到达清晰的OEP。

转储与修复程序

上一节我们成功定位了OEP,本节我们来完成脱壳和修复。

找到OEP后,第一件事是转储程序。无论脱UPX壳还是其他简单壳,这都是必须的步骤。

使用OD的脱壳插件进行转储。我们的OEP地址是 312A7

转储后,使用修复工具进行修复。

以下是修复步骤:

  1. 打开修复工具,加载刚转储出来的文件。
  2. 在OEP处填入我们找到的地址:312A7
  3. 点击“自动查找IAT”,工具可能会提示发现一些“无效函数”。
  4. 点击“获取输入表”,查看无效函数。
  5. 点击“修复转储文件”,在弹出的保存对话框中,注意不要勾选任何附加选项
  6. 保存后,之前的无效函数数量可能会变化,直接点击“修复”即可。

修复完成后,关闭工具。运行修复后的程序,确认可以正常启动。


查壳与体积优化

程序虽然能运行,但我们还需要确认脱壳是否彻底,并优化文件体积。

首先使用查壳工具检查。

  • 用PEiD查壳,可能会显示为“ASPack 1.x - 2.x”。这是因为穿山甲壳将入口点伪装成了ASPack的样式,这是PEiD的误报。
  • 用更准确的工具FI查壳,会显示为“Microsoft Visual C++”,这表明已成功脱壳。

接下来优化文件体积。脱壳后的文件往往包含无用数据,体积较大。

以下是优化步骤:

  • 使用区段编辑工具(如SexyHUD)。
  • 需要对PE文件区段有一定了解。
  • 在工具中,选择删除冗余的头部(HUD)和数据(Data)区段。
  • 此方法在黑鹰破解课程中有详细讲解。
  • 处理前文件大小为1.11MB,处理后约为540KB,体积减少一半以上,且程序运行正常。

关于破解的说明

本课主要聚焦于脱壳技术。对于程序本身的破解,只做简要提示。

该程序注册失败时会提示:“The code you have entered is inferior to the code”。

破解思路如下:

  • 在OD中,通过字符串参考或错误提示框断点来定位关键判断代码。
  • 由于在初级班课程中已经详细讲解过多种破解思路和方法,此处不再赘述。
  • 鼓励大家运用已学知识,自行完成破解分析,以此作为练习。


本节课中我们一起学习了针对“穿山甲”3.x壳的手动脱壳流程。主要内容包括:判断保护模式、使用OD载入并分析、绕过反调试检测、精准定位程序原始入口点、使用工具转储并修复文件,最后还对脱壳文件进行了查壳验证和体积优化。希望你能掌握这个系统的脱壳方法。

天草中级班 - P10:第10课 - 白嫖无双 - BV1qx411k7kd 🔍

在本节课中,我们将学习如何分析一个具有欺骗性质的软件,并揭示其“外挂”的虚假注册机制。我们将通过逆向工程实践,识别软件的真实结构,并学习处理此类“安装包”式程序的方法。


课程概述

本节课将分析一个伪装成外挂的软件。该软件表面显示注册成功,但实际上并未真正验证注册码,属于欺骗行为。我们将通过逆向工具,揭示其真实程序隐藏在临时目录中,并演示如何定位和修改其关键验证逻辑。


初步分析与误导

上一节我们介绍了课程目标,本节中我们来看看具体的分析对象。该软件启动后,表面显示已成功注册,注册码为 我的注册码

使用查壳工具检查主程序,结果显示为“没有壳”。这通常是一个令人高兴的发现,但在此案例中,这恰恰是误导的开始。


发现真实程序

使用OD载入程序进行分析,发现无法找到关键字符串。这是因为主程序实际上是一个“安装包”或“打包器”。

真正的可执行文件被释放到了系统的临时目录中。以下是定位步骤:

  1. 在软件运行时,进入系统临时目录(如 %TEMP%)。
  2. 查找在软件启动时间点新创建的、可疑的可执行文件。
  3. 将其复制出来进行分析。

退出软件后,该临时文件理应自删,但在此例中并未删除,这为我们提供了机会。

对复制出的真实程序进行查壳,发现其为 UPX 壳。我们可以使用OD的插件进行脱壳。


识别程序类型

为什么说原程序是一个“包”?有两个线索:

  1. 启动行为:程序启动时,有明显的解压或释放过程。
  2. 文件信息:使用查壳工具(如 FI)检查原程序,会显示类似 Setup 或安装程序的标识。

这是一种提示,说明它是一个安装包,会将真实程序释放到临时目录运行。


逆向关键验证逻辑

现在,我们对脱壳后的真实程序进行分析,目标是找到注册验证代码。

首先,我们尝试定位注册按钮的事件处理函数。使用辅助工具(如 SPY++Winspector)查找包含“注册”文字的窗口。

找到注册按钮后,在OD中对相关API(如 GetWindowTextA)或按钮事件消息(如 WM_COMMAND)下断点。

程序中断后,单步跟踪,会来到验证逻辑处。这里会出现“注册码不正确”的提示,其附近通常有一个关键的条件跳转指令(如 jejne)。

这个跳转决定了注册是否成功。我们分析堆栈和寄存器,可以找到程序用于比较的“真实验证码”。在本例中,发现它只是一个简单的固定值或机器名,并非基于用户输入的复杂计算,这证实了其欺骗性。


修改程序行为

既然验证是虚假的,我们可以通过修改关键跳转来绕过它。

  1. 在OD中找到导致“注册失败”或“直接退出”的关键跳转指令。
  2. 修改该指令(例如,将 je 改为 jmp),使其无论验证结果如何都跳转到成功流程。

修改后运行程序,虽然可能仍会显示“注册给:xxx”(如机器名),但软件的“试用”限制或弹窗已被去除。

然而,即使用户输入了从这个逆向过程中找到的“真实验证码”,软件本身的功能也并未真正解锁,这进一步证明该软件只是一个骗局。


核心技巧与总结

本节课中我们一起学习了如何分析和处理此类“打包”的欺骗性软件。以下是两个核心要点:

  1. 识别安装包:对于启动异常或查壳信息显示为安装程序的软件,应检查系统临时目录(%TEMP%),寻找其释放出的真实可执行文件进行逆向分析。
  2. 处理启动弹窗:对于程序启动时的骚扰弹窗,可以通过逆向找到其调用代码(通常是一个 MessageBoxCreateWindow 调用),并通过修改或NOP掉相关调用指令来去除它。

总结:本节课通过一个实例,演示了从识别欺骗性软件包、定位真实程序、逆向虚假注册验证到修改程序行为的完整流程。关键在于保持警惕,不轻信表面现象,并通过技术手段深入分析程序的真实行为。

天草中级班 - P11:第11课 - 白嫖无双 🛡️➡️📦

在本节课中,我们将学习如何对一款名为“穿山甲”的双进程标准版加壳程序进行脱壳处理。我们将从识别双进程开始,逐步将其转换为单进程,最终完成脱壳并修复程序,最后还会涉及程序“减肥”的优化操作。


课程概述

本节课演示针对“穿山甲”双进程标准版加壳程序的脱壳流程。我们将使用特定断点将双进程合并为单进程,找到解码后的原始入口点(OEP),并进行修复与优化。


第一步:识别双进程并设置断点

启动目标程序后,可以观察到存在两个相同的进程。这是双进程加壳的典型特征。

以下是操作步骤:

  1. 使用工具检查程序文件。本课涉及三个文件:一个解码工具、一个编码工具以及演示程序。它们属于同类型加壳程序。
  2. 我们需要使用三个关键断点。首先设置第一个断点,其作用是将双进程转换为单进程。
  3. 设置好断点后,按下 Shift + F9 运行程序直至中断。如果中途遇到异常,请在调试器中添加该异常并忽略。


第二步:写入转换代码

中断后,不要取消断点。跳转到地址 00401000,并写入一行特定代码。

请注意:以下代码中,仅有一处地址 12DDB8 需要根据你本机的实际情况进行修改,其他部分保持不变。

// 此处代码示例,关键地址需替换
mov eax, [12DDB8] // 将 12DDB8 替换为你本机调试时看到的实际地址

写入代码后,右键选择“新建 EIP”以设置新的执行点,然后按 F9 继续运行,程序将再次中断。


第三步:定位返回时机并设置新断点

现在可以取消第一个断点。使用 Ctrl + G 跳转到指定地址,并撤销之前的代码选择。

以下是后续操作:

  1. 设置第二个关键断点。
  2. 按下 Shift + F9 运行并中断,此时观察堆栈。
  3. 在堆栈中发现特定数据时,意味着即将到达返回时机。继续执行一次,当经过两个特定标志后,便是正确的返回时机。
  4. 取消第二个断点,按 Alt + F9 执行到返回。

第四步:进入解码区域并修复程序

到达返回时机后,设置第三个断点。中断后取消该断点,再次按 Alt + F9 执行。

此时会遇到一个 CALL 指令,右键选择“步入”(F7)进入。你会发现代码区域变为红色,这标志着“穿山甲”壳的解码已完成,我们已进入原始程序代码区,并且进程已合并为单个。

现在可以对程序进行修复:

  1. 右键选择“使用 OllyDump 脱壳调试进程”。
  2. 在脱壳窗口中,点击“获取 EIP”作为 OEP,然后点击“脱壳”。
  3. 保存脱壳后的文件。


第五步:验证与优化

运行脱壳后的程序,确认功能正常。可以使用进程查看工具检查,确认已变为单进程。

载入脱壳后的文件,观察其入口特征,例如显示为 VC++ 5.0 编译的程序特征。关于破解方法,本课暂不深入,学员可自行研究。

接下来进行程序“减肥”以优化体积:

  1. 观察脱壳后程序体积(例如 888KB)与原程序体积(例如 552KB)的差异。
  2. 使用编辑工具删除文件末尾的冗余数据(通常是壳的残留数据)。
  3. 保存修改后的文件。

再次检查,程序应仍可正常运行,且体积显著减小(例如从 888KB 减至 248KB)。


课程总结

本节课中,我们一起学习了“穿山甲”双进程标准版加壳程序的完整脱壳流程。

我们首先识别了双进程,通过设置断点将其转换为单进程,然后定位到解码后的原始入口点并完成程序修复与转储,最后还对程序进行了体积优化。

“穿山甲”壳主要有几种变体:标准版(含单/双线程)和非标准版(多为双线程)。本节课演示的是标准双线程版的脱壳。非标准版的脱壳方法更为复杂,将在后续课程中讲解。

至此,“穿山甲”壳的三大主要类型已讲解两种。请学员课后自行练习本课涉及的三个文件,并尝试完成破解步骤。

天草中级班 - P12:第12课 - 白嫖无双 - BV1qx411k7kd 🔓

在本节课中,我们将要学习如何对一个使用Asprotect 1.22版本加壳的程序进行脱壳,并重点解决脱壳后程序因“壳的校验”而无法正常运行的问题。我们将通过实践操作,掌握定位和修复壳校验的基本思路与方法。


开始脱壳操作

现在来拖拽这个壳。这是一个名为“aspire2.12”的程序。

它实际上加的是Asprotect 1.22这个版本的壳。对于Asprotect这类壳,即使成功脱壳,程序仍可能存在一些问题,特别是关于一些校验。本节课我们就来具体处理这些问题。

首先,在调试器中设置忽略所有内存访问异常和其他异常。然后,我们直接运行程序,让其触发异常并来到“最后一次异常”处。这里的代码结构与1.23RC版本有些相似,其特点是包含一个XOR操作,后接两个JE指令,再加上一个JMP指令。

我们直接来到最后一次异常之后,在程序的原始入口点00401000处下一个断点(按F2),然后按F9运行到该断点。分析代码时发现情况有些奇怪。我们先查看程序是用什么语言编写的,确认后开始进行修复。


尝试使用插件修复

我尝试使用一个修复插件。

但发现不行,因为这个壳有些特别。

它使用了早期版本的SDK功能。这里需要区分一下:Asprotect早期最基础的API保护技术被称为SDK,这与现代需要绑定硬件的“SDK”概念不同。壳的发展是从简单保护,逐步演进到对API进行处理,最基础的技术就是这种SDK。

我们使用插件,先用针对1.2X版本的插件尝试,不行。然后再用1.22版本的插件尝试,这次可以了。现在开始抓取进程并转储(Dump)程序。


处理脱壳后的错误

转储完成后,程序运行有错误。这个错误并非程序自身的校验,而是“壳的校验”。我们需要找到并修复它。

一个基本的方法是:首先设置调试异常选项,通常忽略内存访问异常和其他异常,然后直接运行程序。此时程序没有任何错误提示,但也无法正常启动。

我们在代码中找到一个出错的调用(CALL),右键选择“返回到调用者”。可以看到返回地址指向了系统领空。我们继续向上查找,找到上一个调用(CALL),就是这里出错了。


对比分析并修复

现在,我们对照原程序(加壳状态)进行分析。在原程序中,跟随这个出错的调用地址,可以看到代码逻辑。例如,有一条指令是将13435A8与零比较,正常情况下不应该跳转。

在脱壳后的程序中,这个调用地址无法正确跟随,说明地址有问题。在数据窗口中查看这个地址,发现它指向了已被脱掉的壳代码区域。

因此,我们需要将这个地址修改为指向脱壳后程序自身的正确地址。复制正确的地址。

打开脱壳后的程序文件进行修改。保持异常设置不变,再次运行。程序会来到另一个类似的问题地址,同样指向壳区域。

我们将其修改为程序内的一个有效地址,例如133xxxx。然后返回边缘跟随,来到修改后的地址处。为了找到合适的替换地址,我们在程序代码段中搜索一个常见的指令,比如RETN。假设我们在00401234找到了一个RETN指令。

于是,我们将之前出错的调用地址都修改为00401234,然后保存文件。


验证修复结果

现在再次载入修复后的程序。

程序运行到之前出错的位置。我们对比原程序,发现原程序此处也是一个调用(CALL)。我们同样将脱壳程序中的这个地址修改为之前找到的RETN地址00401234

修改完成后,大家可以看到,程序现在可以正常运行了。

关于程序本身的破解部分,需要大家课后自行尝试和研究。

(操作失误,删错了某些内容)


课程总结 🎯

本节课的主要目的,是锻炼大家如何修复脱壳后遇到的“壳的校验”问题。我们提供了一种通过对比分析、定位错误调用并将其重定向到程序内部有效地址的修复思路。

当然,这里提供的只是一种思路。这需要大家自己去深入研究和实践,后续课程中也会继续讲解相关内容。

本节课中,我们一起学习了针对Asprotect 1.22壳的脱壳后校验修复流程,掌握了基本的故障定位与地址修复方法。

天草中级班 - P13:第13课 - 白嫖无双 🛡️

在本节课中,我们将学习针对特定加壳程序(Ace ProTech 1.35)的多种脱壳方法。课程将详细介绍如何利用不同的异常设置和断点技巧,定位并到达原始程序入口点(OEP),并完成导入地址表(IAT)的修复。


课程概述与目标确认

我们首先需要确认目标程序的版本。通过分析,确认目标程序使用的是 Ace ProTech 1.35 版本的壳。

本课程的主要目的是向大家介绍几种能够有效到达程序 OEP 的通用方法。


方法一:忽略异常与隐藏调试器

上一节我们确认了目标,本节中我们来看看第一种方法。这种方法基于我们之前课程中提到的设置:忽略内存访问异常并指定异常处理,同时隐藏OD调试器。

以下是具体操作步骤:

  1. 在OD中设置,忽略所有内存访问异常。
  2. 启用插件隐藏OD。
  3. 运行程序,观察是否能直接到达OEP。

经过测试,这种方法对于当前目标不可行。因此,我们需要尝试其他方法。


方法二:利用最后一次内存访问异常

由于第一种方法失败,我们转而使用基于异常断点的方法。首先,确保插件设置正确,并启用“忽略内存访问异常”选项。

运行程序,让其触发并停留在最后一次内存访问异常处。

然后,在代码段(例如 00401000)的起始地址设置一个内存访问断点(F2)。

执行这个步骤的主要目的是为了寻找和处理可能存在的 Stolen Code。如果程序没有 Stolen Code,我们就可以直接到达 OEP。

对于本例,程序没有 Stolen Code,因此设置断点后运行,可以直接到达 OEP。OEP 的特征是典型的 BC++ 程序入口,地址为 0x00401590

到达 OEP 后,接下来需要进行脱壳和修复。我们需要手动查找 IAT。IAT 的起始地址大约是 0x004161F0,大小需要通过计算确定,本例中约为 0xD90

使用插件修复 IAT 时,会发现有很多无效指针,需要耐心等待插件处理完成。

这个壳虽然看似简单,但要完美处理其跨平台等特性,实际上颇具难度,它曾是知名的“四大金刚”壳之一。

修复完成后,可以运行脱壳后的程序进行验证。


方法三:利用 INT1 单步异常

除了内存访问异常,我们还可以利用其他类型的异常。现在,我们来看第三种方法。

将异常类型设置为 INT1 单步异常,并隐藏OD调试器。

运行程序,让其停留在最后一次 INT1 异常处。

与方法二类似,此时在 00401000 地址设置 F2 断点,然后运行(Shift+F9),即可到达 OEP。


方法四:利用 INT3 断点异常

接下来是第四种方法,它与第二种方法原理相似,但使用的异常类型不同。

将异常类型设置为 INT3 断点异常

程序运行后,会停留在最后一次 INT3 异常处。同样地,在 00401000 设置 F2 断点 后运行,即可到达 OEP。

对于 Ace ProTech 1.35 这个版本,其 Stolen Code 通常无法用 INT3 异常法找到,需要使用前面提到的 ESP 定律 来定位。


方法五:利用 .idata 区段断点(最快捷)

最后介绍一种更快捷的第五种方法。如果目标程序存在明显的 .idata 区段(即导入表区段),可以采用此方法。

首先,必须忽略所有异常

然后,在 .idata 区段上设置一个内存写入断点

运行程序,当断点触发后,删除这个内存写入断点。

紧接着,在代码段 00401000 上设置一个内存访问断点

再次运行程序(Shift+F9),即可直接到达 OEP。

介绍这几种方法的目的,是希望大家能够灵活运用不同的调试技巧来应对不同的加壳情况。


修复要点与反调试处理

在修复 IAT 时,有一个关键点需要特别注意。以本程序 OEP 0x00401590 为例,在修复对话框中,对于 USER32.MessageBoxA 这类 API,其显示位置可能在 Message 类别下,务必准确识别和修复。

此外,该壳具备反调试功能。在脱壳后的程序中,可能会遇到烦人的调试提示框。

可以通过在程序启动代码附近(例如调用 MessageBox 显示提示框之前)设置硬件断点,并跳过相关调用,来去除这个提示。具体操作是找到提示框调用指令(通常是 CALL),将其NOP 掉或修改流程直接跳过。


课程总结

本节课中,我们一起学习了针对 Ace ProTech 1.35 壳的五种脱壳方法:

  1. 忽略异常法(本例中无效)。
  2. 最后一次内存访问异常断点法
  3. INT1 单步异常断点法
  4. INT3 断点异常断点法
  5. .idata 区段写入转访问断点法(最快捷)。

核心在于灵活运用不同的异常设置和断点技术来定位 OEP,并掌握手动查找与修复 IAT 的技能。同时,需要注意处理壳可能带来的反调试机制。

天草中级班 - P14:第14课 - 白嫖无双 🛡️

在本节课中,我们将学习如何对一款非标准版的“穿山甲”壳进行手动脱壳与修复。这个过程涉及定位关键代码、打内存补丁、修复输入表以及最终的程序减肥。我们将使用OllyDbg(OD)等工具,一步步完成操作。


概述 📋

本次教程的目标是手动脱掉一个非标准版的穿山甲壳。该壳的选项“内存补丁”已被启用,增加了脱壳难度。我们将通过设置断点、定位OEP、记录关键数据、打补丁、修复输入表等一系列步骤,最终得到一个可运行的、体积更小的程序。


第一步:初始载入与记录入口点

首先,使用OllyDbg载入目标程序。载入后,需要记录程序的入口点地址,后续步骤会用到。

以下是具体操作步骤:

  1. 载入程序后,在CPU窗口的代码段起始位置,可以看到类似 558B 的指令。记下这个地址,例如 0x00401000
  2. 在代码段中右键,选择“转存中跟随”,然后取消当前断点。


第二步:定位原始程序入口点(OEP)

上一节我们记录了初始入口,本节中我们来看看如何找到程序真正的原始入口点(OEP)。这是脱壳的关键一步。

以下是定位OEP的步骤:

  1. 在代码段中下一个断点,然后运行程序(F9)。
  2. 程序中断后,在转存窗口跟随,并取消这个断点。
  3. 下第二个断点,继续运行。中断后,观察代码,这里很可能就是OEP。
  4. 重新载入程序,并保存当前状态。


第三步:搜索关键代码与设置断点

找到OEP后,我们需要搜索壳的关键校验或解密代码。通常会使用搜索全部命令的功能。

操作步骤如下:

  1. 在OD中隐藏调试器(可选)。
  2. 使用“搜索”->“所有参考文本字串”功能。
  3. 在搜索结果中,找到类似 Compare 或关键API调用的位置。例如,找到 CMP EBP, 0xA30 这样的指令。
  4. 双击进入该代码位置。


第四步:修改关键数据与记录补丁参数

在关键比较指令处,我们需要修改条件并记录打补丁所需的数据。

以下是具体操作:

  1. CMP EBP, 0xA30 指令处,设置一个硬件执行断点。
  2. 运行程序(F9)使其中断在此处。
  3. 观察比较结果。如果 EBP 不等于 0xA30,需要将其修改为相等。可以使用 Modify data 功能将 EBP 的值改为 0xA30
  4. 记录四个关键数据(假设用X, Y, G, W表示):
    • X: 当前指令附近的一个特定双字(DWORD)值。
    • Y: 另一个关键地址,例如 0x121B70
    • G: 一个偏移量值。
    • W: 补丁代码的写入地址。


第五步:定位并写入补丁代码

现在,我们利用上一步记录的数据,定位到需要写入补丁代码的具体位置。

操作流程如下:

  1. 在内存中搜索记录的数据 X,找到其出现的位置。通常会有多个结果,需要找到正确的那一个(例如第四个出现的位置)。
  2. 找到的目标地址附近,会有类似 0FF 的指令,这就是补丁写入的起始点。
  3. 计算最终跳转地址:W地址 + G偏移量
  4. 在OD中选中目标地址的代码区域,使用“二进制”->“编辑”功能,写入补丁代码(例如 E9 XXXXXXXX 跳转指令)。
  5. 写入后,删除之前设置的硬件断点。


第六步:执行补丁并完成脱壳

补丁写入后,需要让程序执行到我们修改的代码,完成脱壳过程。

以下是执行步骤:

  1. 跳转到记录的 W 地址处。
  2. W地址 + G偏移量 计算出的位置,设置一个硬件执行断点。
  3. 运行程序(Shift+F9)使其中断。
  4. 中断后,程序已经执行了我们的补丁,此时可以使用OD的“脱壳”功能(例如 OllyDump)来抓取内存中的程序镜像。
  5. 在脱壳时,程序可能有多个进程。根据经验,通常选择第二个进程(子进程)进行抓取。


第七步:修复程序输入表(IAT)

脱壳得到的文件通常无法直接运行,因为输入表(Import Address Table, IAT)被破坏了。我们需要找到并修复它。

修复IAT的步骤如下:

  1. 用PE编辑工具(如LordPE)载入脱壳后的文件。
  2. 将OEP的值修改为之前找到的地址(例如 0x15AC),并保存文件。
  3. 再次用OD载入这个修改后的文件。
  4. 在OD中,找到IAT的起始地址和大小。通常可以在代码中搜索 GetProcAddress 等API调用附近找到线索,或者通过观察内存区段来推断。
    • 例如,IAT可能起始于 0x0F11C,大小需要计算得出(如 0x6C)。
  5. 使用输入表修复工具(如ImportREC),填入正确的OEP和IAT信息(RVA和大小)。
  6. 点击“获取输入表”,工具会列出导入函数。可能会有无效项,将其剪掉。
  7. 修复完成后,保存文件。


第八步:处理双进程与最终修复

某些壳会使用双进程保护。我们需要附加到正确的进程,完成最后的修复。

处理双进程的步骤:

  1. 在ImportREC中,选择正确的进程ID(例如 0x374)。
  2. 在OD中,附加到目标进程(使用OD的H窗口查看进程列表)。
  3. 在附加的进程中,找到之前记录的入口点 558B 所在的地址。
  4. 在该地址设置断点,运行程序使其中断。
  5. 中断后,修改一处关键调用或数据(例如将 0x01040 改为 0x100),以绕过保护。
  6. 修改后,让程序继续运行,完成最终的修复。


第九步:程序减肥(优化体积)

修复后的程序可能包含壳的残留数据,体积较大。我们可以安全地删除一些无用的区段来减小体积。

减肥操作步骤:

  1. 使用PE编辑工具打开最终修复好的程序。
  2. 依次检查各个区段。通常可以安全删除明显是壳残留的区段(如 .tls、某些无名的或特征明显的区段)。
  3. 每删除一个区段后,保存并测试程序是否能正常运行。
  4. 重复此过程,直到无法再删除任何区段而不影响运行为止。
  5. 对比减肥前后的文件大小。例如,原脱壳文件 3.18 MB,加壳原文件 1.08 MB,减肥后可能达到 1.54 MB


总结 🎯

本节课中,我们一起学习了手动脱掉非标准版穿山甲壳的完整流程。

我们首先通过设置断点找到了程序的OEP,然后搜索并修改了关键校验代码,记录了打补丁所需的参数。接着,我们定位到内存中的补丁位置并写入了跳转代码,成功执行脱壳。之后,我们修复了被破坏的输入表,并处理了双进程保护的复杂情况。最后,通过删除无用区段,对脱壳后的程序进行了有效的“减肥”,显著减小了其体积。

这个方法虽然步骤繁琐,但逻辑清晰,是理解此类强壳脱壳原理的有效实践。掌握后,可以应对多种变种壳的挑战。

天草中级班 - P15:第15课 - 白嫖无双 🛡️

在本节课中,我们将学习一种针对特定软件(Keyfile类型)的破解方法。我们将通过分析程序流程、修改关键跳转和内存数据,绕过其试用期限制,最终实现软件的“破解”。


概述

本节课的目标是破解一个具有21天试用期的软件。我们将使用动态调试的方法,定位并修改程序中的关键验证逻辑,从而跳过试用期检查,使软件进入“已注册”状态。


第一步:准备与观察

首先,我们运行原程序。程序界面显示为中文,并提示有21天试用期。点击“继续”按钮可以进入试用。

为了便于后续的字符串搜索和分析,我们需要将软件界面语言切换为英文。因为许多调试工具在搜索字符串时对英文的支持更好。

在设置中找到语言选项,将其更改为英文。


第二步:定位关键字符串

软件切换为英文后,我们使用调试器(如OllyDbg)附加到进程。接下来,我们需要在内存中搜索与注册验证相关的字符串。

我们在字符串参考中搜索“Trial”,发现了多个相关结果。同时,我们也看到了“Continue”这个字符串,它对应程序运行时的“继续”按钮。

我们重点关注“Continue”字符串,并查看哪些代码引用了它。


第三步:分析验证逻辑

在引用“Continue”的代码处设置断点,然后重新运行程序。程序会在断点处暂停。

我们开始单步执行(F7/F8),观察程序的执行流程。很快,我们发现了一个关键的条件跳转指令(如JNZJE)。

CMP EAX, 0
JNZ SHORT 跳转地址

这段代码的含义是:比较EAX寄存器的值是否为0。如果不为0(JNZ),则跳转到某个地址;如果为0,则顺序执行。

通过反复测试和修改这个跳转指令(例如,将JNZ改为JMP强制跳转,或改为NOP填充使其不跳转),我们观察程序行为的变化。

我们发现,当这个跳转生效时,程序会显示“已过期”;当跳转被绕过时,程序则显示“继续试用”。这证实此处是试用期验证的关键点之一。


第四步:追踪数据来源

仅仅修改跳转可能不够,因为程序可能在其他地方还有校验。我们需要知道EAX寄存器中的值(即判断依据)是从哪里来的。

我们向上回溯代码,发现EAX的值来源于一个内存地址。我们查看该内存地址,发现其中存储着一个字符串,例如“3800cc”。

MOV EAX, DWORD PTR [内存地址]

这个字符串看起来像是注册名或密钥。程序会将我们输入的注册信息与这个值进行比较。


第五步:修改与测试

我们尝试将内存中这个关键的字符串修改为我们想要的注册名,例如“3800cc”。

  1. 在调试器中找到存储该字符串的内存地址。
  2. 右键选择“修改数据”,将其内容改为“3800cc”。
  3. 同时,确保之前找到的关键跳转指令被修改为绕过验证(例如改为JMP)。

完成修改后,让程序继续运行。此时,软件界面显示注册给“3800cc”,并且试用期限制似乎已经消失。



第六步:处理文件校验(CRC)

然而,我们发现直接修改内存后,一旦关闭程序再重新启动,修改就会失效。或者,如果修改了程序文件本身,程序甚至无法启动。

这提示程序可能存在文件完整性校验(CRC校验)。程序会检查自身文件是否被篡改,如果发现被修改,就会拒绝运行或重置状态。

以下是应对思路:

  1. 定位校验代码:在程序中搜索“CRC”、“Checksum”、“Registrar”等字符串,或分析程序启动初期的函数调用,找到进行校验的代码段。
  2. 绕过校验:找到校验函数后,可以尝试修改其返回值(例如,强制让它返回“校验通过”),或者直接NOP掉整个校验调用。

在本例中,我们通过搜索发现了一些相关字符串,但由于时间关系,没有深入追踪完整的校验流程。我们采用的内存补丁方法(运行时修改)绕过了静态文件校验,是一种有效的“另类”破解方式。


总结

本节课我们一起学习了一个软件破解的实战流程:

  1. 环境准备:将软件切换为英文以便分析。
  2. 定位关键点:通过搜索字符串和动态调试,找到与试用期验证相关的代码和跳转。
  3. 逻辑分析:理解条件跳转的判断逻辑(CMP + JCC)。
  4. 数据追踪:找到判断依据(如注册名)在内存中的来源。
  5. 实施修改:修改内存数据和关键跳转,实现破解。
  6. 应对保护:意识到并简单探索了CRC文件校验这种反破解机制。

这种方法的核心在于动态调试逻辑分析,通过修改程序运行时的状态来达到破解目的。对于初学者,掌握查找关键跳转和修改内存数据是至关重要的第一步。

天草中级班 - P16:第16课 - 白嫖无双 🛡️

在本节课中,我们将要学习针对特定加壳程序(俗称“穿山甲”)的脱壳与修复技术。课程将演示两种不同的方法,并介绍一个实用工具的使用,以帮助初学者理解脱壳的基本流程和思路。


上一节我们介绍了穿山甲加壳程序的多样性。本节中我们来看看一个具体的、具有严格断点检测的案例。

这个案例非常特别。首先需要判断它是单线程还是双线程加壳。观察发现它只有一个线程,属于单线程。尝试用标准方法读取,发现程序无法正常读取。

它虽然显示为标准壳,但用标准方法直接拖拽分析时,程序无法继续执行。

接下来隐藏调试器OD,并下断点。可以看到,程序依然无法进行。这说明它对断点检测非常严格。

改用HideOD插件的H1选项来隐藏调试器。使用后,程序可以继续,证实了其严格的断点检测机制。

修改相关设置后,找到并撤销一个逃避技巧的调用。然后继续使用断点进行分析。这里需要手动查找输入表(IAT)。

发现只有340个函数,这对于一个大型软件来说是不符合常识的。继续向上查找,因为每个程序的起始部分通常包含注册函数。

记录下起始地址。使用上面的地址进行定位,并需要找到结尾。

按住键盘的向下箭头键查找,可以找到结尾位置。

现在计算大小:

Size = 结束地址 - 起始地址 = 148C

计算RVA(相对虚拟地址):

RVA = 地址 - 0x400000 = 73A8

其中,Size是大小。

现在进行修复操作,结果显示有19个无效指针。

检查无效指针,发现大量指向time等库函数。继续查看下一个,情况类似。

无需理会,直接将这些无效指针减掉。

运行修复后的程序,确认可以正常运行。


上一节我们使用了手动查找和修复IAT的方法。本节中我们使用另一种方法,借助一个专用工具来完成。

首先重新开始。这次先不手动找OEP(原始入口点),而是使用脚本来自动寻找OEP以节省时间。

脚本成功找到OEP。现在打开脱壳工具。

工具中,TN是进程ID,此处显示为7D0

接下来,在工具中打开进程的内存镜像。

找到第一个区段,地址是00401000,该区段长度是58B123

现在需要修复输入表。原始输入表地址在B3ADD8

查看该地址所属区段。

输入表位于这个区段。现在将其移动到另一个空闲区段。不能移动到第一个区段,选择另一个可用区段。

以下是移动IAT的步骤:

  1. 原始位置:BaseAddr
  2. 原始大小:Size
  3. 新位置:CC7(目标区段地址)

点击执行,等待操作完成。我们来对比两种方法的结果。

操作完成,输入表地址已更改为CC7

现在开始脱壳。在脱壳工具中选择Dumper,并将输出格式改为EXE

同样需要进行修复。需要手动填写OEP的RVA和大小。此时IAT大小变为8C7,但总的大小不变。

进行修复。

修复后的程序也可以正常运行。

程序是试用版,需要网上注册购买,没有直接输入注册码的地方。

但可以对其进行破解。两个方法脱壳后的文件大小都是12.9MB,没有区别。

本节课的主要目的是介绍这个脱壳工具的使用。


接下来尝试为脱壳后的文件“减肥”,即删除无用区段。

以下是可删除的区段列表:

  • 删除某个无用区段。
  • 删除另一个无用区段。
  • 删除第三个无用区段。

注意:包含输入表(IAT)的区段不能删除,因为之前已将IAT移动到此区段。

删除部分区段后,程序仍可运行。

此时文件大小减小到9.07MB。

尝试继续删除data段。

删除data段后程序无法运行。因此,包含IAT的.idata段不能删除。文件最多只能减肥到9.07MB,尝试减到8.17MB会导致运行失败。

关于该试用版软件的破解,留给大家自行练习。在初级班课程中讲解的一些破解方法,例如F4暂停法、F2断点法等,都是非常实用且应用广泛的技术,在后续破解外挂等场景中都会用到,请大家务必掌握并多加练习。

本节课中我们一起学习了针对严格检测型穿山甲壳的两种脱壳方法,重点介绍了使用专用工具进行IAT转移和修复的流程,并演示了脱壳后的基本减肥操作。破解实践部分需要大家结合已有知识完成。

天草中级班 - P17:第17课 - 白嫖无双 - BV1qx411k7kd 🔓

在本节课中,我们将要学习如何利用一个API速查表辅助逆向分析,并通过一个简单的实例演示破解过程。课程的核心是掌握工具的使用和关键跳转的分析方法。

概述 📋

本节课的主要内容分为两部分:首先介绍一个实用的API速查表工具及其安装使用方法;其次,通过一个具体的软件实例,演示如何定位并修改关键跳转以实现破解。

API速查表工具安装与使用 🛠️

上一节我们概述了课程目标,本节中我们来看看如何配置和使用API速查表工具。

这是一个非常有用的API速查表。

以下是具体的安装和使用步骤:

  1. 将提供的文件复制到OllyDbg(OD)的安装目录下。
  2. 在OD目录内新建一个文件夹(名称可自定),并将文件放入其中。
  3. 启动OllyDbg。
  4. 在OD菜单中选择“API帮助文件”,然后选择刚才放入的文档文件。

配置完成后,即可在分析程序时使用。以下是使用方法:

  1. 在OD中打开一个可执行文件进行分析。
  2. 在代码区看到某个API函数调用时,右键点击该函数。
  3. 在弹出的菜单中选择“符号名称帮助”。

此时会显示该API的英文帮助信息,内容来源于MSDN,非常方便。

实例破解分析 🧩

掌握了工具的基本用法后,我们进入实战环节,对一个目标程序进行破解分析。

本次的目标程序有一个验证机制。我们随意输入信息后,程序提示失败。

首先,使用OD载入程序。

定位关键代码

尝试使用暂停法(例如在按钮事件处下断点)来定位关键代码。我们发现直接定位按钮事件的方法在此例中不奏效,这与我们之前遇到过的一种情况类似。

因此,我们换用其他方法。以下是寻找关键判断逻辑的步骤:

  1. 使用辅助工具配合查找按钮事件的处理函数。
  2. 在函数内部,可以找到获取用户名和注册码的代码。
  3. 关键位置通常存在决定程序流程的条件跳转指令。

在分析过程中,我们找到了一个关键比较和跳转。该处代码将两个值(例如用户输入与计算出的值)进行比较。

CMP EAX, EDX    ; 比较两个值
JNZ SHORT xxxxxx ; 如果不相等则跳转(通常是跳向失败流程)

分析并修改验证逻辑

我们观察到,如果跳转发生,程序会走向注册失败的流程。我们的目标是阻止这个跳转。

在本次实例中,这个跳转本身是发生的。我们通过修改此处的跳转指令,将其改为无条件跳转(JMP)或直接NOP掉(填充为90 90),从而绕过验证。

; 修改前
JNZ SHORT 失败地址

; 修改后(方法一:强制跳转)
JMP SHORT 成功地址

![](https://github.com/OpenDocCN/sec-notes-zh/raw/master/docs/tiancao/img/f5613dfcaac07602d8d8f72bd5a6f7ba_34.png)

; 修改后(方法二:直接无效化)
NOP
NOP

修改后运行程序,发现程序在目录下生成了一个标志性的DLL文件(例如SATDLL),并且界面显示注册成功。

为了验证,我们删除生成的DLL文件,程序状态恢复为“未注册”。重新运行修改后的程序,DLL文件再次生成,注册状态恢复为“已注册”。这证实了我们的修改是有效的。

总结 ✨

本节课中我们一起学习了两个核心内容。

首先,我们介绍并实践了如何安装和使用一个外置的API速查表工具,这能极大方便我们在逆向工程中查询Windows API函数的功能。

其次,我们通过一个简单的破解实例,完整演练了从载入程序、定位关键验证代码到分析并修改关键跳转指令的整个过程。本次破解的关键在于找到决定注册成功与否的条件跳转,并通过修改其逻辑来绕过验证。

希望本教程能帮助你理解基础破解思路和工具的使用方法。

天草中级班 - P18:第18课 - 白嫖无双 🎮

在本节课中,我们将学习一种有趣的逆向工程思路。我们将分析一个名为“KeyGame”的软件,其特点是无需编写补丁或注册机,而是通过调试让程序自己显示出正确的注册码。整个过程将涉及基础的动态调试与关键跳转修改。


软件初始状态与目标 🎯

首先运行目标程序。在初始界面随意输入信息并点击“Unlock”按钮。

点击后,界面提示变为“Enter Key”,要求输入一个完整的Key文件。

我们的目标是绕过这个验证,让程序直接显示有效的注册码。


程序分析与断点定位 🔍

使用查壳工具检查,确认程序无壳,由Delphi语言编写。虽然程序界面显示了五种算法,看起来复杂,但通过耐心分析可以找到突破口。

我们使用DED(Delphi反编译工具)进行辅助分析。加载程序后,分析过程可能较慢(程序约5.04M)。

在DED中定位到Form2,并进一步查看其上的控件事件。


经过查找,确认关键按钮(“Unlock”)的点击事件处理函数为Button1Click

将此地址记录下来,以便在调试器中下断点。


动态调试与注册码生成 💡

上一节我们定位了关键函数,本节中我们通过动态调试来观察注册码的生成过程。

在调试器中于Button1Click函数入口处下断点,然后运行程序。在程序界面输入任意假注册码并点击“Unlock”,程序会在断点处中断。

单步跟踪程序执行,可以看到程序会读取我们输入的假注册码和本机的机器码。继续跟踪,在一个计算过程后,真正的注册码会出现在某个寄存器或栈地址中。

在本例中,真正的注册码出现在EBP-14这个栈地址中。

真正的注册码 = [EBP-14]

我们可以在内存窗口中查看这个地址的值,例如显示为17C64。这就是程序为我们生成的正确注册码。


修改程序流程显示注册码 ⚙️

为了让程序直接显示这个注册码,我们需要修改一处关键判断。

在代码中,找到将注册码提示信息放入EDX寄存器的指令。通常,其后会有一个条件跳转来决定是显示错误信息还是显示正确注册码。

我们的修改思路是:强制让程序执行显示正确注册码的流程。这可以通过修改条件跳转(例如JNZ改为JZ),或直接NOP掉跳转指令来实现。

修改后保存程序。再次运行,点击“Unlock”按钮。



此时,程序界面会直接显示出正确的注册码17C64,而不会再要求输入Key文件。

点击两次后,软件即显示注册成功,界面非常美观。


注册信息存储位置 📁

为了更彻底地理解,我们可以探究注册成功后的信息保存在何处。

如果继续跟踪未修改的程序流程(即让验证成功通过),会发现程序将注册信息写入某个特定地址或文件。通过搜索字符串或访问注册表,可以定位到存储位置。

例如,在内存中搜索成功提示字符串,可以找到对应的存储关键数据地址。

删除该地址的数据,软件状态会恢复为未注册。这验证了17C64就是我们获取到的真正有效注册码。


核心思路总结 📝

本节课中我们一起学习并实践了一种巧妙的逆向分析方法。整个过程可以总结为以下几步:

  1. 定位关键函数:使用静态分析工具(如DED)找到核心按钮的事件处理函数。
  2. 动态调试分析:在调试器中运行,输入假数据,跟踪程序如何生成真注册码。
  3. 获取关键数据:在内存或寄存器中找到计算出的真注册码。
  4. 修改程序逻辑:通过改变一个关键跳转,迫使程序走向显示真注册码的流程。
  5. 验证与拓展:检查注册信息的存储方式,加深对程序机制的理解。

这种方法的核心在于 “借力打力”——不直接暴力破解算法,而是利用程序自身的计算能力为我们生成合法密钥,并通过极小的修改来改变其输出行为。

这种思路在破解练习或软件分析中非常实用,关键在于培养耐心跟踪和敏锐观察程序数据流的能力。

希望你能掌握这种思路,并应用到未来的学习中。再见。

天草中级班 - P19:第19课 - 白嫖无双 🛡️

在本节课中,我们将要学习软件脱壳的思路与方法,特别是如何通过手动调试来发现关键特征并设置断点。我们将以两个具体版本的加壳软件为例,演示从载入程序到找到原始入口点的完整过程。

课程概述 📖

本节课的核心是讲解脱壳过程中手动调试的思路。许多脱壳教程会直接给出断点位置,但本节课将解释这些断点是如何被发现的。我们将通过实际操作,展示如何观察程序行为、识别特征代码,并最终定位到原始入口点。

手动调试与特征识别 🔍

上一节我们介绍了课程目标,本节中我们来看看如何进行手动调试。手动调试是发现脱壳关键断点的基础,需要观察程序运行时的指令流,寻找具有规律性或标志性的代码片段。

以下是手动调试的核心步骤:

  1. 载入目标程序。
  2. 单步执行或跟踪程序代码。
  3. 观察寄存器、堆栈和代码段的变化。
  4. 记录反复出现或具有特定意义的指令序列。

实战演示:定位OEP 🎯

现在,我们进入实战环节。我们将载入一个加壳软件,并通过手动调试寻找其原始入口点。

首先,我们载入目标程序。程序运行后,会连续弹出多个窗口。

在单步跟踪过程中,需要留意代码的特征。例如,在某个时刻,我们可能会发现一段特殊的指令序列,它标志着壳代码即将执行完毕。

当我们看到类似“江湖遗珠楼”这类未实现的调用或特定跳转时,意味着程序逻辑即将发生关键转变。继续跟踪,在弹窗循环结束后,程序会跳转到一个新的地址。

这个新地址就是我们要找的原始入口点。此时,程序的加壳部分已经完成了解密和加载工作,将控制权交还给了原始代码。

找到OEP后,就可以使用脱壳工具进行内存转储,从而得到脱壳后的可执行文件。

思路总结 💡

本节课中我们一起学习了手动调试定位OEP的思路与方法。关键在于耐心观察和记录程序运行时的特征,例如特定的API调用序列、循环结构或跳转模式。通过分析这些特征,我们可以推断出壳代码的执行逻辑,并最终找到原始程序的起点。

掌握这种手动分析的能力,有助于理解不同保护壳的工作原理,即使在缺乏现成脚本或工具的情况下,也能独立完成脱壳任务。

天草中级班 - P2:第02课 - 白嫖无双 - BV1qx411k7kd 🛡️

在本节课中,我们将学习如何对一个使用ASProtect(ACProtect)加壳的程序进行脱壳。我们将从设置调试器异常选项开始,逐步定位并修复被壳偷取的原始程序入口代码(Stolen Code),最终完成输入表的修复,使程序能够正常运行。


调试器设置与运行

上一节我们介绍了课程目标,本节中我们来看看如何配置调试环境以启动加壳程序。

首先,需要设置OllyDbg(OD)的异常选项。以下是具体步骤:

  1. 在OD中,打开“选项”菜单下的“调试设置”。
  2. 在“异常”选项卡中,勾选“忽略在以下范围之外的异常”。
  3. 确保只勾选“内存访问异常”这一项,其他所有异常选项(如“内存写入异常”)均应取消勾选。
  4. 设置完成后,隐藏OD,然后直接运行程序。

这样操作后,程序将运行起来,并会在遇到内存访问异常时中断。


定位最后一次异常

程序运行后,我们需找到其最后一次内存异常发生的位置,这是接近原始代码的关键一步。

  1. 程序中断后,观察堆栈窗口。
  2. 在堆栈中找到一个较大的返回地址(巨兵),在数据窗口中跟随此地址。
  3. 选中跟随地址处的四个字节,右键设置“内存访问”断点。
  4. Shift + F9 键,让程序继续运行并忽略异常,直到触发刚设置的内存访问断点。
  5. 到达断点后,删除所有现有断点。

查找被偷取的代码(Stolen Code)

找到关键断点后,我们开始寻找被壳隐藏或修改的原始程序入口代码。

  1. 在当前位置按 F4 键运行到所选位置。这是因为此类壳会破坏程序的原始入口点(OEP),这个行为在脱壳领域常被称为“虚拟机”(VM)或“偷取代码”(Stolen Code)。
  2. 要找回这些被偷的代码,需要对不同编程语言的程序入口特征有基本了解。除了BC++和纯汇编程序,大多数高级语言编译的程序入口通常是 Push EBP
  3. 在OD中打开“运行跟踪”功能,设置条件为指令等于 Push EBP,然后进行跟踪。
  4. OD会逐步跟踪并停在符合条件的指令处。对于当前这个壳的版本,停在 Push EBP 附近的几条指令,很可能就是被偷取的原始入口代码。
  5. 记录下这几条指令的字节。返回之前的内存断点处,向上回溯数出相同数量的字节,将这部分字节用NOP指令(0x90)填充。
  6. 在填充结束后的地址处,右键选择“此处新建EIP”,将程序执行流设置到此。

修复输入表与程序转储

成功定位并修复OEP后,最后一步是修复程序的输入表,并最终导出可执行文件。

以下是使用OD的脱壳插件进行修复的步骤:

  1. 使用插件的“转储”功能。注意,在选项中不要勾选“重建输入表”
  2. 点击“修复转储文件”,选择刚才转储的程序。插件可能会报告大量无效的输入表函数指针。
  3. 尝试用“等级1”进行修复,若无法成功,则改用“等级3”修复。
  4. 在修复过程中,可能会遇到指向 GetModuleHandleAGetProcAddress 的无效指针。这是此类壳的常见处理方式,它会对这些关键API进行特殊处理,导致修复工具无法自动识别。
  5. 手动双击无效指针,根据经验,其后的有效指针通常是 LoadLibraryA。将此无效指针修正为 LoadLibraryA 的地址。
  6. 分段进行修复,避免因一次性操作过多导致程序卡死。修复完成后,保存文件。

课程总结

本节课中,我们一起学习了针对ASProtect/ACProtect壳的完整脱壳流程。

我们首先通过配置OD的异常选项来启动加壳程序,然后利用内存断点技巧定位到壳的解码核心区域。接着,我们依据程序入口的通用特征(Push EBP)找回了被壳偷取的原始代码,并修复了执行入口。最后,我们使用脱壳插件转储程序,并通过手动修正关键API指针的方式完成了输入表的修复,最终得到了可以正常运行的程序。

掌握这一流程是理解此类保护机制和进行软件分析的重要基础。

天草中级班 - P20:第20课 - 另类脱壳与绕过试用期 🛡️➡️🔓

在本节课中,我们将学习一种另类的软件脱壳与破解思路,核心目标是绕过程序的试用期限制。我们将通过分析程序调用、修改关键跳转来实现目标,而无需直接破解注册机制。


课程概述 📋

本节课演示如何通过动态调试,定位并修改程序中的关键判断逻辑,从而跳过试用期已到的提示框,使程序能够继续运行。这是一种思路上的启发,旨在拓宽解决此类问题的视野。


第一步:运行程序并定位调用

首先运行目标程序。程序会弹出一个提示试用期已到的对话框。

我们的目标是消除这个提示。为了高效定位产生此对话框的代码,我们使用调试器的“调用栈”功能(文中提及的F12对战调用方法)来快速定位到相关函数,这可以节省大量搜索时间。


第二步:下断点并跟踪程序流

在调用对话框的函数处下好断点,然后开始单步执行程序。

当程序执行到弹出对话框的代码时,我们按F7键步入该函数内部进行详细跟踪。跟踪过程中需要仔细观察代码的逻辑流向。


第三步:分析并修改关键跳转

在跟踪过程中,我们会遇到一系列条件跳转指令。这些跳转决定了程序是继续运行还是退出。

以下是关键发现:

  • 如果程序执行了某个特定的跳转指令,就会直接退出。
  • 因此,我们的目标就是阻止程序执行这个关键的跳转。

第一次尝试时,我们可能任由程序执行,会发现它最终会提示“Fail to run”并退出。这说明我们不允许它完成那个跳转。

解决方案是修改该跳转指令。例如,将条件跳转JE(相等则跳转)修改为NOP(空操作)或无条件跳转JMP,从而改变程序的执行路径。

; 修改前,可能跳向退出流程
JE 0x00401000

; 修改后,使其不跳转或跳向继续运行的流程
NOP
NOP
; 或
JMP 0x00401050

通过这样的修改,程序跳过了退出逻辑,我们发现了程序的原始入口点(OEP)。这意味着我们不仅绕过了试用提示,也完成了脱壳。


第四步:使用单步法验证思路

上一节我们通过修改关键跳转绕过了保护。本节我们用最基础的“单步法”来重新验证这个过程。

单步法的目的是仔细跟踪每一条指令,观察程序状态变化。第一次调试时,我们可能允许程序正常运行并记录下关键点(如弹出对话框的位置)。第二次调试时,就可以有针对性地在关键点进行干预。

同样,在遇到那个会导致退出的关键跳转时,我们运用刚才的思路进行修改。如果不修改,程序就会退出。修改后,程序得以继续执行。

这个方法的核心在于识别出程序流程中的“决策点”,并通过修改CPU指令来影响决策结果。


总结与思路拓展 💡

本节课我们一起学习了一种绕过软件试用期限制的另类方法。我们通过动态调试定位到程序的核心判断逻辑,并通过修改一个关键的条件跳转指令,成功地让程序跳过了试用期检查。

关键公式可以概括为:
拦截关键跳转 -> 修改指令 -> 改变执行路径 -> 达成目标

这种方法的应用范围取决于具体软件的保护方式。本课程的主要目的是提供一种解题思路:在面对软件保护时,除了直接破解算法,还可以从程序流程控制的角度入手。许多解决方案依赖于灵活的思考和细致的观察。

天草中级班 - P21:第21课 - 白嫖无双 - BV1qx411k7kd 🔓

在本节课中,我们将学习如何破解一款名为“阿达宠物园”的VB程序。我们将从分析程序结构开始,逐步定位其注册验证机制,并最终找到破解的关键点。整个过程将涉及静态分析、动态调试以及关键跳转的修改。


程序初步分析 🧐

首先,我们载入“阿达宠物园”的原程序。该程序没有加壳,但阿达系列的游戏通常会加ASI ProType的壳,这个程序是个例外。

上图是未破解的程序界面。我们的目标是使其无需注册即可直接运行,如下图所示。

上一节我们介绍了目标程序,本节中我们来看看如何开始分析。首先尝试常规的字符串搜索方法,但程序没有明显的错误提示信息。

定位关键字符串 🔍

由于是VB程序,字符串通常以Unicode(宽字符)形式存储。因此,我们需要在调试器中搜索Unicode字符串。

以下是搜索到的可用信息,其中包含程序启动时加载的一些资源名称。

在这些字符串中,我们发现了“Rage”等字样,这可能是注册验证相关的关键模块。

我们尝试查找调用或加载这些字符串的代码位置。


通过搜索引用,我们定位到加载相关图片和资源的代码段附近,地址在 B5A88 左右。

分析程序启动流程 🔄

程序启动时,会先加载主界面资源。如果检测到未注册,则会加载注册提示框。我们的思路是找到加载注册框之前的判断逻辑。

以下是程序启动时加载资源的部分代码顺序。


程序首先加载主界面元素(如 B0 Title),然后根据注册状态决定是否加载注册框。

我们在加载注册框之前的代码区域下断点,以分析验证逻辑。

深入动态调试 🐛

程序断下后,观察寄存器状态。发现程序反复操作一个名为 SCAT 的DLL模块。

这个DLL的行为非常可疑。程序持续围绕它进行操作,包括多次清零寄存器(如 ECXEDX)和进行比较。

以下是关键的分析步骤:

  1. 清零操作:一个 call 指令的目的被证实是将 ECXEDX 等寄存器清零。
    ; 示例:清零操作
    XOR ECX, ECX    ; ECX = 0
    XOR EDX, EDX    ; EDX = 0
    
  2. 虚假操作:后续有许多指令是无效操作,例如将0移入本身已经是0的寄存器。
    MOV EAX, 0      ; 无意义的操作,EAX可能本就是0
    
  3. 核心循环:程序陷入一个循环,不断比较和操作与 SCAT.DLL 相关的数据。

如果一个程序持续、高强度地操作某个特定DLL,那么这个DLL很可能就是负责注册验证的核心模块。

定位并修改验证点 ⚙️

通过逐步缩小范围,我们最终将目标锁定在一段比较和跳转代码上。这段代码决定了程序是否弹出注册框。

我们尝试修改相关寄存器的值来影响跳转结果。例如,将决定跳转条件的 ECX 值从0改为1。

; 原始可能代码
CMP ECX, 0
JE SHOW_REGISTER_BOX ; 如果ECX==0,则跳转到显示注册框

![](https://github.com/OpenDocCN/sec-notes-zh/raw/master/docs/tiancao/img/db6c25bb892286a2fa384e9919d8c361_42.png)

![](https://github.com/OpenDocCN/sec-notes-zh/raw/master/docs/tiancao/img/db6c25bb892286a2fa384e9919d8c361_44.png)

; 修改思路:让ECX不为0,从而使跳转不成立
MOV ECX, 1

我们在关键地址设置硬件断点,以防止断点被清除,然后进行多次尝试。

经过多次单步跟踪和值修改测试,我们发现将某个关键比较的结果从“相等”改为“不相等”,可以绕过注册检查。

在修改成功后,程序不再加载注册框,而是直接进入了游戏主界面。

总结 📝

本节课中我们一起学习了如何破解一个VB编写的“阿达宠物园”游戏。整个过程可以总结为以下几步:

  1. 静态分析:确认程序无壳,并搜索Unicode字符串寻找线索。
  2. 动态调试:通过下断点分析程序启动流程,定位注册验证触发点。
  3. 聚焦核心:发现程序反复操作一个特定DLL(SCAT.DLL),判断其为验证核心。
  4. 逆向修改:通过动态跟踪,找到关键的条件判断指令,并修改寄存器值或跳转逻辑,从而绕过注册验证。

破解过程需要耐心和多次尝试,特别是对于这种验证逻辑较为隐蔽的程序。关键思路在于观察程序的异常行为(如反复操作某模块),并逐步缩小关键代码的范围,最终实现对验证机制的绕过。

天草中级班 - P22:第22课 - 白嫖无双 - BV1qx411k7kd 🛡️

在本节课中,我们将学习如何对一个名为“Lineage”的国外小游戏进行破解。该游戏在试用版中存在时间限制。我们将通过分析其程序结构,定位并脱壳核心的DLL文件,最终绕过这一限制。

程序结构与初步分析 🧐

首先运行游戏。启动游戏后,会弹出一个提示框,显示试用版剩余时间:“You can play for 59 minutes in the trial version”。

细心观察任务管理器中的进程列表,会发现程序载入后,除了主EXE文件,还有一个DLL文件在运行。这表明游戏的主要逻辑可能封装在DLL中,而EXE程序只是一个启动外壳。

从文件大小也能佐证这一点:EXE文件约为396KB,而DLL文件超过700KB。

使用调试器定位DLL 🛠️

对于这类程序,我们可以使用调试器(如OD)进行分析。首先,用OD载入游戏的EXE文件。

为了让程序运行起来并将DLL的所有资源释放到内存中,我们需要下一个特定的断点。这个断点是 LoadLibraryALoadLibraryExA。其作用是拦截程序加载动态链接库的行为。

在OD中下好断点后,按 Shift + F9 运行程序。此时,EXE外壳程序界面会出现。点击“开始游戏”。

随后,程序会中断在断点处。观察OD的寄存器窗口(例如EAX),其值会发生变化。连续按 Shift + F9 运行并中断几次,直到寄存器值不再变化,且鼠标光标恢复正常。这通常意味着DLL已完全解码并加载到内存中。

从内存中提取DLL文件 💾

当确认DLL已完整加载后,我们需要将其从进程内存中提取(脱壳)出来。

打开LordPE工具,在进程列表中找到游戏进程。右键选择该进程,然后点击“完整转储”。在保存时,将文件后缀名从默认的 .dll 改为 .exe

接着,结束原游戏进程。尝试运行我们刚转储出来的新文件。此时可能会发现程序无法运行。

解决转储文件无法运行的问题 🔧

程序无法运行可能有多种原因。我们需要返回OD,重新进行调试和转储操作。

重新载入EXE,下相同的断点。关键在于中断后需要按 Shift + F9 运行三次(具体次数需通过调试跟踪确定,本例中为三次)。在第三次中断后,DLL才处于完全解压、可被正确转储的状态。

再次使用LordPE进行完整转储,并保存为 .exe 文件。如果仍然失败,可以尝试使用LordPE的“用PID打开进程”功能,或检查程序是否带有反调试机制。有时,转储工具的不同(如使用PETools)也可能影响结果。

如果转储成功,运行新文件,原先要求注册的启动框将不再出现,意味着时间限制已被移除。

核心步骤与注意事项总结 📝

本节课我们一起学习了针对“外壳EXE+核心DLL”结构程序的破解流程。

  1. 分析结构:通过进程列表和文件大小,识别出核心逻辑在DLL中。
  2. 调试拦截:在OD中对 LoadLibraryA 等API下断点,使程序中断在DLL完全加载的时刻。
  3. 内存转储:使用LordPE等工具,从进程内存中将完整的DLL转储为可执行文件。
  4. 调试技巧:关键点是找到正确的转储时机(例如,本例中需在中断后运行三次)。这需要耐心调试和观察。
  5. 工具与反调试:不同的转储工具可能效果不同。程序可能含有反调试措施,导致过程不稳定,需要多次尝试。

核心操作代码/命令示意

  • 下断点:bp LoadLibraryA
  • 运行至断点:Shift + F9
  • 转储内存:使用LordPE的“完整转储”功能。

最终,通过提取并修复内存中的DLL映像,我们成功移除了该游戏的试用时间限制。如果不成功,请检查断点位置、转储时机,并多尝试几次。

天草中级班 - P23:第23课 - 白嫖无双 🛡️➡️🔓

在本节课中,我们将学习如何对一个使用“address-protect”保护的软件进行脱壳与破解。该保护强度较高,我们将通过OllyDbg(OD)和Import REC等工具,定位关键跳转,修改关键数据,最终解除软件的功能限制。


一、 程序载入与初步分析 🔍

首先,我们使用OllyDbg载入目标程序。

载入后,程序停在入口点。

单步执行时,发现程序存在往回跳转的循环结构。这通常是壳进行解码或解压的典型特征。我们在该循环处下一个硬件断点(F2断点),以便分析。


二、 处理解码循环与定位OEP 🎯

程序内部存在大量花指令。在地址2966处,可以看到类似“石精制”的代码。ECX寄存器被赋值为16,然后跳转到B96

F9运行,程序会执行一次解码循环。如果循环次数很多,手动跟踪效率低下。我们可以尝试修改EIP(指令指针)直接跳过这个解码循环。

成功跳过循环后,我们打开内存映射窗口。首先忽略所有异常,然后在.text等代码区段上设置内存访问断点。

再次运行程序。

发现内存访问断点未能成功中断。我们换用硬件执行断点(F2)。

程序中断在一堆看似乱码的指令中,这是从壳的一个跳转过来的。我们观察堆栈,发现一个CALL指令,其参数可能指向原始程序入口点(OEP)。

在反汇编窗口中跟随该地址,并在此处下硬件执行断点。按Shift+F9(忽略异常运行)让程序继续执行。

程序中断后,观察EDI寄存器,其值就是我们要找的OEP

我们直接跳转到EDI指向的地址,并在此处下断点。再次按Shift+F9运行,程序将停在OEP。


三、 脱壳与修复导入表 💾

成功到达OEP后,就可以进行脱壳了。打开Import REC工具。

Import REC中,选择我们正在调试的进程,并填入OEP的地址(RVA值)。点击“自动查找IAT”,然后点击“获取输入表”。

此时可能会发现非常多的无效指针。点击“修复转储”,选择刚才从OD中脱壳保存出来的文件,Import REC会自动修复这些无效指针。

修复完成后,我们得到一个可运行的脱壳后程序。

运行脱壳后的程序。

程序界面显示许多功能被“锁”住了,并弹出提示框“I‘m sorry...”,表明这是一个试用版本,功能受限。




四、 分析并破解注册验证 🔑

我们的目标是解除这些功能锁。在OD中重新载入脱壳后的程序进行破解分析。

在反汇编窗口中搜索字符串,寻找关键提示信息。我们搜索到了包含“Tarot”的字符串。

找到了两处引用。我们先分析第一处。

该处是一个函数调用(如Foo_Tarot),这很可能是验证功能是否解锁的关键函数。我们在此处下断点。

在OD的异常设置中,忽略所有异常(勾选所有选项),以免影响程序运行。

运行程序,并点击菜单栏的“About”或触发验证的按钮,程序会在我们下的断点处中断。

观察中断处的代码:

TEST AL, AL
JNZ SHORT XXXXXX

这段代码的意思是测试AL寄存器的值。如果AL不等于0(JNZ跳转成立),程序会跳转到“功能锁定”的流程;如果AL等于0,则不跳转,继续执行“功能解锁”的流程。

因此,我们的目标是让AL不等于0

向上追溯AL值的来源,发现它来自局部变量[EBP-2]

MOV AL, BYTE PTR SS:[EBP-2]

我们在给[EBP-2]赋值的指令上下断点,发现它被赋值为0

我们将此赋值语句的结果改为1

修改后,继续单步执行。此时AL=1TEST AL, AL的结果非零,JNZ条件成立,程序跳转到了我们希望的分支。

将此处修改保存到可执行文件。

运行破解后的程序,所有功能锁都已消失,软件可以正常使用。




五、 处理第二处验证点 ✅

之前我们发现了两个“Tarot”字符串引用。现在来验证并破解第二处。

在字符串列表中定位到第二个“Tarot”引用,并跟随到反汇编代码处。

其代码结构与第一处非常相似。同样在关键判断处下断点,运行程序使其中断。

采用同样的方法,找到决定AL值的源头,并将其从0修改为1

保存修改。

运行程序,软件启动后直接显示为已注册的“复活”版本,所有功能均可正常使用。



至此,两处关键验证均已破解,达到了解除软件限制的目的。




六、 总结与延伸思考 🤔

本节课中,我们一起学习了针对一个较强保护壳的完整脱壳与破解流程:

  1. 脱壳阶段:使用OllyDbg跟踪解码循环,通过硬件断点定位原始程序入口点(OEP),然后利用Import REC工具成功脱壳并修复导入表。
  2. 破解阶段:在脱壳后的程序中,通过搜索关键字符串定位验证函数。分析关键跳转逻辑(TEST AL, AL / JNZ),逆向追踪关键数据的来源,并通过修改其值(将0改为1)来改变程序执行流程,最终解除了软件的功能限制。

需要注意的是,本次破解的程序可能未启用最强的保护选项。如果启用了更多反调试或代码混淆技术,破解难度会增大,甚至可能出现假死等现象。建议大家课后可以寻找更多相关资料进行深入研究与实践。


再见。

天草中级班 - P24:第24课 - 白嫖无双 - BV1qx411k7kd 🔓

在本节课中,我们将要学习一种名为“附加挂式”的破解方法。这是一种通过附加到已运行进程进行动态调试的技术,尤其适用于某些无法直接用调试器(如OD)启动的程序。我们将通过一个具体的软件实例,演示如何定位关键校验点,并使用两种修改方法实现破解。


概述与问题引入

首先,我们遇到了一个目标软件。该软件本身没有加壳,但存在一个特殊现象:当它在任务栏中已有实例运行时,如果我们尝试用OD直接载入并运行第二个实例,程序会无故退出,导致动态调试无法进行。

上一节我们介绍了调试的基本概念,本节中我们来看看如何应对这种“无法直接调试”的情况。

为了复现问题并达到教学效果,我们需要先运行一个软件实例,并确保它留在任务栏(即进程仍在运行)。此时,再用OD载入同一个程序,会发现程序立即退出,无法正常调试。


静态分析与关键点定位

既然动态调试受阻,我们首先尝试静态分析,寻找软件注册验证的关键逻辑。

用OD载入程序,直接运行并尝试注册,会弹出一个错误提示。我们的目标是找到判断注册是否成功的关键跳转。

以下是寻找关键跳转的步骤:

  1. 在OD中搜索可能的字符串,例如“Register”或错误提示文本。
  2. 通过字符串引用,定位到相关的代码区域。
  3. 在代码区域分析条件跳转指令,找到决定注册成功与否的判断点。

在本例中,我们找到了一个关键位置。该处代码逻辑是:测试AL寄存器的值,如果等于0则跳转(显示注册失败),反之则继续(注册成功)。

核心判断逻辑可以用以下汇编伪代码描述:

TEST AL, AL
JZ   Label_Registration_Failed

因此,破解的直接思路是让AL的值不等于0,或者修改跳转指令,使其必然不跳向失败分支。

然而,简单修改此处可能无效。因为该软件进行了两次校验。第一次校验时若修改跳转,可能导致第二次校验逻辑错误。我们需要一个更稳妥的方法。


“附加挂式”动态破解法

当程序无法直接用OD启动调试时,我们可以采用“附加挂式”方法。其核心思路是:将OD附加到已经运行在系统中的目标程序进程上,进行动态调试和修改。

以下是具体操作步骤:

  1. 首先,确保目标程序的一个实例已经在运行(并最小化到任务栏)。
  2. 打开OD,不要直接载入文件,而是使用 “附加” 功能。
  3. 在进程列表中选择正在运行的目标程序进程。
  4. 附加时,OD可能会弹出一些警告或程序看似退出,这通常是正常的。观察OD的状态栏,会发现程序已中断在系统模块(如ntdll)中。
  5. 在之前静态分析找到的关键地址处下断点。
  6. F9让程序继续运行,然后在软件界面进行注册操作,触发断点。

成功附加并中断后,我们就能够在动态环境中观察和修改程序逻辑。


两种破解修改方法

在动态调试环境中中断到关键点后,我们可以实施修改。以下是两种有效的修改方案:

方法一:修改判断条件
将决定跳转的TEST AL, AL指令修改为TEST EAX, EAX或其他确保结果非零的操作,或者直接将条件跳转JZ改为无条件跳转JMP或相反条件JNZ

; 原指令
TEST AL, AL
JZ   XXXXXX

; 修改后(示例1,强制AL为非零值)
MOV  AL, 1
NOP
JZ   XXXXXX

; 修改后(示例2,反转跳转条件)
TEST AL, AL
JNZ  XXXXXX ; 或直接 JMP XXXXXX

方法二:绕过校验逻辑
直接修改指令,将AL的值设置为1(或其他非零值),然后继续执行。

; 原指令
... (某些操作影响了AL)
TEST AL, AL

![](https://github.com/OpenDocCN/sec-notes-zh/raw/master/docs/tiancao/img/e469bef188b3cd9e7995f15c4e285f60_5.png)

; 修改后
MOV AL, 1
NOP ; 或跳过原操作
TEST AL, AL

在本次实例中,我们分别尝试了这两种方法,均成功实现了注册。


方法总结与练习要点

本节课我们一起学习了“附加挂式”破解法。这种方法的核心价值在于处理那些对调试器敏感、无法直接运行调试的程序。

总结关键点如下:

  • 适用场景:程序无法用OD直接启动调试时(如进程冲突、反调试等)。
  • 核心步骤:先运行程序,再用OD的“附加”功能连接其进程进行调试。
  • 破解逻辑:结合静态分析找到关键点,在动态附加后修改校验逻辑(如AL寄存器值或跳转指令)。

课后练习时请注意:
为了模拟真实场景,请先运行目标程序并关闭其窗口(确保进程仍在后台),此时任务栏应有其图标。然后再用OD尝试打开或附加,你将重现“直接运行即退出”的问题。请使用本节课教授的附加方法进行破解练习。

这是一种重要的思路,许多现代软件都采用了类似的保护机制,使直接调试变得困难。掌握附加调试技术能有效应对此类情况。


本节课中,我们一起学习了“附加挂式”破解法的原理、操作步骤和两种具体的修改实现方法。

天草中级班 - P25:第25课 - 灰鸽子木马分析与手动脱壳 🕵️♂️

在本节课中,我们将学习如何分析一个被多层壳保护的“灰鸽子”木马样本,并手动完成脱壳过程。课程将涵盖从识别入口点、手动修复导入地址表到初步分析木马行为的关键步骤。


发现与初始运行

上一节我们介绍了课程目标,本节中我们来看看样本的初始状态。

运行样本后,发现系统出现异常。一个未知进程生成后立即退出,这提示我们可能中了病毒。


脱壳过程:定位与循环

由于样本被加壳,我们首先需要使用调试器进行分析。以下是初步分析步骤:

  1. 使用OllyDbg载入样本,忽略所有异常。
  2. 单步跟踪时,发现入口点被伪装。使用F4(运行到所选)快速执行。
  3. 程序出现回跳,在一个地址范围内循环,这是壳的典型特征。


寻找原始入口点

上一节我们遇到了壳的循环,本节中我们来看看如何突破它。

直接使用F4可能导致程序直接运行。我们尝试其他方法。

  1. 在关键跳转处下断点(F2),但程序不执行。
  2. 再次使用F4,观察到两个特征点出现,其模式类似北斗壳,但前面可能加了两层壳或混淆代码。
  3. 跟随跳转,最终到达一个地址8BF91,这里被认为是原始入口点。

手动修复导入地址表

成功定位OEP后,下一步是修复程序的导入函数表。

  1. 尝试使用脱壳工具的“自动搜索IAT”功能,但未发现有效IAT,说明需要手动修复。
  2. 在代码段中向上查找,找到一个跳向API函数的指令,此处是IAT的起始位置。
  3. 记录起始地址(例如00000000),并设置一个足够大的大小(如1000)进行抓取。后续产生的无效指针是因大小设置过大造成的。
  4. 完成转储,生成脱壳后的文件。脱壳成功后,程序图标发生变化。

初步行为分析

脱壳完成后,我们可以开始分析木马的核心逻辑。以下是关键代码片段分析:

  1. 进程名检查:程序比较当前进程名与一个特定名称。若不同(AL=0),则发生跳转。我们可以修改此处进行测试。
    CMP [eax], ebx  ; 比较两个名称
    JNZ somewhere   ; 如果不相等则跳转
    
  2. 网络与文件操作:分析发现CopyFileSleep等函数调用,以及网络活动监控代码。
  3. 持久化机制:木马会向多个注册表路径写入键值,实现开机自启动。清理非常麻烦。
  4. 进程注入:木马倾向于将自身注入到svchost.exeexplorer.exe等系统关键进程中。

尝试修改与清理

基于以上分析,我们可以尝试进行一些修改,例如改变其注入的进程名。但需注意,直接修改字符串可能因长度和校验问题导致程序无法运行。

清理此类木马的建议:

  1. 在安全模式下进行操作。
  2. 检查并清理注册表中的多个自启动项。
  3. 在文件系统中显示所有隐藏文件和文件夹,查找并删除在System32等目录下生成的恶意文件。

本节课中我们一起学习了如何手动为一个多层壳保护的木马样本脱壳,并初步分析了其核心行为,包括进程检查、文件复制、注册表持久化和进程注入。掌握这些步骤对于理解和应对复杂恶意软件至关重要。

天草中级班 - P26:第26课 - 白嫖无双 - BV1qx411k7kd 🔓

在本节课中,我们将学习如何分析并修改一个名为“SprocTek”的软件。我们将使用OD(OllyDbg)工具,通过对比分析、定位关键函数和修改关键跳转等方法,来了解其运行机制并尝试进行修改。


工具准备与初步分析 🛠️

上一节我们介绍了课程目标,本节中我们来看看具体的操作步骤。首先,我们使用OD工具载入目标程序。

运行程序后,发现程序窗口一闪而过,这表明程序可能存在自校验或退出机制。原版程序可以正常运行,因此我们将采用对比分析的方法。

以下是具体步骤:

  1. 同时打开两个OD,分别载入原版程序和待分析程序。
  2. 通过对比两者在启动和退出时的行为差异,快速定位关键代码。

程序启动后退出,通常会调用退出函数。我们关注 ExitProcess 函数。


定位退出函数与关键调用 🔍

上一节我们启动了对比分析,本节中我们来看看如何定位导致退出的具体代码。

在OD中,对 ExitProcess 函数的每个调用参考处设置断点。或者,可以跟进该函数内部,然后在函数头部下断点。

下断点后,按F9运行程序,程序会中断在 ExitProcess 的调用处。此时,观察堆栈,可以看到返回地址。

既然这里调用了退出函数,那么其调用者就是导致退出的关键代码段。我们需要向上查找这个调用发生的函数首部。

程序退出通常是一个 call 指令调用的结果。执行到这个调用后,程序便会逐步退出。

在调用 ExitProcess 的代码附近,利用OD的“查找参考”功能(选择“命令”),可以快速找到所有调用此地址的代码位置。

以下是操作流程:

  1. 对找到的几处调用地址分别下断点。
  2. 重新载入程序,按F9运行。
  3. 程序会中断在其中一个调用处,这里就是导致退出的关键逻辑所在。


分析校验逻辑与修改方案 ⚙️

上一节我们定位到了关键调用点,本节中我们来看看此处的具体校验逻辑。

程序中断后,我们观察周围的代码。这里存在一个比较操作,可能是文件大小校验。

首先,查看原版程序的文件大小。然后,在调试器中查看校验值。例如,代码中可能出现一个类似 cmp 的指令,将某个值与固定值(如 0x7A120)进行比较。

使用计算器将十六进制值 0x7A120 转换为十进制,得到 500,000 字节(约500KB)。这意味着程序可能设定了文件大小上限。

而当前待分析的文件大小已超过这个限制,因此校验失败,程序走向退出流程。

修改方案有两种:

  1. 修改关键跳转:找到校验失败后的跳转指令(如 jejne),将其改为无条件跳转(jmp),绕过退出。
  2. 修改比较值:直接修改代码中的比较值(如将 0x7A120 改为一个更大的值,例如 0x200000 代表2MB),使其大于当前文件大小。

修改完成后保存文件,即可尝试运行。


破解思路延伸与总结 💡

上一节我们解决了文件大小校验,本节中我们简单延伸一下注册验证的破解思路。

在修改了文件校验后,程序可能还会进行用户名和注册码验证。我们可以继续在OD中跟踪相关代码。

通常在获取用户名后,程序会进行长度判断。例如,可能要求用户名长度大于或等于8位。相关汇编代码可能如下:

cmp eax, 8
jl  exit_label ; 如果小于8则跳转到退出

之后,程序会获取并验证注册码,这里包含核心的算法比较。我们可以通过分析比较前后的数据变化,或者尝试修改关键跳转来绕过验证。

本节课中我们一起学习了使用OD进行软件分析的基本流程:

  1. 通过双OD对比和定位 ExitProcess 调用,快速找到程序退出点。
  2. 分析退出点附近的校验逻辑(如文件大小比较)。
  3. 掌握两种常见的修改方法:修改跳转修改关键数据
  4. 初步了解了用户名/注册码验证的跟踪思路。

关键点在于耐心跟踪、理解汇编指令的含义,并大胆尝试修改。请大家课后自行练习,巩固所学知识。

天草中级班 - P27:第27课 - SVKP壳手动脱壳与IAT修复教程 🔧

在本节课中,我们将学习如何手动脱掉SVKP壳,并修复其导入地址表。这是一个相对少见但有一定强度的壳,课程将重点讲解手动查找IAT和修复的方法,这是逆向工程中的一项基础且重要的技能。

概述 📋

本节课是倒数第二课,我们将处理一个SVKP加壳的程序。由于自动脱壳工具可能无法完美修复其IAT,因此需要掌握手动查找和修复IAT的技术。我们将从忽略异常开始,逐步定位OEP,并最终完成程序的修复。


第一步:忽略异常并定位OEP

首先,我们需要使用OllyDbg载入加壳程序。程序运行时会产生异常,这是壳的保护机制之一。

以下是处理异常的步骤:

  1. 运行程序后,OD会提示异常。在OD下方的异常提示框中,将这个异常添加到忽略列表。这是一个基础操作。
  2. 使用 Shift+F9 让程序继续运行并中断在异常处理代码处。这个异常可能会出现两到三次,持续按 Shift+F9 即可。
  3. 程序中断后,删除所有硬件断点,然后按 Alt+F9 执行到返回,即可来到程序的原始入口点附近。


成功到达后,界面应类似上图。我们可以看到一个明显的 push 指令,这是OEP的常见特征。此时,我们已经成功定位到程序的原始入口点。


第二步:手动查找IAT

上一节我们找到了OEP,本节中我们来看看如何手动查找被加密的导入地址表。SVKP壳会加密IAT,使其无法被自动工具识别。

以下是查找IAT的步骤:

  1. 在OEP处,使用 Ctrl+B 搜索十六进制序列 FF 25FF 25 是IAT中 jmp [地址] 指令的机器码,是IAT的起始标志之一。
  2. 搜索后,我们会找到一些 FF 25 的调用。其中,有些地址已被OD识别为API函数名,而有些则显示为数字地址,这表明它们被加密了。
  3. 记录下这些未被识别的地址,例如 402010402014。它们就是我们需要修复的IAT项。


尝试使用Import REC的“等级1”自动修复,通常会失败,因为壳修改了IAT的结构。这引出了我们下一步需要的手动修复技术。


第三步:手动修复IAT项

由于自动修复失败,我们必须手动找出每个加密地址对应的真实API函数。这是本节课的核心手动技巧。

以下是手动修复单个IAT项的通用方法:

  1. 重新载入程序,并在记录下的加密地址(如 402010)上设置内存写入断点
  2. 运行程序 (Shift+F9),程序会在壳向该地址写入数据时中断。
  3. 中断后,单步跟踪 (F7/F8),观察是哪个函数被写入到了这个地址。通常,会看到一个 CALLJMP 指令将API函数的地址移动到目标位置。
  4. 记下这个API函数的名称或地址。
  5. 对另一个加密地址(如 402014)重复上述过程。


通过这种方法,我们可以逐个找出所有被加密的IAT项所对应的真实API。


第四步:使用Import REC进行最终修复

在手动获取了所有加密IAT项的真实API信息后,我们就可以使用Import REC来完成最终的修复工作。

以下是修复步骤:

  1. 在OD中,确保程序停留在OEP。记下OEP的地址(例如 00401000)。
  2. 打开Import REC,选择正在调试的进程。
  3. 在Import REC中填入正确的OEP地址,然后点击“自动查找IAT”。通常效果不佳,直接点击“获取输入表”。
  4. 在出现的函数列表中,找到那些显示为“无效”或“未知”的项,它们对应着我们之前手动找到的加密地址。
  5. 双击这些无效项,在弹出的窗口中,手动填入正确的API函数名称(RVA)。
  6. 所有无效项都修正后,点击“修复转储”,选择之前从OD中脱壳出来的程序文件。
  7. Import REC会生成一个修复后的新文件(通常名为 *_dumped.exe)。

修复完成后,运行新生成的文件,测试程序功能是否正常。如果修复正确,程序应能正常运行。


总结与注意事项 🎯

本节课中我们一起学习了手动脱壳SVKP并修复其IAT的完整流程。关键点在于手动定位加密的IAT地址,并通过内存写入断点追踪其真实的API函数。

核心公式与概念:

  • 定位IAT:在OEP附近搜索 FF 25 (jmp [iat_entry])。
  • 手动修复:对加密地址下内存写入断点,跟踪并记录真实的API地址。

注意事项

  1. 调试过程中,某些软件(如屏幕录像专家)可能会干扰OD的调试,导致返回地址错误或行为异常。若遇问题,可尝试关闭可能冲突的软件。
  2. 手动查找IAT是一项基础且通用的技能,在面对未知壳或强壳时非常有用。
  3. 修复后的程序若运行报错,需检查是否所有必要的IAT项都已正确修复,或是否存在其他反调试机制。

通过本课的学习,你掌握了应对IAT加密壳的一种有效方法。请多加练习,以熟练运用这项重要的逆向工程技术。

天草中级班 - P28:第28课 - 课程总结 🎓

在本节课中,我们将对天草中级班系列课程的核心内容进行回顾与总结。我们将梳理所学的几种主流强壳的脱壳方法、关键技巧以及一些通用的破解思路,帮助你巩固知识体系。

1. 穿山甲壳总结 🐢

上一节我们介绍了课程的整体结构,本节中我们来看看第一个重点:穿山甲壳。这是一种应用非常广泛的强壳。

穿山甲壳主要分为单进程版和双进程版,同时各有标准与非标准变种。

以下是穿山甲壳的主要类型与应对思路:

  • 单进程版:脱壳相对简单。通常可以采用两次断点法,或一次断点在00401000代码段下内存访问断点的方法。但并非所有软件都适用00401000段下断。
  • 双进程版:核心在于处理双进程转单进程的过程。标准版有固定的结合模式。
  • 非标准版:脱壳手法多样。例如在第14课中讲解的方法较为易懂。修复时可能需要使用特定工具。

在辨认穿山甲壳时,可以依靠经验判断,网上也有大量相关的脱壳文章可供参考。

2. SVKP壳的处理 🔐

接下来,我们探讨一个处理起来非常麻烦的壳:SVKP。由于其StoneCode的保护,网上公开的有效处理方法极少。

本课程介绍了两种处理SVKP壳的方法,请务必技术保留。处理IAT(导入地址表)的前期手段是固定的。

以下是SVKP壳的关键点:

  • 模拟跟踪法:此方法可以绕过对IAT的繁琐处理。即使存在无效指针也无需手动剔除,因为它们会自动查询到我们新增区段中的IAT数据。

3. 不脱壳破解与OSProtect 🛡️

本节我们聚焦于不脱壳破解技术,以OSProtect为例。其调试手段与初级班内容相似,但需特别注意代可调试。

以下是OSProtect相关要点:

  • 调试基础:进行代可调试时,需使用h(单步步入)断点。这是非常基础的操作。
  • OSProtect 1.23RC4:这是一个非常有趣的软件,建议多练习。其脱壳与修复方法需要掌握。
    • 特征:最后一次异常通常会停在XOR(异或)指令,后跟两个JMP(跳转),最后是RETN(返回)。
    • 破解注册名:脱壳后可立即破解注册名。方法是在第二次硬盘指纹校验时,在代码段(00401000)下F2断点,返回后观察EAX寄存器存放的地址,该地址即指向注册名。

4. AceProtect 及其变种 🎭

现在,我们来看AceProtect及其变种壳。这类壳会修改PE头,给手动修复带来一些麻烦。

以下是AceProtect系列壳的脱壳思路:

  • 脱壳方法:尝试在LoadLibraryA函数上下两次断点并返回,通常可到达OEP(原始入口点)附近。
  • 通用技巧:主要方法是忽略异常。根据情况选择忽略INT3中断或内存访问异常,然后通过“中跟随”并下内存访问断点(按两次F2)来定位。
  • 寻找系统调用:可采用模拟跟踪或第14课的方法。
  • 修复工具:修复时,可单独用ImportREC(等级3),或使用特定插件,后者非常好用。
  • 指针处理:AceProtect会对MessageBoxA等指针进行处理。如果在user32的函数列表中下断无效,则很可能是MessageBoxA被处理了。

关于AceProtect的IAT和系统调用的完美处理方法,将在高级班中深入讲解。

5. 其他强壳与技巧 💡

除了上述主流壳,课程还涉及其他一些加密强度较高的壳。

在这一部分,你需要掌握的是色中查找函数的方法,即下内存访问断点。不同版本思路类似,但壳的种类日新月异,需要持续学习。

6. 奇妙的破解方法 🪄

本节介绍一些特殊的破解思路,特别是挂式破解法

这种方法适用于破解那些需要输入序列号或注册码的安装程序包。其核心思想是利用程序包的验证机制进行旁路。

7. 断点选择与规律 📏

你可能会问,为什么选择某些特定断点?这并非随意而为。

以下是断点选择的依据:

  • 经验规律:这些是前人通过大量单步跟踪和调试总结出的有效规律。
  • 快速定位:在跳往OEP附近时,常会出现特定函数调用。通过在这些函数上下断,可以快速到达关键位置。高级班将更加强调这些基础。

8. 自校验的应对 🔍

现代软件普遍采用自校验来防止修改。常见校验包括文件名、文件大小、CRC32或MD5校验值等。

以下是应对自校验的常见手段:

  • 双OD对比调试:这是非常有效的方法,需要用到特定的断点技巧。
  • 文件大小校验:可采用第26课讲解的方法处理,这很常见。
  • CRC32等校验:同样推荐使用双OD对比调试。这类校验对作者编程能力要求较高,因此相对少见。

9. 核心调用追踪法 🎯

最后,再次强调F12暂停法配合调用栈回溯的重要性。

此方法可应用于90%以上的软件,对于破解外挂尤其有效。破解外挂的许多思路已在初、中级班提及,关键在于开阔思路、打好基础,并将原理灵活运用。

10. 练习与进阶 🚀

理论需结合实践。课程提供了练习样本,请尝试用所学方法进行脱壳。

需要说明的是,脱壳比普通破解更为深奥。现阶段,你需要记忆各种壳的脱壳方法。随着基础不断牢固,许多原理自然会融会贯通。

建议多上技术论坛交流学习,推荐关注国内的相关安全论坛。


本节课总结:我们一起回顾了天草中级班的核心内容,涵盖了穿山甲、SVKP、OSProtect、AceProtect等主流强壳的脱壳思路、关键技巧以及自校验应对、调用追踪等通用方法。记住,技术之路在于持续练习与思考,将知识转化为解决问题的能力。再见,下次课见!

天草中级班 - P3:第03课 - 白嫖无双 🛡️

在本节课中,我们将学习如何对目标程序 Sproc 1.23RC4 进行脱壳与破解。这是一个非常经典的版本,脱壳过程较为复杂,一步出错就可能需要重来。课程将详细演示脱壳步骤,并介绍两种修复导入表的方法,最后还会讲解一种不脱壳直接破解的思路。


程序特征与准备 🔍

上一节我们介绍了程序的基本特征,本节中我们来看看目标程序的具体表现。

目标程序是 Sproc 1.23RC4。这是一个经典版本,在 Sproc 发展史上是一个重要的转折点。程序具有被抽取代码的特征,即 Stolen Code

通过观察程序入口点,可以确认它是一个 VC 编译的程序头。


定位最后一次异常 🎯

现在,我们开始进行脱壳操作。

首先,在调试器中设置异常选项。忽略除内存访问之外的所有异常,目的是运行到程序的最后一次异常处。

F9 运行程序,直到触发最后一次异常。Sproc 1.23RC4 的最后一次异常有一个典型特征:连续出现两个 retn 指令。看到这个特征即可确认位置。

在第二个 retn 指令处下断点,再次按 F9 运行到此处。


寻找原始程序入口点(OEP) 🧭

程序中断后,观察堆栈窗口。取地址 0012FF68(此为标志性地址,即堆栈中第二个 retn 下方的第二个地址)。

对该地址设置硬件执行断点(命令:hr 0012FF68)。

F9 运行,程序会中断在另一个位置。此时删除硬件断点。

从这里开始,使用 F7 进行单步跟踪。需要仔细寻找被抽取的 Stolen Code 片段。

以下是寻找过程中可能遇到的代码特征示例:

// 示例代码特征 1
mov eax, dword ptr [ebp-0x4]
// 示例代码特征 2
push ebx

一路使用 F7 单步,直到走过一个 popad 指令。之后继续单步,会发现大量代码被虚拟机(VM)保护,呈现被抽取的状态。我们需要记录下这些代码片段的地址,以便后续修复。


修复导入表(方法一) 🔧

上一节我们找到了OEP和需要修复的代码位置,本节中我们来看看如何修复被破坏的导入表。

首先,在OEP处(例如 00401000)新建一个EIP(new origin here)。

然后使用 LordPE 工具完全转存(dump)当前进程。

以下是使用 ImportREC 工具进行修复的步骤:

  1. 附加到OD载入的进程(本例中进程ID为 154)。
  2. ImportREC 中填入OEP地址(例如 00001000)。
  3. 点击“自动查找IAT”,然后点击“获取输入表”。
  4. 此时会看到许多无效的指针。注意:先不要勾选“使用有效指针”等选项。
  5. 首先尝试使用“等级1”修复,这可以修复大部分指针。
  6. 对于剩余的无效指针(例如6个),可以使用 Sproc 1.22 版本的专用插件进行修复。
  7. 修复完成后,点击“转储到文件”,选择刚才dump出的文件进行修复覆盖。

此方法修复后的程序通常可以正常运行。


修复导入表(方法二) ⚙️

除了常规方法,还有一种针对 Sproc 1.23RC4及以下版本 的特殊修复方法。

使用一个特定的辅助工具(如脚本或插件)载入加壳程序。

  1. 在工具中选择OD所在的进程线程(如 154)。
  2. 填入OEP地址。
  3. 点击修复。理论上,此方法可以一次性将所有指针修复为有效状态。

但是,在本例演示中,第二种方法修复后出现了问题:它将一个连续的指针数据(如 92 40 92 240)从中间错误地截断了,导致修复不完整。因此,我们最终仍采用了第一种更稳定的方法。


不脱壳破解思路 💡

如果脱壳过程复杂,我们也可以尝试不脱壳直接破解程序。

关键在于定位程序验证注册信息的关键点。

  1. 运行加壳程序,在调试器中注意堆栈数据。
  2. 第一次会出现“硬盘指纹”信息。
  3. 第二次会出现用户名相关的数据。
  4. 在代码段 00401000 附近下断点,按 Alt+F9 执行到返回。
  5. 观察堆栈,找到一个关键地址(例如 0012FFC0),该地址用于存放或比较用户名。
  6. 在内存中找一个空白区域(例如 003800CC),将我们希望的注册名写入该地址。
  7. 通过补丁(Patch),将原程序指向用户名的指针改写到我们写入的地址(003800CC)。这样,程序读取到的就是我们指定的“正确”用户名,从而达到破解目的。

其核心逻辑可以用以下伪代码表示:

// 原程序逻辑
if (输入用户名 == 正确用户名) {
    注册成功;
}
// Patch后逻辑
if (内存地址[003800CC] == 正确用户名) { // 该地址已被我们写入期望的用户名
    注册成功;
}


总结 📝

本节课中我们一起学习了针对 Sproc 1.23RC4 的完整脱壳流程:

  1. 通过特征定位最后一次异常和原始入口点(OEP)。
  2. 使用 ImportREC 配合等级修复及专用插件修复导入表(方法一)。
  3. 了解了一种针对低版本的特殊修复方法及其可能存在的问题(方法二)。
  4. 最后,探讨了一种不脱壳直接通过内存补丁修改关键数据来实现破解的思路。

这是一个非常经典且有趣的壳,后续课程可能还会对其不同方面进行深入讲解。

天草中级班 - P4:第04课 - 白嫖无双 - BV1qx411k7kd 🛡️

在本节课中,我们将要学习如何对一个高版本(1.41)的程序进行脱壳分析。我们将使用OD(OllyDbg)作为主要工具,通过ESP定律等方法定位OEP(原始入口点),并修复脱壳后的程序文件。整个过程会涉及识别程序结构、分析关键代码以及使用修复工具。


工具载入与初步分析 🔍

首先,使用OD载入目标程序。

每次载入程序,OD都会进行分析。为了避免繁琐,我们可以采用之前学过的方法:忽略分析,直接中断载入。在最后一次异常后,在指定位置按F2设置断点,然后删除该断点并按F4运行到此处。这样可以直接跳转到OEP附近进行观察。

观察程序结构,可以判断这是一个BC++(Borland C++)编写的程序。为了确认,我们可以与已知的BC++程序入口特征进行对比。打好基础,熟悉各种语言的入口特征非常重要。

我已经强调过多次,必须牢记这些入口特征。

大家可以看到,特征非常明显。我们将这段特征代码复制下来以备对照。


定位Stolen Code(被窃取的代码) 🧩

上一节我们识别了程序类型,本节中我们来看看如何找到被壳偷走并隐藏的代码(Stolen Code)。这与之前低版本的方法有所不同。

对于这个高版本壳,我们依然采用“忽略一次内存访问异常”的策略。隐藏OD后,程序运行。此时应用ESP定律:在栈指针(ESP)发生变化后下硬件访问断点。

单步执行并删除不必要的断点后,程序会停在关键代码处。

以下是需要找到的Stolen Code结构:

前三条指令
...
后两条指令(地址待定)

通过对比分析,可以确定前三条指令是固定的。我们需要找到的就是后面两条指令对应的地址。


到达OEP并分析关键值 🎯

为了找到缺失的两条指令地址,我们需要先到达程序的原始入口点(OEP)。

我们可以使用另一种方法:在代码段(.text)设置硬件写入断点,运行程序(F9),断下后删除断点,并在附近下F2断点。继续运行即可到达OEP。

到达OEP后,让OD进行分析。接下来分析关键值X和Y,它们对应着缺失的两条指令。

在代码中,可以看到类似以下结构的指令:

MOV EDI, [地址A]
MOV ECX, [地址B]
CMP EDI, ECX

这里,EDI和ECX寄存器中的值就是我们需要找的X和Y。通过单步执行并观察寄存器窗口,可以很容易地获取这两个地址。

有时代码会被混淆,需要仔细分析。如果单步错过了,可以重新运行程序并快速断在OEP附近。

最终,我们得到了完整的Stolen Code,包括所有五条指令。


修复脱壳文件 🛠️

现在,我们开始修复脱壳后的程序文件。

首先,在修复工具中填入OEP地址。然后处理导入表(Import Table)。将基址(ImageBase)修改为0,并将大小(Size)设置为1000

点击“获取输入表”后,可能会发现很多无效的指针。使用插件的“无效 - 切割”功能进行处理。注意,有一个指针需要特殊处理,它指向MaxDbox相关函数,必须保留。

修复完成后,剪切掉无效指针并再次修复。


测试与优化 📊

运行修复后的程序,可能会出现错误提示,但程序基本可以运行。错误源于脱壳过程中未对某些代码进行特殊处理。

观察导入表函数,可以发现它们按名称顺序排列(如i开头的在一起,K开头的在一起)。我们可以尝试选择不同的函数指针作为起点进行测试,但某些选择仍会报错。

选择timeout相关的指针进行修改后,程序运行可能不再有错误提示。

但是,为了更彻底,我们用OD载入脱壳后的文件进行验证。尝试使用F12暂停法调用堆栈,程序可能会拦截并阻止OD调试。这说明我们的修复方法还不够完美,某些代码仍未完全解码。

我们也可以尝试手动修改未解码区域的指针。观察可知,这些指针的排列有一定规律。

最后,处理文件体积。原始脱壳文件可能很大。使用LordPE等工具的重建功能,并勾选所有压缩选项,可以将文件体积显著减小(例如压缩到原来的10%),而不影响功能。

需要注意的是,这是程序的安装包。在首次运行于新系统时,可能会报错且退出异常,需要在任务管理器中结束进程。第二次运行即可正常。


总结 📝

本节课中,我们一起学习了针对高版本壳的脱壳与修复流程。

我们首先使用OD和ESP定律定位OEP,并分析了BC++程序的入口特征。接着,我们找到了被窃取的代码(Stolen Code)并确定了其完整结构。然后,我们使用工具修复了导入表,并测试了修复效果。最后,我们还探讨了如何优化脱壳后文件的体积。

关键操作可以总结为以下步骤:

  1. 载入与分析:OD载入,忽略分析,快速定位。
  2. 定位Stolen Code:使用ESP定律,对比分析找到完整代码块。
  3. 到达OEP:利用内存写入断点快速到达原始入口。
  4. 修复文件:填写正确OEP,修复导入表,处理无效指针。
  5. 测试与压缩:运行测试,使用重建功能压缩文件体积。

掌握这些步骤,你就能应对许多类似的加壳程序了。

天草中级班 - P5:第05课 - 白嫖无双 - BV1qx411k7kd 🔓

在本节课中,我们将要学习如何对一款名为“OSPROC 2.0”的软件进行不脱壳破解。该软件包含多种校验机制,如CRC校验和内存校验,破解过程具有一定的挑战性。我们将通过动态调试,逐步分析其注册算法,并最终找到有效的破解方法。


软件初步分析 🧐

上一节我们介绍了课程目标,本节中我们来看看目标软件的基本情况。软件启动后,会显示剩余题目数量等信息,并包含3800CC等提示字符串。这表明软件存在功能限制,需要正确的注册码才能解锁。


软件还包含CRC校验和内存校验等保护措施,直接运行会提示剩余题目数量。我们的目标是绕过这些保护。


定位关键代码与算法 🔍

现在我们需要对软件进行分析。首先,在反汇编工具中搜索参考字符串,例如“seo.dus”,以定位到可能与注册验证相关的代码区域。



在代码中,我们发现了“注册成功”的字符串。在其附近找到一个关键的函数调用(CALL),这是验证逻辑的核心。我们在此处下一个硬件执行断点,然后运行程序并输入一个测试注册码,使程序中断在此处。


分析第一段算法 🔢

进入关键函数后,我们开始分析其算法。以下是算法的主要步骤:

  1. 初始化:将字符串“3800CC”的地址放入ECX寄存器。EDX寄存器的初始值设为1(经过一次循环后变为2)。
  2. 循环计算:算法进入一个循环。在循环中:
    • 计算 EDX - 1
    • 根据结果,从ECX指向的字符串中取出特定位置的字符(例如第二位)。
    • 将字符的ASCII码值(十六进制表示,如‘3’变为0x33)进行累加,结果存入ESI寄存器。
  3. 循环控制EAX寄存器控制循环次数,它从字符串的末尾(第六位)开始向左移动。EDX则从字符串开头向右移动。两者相向而行,确保每个字符都被处理。
  4. 结果处理:累加完成后,将累加值(ESI)与EAX相乘,结果存入EAX。例如,计算后EAX的值变为0xB459(十进制为46169)。


分析第二段算法与注册码变换 🔄

上一节我们分析了初步的数值计算,本节中我们来看看算法如何生成最终的注册码比对值。程序继续执行,会进入另一段算法。

  1. 数值变换:程序对之前计算出的数值(如46169)进行进一步处理。例如,通过除以0xA(十进制的10)等操作,得到一个新的数值23084
  2. 字符转换:程序将这个数值(如23084)的每一位数字转换为对应的字符。例如,数字23084会被分别处理。
  3. 循环拼接:在一个循环中,程序依次取出这些数字字符,并与一个基准值(如0x3A)进行比较和运算,最终拼接成一个字符串。这个过程在堆栈上进行。



最终,算法生成了用于比对的字符串“23084”。


验证逻辑与破解关键 🗝️

算法执行完毕返回后,程序开始验证我们输入的假注册码。它计算假注册码的长度和特征,并与算法生成的结果进行比较。

一个关键的发现是:程序将注册码中的“-”减号也当作一位有效字符进行计算。例如,对于假注册码“123-23084”,程序可能将其识别为9位字符进行处理。

如果我们删除假注册码中“-”前面的部分字符(例如,删除第一个“1”),使“-23084”这部分字符串的长度和特征与算法生成的“23084”匹配,程序就会判断为注册成功。

注册成功后,软件会在目录下生成一个K文件(如obot 3800cc.k),其中包含了用户名和注册码信息。



破解方法总结与实践 ✅

以下是基于以上分析的破解步骤:

  1. 输入任意假注册码:在软件注册框中,输入一个格式为“任意字符-23084”的假注册码。例如“aaaa-23084”。
  2. 调整长度:通过删除“-”前面的部分字符,使“-”及其后面的“23084”总长度符合程序的预期。经过测试,保留“-23084”这5位字符即可。
  3. 验证成功:输入调整后的注册码(如“-23084”),点击注册,软件提示成功并生成K文件。


进一步实验发现,K文件中的注册码部分其实是由用户名计算得来,而我们输入框中的“注册码”只是触发了算法并提供了一个比对基准。我们甚至可以手动创建一个符合格式的K文件(例如,内容为用户名-23084),将其放入软件目录,也能实现绕过注册验证的效果。




课程总结 📚

本节课中我们一起学习了针对“OSPROC 2.0”软件的不脱壳破解。我们通过动态调试,逐步分析了其两段式注册验证算法,发现其验证逻辑存在缺陷:程序对注册码中“-”符号的处理方式以及最终的比对逻辑,允许我们通过构造特定格式的字符串(-23084)来绕过验证。核心的破解思路是利用算法生成的固定字符串“23084”作为注册码的后缀,并通过调整前缀长度来满足程序的校验规则

天草中级班 - P6:第06课 - 白嫖无双 🛡️

在本节课中,我们将学习如何对一个加壳的程序(OSPRUG2.X)进行不脱壳破解。我们将使用OllyDbg(OD)进行动态调试,分析其重启验证机制,并最终通过制作内存补丁来绕过注册验证。


概述与程序载入

首先,我们使用OllyDbg直接载入目标程序,并忽略所有异常提示。

载入后,我们隐藏OD窗口,然后直接运行程序。


程序运行后,我们点击“Register”按钮进入注册分析界面。这一步的分析是必不可少的。



初步分析与关键跳转定位

我们在代码中找到了一个关键位置,并在此处下断点。按F9运行后,程序在此中断。

接着我们单步执行。如果未输入用户名和注册码,程序会提示输入;输入后则跳过提示。程序会计算注册码,但我们直接继续执行。

执行过程中,程序放入了一系列字符串。由于程序带壳,直接查看算法可能不准确。

我们尝试使用这些字符串作为注册码,但后续比较显示它们是无效的。


程序最终显示“You must restart”,然后退出。这表明该软件属于重启验证型,直接修改内存提示无法完成注册。



转换思路:分析字符串与API

由于直接寻找注册码行不通,我们转换思路。在OD中查找字符串,发现与注册表相关的信息。


我们将其添加到分析列表。由于当前位于系统模块,我们需要切换到程序模块。

重新分析后,成功查找到有用字符串,其中包含“注册表”关键词。对于重启验证型软件,通常需要拦截其读写注册表的API。



由于程序加壳,使用Ctrl+N查找API可能失败。我们需要使用OD的插件“API Break”来下断点。


设置好插件并隐藏OD后,按F9运行程序,并注意观察与注册表相关的API调用。


动态调试与关键逻辑分析

程序多次中断后,我们来到了一个关键调用。按Alt+F9返回到程序领空。

在返回的代码中,我们看到了程序界面上的一些选项,如“Backup”、“Compression”等。

继续按F9运行并返回,我们最终来到了验证用户名的关键代码段。

以下是关键的分析点:

  1. 代码对用户名和注册码进行检测。
  2. 一个关键跳转指令JNZ决定了BL寄存器的值。
    • 如果跳转发生,则 BL = 0
    • 如果跳转不发生,则 BL = 1
  3. 由于当前未注册,程序会执行跳转,导致BL=0,从而走向失败流程。

我们的目标就是修改这个关键跳转,使其不跳转,从而让BL=1

修改后继续执行,程序成功显示已注册信息,并写入了注册表。


制作内存补丁

找到关键地址后,我们需要制作一个持久化的补丁。我们使用一款补丁工具。

  1. 新建工程,选择目标程序。
  2. 在编辑模式下,选择“Virtual Address”(虚拟地址)模式,因为程序加了壳。
  3. 添加需要修改的地址。我们将关键跳转指令JNZ (操作码 75)改为NOP (操作码 90)
    • 地址1: 0x5425 处的 75 改为 90
    • 地址2: 0x5426 处的 04 改为 90 (此处的04是跳转偏移的一部分,同样nop掉)
  4. 保存修改。

补丁工具提供两种加载器:

  • 普通型Loader:生成一个.exe文件,运行它即可启动已打补丁的程序。
  • 安装型Loader:生成一个安装程序,运行后会创建快捷方式,以后通过该快捷方式启动程序。

我们可以通过工具的“Setting”选项自定义补丁程序的图标。


总结

本节课中,我们一起学习了针对加壳且采用重启验证机制软件的破解方法。

  1. 动态调试:使用OD载入程序,通过API断点定位关键验证代码。
  2. 逻辑分析:分析了决定注册状态的关键跳转 JNZ 及其对 BL 寄存器的影响。
    • 核心逻辑:BL = (JNZ不跳转) ? 1 : 0
  3. 修改与验证:将关键跳转改为NOP,使程序走向注册成功分支。
  4. 制作补丁:使用补丁工具,将内存修改固化为文件补丁,并生成了两种类型的加载器。

这种方法的核心在于绕过验证逻辑,而非破解算法,适用于许多重启验证型软件。

天草中级班 - P7:第07课 - 白嫖无双 - BV1qx411k7kd 🛡️

在本节课中,我们将要学习如何对 SVKP 1.43 壳进行脱壳。这是一个使用“另类脱壳”方法的教程,核心思路是通过添加区段来修复被抽取的代码,而不是直接寻找和修复 Stolen Code。我们将使用特定的插件,并详细讲解每一步操作。


课程概述 📋

SVKP 1.43 是一个在抽取 Stolen Code(字节代码)方面非常激进的壳。本次课程将演示一种不直接处理 Stolen Code 的脱壳方法,即“添加区段法”。我们将使用 OllyDbg(OD)和一个专用脱壳插件来完成此过程。


准备工作 🛠️

在开始脱壳之前,需要完成以下准备工作。

以下是必要的准备工作列表:

  • 将专用的脱壳插件放置到 OllyDbg 的插件目录(plugins)下。
  • 确保 OllyDbg 的异常设置已根据后续步骤进行调整。

寻找伪OEP 🔍

上一节我们介绍了准备工作,本节中我们来看看如何定位程序的伪OEP(原始入口点)。

载入目标程序后,通常会遇到错误提示。我们首先需要找到被抽取代码后剩余的伪OEP部分。

以下是寻找伪OEP的步骤:

  1. 在 OllyDbg 中设置“忽略所有异常”。
  2. SFX 选项卡中选择第二个选项。
  3. 重新载入程序。程序可能会持续运行或触发异常。
  4. 如果遇到“超出边界”等异常,将其地址添加到异常列表并记录。
  5. 通过计算异常触发地址附近的代码范围,可以估算出被抽取的字节数。本例中约为92个字节。

直接在此处分析和修复被大量抽取的代码非常繁琐,因此我们将采用另一种方法。


另类脱壳:添加区段法 🧩

上一节我们尝试了寻找伪OEP,本节中我们来看看核心的“添加区段法”是如何工作的。

此方法的思路是:跟踪壳对 Stolen Code 的处理过程,获取其内存地址,然后通过插件添加新区段来“容纳”这些代码,最后修复导入表并重建程序。

以下是“添加区段法”的详细步骤:

  1. 设置调试环境:取消“内存访问异常”选项,并隐藏 OllyDbg 调试器。
  2. 下硬件断点:在代码段下 硬件访问 断点(DWORD 类型),然后按 SHIFT+F9 运行。
  3. 跟踪内存访问:程序中断后,在数据窗口跟随 EAXEDX 寄存器指向的地址,并下 内存访问 断点。继续按 SHIFT+F9 运行,直到触发最后一次内存访问异常。
  4. 定位代码处理循环:删除硬件断点,在最后一次异常附近单步跟踪。找到一个 CALL 指令,该指令通常负责处理 Stolen Code。在此处下普通 F2 断点。
  5. 使用跟踪功能:运行程序至断点处,然后使用 OllyDbg 的“运行跟踪”功能。在跟踪日志中,可以观察到壳正在处理和写入 Stolen Code 的过程。记录下关键的内存地址(例如 EAX 中的值),这通常是 Stolen Code 被还原到的起始地址。
  6. 使用插件添加区段
    • 打开脱壳插件,点击 Get Map 获取内存映射。
    • 根据上一步记录的关键地址,在映射中找到对应的区段。例如,地址 00B8xxxx 对应区段 .00B6
    • 使用插件的“添加区段”功能,为关键地址所在的区域(如 .00B6)及其后续可能相关的区段(如 .00CC)添加新的、空的区段。可以设置区段大小为 0x3800
  7. 修复程序
    • 在插件中点击 Get EIP 获取当前指令指针,然后点击 DumpUnpack 脱壳。
    • 在修复窗口中,将 OEP 修改为我们之前找到的、处理完 Stolen Code 后的正确入口地址(例如 00401234)。
    • 使用插件的“修复导入表”功能(例如 Level 1)。修复后可能会提示一些“无效指针”,这些指针通常指向我们新添加的区段(如 .00B6, .00B9),不要删除它们,直接点击 Fix
  8. 重建可执行文件
    • 使用 LordPE 等工具修改脱壳后文件的入口点(Entry Point)为正确的 OEP。
    • 最后,使用 LordPE 的“重建 PE”功能。关键点:在重建选项中,只勾选“验证 PE”,其他选项不要勾选,否则生成的文件可能无法运行。

完成以上步骤后,即可得到一个已脱壳、可运行的程序。虽然部分查壳工具可能仍会显示有壳或识别错误,但这只是因为入口点被修改过,程序本身已成功脱壳。


课程总结 🎯

本节课中我们一起学习了针对 SVKP 1.43 壳的“另类脱壳法”。

我们首先了解了 SVKP 壳会大量抽取原始代码(Stolen Code)。接着,我们没有采用直接分析并修复这些代码的传统方法,而是通过动态跟踪,定位壳自身修复代码的逻辑和内存位置。然后,利用专用插件添加新的区段来“接收”这些被还原的代码,最后通过修复导入表和重建 PE 文件来完成脱壳。

这种方法的核心优势在于规避了手动分析和修复大量 Stolen Code 的复杂过程。虽然步骤较多,但对于此类激进抽取代码的壳,这是一种非常实用且高效的解决方案。

天草中级班 - P8:第8课 - 白嫖无双 - BV1qx411k7kd 🔍

在本节课中,我们将学习如何分析一个修改了PE头的加壳程序,并掌握手动修复PE头以及使用调试器到达程序原始入口点(OEP)的方法。课程将涉及使用OllyDbg(OD)和ImportREC等工具。


课程概述 📖

本节课分析的是一个特定版本的加壳程序。该程序的主要特征是其PE头被修改,导致常规分析工具无法正常识别。我们将学习如何识别这种修改,并利用调试技巧绕过保护,最终完成脱壳。


分析被修改的PE头

首先,我们查看目标程序。使用十六进制编辑器打开后,可以发现其PE头已被完全篡改。

正常的PE文件头部包含特定的标识字符串,例如 This program cannot be run in DOS mode。但此加壳程序移除了该字符串,这是其区别于普通加壳软件的一个特点。

由于PE头异常,当使用OllyDbg载入程序时,通常会弹出错误提示。


使用调试器定位OEP

上一节我们看到了OD载入时的错误。本节中,我们来看看如何忽略异常并继续调试。

在OD中,我们需要先忽略所有异常设置并隐藏OD。接着,通过下断点来追踪程序的执行流程。

一个有效的方法是下断点在 LoadLibraryA 这个API函数上。操作如下:

  1. 在OD的命令行输入:bp LoadLibraryA
  2. 按F9运行程序,程序会在调用LoadLibraryA时中断。
  3. 按F7步入(Step Into)函数内部。

步入后,删除当前模块的分析数据,以便更清晰地查看代码。继续单步执行,最终可以到达程序的原始入口点(OEP)。

此时观察OEP的地址,例如可能是 012475,而非常见的 00401000。这证实了我们已经成功绕过外壳,到达了程序本身的代码。


使用工具进行脱壳

找到OEP后,即可使用脱壳工具进行修复。

以下是两种常用的脱壳方法:

  • 使用OD的插件:许多OD插件内置了脱壳功能,只需填入OEP地址即可完成转储(Dump)。
  • 使用ImportREC:Import Reconstruction Tool不仅可用于修复导入表,也具备脱壳(Dump)功能。在进程列表中选择目标程序,填入OEP地址,然后执行“转储”操作。

手动修复PE头的实践

除了动态调试,了解如何手动修复被破坏的PE头也很有价值。这有助于理解PE文件结构,并能作为一种反调试手段。

我们以一个未加壳的程序为例,手动修改其PE头,模拟加壳效果。

操作步骤如下:

  1. 使用十六进制编辑器打开目标程序。
  2. 定位到PE文件头的起始偏移(通常位于0x3C指向的位置)。
  3. 计算并修改SizeOfHeaders等字段的值,例如将其改为 0x8001
  4. 在PE头与第一个节区(如.text)之间插入一定数量的空字节(例如16个0x00)。
  5. 保存修改后的文件。

修改后,再用OD载入该文件,会发现它产生了与加壳程序类似的错误提示,证明了手动修改的有效性。

通过对比修改前后的文件,可以更直观地理解PE头结构变化带来的影响。


课程总结 🎯

本节课我们一起学习了针对修改PE头型加壳程序的分析方法。
我们首先识别了被篡改的PE头特征,然后利用LoadLibraryA断点配合OD调试,一步步追踪并找到了程序的原始入口点(OEP)。
接着,我们介绍了使用工具进行脱壳的流程,并实践了手动修改PE头以理解其结构原理。
掌握这些技能,对于分析各类简单的保护壳和增强逆向工程基础能力至关重要。

天草中级班 - P9:第09课 - 白嫖无双 - BV1qx411k7kd 🔍

在本节课中,我们将学习如何分析和修复一个被SVKP 1.32壳严重抽取代码的程序。我们将从定位OEP开始,逐步处理IAT,并最终修复被抽取的代码,完成脱壳。


第一步:定位OEP(原始入口点)📍

上一节我们介绍了课程目标,本节中我们来看看如何定位程序的原始入口点。

首先,我们需要找到VOP(Virtual OEP)。为了节约时间,我们采用一种快捷的方法。

在操作时,开启屏幕录像软件可能会影响调试器的断点功能,导致断点无法正常中断。

最终,我们找到了目标位置。请注意,上方的循环代码(loop loop loop loop)全部是被抽取了代码的片段。

这就是我们找到的VOP。现在,我们重新开始。


第二步:处理IAT(导入地址表)🔧

上一节我们找到了OEP,本节中我们需要处理程序的导入地址表。

首先,取消所有断点,按F9让程序运行起来。接着,我们需要找到一个特征码。这个特征码是通过分析得来的,请不要询问其来源。

以下是处理IAT的关键步骤:

  1. 定位到开始处理IAT的代码位置。
  2. 识别三个用于处理代码的验证函数。这是一个特征:通常是八个字母,但实际是六个。
  3. 将跳转指令修改为指向第一个代码处理函数,然后是第二个和第三个。
  4. 代码处理完毕后,需要跳转到末尾的ProPaid函数。

这个过程非常繁琐,因此我们使用复制粘贴来节省时间。

处理第二个函数时,我们需要交换一些指令。同时,请注意,开启屏幕录像专家可能导致硬件断点失效。关闭该软件后,断点功能恢复正常。

断点成功中断后,取消断点并返回。接着,设置一个新的断点。

这是一个特有的断点位置,大家只需记住即可。

中断三次后,取消硬件断点。此时,开始设置一个TC-EBP断点。地址12FFC0是起始点。


第三步:分析并修复被抽取的代码(Stolen Code)💉

上一节我们处理了IAT,本节我们将面对最复杂的部分:分析并修复被“偷走”的代码。

用OD载入程序,开启屏幕录像专家可能会引发一些问题。

我们定位到ESP-4的位置,按回车后来到目标代码处。这里就是开始处理Stolen Code的地方,也是我们的“噩梦”开始之处。

如果你发现是SVKP壳,并且被抽取了106字节的代码,都可以按照此方法来寻找。这要求我们对程序的入口特征非常熟悉。

此时我们使用F7进行单步跟踪。第一句是程序特有的典型代码。

继续单步执行。请注意,我已经在关键位置做了标记。从开始到Stolen Code真正大量出现,中间间隔了很长一段代码。Stolen Code的最大部分实际上在最后面。

在跟踪过程中,需要同时注意观察堆栈。这要求我们对程序入口特征有极其深入的了解,这一点我已经强调过很多次。

以下是跟踪过程中需要识别和记录的关键代码特征:

  • C/C++程序入口特征:这类开头通常不变。例如,move指令是典型的第二句。
  • 变形的代码:有些指令是经过变形的,需要特别记住。例如,sub esp, 68是C程序的一个特征。
  • 特定函数调用:如Proxyex,这是一个特征点。
  • 堆栈操作:某些pushpop操作是关键,它们将地址压入或弹出堆栈。

跟踪会持续很长时间,程序最终会自动回到之前提到的尾OEP位置。

这里就是Stolen Code聚集的最后部分。入口特征需要大家在课后按照我的方法进行练习。注意看,这里有一个R,表示第三个位置。

Stolen Code的大部分都在这里了。这一堆指令都是。例如,jmp指令就是一个跳转到OEP的典型特征。

我们找到了OEP。这里有一个特异结构,这些都是刚才找到的代码。但有三个地方非常难找:一个是程序的基本入口特征(通常是-1),另外两个是特定的push指令。

大家看这两句push指令,它们将地址压入堆栈。其中,push后跟jmp指令是C程序入口的一个典型特征。

所以,Stolen Code就是由这些片段组成的。现在,我们将它们填补回去。

粘贴后,代码刚好对齐。

效果非常好。现在,让EIP指向这里。大家可以看到实际的特征代码,例如calljmp等,都是一些特定模式。我再次强调,必须对这些特征非常清楚。

此时,我们可以进行脱壳操作。纠正镜像大小后,可以看到旧的大小和新的相同,因为被抽取的代码已经补回。进行完整脱壳。


第四步:修复导入表与总结 🛠️

上一节我们修复了代码并完成了脱壳,本节我们来进行最后的导入表修复并总结全课。

现在开始修复导入表。

大家看到只有一个无效指针。我按照常规思路,先将其剪掉。

修复后,程序是可以运行的。但是,因为SVKP壳对一些指针进行了处理,所以我们需要找出所有无效指针。

一般来说,SVKP会处理七个常规指针:三个注册函数,加上GetModuleHandleAGetProcAddress等特定函数。在1.43版本中,无效函数可能多达十几个。

我们可以通过对比一个已修复好的程序来加深理解。打开另一个加壳程序进行修复,观察有哪些无效函数。

通过对比发现,SVKP通常会处理GetModuleHandleAGetProcAddressLoadLibraryA等函数,以及三个注册函数。这是规律。

有时修复不成功,可以采用对比和排除的方法。


总结 📝

本节课中,我们一起学习了如何对付一个代码被严重抽取的SVKP 1.32壳。

  1. 定位OEP:使用特征码和断点技巧找到虚拟入口点。
  2. 处理IAT:修改跳转,引导程序正确执行IAT处理流程。
  3. 分析与修复Stolen Code:通过单步跟踪,识别C++入口特征、变形代码和关键调用,将“偷走”的代码块填补回正确位置。
  4. 最终修复:脱壳后,修复导入表,并理解壳处理特定指针的规律。

掌握这些方法需要对程序入口和结构有深刻理解,并通过大量练习来巩固。今天的课程就到这里。

谢谢大家收看,下次见。

天草流初级课程 - P1:汇编基础与寄存器入门 🧠

在本节课中,我们将学习汇编语言的基础知识,特别是与软件破解相关的核心概念。课程将涵盖数制系统、寄存器的基本功能与分类,以及如何利用标志寄存器来辅助分析和修改程序流程。


数制系统:十进制、二进制与十六进制

上一节我们介绍了课程目标,本节中我们来看看汇编中使用的数制系统。在软件破解领域,我们主要接触三种数制:十进制、二进制和十六进制。

  • 十进制:用字母 D 表示,是我们日常生活中最常用的数制。
  • 二进制:用字母 B 表示,是计算机底层运算的基础。
  • 十六进制:用字母 H 表示,是汇编语言和调试工具中默认显示的数制。

以下是数制表示的例子:

  • 十进制的 2 可以表示为 2D
  • 二进制的 2 可以表示为 0010B
  • 十六进制的 2 表示为 2H 或通常直接写作 2

二进制加法的进位规则是核心。例如,0001B(1)加 1 后,结果为 0010B(2),发生了进位。

在调试器(如OD)中,默认显示的数值是十六进制的。这一点非常重要。例如,在OD中看到数字 7,它代表的是十六进制的 7H,其值等于十进制的 7D。但如果将一个较大的十进制数(如 40D)错误地当作十六进制数输入,其实际值会变为十六进制的 28H


寄存器详解

理解了数制后,我们进入核心硬件概念——寄存器。寄存器是CPU内部的高速存储单元,用于临时存放数据、地址和控制信息。

通用寄存器

我们学习的是32位汇编,其通用寄存器在16位(AX, BX, CX, DX)基础上扩展而来。

一个32位寄存器(如EAX)可以划分为两部分:

  • 高16位:无独立名称。
  • 低16位:即原来的AX寄存器。
    • AX寄存器本身又可划分为:
      • AH:高8位(High)。
      • AL:低8位(Low)。

以下是主要通用寄存器及其常见用途:

  • EAX:累加器。是算术运算的主要寄存器,在破解算法验证时至关重要。
  • EBX:基址寄存器。在计算存储器地址时可作为基址使用。
  • ECX:计数器。常在循环操作中用作计数器。
  • EDX:数据寄存器。常与EAX配合用于乘除法运算。
  • ESP:栈指针寄存器。始终指向当前栈顶。我们熟知的“ESP定律”就是基于此寄存器。它的值会随着栈操作(如PUSH, POP)而动态变化。
  • EBP:基址指针寄存器。通常指向当前栈帧的底部,用于访问函数参数和局部变量。

标志寄存器

标志寄存器(EFLAGS)用于记录CPU指令执行后的状态信息,如是否产生进位、结果是否为零等。这些标志位直接影响条件跳转指令的执行。

以下是几个关键标志位:

  • CF(Carry Flag):进位标志。记录无符号数运算的进位或借位。
  • ZF(Zero Flag):零标志。记录运算结果是否为0。
  • SF(Sign Flag):符号标志。记录运算结果的符号(正负)。
  • OF(Overflow Flag):溢出标志。记录有符号数运算是否发生溢出。

在调试器(如OD)中,你可以直接双击这些标志位来切换其状态(0或1)。这在破解时非常有用:当你希望改变一个条件跳转(如JZ, JNZ)的路径时,可以直接修改对应的标志位,而无需费力地修改汇编代码本身。

注意JMP 是无条件跳转指令,不受任何标志位影响。

浮点寄存器

浮点寄存器(FPU)专门用于处理浮点数运算。在破解涉及浮点数验证的软件时,需要关注这些寄存器。相关的高级技巧将在后续课程中结合实例详细讲解。


实践:利用标志寄存器修改程序流程

了解了理论,我们来看看如何应用。在动态调试时,修改标志位是控制程序流程的便捷方法。

操作步骤如下:

  1. 在调试器中找到一条条件跳转指令(如 JZ, JNE)。
  2. 观察其是否跳转,并查看受影响的标志位(如ZF)。
  3. 在寄存器窗口,双击对应的标志位(如ZF),将其值置反(0变1或1变0)。
  4. 此时,该条件跳转的逻辑即被改变,程序执行流随之变化。

重要区别:此技巧主要用于破解(修改软件逻辑)。在脱壳(解除软件保护)过程中,通常不推荐直接修改标志位,而应采用“在跳转指令下一行设断点并运行(F4)”等标准方法,以避免破坏解压或解密流程导致程序崩溃。

如果不确定某条跳转指令受哪个标志位影响,可以采用试验法:依次尝试修改不同的标志位,观察跳转状态是否改变,无效则还原。


课程总结

本节课中我们一起学习了汇编的三大基础:

  1. 数制系统:明确了十进制(D)、二进制(B)、十六进制(H)的区别,以及调试器中默认使用十六进制显示。
  2. 寄存器:认识了通用寄存器(EAX, ESP等)和标志寄存器(CF, ZF等)的功能与作用。
  3. 实践技巧:掌握了通过修改标志寄存器来控制程序条件跳转的实用破解方法,并区分了其在破解与脱壳中的不同应用场景。

掌握这些基础知识是后续深入学习逆向工程与软件破解的坚实第一步。

天草流初级逆向教程 - P10:脱壳与修复实战 🛡️

在本节课中,我们将学习软件逆向工程中的一项基础技能——脱壳。我们将以一个名为“商行远程控制3.0”的软件为例,演示如何定位并脱去其外壳,并对脱壳后的程序进行修复与关键代码分析。

课程概述

本节课的目标程序是“商行远程控制3.0”的免费注册版。我们的任务是分析其保护机制,使用工具进行脱壳,并理解脱壳后程序的关键校验逻辑。

一、 初步分析与脱壳方法尝试

首先,我们尝试使用常见的脱壳方法对目标程序进行分析。

以下是几种常见的脱壳方法及其在本例中的尝试结果:

  1. ESP定律法:观察程序入口点附近的堆栈平衡,可以快速定位到原始程序入口点(OEP)。在本例中,此方法有效。
  2. 两次内存断点法:通过设置内存访问断点来追踪代码执行流程。但本例中程序区段过少,此方法不适用。
  3. 模拟跟踪法:使用OD的跟踪功能。同样因为程序结构简单,未能成功定位到有效代码。

经过尝试,我们确定使用ESP定律是定位该程序OEP的有效方法。

二、 脱壳与修复

成功定位到OEP后,下一步是进行脱壳和修复导入表。

  1. 使用OllyDbg插件(如OllyDump)的脱壳功能,从内存中将程序转储到文件。
  2. 使用导入表修复工具(如ImportREC)对脱壳后的文件进行修复。修复时,需要确保所有函数指针都有效。
  3. 修复完成后,脱壳的程序应能正常运行。

三、 关键代码分析与绕过

程序脱壳后,我们发现了其内部的一个校验机制。接下来,我们将分析并绕过它。

程序运行后,会弹出一个提示窗口。通过分析,我们发现关键跳转位于一处文件大小校验代码之后。

以下是分析过程的核心步骤:

  1. 定位关键调用:在代码中,我们找到了一个GetFileSize的API调用,用于获取程序自身的大小。
  2. 对比分析:我们同时载入原版程序(未脱壳)和脱壳后的程序进行对比。发现原版程序在获取文件大小后,有一个条件跳转被执行,从而绕过了弹窗。
  3. 逻辑分析:该跳转依赖于一个寄存器(BL)的值。在原版程序中,BL被设置为1,导致跳转发生。而在我们的脱壳版中,BL为0,程序没有跳转,从而触发了弹窗。
  4. 寻找赋值点:我们向上追溯代码,找到了将BL设置为1的关键指令位置。
  5. 实施修改:在脱壳后的程序中,修改该处指令,将BL的值设置为1。修改后,关键跳转被触发,弹窗消失。

核心修改可以用以下伪代码表示:

// 修改前
mov bl, 0
// 修改后
mov bl, 1

或者直接修改跳转指令:

// 将条件跳转改为无条件跳转
jne xxxxxxxx -> jmp xxxxxxxx

四、 区段信息修改(可选)

为了使脱壳修改后的程序更具“个性化”,我们可以修改其区段名称。

以下是修改步骤:

  1. 使用区段编辑工具(如PEditor或CFF Explorer)打开脱壳后的程序。
  2. 找到程序的第一个区段,将其名称修改为自定义内容。
  3. 保存修改。

修改后,使用查壳工具再次检查,程序将显示新的区段信息。

课程总结

本节课我们一起学习了软件脱壳的完整流程:

  1. 我们首先尝试了多种脱壳方法,并确定了适用于本例的ESP定律法
  2. 接着,我们使用工具成功脱壳并修复了程序,使其能够运行。
  3. 然后,我们通过对比分析法,定位了程序中的一个关键校验逻辑,并理解了其通过BL寄存器控制跳转的原理。
  4. 最后,我们通过修改关键代码,绕过了该校验,并介绍了修改程序区段信息的可选操作。

通过本课实践,你不仅掌握了脱壳的基本操作,更学会了如何通过静态分析与动态调试相结合的方式,分析和处理脱壳后程序的保护机制。

天草流初级破解教程 - P11:不脱壳破解入门 🛡️➡️🔓

在本节课中,我们将学习不脱壳破解的最简单途径。我们将使用一个特定工具,对一个加了ASPack壳的程序进行关键跳转的定位与修改,从而实现破解。整个过程无需完全脱壳,适合初学者理解基础的内存补丁原理。


课程概述 📋

本节课的目标是了解并实践一种不脱壳的破解方法。我们将分析一个带有ASPack壳的示例程序,使用OllyDbg(OD)定位关键代码,并借助一款专用工具直接对加壳程序打补丁。


工具与目标程序 🛠️

首先,我们使用一个名为“Kerami”的简单程序作为目标。该程序被特意加上了ASPack壳,并包含一个错误提示框。我们的目标是将其修改,使其显示正确的提示。

程序运行时的错误提示如下:


第一步:使用OD定位关键代码 🔍

上一节我们介绍了课程目标,本节中我们来看看如何使用OD进行分析。

将目标程序载入OllyDbg(OD)。

首先,我们需要到达程序的原始入口点(OEP)。这里采用ESP定律:在栈地址下硬件访问断点。

操作:在OD的命令行或寄存器窗口的ESP值上右键,选择“HW break[Access]”下断点。按F9运行程序,程序会中断在OEP附近。

删除硬件断点后,我们便可以在代码区进行分析。通过搜索字符串,可以找到错误提示和正确提示对应的代码位置。

双击错误提示字符串,OD会跳转到引用该字符串的代码处。其下方通常是显示正确提示的代码。在这两段代码之间,必然存在一个关键的判断跳转。


第二步:分析并修改关键判断 🧠

以下是找到的关键代码区域分析:

  1. 在关键跳转之前,通常有一个CALL指令用于执行判断函数。
  2. 紧随其后的便是一个条件跳转指令(如JNZ, JE),它决定了程序走向错误提示还是正确提示。

在本次示例中,我们找到了一个关键CALL和一个关键的JZ跳转。JZ跳转就是我们需要修改的目标。

按F8单步执行,会进入一个系统函数GetDriveTypeA,该函数用于获取驱动器类型。

函数逻辑

  • 函数返回值(通常放在EAX寄存器中)会与一个固定值(本例中是5)进行比较。
  • 如果相等(EAX == 5),则跳转不发生,程序走向错误分支。
  • 如果不相等(EAX != 5),则跳转发生,程序走向正确分支。

在调试器中,我们看到EAX的值是3,因此跳转发生,显示了错误提示。

修改方法
我们可以修改两处中的任意一处来改变程序逻辑:

  1. 修改比较的数值,将CMP EAX, 5中的5改为3
  2. 修改跳转指令本身,将条件跳转JZ(机器码74)改为无条件跳转JMP(机器码EB)。

在OD中直接修改后运行,程序即可显示正确提示。


第三步:使用补丁工具进行持久化修改 🧰

上一节我们在OD中完成了临时修改,本节中我们来看看如何使修改永久生效。由于程序加了壳,直接保存修改或复制修改后的代码到可执行文件会失败。

这时我们需要使用一个专门的补丁工具。该工具可以直接对加壳的程序文件进行补丁。

以下是使用该工具的核心步骤:

  1. 分析程序:将目标程序拖入工具,点击“Chart”或分析按钮。工具会分析出加壳类型,并定位到OEP附近可用于注入代码的地址。

    • 输出信息类似:OEP is around 004053A1A9
  2. 编写补丁脚本:工具允许我们写入汇编代码和补丁数据。补丁数据的格式如下:

    p[地址] [字节1], [字节2], ...
    
    • p 是补丁指令前缀。
    • [地址] 是要修改的内存地址(虚拟地址VA)。
    • [字节] 是要写入的机器码,多个字节用逗号分隔。

    例如,要将地址00401204处的74 17JZ跳转)改为EB 17JMP跳转),应写入:

    p00401204 EB, 17
    
  3. 生成补丁:编写好补丁脚本后,点击工具的“Patch”或类似按钮。工具会自动备份原文件,并生成一个已打补丁的新程序。


第四步:验证与深入学习 ✅

补丁完成后,我们可以验证修改是否成功,并学习如何检查文件变化。

验证:运行新生成的程序,此时应直接显示正确提示。

文件对比:使用十六进制比较工具(如WinHex的“文件比较”功能)对比原文件和补丁后的文件。通过对比,可以清晰地看到在特定偏移地址处,字节数据发生了改变(例如74变成了EB)。

这种方法不仅用于破解,在软件安全领域(如免杀技术中修改壳的特征)也是一种重要的学习手段。


总结与展望 🎯

本节课中我们一起学习了不脱壳破解的初级流程:

  1. 定位:使用OD配合ESP定律到达OEP,并找到程序的关键判断逻辑。
  2. 分析:理解关键CALL和跳转指令的作用,确定需要修改的位点(比较值或跳转指令)。
  3. 工具补丁:利用专用补丁工具,将修改以脚本形式持久化到加壳的程序文件中。

本节课介绍的是借助工具的初级方法。在后续课程中,我们将探讨更高级的技术,例如不依赖特定工具,直接编写Shellcode在内存中解码并修改程序,这对逆向工程的基础能力有更高的要求。

今天的课程到此结束,关键在于大家如何消化吸收这些基础概念,并为学习更复杂的技术做好准备。

天草流初级破解教程 - P12:第11课 - 破解“同意/不同意”对话框 🛡️

在本节课中,我们将学习如何分析和破解一个带有“同意”与“不同意”选项的注册对话框。我们将通过动态调试,理解程序如何根据用户选择决定后续流程,并学习如何修改程序逻辑以达到“白嫖”使用的目的。


一、目标程序与初步分析

本次的目标程序是一个屏幕录像软件的注册机。运行后,它会弹出一个对话框,要求用户选择“同意”或“不同意”。点击“同意”可以进入主界面,点击“不同意”则程序会直接退出。

我们的目标是绕过这个选择,让程序无论点击哪个按钮都能直接进入主界面。

首先,使用OllyDbg(OD)载入目标程序。

上一节我们介绍了使用OD进行基础调试,本节中我们来看看如何处理这种基于用户交互的验证。


二、定位关键判断代码

程序运行后,在OD中按F9让程序运行起来,然后尝试点击对话框上的按钮。为了找到处理这个选择的代码,我们需要在OD中设置断点。

  1. 在OD中按F12暂停程序。
  2. 查看调用堆栈(Call Stack),寻找可能负责创建或处理对话框的函数调用。
  3. 经过跟踪,我们定位到一个关键的内存地址:004083F0。这个地址存储的值将决定程序的走向。
  4. 进一步跟踪,发现程序在地址 00405215 附近调用了产生提示音的函数。为了方便调试,我们可以先将其NOP掉(填充为90)以消除噪音。

以下是定位到的关键代码区域:

004083F0 处进行赋值操作
00405215 处调用声音函数

三、分析程序执行流程

我们通过单步执行(F7/F8)来观察当点击不同按钮时,程序状态的变化。

  1. 点击“同意”时

    • 程序在 004083F0 处将值设置为 1
    • 随后,在条件跳转指令处(例如 test eax, eax / jz),因为EAX寄存器值为1(非零),所以不跳转
    • 程序继续执行,顺利弹出注册机的主界面。
  2. 点击“不同意”时

    • 程序在 004083F0 处将值设置为 0
    • 在相同的条件跳转处,因为EAX为0,所以发生跳转
    • 程序跳转到一个退出函数(如 ExitProcess),导致直接关闭。

核心逻辑可以用以下伪代码表示:

int user_choice = ShowDialog("同意", "不同意"); // 对话框函数,同意返回1,不同意返回0
if (user_choice == 0) {
    ExitProcess(); // 退出程序
} else {
    ShowMainWindow(); // 显示主界面
}

通过以上分析,我们明白了程序的基本逻辑。接下来,我们将尝试修改这个逻辑。


四、实施破解方案

根据上面的分析,我们有几种思路可以绕过这个对话框:

方案一:强制跳转永不发生

  • 原理:修改决定是否跳转的指令,使其无论条件如何都继续执行。
  • 操作:找到关键的 jzje(条件跳转)指令,将其改为 jmp(无条件跳转)或直接NOP掉。
  • 效果:即使点击“不同意”,程序也不会执行退出流程,而是继续显示主界面。

方案二:锁定返回值为1

  • 原理:修改对话框函数的返回值,让它始终返回1(“同意”)。
  • 操作:在函数返回(retn)之前,找到给EAX赋值的指令(如 mov eax, 1),确保其始终被执行。或者,在函数调用后,直接使用OD的汇编功能将EAX改为1。
  • 效果:无论点击哪个按钮,系统都认为用户点击了“同意”。

方案三:绕过对话框调用

  • 原理:直接跳过整个对话框的显示和调用。
  • 操作:找到调用对话框函数的 call 指令,将其NOP掉。但需要注意,这样可能会使程序期望的返回值(EAX)处于未定义状态,可能导致后续判断出错。因此,在NOP掉call之后,通常需要手动给EAX赋一个值(如1)。
  • 效果:程序启动后不再弹出对话框,直接进行后续流程。

在本例实践中,作者采用了方案二,将关键地址的值固定为1,成功实现了无论点击哪个按钮都能进入程序的效果。

需要注意的是,某些程序会在多处对关键变量进行清零或重赋值。因此,修改一处可能不够稳定。最稳妥的方法通常是方案一,即修改关键跳转。


五、核心概念与编程原理

为什么点击“同意”返回1,点击“不同意”返回0?这不是巧合,而是图形界面编程中的一种常见约定。

在许多编程语言和框架中,标准对话框函数(如 MessageBoxDialogBox)的返回值就采用了这种约定:

  • “是”/“确定”/“同意” 通常对应返回值 1 (如 IDOK, IDYES)。
  • “否”/“取消”/“不同意” 通常对应返回值 02 (如 IDNO, IDCANCEL)。

例如,在Delphi或VC++中,都有类似的机制。程序通过判断这个返回值来决定后续分支。理解这个原理,有助于我们在逆向时快速定位到关键判断点。


六、总结与练习建议

本节课中,我们一起学习了如何破解一个带有二元选择(同意/不同意)的对话框程序。

我们主要掌握了以下步骤:

  1. 动态调试定位:使用OD运行程序,通过堆栈和代码跟踪找到处理用户选择的关键代码区域。
  2. 流程分析:通过对比点击不同按钮时的代码执行路径,理解程序的分支逻辑。
  3. 实施修改:学习了三种常见的破解思路——修改关键跳转、固定函数返回值、绕过函数调用,并理解了各自的适用场景。
  4. 原理理解:了解了对话框返回值在编程中的常见约定,这有助于举一反三。

课后练习建议
请寻找一个带有类似“确定/取消”或“是/否”对话框的简单程序,使用本节课的方法进行实战练习。重点提升跟踪代码执行流和分析条件分支的能力。记住,耐心和细致的观察是逆向工程中最重要的品质。


谢谢观看!

天草流初级课程 - P13:脱壳实战(二)🛡️

在本节课中,我们将学习针对三种不同加壳程序的脱壳实战技巧。课程将涵盖处理被修改的PE头、修复附加数据(Overlay)以及手动查找IAT(导入地址表)等核心内容。

处理被修改PE头的程序

上一节我们介绍了基础的脱壳概念,本节中我们来看看一个PE头被修改的程序如何处理。

用OD载入目标程序时,会出现错误提示。这是因为程序的PE头已被修改。正常的PE头以“MZ”和“PE”标识开头,而此程序的相关部分被完全改掉,这是加壳程序的一种反调试手段。

以下是处理步骤:

  1. 设置OD,在系统断点处中断(选项 -> 调试设置 -> 事件 -> 系统断点)。
  2. 打开内存窗口,在PE头所在的段(通常是00401000)下F2断点。
  3. 按Shift+F9运行,程序将中断在系统领空。
  4. 单步(F8)执行,配合F4(运行到选定位置)跳过循环,最终到达程序的原始入口点(OEP)。
  5. 此程序由VB编写,OEP特征明显。使用脱壳插件(如OllyDump)进行转储(Dump)。
  6. 由于壳被报毒,脱壳后可自行处理免杀。

修复带有附加数据(Overlay)的程序

接下来,我们处理一个带有附加数据(Overlay)的NSPack壳程序。

成功到达OEP并转储程序后,使用导入表修复工具(如Import REConstructor)进行修复时,发现指针很少,提示存在无效指针。这通常意味着程序有附加数据未被正确复制。

以下是定位和修复附加数据的步骤:

  1. 使用查壳工具(如PEiD)确认程序存在Overlay。
  2. 使用十六进制编辑器(如WinHex或010 Editor)打开加壳的原程序。
  3. 找到最后一个区段(Section)的末尾。计算公式为:区段起始地址(RVA) + 区段大小(Size) = Overlay起始文件偏移
  4. 记录下这个偏移地址(例如 0x28400)。
  5. 在十六进制编辑器中,从该偏移地址开始,选中直到文件末尾的所有数据,并复制。
  6. 打开之前脱壳并修复的(不完整的)程序,跳转到文件末尾,将复制的Overlay数据粘贴进去。
  7. 保存新文件,程序即可正常运行。

手动查找IAT及处理异常程序

最后,我们分析一个行为特殊的UPX壳程序,并学习手动查找IAT的技巧。

用OD载入程序后,利用ESP定律可以快速到达OEP。程序由BC++编写。但脱壳后的程序运行异常,需要手动修复IAT。

以下是手动查找IAT的步骤:

  1. 在OEP附近的代码中,找到一个明显的系统API调用(例如 call dword ptr [xxxxxxxx])。
  2. 在该调用语句上按回车键(或右键 -> 反汇编窗口中跟随),跳转到该地址所在的内存区域。
  3. 这片充满函数地址的区域就是IAT。向上滚动找到第一个函数地址,此处即为IAT的起始地址(RVA,例如 0x1010C)。
  4. 向下滚动找到IAT的结束地址。计算IAT的大小:结束地址 - 起始地址
  5. 在导入表修复工具中,填入OEP、IAT的RVA和计算出的Size,然后点击“获取导入表”。
  6. 如果发现无效指针,可以尝试将Size适当改大(例如取整为0x1000),或手动剪裁掉无效部分。

这个特定程序奇怪之处在于:脱壳后需要修复两次才能成功;其注册机制异常,注册信息似乎与输入无关且保存在特定注册表键值中。这需要后续进行专门的逆向分析来理解其逻辑。


本节课中我们一起学习了三种脱壳实战场景:修复被破坏的PE头、为脱壳程序追加Overlay数据,以及手动定位并修复IAT。掌握这些技巧能帮助你应对更多复杂的加壳保护。

天草流初级破解教程 - P14:第13课 - 不脱壳破解实战 🛡️💻

在本节课中,我们将学习两种不脱壳破解加壳程序的方法。我们将以一个名为“pyramid”的加壳程序为例,通过实践掌握查找关键跳转、定位注册码以及制作内存注册机的核心技巧。


方法一:动态调试定位法

上一节我们介绍了课程目标,本节中我们来看看第一种破解方法:通过动态调试直接定位注册码。

首先,使用OllyDbg载入目标程序,直接按F9运行程序。

在程序界面输入假注册码“3800cc”,此时会弹出一个错误提示框。

按F12暂停程序执行,在调用堆栈中找到最顶层(最后面)的一个调用。

虽然可以直接在该处下断点,但为了掌握通用的分析思路,我们选择找到该调用入口的头部。

由于程序带壳,普通断点(F2)可能被清除,因此我们在该入口处下一个硬件执行断点。

按F9继续运行程序,然后在程序界面点击“Register”按钮,程序将在所下断点处中断。

在破解过程中,需要时刻关注几个关键窗口:

  • 返回边框(调用堆栈)
  • 注释框
  • 寄存器窗口
  • 堆栈窗口

此外,数据窗口(可通过右键转成窗口查看)在后续分析某些算法时也至关重要。

中断后,在寄存器或数据窗口中可以直接看到明码比较的注册码。将其复制保存即可。

为了理解程序流程,我们可以继续单步(F8)跟踪。程序会进入一个循环,这是其算法部分。

以下是跟踪时需要注意的要点:

  • 算法部分可能较长,需要耐心。
  • 遇到循环回跳时,不要使用F4(运行到选定位置)跳过,以免破坏流程。
  • 如果只求注册码而非分析算法,可以尝试修改关键跳转来快速到达结果判断部分。

我们成功获取了注册码,但暂时不进行注册,以便演示第二种方法。


方法二:内存注册机制作法

上一节我们通过调试找到了注册码,本节我们学习第二种方法:制作内存注册机,以自动获取注册码。

首先,让程序运行起来,然后按Alt+E打开模块列表。

在模块列表中找到程序的主模块(通常是.exe文件本身),双击进入其代码空间。

此时,即可在该模块中搜索字符串。搜索“register”找到相关提示信息。

双击“Register success”或错误提示字符串,反汇编窗口会跳转到相应代码附近。

在加壳程序中,代码可能被混淆。我们需要向上查找一段清晰的代码起点(通常是PUSH EBP)。

在该清晰代码段的起始处下硬件执行断点。

在程序界面输入假注册码并点击注册,程序将中断。

单步跟踪,程序会读取我们输入的注册名和注册码。

关键指令出现在比较环节:CMP EDX, EAX。其中一方是输入的假码,另一方即是正确的注册码。

确认EDX或EAX中的值与我们方法一找到的注册码(例如K5...)一致。

接下来,基于此地址制作内存注册机。关键中断地址是0041D412

内存注册机设置如下:

  • 中断次数:1
  • 指令字节18 99 75 F1 FF(即比较指令机器码)
  • 长度:5
  • 寄存器方式:选择EDX(存储真注册码的寄存器)
  • 内存方式:如果注册码包含字母,应选择“内存方式”;如果仅为数字,可选择“寄存器方式”。
  • 宽字节:如果注册信息是Unicode编码,需勾选此项;本例为ASCII码,无需勾选。

注意:某些注册机生成器版本(如演示所用的)存在界面显示瑕疵,避免勾选无关的选项,否则会导致生成的注册码重复。

生成注册机后,运行它。在目标程序中输入任意注册码,注册机将自动弹出正确的注册码。


知识总结与要点回顾 🎯

本节课我们一起学习了两种不脱壳破解加壳程序的技术。

核心要点总结:

  1. 动态断点:在加壳程序中,硬件执行断点比普通内存断点更可靠。
  2. 关键比较:明码比较的指令(如CMPTEST)附近是破解的突破口,真注册码通常存放在某个寄存器或内存地址中。
  3. 内存注册机原理:通过拦截程序在特定地址、特定时刻的比较操作,直接读取存放正确结果的寄存器(如EDX)或内存地址的值。
  4. 注册机设置
    • 地址和中断次数来自调试器。
    • 寄存器/内存方式选择字母注册码 -> 内存方式纯数字注册码 -> 寄存器方式
    • 宽字节:根据程序字符串编码(Unicode/ASCII)决定是否勾选。

额外发现:目标程序将注册信息加密后保存在其目录下的一个特定文件(如SYSTIMER)中,而非注册表。删除该文件即取消注册。手动伪造该文件无效,因为程序启动时会重新校验。

请结合实操练习以巩固本课内容。熟练掌握这些基础方法,是理解更复杂逆向工程技术的基石。

天草流初级破解教程 - P15:第14课 - 破解重启验证与界面修改 🛠️

在本节课中,我们将学习如何破解一个采用重启验证机制的程序,并修改其用户界面。课程将涵盖脱壳、定位关键验证代码、修改程序逻辑以及通过资源编辑修改界面元素。


课程概述

本节课的目标是分析并破解一个名为“Asparag”的程序。该程序使用重启验证,并具有试用次数限制。我们将从脱壳开始,逐步分析其验证逻辑,并最终实现两种破解方法,同时学习如何修改程序的界面显示。


第一步:程序分析与脱壳

上一节我们介绍了课程目标,本节中我们来看看如何对目标程序进行初步分析和脱壳。

首先,使用查壳工具确认程序由 B4++ 加壳。程序采用重启验证机制,当前状态已显示为“过期”。

我们使用 ESP定律 进行脱壳。在OD中运行程序后,在栈窗口跟随ESP指针,下硬件访问断点。程序中断后,单步跟踪找到程序的原始入口点(OEP)。

以下是脱壳的关键步骤:

  1. 找到OEP地址:0x1438
  2. 使用LordPE的“修正镜像大小”功能。
  3. 使用Import REC修复输入表。
    • 填入OEP:1438
    • 获取的IAT信息为:RVA: 0x311430, Size: 0x1413
  4. 点击“获取输入表” -> “显示无效函数” -> “追踪层级1”进行修复。
  5. 转储并修复进程,生成可运行的脱壳文件。

脱壳完成后,程序可以正常运行,但验证逻辑仍需破解。


第二步:分析重启验证机制

脱壳完成后,我们开始分析程序的重启验证逻辑。重启验证意味着程序会将验证信息(如试用次数)写入系统的某个持久化位置(如注册表或文件),下次启动时读取。

以下是判断验证类型的几种方法:

  • 注册表验证:对 RegOpenKeyExA/WRegQueryValueExA/W 等API下断点,查找程序读取的键值。
  • 文件验证:对 CreateFileA/WReadFile 等API下断点,查找程序读取的文件(DLL除外)。
  • DLL验证:程序可能通过调用特定DLL的函数来读写验证信息。

在本例中,通过分析,我们确定程序结合了 注册表DLL 来存储试用次数。

我们在OD中对 RegQueryValueExA 下断点。运行程序后,程序在读取注册表时中断。在堆栈窗口可以看到程序正在读取一个特定的注册表键值,其数据是经过加密的试用次数信息。

跟踪代码,我们发现一个关键的比较跳转指令(如 jnzje),它决定了是否显示“未注册”或“过期”提示。这个跳转通常位于验证函数之后。


第三步:实施破解(方法一:修改关键跳转)

找到关键跳转后,我们可以实施第一种破解方法——直接修改程序逻辑。

在验证代码之后,通常会有一个条件跳转来决定程序流程。例如:

cmp eax, ebx      ; 比较两个值(如当前次数与总次数)
jnz short label_unregistered ; 如果不相等,跳转到未注册流程

我们的目标是将这个跳转改为无条件跳转(jmp)或无操作(nop),使其跳过验证失败的分支。

操作步骤如下:

  1. 在OD中找到上述关键跳转指令。
  2. 右键该指令,选择“汇编”。
  3. jnz 修改为 jmp,使其无论如何都跳转到注册成功的流程。
  4. 将修改保存到可执行文件。

修改后运行程序,发现不再提示注册或过期,破解成功。这是一种典型的“爆破”思路。


第四步:实施破解(方法二:修改注册表数据)

除了修改程序,我们还可以尝试修改其验证数据源。既然程序从注册表读取加密的试用次数,我们可以尝试修改这个值。

操作步骤如下:

  1. 使用注册表编辑器(如 regedit)找到程序写入的键值路径(通过之前的断点分析获得)。
  2. 尝试将该键值的数值改大(例如,改为一个很大的十进制数或特定的十六进制数据)。
  3. 保存注册表并重启程序。

在某些情况下,直接修改注册表值可能导致程序解密出错而报错。此时,可以结合第一种方法,在程序读取注册表后、进行解密判断前,修改内存中的解密结果,使其始终为一个有效值(如 999)。

这种方法需要更深入的跟踪,以找到存储解密后次数的内存地址,然后通过OD修改该内存值。


第五步:修改程序界面与资源

破解验证逻辑后,我们还可以让程序界面更美观,例如修改“未注册”字样、禁用注册输入框等。

我们使用资源编辑工具(如 Resource Hacker)来修改程序资源。

以下是需要修改的部分及方法:

  • 修改对话框标题或字符串:在“String Table”或“Dialog”中找到相关资源,将“Unregistered”等字样改为自定义信息(如你的名字)。
  • 禁用按钮和输入框:在“Dialog”中编辑主窗口对话框。找到“注册”按钮和用于输入注册码的编辑框,将其属性中的 Enabled 状态由 True 改为 False。这会使它们在界面上显示为灰色不可用状态。

修改完成后保存资源,程序界面即会更新。这通过直接修改资源文件实现,无需修改程序代码。


知识扩展:代码实现界面控制

为了帮助理解资源修改的原理,我们简单看一下在编程中如何通过代码实现类似功能。例如在易语言或Delphi中:

// 在窗口创建事件中,禁用编辑框和按钮
procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Enabled := False; // 禁用编辑框1
  Button1.Enabled := False; // 禁用按钮1
end;

这段代码的含义是:在窗体创建时,将组件 Edit1Button1Enabled 属性设置为 False(假),从而使它们不可用。我们在资源文件中修改的正是这些属性的初始值。


课程总结

本节课中我们一起学习了针对重启验证程序的完整破解流程:

  1. 脱壳:使用ESP定律对B4++加壳程序进行脱壳。
  2. 分析:通过API断点法分析出程序采用注册表+DLL的重启验证机制,并定位到关键验证跳转。
  3. 破解
    • 方法一(爆破):直接修改验证函数后的关键跳转指令(jnz -> jmp),绕过验证。
    • 方法二(改数据):尝试修改注册表中的验证数据,或修改内存中的解密结果。
  4. 美化:使用Resource Hacker修改程序资源,更新字符串并禁用不必要的输入控件。

破解的思路是多样的,核心在于理解程序的验证逻辑和数据流。多跟踪、多尝试,往往能发现不止一种解决方法。请在实践中巩固这些知识。

天草流初级破解教程 - P16:第15课 - 实战破解“PDF转Word”程序 🔓

在本节课中,我们将学习如何对一个名为“PDF转Word”的软件进行破解实战。我们将通过分析其注册验证流程,找到关键跳转或算法,并演示几种常见的破解思路与方法。


概述

本次破解的目标是一个PDF转Word文档的程序(版本3.4.0)。程序运行后需要注册,输入任意邮箱和序列号会提示“序列号错误”。我们将使用调试工具,通过分析字符串、关键比较和堆栈调用,来绕过或理解其注册机制。


第一种方法:字符串查找与关键跳转修改

上一节我们概述了目标程序。本节中,我们来看看第一种破解思路:通过程序提示的错误信息字符串来定位关键代码。

程序弹出“序列号错误”的提示框。我们可以在反汇编工具中搜索这个字符串。

以下是搜索和定位的步骤:

  1. 在反汇编工具中搜索字符串“序列号错误”或“Invalid”。
  2. 找到引用该字符串的代码位置,通常在附近会有条件判断跳转。
  3. 分析该处的跳转逻辑,判断是跳过成功流程还是失败流程。
  4. 通过修改跳转指令(例如将JNZ改为JZ,或直接NOP填充),使程序执行注册成功的分支。

在本次实战中,搜索字符串后,我们找到了两处关键跳转。经过分析,其中一个跳转负责在比较失败(序列号错误)时跳过成功提示。我们修改了这个跳转,使其失效。修改后,程序不再弹出错误提示,而是显示“感谢您注册”。

关键修改点伪代码表示:

CMP ESI, EDI        ; 比较用户输入的序列号与正确序列号
JNZ SHORT FAIL_LABEL ; 如果不相等,则跳转到失败处理
... (成功注册的代码) ...
FAIL_LABEL:
... (弹出错误提示的代码) ...

修改思路:JNZ (不相等则跳转) 修改为 JZ (相等则跳转) 或直接NOP掉,使程序无论对错都继续执行成功流程。


第二种方法:堆栈调用回溯法

如果字符串查找不直接,我们可以尝试另一种通用方法:当程序弹出错误提示框时暂停,然后查看堆栈调用链。

以下是此方法的操作流程:

  1. 运行程序,输入假的用户名和注册码,点击“注册”按钮。
  2. 当错误提示框出现时,立即切换到调试器,按F12暂停程序执行。
  3. 查看调试器的“堆栈”窗口,这里显示了程序执行到当前状态所经过的函数调用序列。
  4. 从堆栈调用链的底层(即最早被调用的、靠近系统API的函数)向上层查找,找到属于程序自身模块的、最接近弹出对话框的那个CALL指令。
  5. 在这个CALL指令的上下文中进行分析,通常就能找到进行序列号验证的关键比较代码。

这种方法不依赖于特定字符串,是从程序行为(弹出对话框)反向追踪代码的通用技巧。


第三种方法:API断点法

除了暂停法,我们还可以主动在可能被调用的关键API函数上设置断点。这对于有注册信息保存行为的程序尤其有效。

以下是设置API断点的步骤:

  1. 在调试器中,对诸如MessageBox(弹窗)、RegQueryValue(查询注册表)、GetDlgItemText(获取文本框内容)等函数设置断点。
  2. F9运行程序,输入注册信息并点击注册按钮。
  3. 程序在调用这些API时会被断下。例如,在MessageBox断下时,我们就能回溯到是哪个验证函数调用了错误提示。
  4. 同样,通过分析该函数上下文,定位核心验证代码。

在本次示例中,我们曾尝试在注册表相关API下断点以寻找注册信息存储位置,但未在该程序中发现。这提示我们,该程序的注册验证可能仅是内存验证,未将状态写入系统。


总结

本节课中,我们一起学习了针对一个具体软件的三种破解实战思路:

  1. 字符串查找法:直接搜索程序界面显示的字符串,快速定位关键代码,适合有明确提示信息的程序。
  2. 堆栈回溯法:在程序发生特定行为(如弹窗)时暂停,通过调用堆栈反向追踪至验证逻辑,是一种通用性强的动态分析方法。
  3. API断点法:对关键系统函数下断点,拦截程序行为并分析上下文,常用于分析程序与系统的交互。

通过修改关键跳转,我们成功绕过了该PDF转换工具的注册验证。需要注意的是,本教程仅用于学习逆向工程技术与软件保护原理,请勿将此类技术用于破坏合法软件的授权机制。

天草流初级破解教程 - P17:第16课 - 破解实战7 🔓

在本节课中,我们将学习如何破解一个使用UPX加壳的程序。这个程序有其特殊之处,我们将通过分析其注册验证逻辑,找到关键跳转并制作补丁,最终实现软件的“白嫖”。


概述

本节课的目标程序是一个经过UPX加壳的软件。虽然UPX壳通常很容易脱掉,但这个程序在脱壳后存在大量无效指针,需要特殊处理。我们将学习如何在OD(OllyDbg)中定位关键的注册验证代码,分析其算法,并通过修改关键跳转来实现破解。


第一步:查壳与脱壳处理

首先,我们检查目标程序,确认它使用了UPX加壳。

使用ESP定律可以快速定位到程序的原始入口点(OEP)。成功到达OEP后,我们可以使用OD的插件进行脱壳。

然而,这个程序脱壳后存在一个奇怪的现象:导入表中有大量无效的指针。

以下是处理无效指针的步骤:

  • 在OD的导入表修复窗口中,你会看到大量标记为“无效”的指针。
  • 不要使用“剪切”功能,因为指针数量过多会导致等待时间很长。
  • 直接使用“删除无效指针”功能即可快速清理。

处理完毕后,保存脱壳后的文件。


第二步:定位关键验证代码

上一节我们处理了程序的壳。本节中,我们来看看如何找到验证注册码的关键代码。

我们可以直接运行未脱壳的程序,在OD中尝试触发注册错误。当弹出错误提示框时,暂停程序,在调用栈中寻找可疑的调用。

另一种方法是,在脱壳后的程序中直接搜索字符串,例如“invalid”、“sorry”或错误提示信息,从而定位到验证函数。

找到关键函数后,开始分析代码。在输入假注册码并点击确定后,程序会进行一系列比较和计算。


第三步:分析算法与关键跳转

我们找到了验证函数,现在来深入分析其逻辑。

通过单步跟踪(F8),我们观察寄存器和跳转的变化。程序的核心验证逻辑通常围绕一个最终决定成功与否的比较指令(如 CMP)和条件跳转指令(如 JZJNZ)。

在本次分析中,我们跟踪到一个关键点:

  1. 程序将我们输入的用户名或注册码进行某种运算。
  2. 运算结果会影响 EAXEBX 等寄存器的值。
  3. 随后,一个 CMP 指令会比较某个值(可能是计算出的结果与正确结果)。
  4. 根据比较结果,一个条件跳转指令(例如 JNZ 0045XXXX)会决定程序是走向“注册成功”还是“注册失败”的流程。

我们的目标是让程序走向成功分支。分析发现,当 EAX 寄存器值为1时,后续流程会判定为成功。因此,我们需要让那个关键的条件跳转不执行(即实现“不跳转”)。


第四步:修改程序与制作补丁

上一节我们确定了关键跳转。本节我们将通过修改二进制代码来绕过验证。

找到关键跳转指令的地址,例如:0045XXXX: 75 07 JNZ SHORT 0045XXXX

  • 75JNZ 的操作码。
  • 我们的目标是将它改为无条件跳转(EB)或直接空操作(NOP)。
  • 在OD中,右键该行代码,选择“二进制”->“编辑”,将 75 07 修改为 EB 07(跳转)或 90 90(两个NOP指令,相当于什么都不做)。

修改后,保存更改到可执行文件,或制作一个补丁(Loader)。

以下是制作补丁(Loader)时需要写入的修改内容示例:

地址: 0045XXXX
原始字节: 75 07
修改为: 90 90

运行修改后的程序或加载补丁,输入任意注册码,验证是否显示注册成功。


第五步:程序的特殊行为与总结

我们成功破解了程序,但它表现出一个特殊现象:只有在运行我们制作的补丁(Loader)时,才会显示为已注册状态;直接运行修改后的主程序,注册信息可能不生效或需要重新输入。

这可能是因为程序将注册信息(如我们第一次输入的假用户名)加密存储在了某个特定位置(如注册表或文件),而补丁直接模拟了已注册的状态。对于这个行为,我们本节课不做深入探究。

最后,提供给大家的软件包通常包含安装程序和绿色压缩包。这是个人习惯,为了方便不喜欢安装软件的用户。你可以先尝试运行绿色版,如果无法运行,再使用安装程序。


总结

在本节课中,我们一起学习了:

  1. 处理UPX脱壳后产生的海量无效指针。
  2. 通过错误提示定位软件验证函数。
  3. 动态跟踪分析注册验证的算法逻辑,并找到决定成败的关键跳转
  4. 通过修改二进制代码(将条件跳转 JNZ 改为 NOP)来绕过验证。
  5. 制作补丁并理解了目标程序可能存在的特殊注册信息存储机制。

破解的核心思路始终是:定位关键判断,并改变其执行流程。希望本课能帮助你更好地理解这一过程。

天草流初级破解教程 - P18:第17课 - 破解次数限制与重启验证 🔓

在本节课中,我们将学习如何分析并破解一个具有“重启验证”机制和次数限制的软件。我们将通过分析程序行为、定位关键模块、修改关键数据,最终达到去除使用次数限制的目的。


课程概述 📖

本节课的目标是破解一个名为“KK糖”的外挂程序。该程序只能使用100次,我们的目标是找到并修改其计数机制,使其不再减少。我们将重点分析其重启验证方式,并定位存储次数的关键文件或代码。

上一节我们介绍了基本的破解思路,本节中我们来看看如何具体分析一个具有复杂验证机制的程序。


第一步:分析程序注册与验证机制 🔍

首先需要弄清楚外挂的注册机制。运行程序并尝试注册,点击确定后程序直接退出,且无任何提示。下次启动时,注册状态并未改变。这表明这是一个重启验证程序。

重启验证通常通过以下几种方式实现:

  • 注册表
  • 独立的DLL文件
  • INI配置文件

我们需要逐一排查。


第二步:排查重启验证的存储位置 🗺️

程序具有反调试机制。我们首先排查注册表方式。

以下是排查注册表的关键步骤:

  1. 在调试器中尝试对注册表相关API(如 OpenKey)下断点。
  2. 发现程序并未调用相关模块,因此排除了通过注册表进行重启验证的可能。

接下来,我们排查文件方式。使用调试器监视文件创建和读取操作(API如 CreateFile)。

以下是监视文件操作时观察到的关键点:

  1. 程序加载了多个DLL文件。
  2. 通过分析,发现其中两个DLL文件可能包含关键信息:一个与次数相关,另一个与注册相关。
  3. 程序并未直接写入DLL文件,因此次数可能记录在其他地方。

我们尝试查找INI配置文件。在程序字符串中搜索“.ini”,虽然找到了相关API调用,但无法确定具体的文件名和路径,这条线索暂时中断。


第三步:下断点并动态跟踪 🎯

由于重启验证的关键可能在INI文件操作上,我们在调试器中对 GetPrivateProfileString(读取INI)和 WritePrivateProfileString(写入INI)等API下断点。

在程序启动前下好断点,然后运行程序。程序很快在读取INI文件的地方中断。

以下是跟踪过程中的关键发现:

  1. 从堆栈和寄存器中可以看到程序正在读取一个数据,其值为“10”,这很可能代表剩余使用次数。
  2. 继续执行,程序会进行判断。如果验证失败,则会向INI文件写入错误信息
  3. 整个计数和验证逻辑都发生在我们之前怀疑的那个关键DLL模块中,证实了我们的分析。

跟踪发现,程序将使用次数通过某种运算(比如 次数 = 100 - 已用次数)转换成一个数值进行存储和判断。


第四步:定位并修改关键代码 ⚙️

通过跟踪,我们找到了负责减少次数和进行验证判断的关键代码位置。

核心的验证逻辑通常是一个条件跳转。例如:

cmp eax, edx          ; 比较两个值(如当前次数和阈值)
jz  label_valid       ; 如果相等(次数有效),则跳转到有效流程
; 否则,继续执行无效流程(如退出、报错)

我们的修改思路是强制让程序跳转到“有效”或“无限”的流程。在这个案例中,我们找到减少次数的代码,将其修改为 nop(空操作) 或直接赋一个固定值(如0),使其不再变化。

修改DLL后,需要将文件名改回原始名称,因为主程序按固定名称加载它。测试发现,修改后程序次数显示为0,且不再减少,达到了去除次数限制的目的。


第五步:优化与界面修改 🛠️

成功破解核心功能后,我们可以进一步优化,例如去除界面上的“未注册”或广告信息。

以下是修改程序界面字符串的通用方法:

  1. 使用十六进制编辑器或资源修改工具(如ResHacker)打开主程序或DLL文件。
  2. 在字符串或资源段中搜索需要修改的文字(如“未注册”)。
  3. 将其替换为想要的文字(如“已破解”),注意长度不应超过原字符串。
  4. 保存文件并测试。

在本例中,我们在主程序文件中将“未注册”的字符串成功替换为了其他文本。


总结与回顾 🎓

本节课中我们一起学习了如何破解一个具有重启验证和次数限制的程序。关键步骤包括:

  1. 分析验证类型:确认为重启验证。
  2. 定位关键点:通过API断点(注册表、文件、INI)排查,找到负责计数和验证的DLL模块。
  3. 动态跟踪:在程序运行时跟踪数据流,理解其计数和判断逻辑。
  4. 修改代码:找到关键判断或计数指令,通过修改汇编代码(如jmpnop)或数据来绕过限制。
  5. 辅助修改:使用工具修改程序界面字符串,完善破解效果。

破解的核心在于耐心分析程序的行为,并准确定位到承载验证逻辑的那一小段代码。希望本课能帮助你理解这个过程。

天草流初级破解教程 - P19:第18课 - 破解重启验证型软件 🔍

在本节课中,我们将学习如何破解一个具有“重启验证”机制的软件。这类软件在注册成功后,需要重新启动程序才能生效,其验证信息通常存储在注册表或文件中。我们将通过定位关键验证代码、分析注册表操作,最终实现破解。


一、 目标程序与初步分析 🎯

本节课的目标程序是一个带有注册验证功能的工具。运行程序时,可能会提示“使用期已满”。

如果您的程序未提示过期,请将系统时间调整到较早的日期(例如2006年或2007年),以触发验证界面。

首先,我们使用查壳工具检查程序。结果显示程序由 BC++ (Borland C++) 编写。对于这类程序,一个常见的分析思路是使用 F12暂停法 来定位关键代码。


二、 定位按钮事件 🔘

上一节我们确认了程序类型,本节我们来看看如何定位其验证逻辑的入口。

程序界面有多个按钮,我们需要找到触发注册验证的那个按钮事件。

  1. 使用相关工具(如SPY++或ResHacker)查看按钮属性。
  2. 发现“注册”按钮的标识为 Button register,“确定”按钮的标识为 Button OK
  3. 我们的目标是找到 Button OK 的点击事件处理函数。

以下是记录的关键标识:

  • Button register - 对应“购买”
  • Button OK - 对应“确定”(验证按钮)

三、 动态调试与寻找关键跳转 ⚙️

找到按钮事件后,我们使用OD(OllyDbg)载入程序进行动态调试。

  1. 在OD中,在 Button OK 的事件处理函数入口处下断点。
  2. 运行程序,输入任意注册码并点击“确定”,程序会在断点处中断。
  3. 单步执行(F8),观察程序流程。
  4. 很快会遇到一个条件跳转指令,其下方是“注册码无效”的提示字符串。
CMP ... ; 比较指令
JNZ ... ; 条件跳转,跳向“注册码无效”
  1. 记录下这个跳转的地址。如果让这个跳转实现(即跳转),则提示失败;如果修改标志位或NOP掉跳转指令,使其不跳转,程序会继续执行。
  2. 尝试NOP掉这个跳转后,程序提示“注册码正确,但已过期”。这说明我们找到了一个验证点,但并非最终关键。

四、 发现重启验证机制 🔄

上一节我们修改了一个跳转,但并未完全成功。本节中我们来看看程序更深层的验证机制。

当我们尝试让程序显示“注册码正确”并关闭后重新启动时,程序依然提示“使用期已满”。这表明程序存在 重启验证 机制。

重启验证意味着:

  • 程序在本次运行时,会将注册成功的信息写入某个持久化位置(如注册表或文件)。
  • 下次启动时,程序会从该位置读取信息进行验证。

因此,我们的破解思路需要转向:找到程序写入和读取注册信息的位置


五、 追踪注册表操作 📝

既然程序验证与重启相关,我们需要监控其对系统的操作。以下是查找其存储位置的方法。

方法一:字符串搜索
在OD中搜索所有字符串,发现了一些与注册表路径相关的片段(如Software\...),但信息不完整。

方法二:API断点法(本节课核心)
更高效的方法是直接对操作注册表的API函数下断点。我们关注 RegOpenKeyRegQueryValue 这类函数。

  1. 在OD中按 Ctrl+N 打开API调用窗口。
  2. 查找 RegOpenKeyA/Ex 函数。
  3. 右键点击该函数,选择 “在每个参考上设置断点”
  4. 运行程序,OD会在每次程序尝试打开注册表项时中断。
  5. 通过观察堆栈或函数参数,可以判断程序正在读取哪个路径。我们过滤掉系统相关的无关操作,直到找到程序读取我们输入的“用户名”和“注册码”的键值(例如名为3800CC的键值)。

这个过程直接定位到了程序验证数据的源头。


六、 分析并修改验证逻辑 💡

找到读取注册信息的代码后,我们在其附近进行分析。

  1. 单步跟踪,会发现另一处关键比较和跳转。此处的逻辑结构与之前找到的类似,但它是决定是否显示“0用户授权”或“站点授权”的核心判断。
  2. 程序会比较从注册表读取的数据与某种算法生成的结果。
  3. 我们找到这个决定最终授权状态的关键跳转(JNZJE),并将其NOP掉或修改为相反逻辑
; 修改前,跳转则失败
CMP EAX, EBX
JNZ SHORT 失败地址

; 修改后,强制跳转失效,走向成功流程
CMP EAX, EBX
NOP
NOP
; 或者直接改为 JMP 到成功地址
  1. 修改后,保存程序。再次运行,无论注册表内信息如何,程序都会显示正确的授权信息(如“站点授权”)。

七、 总结与要点 ✅

本节课中我们一起学习了破解具有重启验证机制软件的全过程。

核心步骤总结:

  1. 初步分析:查壳,确定开发语言,使用F12暂停法定位入口。
  2. 定位事件:找到验证按钮的事件处理函数。
  3. 动态调试:在OD中跟踪,找到第一个验证跳转(通常为明码比较)。
  4. 识别机制:发现修改后仍需重启生效,从而判定为重启验证型。
  5. 追踪数据:使用 API断点法(对RegOpenKey设断)高效定位程序读写注册表的关键代码。
  6. 破解关键:在读取注册表数据后的验证逻辑处,找到并修改最终的核心跳转指令。

关键技巧:

  • 对于重启验证,重点追踪程序对文件或注册表的读写操作。
  • Ctrl+N 查找API并下参考断点,是快速定位系统调用的高效方法。
  • 破解的最终目标通常是让验证函数无论输入何值都返回成功状态

通过本课的学习,你应该掌握了应对简单重启验证软件的基本思路和实战方法。关键在于耐心跟踪数据流,并熟练运用调试工具的各种断点功能。

天草流初级汇编教程 - P2:数据传送与栈操作指令 🧱

在本节课中,我们将学习汇编语言中两类基础且重要的指令:数据传送指令与栈操作指令。它们是理解程序数据流动和函数调用的基石。

上一节我们初步接触了汇编语言,本节中我们来看看具体的数据操作是如何实现的。

数据传送指令 (Move)

数据传送指令 MOV 是汇编中最常用的指令之一,其作用相当于高级语言中的赋值语句,用于在寄存器、内存和立即数之间传送数据。

指令的基本格式为:MOV 目标操作数, 源操作数
其中,操作数可以是寄存器(reg)、内存地址(memory)或立即数(imm)。

例如:

MOV EAX, 1234H  ; 将十六进制数1234传送(赋值)给EAX寄存器

传送填充指令 (Move with Sign/Zero Extension)

传送填充指令用于将位数较短的操作数传送到位数较长的目标操作数中,并根据规则对高位进行填充。这主要分为符号填充和零填充。

以下是两种格式:

  • MOVSX 目标操作数, 源操作数:符号填充。用源操作数的符号位(最高位)填充目标操作数的高位。
  • MOVZX 目标操作数, 源操作数:零填充。用0填充目标操作数的高位。

示例解析
假设 AL = 87H(二进制为10000111,最高位为1,是负数)。

  • 执行 MOVSX CX, AL 后,CX 的高8位会用 AL 的符号位(1)填充,因此 CX = FF87H
  • 执行 MOVZX DX, AL 后,DX 的高8位会用0填充,因此 DX = 0087H

关于汇编书写规范:在汇编中,若一个十六进制数以字母(A-F)开头,为与标识符区分,通常在前面加一个0。例如,0B134H

交换指令 (Exchange)

交换指令 XCHG 用于交换两个操作数的内容。操作数可以是两个寄存器,或一个寄存器与一个内存变量。

指令格式非常简单:XCHG 操作数1, 操作数2

取有效地址指令 (Load Effective Address)

取有效地址指令 LEA 用于将源操作数的有效地址(偏移地址)加载到目标寄存器中,而不是加载该地址处的数据。可以将其理解为高级语言中的“取地址”操作。

指令格式:LEA 目标寄存器, 源内存操作数

例如,LEA EAX, [EBX+ECX*4+10H] 会计算 EBX+ECX*4+10H 这个地址值,并将其存入 EAX,而不是去读该地址的内存内容。

理解了数据的传送与交换,接下来我们看看程序运行时一个至关重要的数据结构——栈。

栈操作指令

栈是一种“后进先出”(LIFO)的数据结构,在汇编中主要用于保存函数返回地址、传递参数和保存寄存器现场。对栈的操作主要分为两类:进栈(压栈)和出栈(弹栈)。

进栈操作 (PUSH)

PUSH 指令用于将操作数压入栈顶。系统会自动完成两步操作:

  1. 栈顶指针 ESP 的值减4(在32位系统中)。
  2. 将操作数存入新的 ESP 所指向的内存地址。

以下是常见的进栈指令:

  • PUSH reg/mem/imm:将寄存器、内存数据或立即数压栈。
  • PUSHA / PUSHAD:将所有通用寄存器的值压栈(PUSHA用于16位,PUSHAD用于32位)。

操作演示
执行 PUSH EDI 前,假设 ESP = 0012FFA4HEDI = 00401000H
执行该指令后:

  1. ESP 先减4,变为 0012FFA0H
  2. 然后将 EDI 的值 00401000H 存入内存地址 0012FFA0H 处。

出栈操作 (POP)

POP 指令用于从栈顶弹出一个数据到目标操作数。其操作与 PUSH 相反:

  1. 将当前 ESP 所指向的内存地址中的数据弹出到目标操作数。
  2. 栈顶指针 ESP 的值加4。

以下是常见的出栈指令:

  • POP reg/mem:将栈顶数据弹出到寄存器或内存。
  • POPA / POPAD:将栈中数据依次弹出到所有通用寄存器(恢复 PUSHA/D 保存的现场)。

重要原则——栈平衡:在程序段中,通常有几次 PUSH 操作,就需要对应几次 POP 操作,以确保函数调用前后栈指针 ESP 恢复到相同位置,这称为栈平衡。脱壳技术中的“ESP定律”正是利用了这一原理。


本节课中我们一起学习了汇编语言的核心数据操作指令。我们掌握了如何使用 MOV 指令传送数据,了解了 MOVSX/MOVZX 进行带填充的传送,以及使用 XCHG 交换数据。更重要的是,我们深入探讨了栈的结构与 PUSH/POP 操作指令,理解了栈在程序运行中的关键作用及栈平衡原则。这些是分析程序流程和进行软件调试与破解的基础。

天草流初级破解教程 - P20:第19课 - 软件注册机制分析与漏洞利用 🛠️

在本节课中,我们将学习如何分析一个简单软件的注册验证机制,并利用其存在的注册表验证漏洞来实现“破解”。我们将使用动态调试工具,观察程序流程,并最终通过修改注册表数据来绕过注册验证。


软件初步分析

首先,我们载入目标程序。这是一个使用VC++编写的软件,没有加壳,也没有复杂的加密算法,非常适合初学者进行分析。

载入程序后,我们直接运行。在未注册状态下,软件界面会明确显示“unregistered”和“000”等信息。

我们的目标是观察注册成功与失败时,程序行为和数据的差异,从而找到验证逻辑的突破口。


动态调试与关键跳转定位

我们使用调试器附加到运行中的程序。在输入假注册码并点击注册后,程序会弹出提示框。我们在此处暂停,开始单步跟踪。

很快,我们找到了一个关键的函数调用(messagebox)和其附近的判断逻辑。上方存在一个跳转指令,它直接决定了程序是走向“注册成功”还是“注册失败”的流程。

核心观察JZJNZ 等条件跳转指令是破解中常需要修改的目标。

我们跟进这个跳转,发现它源自一个循环比较的过程。程序将我们输入的假码与通过某种计算得到的值进行比较。


算法循环分析

在循环内部,程序执行了以下大致操作:

  1. 将我们输入的字符(假码)放入寄存器 DL
  2. 将程序内部计算出的一个字符放入寄存器 BL
  3. DL 中的值进行一个运算(例如 ADD DL, 0x23),结果放入 CL
  4. 比较 DL(或 CL)与 BL 的值。
  5. 如果不相等,则跳出循环,走向失败流程;如果相等,则继续循环,比较下一个字符。

用伪代码描述这个核心比较逻辑:

MOV DL, [用户输入+索引]   ; 取用户输入的一个字符
MOV BL, [内部计算值+索引] ; 取内部计算的一个字符
ADD DL, 0x23            ; 对用户输入字符进行变换
CMP DL, BL              ; 比较变换后的字符与内部值
JNZ 失败流程            ; 如果不相等,则跳转到注册失败

我们跟随循环多次,记录下每次内部计算出的 BL 值。一个有趣的现象出现了:很多组不同的输入,在循环中都能通过每一次的字符比较


发现漏洞与爆破

通过多次测试,我们发现这个软件的验证机制存在逻辑漏洞:它似乎没有对最终比较结果进行全局性唯一性的最终判断。许多不同的字符串都能通过那个循环检查,导致程序误认为注册成功。

因此,最简单的“爆破”方法就是修改那个关键的跳转指令,让它无论如何都跳向成功流程。在汇编层面,这通常是将 JNZ(不相等则跳)改为 JMP(无条件跳),或者将 JNZ 改为 JZ(相等则跳,逻辑反转)。

爆破修改示例
75 15(对应汇编 JNZ SHORT 失败地址)修改为 EB 15(对应汇编 JMP SHORT 成功地址)。

修改后,无论输入什么内容,软件都会显示注册成功。


深入漏洞:注册表验证缺陷

上一节我们介绍了通过修改代码逻辑来爆破软件。本节中我们来看看这个软件更深层次的漏洞——注册表验证机制。

我们关闭软件,检查注册表。发现该软件将注册信息明文保存在注册表中特定位置,例如:

  • RegName: 注册名
  • RegCode: 注册码(或状态)
  • Registered: 注册标志(0为未注册,1为已注册)

我们手动修改注册表:

  1. Registered 的值从 0 改为 1
  2. RegName 中填入任意名字。

以下是修改注册表的关键操作概念:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\目标软件]
"RegName"="任意用户名"
"Registered"="1"

修改完成后,重新启动软件。软件读取注册表后,直接显示为已注册状态,不再要求输入注册码。

这暴露了该软件的安全隐患:其注册验证完全依赖于客户端注册表中易于篡改的明文标志,没有进行任何校验或加密。


漏洞利用总结与编程启示

本节课中我们一起学习了如何分析一个简单的软件注册流程,并发现了其两处关键漏洞:

  1. 验证逻辑漏洞:循环比较逻辑不严谨,可通过爆破关键跳转绕过。
  2. 数据存储漏洞:注册状态明文存储于注册表,可直接篡改。

对于软件开发者而言,这提供了重要的安全启示:

  • 避免弱验证:注册验证算法应具备足够的复杂度,避免简单的单字符顺序比较。
  • 加强数据保护:存储在客户端的敏感数据(如注册标志、试用次数)应进行加密或混淆处理。
  • 增加服务器验证:对于重要软件,核心验证逻辑应放在服务器端。

学习破解的目的,不仅是掌握技术,更是为了理解软件保护的薄弱环节,从而在自身开发时能构建更健壮的安全体系。


课程结束

天草流初级课程 - P21:脱壳与破解实战 🛡️➡️🔓

在本节课中,我们将学习如何对一个加壳程序进行脱壳,并分析其注册验证逻辑以实现破解。课程将涉及穿山甲壳的识别、特定断点的使用、寻找程序原始入口点以及关键的算法分析。


课程概述 📋

本节课将演示一个使用穿山甲壳保护的程序的分析过程。我们将首先识别壳的类型和版本,然后使用OllyDbg配合特定技巧进行脱壳。脱壳成功后,我们将分析程序的注册验证流程,定位关键判断点,并修改其逻辑以实现破解。


第一步:查壳与识别 🔍

首先,我们需要确定目标程序使用了何种保护。使用查壳工具进行检测。

以下是查壳步骤:

  1. 使用PEiD工具进行初步扫描。结果显示为“穿山甲 3.0 到 3.6A”,版本范围较广。
  2. 使用FI(FileInfo)工具进行二次确认。FI报告为“穿山甲 3.05”。

通过运行程序观察,发现其只有一个运行线程,这属于穿山甲壳的“单线程”版本。穿山甲壳还分“标准”与“非标准”模式,本案例属于标准壳。

核心概念: 查壳是逆向分析的第一步,用于确定保护类型和版本,从而选择合适的脱壳方法。


第二步:使用OD载入与初步分析 ⚙️

我们将使用OllyDbg载入程序,并开始寻找脱壳的突破口。

上一节我们识别了壳的类型,本节中我们来看看如何在调试器中定位关键点。

首先,隐藏OllyDbg的调试器特征,以避免壳的检测。然后,我们需要找到一个名为 Magic Jump 的关键跳转。这个跳转负责处理API(应用程序编程接口),如果处理不当,后续使用Import REC修复导入表时会产生大量无效指针。

为了找到这个跳转,我们需要使用一个特定的断点。这个断点经验证,与Magic Jump的位置非常接近。

核心代码(断点设置):

BP GetModuleHandleA + 5

这里的+5是一个偏移量,目的是绕过壳可能设置的针对这个常用断点的检测。你也可以尝试+6+7+16等值。在某些系统(如Windows 2000)上,使用这种带偏移的断点可能无效,此时应直接使用硬件执行断点(HE)。


第三步:寻找返回时机与OEP 🎯

设置好断点后,我们通过多次运行程序来捕捉正确的“返回时机”。所谓返回时机,是指程序执行从壳的代码区域返回到其原始代码区域(即OEP,原始入口点)的关键时刻。

以下是判断返回时机的两种方法:

  1. 观察堆栈变化:当按下Shift+F9运行后,如果程序很快中断,且堆栈没有出现预期的系统返回地址,则不是正确时机。我们需要等待一个“很慢”的中断,此时堆栈会出现系统领空的返回地址。
  2. 观察代码领空:中断后,查看代码是否位于系统领空(如kernel32ntdll模块内)。如果是,则可能是正确的返回时机。

找到正确的返回点后,在其附近寻找一个非常大的跳转(JMP指令),这很可能就是Magic Jump。将其修改为不跳转(NOP填充),以确保后续修复的准确性。

接着,我们下第二个关键断点:

BP GetCurrentThreadId

中断后,跟随代码执行,通常会看到一个CALL EDI或类似的指令,执行后即可到达程序的OEP


第四步:脱壳与修复 🧰

到达OEP后,使用OllyDbg的脱壳插件进行内存转储。

然后使用Import REC工具修复导入表。修复时,如果无效指针的数量在20个以内(前提是Magic Jump已正确处理),可以直接剪切掉。为了增加脱壳后程序在其他电脑上运行的兼容性,建议在Import REC的设置中取消勾选“使用有效化”、“使用新的安全模式”和“追踪注册表项”这三个选项。

核心概念: OEP是程序原始代码的起点,成功到达此处并修复导入表是脱壳成功的关键。


第五步:分析注册验证逻辑 🔐

脱壳完成后,我们开始分析程序的注册验证机制。

首先运行程序,随意输入用户名和注册码,点击注册按钮。程序提示注册失败时,在OllyDbg中暂停。

以下是定位验证代码的步骤:

  1. GetWindowTextA或类似的API上下断点,以捕获程序读取我们输入的注册码的时刻。
  2. 中断后,返回到程序领空,向上查找验证函数的开始位置(函数头)。
  3. 单步跟踪代码,观察程序如何计算和比对注册码。通常会看到复杂的算法循环和字符串比较。
  4. 寻找决定注册成功与否的关键跳转(JNZJE)或关键调用(CALL)。这个跳转通常直接指向成功或失败的提示信息。

在本案例中,我们找到了一个关键CALL,其返回值(EAX寄存器)决定了后续的跳转。当EAX为0时跳向失败,为1时则成功。


第六步:修改代码实现破解 ✏️

分析清楚逻辑后,破解就变得简单了。

我们进入关键CALL内部,找到影响EAX返回值的指令。通常是一处比较或测试指令后的条件设置(如SETZ AL)。

核心修改:
将导致EAX为0的指令(例如SETZ AL)修改为导致EAX为1的指令(例如SETNZ ALMOV AL, 1)。

; 修改前
SETZ AL   ; 如果相等则AL=1,否则AL=0

; 修改后
MOV AL, 1 ; 强制AL=1

保存修改到可执行文件。再次运行程序,输入任意注册码,发现注册成功提示。程序界面也可能从“未注册版”变为“已注册版”或类似状态。


总结 📝

本节课中我们一起学习了针对穿山甲壳程序的完整分析流程:

  1. 查壳识别:使用工具确定保护类型和版本。
  2. 动态脱壳:利用特定断点技巧,在OllyDbg中寻找Magic Jump和OEP,最终完成脱壳与修复。
  3. 静态分析:在脱壳后的程序中,通过API断点定位注册验证函数。
  4. 逻辑破解:分析算法关键点,修改条件判断指令,实现软件破解。

这个案例融合了脱壳与破解的常见技术,关键在于耐心调试、观察细节并理解程序的执行流程。掌握这些基础方法后,可以应对更多类似的保护方案。

天草流初级课程 - P22:手动Patch教程 🛠️

在本节课中,我们将学习如何手动对程序进行Patch(补丁),以实现破解目的。我们将使用一个之前课程中接触过的目标程序作为示例,但这次不使用现成的Patch工具,而是手动完成整个过程。通过本教程,你将掌握手动定位关键代码、注入自定义代码以及修改程序流程的核心方法。

概述

手动Patch的核心步骤分为三部分:首先,找到程序破解的关键位置和原始入口点(OEP);其次,在程序中找到一个安全区域来注入我们自己的代码;最后,修改程序的执行流程,使其跳转到我们注入的代码处,执行后再返回OEP。下面我们将分步详细讲解。

第一步:定位关键点与OEP

上一节我们概述了手动Patch的三个核心步骤。本节中,我们来看看第一步的具体操作:定位关键点与程序的原始入口点(OEP)。

首先,我们需要使用调试器载入目标程序。载入后,通过单步执行,观察程序的运行流程。

在单步执行的过程中,我们需要找到两个关键位置:

  1. 程序破解的关键判断点:通常是决定程序是否注册成功的一个条件跳转指令。
  2. 程序的原始入口点(OEP):这是程序原本开始执行的地方。

以下是定位过程的关键截图:

如图所示,图中指示的位置就是我们需要修改的、跳向OEP的关键跳转指令。我们的目标就是修改这里,让它先跳转到我们注入的代码。

第二步:寻找代码注入区

在找到了需要修改的关键跳转后,我们需要为自定义代码找一个“家”。本节我们将学习如何在程序中找到一个安全的区域来存放我们的代码。

理想的注入区域是程序内未被使用的空白数据区。通常,我们可以查看程序的区段(Section),寻找一个名为 SFX 或包含大量空值(00)的区段。

对于当前示例程序,SFX 区段从地址 00401234 开始。我们可以在该区段内,寻找一段连续的空白区域(显示为 00 的字节)。选择时需确保我们注入的代码长度不会超出该区段的边界。

例如,我们选择了地址 00401500 作为注入点。只要 00401500 加上我们代码的长度不超过 SFX 区段的结束地址,这个位置就是安全的。

第三步:修改流程与注入代码

现在,我们已经拥有了所有“零件”:关键跳转地址、OEP地址和安全的代码注入地址。本节我们将动手修改程序流程并写入破解代码。

首先,修改第一步中找到的关键跳转指令,使其跳转到我们选定的注入地址(例如 00401500)。

然后,在注入地址 00401500 处,开始写入我们的破解代码。代码需要完成两项任务:

  1. 修改内存中某个关键字节的值(例如,将决定注册状态的 85 改为 84)。
  2. 执行完毕后,跳转回程序的原始入口点(OEP)。

以下是两种写入代码的方式:

方法一:修改单个字节
如果只需要修改一个字节,可以使用 mov byte ptr 指令。

mov byte ptr [目标地址], 84h  ; 将目标地址处的字节改为84(十六进制)
jmp OEP地址                   ; 跳回程序原始入口点

方法二:修改多个字节(以双字为例)
如果需要修改多个字节(例如4个字节),则需要使用 mov dword ptr 指令,并注意数据在内存中的存储顺序是反的(小端序)。

mov dword ptr [目标地址], 840FDB84h  ; 写入4个字节的数据
jmp OEP地址                           ; 跳回程序原始入口点

注意:数据 840FDB84h 在内存中会以 84 DB 0F 84 的顺序存储。

写入代码后,保存对程序的修改。运行修改后的程序,如果破解成功,程序将顺利运行,注册限制已被解除。

原理与细节分析

为了加深理解,让我们分析一下代码的工作原理。我们以修改单个字节为例。

在关键跳转处,程序原本的指令可能是一个条件判断。我们将其改为无条件跳转(jmp)到我们的代码区。

在我们的注入代码中,mov byte ptr [xxxx], 84h 这一行直接改变了内存中某个标志位的值。这个值可能就是程序检查注册状态的关键。将其从 85 改为 84,相当于让程序“认为”注册已经成功。

执行完修改后,jmp OEP 让程序无缝地回到原本的执行流程,就像什么都没发生过一样,但程序的状态已经被我们改变了。

一个重要提示:在使用 mov dword ptr 写入数据时,如果你只想修改一个字节,但只提供了一个字节的数据(例如 84h),那么指令会默认用 00 填充其余3个字节。这可能会意外地破坏周围的数据,导致程序崩溃。因此,务必确保提供的数据长度与指令操作长度匹配。

总结

本节课中,我们一起学习了手动Patch程序的完整流程:

  1. 定位:使用调试器找到关键的破解点和程序的OEP。
  2. 找位置:在程序的空白区段(如SFX)中寻找安全的代码注入地址。
  3. 修改与注入:修改关键跳转指向注入地址,并在该地址写入自定义的破解代码(修改内存值并跳回OEP)。

通过这个实践,我们不仅掌握了一种具体的破解技术,更重要的是理解了程序在内存中的运行和修改机制。这为后续学习更复杂的逆向工程知识打下了坚实的基础。

天草流初级破解教程 - P23:第22课、国外破解技术 - 白嫖无双 🧠💻

在本节课中,我们将学习一种源自国外的软件破解技术。这种技术主要针对使用特定加密算法保护的Delphi程序,其核心思路并非直接调试,而是通过分析并修改程序的资源文件来达到破解目的。我们将通过一个具体的实例来演示整个过程。


概述与背景 🌍

纵观软件加密与破解领域,国外的技术水平通常领先于国内。这一点从加壳软件的开发上就可见一斑。例如,国内有“北斗”加壳,而国外(尤其是俄罗斯)则有高手编写的 AS ProtectAC Protect 等强壳。

本节课介绍的技术并非针对加壳软件,而是针对一种算法加密软件。这种软件本身用于保护其他程序,我们今天的目标就是破解一个被它处理过的程序。

我们先来看一下这个加密软件的官方网站。网站上提供了试用版、标准版、专业版和白金版等不同版本。我们今天的目标程序是一个用Delphi编写的、并经过该软件加密的试用版程序。

注:该软件为试用版,功能受限,但破解思路具有通用性。


目标程序分析 🔍

上一节我们介绍了技术背景,本节中我们来看看目标程序的具体情况。

我已经将目标程序脱壳,得到一个可执行文件。运行程序,可以看到界面显示为“试用版”,用户名为“Unregistered User”,并有30天的使用期限。

这个程序是使用Delphi编写的。通过资源编辑工具(如ResHacker)查看其资源,可以找到一个名为 TForm3 的窗体资源。其内容中包含大量与软件状态相关的字符串,例如:

  • IC-News (软件名称)
  • Trial (试用)
  • Unregistered User (未注册用户)
  • 30 days (30天)
  • Registered (已注册)

这些字符串清晰地表明了程序的不同状态。


破解思路与操作 🛠️

了解了程序结构后,本节我们来看看具体的破解思路和操作步骤。

核心思路是:直接修改资源文件中表示程序状态的字符串,将其从“试用”状态改为“已注册”状态。

以下是具体操作步骤:

  1. 定位关键资源:使用资源编辑工具打开目标程序,找到包含状态字符串的窗体资源(本例中为 TForm3)。
  2. 修改状态字符串:将 TrialUnregistered 等相关字符串全部替换为 RegisteredICNX Registered
  3. 保存并测试:保存修改后的程序并运行。此时程序可能会闪退,这提示我们还有其他的验证机制。

程序闪退的原因在于,它可能依赖外部文件或资源段内的特定数据来运行。检查程序目录,发现存在一个额外的数据文件或资源段。在 TForm1 的资源中,我们也发现了与 TForm3 中类似的一堆数据。

  1. 清除验证数据:将 TForm1 资源中那堆类似的数据全部删除(清零)
  2. 最终验证:再次运行修改后的程序。此时程序成功启动,并显示为“已注册”状态,所有试用限制均已解除。

核心操作总结为代码/命令描述

1. 用资源编辑器打开 Program.exe
2. 在 TForm3 资源中,将 “Trial” 替换为 “Registered”
3. 在 TForm1 资源中,找到并清空可疑的数据段
4. 保存修改,运行破解后的程序

技术要点与拓展 💡

上一节我们完成了破解操作,本节中我们来总结一下其中的技术要点和拓展思路。

这种方法的关键在于识别程序的特征:

  1. 编程语言:目标程序由 Delphi 编写。
  2. 加密标志:程序中包含特定加密软件的标志,例如字符串 “Trial License”
  3. 资源特征:在资源文件中存在大量明文的、描述软件状态(试用、注册、天数)的字符串。

当遇到同时具备这些特征的程序时,就可以尝试使用本课介绍的方法:直接用资源编辑工具打开分析,修改关键字符串和清理验证数据,从而实现快速破解。

从技术发展趋势看,国外在Delphi编程和相应保护技术的应用上起步更早、更广泛。因此,学习和掌握这类国外流行的破解思路,对于应对国内未来可能出现的同类保护软件具有重要意义。


总结 📚

本节课中我们一起学习了一种针对特定加密软件的Delphi程序破解技术。

我们首先了解了国外在软件保护技术上的领先地位。然后,我们分析了一个目标程序,发现其通过资源文件明文存储状态信息。接着,我们通过修改资源字符串清理验证数据两步操作,成功将试用版程序转变为已注册版。最后,我们总结了该技术的识别特征和应用场景。

这种方法的优势在于简单直接,无需复杂的动态调试,适合初学者理解和实践。希望你能掌握这种思路,并灵活运用到未来的学习中去。

天草流初级教程 - P24:第23课,keyMake补丁免杀 🛡️

在本节课中,我们将学习一种针对特定程序(如游戏外挂补丁)的免杀技术。核心思路是通过修改程序入口点(OEP)附近的代码,以绕过杀毒软件(如卡巴斯基)的静态特征检测。我们将使用两种方法:一种是不加花指令的直接修改,另一种是加入花指令的修改。


课程概述

本节课的目标是让一个原本会被杀毒软件查杀的程序,通过修改其入口点附近的代码逻辑,实现免杀效果,并且保证程序功能正常。我们将以两个程序为例进行演示。


演示程序对比

以下是两个程序在卡巴斯基扫描下的结果对比:

  • 原始补丁程序:被卡巴斯基检测为“QQ密码病毒”并查杀。
  • 修改后的免杀程序:成功绕过卡巴斯基的检测。

我们接下来的任务就是将第一个程序修改为第二个程序的状态。


核心思路与准备工作

上一节课我们介绍了手动脱壳,本节课的方法与之有相似之处。核心在于修改程序快要跳转到原始入口点(OEP)的代码路径。

首先,我们需要定位到程序的OEP附近。使用调试器(如OD)载入程序,找到跳转到OEP的指令。例如,在演示中,OEP的地址是 0040100C


方法一:直接修改法(无花指令)

这种方法直接替换入口点附近的代码逻辑,欺骗杀毒软件。

以下是操作步骤:

  1. 定位关键跳转:在调试器中,找到程序运行时最终跳向OEP的那条指令(例如 JMP 0040100C)。
  2. 修改代码:将原有的跳转指令修改为一段新的代码片段。这段代码的功能是将OEP地址压入栈中,然后通过 RETN 指令返回,从而跳转到OEP。
  3. 代码示例
    PUSH 0040100C  ; 将OEP地址压栈
    RETN           ; 返回,跳转到OEP
    
  4. 保存修改:在调试器中应用修改,并保存为新文件。
  5. 验证:使用杀毒软件扫描新文件,并运行测试功能是否正常。

关键点:这种方法利用了VC++程序常见的入口点特征进行替换,修改简单直接。


方法二:添加花指令法

为了增加反分析的难度,我们可以在方法一的基础上加入一些无实际作用的花指令。

以下是操作步骤:

  1. 准备花指令代码:在OEP附近找到有足够空余空间(例如全是00的区域)的位置。
  2. 写入混合代码:写入一段结合了花指令和核心跳转逻辑的代码。例如,先将OEP地址存入寄存器(如EAX),再执行一些无意义的操作,最后压栈并返回。
  3. 代码示例
    MOV EAX, 0040100C  ; 将OEP地址存入EAX
    NOP                ; 无意义指令(花指令)
    NOP                ; 无意义指令(花指令)
    PUSH EAX           ; 将EAX(即OEP地址)压栈
    RETN               ; 返回,跳转到OEP
    
  4. 修改跳转:将原程序跳转到OEP的指令,改为跳转到我们刚写入的这段新代码的起始地址。
  5. 栈平衡:务必注意,在修改代码时,要保证堆栈的平衡。如果使用了 PUSH EAX,通常需要在之前或之后有对应的操作来平衡堆栈。

关键点:花指令能干扰简单的静态分析,但核心跳转逻辑(压栈+返回)与方法一相同。


其他思路与注意事项

除了 PUSH + RETN,还可以尝试其他跳转方式,例如直接使用 JMP 指令跳转到一段新的代码区域,再在该区域执行跳转到OEP的操作。方法多种多样,核心是改变杀毒软件识别的固定模式。

需要注意以下几点:

  • 程序兼容性:对于某些带有校验或特殊数据(如本例中的“LOAD”数据)的程序,直接在原始代码上加壳可能导致程序损坏。我们的方法是在入口点“做手脚”,不影响程序主体,兼容性更好。
  • 杀毒软件适应性:此方法主要针对特定杀毒软件(如卡巴斯基)的静态特征检测。对于其他杀毒软件或动态行为检测,效果可能不同。
  • 头部替换:也可以尝试用其他编译器(如Delphi)生成程序的PE头部特征来替换原程序头部,有时也能起到免杀效果。

课程总结

本节课我们一起学习了针对“keyMake”补丁程序的免杀技术。

我们掌握了两种核心方法:

  1. 直接修改法:通过 PUSH OEP_ADDRESSRETN 指令组合,直接修改入口跳转。
  2. 添加花指令法:在核心跳转逻辑中加入干扰指令,增强免杀效果。

这两种方法的共同思路都是修改程序快要跳转到原始入口点(OEP)的代码,从而改变程序的静态特征,绕过杀毒软件的查杀。请务必在理解原理的基础上进行实践,并注意保持修改后程序的正常运行。

天草流初级课程 - P25:破解与手动Patch实例2 🔧

在本节课中,我们将学习如何对一个加壳程序进行手动Patch,以实现软件破解。我们将通过一个具体实例,详细讲解定位关键跳转、修改汇编指令以及如何在加壳程序中写入补丁代码的完整流程。


课程概述 📖

本节课是“破解与手动Patch”系列的第二讲。我们将对一个使用UPX加壳的软件进行破解。核心目标是找到控制软件注册状态的关键跳转,并通过修改最少的字节(本例中为一个字节),使软件显示为已注册版本。

上一节我们介绍了手动Patch的基本概念,本节中我们来看看如何在一个实际的加壳程序中应用这些技巧。


准备工作 🛠️

首先,我们有两个相同的软件文件,其中一个为原始加壳版本,另一个为已脱壳版本用于分析。

运行原始加壳程序时,可能会提示缺少相关文件(如XML)。为了方便调试,可以临时修改文件名以避免此错误。

软件界面中显示“This copy is unregistered”,这表明当前为未注册版本。我们的目标就是改变这个状态。


定位关键代码 🔍

我们将使用调试器载入已脱壳的程序进行分析。

  1. 在反汇编窗口中搜索字符串“This copy is unregistered”。
  2. 在搜索结果附近,可以找到“registered version”等相关字符串。双击进入该代码区域。
  3. 在此区域,通常会存在一个关键的条件跳转指令(如JZ, JNZ),它决定了程序显示注册还是未注册信息。

以下是定位过程的要点:

  • 该跳转指令很可能依赖于某个寄存器的值(例如AL)。
  • 在调试时,可以在该跳转指令处设置断点。
  • 为了触发断点,可以事先在软件中输入一个假的注册码,这样程序运行时就会读取并处理这个信息,从而执行到我们的断点处。

分析并修改关键跳转 ⚙️

当程序在关键跳转处断下后,我们需要观察是哪个条件导致了跳转。

  • 例如,发现AL寄存器的值为00时,程序跳向未注册流程。
  • 我们的目标就是让程序不执行这个跳转,或者让AL的值变为非零(如01)。

通过分析上下文代码,我们发现有一条指令MOV AL, [EBX]EBX指向一个可能由算法生成的字符串。

  • 深入分析这个字符串生成算法(看起来类似MD5)对初学者而言较为复杂。
  • 更简单的方法是:我们注意到ECX寄存器在此处持有一个唯一且非零的值。

因此,我们可以将指令修改为MOV AL, [ECX],这样AL就能获得一个非零值,从而使关键跳转失效。修改汇编指令对应的机器码,可能仅需改变一个字节(例如将8B03改为8B01)。

在脱壳文件中完成此修改并保存后,运行程序,界面应显示为“registered to winning”,表示破解成功。


在加壳程序中手动Patch 🧩

现在,我们需要将上述修改应用到原始的加壳程序上。由于程序被压缩,我们不能直接修改原始代码段。

以下是操作步骤:

  1. 寻找代码位置:在调试器中载入加壳程序,使用ESP定律等方法找到程序的原始入口点(OEP)附近。定位到与脱壳文件中相同功能的关键指令位置(例如地址0045D2D3处的指令MOV AL, [EBX])。
  2. 寻找空闲空间:我们需要在加壳程序的某个区段(如UPX1)中找到一段未被使用的“空闲区域”来写入我们的补丁代码。在内存映射中,通常可以看到大量以00填充的区域。
  3. 编写补丁代码:在选定的空闲地址(例如0065BDC0)编写补丁。补丁需要完成两件事:
    • 执行我们需要的修改(例如MOV AL, 01)。
    • 跳转回原始流程中被我们“覆盖”掉的原指令之后的位置继续执行。
      补丁代码示例如下:
    0065BDC0    B0 01          MOV AL, 1          ; 将AL设置为1
    0065BDC2    EB 0E          JMP 0045D2E3       ; 跳回原流程的下一句指令
    
  4. 修改原指令:将原关键指令(0045D2D3处的8B03)修改为一个跳转指令,让其跳转到我们刚刚编写的补丁地址。
    0045D2D3    E9 E8EA77FF    JMP 0065BDC0       ; 跳转到我们的补丁代码
    
  5. 执行验证:运行修改后的加壳程序。程序执行到0045D2D3时会跳转到我们的补丁,将AL设为1,然后跳回原流程,从而实现与修改脱壳文件相同的破解效果。

核心要点总结 ✨

本节课中我们一起学习了手动Patch的完整实战流程:

  1. 分析定位:在脱壳版本中搜索关键字符串,找到决定注册状态的关键跳转指令。
  2. 策略修改:分析上下文,找到用最少字节修改(通常是一字节)改变程序逻辑的方法(如修改数据来源寄存器)。
  3. 加壳程序Patch:在加壳程序中寻找空闲空间写入补丁代码,并通过修改原指令跳转到补丁,最终实现破解。

手动Patch的关键在于理解程序逻辑灵活运用跳转。这种方法尤其适用于一些压缩壳,对于有强校验的加密壳则会复杂得多。打好汇编和调试基础是掌握更高级破解技术的前提。


注意:本教程仅用于安全技术学习与研究,请勿用于非法用途。

天草流初级破解教程 - P26:第25课 - 破解重启验证与内存注册机制作 🔍

在本节课中,我们将学习如何分析一个采用“重启验证”方式的程序,并利用OD(OllyDbg)动态调试,最终通过制作内存注册机来获取有效的注册码。整个过程将涉及对程序文件的分析、关键字符串的定位、算法逻辑的追踪以及注册机的配置。


概述与程序行为分析

首先运行目标程序,尝试输入假码并点击注册按钮。程序无任何即时反馈,这通常表明其采用了重启验证机制。程序会在关闭或重启后,读取存储在系统某处(如配置文件、注册表)的注册信息进行验证。

因此,我们的首要任务是定位程序用于存储注册信息的文件。


定位与分析配置文件

在程序目录下进行查找,发现一个可疑的配置文件(例如 .ini.dat 或特定名称文件)。这通常是重启验证程序存储注册信息的位置。

使用OD载入目标程序并运行。程序会读取该配置文件。通过OD的字符串参考功能,或在其读取文件内容的API函数(如 GetPrivateProfileString)上设置断点,可以拦截到程序对文件的操作。

程序首先读取文件中存储的用户名,接着读取注册码。我们关注读取注册码后的程序逻辑。


追踪注册码验证算法

当程序读取我们输入的假注册码后,会进入验证流程。在OD中,可以看到程序将用户名通过某种算法转换成一个字符串。

例如,代码中可能出现如下逻辑:

; 假设将用户名转换
CALL 算法函数
MOV 转换后字符串, EAX

随后,程序会将这个转换后的字符串与从配置文件中读取的注册码进行比较。

进一步分析发现,该程序对用户名应用了两套不同的算法,分别生成两个不同的字符串,并据此生成两个可能的有效注册码。这意味着每个用户名可能对应两个有效的注册码。

字符串的生成规则可能涉及截取拼接。例如:

  • 规则1:从转换后的字符串中,以特定间隔截取固定长度的子串。
    • 例如:取前5位,跳过4位,再取5位,跳过2位,再取5位。
    • 伪代码表示:S1 = Mid(原始字符串, 1, 5) + Mid(原始字符串, 10, 5) + Mid(原始字符串, 17, 5)
  • 规则2:将字符串转为大写后,按“3-5-3”结构,间隔特定位置进行截取。
    • 伪代码表示:S2 = UCase(Mid(原始字符串, 1, 3)) + "-" + UCase(Mid(原始字符串, 5, 5)) + "-" + UCase(Mid(原始字符串, 11, 3))

最终,程序会用“-”将截取出的各部分连接,形成完整的注册码字符串,再与我们输入的假码进行比较。


获取与验证注册码

在OD中跟踪到比较指令(通常是 CMPTEST)时,可以在寄存器或堆栈中看到程序计算出的真实验证码

将OD中找到的两个潜在注册码记录下来。退出OD,在原始程序中,将配置文件内的假注册码替换为找到的真注册码。重启程序验证,确认注册成功。

这证明了我们的分析是正确的:程序使用两套算法,为用户名生成两个有效注册码。


制作内存注册机

由于我们已找到生成注册码的关键代码地址,可以制作内存注册机,使其自动为任意用户名计算注册码。

以下是制作步骤:

  1. 定位关键指令:在OD中找到生成第一个注册码并将结果放入 EAX 寄存器的指令地址。再找到生成第二个注册码的指令地址。
  2. 配置注册机
    • 打开内存注册机制作工具(例如 Keymake)。
    • 添加第一个中断地址。
    • 中断次数:1
    • 第一字节:该地址处指令的第一个字节(例如 0x18)。
    • 指令长度:需复制的指令长度。注册码以Unicode字符串形式存在于 EAX 中,每个字符占2字节。例如,10字符的注册码,长度为 10 * 2 = 20 字节,但通常工具按“字节数”输入,这里填 20。注意,有些工具可能自动计算,需根据实际情况调整。
    • 内存方式:选择 EAX 寄存器。
    • 重复以上步骤,添加第二个中断地址以获取第二个注册码。
  3. 测试注册机:运行目标程序和内存注册机。在程序界面输入任意用户名,内存注册机会自动弹出计算出的两个注册码。使用任一注册码均可完成注册。

注意:内存注册机生成的注册码与运行注册机时程序界面中填写的用户名严格对应。


总结

本节课我们一起学习了破解重启验证程序的标准流程。

  1. 分析行为:通过程序无即时反馈判断其为重启验证。
  2. 定位数据:查找并分析程序存储注册信息的配置文件。
  3. 动态调试:使用OD跟踪程序启动时读取配置文件和验证注册码的过程。
  4. 分析算法:理解程序将用户名通过特定算法(截取、拼接、大小写转换)转换为验证码的逻辑。本例中发现了两套并行的算法
  5. 获取密钥:直接从内存中提取算法计算出的有效注册码。
  6. 制作工具:基于关键代码地址,制作内存注册机,实现对该程序注册码的自动计算。

掌握此流程和OD的基本调试技巧,是理解更复杂加密算法的基础。请务必扎实练习,尝试用不同的用户名进行测试,以加深对算法规则的理解。

天草流初级教程 - P27:初级班总结课 📚

在本节课中,我们将对天草流初级班的所有核心内容进行系统性的梳理和总结。课程涵盖了从脱壳、破解到补丁制作的全流程关键技术与思路。


课件与课程安排说明

上一节我们完成了具体的破解案例分析,本节中我们来看看整个初级课程的框架。

我们的前面几节课中,有几节关于汇编的课程后来没有加入。目前没有汇编基础的同学,需要先自学几节汇编基础课,然后在后续课程中慢慢吸收。单独讲解汇编课程非常枯燥,并且大家不知道如何运用,因此没有加入正式课程。

大家在课后需要下功夫学习汇编。这就是“脱课”的内容。


脱壳技术要点 🐚

脱壳是逆向分析的第一步。本课程中专门讲解了三节脱壳课。

以下是脱壳部分的核心要点:

  1. 五种常见语言的特征

    • 大家需要自己手动查找并总结这五种语言的特征,这样印象会更深刻。这五种语言是:汇编、C、Delphi、VB、BC++。
  2. 附加数据的处理

    • 方法是找到最后一个区段的 R offsetR size 并压栈,这代表附加数据。
    • 用到的工具有:OneHexHWalkSensor
  3. 自校验的处理

    • 有两种主要方法:
      • F12堆栈调用法:如果错误提示是“调用错误”,则运行程序,等错误提示出现时按F12暂停,然后分析调用堆栈。
      • 双OD对比法:如果是“文件或数据损坏”类错误提示,用两个OD同时打开程序,分别下 ReadFile 断点,等读取文件时返回,慢慢跟踪。
  4. 手动查找IAT(导入地址表)

    • 这并非仅针对BC++程序。现在很多强壳都需要手动查找IAT,这是必须掌握的技能,否则后续课程将无法理解。

破解技术核心 🔓

我们在初级班中有不少破解课,共有12节。

一般程序的破解(非重启验证或网络验证),目标是找到注册相关的按钮事件。

以下是破解的常用方法:

  • 查找字符串法:在OD中搜索可能的注册成功/失败字符串。
  • 消息断点法:使用 bp rtcMsgBox 下断点,点击目标按钮后程序会中断,然后回溯找到关键代码。
  • 错误提示断点法:如果有错误提示框,可以使用 MessageBoxA/W 断点法。

重启验证破解思路 🔄

重启验证的目标是首先找到验证信息的存储类型。

主要有两大类:

  1. 注册表类型
  2. 文件类型
    • 其中有一个特殊类型是 .ini文件类型
    • 其他如.dll文件类型比较少见,因为编程上处理较难。

方法如下:

  • 针对注册表类型:常用的API是 RegOpenKeyExA/WRegQueryValueExA/W,其中最常用的是后者。
  • 针对文件类型:常用的API是 CreateFileA/W
  • 针对.ini文件类型:常用的API是 GetPrivateProfileStringA(获取)和 WritePrivateProfileStringA(写入),在Delphi编写的程序中常调用。

重启验证软件的通用分析思路:

  1. 试运行程序,输入假码。
  2. 首先在程序安装目录中查找可疑的可写入文件,如.ini文件、配置文件或.data文件。
  3. 可以通过系统“运行”输入 regedit 直接查看注册表。
  4. 或者用OD查找字符串,看有无可疑的注册表路径或文件名信息。这需要一定的经验和直觉。

不脱壳破解与补丁制作 🛠️

上一节我们介绍了重启验证,本节中我们来看看不脱壳破解和后续的补丁制作。

不脱壳破解步骤:

  1. 首先到达OEP(原始入口点),以便能查找字符串。
  2. 或者直接使用F12堆栈调用法。
  3. 注意:在可调试的情况下,找到相关按钮事件后,最好同时下“硬件执行断点”和普通的 F2(BP)断点,这样更保险。因为重新载入程序后,普通断点可能会失效。

补丁制作手段:

  1. 脚本补丁:使用相关工具制作,例如课程中用到的工具。
  2. 手动补丁:需要注意以下几点:
    • 尽量以改动最少的指令来达到破解目的。
    • 找到注入代码的地方。有几个关键点:
      • SFX区段。
      • 跳向OEP的地方如果有空闲空间,此处是首选(例如UPX壳)。
    • 修改代码时,尽量只修改一个字节(byte)。如果修改双字(dword,4字节),要确保空间足够,否则会覆盖后续指令,导致错误。

常用辅助工具:

  • DEDE:对应Delphi和VC++程序。
  • PE Explorer:也可用于分析多种语言编写的程序,破解Delphi时经常使用。
  • Keymake:用于制作内存补丁机。最新版是2.0。注意其生成的补丁可能被卡巴斯基查杀,课程中讲解了免杀方法。

要求对这些工具进行非常熟练的操作,因为后续课程中会经常用到。

另外需留意:Delphi程序的事件调用技术 call dword ptr [eax+$XX],在第22课“国外破解技术”中已有讲解。


学习习惯与方法 💡

最后,我们强调一下学习习惯和方法。

以下是给初学者的建议:

  1. 学习习惯:遇到问题尽量先自己思考解决方案,实在不行再提问。通过自己思考并解决问题,可以积累宝贵经验。
  2. 学习方法:推荐一个非常扎实的方法——每节课后都做内容总结。将老师讲的东西转化为自己的理解。如果只是听讲,知识可能左耳进右耳出。
  3. 注意事项:请不要泄露教程内容。

总结与预告

本节课中,我们一起学习了天草流初级班的完整知识体系总结,包括脱壳的关键点、常规破解与重启验证的分析思路、不脱壳破解技巧、补丁制作方法以及重要的学习态度。

初级班课程至此全部结束。中级班课程预计将于近期(如3月28日或4月初)开课。希望继续学习的同学请留意通知。

再见。

天草流初级课程 - P3:汇编指令(三)算术运算指令 🧮

在本节课中,我们将学习汇编语言中的算术运算指令。这些指令是CPU进行数学计算的基础,在分析软件算法、编写注册机或理解程序逻辑时至关重要。我们将逐一介绍加法、减法、乘法、除法等核心指令及其应用场景。


概述

算术运算指令是反映CPU计算能力的一组指令,在汇编程序中被频繁使用。无论是分析算法、编写注册机,还是处理数据,都会经常遇到它们。这些指令不仅用于处理字符串操作,也用于处理数字数据,因为机器码中既包含字母也包含数字。运算类型包括加、减、乘、除以及其他辅助指令。

操作数与寻址方式

该组指令的操作数可以是8位、16位或32位。当操作数是存储单元时,可以使用任意一种存储单元寻址方式。寻址方式本身是一个较为复杂的话题,我们将在后续课程中专门讲解,因为它涉及到寄存器操作和对内存的直接操作。

加法指令

加法指令用于执行加法运算。在OD(OllyDbg)反汇编出来的代码中,其语法结构与标准汇编相比可能不完全规范,但这通常不影响分析。以下是常见的加法指令:

  • ADD:基本的加法指令。
  • ADC:带进位的加法指令。在实际分析中,这条指令比较少见。
  • INC:加1指令。这条指令非常重要,常用于循环计数。

INC指令的应用场景

当分析算法时,程序常需要对机器码进行逐位处理。例如,从第一位开始,对每一位进行某种运算(如取ASCII码),这时就会用到INC指令来递增计数器。

示例代码:

INC EAX    ; 将EAX寄存器中的值加1

假设EAX的初始值是1,执行上述指令后,EAX的值将变为2。这条指令后面通常会跟随一个条件跳转指令(如JNZJG),用于控制循环,直到处理完所有位。循环过程中,程序会与一个保存了总长度的值(例如也保存在EAX或其他寄存器中)进行比较(CMP),以判断是否继续循环。

减法指令

减法指令与加法指令相对应,用于执行减法运算。

  • SUB:基本的减法指令,与ADD相对应。
  • SBB:带借位的减法指令,与ADC相对应,同样比较少见。
  • DEC:减1指令。

DEC指令的应用场景

DEC指令与INC原理相似但方向相反。例如,如果需要从机器码的最后一位(最高位)开始向前处理,就可能使用DEC指令来递减计数器。具体情况需要根据实际代码逻辑进行分析。

  • NEG:求补指令(取负数)。例如,如果EAX的初值是1,执行NEG EAX后,EAX的值将变为-1。

乘法指令

乘法指令用于执行乘法运算,主要分为无符号乘法和有符号乘法。

  • MUL:无符号数乘法指令。
  • IMUL:有符号数乘法指令。
  • FMUL:浮点数乘法指令(无符号)。
  • FIMUL:有符号浮点数乘法指令。

可以将FIMUL理解为“有符号的浮点乘法指令”。

除法指令

除法指令用于执行除法运算,分类与乘法指令类似。

  • DIV:无符号数除法指令。
  • IDIV:有符号数除法指令。
  • FDIV:浮点数除法指令。
  • FIDIV:有符号浮点数除法指令。

关于浮点运算的更详细内容,可能会在后续课程中根据安排进行讲解。

标志位影响

各种算术运算指令执行后,会影响CPU中的状态标志位(如零标志ZF、进位标志CF等)。在实际的逆向分析中,初学者可以暂时不用深入关注每一个标志位的具体变化。但如果你想更深入地学习汇编语言,建议系统学习标准的Windows环境下的汇编编程,其原理与我们在OD中看到的大体相同。

需要说明的是,OD反汇编引擎需要兼容多种编译器生成的代码,因此其显示的汇编语法可能与教科书上的“标准”汇编略有不同,但这已足够用于软件逆向分析,也体现了OD作者高超的技术水平。


总结

本节课我们一起学习了汇编语言中的核心算术运算指令。我们介绍了加法(ADD, INC)、减法(SUB, DEC, NEG)、乘法(MUL, IMUL)和除法(DIV, IDIV)指令的基本用途和典型应用场景,特别是在算法分析循环中INCDEC指令的关键作用。理解这些指令是逆向工程中分析程序计算逻辑的基础。下节课我们将探讨其他重要的汇编指令集。

天草流初级课程 - P4:汇编4 - 逻辑运算指令 🧮

在本节课中,我们将要学习汇编语言中的逻辑运算指令。这些指令是进行二进制位操作的基础,在软件逆向和算法分析中至关重要。


上一节我们介绍了算术和转移指令,本节中我们来看看另一组重要的指令:逻辑运算指令。

逻辑运算指令主要包括以下几种:

  • AND:逻辑与
  • OR:逻辑或
  • NOT:逻辑非
  • XOR:逻辑异或

这些指令经常被使用,其操作对象是操作数的每一位二进制位。

以下是逻辑运算的基本规则:

  1. 逻辑与 (AND):将源操作数与目的操作数对应的每一位二进制进行“与”运算。规则是:两位都为1时,结果才为1
    • 公式:1 AND 1 = 11 AND 0 = 00 AND 1 = 00 AND 0 = 0
    • 示例:1111 AND 1000 = 1000

  1. 逻辑或 (OR):将对应的二进制位进行“或”运算。规则是:两位中有一位为1,结果就为1

    • 公式:1 OR 1 = 11 OR 0 = 10 OR 1 = 10 OR 0 = 0
  2. 逻辑非 (NOT):对操作数的每一位二进制取反。

    • 示例:NOT 0100 = 1011
  3. 逻辑异或 (XOR):将对应的二进制位进行“异或”运算。规则是:两位不同时,结果为1;相同时,结果为0

    • 公式:1 XOR 1 = 01 XOR 0 = 10 XOR 1 = 10 XOR 0 = 0
    • 一个关键特性:任何数与其自身进行异或,结果都为0。即 A XOR A = 0

理解了基本概念后,我们来看看这些指令在实践中的意义。在分析软件算法时,我们常会用到计算器。需要注意的是,在调试器(如OD)中,数字默认是十六进制显示的。

这里有几个值得注意的现象:

  • 5 XOR 5 = 0
  • 5 AND 5 = 5

在算法分析中,经常能看到类似 EAX XOR EAX 这样的指令,其目的就是将寄存器清零(结果为0),并设置相应的标志位(如ZF零标志位)。这个零标志位会直接影响后续的条件跳转指令(如 JZ/JEJNZ/JNE)。

考虑以下典型代码片段:

MOV EAX, [参数]
CALL 子程序
... (子程序内执行一系列操作)
XOR EAX, EAX  ; 将EAX清零,ZF置1
RETN
... (返回到调用处)
TEST EAX, EAX
JZ 正确流程   ; 因为ZF=1,所以跳转
JMP 错误流程

通过修改逻辑(例如将 XOR EAX, EAX 改为 MOV EAX, 1),可以改变程序的执行流程,这在破解中常被用于“爆破”。


除了使用计算器,我们还可以直接在调试器(OD)中观察逻辑运算的结果。在分析时,应同时关注反汇编窗口寄存器窗口数据窗口

例如,在OD中单步执行一条 XOR EDX, EDX 指令,可以立即在寄存器窗口中看到EDX的值变为0。执行 AND ECX, 0xFF 指令,可以快速看出该操作是取ECX的最低一个字节。

养成记录的习惯很重要。将分析过程中遇到的特定指令效果记录下来,在后续的二次或三次分析时,就能大大提高效率,无需每次都重新计算。


本节课中我们一起学习了汇编语言的核心逻辑运算指令:AND、OR、NOT和XOR。我们了解了它们的基本运算规则,探讨了XOR A, A用于清零和设置标志位的常见用法,并学习了如何在调试环境中观察和应用这些指令。掌握这些指令是理解程序底层逻辑和控制流的关键一步。

天草流初级汇编教程 - P5:汇编指令详解(三)🔍

在本节课中,我们将要学习汇编语言中三个重要的概念:TEST指令、循环指令以及条件转移指令。这些指令在程序流程控制和算法分析中扮演着核心角色。


1. TEST指令 🧪

上一节我们介绍了逻辑运算指令,本节中我们来看看TEST指令。TEST指令用于对两个操作数执行逻辑与(AND)操作,并根据运算结果设置相应的标志位(如零标志位ZF)。关键点在于,它不保存运算结果,因此不会改变原操作数

该指令通常后跟条件转移指令(如JZJNZ),用于根据测试结果决定程序流向。一个常见的模式如下:

CALL  某个子程序    ; 调用一个算法或功能子程序
TEST  EAX, EAX     ; 测试EAX寄存器的值
JZ    标签名        ; 如果结果为0(EAX=0),则跳转

在这个模式中,程序根据子程序的返回值(通常放在EAX中)进行条件判断,从而跳转到不同的分支。


2. 循环指令 🔄

在编程中,循环是重复执行某段代码的结构。在汇编层面,除了专用的LOOP指令外,更常见的是利用比较和条件跳转指令来构建循环。

以下是构建循环的一种通用模式:

MOV ECX, 循环次数     ; 初始化计数器
标签_开始:
    ...               ; 循环体代码
    DEC ECX           ; 计数器减1
    CMP ECX, 0        ; 比较计数器是否为零
    JNE 标签_开始      ; 如果不为零,则跳回开始处继续循环

LOOP指令虽然存在,但在实际分析中遇到较少。上述利用CMPJNE(或JGJNG等)构建循环的方式更为灵活和常见。

为了加深理解,我们来看一个计算1到1000之和的例子,并比较两种实现方法。

示例:求1到1000的和,结果存入AX

以下是两种实现方法:

方法一:使用循环计数器递减

XOR AX, AX      ; 将AX清零 (AX ^ AX = 0)
MOV CX, 1000D   ; 将十进制数1000送入CX
标签_AGAIN:
ADD AX, CX      ; AX = AX + CX
DEC CX          ; CX = CX - 1
LOOP 标签_AGAIN   ; 若CX不为0,则跳回AGAIN继续循环

此方法利用了LOOP指令和计数器CX

方法二:使用额外寄存器累加

XOR AX, AX      ; AX清零
MOV CX, 1000D   ; CX=1000
MOV BX, 1       ; BX=1
标签_AGAIN:
ADD AX, BX      ; AX = AX + BX
INC BX          ; BX = BX + 1
LOOP 标签_AGAIN   ; 循环

此方法使用BX寄存器从1累加到1000。

从程序效率角度看,方法一比方法二更优。因为代码更简洁,使用的指令和寄存器更少,这通常意味着更高的执行效率和更小的程序体积。


3. 转移指令 🚦

转移指令用于改变程序的执行流程,是汇编程序中不可或缺的部分。高级语言中常建议慎用goto(转移语句),但在汇编语言中,灵活运用转移指令是实现复杂逻辑的基础。

转移指令主要分为两大类:无条件转移条件转移

无条件转移指令JMP非常简单,它使程序无条件跳转到指定地址,前面课程已介绍过。

条件转移指令则根据标志寄存器的状态决定是否跳转。它们可分为以下三类:

以下是基于不同标志位的条件转移指令分类:

第一类:基于无符号数比较的条件转移

  • JE/JZ:相等/为零则跳转。 (ZF=1)
  • JNE/JNZ:不相等/不为零则跳转。 (ZF=0)
  • JA/JNBE:高于/不低于且不等于则跳转(用于无符号数)。 (CF=0且ZF=0)
  • JAE/JNB:高于或等于/不低于则跳转。 (CF=0)
  • JB/JNAE:低于/不高于且不等于则跳转。 (CF=1)
  • JBE/JNA:低于或等于/不高于则跳转。 (CF=1或ZF=1)

第二类:基于有符号数比较的条件转移

  • JE/JZ:相等/为零则跳转。 (ZF=1)
  • JNE/JNZ:不相等/不为零则跳转。 (ZF=0)
  • JG/JNLE:大于/不小于且不等于则跳转(用于有符号数)。 (SF=OF且ZF=0)
  • JGE/JNL:大于或等于/不小于则跳转。 (SF=OF)
  • JL/JNGE:小于/不大于且不等于则跳转。 (SF≠OF)
  • JLE/JNG:小于或等于/不大于则跳转。 (SF≠OF或ZF=1)

第三类:基于特殊算术标志位的条件转移

  • JC:有进位则跳转。 (CF=1)
  • JNC:无进位则跳转。 (CF=0)
  • JO:溢出则跳转。 (OF=1)
  • JNO:无溢出则跳转。 (OF=0)
  • JP/JPE:奇偶标志为1(偶数个1)则跳转。 (PF=1)
  • JNP/JPO:奇偶标志为0(奇数个1)则跳转。 (PF=0)
  • JS:符号标志为1(结果为负)则跳转。 (SF=1)
  • JNS:符号标志为0(结果为非负)则跳转。 (SF=0)

助记符多为英文单词缩写,理解其含义有助于记忆,例如:A(Above), B(Below), E(Equal), G(Greater), L(Less), C(Carry), O(Overflow), S(Sign)。

实际应用示例:将大写字母转换为小写字母

假设有一个字符变量char,以下程序段判断它是否为大写字母(A-Z),若是则转换为小写。

    CMP char, 'A'    ; 比较字符是否小于‘A’
    JB   NEXT         ; 若小于,则不是大写字母,跳转到NEXT
    CMP char, 'Z'    ; 比较字符是否大于‘Z’
    JA   NEXT         ; 若大于,则不是大写字母,跳转到NEXT
    ADD char, 20H    ; 是大写字母,加上20H转换为小写(ASCII码差值)
NEXT:
    ...              ; 后续处理

注意:字符在比较时被视为无符号数,因此应使用JB(低于)、JA(高于)等无符号条件跳转指令,而非JLJG等有符号跳转指令。大写字母转小写只需将其ASCII码值加上20H(十进制32)。


总结 📚

本节课中我们一起学习了汇编语言的三个核心指令:

  1. TEST指令:用于测试操作数,设置标志位但不改变原值,常为条件跳转做准备。
  2. 循环结构:掌握了通过CMP与条件跳转指令(如JNE)构建循环的通用方法,并通过求和案例对比了不同实现方式的优劣。
  3. 条件转移指令:系统学习了基于无符号数、有符号数及特殊标志位的各类条件跳转指令及其应用场景,并通过大小写转换示例巩固了理解。

这些指令是控制程序流程的基石,请务必结合实践加以掌握。下一节课,我们将重点讲解子程序的调用与返回机制。


再见。

天草流初级汇编课程 - P6:子程序调用与返回 🧩

在本节课中,我们将学习汇编语言中一个非常重要的概念:子程序的调用与返回。我们将通过一个简单的实例程序,来理解callret指令的工作原理,并学习如何在逆向分析中识别和跟踪它们。


概述:什么是子程序调用?

上一节我们介绍了基本的程序流程控制。本节中,我们来看看一种特殊的流程转移操作——子程序调用。

子程序的调用和返回是一对互逆的操作。调用指令call会改变程序的执行顺序,跳转到子程序的入口地址。与普通的转移指令不同,子程序执行完毕后,CPU需要能够返回到调用指令之后的那条指令继续执行,这个“返回”操作就是通过ret指令完成的。

理论:CALL与RET指令详解

调用指令 (CALL)

call指令的格式根据子程序的属性(近或远)而有所不同。

  • 近调用 (Near Call):如果被调用的子程序属性是“近”的,call指令执行一个近调用。它会将该指令之后地址的偏移量压入堆栈。这样,返回时就能知道该回到哪里。

    • 公式表示:push EIP (下一条指令地址) -> jmp near 子程序地址
  • 远调用 (Far Call):如果被调用的子程序属性是“远”的,call指令执行一个远调用。此时,它不仅要把下一条指令的偏移量压栈,还要把当前代码段寄存器CS的值也压入堆栈。

    • 公式表示:push CS -> push EIP -> jmp far 子程序地址

返回指令 (RET)

返回指令ret(在调试器OD中常显示为retn)的功能与call相反。它从堆栈中弹出返回地址(对于远调用还要弹出CS),并跳转到该地址,从而恢复call之前的执行流程。

其核心原理是:

  1. call指令将返回地址压入堆栈。
  2. 子程序执行。
  3. ret指令从堆栈中弹出返回地址,并跳转回去。

用代码可以简单模拟这个过程:

; 假设调用 call sub_proc
call sub_proc    ; 1. 将下一行地址(00401005)压栈,然后跳转到sub_proc
next_instruction: ; 2. 这是要返回的地址
...

![](https://github.com/OpenDocCN/sec-notes-zh/raw/master/docs/tiancao/img/0a0c5060ff383763b648e30ae9a086bc_1.png)

![](https://github.com/OpenDocCN/sec-notes-zh/raw/master/docs/tiancao/img/0a0c5060ff383763b648e30ae9a086bc_2.png)

sub_proc:
    ... ; 3. 子程序代码
    ret ; 4. 从堆栈弹出00401005,并跳转回去


实践:分析一个简单的注册验证程序

理论部分我们介绍了子程序调用的机制,现在让我们通过一个实际例子来加深理解。我将使用一个用Delphi编写的简单程序作为实例。

这个程序的功能是:输入正确的注册码,一个灰色的窗口控件会变为可用(变色);输入错误的注册码,程序会直接退出。

以下是程序的核心算法逻辑(伪代码):

// 读取输入
reg = StrToFloat(Edit2.Text); // 注册码转换为浮点数
temp = StrToFloat(Edit1.Text); // 机器码转换为浮点数

![](https://github.com/OpenDocCN/sec-notes-zh/raw/master/docs/tiancao/img/0a0c5060ff383763b648e30ae9a086bc_11.png)

// 验证算法
if (reg * temp == 823729842936.0) {
    Memo1.Enabled := True; // 控件启用,变色
} else {
    Application.Terminate; // 程序退出
}

可以看到,算法很简单:注册码 × 机器码需要等于一个固定的常数。

第一步:定位关键代码

由于程序是Delphi编写,我们使用辅助工具Dede来快速定位按钮点击事件的代码起始地址。

以下是使用Dede的步骤:

  1. 加载目标程序。
  2. 在窗口列表中,找到主窗体。
  3. 在“过程”页面,找到Button1Click事件(即注册按钮)。
  4. 双击获取其内存地址。

提示:如果程序有多个按钮,可以使用ResScope等资源工具查看窗体资源,通过按钮的标题文本来确认哪个是我们要找的Button1

获得地址后,用OD载入程序,Ctrl+G跳转到该地址,并在此处下断点。

第二步:动态跟踪与分析

运行程序(F9),输入一个错误的注册码(例如123456789),点击注册按钮。程序会断在我们下的断点处。

现在开始单步跟踪(F8/F7),我们的重点是观察call指令。

  1. 第一个call:跟随F7进入。观察堆栈和寄存器变化。这个call通常用于获取我们输入的注册码字符串

  2. 后续call:继续单步,会遇到多个call。其中一些call负责将字符串转换为浮点数(对应源码的StrToFloat)。

    • 在OD中,浮点转换指令通常是FLDFSTP等。
    • FLD指令:将源操作数压入浮点寄存器栈。
    • FSTP指令:将浮点寄存器栈顶的值弹出并存储到目标内存地址。
  3. 关键比较:在跟踪过程中,你会看到浮点乘法指令FMUL,这就是算法中“注册码 × 机器码”的步骤。之后会有一个浮点比较指令FCOMP,将相乘的结果与一个固定常数(823729842936.0)进行比较。

第三步:修改流程与验证

在比较指令之后,会根据结果进行条件跳转(如jnz)。如果结果不相等(注册码错误),就会跳转到退出流程。

  • 错误路径:让程序自然执行,它会走向退出。
  • 正确路径:我们可以在OD中修改标志位(例如,将零标志ZF改为1),或者直接修改跳转指令,让程序“认为”比较通过。此时继续运行,你会发现那个灰色的控件被启用了,这证明了我们的分析是正确的。

根据算法 注册码 = 固定常数 / 机器码,我们可以计算出正确的注册码。在本例中,如果机器码是111111,那么正确的注册码就是888888(六个8)。输入这个正确的注册码,程序将不会跳转,直接走向成功的分支。


总结与要点

本节课中,我们一起学习了汇编语言中子程序调用与返回的核心机制。

  • 核心指令call用于调用子程序,ret用于从子程序返回。它们通过堆栈传递返回地址,实现“有去有回”的流程控制。
  • 分析技巧:在逆向分析时,遇到call指令不要盲目跳过。按F7跟进,理解其功能(如获取输入、转换数据、进行计算等),是理解程序逻辑的关键。
  • 养成习惯:在动态调试时,对重要的call和关键跳转做好注释,并记录分析结果,这能极大提升后续的分析效率。
  • 实际应用:真实的程序可能包含多层嵌套调用,逻辑更为复杂。掌握本节课的基础,是应对更复杂逆向分析的必经之路。

记住,子程序调用是构建复杂程序的基石,理解它,你就向逆向分析的世界又迈进了一大步。

天草流初级破解教程 - P7:破解实战1 - 白嫖无双 🛡️

在本节课中,我们将学习如何对一个VC++程序进行破解实战。我们将通过两种方法定位关键代码,分析注册验证逻辑,并最终找到注册码的存放位置。课程将巩固之前学习的子群调用、字符串查找和关键跳转分析等知识。


课程概述 📋

本节课是“天草流初级破解教程”的第七课。我们将对一个使用VC++ 6.0编写的程序进行破解分析。主要内容包括:使用插件识别算法、通过字符串查找定位关键代码、利用调用堆栈快速定位按钮事件,以及分析验证逻辑并找到注册码的存储位置。


准备工作与程序载入

上一节我们介绍了子群调用的概念。本节中,我们来看看如何将其应用于实际的VC++程序破解。

首先,我们需要使用OD(OllyDbg)载入目标程序。目标程序是一个VC++ 6.0编译的程序。

在载入前,可以使用一个插件来辅助识别程序可能使用的加密算法(例如Base64或MD5)。这个插件能帮助我们快速了解程序的验证机制。

// 这是一个示例,表示OD载入程序后的入口点
00401000 > $  55            PUSH EBP

载入程序后,我们首先尝试运行程序。


方法一:通过参考字符串定位

一种常见的破解思路是搜索程序中的字符串。以下是操作步骤:

  1. 运行程序(F9)。
  2. 在程序界面输入假用户名和假注册码(例如:3800cc / 123456789),并点击注册按钮。
  3. 程序会弹出错误提示,例如“用户名或注册码不匹配”。
  4. 在OD中暂停程序,右键选择“查找” -> “所有参考文本字串”。
  5. 在出现的字符串列表中,寻找与错误提示相关的字符串(例如“不匹配”或“错误”)。
  6. 双击找到的字符串,OD会跳转到引用该字符串的代码位置。

找到引用错误提示的代码后,我们就在其附近区域。通常,上方不远处会有一个条件跳转(JEJNE等),这个跳转决定了程序是走向成功还是失败提示。这个跳转就是“关键跳转”,而它上方的CALL指令通常是进行注册码验证的“关键CALL”。

我们在关键CALL的地址处按F2下断点,然后重新运行程序并点击注册。程序会在此处中断,此时我们可以按F7键步入(Step into)这个CALL,进入验证函数内部进行分析。


方法二:利用调用堆栈快速定位

在破解某些程序(特别是外挂或带有Nag窗口的程序)时,字符串可能被加密,此时方法一可能失效。我们可以利用OD的调用堆栈功能来快速定位。

以下是操作步骤:

  1. 让程序运行起来,并弹出注册窗口(先不要点击确定)。
  2. 在OD中按F12键暂停程序(Debug -> Pause)。
  3. 点击OD的“K”按钮(或View -> Call Stack),查看调用堆栈窗口。
  4. 在调用堆栈列表中,寻找最接近顶部的、属于目标程序模块的调用(通常不是系统DLL的调用)。
  5. 在该行上右键,选择“显示调用”或类似选项。
  6. OD会跳转到对应的代码位置,这里很可能就是按钮事件处理函数的开始或附近。

这种方法能让我们快速从系统领空返回到程序领空,直达事件处理的核心代码区,效率非常高。


分析验证逻辑与修改

无论通过哪种方法,我们最终都定位到了验证代码附近。以下是一个典型的分析过程:

我们找到了一个关键跳转,例如:

00401234    75 10           JNZ SHORT 00401246   ; 关键跳转,如果不等则跳向失败
00401236    68 00104000     PUSH 00401000        ; 成功提示字符串
...
00401246    68 0C104000     PUSH 0040100C        ; 失败提示字符串

分析发现,如果JNZ跳转实现,就会执行失败分支。我们的目标是让它不跳转。通常,这个跳转依赖于上方一个CALL的返回值(存储在EAX寄存器中)。

  1. 我们在关键CALL处下断点,并Step into进入。
  2. 单步执行(F8)观察代码逻辑,重点关注影响EAX值的指令。
  3. 我们发现,验证函数最终通过比较计算,使EAX变为0(验证失败)或1(验证成功)。
  4. 为了爆破,我们可以尝试在函数返回前(RETN指令处),直接将EAX的值修改为1
    • 在OD的寄存器窗口,右键点击EAX,选择“修改”。
    • 将值改为1
    • 继续运行程序,此时程序应该显示注册成功。

通过这种分析,我们不仅能够实现爆破,更能理解程序的验证流程。


寻找注册码存储位置

成功破解后,我们还可以进一步探索程序将注册信息存储在哪里。这有助于编写注册机或进行深度分析。

常见的存储位置包括:

  • 程序的配置文件(.ini)。
  • 系统注册表。
  • 程序自身目录下的特定文件。

我们可以使用以下方法查找:

  1. 在OD中,对RegOpenKeyExRegSetValueExWriteFile等API函数下断点,跟踪程序写入数据的位置。
  2. 或者,在成功注册后,直接使用系统搜索工具,在注册表或程序目录中搜索用户名或注册码明文。

在本例中,我们发现注册信息被存放在程序目录下的一个特定文件中。删除该文件,程序又会恢复到未注册状态,这方便了我们进行多次测试。


课程总结 🎯

本节课中我们一起学习了针对VC++程序的两种破解定位方法:字符串查找法和调用堆栈法。我们分析了验证函数的关键跳转与关键CALL,并通过修改寄存器值实现了爆破。最后,我们还探索了注册信息的常见存储位置。

通过本课实践,希望大家能够巩固逆向分析的基本流程:定位、分析、修改与验证。这些是后续学习更复杂破解技术的重要基础。请务必花时间动手练习,加深理解。

天草流初级破解教程 - P8:破解实战与弹窗去除 🛠️

在本节课中,我们将学习一个具体的软件破解实战案例。我们将首先分析并找到软件的注册码,然后学习如何去除软件在关闭时弹出的未注册提示窗口。整个过程将使用OllyDbg(OD)工具进行。


软件分析与破解

上一节我们介绍了基本的破解思路。本节中,我们来看看一个风格相似的软件。首先,在软件主页上找到目标软件。

使用PID查看软件信息,确认其使用了类似的加密算法。点击关闭按钮时,如果软件未注册,会弹出一个提示窗口。我们的目标是先破解注册验证,再去除这个弹窗。

使用OllyDbg载入软件,来源选择4.9版本并运行。

不使用常规方法,而是采用一种更直观的方式。在关键代码行下断点,然后按F9运行程序,点击确定后程序会中断。

程序中断后,返回到系统领空。此时,可以观察到注册码已经显示出来。软件有一个注册项,说明注册码可能保存在此处。

返回分析代码,发现其结构与之前分析的软件类似,可能出自同一开发者。代码中存在一个mbscmp比较函数,这是关键点。

在比较函数处下断点,程序中断后单步执行。可以看到真正的注册码被放入ECX寄存器,地址为0012FB30。假注册码地址为0012FB90

代码对两者进行比较。此时可以单步进入查看,但会进入系统领空。系统领空的代码无法修改,尝试修改会导致错误。

代码结构与之前非常相似,但将部分调用内容直接展示出来,没有放入CALL中调用,这使分析更直观。

观察跳转指令,如果跳转实现,则注册失败。取消跳转,使程序继续执行。注册成功的提示信息被存放在内存地址004163C8中,内容为“3Q”。

至此,软件破解完成。破解方法相同,不再赘述。


去除关闭弹窗

破解完成后,现在处理关闭软件时弹出的未注册提示窗口。

直接运行程序,点击关闭触发弹窗。弹出窗口后不要操作,在OllyDbg中按F12暂停程序。

在调用堆栈中,可以看到一系列模块调用。除了一个属于程序本身的领空,其他都是系统领空。找到属于程序领空的调用行。

该调用负责弹出提示窗口。可以从字符串“screen”推断其与屏幕画面相关。在此处找到一个关键跳转指令。

如果这个跳转执行,就不会出现弹窗。运行程序并点击关闭,OD会中断。按照程序原意执行,会出现弹窗;如果强制跳转,则程序直接退出。

这个跳转就是关键跳。可以将其修改为无条件跳转(jmp)。修改时,应尽量改动最少的字节,以保持程序稳定性。例如,将条件跳转改为两个字节的NOP指令,比改为五个字节的jmp指令更优。

保存修改后的程序。再次运行,关闭时不再出现注册提示窗口。


关键思路与注意事项

本节的关键在于讲解思路。俗话说“师父领进门,修行在个人”,完全从头讲解所有内容是不现实的,需要大家课后多练习。

尝试修改内存值使函数返回1,但发现修改的地址00417814位于数据段(.data段)。数据段的内容在程序运行时是动态变化的,程序会多次对其清零,因此直接修改此处无效。

可行的爆破方法只有修改关键跳转指令,或者阻止程序为EAX寄存器赋值0。

最终,我们得到了去除了弹窗的软件版本。可以验证注册码是否正确,并在软件内查找相关注册键值。


总结

本节课中我们一起学习了针对一个具体软件的完整破解流程:

  1. 分析软件并定位注册验证代码。
  2. 通过修改关键跳转实现软件注册。
  3. 分析并去除软件关闭时的未注册提示弹窗。
  4. 理解了修改系统领空代码的局限性以及动态数据地址的特性。

核心思路是定位关键判断点并修改其逻辑。掌握思路后,需要大量练习来巩固技能。

天草流初级逆向教程 - P9:第7-8课补充 - 绕过对话框分析 🧠

在本节课中,我们将学习如何通过逆向工程分析一个程序,定位并绕过其弹出的对话框。核心思路是找到调用对话框的代码,并向上回溯,修改关键跳转指令,使程序执行流程跳过该调用。


分析流程与下断点 🔍

首先直接运行目标程序,当对话框出现时,按下 F12 暂停程序。在调用堆栈中,可以看到一个名为 GetMessageBoxA 的系统函数调用。这表明程序正在调用系统API来显示对话框。

我们的目标是进入程序自身的代码逻辑(即“程序的领空”),而非系统代码。在调用堆栈中双击或右键选择“显示调用”,即可跳转到程序中调用 MessageBox 的具体位置。

; 示例:调用 MessageBox 的汇编代码
CALL <JMP.&user32.MessageBoxA>

找到此处后,我们就定位了对话框调用的起点。


回溯与跳转分析 ⬆️

上一节我们定位了对话框的调用点。本节中,我们来看看如何通过分析其上方代码来绕过它。

程序从入口点开始执行,经过一系列指令后,最终会执行到我们刚才找到的 CALL MessageBoxA 指令。我们的目的是让程序在执行过程中“跳过”这个调用。

因此,需要向上查看代码,寻找可以跳过此 CALL 指令的跳转(JMP 或条件跳转)。逆向思考:既然对话框出现了,说明所有本该跳过此处的跳转指令都“没有生效”。那么,使这些跳转生效,就能阻止对话框弹出。

以下是分析的关键步骤:

  1. 在调用对话框的指令处下断点:这样可以在程序调用对话框前中断,方便观察。
  2. 单步执行(F8):从断点处向上回溯,观察每个跳转指令的执行情况。
  3. 识别关键跳转:找到那些本应跳过对话框调用但实际未跳转的指令。

例如,我们可能发现类似下面的代码结构:

TEST EAX, EAX
JZ SHORT 跳过对话框    ; 如果EAX为0则跳转
CALL <MessageBoxA>     ; 显示对话框
跳过对话框:
...                    ; 后续代码

如果对话框出现了,说明 JZ 跳转条件未满足(即EAX不为0)。我们的目标就是修改逻辑,让这个跳转发生。


修改指令与验证 ✏️

通过回溯分析,我们找到了影响对话框显示的关键跳转指令。现在,我们来看看如何修改它。

一种常见方法是修改条件跳转,或者将其上方的某个赋值指令改为确保跳转发生。例如,如果跳转依赖于 EAX 寄存器的值,我们可以找到给 EAX 赋值的代码。

MOV EAX, DWORD PTR [EBP-4]  ; 假设这里给EAX赋值
TEST EAX, EAX
JZ SHORT 跳过对话框

我们可以将 MOV EAX, DWORD PTR [EBP-4] 修改为 MOV EAX, 0,以确保 JZ(为零则跳转)条件成立。

修改后,重新运行程序。单步执行观察,确认程序流程跳过了 CALL MessageBoxA 指令,并且对话框不再弹出。

注意:修改时需谨慎。有时跳过对话框调用可能会影响后续流程(例如,跳过必要的退出调用会导致程序无法关闭)。如果遇到问题,需要继续分析跳转后的代码,确保程序功能完整。


总结与要点 📝

本节课中,我们一起学习了逆向工程中分析并绕过对话框的基本方法。

  1. 定位:通过调试器在对话框出现时中断,找到程序调用 MessageBox 的代码位置。
  2. 回溯:从调用点向上分析代码逻辑,寻找控制流程跳转到此处的关键跳转指令。
  3. 分析与修改:判断哪些跳转未生效导致了对话框弹出,通过修改指令(如修改寄存器值或跳转条件)使跳转生效。
  4. 验证与调整:修改后运行程序,验证对话框是否被绕过,并检查程序其他功能是否正常。

核心目的是控制程序执行流程,使其跳过不必要的对话框调用。实现方法多样,但分析思路是相通的:即理解代码逻辑,找到关键点,并进行精准修改。

天草高级班 - P1:IP验证破解实战 🛡️

在本节课中,我们将学习针对一个带有IP验证功能的内部软件进行破解。课程将演示三种不同的破解思路,从跟踪按钮事件到修改程序逻辑,帮助你理解网络验证程序的基本分析方法。

概述与程序初探

本节课分析的软件是一个公司内部使用的同步更新程序,其启动后会出现一个登录验证窗口。窗口包含服务器选择(默认为灰色不可选)和密码输入框。

首先,我们使用反汇编工具(如OD)载入程序。目标是找到验证逻辑的入口点。

第一种破解方法:跟踪验证按钮

第一种思路是从“验证”按钮的事件处理代码开始跟踪,定位关键判断并修改其逻辑。

以下是分析步骤:

  1. 在反汇编工具中查找与“验证”按钮相关的字符串或事件。
  2. 在验证按钮的事件处理函数入口处设置断点。
  3. 运行程序,输入任意密码并点击“验证”,程序会在断点处暂停。
  4. 单步执行代码,观察程序流程。重点留意 CMP(比较)和 JZ/JNZ(条件跳转)等指令。
  5. 会发现一个关键比较:程序将某个寄存器(例如 EAX)的值与 1 进行比较。
    CMP EAX, 1
    JZ 验证成功跳转地址
    
  6. 此处的逻辑是:如果 EAX == 1 则跳向验证成功的流程,否则验证失败。我们的目标就是让程序执行这个跳转。
  7. 修改方法:可以将 CMP EAX, 1 的结果强行设置为相等。一种常见做法是将其上方的、决定 EAX 值的指令改为 MOV EAX, 1。或者,直接修改关键跳转指令 JZ 为无条件跳转 JMP
  8. 修改后保存,程序即可实现输入任意密码通过验证。

上一节我们介绍了通过跟踪按钮事件进行破解的方法,本节我们来看看另一种更巧妙的思路。

第二种破解方法:转换按钮功能

第二种思路利用了登录窗口的另一个按钮——“取消”。通过修改程序对“取消”按钮的响应逻辑,使其执行“验证通过”的流程。

以下是分析步骤:

  1. 程序启动时,会调用创建登录框的函数。在此函数调用之后设置断点。
  2. 运行程序,程序暂停后,观察栈和代码流程。会发现登录框显示后,程序根据用户点击的按钮(验证或取消)返回不同的值。
  3. 通常,点击“取消”按钮会触发一个退出程序的流程。找到这个关键跳转。
    TEST EAX, EAX
    JZ 退出流程地址
    
  4. 此处的逻辑是:如果返回值为0(可能代表点击取消),则跳转到退出流程。我们只需将这个跳转指令 JZ 改为 JNZ(非零则跳),或者直接 NOP(空操作)掉,即可使点击“取消”按钮后不退出,而是继续执行(相当于验证通过)。
  5. 修改后,点击“取消”按钮即可直接进入程序主界面。

前面两种方法都需要与界面交互,本节我们学习第三种更底层、更彻底的方法。

第三种破解方法:拦截登录框调用

第三种思路是在程序调用创建登录对话框的函数时进行拦截,直接跳过该调用,使登录框根本不出现。

以下是分析步骤:

  1. 在反汇编工具中,查找类似 Call CreateDialogCall DialogBox 的系统API调用。
  2. 在该调用指令处设置断点。
  3. 运行程序,程序会在此处暂停。此时,我们可以修改函数调用的返回结果,或者直接跳过整个调用。
  4. 一种有效的方法是:将调用登录框函数的 CALL 指令替换为 NOP 指令,或者修改其参数和返回地址,使其立即返回一个表示“验证成功”的值。
  5. 例如,找到调用后,不执行该 CALL,而是直接设置 EAX 寄存器为1(假设1代表成功),然后跳转到验证通过后的代码地址。
    ; 原指令
    CALL 创建登录框函数
    ; 修改为
    MOV EAX, 1 ; 模拟验证成功
    JMP 验证成功后的地址
    
  6. 这样,程序启动后将不再显示登录窗口,直接进入主界面。

总结与思路提炼

本节课中,我们一起学习了针对一个IP验证程序的三种破解方法:

  1. 跟踪按钮法:从用户界面交互点入手,逆向跟踪验证逻辑,修改关键判断条件。
  2. 功能转换法:利用程序已有的其他交互路径(如取消按钮),修改其内部逻辑,使其达到验证通过的目的。
  3. 调用拦截法:在更底层拦截创建验证界面的系统调用,从根本上阻止验证窗口的出现。

这三种方法体现了软件破解中由表及里、从界面到核心的不同层次思路。它们不仅适用于本例的IP验证,也是分析各类网络验证、外挂保护程序的通用技巧。核心在于灵活运用反汇编工具,理解程序流程与控制流,并针对关键点进行精准修改。请注意,本教程仅用于安全技术研究学习,请勿用于非法用途。

天草高级班 - P10:ASProtect 2.1X之VB程序脱壳与OEP修复 🛡️➡️🔓

在本节课中,我们将学习如何对使用ASProtect 2.1X加壳的VB程序进行脱壳,并修复其原始入口点。VB程序的脱壳过程相对简单,是学习此类技术的一个良好起点。

概述与准备

首先,我们需要准备好目标程序与工具。本次教程将使用一个已经预先分析过的VB程序作为示例。

建议使用英文界面的调试器,以符合普遍的操作习惯。

第一步:设置异常并定位关键代码

上一节我们介绍了准备工作,本节中我们来看看如何设置调试器以捕捉壳的执行流程。

在调试器的异常设置中,忽略除“内存访问异常”之外的所有其他异常选项。然后运行程序,程序会中断在异常处。

此时,我们需要在代码中寻找关键点。通过搜索十六进制值“85”,并在其附近设置断点,可以定位到壳对导入表进行处理的相关代码区域。

在该区域下好断点后,使用快捷键运行程序到此断点。

第二步:探查导入表加密情况

在找到处理导入表的关键位置后,我们需要探查导入表是否被加密。

使用调试器的跳转功能,来到程序的原始入口点地址附近(例如 00401000)。观察该区域的代码,如果导入表函数名称清晰可见,则说明导入表可能未被加密或已被部分解密。

通过单步执行并观察寄存器(如ESI)值的变化,可以判断在特定时刻导入表是否已完全呈现。对于VB程序,其加壳强度通常会对导入表处理有所简化。

第三步:定位最后一次异常并找到OEP

确认导入表状态后,我们继续执行程序,寻找壳的“最后一次异常”。这是脱壳过程中的一个关键里程碑。

将异常设置改回“内存访问异常”,然后运行程序。程序会多次中断,直到出现一个固定的地址值(例如 FAA5),这通常就是最后一次异常的位置。

在该地址处按F7键步入,即可进入壳的解码循环内部。继续执行,不久后就会到达程序的原始入口点。VB程序的OEP通常具有固定的特征,例如包含 PUSHCALL 指令的特定模式。

第四步:修复被破坏的导入表

到达OEP后,我们常常发现导入表的地址被替换成了无意义的代码或跳转指令。这是因为壳使用了代码变形技术。

我们需要编写一段脚本或手动操作,将这些被篡改的导入表地址恢复为正确的函数地址。这涉及到:

  1. 申请一块内存用于存放修复代码。
  2. 计算并替换脚本中的关键地址值,包括IAT的起始地址、结束地址以及申请的内存地址。
  3. 特别注意修复代码中的几种关键指令类型(如 JMP 类型的 FF25)。

以下是修复过程中可能用到的代码结构示例(地址为示意):

MOV EAX, [IAT_Start]
CMP EAX, [IAT_End]
JGE Finish
MOV DWORD [EAX], Correct_Function_Address
ADD EAX, 4
JMP Repair_Loop
Finish:
RET

执行修复脚本后,必须设置新的EIP到修复代码的起始处并运行,直到导入表修复完成。

第五步:使用工具修复最终文件

修复完内存中的导入表后,就可以将程序从内存中转储到文件了。

使用调试器的进程转储功能,将当前进程保存为一个新的可执行文件。然后,使用专门的导入表修复工具(如ImpREC)加载这个转储后的文件。

在修复工具中,填入我们之前找到的正确的OEP地址,并让工具自动获取修复后的导入表信息。最后,修复工具会生成一个可以独立运行的、已脱壳的程序。

运行脱壳后的程序,验证其功能是否正常。与原版加壳程序相比,脱壳版通常不再显示壳的启动画面。

总结

本节课中我们一起学习了针对ASProtect 2.1X加壳的VB程序的完整脱壳流程。关键步骤包括:通过异常定位壳代码、探查导入表状态、找到最后一次异常并抵达OEP、修复被变形处理的导入表,最后使用工具完成文件修复。掌握这个流程为分析其他语言编写的、或更强版本的加壳程序奠定了基础。

天草高级班 - P11:传世商王正式版本破宝 - 白嫖无双 🛡️💎

在本节课中,我们将学习如何破解“传世商王”游戏的最新正式版本。我们将从分析原程序的保护机制开始,逐步讲解脱壳的思路与具体操作方法,最终实现无需登录即可进入游戏的目标。


课程概述 📋

原版程序存在一个登录验证界面,用户需要输入账号密码才能进入。我们的目标是绕过这个验证,实现直接登录。程序本身带有壳保护,这增加了分析的难度。本节课将重点讲解如何识别并脱去这个复杂的壳。

上图展示了破解后的程序界面,可以直接登录。而原程序则存在一个登录框。


脱壳思路分析 🧠

程序原先带有一个变形的壳,版本号约为3.4到3.5。这个壳的特点在于它会进行循环解码,这意味着代码在运行时会被多次解密,给静态分析带来了很大困难。

在动态调试时,如果使用常规的ESP定律等方法,会发现程序会多次执行类似 FFA0F95 这样的指令,地址也在不断变化。这正是循环解码的表现。


动态调试与脱壳实战 ⚙️

首先,我们需要配置好调试器(这里以OD为例)。由于壳的复杂性,调试过程中反应可能会比较慢。

在调试时,如果发现程序在循环解码,单纯使用F9(运行)可能无法直接到达程序的原始入口点(OEP)。这时,我们需要结合F8(单步跳过)和F7(单步进入)来跟踪程序的执行流程。

虽然每次循环的地址在变,但程序总会经过某些特定的内存地址或API调用。记住这些关键点对脱壳有帮助。


方法一:使用插件辅助脱壳

对于这种循环多次的复杂壳,手动跟踪非常耗时。一个更高效的方法是使用专门的脱壳插件。

以下是使用PID插件进行脱壳的关键步骤:

  1. 在调试器中加载目标程序。
  2. 在插件菜单中选择PID脱壳工具。
  3. 在关键的内存地址上下断点。这个地址通常是壳完成解码、准备跳转到原始程序代码的位置。
  4. 运行程序,当断点触发时,使用插件的“脱壳”功能。

这个壳的循环解码次数很多,如果对壳的原理不了解,很难判断解码何时结束。但使用插件可以自动化这个过程。


断点设置与跟踪

在实际操作中,找到正确的断点位置可能需要多次尝试。我们需要在一个稳定的、解码完成后必然经过的地址下断点。

有时可能会因为返回(操作)过头而错过正确时机,这时需要重新开始。关键在于观察:当程序运行到断点处时,相关内存区域的数据是否已经解码完成(例如,是否变成了可读的汇编代码)。

通过反复运行(F9)和返回查看,我们可以验证断点是否有效。这个过程可能需要十几次甚至更多次的循环。


课程总结 🎯

本节课我们一起学习了针对“传世商王”游戏复杂壳的破解方法。

我们首先分析了该壳循环解码的特性,然后探讨了手动跟踪与使用PID插件辅助脱壳两种思路。核心在于通过动态调试,在壳完成解码工作的关键时刻下断点,并抓取完整的程序代码。

通过本课的学习,你应该掌握了对类似多层循环壳的基本分析方法和脱壳流程。记住,耐心观察和利用工具是破解过程中的关键。

天草高级班 - P12:手脱ASPr 2.X-1 🛡️➡️📦

在本节课中,我们将学习手动脱去ASProtect 2.X壳的完整流程。这个过程涉及使用脚本辅助、手动修复导入表、大量补区段以及处理程序校验。虽然步骤繁琐,但我们将一步步拆解,力求清晰明了。


概述与准备工作

首先,我们需要明确目标:手动脱掉一个被ASProtect 2.X加壳的程序。为了减轻部分工作量,我们会借助一个脚本来处理初始的导入表(IAT)和代码修复。

准备工作如下:

  • 使用OD(OllyDbg)载入目标程序。
  • 准备好辅助脱壳的脚本插件,并将其放入OD的插件目录。
  • 在OD中设置忽略所有异常,以便顺利运行。

第一步:使用脚本辅助处理

上一节我们介绍了准备工作,本节中我们来看看如何使用脚本进行初步脱壳。

运行脚本工具后,程序会进行初步的修复。此时不要关闭进程(PID),因为后续步骤还需要用到它。

以下是关键操作点:

  • 在OD中运行脚本插件。
  • 处理完成后,在日志中查看结果。
  • 保持程序进程处于运行状态。

第二步:定位并修复Stolen Code

初步处理后,我们面临更复杂的步骤:定位并修复被壳偷走的代码(Stolen Code)。

我们需要找到StolenCode的起始地址。方法是忽略除内存访问和指定异常外的所有异常,然后反复按Shift+F9,直到程序跑飞前的最后一次异常。记录下此时的地址,这就是StolenCode的OEP(原始入口点)。

找到地址后,开始修复:

  1. 在脱壳工具中填入1000(大小)。
  2. 点击“获取IAT”。
  3. 计算IAT,并将OEP替换为我们找到的StolenCode的OEP。

第三步:手动补区段 🧩

修复入口点后,最繁琐的部分来了:手动补充大量被壳混淆或移除的区段。

我们需要打开两个OD实例:一个用于脱壳的主程序,另一个用于提供区段数据。从特定的内存地址(例如0x0147开始)逐个补入区段。

以下是补区段的核心步骤:

  1. 在提供区段的OD中,右键选择“区域脱壳”,从起始地址(如0x0147)开始。
  2. 在脱壳OD中,点击“区段”,右键选择“磁盘载入”。
  3. 关键:编辑载入的区段,将其起始地址(RVA)减去基址(例如0x4000)得到正确的值。
  4. 将补好的区段保存到新文件,避免混乱。
  5. 重复此过程,一直补到超过我们找到的StolenCode的OEP地址(例如0x0161)之后。通常需要补充十几个甚至更多区段。

注意:补区段是个重复性很高的体力活,必须仔细核对每一个地址,一旦出错可能导致前功尽弃。


第四步:处理程序校验与修复

补完区段后,程序可能仍有校验保护,导致无法运行或弹出错误。

我们需要对修复后的程序进行验证。使用PE工具检查是否有无效的区段。如果错误出现在我们补充的区段范围内,通常问题不大。

有时,程序会因为试用期过期或OEP不正确而无法运行。此时,需要重新核对或寻找正确的OEP。可以通过在内存镜像中查看代码段来确认。

找到正确的OEP后,在脱壳工具中替换它。程序运行后,可能会在特定代码处退出,这是壳的退出校验。特征通常是将连续的三行代码改为跳转指令(JMP)。找到这个位置,修改代码使其跳过退出流程。


总结与回顾

本节课中,我们一起学习了手动脱ASProtect 2.X壳的四个核心步骤:

  1. 脚本辅助:利用脚本处理初步的IAT和代码修复,减轻负担。
  2. 定位修复:找到被偷走的代码(Stolen Code)的起始地址,并修复OEP。
  3. 补全区段:手动、大量地补充被壳处理过的区段,这是最耗时的一步。
  4. 处理校验:修复程序自身的校验机制,确保脱壳后的程序能正常运行。

整个过程非常繁琐,尤其是手动补区段,需要极大的耐心和细心。目前对于此壳的脱壳还没有完美的自动化方法,这种手动结合脚本的方式是较为可行的方案。希望本教程能帮助你理解其中的原理和操作流程。

天草高级班 - P13:ACProtect不脱壳补丁实战 🛡️

在本节课中,我们将学习如何对一款名为“台湾热血英雄”的、受ACProtect强壳保护的外挂程序进行不脱壳破解。核心目标是绕过其账号激活验证,使未激活账号也能登录使用。我们将通过定位关键跳转并打补丁的方式完成。

环境准备与问题分析

要使目标程序运行,首先需要将六个必需的DLL文件复制到程序目录下。

该外挂原本设计为必须使用已激活的账号才能登录。我们的破解目标是将它修改为未激活账号也能登录。程序在登录时需要使用代理服务器,但课程演示中提供的代理已经失效。不过,即使代理失效、界面提示“无效账号”,只要完成正确的破解,程序依然可以成功登录。

破解思路与定位

目标程序使用了ACProtect强壳。完美脱壳并修复程序较为繁琐,且可能产生跨平台兼容性问题(例如在自己电脑上可用,在其他电脑上不可用)。因此,我们选择不脱壳,直接对加壳后的程序打补丁。

上一节我们分析了程序的基本情况,本节中我们来看看如何定位关键代码。

由于程序加壳,直接搜索中文字符串“无效账号”是无效的。需要使用特定的插件(如“字幕圈插件”)来查找中文字符串引用,或者搜索其英文提示。

通过分析,我们发现该外挂虽然有两层验证,但只需修改一个关键跳转即可绕过。这个跳转位于“登录失败”的逻辑判断处。

以下是定位和修改的关键步骤:

  1. 使用调试器附加已加壳的程序。
  2. 借助插件或搜索英文文本,定位到与登录验证相关的代码区域。
  3. 找到判断登录失败并跳转的指令。在本次案例中,其地址是 40503
  4. 该处的跳转指令原本在验证失败时执行,我们需要将其修改为无条件跳转(JMP),直接跳过失败处理流程。

核心修改是将原验证跳转改为强制跳转,代码形式如下:

原指令:JNZ 某地址  ; 如果非零(验证失败)则跳转到失败处理
修改为:JMP 某地址  ; 无条件跳转到成功流程

在地址 40503 处,实际是将其改为 JMP 到登录成功的代码段。

补丁制作与验证

找到地址 40503 处的“登录失败”跳转后,将其修改为 JMP 指令即可完成核心破解。

修改后,程序运行时可能仍会显示“无效账号”的提示(因为代理失效或提示逻辑未修改),但登录功能已经可以正常使用。

此外,程序还有一个“派系”或功能限制,原设计只能在游戏内使用。我们可以通过类似的方法定位并修改另一处限制判断(例如在地址 489D 附近),将其改为始终允许,从而解除该限制。

以下是完整的操作回顾列表:

  • 定位关键点:在加壳程序中,通过插件辅助查找字符串,定位到登录验证函数。
  • 修改跳转:将验证失败跳转(如 JNZ)修改为无条件跳转(JMP)。
  • 解除限制:搜索并修改其他功能限制判断,扩大程序使用范围。
  • 测试验证:寻找有效的台湾地区SOCKS5代理进行功能测试(注意:HTTP代理可能无效)。

总结与提醒

本节课中,我们一起学习了针对ACProtect强壳保护程序的不脱壳补丁技术。我们分析了外挂的验证逻辑,定位到核心的登录判断跳转(地址 40503),并通过将其修改为 JMP 指令,成功绕过了账号激活验证。同时,也简要介绍了如何解除程序的其他功能限制。

需要强调的是,此类破解方法仅供安全技术研究学习使用。请勿将技术用于非法用途或广泛传播,以免对软件开发者造成损害,并可能带来法律风险。

关注“星空识别者”的单元!

天草高级班 - P14:为ASProtect 2.X制作内存补丁 🛠️

在本节课中,我们将学习如何为使用ASProtect 2.X壳保护的程序制作一个内存补丁。这种方法的核心思路是:通过调试器定位关键校验代码,在程序运行时动态修改内存中的特定数据(如硬件指纹和注册码),从而实现“破解”或“白嫖”的目的。我们将一步步分析并构建一个可用的补丁程序。


概述与思路

首先,我们需要理解核心思路。目标程序在运行时,会从特定内存地址读取硬件指纹信息并与注册信息进行比对。我们的补丁目标是在程序运行时,将内存中的硬件指纹替换为我们指定的注册名,并将一段关键的校验代码替换为我们的“派系代码”(即绕过校验的指令),从而让程序认为注册是合法的。

以下是实现此目标的主要步骤:

  1. 定位程序中存放硬件指纹的内存地址。
  2. 定位程序中进行关键校验(派系)的代码位置。
  3. 在程序内存中找到可用的空白区域,用于存放我们的补丁代码(注册名和派系代码)。
  4. 修改原程序指令,使其跳转到我们的补丁代码执行,执行完毕后再跳转回原程序。
  5. 处理壳的CRC校验,确保补丁能被正确加载。
  6. 将上述所有信息整合,编写并编译一个Loader(加载器/补丁程序)。

第一步:定位硬件指纹地址

上一节我们介绍了整体思路,本节中我们来看看如何定位关键的硬件指纹地址。

  1. 使用调试器(如OD)附加目标程序。
  2. 在调试器中设置异常选项:忽略除“内存访问”之外的所有异常。
  3. Shift+F9 运行程序,直到硬件指纹信息第二次出现在调试器中。记录下此时 EBP 寄存器指向的地址,例如 F0137884。这个地址存放的就是程序的硬件指纹。
  4. 分析代码发现,程序会从 [EBP+8] 的位置读取数据作为用户名进行校验。因此,我们的目标就是将 [EBP+8] 地址处的内容替换为我们想要的注册名。

核心概念:关键内存地址的定位。
目标地址 = EBP + 8


第二步:定位派系代码位置

找到了硬件指纹,接下来我们需要定位程序中进行核心校验的代码段,我们称之为“派系代码”的位置。

  1. 继续按 Shift+F9 运行程序,这是硬件指纹第三次出现。
  2. 观察堆栈指针 ESP 的值,例如 0012FF20
  3. 在内存窗口中查找地址 0012FF1C(即 ESP-4)附近的内容。找到其下方的一个 CALL 指令,这个 CALL 指令所在的区域就是需要进行“派系”校验的关键代码位置。

第三步:寻找可用的代码存放空间

我们已经知道了要修改哪里,以及要放入什么代码。现在需要找到程序内存中未被使用的“空白”区域来存放我们的补丁代码。

  1. 在调试器中,让调试器捕获所有异常。
  2. 转到程序的最后一个区段(Section)。
  3. 选中该区段的所有代码,右键选择用 FF 填充。
  4. Shift+F9 继续运行程序。你会发现,部分被填充的 FF 被程序运行时清零了。
  5. 那些被清零的地址是程序运行时会用到的。而未被清零的、仍为 FF 的地址空间,就是我们可以安全写入补丁代码的地方
  6. 选择两个连续的空白地址,一个用于存放我们的注册名,另一个用于存放派系代码。

第四步:编写并植入补丁代码

现在,我们将把理论付诸实践,在找到的空白地址处写入我们的补丁指令。

  1. 在第一个选定的空白地址处,写入我们的注册名。
  2. 在第二个选定的空白地址处,开始写入派系代码。这通常是一段汇编指令,例如:
    MOV DWORD PTR [EBP+8], 我们的注册名地址
    
    这条指令的作用是将我们指定的注册名地址移动到 [EBP+8] 位置,覆盖原来的硬件指纹。
  3. 修改原“派系代码”位置的指令。通常,我们将原指令的第一个字节改为 JMP 指令(操作码 E9),跳转到我们刚才写入派系代码的地址。
  4. 在我们的派系代码末尾,需要补上被我们覆盖的原指令,然后再用一条 JMP 指令跳回原程序流的下一条指令,保证程序能继续正常运行。

核心概念:代码跳转与还原。

原程序: [原校验代码]
修改为: JMP 到我们的补丁地址

我们的补丁:
    MOV DWORD PTR [EBP+8], 注册名地址  ; 替换指纹
    ... (其他必要操作) ...
    [被覆盖的原指令]                   ; 还原被修改的原指令
    JMP 回原程序下一条指令地址          ; 跳转回去

第五步:处理CRC校验

ASProtect等加壳程序通常带有CRC(循环冗余校验)或内存校验机制,会检测代码段是否被修改。如果直接加载修改后的程序,可能会报错。

  1. 我们需要找到一个合适的API函数调用时机,在壳完成自解密、但CRC校验尚未开始的时刻注入我们的补丁。
  2. 通过调试,在程序解密自身代码的过程中,在 00401000 等代码区段设置内存访问断点。
  3. 找到像 CreateFile 这样的API函数被调用的时刻,这个时刻往往标志着主要解密完成。记录下这个API函数的地址。
  4. 在我们的Loader中,将补丁注入的时机设定在这个API被调用之后。这样,补丁就能在CRC校验开始前成功植入。


第六步:构建Loader程序

所有关键信息都已找到,现在需要将它们整合到一个独立的Loader程序中。这个Loader将负责启动目标程序并注入我们的内存补丁。

以下是Loader代码中需要配置的关键部分:

  1. 用户名字符串:即我们想要显示的注册名。
  2. 用户名写入地址:即第一步找到的 [EBP+8] 对应的实际地址。
  3. 派系代码跳转地址:即原程序中被我们修改为 JMP 指令的地址。
  4. 派系代码内容:即我们在第四步中编写的那一串汇编指令对应的机器码。
  5. 注入时机API地址:即第五步找到的 CreateFile 等API的地址,用于确定补丁注入时机。
  6. 长度信息:需要准确指定注册名字符串的长度和派系代码机器码的长度,以便Loader进行精确的内存写入。

核心概念:Loader的配置数据结构(示例)。

// 伪代码结构
struct PatchConfig {
    char username[24];        // 注册名
    DWORD username_addr;      // 用户名写入地址
    DWORD jump_patch_addr;    // 需要打JMP补丁的地址
    BYTE patch_code[50];      // 派系代码机器码
    DWORD api_hook_addr;      // 注入时机API地址
    int username_len;         // 注册名长度
    int patch_code_len;       // 派系代码长度
};

使用汇编编译器(如RadASM)创建一个新工程,将编写好的Loader源码粘贴进去,添加图标资源,然后进行编译和连接,最终生成一个可执行的 .exe 文件。运行这个Loader,它便会自动启动目标程序并完成内存补丁的注入。


总结

本节课中,我们一起学习了为ASProtect 2.X加壳程序制作内存补丁的完整流程。

我们首先通过调试定位了硬件指纹关键校验代码的内存地址。接着,找到了内存中可用的空白区域来存放自定义代码。然后,我们设计了补丁逻辑:重定向程序执行流到我们的代码,替换校验数据,并还原现场以保证程序稳定运行。我们还探讨了如何绕过壳的CRC校验,选择合适的注入时机。最后,我们将所有偏移地址、机器码和配置信息整合,构建了一个能够自动完成上述所有步骤的Loader程序

这个过程融合了逆向分析、调试技巧和简单的编程,是理解软件保护与破解原理的经典实践。请务必在合法授权的范围内进行技术学习和研究。

天草高级班 - P15:WinMPG VideoConvert 破解教程 🔓

在本节课中,我们将学习如何破解 WinMPG VideoConvert 软件。我们将从尝试不同的破解思路开始,逐步分析程序逻辑,最终找到关键跳转并修改代码,实现软件的完全破解。整个过程涉及动态调试、回溯分析、内存修改和补丁制作。


概述与初步尝试

运行程序后,会看到一个注册窗口。输入错误的注册码会提示错误。注册信息保存在特定位置。

首先尝试直接载入程序进行分析,但发现程序结构特殊,跳转指令(JMP)非常多,直接跟踪容易迷失方向,这是第一种方案,但效果不佳。


第二种思路:利用错误提示回溯

上一节我们尝试直接跟踪但失败了。本节中我们来看看利用程序的错误提示信息进行回溯分析的方法。

程序有明确的错误提示,这为我们提供了切入点。使用暂停执行的方法(如F12暂停),在错误提示出现的代码行设置断点。

以下是回溯分析的核心步骤:

  1. 在错误提示出现的代码行设置断点。
  2. 程序中断后,向上回溯调用栈,寻找导致错误的分支跳转来源。
  3. 由于跳转极多,需要耐心地、逐层向上跟踪,并在关键跳转处设置断点作为路标,防止跟错。
  4. 回溯的目标是找到一个非无条件跳转(如 JZ, JNZ 等条件跳转指令),它决定了程序是走向成功还是错误分支。

这个过程相当有难度,因为程序几乎全部使用 JMP 指令来模拟函数调用,导致调用链非常扁平且混乱。


发现关键跳转与初步修改

通过艰难的回溯,我们最终找到了一个关键的条件跳转指令 JE。该跳转依赖于 EAX 寄存器的值。

  • 关键逻辑EAX 为 0 时跳转,导致注册失败;EAX 不为 0 时则不跳,可能走向成功。
  • 修改尝试:在调试器中修改该 JE 指令为 NOP(空操作)或直接修改 EAX 寄存器的值为 1。

进行此修改后,程序提示注册成功,界面上的“华军购买”按钮变成了“访问主页”。然而,关闭程序重新启动后,依然弹出注册窗口,这说明存在重启验证。


第三种思路:从界面变化入手

上一节我们虽然找到了一个关键点,但未能解决重启验证。本节我们来看看一个更有效的突破口:程序界面在注册成功后的变化。

我们注意到,注册成功后,程序界面上的“华军购买”字样会变为“访问主页”。这是一个明确的标志,我们可以通过搜索内存中这些字符串的引用地址来定位关键代码。

以下是具体操作步骤:

  1. 在内存中搜索字符串“访问主页”。
  2. 在找到的引用地址处设置断点。
  3. 运行程序并尝试注册,观察断点何时被触发。
  4. 分析断点附近的代码,特别是哪些指令或函数调用决定了这个字符串的显示。

通过跟踪,我们发现了一个关键的函数调用(CALL)。这个函数负责处理注册状态并更新界面。进一步跟踪这个函数,我们发现了决定注册状态的核心机制。


深入核心:分析验证算法与内存修改

跟踪到关键函数内部后,我们使用内存转存功能监视相关数据的变化。

我们发现一个重要现象:程序会将我们输入的注册名的前四个字符的内存区域清零。例如,注册名 “WYNEYR”,在内存中其前四个字符 “WYNE” 对应的字节被置为 00

  • 关键内存地址:例如 0012C8B8,此处存放了处理后的注册名信息。
  • 关键指令:存在类似 MOV BYTE PTR [EAX], 00 的指令,将内存字节清零。
  • 破解关键:我们需要阻止这个清零操作,或者将清零后的值改为有效值(如 01)。

我们在调试器中手动将清零后的内存字节从 00 改为 01,程序界面立即显示为已注册状态,并且重启后验证也通过了。这证实了我们的分析是正确的。


编写补丁代码

由于直接修改内存只是临时生效,我们需要修改程序文件本身。目标地址的代码空间有限,我们需要写入自定义的汇编代码。

我们需要实现的功能是:在程序试图将关键内存位置清零时,我们将其设置为 01

补丁代码逻辑如下:

; 原指令可能是:MOV BYTE PTR [EAX], 00
; 我们将其修改为:
MOV BYTE PTR [EAX], 01 ; 将00改为01
; 然后继续执行原程序后续的指令(可能需要一个JMP跳回原流程)

由于空间不足,我们可能需要将原指令替换为一个 JMP 指令,跳转到程序文件其他空白区域执行我们编写的上述代码,然后再跳转回来。

使用十六进制编辑器或专用补丁工具,将找到的 00 字节修改为 01,并妥善处理代码跳转。修改后保存文件,即可获得破解版。


制作补丁程序

为了方便分发,我们可以制作一个补丁程序(Patch)。补丁程序会自动比较原始文件和已破解文件的差异,并将差异部分应用到用户的本机程序上。

可以使用如 “Patch Maker” 之类的工具。流程如下:

  1. 选择原始文件(Original)和已破解的文件(Patched)。
  2. 工具会自动比较差异。
  3. 设置补丁程序的界面信息(如皮肤、图标)。
  4. 生成一个独立的 EXE 补丁程序。用户运行此补丁,选择其本机的原始程序,即可自动完成破解。


总结与思路回顾

本节课中我们一起学习了破解 WinMPG VideoConvert 软件的完整过程。

我们尝试并总结了三种思路:

  1. 直接跟踪法:因程序跳转过多而失败,但这是最初的尝试。
  2. 错误提示回溯法:通过错误信息定位,逆向回溯找到关键跳转。此方法可行但过程繁琐,找到了一个关键点,但未解决重启验证。
  3. 界面特征分析法:通过观察注册成功后的界面变化(“华军购买”变“访问主页”),搜索字符串引用,定位到核心验证函数。通过内存转存跟踪,发现了程序将注册名前四字符清零的验证逻辑,并通过修改内存和最终编写补丁代码实现了完美破解。

核心技巧包括:利用程序反馈信息、字符串搜索、内存断点与转存分析、以及在小空间内编写并注入自定义汇编代码。

破解的关键在于思路的灵活转换对程序行为的细致观察。从失败的方法中吸取教训,找到更有效的突破口,是逆向工程中的重要能力。请务必按照这三种思路多练习几次,以深刻理解其中的每一步。

天草高级班 - P16:不脱壳破解ACProtect 🛡️➡️🔓

在本节课中,我们将学习如何在不脱壳的情况下,破解一个受ACProtect保护的软件。我们将通过动态调试技术,定位并绕过其保护机制,最终实现破解。

初始分析与环境准备

首先运行目标程序。由于未安装,程序启动后会弹出一个提示框。我们需要去除这个法人信息框。

接下来,我们使用OllyDbg(OD)加载程序进行初步分析。清除所有现有断点后,使用“运行到用户代码”功能。很明显,这里并非原始入口点(OEP),OEP已被保护壳偷取。

使用进程查看器确认目标进程,并用查壳工具FI确认其ACProtect版本为1.32,这是一个相对较高的版本。

在调试过程中,程序可能无法正常运行或导致调试器被检测。此时需要更换OD版本或调试系统环境以绕过反调试。

设置断点与初步追踪

在OD中忽略所有异常,并在代码段(.text)设置内存写入断点,在数据段设置内存访问断点。随后删除这些断点,采用“暂停法”进行追踪。

当程序中断后,我们寻找函数头部。记住一个常识:在代码段下断应使用“硬件执行断点”。程序中断在获取用户名的代码处,我们在此处做标记并下断。

调试软件的技术手段同样适用于分析外挂等程序。我们可以利用堆栈回溯查看调用链。

关键跳转分析与修改

在关键判断点,程序有一个条件跳转。我们尝试将其NOP掉(即改为空操作),然后继续运行。程序提示“连接服务器”后直接退出,说明此处并非真正的验证关键点。

重新开始调试,单步跟踪程序为何退出。在跟踪过程中,程序运行变得缓慢,这可能是触发了某些延时或反调试陷阱。

以下是需要关注的核心代码逻辑,通常验证会包含一个条件判断:

CMP EAX, EBX    ; 比较两个值
JNZ LABEL_EXIT  ; 如果不相等,则跳转到退出流程

我们需要找到正确的比较和跳转指令,并进行修改。

定位与绕过保护

通过反复跟踪和尝试,在程序多次循环或解码后的某个稳定位置,我们找到了真正的验证代码段。此处的跳转决定了程序是否进入付费功能或弹出注册框。

修改此关键跳转(例如,将JNZ改为JZ,或直接NOP掉),使程序流程走向已验证的分支。

最终,程序成功运行,不再弹出注册提示框,实现了不脱壳状态下的破解。

总结

本节课我们一起学习了针对ACProtect加壳软件的“不脱壳破解”方法。核心步骤包括:准备调试环境、动态追踪程序流程、定位关键验证代码、以及修改关键跳转指令。这种方法的核心思想是在内存中直接修改运行时的程序逻辑,绕过保护壳的验证,而无需完全脱去外壳。掌握此技术需要对程序运行流程有清晰的跟踪和分析能力。

天草高级班 - P17:使用脚本脱壳ASProtect 2.X 🛡️➡️📜

在本节课中,我们将学习如何使用脚本对使用ASProtect 2.X加壳的程序进行脱壳。我们将介绍两个不同的脚本,并演示其操作流程。

概述

ASProtect是一种常见的软件保护壳。手动脱壳过程繁琐,而使用脚本可以自动化关键步骤,提高效率。本节课将演示两个脚本的使用,一个由国内开发者编写,另一个是与国外开发者合作修改的。

脚本准备与程序载入

首先,我们有两个脚本。一个脚本是与国外开发者合作修改的,但存在一些问题。另一个脚本由国内开发者编写,非常完美,于今年3月份修改完成。

演示使用的程序是一个未安装的游戏,运行可能会报错,但这不影响我们的演示目的。

程序载入后,可能会检测到调试器。我们忽略相关提示,通过两次F9让程序运行起来。

定位关键信息

程序运行后,我们需要定位几个关键地址,这是运行脱壳脚本的前提。

以下是需要查找的关键信息:

  1. OEP(原始入口点):在本例中,OEP的地址是0x0041****
  2. IAT(导入地址表)起始地址:通过右键搜索命令序列,我们找到IAT起始于0x0050F0C0
  3. 特定指令序列:我们需要找到FF25(远跳转)指令的位置,这通常与获取API地址有关。

使用第一个脚本(国内版)

在记录下OEP、IAT起始地址和FF25指令地址后,我们开始运行第一个脚本。

该脚本的使用步骤如下:

  1. 运行脚本,在弹出的对话框中输入之前记录的 IAT起始地址
  2. 点击确定后,脚本会要求输入 FF25指令的地址
  3. 最后,脚本会自动处理并完成脱壳。

运行后,程序可能会退出,这可能与程序本身的兼容性有关。我们转而尝试第二个脚本。

使用第二个脚本(修改版)

第二个脚本适应性更强。对于没有Stolen Code的程序,这个脚本可以完美运行。

操作流程如下:

  1. 载入脚本并运行。
  2. 脚本会自动搜索并修复IAT。
  3. 修复过程会显示在日志中,可以看到它逐个修复导入函数。
  4. 修复完成后,手动将OEP设置为之前记录的地址(例如0x0041****)。
  5. 最后,将脱壳后的程序转储并保存。

这个脚本的修复过程虽然稍慢,但非常彻底。

脚本对比与总结

本节课中我们一起学习了两个脱壳脚本的应用。

两个脚本的主要区别在于修复时机:

  • 第一个脚本:倾向于在到达OEP之后进行修复。
  • 第二个脚本:在到达OEP之前就开始修复,通常更完善。

第二个脚本经过了更多测试和修正,因此更推荐使用。对于ASProtect 2.X的壳,当程序没有Stolen Code时,使用第二个脚本可以高效、完整地完成脱壳工作。

通过脚本自动化,我们避免了繁琐的手工查找和修复IAT的过程,大大提升了脱壳效率。

天草高级班 - P18:新补丁工具Loader-ASPr 2.X 🛠️

在本节课中,我们将学习如何使用一款名为Loader-ASPr 2.X的新补丁工具。这款工具主要用于对抗“Split Attack 2.0”等保护机制,其核心功能是通过创建补丁来修改目标程序的执行流程,从而绕过某些限制或窗口。

工具加载与初步分析 🚀

首先,我们启动Loader-ASPr 2.X工具。工具界面会显示一个由“雷神”整理的操作窗口。

我们载入目标程序,或者直接运行程序。

程序运行后,我们按下F2键暂停其执行。这是一个非常常规的调试操作。接下来,我们需要找到程序中的关键断点(此处视频中称为“断手”),通常我们会使用硬件执行断点。

如果中断失败,可能是因为录屏软件(如屏幕录像专家)的干扰。关闭录屏软件后,我们成功在目标CALL指令处下断点。删除之前的硬件断点,重新运行程序,断点成功命中。

分析跳转与修改方案 🔍

断下后,我们观察程序的跳转指令。为了让程序按我们的意图执行,我们需要修改此处的代码。如果直接将其改为JMP指令,会改动4个字节,并且在写补丁时,会连带影响相邻的2个字节,总共需要处理6个字节。

因此,我们采用另一种更高效的方法,只需修改2个字节即可达到目的。修改完成后,我们记录下这个地址。

配置Loader-ASPr补丁 ⚙️

现在,我们打开Loader-ASPr工具来创建补丁。运行工具,使其界面出现。

以下是配置补丁的关键步骤:

  1. 在工具中选择目标窗口。通过WordTitle(窗口标题)来定位。
  2. 在众多窗口标题中,找到我们的目标程序窗口和OD调试器的窗口标题。
  3. 工具中有一个选项是Lag(提示框),其描述为“Let the loader... kill it”,意思是让加载器关闭该窗口。
  4. 我们将之前记录下的修改地址(例如9019)填入对应位置。

配置完成后,关闭配置窗口。工具提示开始安装补丁。我们选择原始程序文件,补丁安装成功。

测试补丁与问题排查 🐛

安装后,会生成一个新的已打补丁的程序文件。双击运行这个新文件。然而,测试发现程序在关闭目标窗口后,自身也立即退出了,这不是我们想要的结果。

这说明我们的修改或配置可能存在问题。我们需要重新分析。


我们重新尝试中断程序。有时需要多按几次F2才能成功中断。中断后,我们发现除了之前修改的跳转,还有另一个地方也需要修改,因为正是这里导致了程序的直接退出。

我们让目标窗口再次出现,并尝试在另一个地址(例如20F2)下断点进行分析。发现程序依然直接退出,未能有效处理窗口。

检查后发现,是在Loader-ASPr工具中配置时,可能选错了窗口标题。我们重新安装并配置补丁。

工具的高级功能 💡

重新生成补丁后,问题得到解决。Loader-ASPr 2.X是一个非常强大的工具。

它除了我们刚才使用的功能外,还有其他模式:

  • Standard:标准模式,用于创建常规的补丁。
  • Class Name:可以分析当前计算机上运行的所有程序的窗口组件信息。

这些功能为分析和修改程序提供了更多便利。

总结 📝

本节课我们一起学习了Loader-ASPr 2.X补丁工具的基本使用方法。我们经历了从加载程序、分析关键跳转、配置补丁到测试和排查问题的完整流程。关键在于定位正确的修改点,并在工具中准确配置目标窗口和补丁地址。这款工具功能丰富,是进行软件分析和修改的有效助手。

天草高级班 - P19:脱壳破解实战 🛡️➡️🔓

在本节课中,我们将学习一个针对特定加壳程序的脱壳与破解实战过程。课程基于Azure Talk的教程,并重点演示如何使用脚本自动化完成脱壳,避免繁琐的手动操作。

课程概述 📋

本节课的核心是处理一个没有int 3异常的程序。我们将通过忽略所有异常并运行预设脚本的方式,快速定位到程序的原始入口点(OEP),从而完成脱壳。

分析程序异常 🔍

首先,我们需要分析目标程序的异常情况。

观察发现,该程序没有int 3异常。因此,我们的策略是忽略所有异常,直接运行到最后一次异常发生的位置。

记录下最后一次异常的地址为AF-46,这个位置就是程序的OEP。这个过程无需手动演示。

使用脚本自动化脱壳 🤖

上一节我们确认了OEP的位置,本节中我们来看看如何利用脚本高效完成脱壳。

我们需要执行的操作是:忽略所有异常,然后运行脚本。这个脚本是事先准备好的,因为目标程序没有使用Stone Cold保护,所以用此脚本处理非常合适。

当然,手动脱壳也是可行的,但过程会非常劳累。

以下是执行脚本后程序运行至OEP的界面示意:

通过脚本自动化运行,程序顺利在OEP处中断,为后续的修复和转储操作做好了准备。

总结 🎯

本节课中我们一起学习了针对无int 3异常程序的脱壳方法。核心步骤是忽略所有异常并运行自动化脚本,从而快速、准确地定位到原始入口点(OEP)。这种方法相比手动跟踪,极大地提升了效率。

天草高级班 - P2:SVKP 1.4X进阶脱壳教程 🛡️➡️📦

概述

在本节课中,我们将学习如何对一个使用SVKP 1.4X加壳的程序进行手动脱壳。我们将从定位原始入口点开始,逐步处理反调试函数和加密调用,最终完成输入表修复和区段重建,以获取可正常运行的原程序。


第一步:定位原始入口点(OEP)

首先,我们需要探测程序的原始入口点。为了快速完成,我们将使用特定工具进行探测。

以下是探测OEP的步骤:

  1. 加载加壳程序并激活探测功能。
  2. 工具会中断在OEP附近。如图所示,此处即为OEP。
  3. 确认OEP地址。同时可以观察到,入口点处的7个字节被壳抽取了。这通常是Push 60等指令。
  4. 为了验证,可以单步执行Push指令,观察其值被压入的位置,从而确认OEP和被抽取的字节。

第二步:处理反调试与加密调用

上一节我们定位了OEP,本节中我们来看看如何绕过壳的反调试机制。

在OEP之后,需要查找一个关键特征码。找到的位置会与之前版本有所不同。

以下是关键特征的分析与处理步骤:

  1. 在特征位置,可以看到GN1GNZ等指令。这些是壳的反调试函数。
  2. 与之前处理方法不同,本次需要将程序流程跳转至KUR模块的加载处。
  3. 接着会遇到反调试函数和DLL加密调用。如图所示,需要修改此处调用,否则会指向user32的加密函数。
  4. 修改方法是将相关调用全部改为NOP或直接跳转,从而绕过加密检查。

完成修改并跳转后,即可进行下一步。


第三步:中断执行与修复准备

处理完反调试后,我们需要让程序执行到OEP的修复点。

以下是设置断点与中断的步骤:

  1. 在关键跳转后的地址设置断点。
  2. 运行程序,程序会在加密代码执行时中断。有时需要暂停后再继续才能成功断下。
  3. 删除硬件断点,让程序继续执行。通常会中断三次。
  4. 最终,程序会停在OEP的解释代码处,例如地址12FSC。此时单步跟踪,可以看到被抽取的原始字节(例如49F390)被恢复。

此时,将EIP设置为OEP,即可准备进行最终的脱壳操作。


第四步:修复输入表与转储程序

现在程序已到达OEP,我们需要将其从内存中转储并修复。

以下是使用工具进行转储和初步修复的步骤:

  1. 使用脱壳工具的“转储”功能,指定OEP地址(例如436 CC)。
  2. 尝试修复输入表,选择“全部有效”。
  3. 初步修复后,运行程序可能会报错或引发异常。这表明还有未处理的加密区段。

第五步:处理异常与重建区段

上一节修复后程序仍无法运行,本节我们通过处理异常来定位和修复缺失的区段。

程序报错通常是因为访问了被壳加密或混淆的内存区段。

以下是定位和修补区段的步骤:

  1. 在调试器中设置“忽略内存访问异常”,让程序运行直到尝试访问无效内存时中断。
  2. 中断后,查看引发异常的地址(例如D47D0D02)。这指向一个缺失的区段。
  3. 尝试手动计算并在转储文件中添加该区段。例如,使用LoadPE等工具,根据异常地址计算区段的RVA(相对虚拟地址)和大小。
    • 公式区段RVA = 异常地址 - 映像基址
  4. 添加新区段后,务必保存文件。
  5. 最后,使用修复工具“重建PE头”并修正入口点(OEP)。
  6. 完成重建后,新的程序文件应能正常运行,且没有跨平台问题。

这个修补区段的技巧在处理如ASProtect等复杂壳时经常用到,可能需要反复查找和添加多个区段。


总结

本节课中我们一起学习了手动脱壳SVKP 1.4X的完整流程。关键步骤包括:定位并确认OEP、识别并绕过反调试与加密调用、通过中断到达清理后的代码、转储进程并修复输入表,以及最后通过分析异常来定位和重建缺失的代码区段。掌握这些技巧对于处理同类或更复杂的保护壳至关重要。

天草高级班 - P20:躲避ASProtect内存校验 🛡️

在本节课中,我们将学习如何破解一个受ASProtect保护的软件。与常规破解不同,本次的重点在于躲避其内存校验机制,即CRC校验。这是一个比较棘手的问题,我们将通过实践来找到解决方案。

初始分析与尝试

首先,我们忽略所有异常并打开目标程序。程序界面显示“我还有14天”,这表明它可能是一个试用版软件。

我们尝试直接运行,但程序无法正常启动。


在调试器中,我们忽略所有异常后,查看内存映射,发现代码段似乎被抽取了。这是一个典型的加壳特征。

定位关键字符串

为了找到突破口,我们搜索程序中的字符串。因为是英文程序,我们搜索关键词“Day”。

搜索结果显示了一个地址,附近有字符串“Registered Queue”。这很可能是一个关键判断点。

我们在该地址设置断点并重新运行程序。

程序中断后,我们观察到一个关键跳转。如果执行这个跳转,程序会显示“已注册”状态。我们的目标是阻止这个跳转发生

我们尝试修改跳转条件,但直接修改后程序没有任何反应,说明这并非最底层的校验点。

追踪校验值的来源

我们需要找到决定这个跳转的关键值是从哪里被写入的。

我们在内存地址上设置硬件写入断点,追踪是哪个指令修改了这个关键内存地址的值。

通过单步跟踪,我们发现一个指令将值写入EAX寄存器,而这个值最终被用于判断。

我们记录下这个值:0x1762631

尝试修改与补丁

我们尝试直接将该内存地址的值修改为1,然后运行程序。修改后,程序似乎可以运行了。

于是,我们尝试制作一个内存补丁。在补丁工具中,我们定位到需要修改的字节序列。

原始字节可能是:

55 56 57 58 59 60 5A 5B

我们需要将其中某个字节(例如偏移处的0x5B)从0x00改为0x01

然而,当我们保存补丁并应用时,程序检测到了修改并拒绝运行。这表明ASProtect的内存校验机制在起作用,它检测到了代码被篡改。

实施“躲避”策略

既然直接修改会被检测到,我们就需要换一种思路:躲避校验,而不是硬碰硬。

我们不再修改判断值本身,而是修改进行校验的代码指令,使其失效。以下是具体步骤:

  1. 找到执行校验的几条关键汇编指令。
  2. 将这些指令替换为无操作的NOP指令(机器码0x90)。

例如,我们找到6个字节的校验代码,将其全部替换:

原指令: 48 84 ... (示例)
修改为: 90 90 90 90 90 90

应用此补丁后,程序成功运行,不再进行内存校验。但这种方法有时不稳定,因为校验可能发生在多处。

更稳定的方法:在OEP后处理

为了更稳定,我们采用另一种方法:

  1. 让程序正常运行到原始入口点(OEP),即壳解密完原始代码之后。
  2. 在OEP之后的代码中,找到写入校验值的内存地址。
  3. 在该地址设置硬件写入断点
  4. 当程序试图写入校验值时中断,此时我们再修改该值为我们想要的值(例如1)。
  5. 因为此时壳的校验代码已经执行完毕,我们的修改不会被再次检查。

这种方法利用了时机差,成功地“躲避”了ASProtect的内存校验。

总结

本节课我们一起学习了如何应对带有内存校验机制的ASProtect壳。

  • 我们首先尝试了直接修改关键判断点,但被校验机制阻止。
  • 接着,我们学习了通过修改校验代码为NOP的方式来使其失效。
  • 最后,我们掌握了一种更稳定的方法:在程序完全脱壳后(OEP之后),再拦截并修改关键值,从而巧妙地躲避内存校验。

核心思路在于理解壳的校验逻辑,并选择在合适的时机(校验完成前或完成后)进行操作,避免与其正面冲突。这需要灵活的调试技巧和对程序执行流程的准确把握。

天草高级班 - P21:一流QQ挖金子外挂破解教程 💎

在本节课中,我们将学习如何对一个名为“一流QQ挖金子”的外挂程序进行破解分析。课程将重点介绍两种绕过其注册验证机制的方法,并演示如何使用特定工具完成内存补丁的制作。

课程概述

这个外挂程序最初由姚夏和哈利辛进行了破解分析,但存在一些不稳定的问题。本节课程将作为补充,详细讲解如何稳定地处理其验证流程。程序明显被抽取了代码,我们的目标是找到并修改其关键跳转,以跳过烦人的未注册提示和网页弹窗。

分析入口点与关键跳转

首先,我们需要定位程序的原始入口点。通过分析,可以明显看出程序的代码被抽取了。

我们运行程序并删除断点,可以观察到关键的机器码。如果没有注册,程序每次都会打开一个网页,这非常麻烦。通过分析跳转指令,我们找到了导致弹窗和打开网站的关键代码段。

很明显,程序会弹出提示框并打开其网站。我们需要修改这个跳转,将其改为 NOP 指令,从而消除注册相关的提示。

方法一:使用工具直接修改

这里提供两种处理思路。第一种方法是使用一个外部工具直接进行内存补丁。

我们将使用一个非常实用的工具。首先,在工具中填入名称,并取消启动提示框。关键步骤是找到目标地址,其指令为 0F84。根据上节课的知识,我们需要将其修改为 90 19

以下是修改的核心操作:

原指令:0F84 [目标地址]
修改为:90 19

方法二:通过内存写入绕过校验

如果第一种方法使用的工具不兼容,我们可以采用第二种思路。这种方法同样在上节课中提到过,即在程序到达原始入口点后,向特定内存地址写入一个值来绕过校验。

具体操作是,使用 DVP 工具创建一个补丁。编辑时,注意虚拟类型设置为 ENF,将指令从 84 修改为 90 19

同时,需要指定一个内存地址,例如 00401234,并为其写入特定的校验值。核心是写入以下数据:

地址:00401234
数据:8B442414C3

最后,保存补丁并创建执行路径。这种方法通过提供一个内存校验值,来欺骗程序的内存检验机制。

方法对比与总结

本节课我们一起学习了两种绕过“一流QQ挖金子”外挂注册验证的方法。

  • 方法一:直接使用工具修改关键跳转指令。这种方法较为直接。
  • 方法二:在程序运行时向特定内存地址写入校验值。这种方法适应性更强,在某些情况下更稳定。

两种方法都可以实现破解目的,但使用 DUP 工具进行内存写入的方式通常更可靠一些。课程中使用的工具将会提供给大家。

通过本课的学习,你应该掌握了针对此类验证的基本分析思路和两种实用的破解技术。

天草高级班 - P22:跳过服务器登陆限制 🚫➡️✅

在本节课中,我们将学习如何绕过一款软件的网络登录验证,将其改造为本地验证,从而实现无需有效账号即可登录。核心思路是定位并修改程序中的关键网络验证调用和跳转逻辑。

概述

我们将分析一个带有网络验证的程序,通过调试工具找到验证服务器地址、关键判断跳转,并将其修改为指向本地或直接跳过验证,最终实现软件的本地破解运行。


定位程序入口与界面

上一节我们介绍了课程目标,本节中我们来看看如何开始操作。首先,目标程序已被脱壳,我们可以从程序入口点开始分析。

在调试器中载入程序,定位到程序的入口点(Entry Point)。尝试直接修改或跳过登录框的创建代码,虽然可以移除登录界面,但这可能导致软件功能异常,因此这不是最佳方案。


查找按钮事件与验证逻辑

既然直接移除界面不可靠,我们需要找到更深层的验证逻辑。以下是查找关键验证点的步骤:

  1. 定位按钮点击事件:在调试器中运行程序,在登录按钮的事件处理函数上下断点。
  2. 跟踪输入获取:程序会获取用户名和密码输入框的内容。如果内容为空,程序会提示错误并跳转到错误处理流程。
  3. 发现网络验证调用:继续单步执行,会遇到一段代码导致程序停滞或进入循环,这通常是一个网络验证的 CALL 指令。其所在的代码段末尾往往是验证的关键点。

进入这个关键的 CALL 内部进行分析。


分析并修改网络验证

进入疑似网络验证的 CALL 后,我们的目标是找到服务器地址和验证结果判断。

  1. 定位服务器地址:在代码中查找类似网址的字符串,例如 http://xxx.xxx.xxx。这就是程序连接进行验证的服务器地址。
  2. 修改为本地验证:为了绕过服务器,我们可以将此地址修改为本机回环地址 127.0.0.1 或一个无意义的本地地址。在十六进制编辑或调试器中直接修改该字符串。
    ; 修改前可能类似
    push offset aHttpWwwAuthse_0 ; "http://www.authserver.com/api/login"
    ; 修改后
    push offset aHttp127001      ; "http://127.0.0.1/fakepath"
    
  3. 处理验证结果跳转:修改地址后,程序依然会执行验证并返回结果。我们需要找到根据验证结果进行判断的跳转指令(通常是 JNZ, JZ, JE, JNE 等)。
    • 这些跳转决定了程序是走向“登录成功”分支还是“登录失败”分支。
    • 通过分析,强制让程序跳转到成功分支。例如,将一个关键的 JNZ(结果非零则跳转至失败)改为 JMP(无条件跳转至成功地址),或者直接 NOP 掉。


测试与修复

完成上述修改后,保存程序并运行测试。

  1. 检查修改是否生效:运行修改后的程序,观察登录行为。如果提示“连接服务器失败”,说明还有网络连接相关的 CALL 需要跳过或修改。
  2. 查找遗漏的验证点:重新调试,在程序提示“验证失败”的地方下断点,回溯找到新的判断跳转点,并应用同样的方法进行修改。
  3. 验证功能完整性:成功跳过登录后,还需测试软件的主要功能是否正常。本例中,软件的部分功能(如搜索引擎列表)可能依赖服务器数据,因此即使登录成功,那些功能也可能无法使用。这属于软件自身的设计限制。

总结

本节课我们一起学习了绕过服务器登录验证的基本方法。核心步骤可总结为以下流程:

  1. 定位:找到程序中的网络验证调用和服务器地址。
  2. 修改:将服务器地址改成本地地址,并修改关键的条件跳转指令,使验证逻辑总是走向成功。
  3. 测试:运行修改后的程序,检查登录是否成功以及核心功能是否受影响。

这种方法的核心思想是 将网络验证转化为本地验证,通过干预程序的判断逻辑来实现破解。需要注意的是,这种方法适用于学习逆向工程思路,实际应用时应尊重软件版权。

天草高级班 - P23:ASProtect 2.x 脱壳后直接注册教程 🔓

在本节课中,我们将学习如何对使用 ASProtect 2.x 加壳的程序,在脱壳后直接完成注册。我们将重点分析脱壳后的关键步骤,并对比其与早期版本(如 1.23 RC4)处理手法的异同。


上一节我们介绍了 ASProtect 壳的基本处理方法。本节中,我们来看看针对 2.x 版本,脱壳后如何直接实现注册。

首先,我们到达程序的原始入口点(OEP)进行检查。

检查是否存在 Stolen Code。非常好,这里没有。这是因为使用脚本可以高效处理,手动操作会非常麻烦。

以下是处理 ASProtect 壳的几种核心方案总结。当你遇到此类壳时,可以遵循以下决策流程:

  1. 检查 Stolen Code:首先判断 OEP 处是否存在被窃取的代码。
  2. 使用脚本自动化:利用脚本处理前期的通用脱壳步骤。
  3. 手动补区段:如果存在 Stolen Code,则需要手动修补相应的代码区段。
  4. 修复 Import Table:修复导入表是脱壳后的必要步骤。
  5. 处理资源与校验:处理可能存在的资源加密和运行校验。

这种脱壳后直接破解的处理手段,在之前讲解 ASProtect 1.23 RC4 的课程中也介绍过,两者的核心思路是相似的。

右键菜单中会出现相关选项。后面的几个无效调用可以剪掉或跳过。程序可以运行,但先不着急。我们需要处理异常,按 Shift + F9 跳过异常。

关键点:注意观察堆栈。在 1.23 RC4 版本中,我们是在第二次出现硬盘指纹信息时进行关键处理的。现在,我们来看 ASProtect 2.11x 版本有何区别。

先向下查找。

找到一个关键地址,并将其记录下来。

保存为断点 1。测试发现,仅一次操作可能还不够。我们再次进行修复操作。关键点又回到了这里。我们取消之前的修改,然后撤销操作。这样一来就成功了。

这说明,ASProtect 1.23 RC4 与 2.x 版本在关键点的触发次数上存在一些区别。在前面的课程中,制作内存补丁也需要三次中断。因此,唯一的不同点在于关键事件触发的时机,但整体的处理手段是相同的。

本节课主要向大家强调以下几点:

  1. 拿到一个 ASProtect 2.x 加壳的程序后,首先检查 OEP 处是否存在 Stolen Code
  2. 如果存在 Stolen Code,就必须手动补区段,正如前面课程所介绍。
  3. 如果不存在 Stolen Code,则无需补区段,脚本通常能自动处理前两步。
  4. 后续步骤都是修复问题,例如修复导入表。
  5. 如果存在 Stolen Code,那么后续肯定需要修复 Rotor Check(反调试校验)。
  6. 每个版本都可能存在资源加密等问题,但如果有 Stolen Code,则 Rotor Check 绝对存在,这一点必须牢记。


总结

本节课中,我们一起学习了针对 ASProtect 2.x 壳脱壳后直接注册的流程。核心在于判断 Stolen Code 是否存在,并以此决定是否需要手动补区段。同时,我们对比了 2.x 与 1.23 RC4 版本在关键断点触发上的细微差别,但整体遵循相似的处理逻辑:使用脚本辅助、修复导入表、处理反调试校验。掌握这个决策树,能帮助你更系统地应对此类加壳程序。

天草高级班 - P24:完全手脱ASPr壳实战教程 🛡️➡️📦

在本节课中,我们将学习如何手动脱去一个名为“ASPr”的强壳。整个过程涉及分析变形代码、修复导入地址表(IAT)以及重建程序区段。我们将使用OllyDbg(OD)作为主要调试工具,通过一系列步骤,最终得到一个可运行的、已脱壳的程序文件。


概述与初始设置

首先,我们忽略除“硬件断点”和“内存访问”之外的所有异常。在程序中断两次之后,我们打开了内存镜像,并定位到了一个“假OEP”。

接下来,我们查看调用表。将视图转换为窗口模式,可以看到IAT的起始地址是0x2500。这些IAT条目本身并未加密,但调用它们的代码被变形处理了。

我们需要确认API调用是CALL类型还是JUMP类型。通过查找所有模块间的调用,可以清晰地看到,所有API都是通过FF15(CALL)指令来调用的。


定位关键代码与申请内存

上一节我们确认了API的调用方式。本节中,我们将定位到变形代码的关键位置并进行处理。

我们直接运行到最后一个异常处,然后按F9继续。此时不要返回,我们需要查找+B位置的代码。

找到目标位置后,我们使用一个插件来申请新的内存空间。申请的内存地址是0x164,我们将在此处写入修复代码。

以下是写入0x164地址的关键代码及其含义:

  • 15000:这个地址指向我们之前看到的FF15调用指令块,即API调用被代码变形处理的位置。
  • 42500:这是.code代码段之后下一个区段的起始地址。同时,它也是IAT的起始地址。注意,这里的15FF代表IAT的结束位置。


隐藏OD与获取IAT

写入修复代码后,我们隐藏OllyDbg的调试器状态,以避免被壳检测到。然后运行脚本,程序会开始运行并变得有些卡顿,这是一个正常的现象。

接下来,我们需要获取完整的IAT。由于程序运行缓慢,我们尝试从IAT的提示位置0x2500开始查找。如果直接搜索不成功,我们可以换一种方式:在代码起始地址0x00401000处搜索FF15指令,然后查看所有模块间的调用。此时会发现,原先被变形处理的调用都已经被修复了。


抓取与重建程序区段

IAT修复完成后,我们还需要处理被壳分散或变形的程序代码段。本节我们将手动抓取并重建这些区段。

以下是抓取区段的操作流程:

  1. 使用工具抓取内存中有效的代码块。
  2. 从地址0x130开始,依次抓取各个区段的数据。
  3. 为每个抓取的数据块命名并保存到本地目录。
  4. 这个过程需要重复数十次,非常繁琐。例如,抓取地址0x1300x1310x140等处的数据,并注意计算每个区段的大小。
  5. 最终,我们为本例添加了33个新的区段。


修复转储文件与最终验证

所有区段抓取并添加后,我们使用工具重建最终的脱壳文件(转储)。在保存时,务必注意修正OEP(原始程序入口点)为正确的值。

保存后运行修复工具(例如Import REConstructor),对刚转储的文件进行IAT修复。修复过程中可能需要按F7单步进入某些调用,以确保修复准确无误。

修复成功后,我们就得到了一个完整的、可独立运行的脱壳程序。最后可以查壳验证,确认其为“未知”或已成功脱壳。


总结

本节课中,我们一起学习了手动脱去ASPr强壳的完整流程。

  1. 分析阶段:我们定位了假OEP,分析了未加密但被变形的IAT,并确认了API的调用类型为CALL (FF15)
  2. 修复阶段:我们通过编写脚本修复了变形的API调用代码,并成功获取和重建了完整的IAT。
  3. 重建阶段:我们手动抓取了数十个被壳处理过的程序区段,并将它们添加回转储文件中。
  4. 最终处理:我们修正了OEP,并使用修复工具完成了最终的文件重建,得到了可运行的脱壳程序。

整个过程技术性强且步骤繁琐,但通过逐步分析、耐心修复和重建,最终能够成功攻克此类强壳。

天草高级班 - P25:某商业软件算法分析 🧠

在本节课中,我们将对一个商业软件的注册算法进行逆向分析。课程将详细拆解其注册码的生成过程,从机器码处理到最终注册码的构造,帮助初学者理解算法分析的基本思路和关键步骤。


软件初步探查 🔍

首先使用查壳工具检查目标软件。该软件使用UPX加壳。按钮事件等基础信息已预先定位,此处不再赘述。

这是当前分析所用的机器码。

原先笔记本的机器码是另一个值,现已更换。为了节约时间,不演示为何要进入特定代码段。首次跟踪时通过弹窗等信息可发现问题,第二次分析便知道需要进入哪些关键调用。


关键数值与浮点运算 ⚙️

在代码中观察到数值 0x7B7,其十进制值为 1975。另一个关键数值 0x0B 是十六进制,对应十进制 11

进入关键调用后,在堆栈中看到数值 1231243756。在数据窗口中跟随查看,但该地址暂无有效值。

进行逆向分析时,第一个注意事项是观察关键代码段。其中,浮点运算指令的出现是需要重点关注的信号。

跟踪发现,程序将某个值放入了浮点寄存器,随后通过弹栈指令 FSTP 将其弹出到 ESP 指向的地址。


算法结构解析 🧩

在代码中看到字符串 YYYYMMDD,这通常代表日期格式(年-月-日)。遇到此类格式字符串,往往意味着算法与日期或固定数值相关。

程序获取了固定数值 19751128。随后,代码将寄存器 EAXEBX 清零,说明后续计算会用到它们。寄存器 ESI 则保存了 19751128 这个值。

程序开始循环处理这个数值的每一位(字节):

  1. 取第一个字节 0x31(字符 '1')放入 BL
  2. BL0x200x2D0x2B0x240x780x580x30 等字符的ASCII码进行比较。
  3. 由于 0x31 不等于上述任何值,跳转到减法处理部分:BL = BL - 0x30。这样,字符 '1' 就转换成了数字 1
  4. 接着与 0x39(数字 9 的ASCII码)进行比较。
  5. 程序继续获取下一位数字 '9',并进入下一次循环。

在循环中,EAX 的值不断变化。循环结束后,EAX 中的值就是 19751128 经过初步处理的结果。

EAX 中的结果 19751128 被返回。


核心算法步骤分解 🔢

进入下一个关键调用,算法涉及多层处理。

第一轮算法:

  1. 机器码转16进制:将输入的机器码转换为16进制数值。
  2. 与固定值异或:将上一步的结果与 1975(固定值)的16进制形式进行异或运算。
    • 公式result1 = hex(machine_code) XOR 0x7B7
  3. 除法与重组:将异或结果除以 0xA(十进制10),得到商和余数。
    • 公式商 = result1 // 10, 余数 = result1 % 10
  4. 字符串构造:将余数倒序排列并连接,形成第一部分的中间结果 2245139670

第二轮算法:

  1. 字符提取与运算:从第一轮得到的字符串中提取特定位置的字符。
    • 取第一位 '2' 和第二位 '2'(注意有两个 '2',需通过后续分析确定具体取哪一个)。
  2. 加法与取模:将两个字符对应的数值相加,然后加上 0x40(十进制64),结果存入 ECX(例如得到 0x855)。
  3. 除法求余:使用 CDQ(扩展符号位)和 IDIV 指令,用 EAX 除以 ECX
    • 代码描述
      CDQ          ; 将EAX符号位扩展到EDX
      IDIV ECX     ; (EDX:EAX) / ECX,商在EAX,余数在EDX
      
  4. 结果处理:将得到的余数(在 EDX 中)与整数部分(在 EAX 中)相加。
  5. 字符转换:对结果进行一系列转换,包括转大写、加特定数值(如 0x30/0x41/0x4F)等,生成几个关键的字母或数字。

第三轮算法(最终组合):

  1. 交叉插入:将前两轮算法产生的多个字符(例如 BJP0)按照特定规则插入到第一轮产生的数字串(如 19832015101033)中。
  2. 插入规则:例如,将某个字符插入到数字 '9' 之后,将另一个字符插入到数字 '0' 的中间位置等,形成最终的注册码。
    • 过程描述:这是一个交叉插入的过程,算法固定了插入位置。

第三部分算法的入口。

最终注册码的生成逻辑是交叉插入。


辅助分析与注册机实现 💻

分析时可以使用提供的 UDD 备份文件,将其拷贝到 ODUDD 目录中,这样就能看到预先添加的注释,方便理解。

以下是注册机代码的关键逻辑解析:

首先定义变量,包括整数变量和字符串变量。如果机器码输入框为空,则不做处理。

第一步:异或与循环

  1. 将机器码字符串与固定字符串 "19751128" 进行逐字符异或,结果赋值给整数变量 MKY
    • 代码逻辑MKY = 机器码 XOR "19751128"
  2. 进行一个循环(i09),在每次循环中:
    • MKY 除以 100xA),得到商 MKY1 和余数。
    • 将余数转换为字符,拼接到临时字符串 Tempo1前端(实现倒序)。
    • 将商 MKY1 赋值给 MKY,进行下一轮循环。
  3. 循环结束后,Tempo1 中即为第一轮算法生成的数字字符串(如 "2245139670")。

第二步:字符提取与运算
这部分对应逆向分析中的第二轮算法,代码非常直接。

  1. 使用 Copy 函数从 Tempo1 中截取特定位置的字符(如第1位、第2位)。
  2. 将截取的字符转换为整数,加上固定值(如 0x30),然后可能再转换为十六进制或进行其他运算。
  3. 分别处理第1&2位、第3&4位、第5&6位、第7&8位以及第9位数字,通过加不同常数(0x30, 0x41, 0x4F)生成不同的字符(B, J, P, 0等)。

第三步:交叉插入生成最终码
将第二步生成的几个字符,按照固定规则插入到第一步生成的数字字符串 Tempo1 的特定位置,最终生成完整的注册码。


总结 📝

本节课我们一起学习了对一个商业软件注册算法的完整分析过程。

我们首先进行了基础的查壳和定位。随后,通过跟踪分析,发现了算法核心围绕一个固定日期值 19751128 展开。算法主要分为三个部分:

  1. 机器码与固定值异或,再通过除法和倒序生成数字基底。
  2. 对数字基底进行分段运算,通过加法、取模、加常数等方式生成几个关键字符。
  3. 将关键字符按固定规则交叉插入到数字基底中,形成最终注册码。

整个算法的强度较弱,关键点在于识别出固定值、理解字符与数字的转换过程以及掌握交叉插入的规律。通过编写注册机代码,我们完整复现了这一过程。希望本教程能帮助你理解算法分析的基本方法。

天草高级班 - P26:Armadillo全保护脱壳教程 🛡️➡️🔓

在本节课中,我们将学习如何对一个启用了全部保护选项的Armadillo壳进行手动脱壳。Armadillo是一款强大的商业保护壳,其“全保护”模式集成了多种反调试和代码混淆技术,对脱壳过程提出了更高的挑战。我们将逐步分析其保护机制,并演示完整的脱壳与修复流程。

概述与保护机制分析

上一节我们介绍了Armadillo壳的基本概念。本节中,我们来看看当启用“全保护”模式时,程序具体集成了哪些防护措施。

该壳程序启用了全部保护选项。在配置工具中查看版本或保护方式时,若提示错误,勾选特定选项可降低被检测的概率。这相当于激活了所有可用的保护功能。

Armadillo全保护模式主要提供了四种核心保护方式,并包含多个子选项:

以下是主要的保护选项及其作用:

  1. Code Splicing(代码拼接):此选项会对代码进行变形。在后续修复时,我们可以看到其效果。
  2. Import Table Elimination(IAT加密):此选项对导入地址表进行加密处理。
  3. CC(远地址跳转):即“Call to Call”,程序内部的一些调用地址,在脱壳后,跳转指令仍然指向壳的地址。由于壳已被脱去,这会导致程序运行出错。
  4. Memory Protection(内存保护):此选项用于防止内存补丁和内存转储。

此外,还有一个关键的保护机制,其作用类似于自校验或附加数据。脱壳后,部分功能无法使用,通常就是由这个机制造成的。

手动脱壳尝试与问题

了解了保护机制后,我们首先尝试手动脱壳。由于程序集成了多达五六种保护方式,手动操作经常会遇到问题。

在手动附加调试时,程序会提示无法调试。隐藏调试器后,发现完全无法断下任何断点。我们尝试了多种方法,包括设置特定的断点(如OutputDebugString),但在某些操作系统(如Windows XP SP2)上均告失败。

以下是手动脱壳过程中遇到的主要困难:

  • 调试器被强力检测,无法正常中断。
  • 不同版本的调试器或操作系统环境,行为可能不一致。
  • 在SP2系统上,手动脱壳的可行性很低。

因此,我们将转向使用脚本辅助完成前期工作。

使用脚本进行前期处理

由于手动脱壳困难,我们使用一个专用脚本来处理初始步骤。这个脚本在SP2系统下也可能不太稳定,但它是必要的起点。

运行脚本后,我们需要记录关键信息:

  • OEP(原始入口点):地址为 004716D4。注意,国外编写的工具习惯在地址前加一个0,实际是4716D4
  • OEP起始字节:前两个字节是 55 8B(对应十六进制显示可能为 8B55)。
  • 壳的起始字节:载入时,壳的前两个字节是 60 18。务必与OEP的字节区分开。

记录完成后,脚本会执行子进程与父进程的分离。此时在进程列表中可以看到两个进程:父进程和子进程(黑框)。我们需要附加到子进程上进行后续操作。

修复IAT与处理远地址跳转

成功分离进程并附加后,我们进入核心的修复阶段。首先需要修复被加密的导入地址表和处理远地址跳转。

在代码中搜索 FF 25(间接调用指令),可以找到IAT的调用点。右键选择“跟随表达式”,然后向上查找IAT的起始位置。例如,可能找到地址 0016733C

然而,0016733C 这个地址明显超出了程序正常的镜像范围(例如 0062xxxx),这就是“远地址跳转”的保护效果。调用指向了壳空间内的地址,脱壳后这些地址将失效。

以下是修复IAT的步骤:

  1. 使用工具(如Armadillo Find Protected)载入加壳程序,它会自动创建两个进程。
  2. 附加到子进程上(进程ID可通过工具查看)。
  3. 在特定位置(如CreateThread调用处)下硬件执行断点,然后运行程序中断。
  4. 单步跟踪,找到程序解压IAT并准备跳回OEP的时机。
  5. 在内存映射中,搜索IAT的特征字节(如3C 3C),找到已解密的IAT数据块。
  6. 将这些正确的IAT数据复制出来。

重建IAT与最终脱壳

找到正确的IAT数据后,我们需要在脱壳后的程序中重建它。

首先,使用修复工具(如ArmaGeddon)处理代码拼接。工具可能自动定位到相关区域,但需要手动调整代码段的长度(例如,从默认的1000改为2000),然后进行修复。

接下来,修复IAT的远地址跳转:

  1. 在修复工具中,指定原始IAT的地址和长度。
  2. 为IAT选择一个新地址,通常找一个有足够空间的区段(如.data段),避免放在.text段。
  3. 执行修复,工具会将远地址调用修正为指向新地址的有效调用。

完成IAT修复后,进行最终的转储操作:

  1. 在转储工具中填入之前记录的OEP地址(004716D4)。
  2. 将IAT的RVA和大小设置正确。
  3. 在修复选项中,不要勾选前三个选项(如“使用新的输入表”、“追踪真正的调用”等),以增加脱壳后程序的兼容性。
  4. 执行转储和修复。

处理附加数据与程序校验

脱壳修复后的程序可能仍无法正常运行,点击按钮会报错。这是因为还有“附加数据”或自校验保护未被处理。

此时,需要使用专门的补丁工具(如Armadillo KillerAnti-Armadillo):

  1. 载入原版加壳程序。
  2. 点击出错的按钮,让工具分析并修复其中的硬编码或校验代码。
  3. 再载入我们脱壳修复后的程序,应用刚才生成的修复数据。
  4. 保存最终的文件。

处理完成后,脱壳程序的功能应恢复正常。使用查壳工具检查,可能会显示为“Microsoft Visual C++”编译,这是正常的,因为壳的特征已被去除。新增的区段是修复过程中重建IAT所产生的,对破解已无影响。

总结与资料

本节课中,我们一起学习了针对Armadillo全保护壳的完整脱壳流程。

我们首先分析了其集成的多种保护机制,包括代码拼接、IAT加密、远地址跳转、内存保护和附加数据校验。然后,我们经历了从手动尝试失败,到使用脚本分离进程,再到逐步修复IAT、处理远地址跳转,最后处理附加数据校验的完整过程。关键点在于耐心地寻找已解密的IAT数据,并正确地将其重建到脱壳后的镜像中。

这个过程涉及多个工具和细致的操作,需要根据实际情况灵活调整。提供的脚本和步骤记录是攻克此类强壳的重要参考。


本节课中我们一起学习了:

  • Armadillo全保护模式的核心机制。
  • 使用脚本辅助进行子进程分离。
  • 定位并修复被加密和混淆的IAT。
  • 处理远地址跳转保护。
  • 使用工具修复脱壳后的附加数据校验。
  • 最终完成可运行脱壳文件的生成。

天草高级班 - P27:高级班总结 🎓

在本节课中,我们将对天草高级班课程的核心内容进行系统性的梳理与总结。课程主要涵盖了多种高级壳的脱壳方法、网络验证的破解思路以及一些重要的学习建议。


一、 高级壳处理总结

上一节我们介绍了课程的整体框架,本节中我们来看看在高级班中重点讲解的几种壳及其核心处理方法。

1. SVKP壳

SVKP壳的处理流程存在多种变通模式。在高级班中讲解的SVKP案例与初级课程中的有所不同,因此需要学会举一反三。建议学员自行寻找更多SVKP加壳的程序进行练习。

2. AS Protector壳

AS Protector壳(有时也称作Anti-Corrupt)的不脱壳破解方法是必须掌握的核心技术。该方法在国内教程中较为少见。

以下是处理AS Protector壳的关键步骤:

  • 躲避内存校验:课程中介绍了两种方法,需要仔细理解。
  • 手动脱壳流程
    1. 处理IAT(导入地址表)。
    2. 寻找两个特征值 0x85
    3. 处理代码变形。
  • 脚本辅助:课程中提供了一个最佳脚本,可用于自动处理修复IAT和代码变形前两项工作。该脚本已随课程提供。

使用脚本时需注意IAT项的类型:

  • Call类型:特征为 FF 15
  • Jump类型:特征为 FF 25
    识别类型后,只需补充相应的区段即可。

最后是修复 Load Check。判断是否为 Load Check 的方法是:当补完所有认为必要的区段后运行程序,若出现“Protect Error”或错误地址指向已补区段,即可确定。修复方法是找到特定代码并替换,具体替换代码在课程中已讲解。

3. 穿山甲全保护壳

穿山甲全保护壳目前应用广泛,其保护模块需分别处理:

以下是穿山甲全保护壳各模块的处理方法:

  • Debug Blocker(阻止调试器):处理方法为忽略所有异常并隐藏OD。推荐使用修复版OD,例如 Dbg,其在调试此类壳时表现稳定。
  • Comii(双进程保护):存在父子进程互相检验。需要进行进程分离,推荐使用脚本处理,以避免手动操作时的时差导致检测。手动方法也必须掌握。
  • Import Enable(输入表保护):使用特定工具载入加壳程序,通过修改 MultiJump 来获取正确的IAT。这是脱标准版或此类全保护壳的基础流程。
  • Crossplane(原地跳转):使用专用工具处理。注意:使用 0.95 版工具时,若识别出的区段大小为 0x10000,建议手动改为 0x20000 以确保安全。
  • CC Protection(代码变形):将代码变为 0xCCint 3 中断)。脱壳后若点击按钮程序退出,往往是此保护所致。使用对应工具处理,方法在课程中已说明。
  • Memory Protection(内存保护):防止内存补丁。这是后三种保护中重点需要掌握的。

对于AS Protector和穿山甲壳,虽有直接打内存补丁的方法,但需要自行编写大量代码,对当前学员能力要求较高。未来可视大家能力提升情况再作讲解。


二、 算法分析与网络验证

1. 算法分析

在初、中、高级班中,均未深入讲解算法分析,仅在一个商业软件案例中略有提及。这主要出于两点考虑:一是学员当前的汇编阅读能力普遍需要锻炼;二是算法分析通过视频教学难度较大。未来将根据大家能力的提升,适当加强此部分内容。

2. 网络验证破解

网络验证是许多学员感兴趣的部分,其难度并非想象中那样高,关键在于跟踪。

以下是网络验证的几种常见类型及破解思路:

  • 登录框类型:多数(约90%)可使用 F12堆栈调用法 直接定位并跳过登录验证过程。
  • IP验证类型:属于半网络验证,强度不高。只需找到验证IP的跳转指令并修改,使其跳过验证即可。
  • 服务器检测类型:这是完整的网络验证。软件需与服务器通信并处理返回数据。破解思路如下:
    1. 找到按钮事件:根据语言不同(Delphi/BC++/VB/C++)使用不同方法或F12法定位。
    2. 找到网络验证调用CALL:跟踪并尝试使其失效。
    3. 修改验证地址:尝试将验证服务器地址改为本地地址 127.0.0.1,但之后仍需跟踪和修改大量后续跳转。
  • 重启验证类型:重点吸收第15课的破解思路。该课演示了多种思路的尝试和详尽的记录过程。

核心建议:破解网络验证必须有耐心,并务必做详细记录。跟踪代码可能很长,不做记录极易前功尽弃。课程中推荐使用 NotePad 插件来保存跟踪记录,并注意随时保存,防止OD关闭导致记录丢失。


三、 学习建议与课程总结

破解最重要的是思路。拿到软件不要急于动手,应先试用,寻找疑点和漏洞,整理成文,再逐一尝试。破解是一项需要时间和耐心的工作。

高级班课程至此全部结束。后续会根据学员的需求和基础,不定期更新一些教程。最后提醒大家,学习要循序渐进,跟着教程扎实掌握,不要浮躁。


本节课中我们一起学习了高级班涵盖的多种壳的脱壳要点、网络验证的破解分类与思路,并强调了记录与耐心在破解中的重要性。希望学员们能巩固知识,灵活运用。

天草高级班 - P3:跳过服务器检测 🛡️

在本节课中,我们将学习如何逆向分析一个具有网络验证功能的软件,并跳过其服务器检测,从而实现“白嫖”使用。整个过程涉及脱壳、定位关键跳转、修改程序逻辑等步骤。

概述与脱壳 🧩

首先,我们面对的是一个需要连接服务器进行验证的软件。它提供试用功能,但我们的目标是绕过这个验证。

我们首先需要对软件进行脱壳处理。该软件使用了SPEC壳,我们使用Proxylin工具进行脱壳。脱壳完成后,我们找到了程序的原始入口点(OEP)。

定位关键代码与提示框 🔍

上一节我们完成了脱壳,本节中我们来看看如何定位程序中的验证逻辑。

我们使用F12暂停法来寻找程序弹出提示框的相关代码。当程序运行并弹出提示框时,我们暂停它,然后在调用堆栈中寻找返回地址。

以下是定位关键调用过程的步骤:

  1. 在程序运行时按下F12暂停。
  2. 在调用堆栈(Stack)中右键点击返回地址,选择“在反汇编中跟随”。
  3. 这样就定位到了调用提示框函数的位置。我们在该调用的起始处(断首)下一个断点。
  4. 重新载入程序,运行后会断在我们下的断点处。

分析并修改验证跳转 ⚙️

找到了关键调用后,我们需要分析其周围的判断逻辑。

我们单步执行(F8)代码,观察寄存器和跳转指令。关键点通常是一个比较(CMP)指令后跟一个条件跳转(如JEJNZ)。

例如,我们可能看到类似这样的代码:

CMP EBX, 0
JE SHORT 跳过验证
CALL <验证函数>

我们的目标是让程序执行JE跳转,从而绕过CALL <验证函数>

直接修改JE为无条件跳转JMP是一种方法,但有时修改的字节较多。另一种更精巧的方法是修改前面的代码,确保比较条件成立。例如,如果CMP EBX, 0判断EBX是否为0,我们可以在前面添加指令将EBX清零:

MOV EBX, 0
NOP
NOP

这样就能保证跳转发生。

处理网络验证与版本判断 🌐

跳过了本地提示框后,程序可能还会尝试连接服务器进行网络验证。

我们继续使用断点法,寻找连接服务器或进行网络验证的CALL指令。找到之后,我们的目标通常是直接“NOP掉”(用空指令0x90填充)这个调用,或者修改其前面的跳转逻辑,使其无法执行。

此外,软件可能有“标准版”和“专业版”之分,验证结果会影响显示的版本。我们需要找到判断版本的代码位置。

以下是修改的关键点列表:

  1. 网络验证调用:找到类似CALL [API函数]CALL <内部验证函数>的指令,将其NOP掉。
  2. 版本标志判断:寻找比较版本字符串(如“Standard”和“PRO”)的代码,修改跳转,让程序始终走向“专业版”分支。
  3. 多处修改:此类验证通常不止一处,需要耐心寻找并修改所有相关跳转和调用。

在修改过程中,建议使用OD的笔记插件记录下每个修改的地址和内容,方便后续管理和保存补丁。

测试与总结 ✅

完成所有修改后,保存对程序的更改(打补丁),然后运行测试。

本节课中我们一起学习了逆向分析中跳过服务器检测的完整流程:

  1. 脱壳:为分析原始代码做准备。
  2. 定位:使用F12暂停法定位关键函数调用。
  3. 分析:阅读汇编代码,理解验证逻辑和关键跳转。
  4. 修改:通过修改指令或数据,改变程序执行流程,绕过验证和版本限制。
  5. 测试:验证修改是否成功,软件功能是否正常。

这个过程锻炼了代码跟踪、逻辑分析和手动打补丁的能力。请务必自行练习,巩固所学知识。

天草高级班 - P4:tElock另类脱壳破解教程 🛡️➡️🔓

在本节课中,我们将学习一种针对tElock壳的另类脱壳与破解方法。该方法不依赖传统的寻找OEP和修复IAT的“正规”流程,而是通过一种技巧来定位并修复被混淆的IAT,并最终破解软件的演示版限制。

一、 脱壳与定位OEP

上一节我们介绍了壳的基本概念,本节中我们来看看如何绕过tElock的异常并找到程序的原始入口点。

首先,在OD中载入目标程序。忽略所有异常设置,然后使用Shift+F9多次运行程序以通过异常。

使用F9运行时可能会遇到错误提示,但持续忽略并运行,最终会到达OEP。从入口点的特征可以判断这是一个BC++编写的程序。

二、 处理被混淆的IAT

找到OEP后,常规方法是手动查找并修复IAT。我们首先尝试使用Import REC的自动查找功能。

以下是尝试修复IAT的步骤:

  1. 在Import REC中附加进程,填入OEP地址并点击“自动查找IAT”。
  2. 点击“获取输入表”,会发现存在大量无效函数。
  3. 尝试使用等级1或等级3修复,以及相关插件,均无法修复成功。所有函数显示为Fail

这表明tElock对IAT进行了特殊处理,常规方法失效。

三、 另类IAT修复技巧

既然自动修复无效,我们需要手动分析并修复IAT。关键在于识别出IAT的真实数据块。

  1. 在OD的数据窗口中,右键点击无效的IAT区域,选择“二进制”->“编辑”,可以看到大量空数据(00)。
  2. 我们需要找到包含有效API地址的数据块。观察汇编代码,例如一条mov eax, dword ptr [xxxxxxxx]指令,其中的地址xxxxxxxx可能指向IAT。
  3. 在数据窗口中跟随该地址。如果看到以系统DLL基址(如77xxxxxx)开头的地址,那很可能就是有效的IAT项。
  4. 确定IAT的起始和结束位置。核心操作是:复制从IAT起始地址开始,到结束地址后额外4个字节空数据(00 00 00 00) 的整个数据块。
  5. 在Import REC的窗口中找到对应的RVA地址,将复制的数据粘贴进去。粘贴后,Import REC应立即识别出部分有效的API函数。

重复此过程,处理其他IAT块。例如:

  • 第一个块:起始于 xxxxxxxx,复制数据并粘贴到Import REC中对应的RVA。
  • 第二个块:可能需要地址偏移,例如xxxxxxxx+5,同样复制并粘贴。
  • 确保每次粘贴的数据末尾都包含4字节的空数据作为分隔。

修复完成后,剪掉所有仍然无效的函数,然后转储并修复文件。修复后的程序应能正常运行。

四、 破解演示版限制

程序运行后,我们发现这是一个“演示版”,保存等功能被禁用。破解演示版的关键是找到控制功能开关的标志位。

以下是使用F12堆栈调用法结合数据断点进行破解的步骤:

  1. 在OD中运行修复后的程序,尝试点击被禁用的功能(如“保存”)。
  2. 程序中断后,在堆栈窗口中右键,选择“反汇编窗口中跟随”。
  3. 在代码中寻找关键调用(call)。一个重要的经验特征是:在关键判断call之后,经常会有对al寄存器的测试,例如 test al, al
  4. 找到疑似关键call后,在其上方或下方设置断点,重新运行并触发功能。
  5. 程序中断后,步入(F7)该call。观察其如何影响al的值(通常al=0代表失败,al=1代表成功)。
  6. 分析是哪个内存地址或寄存器(如[ecx+24])的值最终决定了al。记下这个地址。

五、 定位并修改关键标志

上一节我们找到了影响功能的关键地址,本节中我们来看看如何定位该标志的写入点并永久修改。

  1. 在数据窗口中跟随(Ctrl+G)上一步找到的关键地址(例如[ecx+24])。
  2. 在该地址的字节(Byte)上右键,选择“断点”->“硬件访问”->“Byte”。
  3. 重新运行程序。程序会在任何指令向该地址写入数据时中断。
  4. 中断后,观察写入的代码。这通常是程序初始化时设置演示版标志的地方。
  5. 将写入的值从1(演示版)修改为0(正式版)。注意:修改时机很重要,必须在程序使用这个值进行判断之前完成修改。有时需要回溯到更早的代码,或在写入指令之后直接修改内存。
  6. 修改后继续运行程序。此时被禁用的功能应该已经可用。

六、 总结

本节课中我们一起学习了针对tElock壳的完整另类处理流程:

  1. 脱壳:通过忽略异常到达OEP。
  2. 修复IAT:通过手动识别并复制内存中的有效IAT数据块到Import REC中,解决了自动修复失效的问题。
  3. 破解限制:使用F12堆栈调用法找到关键判断call,再通过硬件访问断点定位并修改决定软件是否为演示版的内存标志位。

这种方法的核心在于灵活运用调试器,直接分析内存数据和行为,绕过壳的保护机制。特别是修复IAT的技巧和利用硬件断点追踪标志位写入点,是处理此类强壳的有效手段。

天草高级班 - P5:服务器检测破解教程 🔍

在本节课中,我们将学习如何对一个带有服务器检测和网络验证功能的加壳程序进行破解分析。我们将从脱壳开始,逐步定位关键验证代码,并尝试修改程序逻辑以达到绕过验证的目的。


脱壳与初步分析 🛡️

上一节我们介绍了课程目标,本节中我们来看看如何对目标程序进行脱壳和初步分析。

程序被一个压缩壳保护。虽然加壳,但其导入地址表(IAT)和API调用仍然可见,这表明它并非加密壳,这简化了我们的分析工作。

以下是脱壳的具体步骤:

  1. 使用调试器载入程序。
  2. 在程序入口点,使用F8进行单步执行。
  3. 注意观察堆栈指针(ESP)的变化规律。一种常见方法是:在ESP寄存器内容发生变化后,对栈地址下硬件访问断点。
  4. 运行程序(Shift+F9),程序会在断点处中断。此时可能已接近原始程序入口点(OEP)。
  5. 通过反复尝试和跟踪(例如,连续按F8四次),最终定位到程序的真实OEP。

此外,还可以尝试使用“ESP定律”或通过单步跟踪总结出的特定断点来快速到达OEP。找到OEP后,即可使用工具进行脱壳和修复。


定位验证关键点 🎯

成功脱壳后,我们接下来需要定位程序进行服务器验证的关键代码位置。

程序启动后会尝试连接服务器进行验证,并提示“Not Regist”等错误信息。我们的目标是找到生成这些提示的逻辑判断处。

以下是定位关键点的步骤:

  1. 在脱壳后的程序中,搜索字符串“Not Regist”或相关错误信息。
  2. 在找到的字符串引用处下断点。
  3. 重新运行程序,程序会在验证逻辑处中断。
  4. 分析断点附近的代码,通常会发现决定程序流程的关键跳转指令(如jnz, je)。

在本例中,分析发现一个关键跳转:若跳转实现,则提示未注册;若跳转未实现,则验证通过。我们的初步思路就是修改这个跳转。


深入分析与补丁制作 ⚙️

仅仅修改一个跳转可能并不完美。程序可能在多处调用验证函数,我们需要找到最根本的验证函数并进行处理。

上一节我们找到了一个关键跳转,本节中我们来看看如何系统地找到并修改核心验证逻辑。

为了找到所有验证相关的调用,我们可以采取以下方法:

  1. 在代码中查找所有调用验证函数(call)的指令。
  2. 对这些调用点逐一进行测试和分析,判断哪个是程序启动时执行的主验证流程。
  3. 进入关键的call内部进行分析。通常,验证函数会检查机器码、邮箱地址等信息,并通过返回值(通常存放在EAX寄存器中)判断是否成功。
  4. 我们的目标是让验证函数返回一个表示成功的值(例如0)。可以通过修改验证函数内部的比较指令或直接修改其返回值来实现。

例如,在分析中,我们发现验证函数内部会将EAX0比较。我们可以将test eax, eax之类的指令修改为xor eax, eax,从而强制让EAX0,使后续判断走向成功分支。

; 修改前,EAX值影响跳转
test eax, eax
jnz failed_label

; 修改后,强制EAX=0,确保跳转不实现
xor eax, eax
test eax, eax
jnz failed_label ; 此句将永远不会跳转

尝试制作内存补丁 💉

直接修改程序文件(打补丁)是另一种方法。但有时程序代码段没有足够空间写入新指令,我们可以尝试制作内存补丁。

上一节我们通过修改代码逻辑实现了破解,本节中我们来看看另一种方法——内存补丁。

内存补丁的思路是:在程序运行时,将一小段我们自己的代码注入到进程空间,由这段代码来修改关键的验证逻辑。

以下是基本步骤:

  1. 定位需要修改的指令在内存中的确切地址。
  2. 编写一小段汇编代码,用于修改目标地址的指令(例如,将jnz改为jmp)。
  3. 这段补丁代码需要被插入到程序代码的“空隙”处(即全是0090等无效数据的区域)。
  4. 修改程序入口点或某个早期调用的函数,使其首先执行我们的补丁代码,然后再继续原流程。

然而,这种方法实施起来较为复杂,需要精确计算地址和跳转,并且找到合适的代码空隙也不容易。在本案例的尝试中,由于地址空间问题,未能成功制作出有效的文件补丁。


总结与练习 📝

本节课中我们一起学习了针对带服务器检测的加壳程序的完整破解流程。

我们首先对压缩壳进行了手动脱壳。接着,通过字符串检索定位到验证提示信息,并分析了关键跳转。为了更彻底地破解,我们深入追踪了验证函数的调用,并修改了其内部逻辑,强制返回验证成功。最后,我们还探讨了制作内存补丁的复杂性和挑战。

核心要点总结:

  • 脱壳:观察IAT和ESP变化,使用断点法定位OEP。
  • 定位:利用字符串检索找到程序验证逻辑入口。
  • 分析:逆向关键call和跳转指令,理解验证流程。
  • 修改:通过汇编指令(如xor eax, eax)改变程序执行路径。
  • 公式/代码表示关键修改:
    ; 关键修改:将影响流程的寄存器置零
    mov eax, 0 ; 或 xor eax, eax
    ; 替代了原本可能失败的比较或赋值操作
    

建议课后寻找类似软件进行练习,巩固脱壳、关键点定位和代码修改的技能。也可以尝试下载该软件的新版本,分析其验证机制是否发生变化。

天草高级班 - P6:EXEcryptor 2.x 脱壳与破解教程 🔓

在本节课中,我们将学习如何对一个使用 EXEcryptor 2.x 壳保护的软件进行脱壳与破解。课程将详细演示从识别加壳程序、定位原始入口点(OEP)、修复导入地址表(IAT),到最终绕过注册验证的完整流程。

识别加壳程序与保护机制

上一节我们介绍了课程背景,本节中我们来看看如何识别目标程序及其保护措施。

目标程序是一个压缩软件,使用了 EXEcryptor 2.x 壳进行保护。通过查看其进程内存区段,可以发现其特征。

该壳版本并非最新,但保护机制已足够复杂。它采用了一种名为 Callback Tempo 的技术,通过多次回调检测来干扰调试器(如OD)的正常工作,并完成自身的解密。

使用OD进行动态分析并定位OEP

了解了保护机制后,我们使用OllyDbg(OD)载入程序,开始动态分析。

程序载入后,其保护措施会立即生效。我们首先需要处理其对OD的检测。在内存中定位到关键的回调函数地址 006F010

以下是具体的操作步骤:

  1. 在OD中设置系统断点。由于壳的干扰,每次中断都像是一次性的(One Shot),我们需要删除无关断点。
  2. 在之前记录的 006F010 地址处下断点并中断程序。
  3. 中断后,单步跟踪(F7/F8),关注 CALL 指令和 JMP 指令,逐步接近程序的原始代码。
  4. 经过多次 F9(运行)和跟踪,最终会跳转到一个看起来像正常程序代码的区域,此处即为程序的 原始入口点(OEP)。在本例中,OEP位于类似 00A7xxxx 的地址。

成功定位OEP后,即可进行脱壳。但请注意,这仅仅是开始,后续还有修复工作。

修复导入地址表(IAT)与程序映像

成功脱壳后,我们得到了程序的原始代码,但此时程序还无法运行,因为导入地址表(IAT)被壳修改了。

我们需要使用 Import REC 等工具来修复IAT。

  1. 在Import REC中附加到进程,填入找到的 OEP地址(例如 7940)。
  2. 点击“获取导入表”,软件会自动搜索IAT的起始位置和大小。例如,IAT起始于 68258,大小约为 B9C 字节。
  3. 点击“修复转储”,选择刚才脱壳保存的程序文件。修复工具会生成一个修复后的新文件(通常名为 *_dumped.exe)。

然而,直接运行修复后的程序可能会立刻崩溃或退出。这是因为在OEP处,程序的寄存器堆栈状态与原始加壳程序运行时不一致。程序运行依赖于这些正确的初始环境。

重建程序运行环境

为了解决运行环境问题,我们需要手动重建OEP处的寄存器与堆栈状态。

具体方法是:在OD中运行原加壳程序,在到达OEP时,记录下所有通用寄存器(EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI)的值,以及堆栈顶部的若干个DWORD值。

然后,我们通过编写一小段汇编代码,在修复后的程序开头(例如新建一个代码段)来精确设置这些值。代码逻辑是“反着”将堆栈值压入,再设置寄存器。

例如,重建代码可能类似如下结构(具体值需根据记录修改):

PUSH 12FFA0
PUSH 12FF0C
...
MOV EAX, 12FC04
MOV EBX, 0012F000
...
JMP 真正的OEP地址

这段代码的作用是模拟壳在跳转到OEP前所做的现场布置。

我们可以使用 LordPECFF Explorer 等工具,在修复后的程序中新建一个区段,将上述汇编代码的机器码写入,并将程序的入口点(Entry Point)修改为这个新区段的起始地址。这样,程序会先执行我们的“环境初始化”代码,再跳转到真正的OEP。

完成此步骤后,脱壳修复的程序应能正常运行,但会显示未注册。

分析并绕过注册验证

程序运行后,我们进入破解阶段。该程序采用重启验证,直接跟踪注册流程可能较复杂。

一个技巧是:在注册框中尝试输入,程序可能会生成或校验一个特定的关键值。我们可以在OD中搜索这个错误提示字符串或相关常量来定位关键判断点。

例如,通过搜索,我们定位到一个关键比较指令:

CMP DWORD PTR [71AC70], 00A65D2C

此指令将内存地址 [71AC70] 中的值与固定值 00A65D2C 进行比较。这个固定值很可能就是注册成功与否的“钥匙”。

我们的目标是让这个比较成功。有以下两种思路:

  1. 暴力修改:直接修改该判断处的汇编代码,例如将 CMP 改为 MOV,或强制让跳转实现。
    MOV DWORD PTR [71AC70], 00A65D2C  ; 直接写入正确值
    JMP 验证成功地址                   ; 或强制跳转
    
  2. 追溯算法:查找是哪个函数向 [71AC70] 写入了数据,分析其生成算法。但此壳代码混淆严重,分析难度大。

在本例中,我们采用第一种方法。在关键判断点写入机器码,直接设置正确的比较值并引导程序走向成功分支。写入的机器码需要精确计算,例如:

C6 05 7071AC00 A6
C6 05 7471AC00 5D
C6 05 7871AC00 2C
C6 05 7C71AC00 00

这段代码分四次向 71AC70 开始的地址写入双字 00A65D2C

应用此补丁后,程序的注册验证即被绕过。

总结与注意事项

本节课中,我们一起学习了针对 EXEcryptor 2.x 壳的完整脱壳与破解流程。

  1. 识别与分析:通过内存区段和反调试行为识别目标壳。
  2. 动态脱壳:使用OD绕过反调试,耐心跟踪定位程序的原始入口点(OEP)。
  3. 修复与重建:使用工具修复IAT,并手动编写代码重建程序运行所需的寄存器与堆栈环境,这是使脱壳程序能运行的关键。
  4. 破解验证:通过搜索字符串定位关键判断点,并采用打补丁的方式修改程序逻辑,绕过注册验证。

请注意,课程中使用的一些辅助工具(如特定的内存修改工具)可能会被安全软件报毒,这通常是工具行为引起的误报,请在使用时自行判断。

本课程内容较为综合,涉及脱壳的多个难点,希望你能通过练习掌握其中的思路和方法。

天草高级班 - P7:Patch破解ACProtect(带网络验证) 🔓

在本节课中,我们将学习一种针对带有网络验证的ACProtect壳程序的破解技术。我们将不采用传统的脱壳方法,而是直接通过Patch(打补丁)的方式绕过其网络验证,实现程序的破解。本教程将详细展示整个分析、定位和修改的过程。


课程概述

本节课的目标是破解一个带有网络验证的游戏外挂程序。我们将演示如何在不脱壳的情况下,通过分析程序流程、定位关键验证点,并直接修改内存或文件数据来绕过验证。这种方法对于处理某些复杂的保护壳尤为有效。


尝试传统脱壳

上一节我们介绍了课程目标,本节中我们先来看看如果尝试用传统方法脱壳会遇到什么情况。

我们首先尝试使用脱壳工具处理目标程序。

运行脱壳工具后,程序似乎很快到达了OEP(原始入口点)。

然而,当我们尝试运行修复后的程序时,却发现无法正常工作。

这表明修复过程可能存在问题。我们尝试手动修复导入表。

在修复过程中,我们遇到了一些无效的指针,需要将其修改为正确的函数名,例如 MacOS Pulse

但修复过程并不顺利,某些项无法简单地修复。这说明了完美脱壳的难度。因此,我们决定放弃脱壳,转而采用更直接的Patch方法。


定位关键验证代码

既然脱壳遇到困难,本节中我们来看看如何在不脱壳的情况下,从程序内部定位网络验证的关键跳转。

我们让程序运行起来,并在其内部搜索相关的提示字符串。

以下是搜索字符串的步骤:

  1. 在反汇编窗口中,使用字符串搜索功能。
  2. 首先尝试搜索英文,但本程序包含中文提示。
  3. 我们搜索到了“验证失败”等关键字符串。

在“验证失败”的代码上方,通常存在一个条件跳转指令。这个跳转决定了程序是走向验证成功还是失败的分支。

我们记录下这个关键跳转的地址。对于只有一层网络验证的程序,修改此处跳转往往就能绕过验证。


分析壳的解码循环

上一节我们找到了验证跳转,但那是程序运行后的逻辑。本节中我们来看看如何在程序解码阶段就介入,这是本方法的核心。

我们重新加载程序,并在解码循环处下断点。通过多次 Shift+F9 跳过异常,我们来到了一个循环解码的区域。

这个循环是ACProtect壳进行代码解码的地方。解码完成后,程序才会跳转到OEP执行。这个解码循环的出口,就是我们的突破口。

我们观察到一条关键指令:

Move Word PTR DS:[...], 9090

这条指令会将某个地址的数据修改为 9090(即NOP指令的机器码)。同时,我们注意到一个名为 R1 TM8 的地址需要被考虑进去。


实施Patch

经过前面的分析,我们已经找到了关键点。本节中我们来看看如何制作并应用我们的补丁。

补丁的原理是:在壳的解码阶段,就强制修改内存中验证跳转处的指令,使其直接跳转到验证成功的流程。

以下是制作补丁的步骤:

  1. 使用补丁制作工具(如Loader)。
  2. 在“添加”数据时,填入我们找到的关键地址。
  3. 在地址后需要加上偏移 R1 TM8
  4. 写入的数据是 9090,即两个NOP指令,用于覆盖原有的条件跳转。

如果有多个需要修改的验证点,就重复此步骤添加多个地址。

应用此补丁后运行原程序,补丁会在解码阶段自动修改内存,从而绕过网络验证。


课程总结

本节课中我们一起学习了针对带ACProtect壳及网络验证程序的Patch破解技术。

我们首先尝试了传统脱壳并发现了其难度,进而转向直接分析。通过定位程序内的验证失败字符串,我们找到了关键跳转。更进一步,我们深入壳的解码循环,发现了可以在解码过程中动态修改代码的漏洞。最后,我们利用这个漏洞制作了一个内存补丁,在程序运行时自动将验证跳转修改为NOP,从而成功绕过了网络验证。

这种方法的核心思想是 “以子之矛,攻子之盾”,利用壳自身解码和修改代码的机制,来达成我们的破解目的。它避免了复杂的脱壳和修复过程,对于某些特定保护非常有效。

出品人:宋佩岩

天草高级班 - P8:Acprotect全保护脱壳实战 🛡️➡️📦

在本节课中,我们将学习如何对一个施加了Acprotect(此处原文为S-ProTek/Ace Pro Tech)全保护的软件进行手动脱壳。我们将使用OllyDbg(OD)作为调试工具,并详细讲解定位原始入口点(OEP)、修复被偷取的代码以及重建导入地址表(IAT)的完整过程。

初始设置与异常处理

首先,我们启动目标程序并使用OllyDbg附加进程。为了顺利调试,需要对OD进行一些设置。

以下是需要进行的初始设置步骤:

  • 忽略OD中的所有异常选项。
  • 隐藏调试器,以避免被保护机制检测。

设置完成后,运行程序。OD可能会被强制关闭,这表明保护强度很高。我们重新附加,并确保断点设置成功。

定位被偷取的OEP

上一节我们完成了调试器的设置,本节中我们来看看如何找到真正的程序入口点。通过多次在系统领空(如ntdll模块)中断并返回,我们可以逐步接近OEP。

以下是定位OEP的关键操作:

  1. 在系统API调用处(如图中位置)下断点。
  2. 运行程序(F9)中断后,将寄存器窗口的数值清零zero)。
  3. 重复“运行->清零”的过程数次。
  4. 删除断点,继续运行,程序最终会跳转到被偷取的OEP区域。

此时可以看到,OEP的代码并非原始代码,而是被保护程序修改过的。

分析被破坏的IAT

找到OEP后,下一步是检查程序的导入地址表(IAT)状态。IAT存储了程序调用外部函数(如系统API)的地址,是脱壳后修复的关键。

在内存映射窗口中查看,可以明显发现IAT区域被破坏。原始IAT的地址(例如0x401468)处没有有效数据,这表明保护程序使用了“Stolen Code”和“IAT Redirection”技术。

寻找并修复Stolen Code

为了修复程序,我们需要找到被偷取并转移到其他位置的原始代码片段(Stolen Code)。这需要更精细的跟踪。

以下是寻找Stolen Code的步骤:

  1. 在疑似代码转移的指令(如jmpcall)处下硬件执行断点。
  2. 运行程序,跟踪跳转目标。
  3. 遇到保护程序的反调试陷阱(如对GetTickCount等API的特殊处理)时,通过修改标志位或跳过检查来规避。
  4. 最终会到达一片存放着原始OEP代码的区域,这就是Stolen Code。

找到后,需要将这些二进制代码复制下来。使用OD的二进制复制功能(Copy -> Binary),然后返回到原始的OEP地址,进行二进制粘贴(Binary Paste),覆盖被破坏的代码。

使用工具重建IAT

代码修复后,我们需要修复IAT。这里使用一个专用的修复工具(如Imports Reconstructor)。

以下是使用工具修复IAT的流程:

  1. 在OD中查看进程ID和代码段(.code section)的地址范围。
  2. 打开修复工具,填入当前进程ID。
  3. 在工具的“IAT Info”中,填入原始IAT的地址(如0x401468)和大小。
  4. 点击“Get Imports”按钮,工具会尝试分析并列出所有导入函数。
  5. 如果列表正确,点击“Fix Dump”按钮,并选择之前修复了Stolen Code的程序副本进行修复。

注意:填入的OEP地址(RVA)必须准确,否则修复会失败。如果修复后的程序无法运行,需要检查OEP地址和IAT地址是否正确,并重复上述步骤。

处理修复过程中的问题

修复过程可能不会一次成功。常见问题包括地址填写错误、或运行环境干扰导致地址变化。

例如,在操作过程中,如果发现粘贴的二进制代码有误(如某个字节错误),会导致程序行为异常。此时需要重新跟踪,再次复制正确的Stolen Code。

另一个问题是,运行了屏幕录像软件等外部程序可能导致内存地址发生变化,使得之前找到的地址失效。这种情况下,需要关闭可能干扰的程序,重新开始脱壳流程。

关键是要保持耐心,仔细核对每一步的地址和数据。

最终修复与验证

经过多次尝试和修正,最终成功修复了IAT。修复工具生成了一个可运行的程序副本。

为了验证脱壳是否完全成功,可以使用查壳工具(如PEiDDIE)检查修复后的文件。结果显示为“Microsoft Visual C++”或其他原始编译器信息,表明外壳已被成功去除。

本节课中我们一起学习了手动脱去Acprotect全保护外壳的完整流程。我们主要经历了以下几个阶段:调试器设置与反反调试对抗、定位被偷取的OEP、分析被破坏的IAT、手动修复Stolen Code、使用工具重建IAT,以及处理修复过程中的各种问题。掌握这个流程需要大量的练习和对Windows PE结构的深入理解,希望本教程能为你的学习提供清晰的路径。

天草高级班 - P9:传世VIP 2.31版破解教程 🔓

在本节课中,我们将学习如何对“传世VIP 2.31版”软件进行破解分析。课程将引导你定位关键代码、理解验证逻辑,并最终绕过其充值限制。


课程概述与目标 🎯

本节课的目标是破解“传世VIP 2.31版”。该软件在未充值状态下会提示“账号尚未充值”。我们将通过逆向工程,定位并修改其验证逻辑,实现“白嫖”使用。

软件界面如下:

课程结束后,请记得将系统还原。


初步分析与尝试 🧐

首先,我们尝试使用常规的破解方法。在上一节课程中,我们学习了使用特定工具(如L12堆栈工具)来跳过登录框。本节中,我们来看看这种方法是否适用。

运行程序并附加调试器后,我们定位到程序的入口点(OEP)。然而,我们发现这里出现的并非传统的登录框,而是在成功登录后弹出的另一个验证窗口。

因此,直接跳过登录框的L12堆栈方法在此处无效。我们需要寻找新的突破口。


定位关键验证字符串 🔍

既然无法绕过初始界面,我们需要深入程序内部查找关键线索。上一节我们分析了入口点,本节中我们来看看如何定位验证逻辑。

我们可以在程序的字符串中搜索与登录或充值相关的提示信息。例如,搜索“登陆提示”或“充值”等字符串。

在字符串列表中,我们找到了“登陆提示”。但需要注意的是,程序中有多处“登陆提示”,我们需要找到后面紧接着进行验证判断的那一处。

我们选择最可能的一处“登陆提示”,然后向上查找代码段的起始位置(函数头部)。


分析验证逻辑与数据获取 💻

找到疑似验证函数后,我们需要分析其逻辑。函数会获取我们输入的用户名和密码。这里有一个细节:我上次输入的是大写“CC”,但程序内部可能仍按小写处理。

以下是获取登录信息的代码示意:

; 假设的汇编代码片段,用于获取用户名
mov eax, [user_name_address]

同时,程序会调用一个DLL(动态链接库)来进行进一步的验证或数据交互。我们需要分析这个DLL的作用。

由于记忆有些模糊,我们直接使用调试器附加到进程,并在这个验证函数上下断点,动态跟踪数据的流向和判断条件。


动态调试与关键跳转修改 ⚙️

通过动态调试,我们一步步执行程序。当程序执行到验证函数时,观察寄存器和堆栈的值。

我们发现了一个关键的比较指令(CMP)和紧随其后的条件跳转指令(JE 或 JNZ)。这个跳转决定了程序是走向“充值成功”的流程,还是弹出“未充值”的提示。

我们的目标就是修改这个关键跳转。例如,如果原指令是JNZ(结果不为零则跳转到失败流程),我们可以将其改为JZ(结果为零则跳转),或者直接改为无条件跳转JMP,从而绕过充值检查。

修改指令的示例如下:

; 原指令:若不满足条件则跳转到失败提示
JNZ 0x0045ERROR

![](https://github.com/OpenDocCN/sec-notes-zh/raw/master/docs/tiancao/img/ee7fa145d3e45cb36a32d3979b698b8d_17.png)

; 修改为:无条件跳转到成功流程
JMP 0x0045SUCCESS

在内存中直接修改该指令的机器码,并保存到可执行文件。


测试与验证 ✅

修改完成后,我们保存文件并重新运行程序。使用之前注册但未充值的账号进行登录。

如果破解成功,程序将不再弹出“账号尚未充值”的提示,而是能够正常进入软件的所有功能界面。

请务必进行多项功能测试,以确保破解完整,没有其他暗桩或后续验证。


课程总结 📝

本节课中,我们一起学习了针对“传世VIP 2.31版”的破解流程。

我们首先尝试了常规方法并发现其局限性,进而通过字符串定位法找到关键验证函数。接着,我们使用动态调试技术分析了验证逻辑,并定位到核心的条件跳转指令。最后,通过修改该跳转指令,我们成功绕过了软件的充值验证。

核心破解思路可以总结为:定位验证点 -> 分析判断逻辑 -> 修改关键跳转

请记住,本教程仅用于逆向工程技术学习。请在法律允许的范围内使用这些知识,并尊重软件开发者的劳动成果。

posted @ 2026-02-05 08:55  绝不原创的飞龙  阅读(4)  评论(0)    收藏  举报