羽夏逆向指引——破解第一个程序
2021-11-20 15:14
501 查看
写在前面
此系列是本人一个字一个字码出来的,包括示例和实验截图。可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。
你如果是从中间插过来看的,请仔细阅读 羽夏逆向指引——序 ,方便学习本教程。
小白鼠
本次示例使用
C++,也就是编译型语言,为了方便查看汇编代码和编码故使用
VS 2022,其他
IDE即可,代码如下:
#include <iostream> using namespace std; int main(int argc, char* argv[]) { int x = 0; cout << "请输入密钥:" << endl; cin >> x; if (x == 1234) { cout << "成功,By.寂静的羽夏,CNBLOG Only!!!" << endl; } else { cout << "失败,By.寂静的羽夏,CNBLOG Only!!!" << endl; } system("pause"); return 0; }
是不是代码很简单?判断你输入的是不是
1234,是的话就是成功,反之失败。这是最简单的校验示例了。
分析
代码的流程十分简单,我们可以用下面的流程图进行表示:
graph TD; A("程序开始")-->B("显示信息,提示输入")-->C[/"获取输入"/]-->D{"判断是否和 1234 相等"}; D--YES-->E("输出正确信息")-->F("程序结束"); D-.NO.->G("输出错误信息")-->F;由于是初次分析编译型程序,我们直接在
IDE先看看它的反汇编:
#include <iostream> using namespace std; int main(int argc, char* argv[]) { 00E42550 push ebp 00E42551 mov ebp,esp 00E42553 sub esp,0D0h 00E42559 push ebx 00E4255A push esi 00E4255B push edi 00E4255C lea edi,[ebp-10h] 00E4255F mov ecx,4 00E42564 mov eax,0CCCCCCCCh 00E42569 rep stos dword ptr es:[edi] 00E4256B mov eax,dword ptr [__security_cookie (0E4C004h)] 00E42570 xor eax,ebp 00E42572 mov dword ptr [ebp-4],eax 00E42575 mov ecx,offset _3226632D_ConsoleApplication3@cpp (0E4F066h) 00E4257A call @__CheckForDebuggerJustMyCode@4 (0E41384h) int x = 0; 00E4257F mov dword ptr [x],0 cout << "请输入密钥:" << endl; 00E42586 mov esi,esp 00E42588 push offset std::endl<char,std::char_traits<char> > (0E4103Ch) 00E4258D push offset string "\xc7\xeb\xca\xe4\xc8\xeb\xc3\xdc\xd4\xbf\xa3\xba" (0E49B30h) 00E42592 mov eax,dword ptr [__imp_std::cout (0E4D0DCh)] 00E42597 push eax 00E42598 call std::operator<<<std::char_traits<char> > (0E411A9h) 00E4259D add esp,8 00E425A0 mov ecx,eax 00E425A2 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0E4D0A8h)] 00E425A8 cmp esi,esp 00E425AA call __RTC_CheckEsp (0E4128Fh) cin >> x; 00E425AF mov esi,esp 00E425B1 lea eax,[x] 00E425B4 push eax 00E425B5 mov ecx,dword ptr [__imp_std::cin (0E4D098h)] 00E425BB call dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (0E4D09Ch)] 00E425C1 cmp esi,esp 00E425C3 call __RTC_CheckEsp (0E4128Fh) if (x == 1234) 00E425C8 cmp dword ptr [x],4D2h 00E425CF jne __$EncStackInitStart+0A0h (0E425FCh) { cout << "成功,By.寂静的羽夏,CNBLOG Only!!!" << endl; 00E425D1 mov esi,esp 00E425D3 push offset std::endl<char,std::char_traits<char> > (0E4103Ch) 00E425D8 push offset string "\xb3\xc9\xb9\xa6\xa3\xacBy.\xbc\xc5\xbe\xb2\xb5\xc4\xd3\xf0\xcf\xc4\xa3\xacCNBLOG Onl@"... (0E49B64h) 00E425DD mov eax,dword ptr [__imp_std::cout (0E4D0DCh)] 00E425E2 push eax 00E425E3 call std::operator<<<std::char_traits<char> > (0E411A9h) 00E425E8 add esp,8 00E425EB mov ecx,eax 00E425ED call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0E4D0A8h)] 00E425F3 cmp esi,esp 00E425F5 call __RTC_CheckEsp (0E4128Fh) } 00E425FA jmp __$EncStackInitStart+0C9h (0E42625h) else { cout << "失败,By.寂静的羽夏,CNBLOG Only!!!" << endl; 00E425FC mov esi,esp 00E425FE push offset std::endl<char,std::char_traits<char> > (0E4103Ch) 00E42603 push offset string "\xca\xa7\xb0\xdc\xa3\xacBy.\xbc\xc5\xbe\xb2\xb5\xc4\xd3\xf0\xcf\xc4\xa3\xacCNBLOG Onl@"... (0E49D40h) 00E42608 mov eax,dword ptr [__imp_std::cout (0E4D0DCh)] 00E4260D push eax 00E4260E call std::operator<<<std::char_traits<char> > (0E411A9h) 00E42613 add esp,8 00E42616 mov ecx,eax 00E42618 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0E4D0A8h)] 00E4261E cmp esi,esp 00E42620 call __RTC_CheckEsp (0E4128Fh) } system("pause"); 00E42625 mov esi,esp 00E42627 push offset string "pause" (0E49B40h) 00E4262C call dword ptr [__imp__system (0E4D1D4h)] 00E42632 add esp,4 00E42635 cmp esi,esp 00E42637 call __RTC_CheckEsp (0E4128Fh) return 0; 00E4263C xor eax,eax }
看不懂汇编的意思的,请自学汇编。看不懂整体汇编语言的作用,请学习我的教程 羽夏看C语言 即可看懂。 输出信息最关键的当然是判断了,也就是下面这个部分:
if (x == 1234) 00E425C8 cmp dword ptr [x],4D2h 00E425CF jne __$EncStackInitStart+0A0h (0E425FCh)
如果我把
jne给干掉,无论我输入什么,都会输出正确。也就是我们把判断失败的链条给砍断了,只能走这条路。 graph TD; A("程序开始")-->B("显示信息,提示输入")-->C[/"获取输入"/]-->D{"判断是否和 1234 相等"}; D--->E("输出正确信息")-->F("程序结束"); G("输出错误信息")-->F("程序结束");
上面只改汇编代码的,通过修改汇编以修改代码执行流程实现自己的目的,就是我们所谓的暴力破解,也就是
patch,俗称打补丁。如果我逆向分析得到密钥就是
1234,直接输入,这东西也就是所谓的
key。当然,现实中验证不可能这么简单,它们往往是通过某种算法,通过获取计算机独特的信息生成注册码(如果我们能够知道算法是什么,根据算法来写出算
key的软件,俗称
注册机),然后根据校验函数进行校验,大大增大了分析难度。这就是说为什么爆破比注册机简单很多。 但是爆破也不是那么轻松的,如果在校验函数中生成了大量的中间结果,如果它再拿来这东西校验的话,被破解的难度就会提高一个层次。发现被破解,直接退出程序,这往往是付费软件防破解的一个手段。 当然,我们破解软件的时候不可能是拿到别人的源码按照本篇文章开头那样进行分析。我们来真枪实战一下。假设这个软件是我们未知的,如何分析这个程序。
小试牛刀
本小节是按照常规套路进行分析,分析不同类型的软件可能流程不太一样,比如分析病毒你肯定不能直接在真机上跑,有些病毒还有反虚拟机的模块,甚至变形多态;编译型代码软件分析和解释型代码软件分析也不太一样;只是分支操作有所不同。接下来是编译型软件的常规操作:
查壳,看看是啥软件编写的:
直接拖到
IDA瞅一瞅,如果没报错,说明软件可以直接分析。如果报错,说明软件被加壳或者加密。当然这软件没有加壳,所以正常,由于软件短小精悍,我们很快定位到主函数部分,下面是它的流程:
看到汇编代码后,
IDA会有一些注释,看下图:
这个明明是字符串,但
IDA没有识别出来。这个是经常会遇到的情况。有时候
IDA识别不出
Unicode字符串,甚至识别错误以为是函数偏移,不要仅依赖这个软件。那么我们如何处理呢?把光标放到上面字符串的首地址,按下
ALT + A,将会弹出下面的窗体:
这个就是将数据转化为字符串,由于这个是普通的
ASCII字符串,直接选
C-style即可。
转换成功后结果如下:
听说
IDA有一个
F5的功能,的确,它是
IDA的一个插件。如果没有选择调试器的情况下,它会默认调用
Hex-ray这个插件翻译成
伪C代码。但是不要过度依赖这个插件,因为翻译出来的东西很多是不准确的,甚至是错误的。比如函数调用的参数个数、多个变量其实就是一个变量、类型不对等等。这些东西都需要依赖自身的开发经验和分析进行调整。经过分析和重命名后,如下所示:
通过分析,我们既得到了
Key,也分析透了函数流程。
暴力破解
IDA其实可以
patch软件的,怎么搞自行学习,比较麻烦,一旦
patch就会导致原来的分析流程变化,自认为不太适用,如果用于去除花指令的话另当别论。在动态调试器里修改是最快捷的,最舒服的方式。如下的动图进行演示:
结语
学习本篇指引,如果
IDA不会使用的话,请自行找教程进行练习。本系列教程只是个指引,故不提供
IDA使用教程。
IDA有如下基本操作:函数和变量的重命名、跳转、类型转化、搜索字符串、查找引用是最常用的操作了,请自行学习。
下一篇
羽夏逆向指引——补丁
相关文章推荐
- Android逆向笔记(2)--- 破解第一个程序
- Android逆向实例笔记—破解第一个Android程序_crackme02
- 逆向工程入门破解一个小程序,可作为第一个练手的小项目
- 看雪加密解密第一个traceme程序破解
- 使用ollydbg破解第一个小程序
- 分析Android程序之破解第一个程序
- 自用Android程序破解,逆向分析工具集
- Android逆向实例笔记—续力破解三个Android程序
- 逆向破解——程序去除自校验
- [.NET逆向] 新手破解.NET程序
- 羽夏逆向破解日记簿——关于逆向epub格式转化器与思考
- 【Android SDK程序逆向分析与破解系列】之四:Android可执行程序ODEX分析
- 【Android SDK程序逆向分析与破解系列】之一:Android安装程序APK分析
- 逆向小程序破解js-(逆向篇)
- 使用天乐软件加密狗(JDProtect)保护您的软件,防止程序被跟踪/逆向/反编译/破解
- 【Android SDK程序逆向分析与破解系列】之二:Android可执行程序DEX分析(一)
- 逆向破解程序脱壳篇-压缩壳
- 使用天乐软件加密狗(JDProtect)保护您的软件,防止程序被跟踪/逆向/反编译/破解
- 自用Android程序破解,逆向分析工具集
- 【Android SDK程序逆向分析与破解系列】之五:Android APK的静态分析