微信公众号:计算机与网络安全ID:Computer-network有时破解一个程序可能会释放它,而破解的程序往往只是其中一个程序的修改,有无需打包整个软件重新发布,只需要发布补丁即可。发布补丁有三种常见情况。第一种情况是直接发布修改后的文件。第二种情况是发布一个文件补丁修改待破解的原程序。最后一种情况是发布内存补丁。补丁,它不修改原文件,而是修改内存中的指定部分。三种情况各有优势。第一种情况,修改后的程序发布,用户只需简单替换即可。但有一个问题。如果程序版本较多,直接替换可能会导致替换后的程序无法使用。第二种方法是发布文件补丁。该方法需要编写一个简单的程序修改待破解的程序。破解前可以判断文件的版本。如果补丁与待破解程序版本相同则破解,否则不破解。但有时文件修改后,程序可能无法运行,因为有些程序会对自己进行校验和比较,当校验和发生变化时,程序将无法运行。最后一种方法是内存补丁,同样需要自己编写,编写的补丁需要和待破解的程序放在同一目录下。在执行待破解程序时,需要执行内存补丁程序。内存补丁程序会运行待破解的程序,然后比较补丁和程序的版本,最后破解。同样,如果有内存检查,也会导致程序无法运行。不过不管是文件校验还是内存校验,都可以继续对校验部分打补丁,突破程序校验部分。本文写了一个文件补丁和一个内存补丁。1、文件补丁用OD修改CrackMe相对容易。没有OD怎么修改呢?实际上,修改OD中的反汇编指令后,相应地,修改了文件中的机器码。只要能在文件中定位到指令对应的机器码的位置,就可以直接修改机器码。JNZ对应的机器码指令是0x75,JZ对应的机器码指令是0x74。也就是说,只要在文件中找到要修改的位置,用十六进制编辑器将0x75修改为0x74即可。这个内存中的地址怎么定位到文件地址呢?这就是PE文件结构中VA转FileOffset的知识。具体手动步骤请自行尝试,这里可以直接写代码修改。为了简单起见,这里使用控制台来写,直接操作文件,省略了中间步骤。一旦有了想法,就不难了。文件补丁的代码如下:#include#includeintmain(intargc,char*argv[]){//VA=00401EA8//FileOffset=00001EA8DWORDdwFileOffset=0x00001EA8;BYTEbCode=0;DWORDdwReadNum=0;//判断参数if(argc!=2){printf("Pleaseinputtwoargument\r\n");return-1;}//打开文件HANDLEhFile=CreateFile(argv[1],GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if(hFile==INVALID_HANDLE_VALUE){return-1;}SetFilePointer(hFile,dwFileOffset,0,FILE_BEGIN);ReadFile(hFile,(LPVOID)&bCode,sizeof(BYTE),&dwReadNum,NULL);//比较当前位置是否为JNZif(bCode!='\x75'){printf("%02X\r\n",bCode);CloseHandle(hFile);return-1;}//修改为JZbCode='\x74';SetFilePointer(hFile,dwFileOffset,0,FILE_BEGIN);WriteFile(hFile,(LPVOID)&bCode,sizeof(BYTE),&dwReadNum,NULL);printf("WriteJZisSuccessfully!\r\n");CloseHandle(hFile);//RunWinExec(argv[1],SW_SHOW);getchar();return0;}代码给出了详细的注释,拖拽CrackMe文件即可o文件补丁或在命令行输入命令,如图1所示。图1CrackMe上的文件打补丁通常,在打文件补丁之前,需要先对比一下要修改的位置,以免修改错误。程序使用的方法是将要修改的部分读出来,看是否和OD调试时的值相同,如果相同则打补丁。由于这只是对编程知识的介绍,所以针对的是CrackMe。如果你破解某个软件,打个文件补丁发布给别人使用,没有做相应的判断就直接修改,很可能会导致这个软件无法使用,因为别人用的这个软件之后是无法确认的是否对外发布版本等因素。因此,最好在进行文件补丁时进行判断,或者使用CopyFile()来备份文件。2、内存补丁与文件补丁相比,还有一种补丁叫做内存补丁。这种补丁是在程序加载到内存后修改,也就是说文件本身没有修改。要将CrackMe加载到内存中,可以调用CreateProcess()函数加载内存。这个函数参数多,功能强大。使用CreateProcess()创建子进程,并在创建过程中挂起子进程,然后就可以放心使用WriteProcessMemory()函数修改CrackMe了。整个过程比较简单,直接看源码吧:#include#includeintmain(intargc,char*argv[]){//VA=004024D8DWORDdwVAddress=0x00401EA8;BYTEbCode=0;DWORDdwReadNum=0;//判断参数个数if(argc!=2){printf("Pleaseinputtwoargument\r\n");return-1;}STARTUPINFOsi={0};si.cb=sizeof(STARTUPINFO);西。wShowWindow=SW_SHOW;si.dwFlags=STARTF_USESHOWWINDOW;PROCESS_INFORMATIONpi={0};BOOLbRet=CreateProcess(argv[1],NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,//挂起子进程NULL,NULL,&si,&pi);if(bRet==FALSE){printf("CreateProcessError!\r\n");return-1;}ReadProcessMemory(pi.hProcess,(LPVOID)dwVAddress,(LPVOID)&bCode,sizeof(BYTE),&dwReadNum);//判断是否为JNZif(bCode!='\x75'){printf("%02X\r\n",bCode);CloseHandle(pi.hThread);CloseHandle(pi.hProcess);return-1;}//修改JNZ为JZbCode='\x74';WriteProcessMemory(pi.hProcess,(LPVOID)dwVAddress,(LPVOID)&bCode,sizeof(BYTE),&dwReadNum);ResumeThread(pi.hThread);CloseHandle(pi.hThread);CloseHandle(pi.hProcess);printf("WriteJZisSuccessfully!\r\n");getchar();return0;}代码中的注释也比较详细,代码的关键是比较,否则会导致程序崩溃。暂停,这样做的好处是在某些情况下,可能没有机会打补丁,需要打补丁的地方已经执行了。应用补丁后,只需恢复线程并继续运行即可。参考资料:C++黑客编程秘诀与防范(第3版)