[译]开源软件保护-第2部分
By robot-v1.0
本文链接 https://www.kyfws.com/applications/open-source-software-protection-part-2-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 12 分钟阅读 - 5801 个词 阅读量 0[译]开源软件保护-第2部分
原文地址:https://www.codeproject.com/Articles/4064/Open-source-software-protection-Part-2
原文作者:Kamal Shankar
译文由本站 robot-v1.0 翻译
前言
This is a better alternative to my previous soulution
这是我以前的解决方案的更好选择
- Exe Protector的下载源(新)-34.8 Kb(Download source for Exe Protector (new) - 34.8 Kb)
- Exe Protector的下载源(旧)-23.5 Kb(Download source for Exe Protector (old) - 23.5 Kb)
- 下载硬件ID密钥生成程序-17.1 Kb(Download HardwareID Keygeneration program - 17.1 Kb)
- Loader32对话框实现的下载源-32.8 Kb(Download source for Loader32 Dialog implementation - 32.8 Kb)
- Loader32服务实现的下载源-16.5 Kb(Download source for Loader32 Service implementation - 16.5 Kb)
- 下载支持的DLL,库文件和头文件-77.5 Kb(Download supporting DLL, library files and headers - 77.5 Kb)
基于对话框的Loader32代码实现的屏幕截图(Screenshot of the Dialog based implementation of Loader32 Code)
介绍(Introduction)
这是我上一篇有关保护的文章的更新-(This is an update to my previous article on protection -) 开源软件保护系统(Open source software protection system) 您会在这里找到所概述的所有主要思想.本文尝试实现基于软件的解决方案,以实现自提交文章以来收到的想法.(where you will find all the main ideas outlined. This article tries to implement a software based solution to realize the idea(s) that I had received since I submitted the article)
在继续之前,我必须强调一个事实,即不得将此保护系统视为生产级别的复制保护-该软件无法"复制保护"任何软件-(Before I proceed, I must stress upon the fact that this protection system must not be viewed as a production level copy protection - there is no way this software “copy protects” any software -)可以无限复制受此保护系统"保护"的程序的副本-只有在假定/授权运行该程序的计算机上,这些程序才能正常运行.(one can make unlimited copies of programs “protected” with this protection system - only that none of them will run correctly except on the computer on which the program was supposed/authorized to run on.)
我以前的(My previous) 查看文件的许可证(License for viewing the document) 站立,我也想添加以下子句:(stands and I would like to add the following clause too:)
- 您必须同意发布对提供的源代码所做的任何主要/重要更改.(You must agree to post any major/important changes that you make to the source code provided.)
- 第三方必须在商业上实施此保护系统(Commercial implementation of this protection system by third parties must)**授权(authorize)**首先由我使用.(the use from me first.) 重大/重要的代码更改是指使代码更高效或更稳定或更有效的任何内容.(By major/important code changes I mean anything which makes the code more efficient or stable or effective etc.)
有(Having)*同意(agreed)*许可证,您现在可以继续.(to the License, you may now proceed.)
背景(Background)
本文考虑到您已经经历了(This article takes into account that you have gone through my) 以前的杂乱(previous ramblings) ,并且能够理解我到底要做什么;)另外,如果您希望了解本文提供的代码,则要修改源代码,必须对PE系统/布局有一定的了解,否则,可以只需构建代码并发布建议和错误报告即可!(, and been able to fathom what exactly I am up to ;) Also, if you wish to understand the code provided with this article, a working knowledge of the PE System /Layout is necessary if you want to modify the sources, otherwise you can just Build the code and post suggestions and bug reports anyway!)
保护系统背后的主要逻辑是,任何PE文件的核心都是1个(或多个)机器可执行部分,称为代码部分,通常位于名为的部分中(The main logic behind the protection system is that the heart of any PE file is 1 (or more) machine executable sections called the code section residing in usually section named).文本(.text*)*要么(*or*)*.码(*.code*)*等等.在执行本节的过程中,Win32程序加载器会寻找一个名为RVA的特殊RVA.(*etc. During execution of this section the Win32 Program loader looks for a special RVA named*) AddressOfEntryPoint
,这是从此处开始代码部分的RVA.我可能会强调,PE Header实际上实际上是一个结构数组,要了解PE File,您只需要输入成员变量即可.(*, which is the RVA from where code section is started. I may emphasize that the PE Header is actually an array of structures really, and to learn about the PE File you just need to rope in the member variables.*)
现在,如果缺少程序入口点,Win32 Loader很有可能会拒绝运行它.该程序(EXEProt)的作用是使PE文件中的所有内容保持不变,除了以下代码(Now if the program entry point is missing, in all possibility, the Win32 Loader will refuse to run it. What this program (EXEProt) does is leave everything in the PE file intact except the code following from) AddressOfEntryPoint
-它与EXE文件分开(暂时-请参阅(- which it separates from the EXE file(for the time being - Please see)*加载程序32Dlg.cpp(Loader32Dlg.cpp)*以了解为什么我必须将其放在单独的文件中),并使用带有计算机HardwareID密钥的Blowfish对其进行加密.(of Loader232 Dialog implementation to understand why I had to put it in a separate file), and encrypt it using Blowfish with the Machine HardwareID Key.)
Loader32程序仅执行(The Loader32 program just does a) CreateProcess()
在"保护"文件上并带有"挂起"状态标志,这样(on the Protected file with the Suspend state flag on, so that) CreateProcess()
只是在程序入口处停止(包含二进制文件(just stops at the Program entry point(which contains binary) NULL
暂时),然后使用一个(s for the time being) , and then using a single) WriteProcessMemory()
写入解密的代码(通过解密获得(writes the decrypted code (obtained by decrypting) "code_sec.dat"
内容(使用当前的硬件密钥)存储到过程存储器中,然后继续执行该过程.因此,如果在运行HarewareID程序的同一台计算机上运行Loader32程序,则将获得正确的解密数据,否则解密的数据将成为垃圾,并且受保护的程序将崩溃.(contents using the current Hardware key) into the process memory, and resumes the process. Hence, if the Loader32 Program is being run on the same machine on where HarewareID program was run, the correct decrypted data will be obtained, else the decrypted data will be garbage and the Protected program will crash.)
实际上,在Matitiahu Allouche在他的著作中指出了这种方法缺乏用户友好性之后(Well actually after Matitiahu Allouche pointed out the lack of user friendliness in such a approach in his) 发布(post) 现在,该程序已更新,以警告用户是否在其他(未经授权)的计算机上运行该程序.(the program has now been updated to alert the user if he is running the program on a different (unauthorised) machine.)
在这样的计算机上显示以下对话框:(The following dialog is displayed on such a computer:)
在未经授权的计算机上运行Loader时的结果(Outcome when the Loader was run on an unauthorised computer)
使用代码(Using the code)
已经提供了源代码,它们是完全合格的代码-要获得有效的演示,只需编译它们即可!(The sources have been provided and they are FULLY qualified code - to get a working demo, just compile them!)
当前的实现应该是(The current implementation is supposed to be)外用(externally applied),即软件开发人员(, ie, the software developer)**才不是(does not)**需要使(need to make)**任何(any)**更改他的代码以使用此保护.对于软件发布者和用户本人来说,实施保护都非常容易(changes to his code to use this protection. Implementing the protection is extremely easy for both the software publisher and the user himself)
它包括以下步骤:(It consists of the following steps:)
-
感兴趣的客户将HardwareID程序下载到他的计算机上,然后运行它.名为的文件(The interested customer downloads HardwareID program to his computer, and runs it. A file named)*SysData.DAT(SysData.DAT)*创建包含系统硬件信息以及MD5密钥(该密钥对于计算机而言应是唯一的).鼓励用户浏览内容.这样做是为了尊重他的信息隐私.如果对此感到满意,则可以将文件发送给软件发布者.在这里我要补充一点(containing system hardware info alongwith a MD5 Key (which should be unique to the computer) is created. The user is encouraged to go through the contents. This has been done to honour his privacy of information. If he is satisfied with it, he would send the file to the software publisher. Here I should add that tampering of)
SysData.DAT
由于HardwareID密钥实际上是包含硬件详细信息的第一个字符串的MD5摘要,因此可以轻松检测到内容.(contents can be easily detected as the HardwareID Key is actually the MD5 Digest of the first string contaning the hardware details.) -
软件发布者获取(并验证)密钥,并使用它使用EXEProt应用程序"保护"应用程序文件.按照当前的实现,四个文件-(The software publisher takes(and verifies) the key and using it “protects” the application program file(s) using EXEProt application. As per the current implementation, four files -)真正的EXE(Real.EXE)(程序文件中除了代码部分以外的所有内容),((the program file having everything but code section),)DAT补丁(Patch.DAT)(包含代码部分的虚拟大小),((containing the Virtual size of the code section),)代码_DAT(Code_Sec.DAT)(加密代码部分)和(( the encrypted code section) and)**code_sig.sig(code_sig.sig)**包含加密的MD5摘要(containing the MD5 digests of the encrypted)**code_sec.dat(code_sec.dat)**并且还创建了解密内容.(and also of it’s decrypted contents are created.)
-
发布者现在可以选择是否加载受保护的文件需要用户干预,然后他可以使用(The publisher now chooses if loading the protected file needs user intervention, then he can use the)
Loader32Dialog
实现,否则他会使用快速且安静(implementation, else he uses the fast and quiet)Loader32Service
程序以动态加载受保护的文件(program to dynamically load the protected file(s)) -
发布者可以将文件名和注册表常量更改为上述程序,以反映当前应用程序的文件名和注册表常量.所有变量均已明确记录在案,并且常量已定义#,因此只需几秒钟即可更改它们.他可以选择继续使用默认名称(如果适合他的话)(The publisher can change the filename, and registry constants into the above mentioned programs to reflect that of the current application. All variables have been clearly documented, and the constants are #defined, so it will take only a few seconds to change them. He can alternatively, continue using the default names (if it suits him))
-
发布者将加载程序和其他三个文件发送给客户(The publisher sends the customer the Loader and the other three files)
-
保护系统到位(The protection system is in place) 的心(The heart of the)
CEXEProtector
是以下功能:(is the following function:)
BOOL CExeProtector::DumpSection(DWORD nFileOffset,
DWORD dwNumberOfBytesToRW, DWORD /*dwRVA*/)
{
/*
Previously, this function used file streams,
but due to severe performance bottlenecks, I
have replaced them with
Win32 APIs. - Kamal Shankar 17th June 2003
Note: The program after the above modification was not as
extensively tested as with
previously file stream based versions.
szMyFileName contains the original EXE filename which we are to protect.
We will read the WHOLE contents of szMyFileName into a
unsigned char array - ucOriginalBuffer.
ucOriginalBuffer is a 1:1 mapping of the file on disc,
and by directly loading the file into
memory, we will be boosting program performance like anything.
Now all furthur operations will be solely memory based.
*/
HANDLE hFile = CreateFile(szMyFileName,GENERIC_READ,FILE_SHARE_READ,
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL);
if(!hFile)
{
AfxMessageBox(("File open error!\n["+(CString)szMyFileName+"]")
,MB_OK|MB_ICONINFORMATION);
return FALSE;
}
DWORD dwBufferSize=GetFileSize(hFile,NULL),dwBytesRead=0;
if(dwBufferSize==-1) return FALSE;//Could not get file size
unsigned char *ucOriginalBuffer =
new unsigned char[dwBufferSize]; //Check for exceptions
if(!ucOriginalBuffer)
{
MessageBox(NULL,"There was an error in dynamically"
" allocating memory block.",
"Not enough memory",MB_OK|MB_ICONSTOP);
return FALSE;
}
BOOL bResult=ReadFile(hFile,ucOriginalBuffer,dwBufferSize,
&dwBytesRead,NULL);
CloseHandle(hFile);
if(!bResult||(dwBufferSize!=dwBytesRead))
{MessageBox(NULL,"There was an error in reading"
"the file contents into memory.",
"File I/O Error",MB_OK|MB_ICONSTOP);
delete[] ucOriginalBuffer;return FALSE;}
unsigned char *ucTempHexBuffer =
new unsigned char[dwNumberOfBytesToRW]; //Check for exceptions
if(!ucTempHexBuffer)
{MessageBox(NULL,
"There was an error in dynamically allocating memory block.",
"Not enough memory",MB_OK|MB_ICONSTOP);
delete[] ucOriginalBuffer;return FALSE;}
ZeroMemory(ucTempHexBuffer,dwNumberOfBytesToRW);
CopyMemory(ucTempHexBuffer,(ucOriginalBuffer+nFileOffset),
dwNumberOfBytesToRW);
ZeroMemory((ucOriginalBuffer+nFileOffset),dwNumberOfBytesToRW);
BF_Encrypt(&ucTempHexBuffer,szKey,dwNumberOfBytesToRW); //szKey
hFile=CreateFile(szProtectedFileName,GENERIC_WRITE,FILE_SHARE_WRITE,
NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_
NORMAL,NULL);
if(!hFile)
{
AfxMessageBox(("File open error!\n["+(CString)szProtectedFileName+"]")
,MB_OK|MB_ICONINFORMATION);
return FALSE;
}
bResult=WriteFile(hFile,ucOriginalBuffer,dwBufferSize,
&dwBytesRead,NULL);
if((!bResult)||(dwBufferSize!=dwBytesRead))
//Read dwBytesRead as dwBytesWritten
{
AfxMessageBox(("File write error!\n["+(CString)szProtectedFileName+"]")
,MB_OK|MB_ICONSTOP);
return FALSE;
}
CloseHandle(hFile);
if(ucTempHexBuffer!=NULL) delete[] ucTempHexBuffer;
ucTempHexBuffer=NULL;
if(ucOriginalBuffer!=NULL) delete[] ucOriginalBuffer;
ucOriginalBuffer=NULL;
return TRUE;
}
对于看过我以前版本的任何人来说,显而易见的是,此功能已被完全重写.(As obvious to anybody who has seen my previous version, this function has been rewritten fully.)
我已经淘汰了基于流的文件I/O,以支持更快的Win32 API,并且原始文件只能读取一次!(I have scratched out stream based file I/O in favour of the faster Win32 APIs, and the original file is read ONLY once !)
所有加密和保护过程均直接通过内存(RAM)完成-在最坏的情况下,程序性能提高了129.78%(15 MB文件参考)!(All encryption and protection procedures are done DIRECTLY from memory (RAM) - this way program performance has shot up by 129.78 % in the worst case scenario (15 MB File reference) !)
我正在一次将整个原始文件读入内存.我还获得了与程序"入口点"相对应的文件偏移量.由于加载到内存中的文件是磁盘上映像的1:1映射,因此我读出了代码部分,并将其加密为(I am reading in the original file wholly, once, into memory. I am also obtaining the file offset corresponding to the Program “Entry Point”. As the file loaded in memory is a 1:1 mapping of the image on disc, I read out the code section, encrypt it into)code_sec.dat(code_sec.dat),将本节归零,然后将其写入名为(, zero out this section, and write it into a file named)真正的EXE(Real.EXE).(.)
文件,(The file,)**code_data.dat(code_data.dat)**由EXEProt使用程序对话框TextBox中提供的密钥解密.(is decrypted by EXEProt using the key supplied into the program dialog TextBox.)
以前版本的用户注意.(Note to users of previous version.)
充分地(fully) ExeProt
*仍然(still)*ExeProt_Src.zip(ExeProt_Src.zip) 这里(here) ExeProtNew_Src.zip(ExeProtNew_Src.zip) 这里(here) ## 程序错误和解决方法(Program BUGs and workarounds)
- BUG:处理器速度因处理器负载/电源管理而异:(BUG: Processor speeds variable due to processor load/power management:)
细节:(Details:)
谢谢Bryan Cook和Ries.(Thank you Bryan Cook and Ries.)
布莱恩的(Bryan’s) timeBeginPeriod()
hack/test原为(hack/test was)**真(really)**太好了!我在这里看到一个很棒的程序员:=)他所做的是,设置了一个高精度多媒体计时器,同时也运行了我的系统指纹代码((great ! I see a great programmer here :=) What he did was, set up a high precision multimedia timer and simultaneously ran my system fingerprinting code too (which)*也(also)*设置一个高优先级的多媒体计时器以猜测处理器速度).结果,多媒体计时器的精度下降,导致系统指纹与他的多媒体计时器不同.(set up a high priority multimedia timer to guess the processor speed). As a result, the precision of the multimedia timers drop, resulting in a different system fingerprint than when his multimedia timer was)**不(not)**运行!(running !)
另一方面,Ries使我注意到了笔记本电脑的电源管理功能.在他的情况下,当他使用电池运行i686时,电源管理电路会根据系统负载动态更改CPU电压,从而改变CPU速度.(Ries on the otherhand, brought laptop power management features to my notice. In his case, when he was running his i686 from batteries, the power management circuits dynamically changed CPU voltage as per system load, thus varying the CPU speed.)
原因:(Reason:)
实际上,处理器速度的枚举是由(Actually the Processor speed enumeration is done by the) SysInfo
Paul Wendt编写的类,以及处理器速度确定功能(class written by Paul Wendt, and the processor speed determining function) calculateCpuSpeed()
来自AND!它也意外地使用了多媒体计时器.(comes from AMD ! It incidentally uses multimedia timers too.)
简而言之,它的作用是:(In a nutshell here’s what it does:)
- 将优先级设置为高,以便获得正确的CPU速度的机会更大.(*Sets the priority to high,so that there will be a greater chance of getting the correct CPU speed.*)
- 函数进行迭代,直到连续获得两个相同的结果.(*Function iterates till it gets two results in a row that are identical.*)
但是,请相信我,它运作良好(除非您提出要求,否则除外)(But believe me, it works well (except in cases like you have brought forward))
解决方法:(Workaround:)
函数调用(The function call to) CheckMachine(char* szSystemInfoDumpString,char* szSystemKey,BYTE nMaskBits)
,(,) nMaskBits
表示要屏蔽的信息,可以采用以下值:(denotes the information to mask and can take the following values:)
Value Masks
----- -------
00 Nothing (copies ALL system info)
01 OS Name
02 Processor speed
03 Processor string
04 Total RAM on system
05 Drive names
06 Volume names
07 Volume serial numbers
The default value is 0 (defined in the corresponding Header)
如果您有理由相信处理器速度可能会有所不同,则将默认值'00’替换为'02'(If you have reason to believe that the processor speed may vary, then replace the default value of ‘00’ to ‘02’)
因此,对于Bryan和Ries(以及其他面临相同问题的人),将'02’传递给(Thus for Bryan and Ries (and others facing the same problem), by passing ‘02’ to) nMaskBits
,您应该得到一个(, you should get a)*一致的(consistent)*系统指纹.(system fingerprint.)
历史(History)
- 2003年5月5日-更新(改写)了整篇文章,以及部分原文(5th May 2003 - Updated (rewrote) the whole article, and also some of the source)
- 2003年5月15日-根据您的有用反馈,更新了所有文件的文章和链接和代码:)(15th May 2003 - Updated article+links and code of ALL files based on your helpful feedback :))
- 2003年6月23日-重新编写程序代码.所有保护过程都移到一个类中,并且刮擦了次要文件I/O.这些操作现在直接在RAM中完成.(23rd June 2003 - Rewrote program code. All protection procedures moved into a class, and minor file I/O scratched. Those operations NOW directly done in RAM.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
VC7.0 VC7.1 C++ VC6 WinXP Win2003 Win2K MFC Visual-Studio Dev 新闻 翻译