DaShanRen 发表于 2014-7-28 16:58:08

一含自杀代码的软件注册算法分析

准备阶段:
分别对硬盘序列号和厂商编码进行计算,得到两组数据,到这里成型:
0048934C   MOV EDX, DWORD PTR DS:                  ;数字 25517715
00489352   PUSH EDX                                          ; /Arg1 => 0020EFDC
00489353   CALL NEAR DWORD PTR DS:[<&msvbvm60.rtcR8ValFromBs>; \转化为整数
00489359   MOV EBX, DWORD PTR DS:[<&msvbvm60.__vbaFpI4>]   ;msvbvm60.__vbaFpI4
0048935F   CALL NEAR EBX                                     ;<&msvbvm60.__vbaFpI4>
00489361   XOR EAX, 246E69                                 ;25517715 xor 246E69=27341050
00489366   PUSH EAX                                          ; /Arg1
00489367   CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaStrI4>]   ; \转化为数字--结果1
0048936D   MOV EDX, EAX
0048936F   MOV ECX, u中文内?0050D05C
00489374   CALL NEAR ESI
00489376   MOV EAX, DWORD PTR DS:                  ;数字 35567050
0048937B   PUSH EAX                                          ; /35567050
0048937C   CALL NEAR DWORD PTR DS:[<&msvbvm60.rtcR8ValFromBs>; \转化为整数
00489382   CALL NEAR EBX
00489384   XOR EAX, 856AC5                                 ;35567050 xor 856AC5=43769615
00489389   PUSH EAX                                          ; /Arg1
0048938A   CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaStrI4>]   ; \转化为数字--结果2
虽说后面还要对结果1、2进行计算,但
结果1=27341050
结果2=43769615
是重要的步骤过程。

接下来是重启之后的重要校验:
0048939C   MOV EDX, DWORD PTR DS:                  ;43769615
004893A2   PUSH 1                                          ; /Arg4 = 00000001
004893A4   PUSH ECX                                          ; |Arg3
004893A5   PUSH EDX                                          ; |Arg2 => 00207224
004893A6   PUSH EDI                                          ; |Arg1
004893A7   CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaInStr>]   ; \43769615 在注册码解密数据中?
004893AD   MOV ECX, DWORD PTR DS:                  ;27341050
004893B3   XOR EBX, EBX
004893B5   TEST EAX, EAX
004893B7   MOV EAX, DWORD PTR SS:
004893BA   PUSH 1                                          ; /Arg4 = 00000001
004893BC   PUSH EAX                                          ; |Arg3
004893BD   PUSH ECX                                          ; |Arg2 => 001984EC
004893BE   PUSH EDI                                          ; |Arg1
004893BF   SETLE BL                                          ; |
004893C2   CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaInStr>]   ; \27341050 在注册码解密数据中?

如果这里通不过的话,就要求进行软件注册了。
上面的校验过程省略了注册码的变换描述。


注册码变换:
004726EFMOV DWORD PTR SS:, 5                     ;循环==>
……
00472727CALL NEAR DWORD PTR DS:[<&msvbvm60.rtcMidCharVar>>; \读取注册码一位
……
00472752MOV DWORD PTR SS:, u中文内?00410C14         ;“-”
……
00472774CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaVarTstEq>>; \是否是“-”
……
0047277FJE u中文内?00472A81                                  ;不是则跳
……
00472858CALL NEAR DWORD PTR DS:[<&msvbvm60.rtcMidCharVar>>; \是则截取“-”号前面一截
……
004729ACCALL u中文内?00472EC0                              ;换序
……
004729CCCALL u中文内?00472150                              ;换算
……004729E8PUSH EDX                                          ; /Arg3
004729E9LEA EAX, DWORD PTR SS:                   ; |
004729EFPUSH EAX                                          ; |Arg2
004729F0LEA ECX, DWORD PTR SS:                   ; |
004729F6PUSH ECX                                          ; |Arg1
004729F7CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaVarCat>]; \__vbaVarCat
004729FDPUSH EAX                                          ; /Arg3
004729FELEA EDX, DWORD PTR SS:                   ; |
00472A04PUSH EDX                                          ; |Arg2
00472A05LEA EAX, DWORD PTR SS:                   ; |
00472A0BPUSH EAX                                          ; |Arg1
00472A0CCALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaVarCat>]; \__vbaVarCat--结果拼接
……
00472AADJNZ u中文内?004726EF                                 ;循环<==
注册分为n段,每段之间用“-”号连接,这段代码的作用就是按“-”将注册码进行切割,其中前n-1段注册码在这段代码中进行解密。解密的结果仍按原来的顺序用“-”号进行连接。
最后一段用下面的代码解密并连接:
00472CE9CALL u中文内?00472EC0                              ;换序
……
00472D09CALL u中文内?00472150                              ;换算
00472D0ELEA EDX, DWORD PTR SS:
00472D11PUSH EDX                                          ; /Arg3
00472D12LEA EAX, DWORD PTR SS:                   ; |
00472D18PUSH EAX                                          ; |Arg2
00472D19LEA ECX, DWORD PTR SS:                   ; |
00472D1FPUSH ECX                                          ; |Arg1
00472D20CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaVarCat>]; \__vbaVarCat
其实处理方法完全相同。
前面的检验就是使用了这里解密的数据。

从前面的检验不难看到,注册码至少需要两段。
如果恰好两段,且也满足条件,是能够注册的,所有的使用和现实均正常。
如果你认为注册码恰好两段,那你就错了,在某个时刻(某个动作下)你会发现你的注册信息以及主程序悄然失踪。那就是说,程序还有暗桩。
请看:
0048D1D2   PUSH u中文内?00411978                               UNICODE "*.EXE"
0048D1F2   MOV EDX, u中文内?00411988                           UNICODE "in.bat"
0048D216   PUSH u中文内?0041199C                               UNICODE ":START"
0048D22A   PUSH u中文内?004119B8                               UNICODE "del "
0048D252   PUSH u中文内?004119C8                               UNICODE "if exist "
0048D262   PUSH u中文内?004117B8                               UNICODE " GOTO START"
0048D28D   PUSH u中文内?004119B8                               UNICODE "del "
0048D292   PUSH u中文内?00411988                               UNICODE "in.bat"
这些地址指示的位置是生成一个批处理程序"in.bat",然后反复寻找当前"程序名*.EXE",一经发现,立马"del ",当已经没有时,"in.bat"就自杀。
有了这段地址代码,定位相应代码就不成问题了。那触发这个批处理程序的原因是什么呢?接着看:
1、机器码和解密数据之间是什么关系呢:
回忆我的过程结果:
结果1=27341050
结果2=43769615
0046BEA1MOV EAX, DWORD PTR DS:                  ;27341050
……
0046BEAACALL NEAR DWORD PTR DS:[<&msvbvm60.rtcR8ValFromBs>; \转化为整数
……
0046BEB8XOR EAX, 23D0B5                                 ;27341050 xor 23D0B5=25354319
0046BEBDPUSH EAX                                          ; /Arg1
0046BEBECALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaStrI4>]   ; \转化为数字
……
0046BECDMOV ECX, DWORD PTR DS:                  ;43769615
0046BED3PUSH ECX                                          ; /Arg1 => 00000000
0046BED4CALL NEAR DWORD PTR DS:[<&msvbvm60.rtcR8ValFromBs>; \转化为整数
0046BEDACALL NEAR EBX
0046BEDCXOR EAX, 274996                                 ;43769615 xor 274996=45913753
0046BEE1PUSH EAX                                          ; /Arg1
0046BEE2CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaStrI4>]   ; \转化为数字
……
0046BF05MOV ECX, DWORD PTR DS:                  ;25354319
0046BF0BMOV EBX, DWORD PTR DS:
0046BF0DPUSH ECX                                          ; /Arg2 => 00000000
0046BF0EPUSH u中文内?00410244                           ; |Arg1 = 00410244
0046BF13MOV DWORD PTR SS:, EAX                  ; |
0046BF19CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaStrCat>]; \拼接
……
0046BF26MOV EDX, DWORD PTR DS:                  ;45913753
0046BF2CPUSH EAX                                          ; /Arg2
0046BF2DPUSH EDX                                          ; |Arg1 => 00000000
0046BF2ECALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaStrCat>]; \拼接成机器码
而机器码恰好就是:25354319_45913753.
原来由机器码得到参与比较的数据要经过这样的计算。
2、注册码解密过程:
1)、换序
为便于观察,假设一段注册码为:12345678。换序后的结果为:81726354。
2)、换算
0047234FMOV DWORD PTR SS:, 8                     ;循环==>
……
0047238ACALL NEAR DWORD PTR DS:[<&msvbvm60.rtcMidCharVar>>; \读取一位
……
004723C4CALL NEAR DWORD PTR DS:[<&msvbvm60.rtcAnsiValueBs>; \取ASC码
004723CAMOV SI, AX                                        ;转移结果
004723CDPUSH u中文内?00410C0C                              ; /C
004723D2CALL NEAR DWORD PTR DS:[<&msvbvm60.rtcAnsiValueBs>; \取C的ASC码
004723D8SUB SI, AX                                        ;两者相减
……
00472426CALL NEAR DWORD PTR DS:[<&msvbvm60.rtcR8ValFromBs>; \取结果
0047242CFADD QWORD PTR DS:                        ;+0.5
00472432CMP DWORD PTR DS:, 0
00472439JNZ SHORT u中文内?00472443
0047243BFDIV QWORD PTR DS:                        ;/2.5
……
0047245ECALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaFPInt>]   ;取整
……
004724A8CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaVarCat>]; \转化为数字进行拼接
……
004724E8JNZ u中文内?0047234F                              ;循环<==
经过这段代码的运算,注册码的每一段就算解密好了。

3、暗桩校验:
004BA207CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaInStrVar>>; \NMZH-是否在注册码中
……
004BA243TEST ECX, ECX
004BA245JE SHORT u中文内?004BA26D                         ; 在就跳
……
004BA252CALL u中文内?0048D380                           ;跳至批处理
004BA257LEA ECX, DWORD PTR SS:
004BA25ACALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaFreeVar>] ;msvbvm60.__vbaFreeVar
004BA260MOV DWORD PTR SS:, 15                      ; |
004BA267CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaEnd>]   ; \__vbaEnd
004BA26DMOV DWORD PTR SS:, 17
……
004BA293CALL u中文内?004725F0                              ;注册码变换
……
004BA2A2CALL NEAR DWORD PTR DS:[<&msvbvm60.rtcRightCharVa>; \读取解码结果的后4位
……
004BA2CCCALL NEAR DWORD PTR DS:[<&msvbvm60.rtcRightCharVa>; \读取机器码计算过程中间值的后4位
……
004BA2DDCALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaVarTstNe>>; \比较
……
004BA30BTEST EDX, EDX
004BA30DJE SHORT u中文内?004BA335
……
004BA31ACALL u中文内?0048D380                              ;跳至批处理
004BA31FLEA ECX, DWORD PTR SS:
004BA322CALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaFreeVar>] ;msvbvm60.__vbaFreeVar
004BA328MOV DWORD PTR SS:, 19                      ; |
004BA32FCALL NEAR DWORD PTR DS:[<&msvbvm60.__vbaEnd>]   ; \__vbaEnd
004BA335MOV DWORD PTR SS:, 1D

总结:
1、注册验证模式为:F(注册码)=机器码。
2、难点:分散式检验;
3、虽有破坏性代码,但只自杀,不伤及无辜。
4、我贴的代码是为需所用,顺序和位置可能有些混乱,且同等代码存在多处;且为节省篇幅,只保留关键代码。你若也想跟踪,找特征码定位。
5、作者的相关软件的注册功能模式类似。

逍遥枷锁 发表于 2014-7-28 18:11:49

老师出个教程讲解下最好。谢谢

pentium450 发表于 2014-7-28 18:12:32

正在纠结这个问题呢,楼主的文章就来了,及时雨啊!不仅自杀,还有自行退出,很有意思!感谢!!

阳光宅男 发表于 2014-7-28 18:55:42

学习了,感谢分享了

montana 发表于 2014-7-28 19:28:22

楼主厉害,学习了。

冷月孤心 发表于 2014-7-28 20:00:22

学习了,感谢分享精品文章

cfc1680 发表于 2014-7-29 08:19:18

支持一下了,感谢分享啦

湘灵鼓瑟 发表于 2014-7-29 14:33:47

学习,感谢分享

老万 发表于 2014-7-29 17:13:52

楼主厉害,学习了

beijingren 发表于 2014-8-25 16:28:47

拜读学习了,马克一下
页: [1] 2
查看完整版本: 一含自杀代码的软件注册算法分析