深入底层逆向分析TDC‘s keygenme(手脱压缩壳)
系统 : Windows xp
程序 : TDC‘s keygenme
程序下载地址 :http://pan.baidu.com/s/1gdWyt6z
要求 : 脱壳 & 注册机编写
使用工具 :OD & IDA & PEID & LordPE & Import REC
可在“PEDIY CrackMe 2007”中查找关于此程序的分析,标题为“TDC.Crackme10算法分析”。
首先了解一下壳是什么:
作者编好软件后,编译成exe可执行文件。
1.有一些版权信息需要保护起来,不想让别人随便改动,如作者的姓名等,即为了保护软件不被破解,通常都是采用加壳来进行保护。
2.需要把程序搞的小一点,从而方便使用。于是,需要用到一些软件,它们能将exe可执行文件压缩,
3.在黑客界给木马等软件加壳脱壳以躲避杀毒软件。
--源自百度百科
简单说来,壳就是用来防止破解者对于程序文件的非法修改。壳又分为压缩壳、加密壳。他们的应用范围也不尽相同,压缩壳主要用来压缩程序文件的体积,对于破解者来说强度不大。而加密壳以加密保护为重点,用尽了各种反跟踪技术,保护重点是在OEP(程序入口点)的隐藏和IAT(输入表)的加密上。
这篇博文中我们来尝试破解一个被压缩壳保护、带有简单反调试的程序。
将程序拖入PEID查看状态:
显示该程序加了upx壳,可以利用网上的脱壳机来脱壳。这里,为了加深印象,我们尝试手动脱壳。od载入程序,断在外壳处:
> $ pushad
. BE mov esi,
. 8DBE 0090FDFF lea edi, dword ptr [esi+FFFD9000]
0044633C . push edi
0044633D . 83CD FF or ebp, FFFFFFFF
. EB jmp short
单步执行pushad之后,esp指向0012FFA4 ,键入命令hr 12FFA4下硬件断点,F9运行程序断在此处
.- E9 73ABFBFF jmp 0040100B
单步执行跳转进入OEP:
0040100B 6A 0A push 0A
0040100D B90B0000 push 0BB9
FF35 push dword ptr []
E8 call ; jmp to kernel32.FindResourceA
0040101D A3 1C624000 mov dword ptr [40621C], eax
FF35 1C624000 push dword ptr [40621C]
FF35 push dword ptr []
0040102E E8 call 0040133A ; jmp to kernel32.LoadResource
A3 mov dword ptr [], eax
E8 C3FFFFFF call
0040103D FF35 1C624000 push dword ptr [40621C]
FF35 push dword ptr []
E8 F8020000 call ; jmp to kernel32.SizeofResource
0040104E A3 mov dword ptr [], eax
FF35 push dword ptr []
E8 E2020000 call ; jmp to kernel32.SetHandleCount
0040105E 8BF0 mov esi, eax
A1 mov eax, dword ptr []
83C0 add eax,
push eax
6A push
0040106B E8 C4020000 call ; jmp to kernel32.GlobalAlloc
A3 mov dword ptr [], eax
此时单击菜单Debug->hardware breakpoints删除之前设置的硬件断点。
打开LordPE选择keygenme并单击右键选择“完整转存”:
保存dump文件之后,再打开输入表重建工具Import REC附加到keygenme:
填写OEP为“0000100B”,依次单击“自动查找IAT”、“获取输入表”:
最后,单击“修复转存文件”,选中之前的dump文件,则脱壳成功。
脱壳成功之后我们用IDA载入程序,shift + F12查看字符串表:
发现提示成功的字串“Congratulations! You are now level 2! :-)”,双击定位,通过交叉引用来到调用它的位置并向上翻找出关键算法:
TDC0: cmp dword ptr [ebp+0Ch], 111h
TDC0: jnz loc_401292
TDC0:0040118E cmp dword ptr [ebp+10h], 3E9h
TDC0: jnz loc_40125C
TDC0:0040119B push 33h
TDC0:0040119D push offset dword_40622C
TDC0:004011A2 push 7D1h
TDC0:004011A7 push dword ptr [ebp+]
TDC0:004011AA call GetDlgItemTextA
TDC0:004011AF imul eax,
TDC0:004011B2 cmp eax, 0FAh
TDC0:004011B7 ja loc_401246
TDC0:004011BD cmp eax, 1Eh
TDC0:004011C0 jb loc_401246
TDC0:004011C6 push
TDC0:004011C8 push
TDC0:004011CA push 7D2h
TDC0:004011CF push dword ptr [ebp+]
TDC0:004011D2 call GetDlgItemInt
;省略一部分关键代码:
这里,选中关键代码,单击“视图”->"图表"->"流程图"显示关键算法的流程:
可以直观的看出,关键算法处用了不少分支语句,程序中肯定加了不少对输入的判断。
打开OD载入程序,输入命令"bp 004011AA",F9运行,程序断在此处:
0040119B 6A push
0040119D 2C624000 push 0040622C ; ASCII "pediy"
004011A2 D1070000 push 7D1
004011A7 FF75 push dword ptr [ebp+]
004011AA E8 AF010000 call <jmp.&user32.GetDlgItemTextA>
004011AF 6BC0 imul eax, eax, ; 用户名字串长度*5
004011B2 3D FA000000 cmp eax, 0FA ; 高于0xFA?
004011B7 0F87 ja ; 提示Name must be between 5 and 51 length
004011BD 83F8 1E cmp eax, 1E ; 低于0x1E?
004011C0 0F82 jb ; 提示Name must be between 5 and 51 length
004011C6 6A push
004011C8 6A push
004011CA D2070000 push 7D2
004011CF FF75 push dword ptr [ebp+]
004011D2 E8 call <jmp.&user32.GetDlgItemInt> ; 获取序列号
004011D7 A3 5F624000 mov dword ptr [40625F], eax ; 并保存
004011DC 2C624000 push 0040622C ; 用户名入栈
004011E1 E8 C2000000 call 004012A8
跟入4012A8:
004012A8 push ebp
004012A9 8BEC mov ebp, esp
004012AB 803D B4124000 C>cmp byte ptr [4012B4], 0CC ; 检测断点,发现则跳转出错
004012B2 je short
004012B4 33C0 xor eax, eax
004012B6 33C9 xor ecx, ecx
004012B8 803D D4124000 C>cmp byte ptr [4012D4], 0CC ; 检测断点,发现则跳转出错
004012BF je short
004012C1 33D2 xor edx, edx
004012C3 8B55 mov edx, dword ptr [ebp+] ; 取用户名字串
004012C6 8A02 mov al, byte ptr [edx] ; 迭代字串
004012C8 84C0 test al, al ; 迭代完毕?
004012CA je short 004012D4 ; 则跳转
004012CC 03C8 add ecx, eax ; 累加
004012CE C1C1 rol ecx, ; 循环左移8位
004012D1 inc edx ; 循环变量自增
004012D2 ^ EB F2 jmp short 004012C6
004012D4 83F1 xor ecx, ; 结果与2异或
004012D7 83E9 sub ecx,
004012DA 81F1 xor ecx, ; 结果与1337异或
004012E0 push esi
004012E1 :8B35 >mov si, word ptr [] ; 获取当前系统年份
004012E8 :03CE add cx, si ; 加入cx
004012EB 803D FA124000 C>cmp byte ptr [4012FA], 0CC ; 检测断点,发现则跳转出错
004012F2 je short
004012F4 5E pop esi
004012F5 A1 5F624000 mov eax, dword ptr [40625F] ; 获取序列号
004012FA 3BC1 cmp eax, ecx ; 与F(用户名)的结果是否一致?
004012FC jnz short ; 否则置406263为1
004012FE 33C0 xor eax, eax
EB 0E jmp short
33C0 xor eax, eax
inc eax
EB jmp short
C605 >mov byte ptr [],
0040130E EB jmp short
C605 >mov byte ptr [],
C9 leave
C2 retn
该子程序布下了简单的反调试,众所周知,断点的原理是将指定地址的指令替换为int 3中断,int 3的十六进制表示为0CC。通过实时检测载入内存的指令是否是0CC,就可以判断自身有没有被调试。在不改动程序文件的条件下,我们只能尽量少下断点,避免触发反调试。
回到剩下的关键算法:
004011E6 803D >cmp byte ptr [],
004011ED je short
004011EF 85C0 test eax, eax
004011F1 jnz short 0040120A
004011F3 push ; ASCII "Congratulations! You are now level 2! :-)"
004011F8 D2070000 push 7D2
004011FD FF75 push dword ptr [ebp+]
E8 call <jmp.&user32.SetDlgItemTextA>
E9 jmp 004012A2
0040120A 803D 1C124000 C>cmp byte ptr [40121C], 0CC
1F je short
803D C>cmp byte ptr [], 0CC
0040121A je short
0040121C 6A push
0040121E D9604000 push 004060D9 ; ASCII "Sorry, that is incorrect my mate. Try again."
D2070000 push 7D2
FF75 push dword ptr [ebp+]
0040122B E8 4C010000 call <jmp.&user32.SetDlgItemTextA>
EB jmp short 004012A2
push ; ASCII "Debugger detected, check cancelled!"
D2070000 push 7D2
0040123C FF75 push dword ptr [ebp+]
0040123F E8 call <jmp.&user32.SetDlgItemTextA>
EB 5C jmp short 004012A2
push ; ASCII "Name must be between 5 and 51 length"
0040124B D2070000 push 7D2
FF75 push dword ptr [ebp+]
E8 call <jmp.&user32.SetDlgItemTextA>
至此,程序算法流程已经分析完毕。马上动手编写注册机。
我们直接打开http://www.cnblogs.com/ZRBYYXDM/p/5115596.html中搭建的框架,并修改OnBtnDecrypt函数如下:
void CKengen_TemplateDlg::OnBtnDecrypt()
{
// TODO: Add your control notification handler code here
CString str;
GetDlgItemText( IDC_EDIT_NAME,str ); //获取用户名字串基本信息。
int len = str.GetLength(); if ( len <= && len >= ){ //格式控制。
unsigned int sum = ; for ( int i = ; i != len ; i++ ){
sum += str[i];
sum = ( sum >> ( - ) ) | ( sum << ); //循环左移8位
} sum = ( (sum ^ ) - 0x50 ) ^ 0x1337; SYSTEMTIME st;
CString strDate,strTime;
GetLocalTime(&st); //获取系统时间
int year = st.wYear; __asm{ //为了防止进位内嵌汇编进行低位相加。
push eax;
push ebx; mov eax,year;
mov ebx,sum; add bx,ax;
mov sum,ebx; pop ebx;
pop eax;
} CString PassWord;
PassWord.Format( "%u",sum );
SetDlgItemText( IDC_EDIT_PASSWORD,PassWord );
}
else
MessageBox( "用户名格式错误!" );
再在OnInitDialog中添加此代码修改标题:SetWindowText(_T("TDC‘s keygenme_Keygen"));
运行效果:
至此,TDC‘s keygenme的破解就完成了。
PS:情人节到了,愿天下情侣可以天长地久,也希望和我一样的单身汪不要气馁,多多努力,争取早日脱单。
祝诸君,胸中沟壑自成!
深入底层逆向分析TDC‘s keygenme(手脱压缩壳)的更多相关文章
- 简单脱壳教程笔记(2)---手脱UPX壳(1)
本笔记是针对ximo早期发的脱壳基础视频教程,整理的笔记. ximo早期发的脱壳基础视频教程 下载地址如下: http://down.52pojie.cn/%E5%90%BE%E7%88%B1%E7% ...
- 【个人笔记】ximo早期发的脱壳教程——手脱UPX壳
[个人笔记]ximo早期发的脱壳教程--手脱UPX壳 壳分为两种:压缩壳和加密壳,UPX是一种很简单的压缩壳. 手脱UPX壳: 工具:ExeinfoPE.OD 对象:rmvbfix 方法1:单 ...
- 填坑专记-手脱FSG壳
妈呀,脱FGS壳真的是坎坷颇多,多亏吾爱破解前辈们的帮忙.我一定要记录下来,省的以后再无法解决. 已经查看是FSG壳了.找到入口也容易了.重点就是脱壳并修复好它. 脱壳 OEP为: 使 ...
- 简单脱壳教程笔记(8)---手脱EZIP壳
本笔记是针对ximo早期发的脱壳基础视频教程,整理的笔记.本笔记用到的工具下载地址: http://download.csdn.net/detail/obuyiseng/9466056 EZIP壳 : ...
- <逆向学习第三天>手动脱FSG壳,修复IAT。
其实对于简单的壳来说,脱壳常用的方法也无非是那几种,但是每种有每种的好处,具体使用那种方法视情况而定,我今天学习的这个壳很简单,但是重点在于修复IAT. 一.查壳: FSG 2.0的壳. 二.脱壳: ...
- 手脱UPX壳的方法
0X00 了解 upx UPX作为一款元老级PE加密壳,在以前的那个年代盛行,著名病毒[熊猫烧香]就是使用这款加密壳. 0X01 单步跟踪法 就是使用ollydbg加载程序后,按F8进行单 ...
- 手脱无名壳tslgame_rl
1.使用Detect It Easy查壳,该壳未显示出壳信息,至于为何有壳,我们使用IDA打开,查看其的导入表,其中没有太多函数使用: 2.我们使用x32dbg打开,运行至入口点,此处没有pushad ...
- 手脱PESpin壳【06.exe】
1.查壳 2.LoradPE工具检查 一方面可以用LoradPE工具查看重定位,另一方面也可获取一些详细信息 3.查找OEP ①未发现pushad 开始未发现pushad,进行单步步入,很快就能找到p ...
- 手脱NsPacK壳
1.查壳 使用PEiD未能检测到壳信息,这时,我们更换其他工具 从图中可以看到壳的信息为[NsPacK(3.x)[-]] 2.百度壳信息 北斗程序压缩(Nspack)是一款压缩壳.主要的选项是:压缩资 ...
随机推荐
- struts2整合CKEditor和CKFinder实现上传
上一篇文章给大家分享了CKEditor+CKFinder+JSP实现了在线编辑器上传图片的功能,这里在给大家分享一下如何在前面的基础上在struts2下实现这样的功能. 实现与Struts2的整合,整 ...
- jdbc根据实例名 连接 sql server
jdbc:sqlserver://PC;instanceName=sql2012;databaseName=xxxxx
- Entityframework 事务
Working with Transactions (EF6 Onwards) This document will describe using transactions in EF6 includ ...
- 封装Js事件代理方法
// 封装事件代理 function delegateEvent(element, tag, event, listener) { // 判断是否支持addEventlistener if(eleme ...
- codeforces C. Bits(数学题+或运算)
题意:给定一个区间,求区间中的一个数,这个数表示成二进制的时候,数字1的个数最多! 如果有多个这样的数字,输出最小的那个! 思路:对左区间的这个数lx的二进制 从右往左将0变成1,直到lx的值大于右区 ...
- UVAoj 11324 - The Largest Clique(tarjan + dp)
题意:给定一个有向图,寻找一个点数最大集合,使得这个集合中的任意两个点 u,v, 都有u->v 或者 v->u 或者u<==>v 思路:首先将强连通分量通过tarjan算法求出 ...
- 高手详解SQL性能优化十条建议
1.查询的模糊匹配 尽量避免在一个复杂查询里面使用 LIKE '%parm1%'—— 红色标识位置的百分号会导致相关列的索引无法使用,最好不要用. 解决办法: 其实只需要对该脚本略做改进,查询速度便 ...
- rabbitmq使用dead letter机制来进行retry
首先建立 工作exchange和工作queue,指定工作队列的x-dead-letter-exchange到重试exchenge var workQueueArgs = new Dictionary& ...
- UML系列05之 基本流程图
概要 软件的基本流程图是我们在学习编程时的必修课,它很简单,却很实用.需要说明的是,UML并不包括软件的基本流程图,但是为了方便我自己查阅,所以将基本软件流程图归纳到UML系列当中.读者切不要认为基本 ...
- ASP.NET中WebService的两种身份验证方法
一.通过SOAP Header身份验证 此方法是通过设置SOAP Header信息来验证身份,主要通过以下几步: 1.在服务端实现一个SOAP Header类 public class Credent ...