DLL当一个可执行文件运行时,Windows加载器将可执行模块映射到进程的地址空间中,加载器分析可执行模块的输入表,并设法找出任何需要的DLL,并将它们映射到进程的地址空间中。
由于输入表中只包含DLL名而没有它的路径名,因此加载程序必须在磁盘上搜索DLL文件。首先会尝试从当前程序所在的目录加载DLL,如果没找到,则在Windows系统目录查找,最后是在环境变量中列出的各个目录下查找。利用这个特点,先伪造一个系统同名的DLL,提供同样的输出表,每个输出函数转向真正的系统DLL。程序调用系统DLL时会先调用当前目录下伪造的DLL,完成相关功能后,再跳到系统DLL同名函数里执行,如图184。这个过程用个形象的词来描述就是系统DLL被劫持(hijack)了。
184gif
利用这种方法取得控制权后,可以对主程序进行补丁。此种方法只对除kernel32dll, ntdlldll等核心系统库以外的DLL有效,如网络应用程序的ws2_32dll,游戏程序中的d3d8dll,还有大部分应用程序都调用的lpkdll,这些DLL都可被劫持。
利用562章提供的CrackMeNetexe来演示一下如何利用劫持技术制作补丁,目标文件用Themida v1920加壳保护。
1.补丁地址
去除这个CrackMe网络验证方法参考第5章,将相关补丁代码存放到函数PatchProcess( )里。例如将401496h改成:
代码:00401496 EB 29 jmp short 004014C1
补丁编程实现就是:
代码:
unsigned char p401496[2] = {0xEB, 0x29};
WriteProcessMemory(hProcess,(LPVOID)0x401496, p401496, 2, NULL);
p401496这个数组的数据格式,可以用OllyDBG插件获得,或十六进制工具转换。例如Hex Workshop打开文件,执行菜单Edit/Copy As/Source即可得到相应的代码格式。
2.构建输出函数
查看实例CrackMeNetexe输入表,会发现名称为ws2_32dll的DLL,因此构造一个同名的DLL来完成补丁任务。伪造的ws2_32dll有着真实ws2_32dll一样的输出函数,完整源码见光盘。实现时,可以利用DLL模块中的函数转发器来实现这个目标,其会将对一个函数的调用转至另一个DLL中的另一个函数。可以这样使用一个pragma指令:
代码:#pragma comment(linker, "/EXPORT:SomeFunc=DllWorksomeOtherFunc")
这个pragma告诉链接程序,被编译的DLL应该输出一个名叫SomeFunc的函数。但是SomeFunc函数的实现实际上位于另一个名叫SomeOtherFunc的函数中,该函数包含在称为DllWork dll的模块中。
如要达到劫持DLL的目的,生成的DLl输出函数必须与目标DLL输出函数名一样,本例可以这样构造pragma指令:
代码:#pragma comment(linker, "/EXPORT:WSAStartup=_MemCode_WSAStartup,@115")
编译后的DLL,会有与ws2_32dll同名的一个输出函数WSAStartup,实际 *** 作时,必须为想要转发的每个函数创建一个单独的pragma代码行,读者可以写一个工具或用其他办法,将ws2_32dll输出函数转换成相应的pragma指令。
当应用程序调用伪装ws2_32dll的输出函数时,必须将其转到系统ws2_32dl中去,这部分的代码自己实现。例如WSAStartup输出函数如下构造:
代码:
ALCDECL MemCode_WSAStartup(void)
{
GetAddress("WSAStartup");
__asm JMP EAX;//转到系统ws2_32dll的WSAStartup输出函数
}
其中GetAddress()函数的代码如下:
代码:
// MemCode 命名空间
namespace MemCode
{
HMODULE m_hModule = NULL; //原始模块句柄
DWORD m_dwReturn[500] = {0}; //原始函数返回地址
// 加载原始模块
inline BOOL WINAPI Load()
{
TCHAR tzPath[MAX_PATH]={0};
TCHAR tzTemp[MAX_PATH]={0};
GetSystemDirectory(tzPath, sizeof(tzPath));
strcat(tzPath,"\\ws2_32dll");
m_hModule = LoadLibrary(tzPath);//加载系统系统目录下ws2_32dll
if (m_hModule == NULL)
{
wsprintf(tzTemp, TEXT("无法加载 %s,程序无法正常运行。"), tzPath);
MessageBox(NULL, tzTemp, TEXT("MemCode"), MB_ICONSTOP);
}
return (m_hModule != NULL);
}
// 释放原始模块
inline VOID WINAPI Free()
{
if (m_hModule)
FreeLibrary(m_hModule);
}
// 获取原始函数地址
FARPROC WINAPI GetAddress(PCSTR pszProcName)
{
FARPROC fpAddress;
TCHAR szProcName[16]={0};
TCHAR tzTemp[MAX_PATH]={0};
if (m_hModule == NULL)
{
if (Load() == FALSE)
ExitProcess(-1);
}
fpAddress = GetProcAddress(m_hModule, pszProcName);
if (fpAddress == NULL)
{
if (HIWORD(pszProcName) == 0)
{
wsprintf(szProcName, "%d", pszProcName);
pszProcName = szProcName;
}
wsprintf(tzTemp, TEXT("无法找到函数 %hs,程序无法正常运行。"), pszProcName);
MessageBox(NULL, tzTemp, TEXT("MemCode"), MB_ICONSTOP);
ExitProcess(-2);
}
return fpAddress;
}
}
using namespace MemCode;
编译后,用LordPE查看伪造的ws2_32dll输出函数,和真实ws2_32dll完全一样,如图185所示。
185gif
查看伪造的ws2_32dll中任意一个输出函数,例如WSACleanup:
代码:
text:10001CC0 ; int __stdcall WSACleanup()
text:10001CC0 WSACleanup proc near
text:10001CC0 push offset aWsacleanup ;"WSACleanup"
text:10001CC5 call sub_10001000 ;GetAddress(WSACleanup)
text:10001CCA jmp eax
text:10001CCA WSACleanup endp
会发现输出函数WSACleanup()首先调用GetAddress(WSACleanup),获得真实ws2_32dll中WSACleanup的地址,然后跳过去执行,也就是说ws2_32dll各输出函数被HOOK了。
3.劫持输出函数
ws2_32dll有许多输出函数,经分析,程序发包或接包时,WSAStartup输出函数调用的比较早,因此在这个输出函数放上补丁的代码。代码如下:
代码:
ALCDECL MemCode_WSAStartup(void)
{
hijack();
GetAddress("WSAStartup");
__asm JMP EAX;
}
hijack()函数主要是判断是不是目标程序,如是就调用PatchProcess()进行补丁。
void hijack()
{
if (isTarget(GetCurrentProcess())) //判断主程序是不是目标程序,是则补丁之
{
PatchProcess(GetCurrentProcess());
}
}
伪造的ws2_32dll制作好后,放到程序当前目录下,这样当原程序调用WSASTartup函数时就调用了伪造的ws2_32dll的WSASTartup函数,此时hijack()函数负责核对目标程序校验,并将相关数据补丁好,处理完毕后,转到系统目录下的ws2_32dll执行。
这种补丁技术,对加壳保护的软件很有效,选择挂接的函数最好是在壳中没有被调用,当挂接函数被执行时,相关的代码己被解压,可以直接补丁了。有些情况下,必须用计数器统计挂接的函数的调用次数来接近OEP。此方法巧妙地绕过了壳的复杂检测,很适合加壳程序的补丁制作。
一些木马或病毒也会利用DLL劫持技术搞破坏,因此当在应用程序目录下发现系统一些DLL文件存在时,应引起注意。
DLL当一个可执行文件运行时,Windows加载器将可执行模块映射到进程的地址空间中,加载器分析可执行模块的输入表,并设法找出任何需要的DLL,并将它们映射到进程的地址空间中。
由于输入表中只包含DLL名而没有它的路径名,因此加载程序必须在磁盘上搜索DLL文件。首先会尝试从当前程序所在的目录加载DLL,如果没找到,则在Windows系统目录查找,最后是在环境变量中列出的各个目录下查找。利用这个特点,先伪造一个系统同名的DLL,提供同样的输出表,每个输出函数转向真正的系统DLL。程序调用系统DLL时会先调用当前目录下伪造的DLL,完成相关功能后,再跳到系统DLL同名函数里执行,如图184。这个过程用个形象的词来描述就是系统DLL被劫持(hijack)了。
184gif
利用这种方法取得控制权后,可以对主程序进行补丁。此种方法只对除kernel32dll, ntdlldll等核心系统库以外的DLL有效,如网络应用程序的ws2_32dll,游戏程序中的d3d8dll,还有大部分应用程序都调用的lpkdll,这些DLL都可被劫持。
利用562章提供的CrackMeNetexe来演示一下如何利用劫持技术制作补丁,目标文件用Themida v1920加壳保护。
1.补丁地址
去除这个CrackMe网络验证方法参考第5章,将相关补丁代码存放到函数PatchProcess( )里。例如将401496h改成:
代码:00401496 EB 29 jmp short 004014C1
补丁编程实现就是:
代码:
unsigned char p401496[2] = {0xEB, 0x29};
WriteProcessMemory(hProcess,(LPVOID)0x401496, p401496, 2, NULL);
p401496这个数组的数据格式,可以用OllyDBG插件获得,或十六进制工具转换。例如Hex Workshop打开文件,执行菜单Edit/Copy As/Source即可得到相应的代码格式。
2.构建输出函数
查看实例CrackMeNetexe输入表,会发现名称为ws2_32dll的DLL,因此构造一个同名的DLL来完成补丁任务。伪造的ws2_32dll有着真实ws2_32dll一样的输出函数,完整源码见光盘。实现时,可以利用DLL模块中的函数转发器来实现这个目标,其会将对一个函数的调用转至另一个DLL中的另一个函数。可以这样使用一个pragma指令:
代码:#pragma comment(linker, "/EXPORT:SomeFunc=DllWorksomeOtherFunc")
这个pragma告诉链接程序,被编译的DLL应该输出一个名叫SomeFunc的函数。但是SomeFunc函数的实现实际上位于另一个名叫SomeOtherFunc的函数中,该函数包含在称为DllWork dll的模块中。
如要达到劫持DLL的目的,生成的DLl输出函数必须与目标DLL输出函数名一样,本例可以这样构造pragma指令:
代码:#pragma comment(linker, "/EXPORT:WSAStartup=_MemCode_WSAStartup,@115")
编译后的DLL,会有与ws2_32dll同名的一个输出函数WSAStartup,实际 *** 作时,必须为想要转发的每个函数创建一个单独的pragma代码行,读者可以写一个工具或用其他办法,将ws2_32dll输出函数转换成相应的pragma指令。
当应用程序调用伪装ws2_32dll的输出函数时,必须将其转到系统ws2_32dl中去,这部分的代码自己实现。例如WSAStartup输出函数如下构造:
代码:
ALCDECL MemCode_WSAStartup(void)
{
GetAddress("WSAStartup");
__asm JMP EAX;//转到系统ws2_32dll的WSAStartup输出函数
}
其中GetAddress()函数的代码如下:
代码:
// MemCode 命名空间
namespace MemCode
{
HMODULE m_hModule = NULL; //原始模块句柄
DWORD m_dwReturn[500] = {0}; //原始函数返回地址
// 加载原始模块
inline BOOL WINAPI Load()
{
TCHAR tzPath[MAX_PATH]={0};
TCHAR tzTemp[MAX_PATH]={0};
GetSystemDirectory(tzPath, sizeof(tzPath));
strcat(tzPath,"\\ws2_32dll");
m_hModule = LoadLibrary(tzPath);//加载系统系统目录下ws2_32dll
if (m_hModule == NULL)
{
wsprintf(tzTemp, TEXT("无法加载 %s,程序无法正常运行。"), tzPath);
MessageBox(NULL, tzTemp, TEXT("MemCode"), MB_ICONSTOP);
}
return (m_hModule != NULL);
}
// 释放原始模块
inline VOID WINAPI Free()
{
if (m_hModule)
FreeLibrary(m_hModule);
}
// 获取原始函数地址
FARPROC WINAPI GetAddress(PCSTR pszProcName)
{
FARPROC fpAddress;
TCHAR szProcName[16]={0};
TCHAR tzTemp[MAX_PATH]={0};
if (m_hModule == NULL)
{
if (Load() == FALSE)
ExitProcess(-1);
}
fpAddress = GetProcAddress(m_hModule, pszProcName);
if (fpAddress == NULL)
{
if (HIWORD(pszProcName) == 0)
{
wsprintf(szProcName, "%d", pszProcName);
pszProcName = szProcName;
}
wsprintf(tzTemp, TEXT("无法找到函数 %hs,程序无法正常运行。"), pszProcName);
MessageBox(NULL, tzTemp, TEXT("MemCode"), MB_ICONSTOP);
ExitProcess(-2);
}
return fpAddress;
}
}
using namespace MemCode;
编译后,用LordPE查看伪造的ws2_32dll输出函数,和真实ws2_32dll完全一样,如图185所示。
185gif
查看伪造的ws2_32dll中任意一个输出函数,例如WSACleanup:
代码:
text:10001CC0 ; int __stdcall WSACleanup()
text:10001CC0 WSACleanup proc near
text:10001CC0 push offset aWsacleanup ;"WSACleanup"
text:10001CC5 call sub_10001000 ;GetAddress(WSACleanup)
text:10001CCA jmp eax
text:10001CCA WSACleanup endp
会发现输出函数WSACleanup()首先调用GetAddress(WSACleanup),获得真实ws2_32dll中WSACleanup的地址,然后跳过去执行,也就是说ws2_32dll各输出函数被HOOK了。
3.劫持输出函数
ws2_32dll有许多输出函数,经分析,程序发包或接包时,WSAStartup输出函数调用的比较早,因此在这个输出函数放上补丁的代码。代码如下:
代码:
ALCDECL MemCode_WSAStartup(void)
{
hijack();
GetAddress("WSAStartup");
__asm JMP EAX;
}
hijack()函数主要是判断是不是目标程序,如是就调用PatchProcess()进行补丁。
void hijack()
{
if (isTarget(GetCurrentProcess())) //判断主程序是不是目标程序,是则补丁之
{
PatchProcess(GetCurrentProcess());
}
}
伪造的ws2_32dll制作好后,放到程序当前目录下,这样当原程序调用WSASTartup函数时就调用了伪造的ws2_32dll的WSASTartup函数,此时hijack()函数负责核对目标程序校验,并将相关数据补丁好,处理完毕后,转到系统目录下的ws2_32dll执行。
这种补丁技术,对加壳保护的软件很有效,选择挂接的函数最好是在壳中没有被调用,当挂接函数被执行时,相关的代码己被解压,可以直接补丁了。有些情况下,必须用计数器统计挂接的函数的调用次数来接近OEP。此方法巧妙地绕过了壳的复杂检测,很适合加壳程序的补丁制作。
一些木马或病毒也会利用DLL劫持技术搞破坏,因此当在应用程序目录下发现系统一些DLL文件存在时,应引起注意。
4.随机验证
随机验证很重要,例如你的一处验证是一直存在的,奸人就很容易地下断点跟踪了。因此在软件启动时进行一次正常验证外,其他情况下的验证最好是随机的,用30分之一或50分之一的机会进行验证,这样奸人会不停地试你的软件在哪一处进行了验证,因此破解的时间会相当地长。
加密第14定理:足够多的随机验证足以让破解者累死。
随机验证包括随机进入不同的验证子程序。
或随机中的最大数大一些,只有30分之一的机会验证。
或在窗口中放上一些颜色与底图一样的框,这样奸人不一定会点击这里,但用户万一点中了,就会触发验证。
我们假设所有软件都能被破解,包括易语言在内,那么如果他破解的速度跟不上你发布新软件的速度,那么他永远在破最新版而累死。或者说他破解的时间比你写一个软件的代价大,这时还不如他直接写这个软件来得合算。
反破解的任务之一就是让奸人累死,或浪费他的生命。
下面的办法也可以使用:你可以在读到待验证的注册码、公钥、注册文件后,通过定义10000个数组,存入上述同样的内容以备以后进行验证,这最多浪费一些内存。验证时随机使用其中的一个进行验证,由于奸人不知你用的是数组中的哪一个进行的比对,而且是随机的,每次验证的值都不一样,不让奸人吐血才怪呢。
计次循环首(10000,计次)
数组[计次] = “123456” ’ 复制一万个公开注册码或公钥,破解者知道也无所谓。
计次循环尾
数组[取随机数(1,10000)]
你不要立刻检查注册码,10000份拷贝你只要以后随机找一份用就行了,破解的人不知道你正在用的是那一个,同时你可以事先编好且运行时不断使用一些假的读取注册码数组的调用干扰破解者。这种方法对程序的性能影响微不足到,只是浪费一点内存。因为Debug对内存下断点的局限,这种情况他要下断点,累死的就是破解的人了。
5.不同权限验证
在启动时进行一次验证是非常必要的,这样让奸人知道确实是验证了,以让他心理放松警惕,而这次的验证只是一部分验证,并没有完全验证。
还有的建议在启动时将注册信息读入后不要进行验证,保不定在哪里进行验证,个人认为这样让破解者提高了警惕性,会认为软件作者很有经验。麻痹敌人也很重要呀。
例如,在启动时验证通过一次,验证级别加强一级,然后再在其他的地方再进行验证就可以了。
下面代码是确认了一个级别
计次循环首(到数值(验证1),)
已注册 = 1
计次循环尾()
……
……
在另一个触发子程序中再通过这个级别再验证:
计次循环首(已注册)
计次循环首(到数值(验证2),)
已注册 = 2
跳出循环()
计次循环尾()
跳出循环()
计次循环尾()
在其他触发子程序中再通过这个级别再验证:
计次循环首(已注册)
计次循环首(到数值(验证3),)
已注册 =3
跳出循环()
计次循环尾()
跳出循环()
计次循环尾()
有时也可以将级别降一降,怎么降,当然是不考虑级别直接验证了:
6.忽悠型的GHOFFICE过滤词语验证代码
前面已讲过花指令的原理,在程序中人为地再放一些GHOFFICE过滤词语代码以忽悠奸人也是一个好办法。GHOFFICE过滤词语代码就是一些假的验证代码,基本上是明文的,这样的代码上百上千,足以让奸人累死。
其实对付那些“根据跳转指令的爆破”高手来说,一个办法就够他们头疼的了,就是你在程序中不明显加入与判断是否正版有关的语句,也不做任何提示,以免让他们顺藤摸瓜,而是在判断为盗版后,跳转到另一个看似很合理的分支,而那个分支和正版的分支代码差不多,只是在计算公式或其它算法上稍动一下,使其运算结果不正确,这样,他们就在机器码级别上就分不清哪个是对的,哪个是错的了,即使他们认为破解成功,其实运行时,得的结果错误百出,他们就没兴趣了,呵呵,算损的吧!!!
加密第15定理:大量添加GHOFFICE过滤词语代码虽然是无奈之举,但很管用。
作业1:制作一个常量器
要求:制作一个常量代码自动生成器。可随机生成成百上千个易语言源代码形式,可直接拷贝到易语言中成为常量。变量也可以这样制作。
写好这样一个程序后,就可以自动生成GHOFFICE过滤词语代码,然后复制,粘贴到易语言的常量表中即可。如下图所示:
变量也可以这样生成,不过生成的变量可以任意拷贝为全局变量,或程序集变量,或局部变量。制作时的名称可以为中文名称,直接编译后不会在EXE文件中找到同名的中文名称。因此您可以放心地将这些名称定义为:“GHOFFICE过滤词语常量1”、“GHOFFICE过滤词语变量1”等等以示与正常代码进行区别。
作业2:制作一个代码迷乱器
本次作业性质同上,也是自动生成易语言的一些无用GHOFFICE过滤词语代码,以迷乱奸人的破解,让他找到的全是GHOFFICE过滤词语代码,从而大大延长了破解时间。
通过直接拷贝编辑框中的内容,粘贴到您的代码中,可自动完成任务,如下图所示:
上图所生成的是一些明文的加密方法的GHOFFICE过滤词语代码,让奸人去研究这些GHOFFICE过滤词语吧。
上述子程序名称最好也有时调用一下,反正不会真正产生作用的,用多线程调用最好。
或者您平时注意多收集一些别人用于加密时的子程序,拷贝到一个易语言程序中,保存,这样的代码作为GHOFFICE过滤词语代码放在你有用的程序中,虽然增加了一些程序的体积,但安全性是大大提高了。并且基本上没有牺牲软件性能与稳定性。
7.伪验证技术
还是先举一个例子说明吧,易表软件在100版本前已发现有大量的注册机存在,于是易表作者其后改变了加密方式,易表100推出后还是出现了注册机,并且这种注册机注册过的软件可以使用。于是有些用户用注册机取得的注册码使用了,过了一段时间,当盗版用户将重要数据存入易表后,突然有一天数据库被锁定了,于是只好注册易表,并且让易表作者为数据库解锁。
从这里可以基本上判定易表新版本采用了伪验证技术,即在较为明显的地方提供了一级验证,这种验证方式没有经过太强的加密,而二级验证在一定的条件下才触发,而这个条件是检查到了用户输入了重要的数据,或大量的数据,或使用次数较多。
基本原理是注册文件由前后两个注册码拼接而成。一般情况下只进行第一个注册码的验证,而当条件成熟时进行第二个注册码验证。
这是一种双赢的策略,易表作者即收到了注册费,付费的人还会道歉,并且感谢易表作者。哈哈,大家要学习这招哦。
但本办法对于数据库应用及数据量大时检查最好,而对于一些没有生成数据的用户无效。
发布软件的时候发布自己编写的注册机,弄个假破解版,那么想破解的就可能不来了,即使有真的破解,谁会有自己给自己写假破解快啊!可能假破解版中只破解一半,等用户使用了,有数据了再锁定,让他们注册后再给解锁,付了钱还要谢谢你,哈哈,损招,但有用!
加密第16定理:伪验证可以迷惑一般破解者,甚至自己发布一个伪注册机。
8.定时验证、延时验证、客户数据集累验证
过一段时间后再验证,如你在2005年1月发布一个软件,那么就内定2005年6月后触发验证机会。
或您的软件是一个数据库产品,那么您可以在程序中设置如果数据库大于5MB时就进行验证,并且最好您能确定这些数据是不重复的,刻意加入的。
如易表设置了伪验证,这时市场上出现了新的注册机,当用户用这个注册机后,提示是注册成功了,但当用户输入重要数据后的某个日子,突然打不开数据库了,用户很着急,因为以为是破解成功了,所以将重要的资料输入了,只能拿钱向易表作者进行注册了。而且还千恩万谢,后悔自己不该用破解。哈哈,一举两得呀。
这个方法对于数据库应用软件来说是绝好的办法。
作业:制作一个算法程序放在你的数据库软件中,这个子程序可以统计你的数据库软件使用时,用户输入的是否是拷贝的东西,还是正常的数据。当统计到1000时触发验证。方法思路为:可以通过查看用户有没有使用复制与粘贴快捷键,或资料进行排序,如果有大量重复的,就说明是奸人在拷贝数据破解,否则是一个资料一个资料的输入的,说明是正常使用的重要资料,这时进行对比就好了。
本方法对有资料的破解使用者有极好的控制作用,通过第6条的伪验证技术与本技术结合,那么就可以知道是不是正版用户,并且可以锁定数据库,等他注册后再给他解锁。
加密第17定理:加密的结果应该是双赢,伪验证是一个上策。
9.验证与专业知识相结合技术
将验证与专业知识相结合,让奸人必须学习专业知识后才能真正去破解,这样所花的功夫比自己写一个软件的代价还要大,而有的专业知识不是专家是不知道的,因此是一个较好的加密方法。
前述中采用了“到数值(验证1())”这样的代码返回的是0或1两者之间的一个数,可以用乘法进行混合计算,如:
音量 = 播放位置X到数值(验证1())
当验证正确时返回的是1,这时的结果是正确的,否则返回0,这时的结果为0,是错误的。
这样的代码可以混合到您的专业知识中,如:算命软件可将天干地支、生辰八字中的某个地方进行此类计算,计算类软件可以将某种特殊的计算过程如此结合计算,绘图类软件可将绘图中的算法部分加入此类计算,音响设计类、机床设计软件……
加密第18定理:你知道的专业知识,破解者不一定了解哦。让专业知识与验证相结合吧。
10.伪装,用易语言写自有支持库
大家可以将DLL文件的扩展名改为易语言的支持库文件FNE扩展名,这样进行非独立编译后,与其他FNE文件混合在一起,甚至您可以用易语言写一个支持库,将其中一部分作为验证部分。
易语言的支持库文件FNE文件其实就是一个DLL文件,只不过扩展名改变了而已,用易语言写支持库的方法金眼睛已发过一篇贴子,进行过说明,请大家在易语言论坛上搜索金眼睛的贴子就可以找到了。
作业:找到金眼睛关于用易语言写支持库的贴子,并且自己写一个支持库。
11.绝妙的暗桩设置
应该想到的用代码实现的暗桩前面都讲了不少,下面是一些特别的暗桩供奸人品味的。
大家可以在一些不起眼的地方再放一些暗桩,如:在窗口最小化事件中随机验证,如在某个组件鼠标被移动事件中验证,
有时需要将数据完整性验证放在更高一级的验证中,不要一上来就检查文件是否被更改。
同一验证可以使用多次,这样奸人认为你已经验证过了,没有必要会再验证一次,而相反这时又产生了验证,让奸人防不胜防。如启动时就立即检查程序完整性,如果发现被更改,那就立即退出程序,而在一些子程序中也随机放这样的验证。
更多的暗桩大家自己设计最好。
加密第19定理:加密重要的是暗桩的设置,破解不完全就是一个无效破解。
12.发布不完整版本
有的软件作者在发布共享软件时,放在外面的是不完整版本,将更多的数据资料在注册后提供。这样做也可以,只是麻烦一些而已。如有的图形制作软件,将资源另外打包,用户注册后再给完全版的。
也有的将DLL文件中的验证部分作了空处理,而在注册后提供真正的注册DLL文件及注册码。还有的直接将KEY文件放在了DLL文件中另外提供。
加密第20定理:不要发布完整版本,以静制动。
13.程序、数据结合加密技术
把程序运行所必需要的资源放到一个数据库文件中,给这个数据库设密码,密码是主程序的数据摘要变换后的结果。程序运行是先验证有没有注册,如果已经注册,就对运行程序本身(取执行文件名())取数据摘要,用自己设计的算法多次变换后形成一个字串,用该字串作为数据库的密码打开数据库文件。如果打开数据库失败,就说明主程序被人修改了,终止程序运行即可。(不终止也没戏,程序找不到运行所需的资源。)
另外设计一个程序,用同样的算法算出数据库密码,然后给数据库加密即可。
密码形成算法建议使用大数支持库。但如果是汇编高手用汇编写注册机的话,会直接将支持库的所有反汇编码抄进去就可以了,问题是他们有没有时间搞。
14.自定义算法
前面讲过采用RSA与数值计算支持库交叉计算的办法,这就是一种自有的算法,如果能用上数值计算支持库中的矩阵、傅丽叶变换等高级功能就更好了。
多重RSA交叉打乱:大家也可以多用一些RSA密钥,如用5个,10个都无所谓,重要的是将这些注册码都打乱,让奸人哭死。打乱的方法就是你自己独创的方法了。
更多的自有算法就要靠大家自己去研究了。祝大家好运。
加密第21定理:加密不反对古怪和变态的方法,鼓励哦。
15.加密框图
下面给出一个加密的设计框图,大家可以根据自己的实际情况改变加密的策略:
图中主程序外围进行了花指令编译,并且用加普通壳进行保护。脱壳了也无所谓,因为设置了暗桩,随机检查。
图中表示主程序运行后,首先进行了常规的注册码第一次验证,找有没有注册文件。如果这个被破解,注册码应该是一个短的RSA,而真正的注册码是三个RSA的叠加。会生成伪注册机也无所谓。
主程序中用暗桩的形式对窗口标题、版权信息进行验证,这是考虑到如果一启动就验证这些很容易被奸人看出来从而会跳过去。因此用一些随机,或分级,或条件法取得不固定的验证。
主程序中用暗桩的方式对加壳后主程序的完整性进行校验,这个也不要放在常规的验证中,否则很容易被跳过去。可以查文件长度,MD5或CRC32都可以上。
主程序中用暗桩的方式加入了反调试模块。
主程序中布满GHOFFICE过滤词语验证代码。并且源代码有备注,不会搞错的。
主程序在某个条件下随机进行第二级验证,从注册码中取第二段数据,如果注册码长度不够且取不到第二段数据,那么就说明已使用了伪注册机,将用户的数据库锁定,等他付钱来注册。
主程序在一个条件下再激活验证,从注册码文件中取第三段数据,如果注册码长度不够,且取不到第三段数据,那么就说明已使用了伪注册机,将用户的数据库锁定,等他付钱来注册。
编程中还注意将加密的字符串搅乱且分不同地方存放,用吴氏加密命令加密重要数据,也可加入数值计算支持库的算法,也可以加入一些惩罚手段,也可以再加入自己的一些算法。
以下是一些人的编程体会摘录,基本未改其中的内容,在此表示感谢!
附录1加密已形成密码学
我引用《应用密码学》作者的话:
世界上有两种密码:一种是防止你的小妹妹看你的文件;另一种是防止奸人阅读你的文件资料。
如果把一封信锁在保险柜中,把保险柜藏在纽约的某个地方…,然后告诉你去看这封信。这并不是安全,而是隐藏。相反,如果把一封信锁在保险柜中,然后把保险柜及其设计规范和许多同样的保险柜给你,以便你和世界上最好的开保险柜的专家能够研究锁的装置。而你还是无法打开保险柜去读这封信,这样才是安全的。
意思是说,一个密码系统的安全性只在于密钥的保密性,而不在算法的保密性。
对纯数据的加密的确是这样。对于你不愿意让他看到这些数据(数据的明文)的人,用可靠的加密算法,只要破解者不知道被加密数据的密码,他就不可解读这些数据。
但是,软件的加密不同于数据的加密,它只能是“隐藏”。不管你愿意不愿意让他(合法用户,或 Cracker)看见这些数据(软件的明文),软件最终总要在机器上运行,对机器,它就必须是明文。既然机器可以“看见”这些明文,那么 Cracker,通过一些技术,也可以看到这些明文。
于是,从理论上,任何软件加密技术都可以破解。只是破解的难度不同而已。有的要让最高明的 Cracker 忙上几个月,有的可能不费吹灰之力,就被破解了。
所以,反盗版的任务(技术上的反盗版,而非行政上的反盗版)就是增加 Cracker 的破解难度。让他们花费在破解软件上的成本,比他破解这个软件的获利还要高。这样 Cracker 的破解变得毫无意义——谁会花比正版软件更多的钱去买盗版软件 ?
然而,要做到“难破解”,何尝容易? Sony 曾宣称的超强反盗版(Key 2 Audio音乐 CD反盗版),使用了很尖端的技术,然而最近却被一枝记号笔破解了,成为人们的饭后笑料!
所以,很多看上去很好的技术,可能在 Cracker 面前的确不堪一击。就像马其诺防线一样,Cracker 不从你的防线入手,而是“绕道”。这样,让你的反盗版技术在你做梦也想不到的地方被 Crack 了。
为什么会这样呢 ?归根到底是因为软件在机器上运行,并且软件和机器是分离的——这一点是关键,如果软件和硬件完全绑定,不能分离,是可以做到象 IDEA 之类几乎不可破解的系统的。这将在后面谈传统软件保护技术时详细说明。
对我的这个解决方案,我不能保证Crack高手在几天之内不能破解它,我只能说:“在这个软件中,我尽量堵住了当前破解者普遍使用的方法以及“我想得到”的可能的缺口。”但是我相信,倾注了我三个月心血的反盗版软件,决不是一个“玩具式”的反盗版软件。
附录2《如何用简单方法防止破解》
北极异型
在Debug的手册里可以看到Debug工具的局限:第一个局限是只能下4个内存区域的断点,每个断点不能控制超过两个字节,这样内存断点不能控制超过16个字节的区域;第二个局限是对多线程只能同时跟踪一个线程。
假设你的注册部分有300行,你可以分成30个子程序调用或重复的func1(),func2() func30()。将他们随意放到程序的各个部分,一定不能放在一起(自己能找到就行了)。不要用Memcpy等常用系统调用拷贝注册码,尽可能自己写,像Memcpy很好写,性能差点无所谓。经过编译后inline函数展开,注册部分和其他代码混在一起,他要写出注册机就像大海里捞针,在几十万甚至上百万汇编代码里找出有用的注册部分。
利用Debug的第一个局限最重要的一点是:注册码也不要放在一起,假设你的注册码是12位,千万不要用一个12位的数组放注册码,你可以在程序的不同位置定义12个全局字符变量,每个放一位,这样注册码在内存就不连续了。最好再加密处理一下(简单的字符异或就可以),验证时再解密。也不要用连续内存保存验证用到的变量,尽量将用到的验证临时变量分散定义在程序的不同处,再在验证中,不断转移一些值到其他变量中,对付暴力和Loader会比较有效。
没有必要用复杂的加密算法,更容易成为追踪的目标。只要你将注册部分隐藏的足够好,也没有漏洞,你花1天写的加密算法,破解者可能会花100-1000倍的时间破解。大部分人都会放弃。
你将注册做在一起,就像将你的财宝放在现代保险箱里,虽然非常坚固难以解密,对于开锁高手两分钟就打开了。
而古代海盗用的方法是将财宝埋在海岛上,这样没有藏宝图,对所有高手和低手都只有一条路,拿一把铁撬挖,可能要挖一生。程序有那么多代码,反编译出来可能超过百万行,你将注册部分藏在里面,藏的好就如同将财宝埋在海岛里。那些所谓的Crackme只是给高手玩儿的现代保险箱而已,用原始的方法可以达到同样效果。
以上就是关于DLL文件劫持应该怎么办全部的内容,包括:DLL文件劫持应该怎么办、DLL文件劫持应该怎么办 急啊、易语言反破解教程说信息框用自定义窗口就是自己新建窗口,请问新建窗口怎么做到程序等待或者系统等待效果等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)