【文章标题】: Radmin_Commu_Client 3.0 主程序去自校验
【文章作者】: CCDebuger
【软件名称】: Radmin Communication Client 3.0
【下载地址】: http://www.radmin.com/
【使用工具】: OD,PEiD,LordPE,ResScope
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!

【详细过程】 
没事在汉化新世纪逛的时候看到一个求解自校验的,说想汉化 Radmin Communication Client 3.0 这个软件,可主程序只要修改就会出错,无法运行。这个软件是开发远程控制软件 Remote Administrator 的公司出的产品,是这个公司最新出的产品 radmin communication server v3.0 中的客户端。这东西没用过,看介绍好像是用来在企业网内部聊天的。不管它,我这只看它是怎么自校验的。先用 PEiD 检测一下程序,显示为 Microsoft Visual C++ 6.0,呵,原来是无壳的。随便修改一下资源,保存后运行一下,跳出一个对话框:“Executable file is corrupted”,呵呵,当然出错了,要不然我干嘛?既然有对话框,我就先拦对话框。用 OD 载入,命令行中输入 bp MessageBoxA,F9运行,被拦下。ALT+F9 返回,听得一声出错时的响声,又被OD拦下。不管,F9运行,弹出出错对话框,点确定,又被OD拦下。现在再按 ALT+F9 返回,来到这里:

  009AF6CD    68 44A8A400          PUSH 0A4A844                                 ; ASCII "Error"
  009AF6D2    68 24A8A400          PUSH 0A4A824                                 ; ASCII "Executable file is corrupted"
  009AF6D7    53                   PUSH EBX
  009AF6D8    FF15 FC23A300        CALL DWORD PTR DS:[A323FC]                   ; USER32.MessageBoxA
  009AF6DE    8B4424 10            MOV EAX,DWORD PTR SS:[ESP+10]                ; 我们返回的位置
  009AF6E2    3BC3                 CMP EAX,EBX
  009AF6E4    895C24 0C            MOV DWORD PTR SS:[ESP+C],EBX

009AF6DE 地址处就是我们返回的位置。看看这个代码的地址,感觉不对,地址是 009XXXXX,这是哪个的领空?一看OD的标题,竟然还是我们调试程序的领空!这怎么可能?这个程序的基址是01400000,怎么也不可能跑到这样的地址来啊。有问题,重新载入程序,打开 RUN 跟踪,添加所有函数入口,F9运行程序,等弹出出错对话框后按 F12 暂停程序,现在我们来看一下RUN跟踪的记录,在RUN跟踪记录中看一下主程序模块的最后一条指令:

RUN 跟踪, 选定行
返回=7.
线程=主
模块=Rcomclt
地址=01401189
=POP ESI
修改后的寄存器=ESI=00A56000

上面是复制出来的结果,在 OD 中我们见到的就是一条记录。现在在记录表格中双击这条,来到这里:
  
  01401150  /$  56                 PUSH ESI
  01401151  |.  8BF1               MOV ESI,ECX
  01401153  |.  8B06               MOV EAX,DWORD PTR DS:[ESI]
  01401155  |.  85C0               TEST EAX,EAX
  01401157  |.  57                 PUSH EDI
  01401158  |.  8B3D 08B04001      MOV EDI,DWORD PTR DS:[<&KERNEL32.VirtualFree>]    ;  kernel32.VirtualFree
  0140115E  |.  74 0A              JE SHORT Rcomclt.0140116A
  01401160  |.  68 00800000        PUSH 8000                                         ; /FreeType = MEM_RELEASE
  01401165  |.  6A 00              PUSH 0                                            ; |Size = 0
  01401167  |.  50                 PUSH EAX                                          ; |Address
  01401168  |.  FFD7               CALL EDI                                          ; \VirtualFree
  0140116A  |>  8B46 08            MOV EAX,DWORD PTR DS:[ESI+8]
  0140116D  |.  85C0               TEST EAX,EAX
  0140116F  |.  C706 00000000      MOV DWORD PTR DS:[ESI],0
  01401175  |.  74 0A              JE SHORT Rcomclt.01401181
  01401177  |.  68 00800000        PUSH 8000
  0140117C  |.  6A 00              PUSH 0
  0140117E  |.  50                 PUSH EAX
  0140117F  |.  FFD7               CALL EDI
  01401181  |>  5F                 POP EDI
  01401182  |.  C746 08 00000000   MOV DWORD PTR DS:[ESI+8],0
  01401189  |.  5E                 POP ESI                                           ;  我们来到的地方
  

呵呵,这里竟然调用了 VirtualFree?我来查查函数参考,应该还有 VirtualAlloc。一查果然有,先在 VirtualAlloc 函数上全部设上断点,再在上面的01401150地址处设个断点,CTR+F2重新载入程序,F9运行,断下:
  
  01401348   .  FF15 0CB04001   CALL DWORD PTR DS:[<&KERNEL32.VirtualAlloc>]  ; \断在这里
  0140134E   .  8945 E8         MOV DWORD PTR SS:[EBP-18],EAX                 ;  保存已分配内存的开始地址,我这是8D0000
  01401351   .  837D E8 00      CMP DWORD PTR SS:[EBP-18],0
  01401355   .  75 1E           JNZ SHORT Rcomclt.01401375
  01401357   .  6A 00           PUSH 0                                        ; /Style = MB_OK|MB_APPLMODAL
  01401359   .  68 18284101     PUSH Rcomclt.01412818                         ; |Title = ""
  0140135E   .  68 D8004101     PUSH Rcomclt.014100D8                         ; |err0
  01401363   .  6A 00           PUSH 0                                        ; |hOwner = NULL
  01401365   .  FF15 A0B04001   CALL DWORD PTR DS:[<&USER32.MessageBoxA>]     ; \MessageBoxA
  0140136B   .  B8 01000000     MOV EAX,1
  01401370   .  E9 C3060000     JMP Rcomclt.01401A38
  01401375   >  8B4D E8         MOV ECX,DWORD PTR SS:[EBP-18]                 ;  分配内存开始地址送ECX
  01401378   .  894D EC         MOV DWORD PTR SS:[EBP-14],ECX
  0140137B   .  8D55 BC         LEA EDX,DWORD PTR SS:[EBP-44]
  0140137E   .  52              PUSH EDX
  0140137F   .  8B45 EC         MOV EAX,DWORD PTR SS:[EBP-14]                 ;  分配内存开始地址送EAX
  01401382   .  50              PUSH EAX
  01401383   .  8B4D C0         MOV ECX,DWORD PTR SS:[EBP-40]                 ;  我这里是14353B4,原来是资源段2032项的VA
  01401386   .  8B51 04         MOV EDX,DWORD PTR DS:[ECX+4]                  ;  资源段2032项的大小
  01401389   .  8B4D DC         MOV ECX,DWORD PTR SS:[EBP-24]                 ;  资源段2032项的VA送ECX
  0140138C   .  83C1 08         ADD ECX,8
  0140138F   .  E8 2CFDFFFF     CALL Rcomclt.014010C0                         
  


现在看到程序是读资源段中153下ID为2032资源中的内容。我感兴趣的就是程序分配的起始地址为 8D0000 的那块内存。我想看看它要干什么。现在在数据窗口中转到 8D0000 处,现在还都是空的,在这里设个内存写入断点,看看什么时候有东西写进来。设好后F9运行,断下:
  
  01403520  |.  8806            |MOV BYTE PTR DS:[ESI],AL                     ;  断在这里
  01403522  |.  E9 AB020000     |JMP Rcomclt.014037D2                         ;  来了个大跳
  
  ...
  
  014037D2  |> \3B5424 38       |CMP EDX,DWORD PTR SS:[ESP+38]                ;  看看信息窗口,原来还是读2032那个资源中的部分内容
  014037D6  |.  73 50           |JNB SHORT Rcomclt.01403828
  014037D8  |.  3B7424 3C       |CMP ESI,DWORD PTR SS:[ESP+3C]
  014037DC  |.  73 4A           |JNB SHORT Rcomclt.01403828
  014037DE  |.^ E9 9DFCFFFF     \JMP Rcomclt.01403480                         ;  又来个大跳,难道是跳大神?
  
  ...
  
  01403480  |> /83FB 0F         /CMP EBX,0F                                   ;  与F比较,我哪知道它什么时候等于F?设个条件断点吧,省得我老按F8
  01403483  |. |73 24           |JNB SHORT Rcomclt.014034A9
  01403485  |. |33C0            |XOR EAX,EAX
  01403487  |. |8A42 01         |MOV AL,BYTE PTR DS:[EDX+1]                   ;  2032资源段中的内容
  0140348A  |. |42              |INC EDX                                      ;  指针加1
  

在01403480地址处设个条件断点,按SHIFT+F2,输入 ebx==0F,现在F9运行,断下,EBX值为F。F8再看看,还在这段代码里跳?晕。不看了,这么多东西看完要得颈椎病。管它,就是解密2032的资源写到分配的内存地址中嘛,CTR+F9返回。这个返回时间可够长的,半天才来到这:

  0140387A  \.  C3              RETN


F8单步一下,来到这:

  
  014046A2  |.  8B4424 14       |MOV EAX,DWORD PTR SS:[ESP+14]                ;  返回到这
  014046A6  |.  8B48 0C         |MOV ECX,DWORD PTR DS:[EAX+C]
  014046A9  |.  8B50 10         |MOV EDX,DWORD PTR DS:[EAX+10]
  014046AC  |.  8B38            |MOV EDI,DWORD PTR DS:[EAX]
  014046AE  |.  8B40 04         |MOV EAX,DWORD PTR DS:[EAX+4]
  014046B1  |.  8B5E 30         |MOV EBX,DWORD PTR DS:[ESI+30]
  014046B4  |.  8B6E 34         |MOV EBP,DWORD PTR DS:[ESI+34]
  014046B7  |.  894C24 2C       |MOV DWORD PTR SS:[ESP+2C],ECX
  014046BB  |.  895424 24       |MOV DWORD PTR SS:[ESP+24],EDX
  014046BF  |.  897C24 1C       |MOV DWORD PTR SS:[ESP+1C],EDI
  014046C3  |.  894424 10       |MOV DWORD PTR SS:[ESP+10],EAX
  014046C7  |.  895C24 18       |MOV DWORD PTR SS:[ESP+18],EBX
  014046CB  |.  E9 13060000     |JMP Rcomclt.01404CE3                         ;  又是一个大跳
  

乖乖,后面还有一长串代码,坚决不看了,珍爱生命,远离破解!F9运行,断在 VirtualAlloc 函数上。

  
  014014C2   .  FF15 0CB04001   CALL DWORD PTR DS:[<&KERNEL32.VirtualAlloc>]  ; \VirtualAlloc
  014014C8   .  A3 10284101     MOV DWORD PTR DS:[1412810],EAX                ;  这次分配了个起始地址9A0000的
  

向下看看:
  
  0140154D   .  66:C702 4D5A    MOV WORD PTR DS:[EDX],5A4D                    ;  呵,MZ,看样子它不在内存中整出一个PE文件出来是不罢休啊
  01401552   .  8B45 E4         MOV EAX,DWORD PTR SS:[EBP-1C]
  

不管,F9,又断在 VirtualAlloc 上:
  
  014015BE   .  FF15 0CB04001   CALL DWORD PTR DS:[<&KERNEL32.VirtualAlloc>]  ; \VirtualAlloc
  014015C4   .  85C0            TEST EAX,EAX                                  ;  内存地址9A1000,.text段
  

F9,又断在上面的位置,这次分配的内存地址是00A32000了。再F9,还是断在上面的位置,分配的内存地址变为00A4A000。F9,第三次断,分配地址00A56000。继续F9,断:

  
  014010E2  |.  8B3D 0CB04001   MOV EDI,DWORD PTR DS:[<&KERNEL32.VirtualAlloc>] ;  断在这
  014010E8  |.  6A 40           PUSH 40                                         ; /Protect = PAGE_EXECUTE_READWRITE
  014010EA  |.  68 00100000     PUSH 1000                                       ; |AllocationType = MEM_COMMIT
  014010EF  |.  8BF1            MOV ESI,ECX                                     ; |
  014010F1  |.  68 00100000     PUSH 1000                                       ; |Size = 1000 (4096.)
  014010F6  |.  6A 00           PUSH 0                                          ; |Address = NULL
  014010F8  |.  C746 04 0000000>MOV DWORD PTR DS:[ESI+4],0                      ; |
  014010FF  |.  C746 0C 0000000>MOV DWORD PTR DS:[ESI+C],0                      ; |
  01401106  |.  FFD7            CALL EDI                                        ; \VirtualAlloc
  01401108  |.  85C0            TEST EAX,EAX                                    ;  地址00A60000
  


管它,让它去解密代码去,继续F9,来到这:

  
  01401150  /$  56              PUSH ESI
  01401151  |.  8BF1            MOV ESI,ECX
  01401153  |.  8B06            MOV EAX,DWORD PTR DS:[ESI]
  01401155  |.  85C0            TEST EAX,EAX
  01401157  |.  57              PUSH EDI
  01401158  |.  8B3D 08B04001   MOV EDI,DWORD PTR DS:[<&KERNEL32.VirtualFree>]  ;  kernel32.VirtualFree
  0140115E  |.  74 0A           JE SHORT Rcomclt.0140116A
  01401160  |.  68 00800000     PUSH 8000                                       ; /FreeType = MEM_RELEASE
  01401165  |.  6A 00           PUSH 0                                          ; |Size = 0
  01401167  |.  50              PUSH EAX                                        ; |起始地址A60000处
  01401168  |.  FFD7            CALL EDI                                        ; \VirtualFree
  


原来释放了一块内存,后面我们没断点了,不要瞎来,F8慢慢跟:
  
  01401177  |.  68 00800000       PUSH 8000
  0140117C  |.  6A 00             PUSH 0
  0140117E  |.  50                PUSH EAX                                        ;  内存地址00A70000
  0140117F  |.  FFD7              CALL EDI                                        ;  调用VirtualFree函数
  01401181  |>  5F                POP EDI
  01401182  |.  C746 08 00000000  MOV DWORD PTR DS:[ESI+8],0
  01401189  |.  5E                POP ESI                                         ;  我们来到的地方
  0140118A  \.  C3                RETN
  

看了00A70000处的数据,原来是 KERNEL32.DLL 什么的,呵呵,毁尸灭迹啊!经过上面那个 RETN,我们来到这:

  01401909   .  C745 E0 00000000  MOV DWORD PTR SS:[EBP-20],0                     ;  来到这里
  01401910   .  EB 09             JMP SHORT Rcomclt.0140191B
  01401912   >  8B4D E0           MOV ECX,DWORD PTR SS:[EBP-20]
  01401915   .  83C1 01           ADD ECX,1
  01401918   .  894D E0           MOV DWORD PTR SS:[EBP-20],ECX
  0140191B   >  8B55 E0           MOV EDX,DWORD PTR SS:[EBP-20]
  0140191E   .  3B55 AC           CMP EDX,DWORD PTR SS:[EBP-54]                   ;  .text段
  01401921   .  0F83 C4000000     JNB Rcomclt.014019EB
  01401927   .  BA 38004101       MOV EDX,Rcomclt.01410038                        ;  mycol
  0140192C   .  8B45 E0           MOV EAX,DWORD PTR SS:[EBP-20]
  0140192F   .  6BC0 18           IMUL EAX,EAX,18
  01401932   .  8B4D A8           MOV ECX,DWORD PTR SS:[EBP-58]                   ;  段名称
  01401935   .  03C8              ADD ECX,EAX
  01401937   .  E8 14010000       CALL Rcomclt.01401A50                           ;  这里把段名称与上面的mycol比较
  


再来到下面:
  
  0140198B   > /8B55 F4           MOV EDX,DWORD PTR SS:[EBP-C]
  0140198E   . |83C2 01           ADD EDX,1
  01401991   . |8955 F4           MOV DWORD PTR SS:[EBP-C],EDX
  01401994   > |8B45 F4           MOV EAX,DWORD PTR SS:[EBP-C]
  01401997 > . |3B85 A8FAFFFF     CMP EAX,DWORD PTR SS:[EBP-558]                  ;  代码段大小,24CC
  0140199D   . |73 45             JNB SHORT Rcomclt.014019E4
  0140199F   . |8B4D F4           MOV ECX,DWORD PTR SS:[EBP-C]
  014019A2   . |8B15 14284101     MOV EDX,DWORD PTR DS:[1412814]                  ;  内存地址00A56000
  014019A8   . |8B048A            MOV EAX,DWORD PTR DS:[EDX+ECX*4]
  014019AB   . |0305 10284101     ADD EAX,DWORD PTR DS:[1412810]                  ;  内存地址009A0000
  014019B1   . |8B4D F4           MOV ECX,DWORD PTR SS:[EBP-C]
  014019B4   . |8B15 14284101     MOV EDX,DWORD PTR DS:[1412814]
  014019BA   . |89048A            MOV DWORD PTR DS:[EDX+ECX*4],EAX
  014019BD   . |8B45 F4           MOV EAX,DWORD PTR SS:[EBP-C]
  014019C0   . |8B0D 14284101     MOV ECX,DWORD PTR DS:[1412814]
  014019C6   . |8B1481            MOV EDX,DWORD PTR DS:[ECX+EAX*4]
  014019C9   . |8995 A4FAFFFF     MOV DWORD PTR SS:[EBP-55C],EDX
  014019CF   . |8B85 A4FAFFFF     MOV EAX,DWORD PTR SS:[EBP-55C]
  014019D5   . |8B08              MOV ECX,DWORD PTR DS:[EAX]
  014019D7   . |034D D8           ADD ECX,DWORD PTR SS:[EBP-28]
  014019DA   . |8B95 A4FAFFFF     MOV EDX,DWORD PTR SS:[EBP-55C]
  014019E0   . |890A              MOV DWORD PTR DS:[EDX],ECX
  014019E2   .^\EB A7             JMP SHORT Rcomclt.0140198B
  

不管它,我知道它是往内存地址00A56000开始处的空间写东西就行了,要用到这里我再研究。在01401997地址那条指令上设个条件断点,EAX==24CC,F9继续,条件断点断下后,我们F8,到这:
  
  01401A0E   > \68 00800000       PUSH 8000                                       ; /FreeType = MEM_RELEASE
  01401A13   .  6A 00             PUSH 0                                          ; |Size = 0
  01401A15   .  8B4D E8           MOV ECX,DWORD PTR SS:[EBP-18]                   ; |
  01401A18   .  51                PUSH ECX                                        ; |Address = 008D0000
  01401A19   .  FF15 08B04001     CALL DWORD PTR DS:[<&KERNEL32.VirtualFree>]     ; \又要毁尸灭迹了
  01401A1F   .  8B15 10284101     MOV EDX,DWORD PTR DS:[1412810]                  ;  9A0000,解密后代码基址,以后要用
  01401A25   .  0395 D8FEFFFF     ADD EDX,DWORD PTR SS:[EBP-128]
  01401A2B   .  8955 B8           MOV DWORD PTR SS:[EBP-48],EDX
  01401A2E   .  8B45 B8           MOV EAX,DWORD PTR SS:[EBP-48]
  01401A31   .- FFE0              JMP EAX                                         ;  我这EAX是00A1D855,要跳到解完密的代码那执行了
  

继续F8,一看全是PE文件初始化的一些代码。向下翻翻代码,F4到这:
  
  00A1D92F    50                  PUSH EAX                                        ; F4运行到这,看看还要干什么
  00A1D930    E8 3B18F9FF         CALL 009AF170                                   ; 跟进去
  

F4到 00A1D92F 的时候发现 EAX 就是原程序的镜像基址01400000,跟进00A1D930地址处的那个CALL,看看干了些什么,来到下面:
  
  009AF1EB    68 F4A5A400         PUSH 0A4A5F4                                    ; UNICODE "*.lng_rcc"
  009AF1F0    68 8EA00000         PUSH 0A08E                                      ; 要开始判断是否有语言文件了
  

继续,到这:
  
  009AF263    E8 A8F60000         CALL 009BE910
  009AF268    85C0                TEST EAX,EAX
  009AF26A    0F84 58040000       JE 009AF6C8                                     ; 这里一跳就完蛋了,EAX不能为0
  

在009AF268地址处这条指令检测EAX时,我先修改一下寄存器,让EAX等于1,下面那条指令它肯定不会跳的。继续,来到这:
  
  009AF27F    E8 BCFA0000         CALL 009BED40
  009AF284    85C0                TEST EAX,EAX
  009AF286    0F84 3C040000       JE 009AF6C8                                     ; 这里一跳也完蛋,EAX不能为0
  

同样在执行到009AF284地址处的指令时把EAX改为1,顺便看一下地址 009AF6C8 处都有什么东西:
  
  009AF6C8    68 30200000         PUSH 2030
  009AF6CD    68 44A8A400         PUSH 0A4A844                                    ; ASCII "Error"
  009AF6D2    68 24A8A400         PUSH 0A4A824                                    ; ASCII "Executable file is corrupted"
  009AF6D7    53                  PUSH EBX
  009AF6D8    FF15 FC23A300       CALL DWORD PTR DS:[A323FC]                      ; USER32.MessageBoxA
  

呵呵,到这就歇菜了。当场给你个出错对话框。我们上面都改了标志位,现在肯定不会跳到这了。分析过原程序,这里的EAX都是1。F9运行一下,呵呵,现在正常了。到此就要考虑了,不可能我每次都挂个调试器来修改标志位让程序运行啊。做个 Loader?人家运行你的汉化版之前还要先运行 Loader 来修改内存,多郁闷啊!还有种办法就是把解密算法全部搞清楚,写个程序,把解密后的代码修改了再加密回去,替换原来的资源。这。。。这也太让人抓狂了吧?这活我不干。还是找个简单的方法吧,我在程序跳到内存中已解密后的代码执行之前,让程序先把解密后的代码按我的要求修改一下,然后再让程序去执行,这样行吧?我只要把原来那两条 TEST EAX,EAX 指令改为 INC EAX 就行了,当然后面还要补个 NOP 来补足代码长度。INC EAX 再加上 NOP 机器码是 4090。现在剩下的问题就是在哪里接管程序,在什么地方写我们的补丁代码了。我现在先考虑这一段代码:

  
  01401A1F   .  8B15 10284101     MOV EDX,DWORD PTR DS:[1412810]                  ;  9A0000,解密后代码镜像基址,以后要用
  01401A25   .  0395 D8FEFFFF     ADD EDX,DWORD PTR SS:[EBP-128]
  01401A2B   .  8955 B8           MOV DWORD PTR SS:[EBP-48],EDX
  01401A2E   .  8B45 B8           MOV EAX,DWORD PTR SS:[EBP-48]
  01401A31   .- FFE0              JMP EAX                                         ;  我这EAX是00A1D855,要跳到解完密的代码那执行了
  

这里是在跳到在内存中解密后的代码执行前我最后一面见到的主程序。舍不得啊!就在这我们交流一下吧。现在先到主程序中找块空地,我用个小工具 Topo 1.2,在地址 0140A4B2 处找了块空地,现在开始 patch:
  
  01401A1F   .  8B15 10284101      MOV EDX,DWORD PTR DS:[1412810]               ;  9A0000,解密后代码镜像基址
  01401A25   .  0395 D8FEFFFF      ADD EDX,DWORD PTR SS:[EBP-128]
  01401A2B      E9 828A0000        JMP Rcomclt.0140A4B2                         ;  跳到我们要打补丁的地方
  01401A30      90                 NOP
  01401A31   .  FFE0               JMP EAX
  


补丁代码:
  
  0140A4B2      60                 PUSHAD                                       ;  保护现场
  0140A4B3      2B95 D8FEFFFF      SUB EDX,DWORD PTR SS:[EBP-128]               ;  获取解密后代码镜像基址
  0140A4B9      C682 68F20000 40   MOV BYTE PTR DS:[EDX+F268],40                ;  下面四条指令是修改内存中的代码
  0140A4C0      C682 69F20000 90   MOV BYTE PTR DS:[EDX+F269],90
  0140A4C7      C682 84F20000 40   MOV BYTE PTR DS:[EDX+F284],40
  0140A4CE      C682 85F20000 90   MOV BYTE PTR DS:[EDX+F285],90
  0140A4D5      61                 POPAD                                        ;  恢复现场
  0140A4D6      8955 B8            MOV DWORD PTR SS:[EBP-48],EDX                ;  恢复前面我们修改的指令
  0140A4D9      8B45 B8            MOV EAX,DWORD PTR SS:[EBP-48]
  0140A4DC    ^ E9 5075FFFF        JMP Rcomclt.01401A31                         ;  返回原位置继续执行
  

patch 完后在OD中右键选择复制到可执行文件->所有修改,保存为 Rcomclt1.exe 文件。运行一下,呵呵,一切OK!

【经验总结】 
这个软件的自校验真是别具一格,把可执行代码加密后放在资源中,运行的时候再动态在内存中解密出代码执行,整个实现的就是个加密壳的功能,而且你又不好脱出来。解密的代码不光含有自校验部分,还有程序初始化及其它的一大堆代码,你还没法禁用它。这种方法还是值得学习的。 

【版权声明】: 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!