首先呢,什么是KeyFile?KeyFile的意思就是密钥文件,它主要呢存在于某些需要注册的软件中,就是说你用一款软件它需要注册并且购买它才可以解锁使用它,这个KeyFile呢就是软件的某个文件,每次软件启动都要从系统根目录或者软件目录中搜寻这个密钥文件,文件里面记录着你的注册信息和注册码以及一些数据,软件利用某种函数将这些数据转化为它可以识别的数据在判断注册的信息是否一致如果一致就进入购买后的版本如果不一致就进入一个试用期的版本。当然呢,还有一种情况是你当时下载的就是个未注册版本,当你注册之后软件向后台发送一些注册数据,然后作者将KeyFile文件发给你,让你放在某个目录下。相比来说第二个情况给作者带来了点麻烦(个人觉得),第一种情况是大部分注册软件经常使用的一个方法。
那么我们接下来就是要分析软件是如何利用KeyFile具体的操作的:
1.我们要准备一些常识,想想看既然是密钥文件跟软件挂钩,那么软件加载时必须要打开KeyFile文件那么我们不妨知道在Windows API中有那些常用的与文件相关的函数呢?
①、创建或打开对象,成功返回一个文件句柄
HANDLE CreateFile(
LPCTSTR lpFileName, // 指向文件名的指针
DWORD dwDesiredAccess, // 访问模式(写 / 读)
DWORD dwShareMode, // 共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 指向安全属性的指针
DWORD dwCreationDisposition, // 如何创建
DWORD dwFlagsAndAttributes, // 文件属性
HANDLE hTemplateFile // 用于复制文件句柄
);
②读取文件数据、从文件指针指向的位置开始将数据读出到一个文件中
HANDLE CreateFile(
LPCTSTR lpFileName, // 指向文件名的指针
DWORD dwDesiredAccess, // 访问模式(写 / 读)
DWORD dwShareMode, // 共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 指向安全属性的指针
DWORD dwCreationDisposition, // 如何创建
DWORD dwFlagsAndAttributes, // 文件属性
HANDLE hTemplateFile // 用于复制文件句柄
);
③ FindFirstFileA 确定注册文件是否存在
GetFileSize、GetFileSizeEx 获得注册文件的大小
GetFileAttributesA/GetFileAttibutexExA 获得注册文件的大小
SetFilePointer/SetFilePointerEx 移动文件指针
-----------------------------------↑↑↑↑-↑↑↑↑-↑↑↑↑-------------------------------------------------
以上都是些常用OD断点函数
准备的工具是:FileMon文件系统检测软件、OllyDbg反汇编、加密与解密里面例子程序PacMe.exe
首先:打开FileMon工具
、包含一栏中写入PacMe表示它将监测PacME名称的程序点击“确定”。
是个这样的界面
我们看快捷键Ctrl+E表示开始“捕获事件”,我们打开PacMe.exe程序,很好我们捕获到了例子程序的文件事件
2、接下来我们用OD打开它:,嘿嘿我进入了例子程序领空可以看到例子程序用到的API函数
然后我Ctrl+G查找,然后猜想输入一个文件函数我就输入“CreateFileA”这个打开文件函数
点“OK”好我们就来到这个函数开始的地方
(注意这是系统领空系统中的CreateFile函数也就是程序调用它API)
我们给它下一个断点按F2下断点也可以双击他的虚拟地址,好,我重新打开例子程序按F9让它跑起来,接下来我点击例子中的“Check”按钮
嘿嘿,猜想正确正好断到我们刚才下的断点处,然后我们按Alt+F9回到函数调用处的下一个指令,然后我们在CALL调用函数参数上下个断点
然后重新例子跑起来,点击按钮check,中段到断点那里,按F7单步走,看右面寄存器变化,到push edx时候右面显示出了
我往下看下发现,这里有个跳转语句
CreateFIle函数返回的是一个句柄给eax,cmp eax,-0x1 与它作比较如果相等就跳到00401747处这时候F7跟着走到JE处我发现跳走了哼哼!知道为什么调走吗,因为程序没有找到密钥文件直接跳到关闭文件对象
然后我利用十六进制编辑工具写一个密钥文件.bit后缀名 我写1234567890的这个数据
然后我保存WazyWeb.bit,

004016E3 . A3 44344000 mov dword ptr ds:[0x403444],eax ; 将句柄放入403444内存中
重新跑程序进入断点出,接着F7走到je处嘿嘿小样不跳了吧!来到这里
小样你找到了你的密钥文件开始真正解密了吧!
下面是具体分析

004016E8 . 6A 00 push 0x0 ; /pOverlapped = NULL
004016EA . 68 48344000 push PacMe.00403448 ; |指向实际读取的字节指针
004016EF . 6A 01 push 0x1 ; |要读入的字节数 为1
004016F1 . 68 FA344000 push PacMe.004034FA ; |用于保存读入数据的缓冲区Buffer
004016F6 . FF35 44344000 push dword ptr ds:[0x403444] ; |读入文件的句柄
004016FC . E8 11010000 call <jmp.&KERNEL32.ReadFile> ; \读文件 成功非0 不成功返回0.
004016EA . 68 48344000 push PacMe.00403448 ; |指向实际读取的字节指针
004016EF . 6A 01 push 0x1 ; |要读入的字节数 为1
004016F1 . 68 FA344000 push PacMe.004034FA ; |用于保存读入数据的缓冲区Buffer
004016F6 . FF35 44344000 push dword ptr ds:[0x403444] ; |读入文件的句柄
004016FC . E8 11010000 call <jmp.&KERNEL32.ReadFile> ; \读文件 成功非0 不成功返回0.
我们F7跟进遇到call 系统API跳走接着走
00401701 . 0FB605 FA3440>movzx eax,byte ptr ds:[0x4034FA] ; 读WazyWeb.bit第一个字节并扩充放到eax中
00401708 . 85C0 test eax,eax ; 判断eax中的数据是否为0
0040170A . 74 3B je short PacMe.00401747 ; 如果文件数据是空的则关闭对象句柄、
00401708 . 85C0 test eax,eax ; 判断eax中的数据是否为0
0040170A . 74 3B je short PacMe.00401747 ; 如果文件数据是空的则关闭对象句柄、
接着走
走到第二个读文件结束看见下面有个CALL 我想着应该是软件作者计算填写注册信息的地方我F7跟进去看看情况果然有情况
xor eax edx ecx初始化清0开始操作
00401000 /$ 33C0 xor eax,eax ; 清0
00401002 |. 33D2 xor edx,edx ; 清0
00401004 |. 33C9 xor ecx,ecx ; 清0
00401006 |. 8A0D FA344000 mov cl,byte ptr ds:[0x4034FA] ; 第一次读取的字节放入cl中
0040100C |. BE 88324000 mov esi,PacMe.00403288 ; 第二次缓存区域数据给esi
00401011 |> AC /lods byte ptr ds:[esi] ; 将[esi]指向的下一个字节数据放到eax中
00401012 |. 03D0 |add edx,eax ; edx实现一个累加存储效果
00401014 |.^ E2 FB \loopd short PacMe.00401011
00401016 |. 8815 FB344000 mov byte ptr ds:[0x4034FB],dl ; 将计算结果放到[4034FB]处
0040101C \. C3 retn
将计算结果放入004034FB存储区中!!
走到第二个读文件结束看见下面有个CALL 我想着应该是软件作者计算填写注册信息的地方我F7跟进去看看情况果然有情况
00401000 /$ 33C0 xor eax,eax ; 清0
00401002 |. 33D2 xor edx,edx ; 清0
00401004 |. 33C9 xor ecx,ecx ; 清0
00401006 |. 8A0D FA344000 mov cl,byte ptr ds:[0x4034FA] ; 第一次读取的字节放入cl中
0040100C |. BE 88324000 mov esi,PacMe.00403288 ; 第二次缓存区域数据给esi
00401011 |> AC /lods byte ptr ds:[esi] ; 将[esi]指向的下一个字节数据放到eax中
00401012 |. 03D0 |add edx,eax ; edx实现一个累加存储效果
00401014 |.^ E2 FB \loopd short PacMe.00401011
00401016 |. 8815 FB344000 mov byte ptr ds:[0x4034FB],dl ; 将计算结果放到[4034FB]处
0040101C \. C3 retn
将计算结果放入004034FB存储区中!!
返回到执行时!核心来了我刚才只是弄得只是作者的小皮毛接下来就是作者真的核心算法计算用户注册码序列号了转化的





































