【文章标题】: 关于时间限制的拆除
【文章作者】: kanghtta
【作者主页】: http://kanghtta.cublog.cn
【下载地址】: 见附件
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
废话
大家好,好久不见了! 前段时间朋友让我CRACK一个国产软件,和软件的作者交流了一下,由于征得他的同意,就开始破了
VB写的,运行5分钟就自动退出,由于对反编译的p-code不太熟悉,就把它爆破了!
可这两天又从网上下了一个,一开始就遇上用易语言写的壳,好不容易把这层乌龟盖那了,又遇上异常处理,附加数据,
反跟踪,这些内容都不会阿,怎么办啊,学阿! 所以crackme就做的少了点。不过也打算潜水段时间来把操作系统的,内存管理,进程管理
还有文件,和I/o等知识好好深入一下,自我感觉破解的过程中如果你对这些知识有一个深度的掌握,更能让你得心应手。
正文:
今天写的是关于时间限制的拆除,我用的是段大哥出的 《加密与解密》第二版,首先声明,本文大部分内容来源于这本书中,
只不过是当作小弟的学习笔记写出来;也算是对所学知识的一种总结和巩固;
理论:
1:)时间限制
当我们运行某个没注册的软件时,可能会发现它会给你一个提示,意味着你使用的是未注册版本;大一点的软件一般让你用
30天,卡巴 的试用 也算是一种时间限制吧! 它在你使用了30天后,会提示你重新注册,不过有个办法可以绕过这个限制,
就是找到相关的注册表键值,删了就可以! 其它小一点的软件,可能会让你运行几分钟后,就将程序杀了!
总的来说,时间限制有两类:
第一种: 限制你每次运行的时间,比如:每次你只能使用目标程序几分钟,或几个小时;
第二种:不限制你每次运行的时间,但是有个时间段的限制, 如上面说的卡巴;
2:)关于时间限制的保护方式
程序在第一次运行的时候,或在安装的时候,取得当前系统日期,并且将它记录在系统中的某个地方;程序在每次运行时
都要取系统时间,并将其和记录下的时间进行比较,当其差值大于某个约定的时间后就停止运行,并提示用户注册;
3:)定时器
有时间限制的程序,自然有一个定时器统计程序运行的时间。
那么什么是定时器以及如何使用它?
一个内在的例行程序,只要指定的时间间隔过去,系统就发送一个WM_TIMER消息到对应的消息队列中;它在程序运行时
不可见的;
下面是罗云兵的 win32 程序设计书中有关定时器的一部分:
Windows 的定时器是一种输入设备,它周期性地在指定的间隔时间通知应用程序。它可以用向指定窗口发送 WM_TIMER 消息或者调用指定的过程来执行用户的程序。定时器的应用主要包括下面一些地方:
时钟程序 - 显然,这是定时器最直接的应用。
多任务 - 如果程序有大量的数据处理,除了用多线程的办法,还可以用定时器,在每一个定时器消息中处理一小块内容。
定时显示程序的状况 - 定时器就相当于 Dos 编程中的自己挂接在 int 1ch 上面的要定时处理的程序,它可以定时显示程序运行的情况,如发送了多少内容,接收了多到内容等等。
在游戏程序中使用定时器可以消除在不同处理器下用延时来保持速度一致所造成的误差。
用于数据流处理 - 在音频、视频的播放中,需要隔一段时间处理一段数据。
总的来说,在 Dos 下实现精确定时的唯一方法是在 int 1ch 时钟中断中处理程序,但你使用起来必须遵守很多的规范,而在 Windows 的定时器中,你可以用 SetTimer 函数分配不止一个的定时器,比如说,在你的文本编辑程序中,你可以使用一个间隔1秒的定时器来在状态栏中显示时钟,同时分配一个10分钟的定时器来实现定时存盘的功能。定时器实际上是 Windows 对时钟中断的一种扩展,它的本质还是基于时钟中断的,所以你实际上无法把定时器的间隔设置到55毫秒以下,另外,定时器的精度也是以55毫秒为倍数的,比如说,你设置了一个1秒的定时器,它实际上是在每989毫秒的时候发生的。和在 Dos 下使用时钟中断,windows 的定时器还有下面一些要点:
在 Dos 中,你的程序随时可能被 int 1ch 打断,而在Windows 中,Windows 通过 WM_TIMER 消息把定时器消息放入正常的消息队列中,所以你不必担心你的程序在别的处理中被定时器打断。
不可能有同时两条以上的 WM_TIMER 消息,如果在一个还在消息队列中,窗口再得到一条 WM_TIMER 消息,两条消息会被合并为一条,所以在程序比较忙的时候可能会丢失 WM_TIMER 消息。
WM_TIMER 消息的级别是很低的,程序只有在消息队列中没有其他消息的情况下,才会接收 WM_TIMER 消息,你可以通过下马方法验证:在一个设置了定时器的窗口上按住标题栏移动窗口,你会发现定时器停止了工作,当你松开鼠标后,在这个过程中丢失的 WM_TIMER 消息并没有被补上,所以如果你设计一个时钟程序,你不能使用定时器消息来计数,而必须在消息中每次获取正确的系统时间。
讲了这么多定时器的特点,下面是定时器相关的API,你会发现除了在使用中要注意的这些特性,定时器的API真是又少又简单:
建立定时器
SetTimer(
HWND hWnd, // handle of window for timer messages
UINT nIDEvent, // timer identifier
UINT uElapse, // time-out value
TIMERPROC lpTimerFunc // address of timer procedure
);
hWnd 是 windows 发送 WM_TIMER 的窗口,nIDEvent 是定时器的编号,在 WM_TIMER 中出现在 wParam 参数中,用来区分在多个定时器的情况下,这条消息是由哪个定时器产生的。uElapse 是定时器间隔的毫秒数,如果你要设置一个1秒的定时器,这个值就是1000,lpTimerFunc 是处理定时器消息的过程,如果这个参数不是 NULL,windows 在到时间后会调用lpTimerFunc 指定的过程,调用的参数是 CALLBACK TimerProc(hwnd,WM_TIMER,iTimerID,dwTime),iTimerID 是定时器 ID,dwTime 是系统时间;如果 lpTimerFunc 参数是 NULL,Windows 会把 WM_TIMER 消息放入消息循环中,消息的 hWnd 是第一个参数中指定的 hWnd,也就是说向这个窗口发送了 WM_TIMER 消息。
另外,如果你的程序没有窗口,你也可以用这种办法建立定时器:invoke SetTimer,NULL,NULL,uElapse,TimerProc,函数会返回一个系统定义的 TimerID供你在 KillTimer 中使用。
取消定时器
KillTimer(
HWND hWnd, // handle of window that installed timer
UINT uIDEvent // timer identifier
);
取消定时器只需对应 SetTimer 时的 hWnd 和 uIDEvent 调用 KillTimer 函数就行了。
4: ) 获取时间的API函数
取得时间的API函数一般有:
一:GetSystemTime
The GetSystemTime function retrieves the current system date and time. The system time is expressed in Coordinated Universal Time (UTC).
GetSysTemTime函数获取当前系统日期和时间.系统时间基于格林威治时间表示.
VOID GetSystemTime(
LPSYSTEMTIME lpSystemTime // address of system time structure
);
Parameters
参数: lpSystemTime
Pointer to a SYSTEMTIME structure to receive the current system date and time.
指向一个返回当前系统日期和时间的SYSTEMTIME结构指针;
Return Values: 函数无返回值;
二:GetLocalTime
The GetLocalTime function retrieves the current local date and time.
GetLocalTime函数返回本地当前的日期和时间;
VOID GetLocalTime(
LPSYSTEMTIME lpSystemTime // address of system time structure; 参数同上
);
三:GetFileTime
GetFileTime
函数返回文件被创建或是最后一次被存取和修改的日期和时间;
BOOL GetFileTime(
HANDLE hFile, // handle to the file 文件句柄
LPFILETIME lpCreationTime, // address of creation time 创建文件的FILETIME结构体地址
LPFILETIME lpLastAccessTime, // address of last access time 最后存取[访问]文件的FILETIME结构体地址
LPFILETIME lpLastWriteTime // address of last write time 最近写入时间的FILETIME结构体地址
);
四: 软件作者也可能用高级语言封装的类来操作系统时间,但封装的类同样要调用这几个函数;
还有一种方法就是读取需要频繁访问的系统文件的最后修改日期,在利用FileTimeToSystemTime()将其转换为系统时间格式;
从而取得当前系统时间;
注意:如果你打算在你的软件中打算用时间保护,那么你的软件必须要能防RegMon FIleMOn之类的监视软件,
否则,拆除这种时间保护将会变得相当容易;
实践练手:
首先照本宣科,由于时间保护的软件我机子里库存不多,就用下看雪老大光盘里的:timer.exe
1: 运行下程序看看,有两个文本框:
第一个: 演示版本将运行20秒,20秒后它将自己关闭,你需要重新运行它;
第二个: 这是一个演示时间限制的crackme版本
2: 用PEid查看
什么都没找到 * 嘿嘿,,,
3:OD载入看看,ctrl+N 看看有没有我们上面所说的那些函数;
名称位于 Timer
地址 区段 类型 ( 名称 注释
00402024 .rdata 输入 ( USER32.DestroyWindow ;销毁窗口
00402018 .rdata 输入 ( USER32.DialogBoxParamA :对话框创建
00402010 .rdata 输入 ( USER32.EndDialog : 结束对话框
00402004 .rdata 输入 ( KERNEL32.ExitProcess
0040201C .rdata 输入 ( USER32.GetDlgItem
00402000 .rdata 输入 ( KERNEL32.GetModuleHandleA
00402028 .rdata 输入 ( USER32.KillTimer :关闭定时器
0040202C .rdata 输入 ( USER32.LoadIconA
0040200C .rdata 输入 ( USER32.PostMessageA
00402014 .rdata 输入 ( USER32.SendMessageA
00402030 .rdata 输入 ( USER32.SetTimer :创建定时器
00402020 .rdata 输入 ( USER32.wsprintfA
00401000 .text 输出 <模块入口点>
4: 程序用setTimer创建了一个定时器,在命令窗口中: bpx SetTimer 对此函数下断,
或者在00402030 .rdata 输入 ( USER32.SetTimer 这行上F2 也可以;
F9运行程序,程序被断下来了;
004010BC . /0F85 86000000 jnz 00401148
004010C2 . |8B7424 08 mov esi, dword ptr [esp+8] ; Case 110 (WM_INITDIALOG) of switch 004010A5
004010C6 . |6A 00 push 0 ; /Timerproc = NULL
004010C8 . |68 E8030000 push 3E8 ; |Timeout = 1000. ms
004010CD . |6A 01 push 1 ; |TimerID = 1
004010CF . |56 push esi ; |hWnd
004010D0 . |FF15 30204000 call dword ptr [<&USER32.SetTimer>] ; \SetTimer
004010D6 . |A1 04304000 mov eax, dword ptr [403004]
如何拆除世间限制呢:我们在来看看SetTimer函数的定义:
SetTimer
The SetTimer function creates a timer with the specified time-out value.
SetTimer函数创建用指定的时间间隔值创建一个定时器
UINT SetTimer(
HWND hWnd, // handle of window for timer messages ; Timer消息发往窗口句柄
UINT nIDEvent, // timer identifier ;定时器标识
UINT uElapse, // time-out value : 以毫秒为单位的时间间隔值,每隔这个时间,定时器就发送定时器消息给指定的窗口
TIMERPROC lpTimerFunc // address of timer procedure 如果此值为NULL,系统发送一Wm_timer消息给应有程序消息队列;
);
方法一: 用JMP 跳过
1:) 用Hiew 打开Timer.exe,回车来到代码模式
2:) F5键后输入虚拟地址 .004010C6
3: ) F3进入编辑模式后,回车键输入: jmp 10D6 后ESC键退出,F9 保存;
4: ) 运行程序,那个计数的窗口里再也没有数值跳动了;
方法二:改UINT uElapse, // time-out value 这个参数的值
注意: 参数调用约定的堆栈顺序,还有就是UINT是16位无符号整数,注意修改后的值不要超出表示范围,否则出现上溢;
unsigned [int] 表示数的范围 : 0-4294967295 应该有好几天了吧; 嘿嘿...
1:) 用Hiew 打开Timer.exe,回车来到代码模式
2:) F5键后输入虚拟地址 .004010C8
3: ) F3进入编辑模式后,回车键输入: PHSH 71266300 后ESC键退出,F9 保存;
4: ) 运行程序,那个计数的窗口里再也没有数值跳动了;
方法三: 利用WM_TIMER , 这时你需要查找 VC头文件WINUSER.H 的消息定义 得到: #define WM_TIMER 0x0113
在W32Dasm里查找113字串 ,在find text 对话框中输入 : 113
:00401175 3D13010000 cmp eax, 00000113 每隔一秒出现一次
:0040117A 75CC jne 00401148
:0040117C A108304000 mov eax, dword ptr [00403008]
:00401181 83F813 cmp eax, 00000013 是否超过20秒,13为16进制数
:00401184 7FB1 jg 00401137 比较 ,超时跳走就完蛋,
:00401186 40 inc eax
:00401187 8D4C240C lea ecx, dword ptr [esp+0C]
:0040118B 50 push eax
:00401184 7FB1 jg 00401137
我们将此处用NOP代替,直接爆了它;
1:) 用Hiew 打开Timer.exe,回车来到代码模式
2:) F5键后输入虚拟地址 .00401184
3: ) F3进入编辑模式后,回车键输入: 9090 后ESC键退出,F9 保存;
4: ) 运行程序,那个计数的窗口里计数数值超过20了,但程序还没有退出;
好了,今天就到这了,吃饭去了,今天下了很大雨哦; 我喜欢这天气; 祝大家玩得愉快;
--------------------------------------------------------------------------------
【经验总结】
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2008年05月03日 PM 06:34:39
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: