欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]Ultra Video Splitter 6.4.1208 算法分析

[原创]Ultra Video Splitter 6.4.1208 算法分析

Ultra Video Splitter 6.4.1208 算法分析

【文章标题】Ultra Video Splitter 6.4.1208算法分析
【文章作者】JieLiuHouZi
【原版下载】www.aone-soft.com
【保护方式】序列号
【分析过程】

1.关键代码定位

随便输入一组用户名和密码,点击“确定”,弹出“无效注册用户名或注册码”

可以以此为突破口,OD中载入搜索字符串,但是搜索相关关键字,并没有找到任何有用信息。怀疑是加密了,或者“语言包”(我也是一点一点摸索出来的,很多国外的软件都是这么搞的。没思路时,可以去安装目录下碰碰运气)

进入安装目录,找到“Languages”文件夹,找到一堆语言包

其中有这个,那么在OD中搜索“Invalid License”,就可以快速定位到关键点了

定位到以下代码段:

.text:0040DEFC                 mov     [esp+18h], eax

.text:0040DF00                 mov     [esp+38h], ebx

.text:0040DF04                 cmp     dword_4F9FD0, ebx ; 关键点在这个全局变量,ebx在上面已清零

.text:0040DF0A                 jnz     loc_40E076      ; 跳转到注册成功,否则注册失败

.text:0040DF10                 push    ecx

.text:0040DF11                 mov     ecx, esp

.text:0040DF13                 mov     [esp+24h], esp

.text:0040DF17                 push    offset aNotFoundError ; "Not Found Error"

.text:0040DF1C                 call    sub_4057E0

.text:0040DF21                 push    ecx

.text:0040DF22                 mov     byte ptr [esp+40h], 1

.text:0040DF27                 mov     ecx, esp

.text:0040DF29                 mov     [esp+2Ch], esp

.text:0040DF2D                 push    offset aInvalidLicense ; "Invalid License"

.text:0040DF32                 call    sub_4057E0

.text:0040DF37                 push    ecx

.text:0040DF38                 mov     byte ptr [esp+44h], 2

.text:0040DF3D                 mov     ecx, esp

.text:0040DF3F                 mov     [esp+34h], esp

.text:0040DF43                 push    offset aDlg     ; "Dlg"

IDA中参考引用查看是那里修改了这个全局变量,重点关注这两个mov指令

来到下面这段代码,大致功能是:

   1.先调用CreateFileMappingA()创建共享内存区域

   2.再调用CreateProcessA()创建进程avm.exe,其中命令行参数是" -sfm Ultra_Video_Splitter -checkreg"

   3.具体的算法验证模块是在avm.exe进程中完成的。算法验证完成后,会将验证结果写入到之前创建的“内存共享区”中

   4.最终根据“内存共享区”中的结果判断是否注册成功

.text:00434AC9                 mov     dword ptr [esi], offset ??_7CSFMServer@@6B@ 

.text:00434ACF                 mov     [esi+4], ebx

.text:00434AD2                 mov     [esi+8], ebx

.text:00434AD5                 mov     [esi+0Ch], ebx

.text:00434AD8                 mov     [esi+10h], ebx

.text:00434ADB                 mov     [esi+14h], ebx

.text:00434ADE                 mov     [esi+18h], ebx

.text:00434AE1                 mov     [esi+1Ch], ebx

.text:00434AE4                 call    sub_430010      ; 该函数中创建文件映射,共享内存

.text:00434AE9                 jmp     short loc_434AED

.text:00434AEB ; ---------------------------------------------------------------------------

.text:00434AEB

.text:00434AEB loc_434AEB:

.text:00434AEB                 xor     esi, esi

.text:00434AED

.text:00434AED loc_434AED:

.text:00434AED                 mov     [esp+82Ch+var_810], esi

.text:00434AF1                 mov     esi, [esi+0Ch]

.text:00434AF4                 cmp     esi, ebx

.text:00434AF6                 jz      short loc_434AA1

.text:00434AF8                 mov     [esp+82Ch+var_818], esi

.text:00434AFC                 jmp     short loc_434B02

.text:00434AFE ; ---------------------------------------------------------------------------

.text:00434AFE

.text:00434AFE loc_434AFE:

.text:00434AFE                 mov     [esp+82Ch+var_818], eax

.text:00434B02

.text:00434B02 loc_434B02:

.text:00434B02                 mov     edi, [esp+82Ch+var_818]

.text:00434B06                 add     edi, 10h

.text:00434B09                 call    _appsys_regdata_ini@0

.text:00434B0E                 push    eax             ; Src

.text:00434B0F                 push    400h            ; SizeInBytes

.text:00434B14                 push    edi             ; Dst

.text:00434B15                 call    _strcpy_s       ; 将data.ini文件路径复制到共享内存

.text:00434B1A                 add     esp, 0Ch

.text:00434B1D                 push    offset byte_4F9D80 ; 字符串“Ultra_Video_Splitter”

.text:00434B22                 lea     eax, [esp+830h+Dest]

.text:00434B26                 push    offset aSfmSCheckreg ; " -sfm %s -checkreg"

.text:00434B2B                 push    eax             ; Dest

.text:00434B2C                 mov     [esp+838h+Dest], bl

.text:00434B30                 call    _sprintf        ; 格式化命令行参数为" -sfm Ultra_Video_Splitter -checkreg",作为CreateProcessA()函数的参数

.text:00434B35                 add     esp, 0Ch

.text:00434B38                 lea     esi, [esp+82Ch+hHandle]

.text:00434B3C                 call    sub_430550      ; 获取当前文件路径“C:\Program Files\Ultra Video Splitter\”

.text:00434B41                 mov     ecx, [eax]

.text:00434B43                 push    offset aAvmExe  ; "avm.exe"

.text:00434B48                 push    ecx

.text:00434B49                 lea     edx, [esp+834h+ApplicationName]

.text:00434B50                 push    offset aSS_0    ; "%s%s"

.text:00434B55                 push    edx             ; Dest

.text:00434B56                 call    _sprintf        ; 格式化为:"C:\Program Files\Ultra Video Splitter\avm.exe",作为CreateProcessA的参数

.text:00434B5B                 mov     eax, [esp+83Ch+hHandle]

.text:00434B5F                 add     eax, 0FFFFFFF0h

.text:00434B62                 add     esp, 10h

.text:00434B65                 lea     ecx, [eax+0Ch]

.text:00434B68                 or      edx, 0FFFFFFFFh

.text:00434B6B                 lock xadd [ecx], edx

.text:00434B6F                 dec     edx

.text:00434B70                 test    edx, edx

.text:00434B72                 jg      short loc_434B7E

.text:00434B74                 mov     ecx, [eax]

.text:00434B76                 mov     edx, [ecx]

.text:00434B78                 push    eax

.text:00434B79                 mov     eax, [edx+4]

.text:00434B7C                 call    eax

.text:00434B7E

.text:00434B7E loc_434B7E:

.text:00434B7E                 lea     ecx, [esp+82Ch+hHandle]

.text:00434B82                 push    ecx             ; int

.text:00434B83                 mov     ecx, dword_4FA4DC

.text:00434B89                 lea     edx, [esp+830h+Dest]

.text:00434B8D                 push    edx             ; lpCommandLine=" -sfm Ultra_Video_Splitter -checkreg"

.text:00434B8E                 lea     eax, [esp+834h+ApplicationName]

.text:00434B95                 push    eax             ; lpApplicationName="C:\Program Files\Ultra Video Splitter\avm.exe"

.text:00434B96                 call    sub_432750      ; 函数内部调用CreateProcessA创建进程

.text:00434B9B                 add     esp, 0Ch

.text:00434B9E                 test    eax, eax

.text:00434BA0                 jz      short loc_434BE0

.text:00434BA2                 mov     esi, [esp+82Ch+hHandle]

.text:00434BA6                 mov     ebp, ds:WaitForSingleObject

.text:00434BAC                 push    1               ; dwMillisecondsA

.text:00434BAF                 call    ebp ; WaitForSingleObject

.text:00434BB1                 test    eax, eax

.text:00434BB3                 jz      short loc_434BC3

.text:00434BB5

.text:00434BB5 loc_434BB5:

.text:00434BB5                 call    sub_432600

.text:00434BBA                 push    1               ; dwMilliseconds

.text:00434BBC                 push    esi             ; hHandle

.text:00434BBD                 call    ebp ; WaitForSingleObject

.text:00434BBF                 test    eax, eax

.text:00434BC1                 jnz     short loc_434BB5

.text:00434BC3

.text:00434BC3 loc_434BC3:

.text:00434BC3                 push    edi             ; edi指向之前创建的文件映射的一块内存,存储是否注册成功的标志

.text:00434BC4                 call    j__atol

.text:00434BC9                 mov     ebp, eax

.text:00434BCB                 add     esp, 4

.text:00434BCE                 cmp     ebp, ebx

.text:00434BD0                 jnz     short loc_434C22 ; 跳转到失败,不跳则成功

.text:00434BD2                 mov     eax, 1

.text:00434BD7                 mov     dword_4F9FD0, eax ; 全局变量赋值为1表示成功

.text:00434BDC                 mov     [esp+82Ch+var_814], eax

.text:00434BE0

.text:00434BE0 loc_434BE0:

.text:00434BE0                 cmp     [esp+82Ch+var_818], ebx

.text:00434BE4                 jz      short loc_434BF5

.text:00434BE6                 push    400h            ; Size

.text:00434BEB                 push    ebx             ; Val

.text:00434BEC                 push    edi             ; Dst

.text:00434BED                 call    _memset

.text:00434BF2                 add     esp, 0Ch

.text:00434BF5

.text:00434BF5 loc_434BF5:

.text:00434BF5                 mov     ecx, [esp+82Ch+var_810]

.text:00434BF9                 cmp     ecx, ebx

.text:00434BFB                 jz      short loc_434C05

.text:00434BFD                 mov     edx, [ecx]

.text:00434BFF                 mov     eax, [edx]

.text:00434C01                 push    1

.text:00434C03                 call    eax

.text:00434C05

.text:00434C05 loc_434C05:

.text:00434C05                 mov     ecx, [esp+82Ch+var_8]

.text:00434C0C                 mov     eax, [esp+82Ch+var_814]

.text:00434C10                 pop     edi

.text:00434C11                 pop     esi

.text:00434C12                 pop     ebp

.text:00434C13                 pop     ebx

.text:00434C14                 xor     ecx, esp

.text:00434C16                 call    @__security_check_cookie@4

.text:00434C1B                 add     esp, 81Ch

.text:00434C21                 retn

.text:00434C22 ; ---------------------------------------------------------------------------

.text:00434C22

.text:00434C22 loc_434C22:                                   ;走到这里注册失败

.text:00434C22                 mov     dword_4F9FD0, ebx

.text:00434C28                 call    _appsys_regdata_ini@0 ; appsys_regdata_ini()

.text:00434C2D                 mov     esi, eax

.text:00434C2F                 call    sub_430EE0

.text:00434C34                 cmp     ebp, 2

.text:00434C37                 jnz     short loc_434BE0

.text:00434C39                 jmp     loc_434C40

这里需要知道创建文件映射时的“对象名称”,也就是下图的“Ultra_Video_Splitter”,后面在调试avm.exe时会用到

接下来进入avm.exe分析,OD载入(注意输入命令行参数)。因为使用的是文件映射的方式实现的内存共享,所以在avm.exe中指定会打开内存共享对象,所以我们可以对OpenFileMappingA()下断点,坐等它来

下面将调用MapViewOfFile()将一个文件映射对象映射到当前应用程序的地址空间,注意返回值就是共享内存的首地址

到此就可以在验证注册是否成功的那个标志下内存写入断点,等着看是那里修改了该标志。

内存断点断下来之后,中间需要几次回溯,才能定位到注册验证的关键函数(在AppSys.dll中)

.text:10001D8B                 push    edx             ; 假码

.text:10001D8C                 push    eax             ; 用户名

.text:10001D8D                 call    _appsys_check@8 ; 算法校验函数

大致看一下这个注册函数

.text:10001870                 sub     esp, 484h

.text:10001876                 call    sub_100012C0

.text:1000187B                 test    eax, eax

.text:1000187D                 jz      loc_10001B43

.text:10001883                 push    ebx

.text:10001884                 push    ebp

.text:10001885                 push    esi

.text:10001886                 push    edi

.text:10001887                 mov     ecx, 40h

.text:1000188C                 xor     eax, eax

.text:1000188E                 lea     edi, [esp+494h+szAppName]

.text:10001895                 lea     edx, [esp+494h+szAppName]

.text:1000189C                 rep stosd

.text:1000189E                 stosb

.text:1000189F                 mov     ecx, 40h

.text:100018A4                 xor     eax, eax

.text:100018A6                 lea     edi, [esp+494h+szUserName]

.text:100018AD                 rep stosd

.text:100018AF                 stosb

.text:100018B0                 mov     ecx, 40h

.text:100018B5                 xor     eax, eax

.text:100018B7                 lea     edi, [esp+494h+szBuffer]

.text:100018BE                 rep stosd

.text:100018C0                 stosb

.text:100018C1                 mov     eax, dword_1001A024

.text:100018C6                 sub     edx, eax

.text:100018C8

.text:100018C8 loc_100018C8:

.text:100018C8                 mov     cl, [eax]

.text:100018CA                 mov     [edx+eax], cl

.text:100018CD                 inc     eax

.text:100018CE                 test    cl, cl

.text:100018D0                 jnz     short loc_100018C8 ; 拷贝字符串Utra Video Splitter

.text:100018D2                 mov     eax, [esp+494h+arg_0] ; 用户名

.text:100018D9                 lea     edx, [esp+494h+szUserName]

.text:100018E0                 sub     edx, eax

.text:100018E2

.text:100018E2 loc_100018E2:

.text:100018E2                 mov     cl, [eax]

.text:100018E4                 mov     [edx+eax], cl

.text:100018E7                 inc     eax

.text:100018E8                 test    cl, cl

.text:100018EA                 jnz     short loc_100018E2 ; 拷贝用户名

.text:100018EC                 xor     ecx, ecx

.text:100018EE

.text:100018EE loc_100018EE:

.text:100018EE                 mov     al, [esp+ecx+494h+szUserName] ; 下面这个循环用来填充用户名中间的非法字符

.text:100018F5                 cmp     al, ' '

.text:100018F7                 jz      short loc_1000191C

.text:100018F9                 test    al, al

.text:100018FB                 jz      short loc_1000191C

.text:100018FD                 cmp     al, '0'

.text:100018FF                 jl      short loc_10001905

.text:10001901                 cmp     al, '9'

.text:10001903                 jle     short loc_1000191C

.text:10001905

.text:10001905 loc_10001905:

.text:10001905                 cmp     al, 'a'

.text:10001907                 jl      short loc_1000190D

.text:10001909                 cmp     al, 'z'

.text:1000190B                 jle     short loc_1000191C

.text:1000190D

.text:1000190D loc_1000190D:

.text:1000190D                 cmp     al, 'A'

.text:1000190F                 jl      short loc_10001915

.text:10001911                 cmp     al, 'Z'

.text:10001913                 jle     short loc_1000191C

.text:10001915

.text:10001915 loc_10001915:

.text:10001915                 mov     [esp+ecx+494h+szUserName], cl

.text:1000191C

.text:1000191C loc_1000191C:

.text:1000191C                 inc     ecx

.text:1000191D                 cmp     ecx, 100h

.text:10001923                 jl      short loc_100018EE

.text:10001925                 xor     eax, eax

.text:10001927

.text:10001927 loc_10001927:

.text:10001927                 mov     cl, [esp+eax+494h+szUserName] ; 用户名的字符串

.text:1000192E                 mov     bl, [esp+eax+494h+szAppName] ; 字符串:Utra Video Splitter

.text:10001935                 add     cl, bl

.text:10001937                 mov     [esp+eax+494h+szBuffer], cl ; 新的缓冲区:接受用户名和Utra Video Splitter相加的和

.text:1000193E                 inc     eax

.text:1000193F                 cmp     eax, 100h

.text:10001944                 jl      short loc_10001927 ; 这一段循环就是用户名和Utra Video Splitter相加放到一个新的缓冲区

.text:10001946                 push    0

.text:10001948                 lea     ecx, [esp+498h+obj_0]

.text:1000194C                 call    W_sub_10004070  ; 构造函数

.text:10001951                 push    0

.text:10001953                 lea     ecx, [esp+498h+obj_1]

.text:10001957                 call    W_sub_10004070  ; 构造函数

.text:1000195C                 lea     ecx, [esp+494h+obj_3]

.text:10001960                 call    sub_10001B50    ; 对象数组的构造函数,有4个对象元素

.text:10001960                                         ; ;

.text:10001965                 push    4               ; DWORD数组的元素个数

.text:10001967                 push    offset dword_100160B0 ; DWORD数组

.text:1000196C                 lea     ecx, [esp+49Ch+obj_5]

.text:10001970                 call    W_sub_10003F40  ; 对m_pObj->m_pdwAry数组进行赋值

.text:10001975                 push    4

.text:10001977                 push    offset dword_100160C0

.text:1000197C                 lea     ecx, [esp+49Ch+obj_6]

.text:10001980                 call    W_sub_10003F40

.text:10001985                 mov     edx, dword_100160D0

.text:1000198B                 lea     ecx, [esp+494h+obj_2]

.text:1000198F                 push    edx

.text:10001990                 call    W_sub_10004070  ; 构造函数

.text:10001995                 lea     eax, [esp+494h+obj_2]

.text:10001999                 lea     ecx, [esp+494h+obj_4]

.text:1000199D                 push    eax

.text:1000199E                 call    W_sub_100040D0  ; 对象拷贝

.text:100019A3                 lea     ecx, [esp+494h+obj_2]

.text:100019A7                 call    W_sub_10004120  ; 引用计数-1

.text:100019AC                 push    8

.text:100019AE                 push    offset dword_100160D4

.text:100019B3                 lea     ecx, [esp+49Ch+obj_3]

.text:100019B7                 call    W_sub_10003F40

.text:100019BC                 mov     ecx, 8

.text:100019C1                 xor     eax, eax

.text:100019C3                 lea     edi, [esp+494h+var_42C]

.text:100019C7                 xor     edx, edx

.text:100019C9                 rep stosd               ; memset(edi, 0, 8)

.text:100019CB                 lea     edi, [esp+494h+szBuffer]

.text:100019D2                 or      ecx, 0FFFFFFFFh

.text:100019D5                 repne scasb

.text:100019D7                 not     ecx

.text:100019D9                 dec     ecx

.text:100019DA                 jz      short loc_100019FD

.text:100019DC

.text:100019DC loc_100019DC:

.text:100019DC                 mov     cl, [esp+edx+494h+szBuffer]

.text:100019E3                 lea     edi, [esp+494h+szBuffer]

.text:100019EA                 mov     [esp+edx+494h+var_42C], cl

.text:100019EE                 or      ecx, 0FFFFFFFFh

.text:100019F1                 xor     eax, eax

.text:100019F3                 inc     edx

.text:100019F4                 repne scasb

.text:100019F6                 not     ecx

.text:100019F8                 dec     ecx

.text:100019F9                 cmp     edx, ecx

.text:100019FB                 jb      short loc_100019DC

.text:100019FD

.text:100019FD loc_100019FD:

.text:100019FD                 mov     ecx, 8

.text:10001A02                 xor     eax, eax

.text:10001A04                 lea     edi, [esp+494h+nAry]

.text:10001A08                 rep stosd

.text:10001A0A

.text:10001A0A loc_10001A0A:

.text:10001A0A                 xor     edx, edx

.text:10001A0C                 xor     ecx, ecx

.text:10001A0E                 mov     dl, [esp+eax+494h+var_42C]

.text:10001A12                 mov     cl, [esp+eax+494h+var_42B]

.text:10001A16                 shl     edx, 8

.text:10001A19                 add     edx, ecx

.text:10001A1B                 xor     ecx, ecx

.text:10001A1D                 mov     cl, [esp+eax+494h+var_42A]

.text:10001A21                 add     eax, 4

.text:10001A24                 shl     edx, 8

.text:10001A27                 add     edx, ecx

.text:10001A29                 xor     ecx, ecx

.text:10001A2B                 mov     cl, [esp+eax+494h+var_42D]

.text:10001A2F                 shl     edx, 8

.text:10001A32                 add     edx, ecx

.text:10001A34                 cmp     eax, 20h

.text:10001A37                 mov     [esp+eax+494h+var_470], edx

.text:10001A3B                 jl      short loc_10001A0A

.text:10001A3D                 lea     edx, [esp+494h+nAry]

.text:10001A41                 push    8               ; DWORD数组的长度

.text:10001A43                 push    edx             ; DWORD数组

.text:10001A44                 lea     ecx, [esp+49Ch+obj_0]

.text:10001A48                 call    W_sub_10003F40  ; 对m_pObj->m_pdwAry数组进行赋值

.text:10001A4D                 lea     eax, [esp+494h+obj_0]

.text:10001A51                 lea     ecx, [esp+494h+obj_2]

.text:10001A55                 push    eax

.text:10001A56                 push    ecx

.text:10001A57                 lea     ecx, [esp+49Ch+obj_3] ; this指针指向含有4各对象的数组的第一个对象

.text:10001A5B                 call    W_sub_10003330  ; 巨麻烦的算法函数

.text:10001A60                 push    eax

.text:10001A61                 lea     ecx, [esp+498h+obj_1]

.text:10001A65                 call    W_sub_100040D0  ; 对象拷贝

.text:10001A6A                 lea     ecx, [esp+494h+obj_2]

.text:10001A6E                 call    W_sub_10004120  ; 引用计数-1

.text:10001A73                 lea     edx, [esp+494h+nAry]

.text:10001A77                 push    8

.text:10001A79                 push    edx             ; dwAry:目的操作数

.text:10001A7A                 lea     ecx, [esp+49Ch+obj_1]

.text:10001A7E                 call    W_sub_10003F80  ; 复制指定长度的数组元素到dwAry

.text:10001A7E                                         ; ;

.text:10001A83                 mov     esi, [esp+494h+nAry+4] ; 下面这一串运算最终计算出注册码,是一个DWORD数据

.text:10001A87                 mov     edi, [esp+494h+nAry+1Ch]

.text:10001A8B                 mov     eax, [esp+494h+nAry+10h]

.text:10001A8F                 mov     ecx, [esp+494h+nAry+0Ch]

.text:10001A93                 mov     edx, [esp+494h+nAry+8]

.text:10001A97                 mov     ebp, [esp+494h+nAry+14h]

.text:10001A9B                 lea     ebx, [eax+ecx]

.text:10001A9E                 add     ebx, edx

.text:10001AA0                 add     ebx, esi

.text:10001AA2                 xor     edi, ebx

.text:10001AA4                 mov     ebx, [esp+494h+nAry]

.text:10001AA8                 add     ebp, ebx

.text:10001AAA                 mov     ebx, [esp+494h+nAry+18h]

.text:10001AAE                 xor     ebx, ebp

.text:10001AB0                 mov     ebp, [esp+494h+nAry+14h]

.text:10001AB4                 mov     [esp+494h+nAry+18h], ebx

.text:10001AB8                 add     ebx, ebp

.text:10001ABA                 mov     ebp, [esp+494h+nAry]

.text:10001ABE                 mov     [esp+494h+nAry+1Ch], edi

.text:10001AC2                 add     ebx, ebp

.text:10001AC4                 add     ebx, edi

.text:10001AC6                 add     ebx, eax

.text:10001AC8                 lea     eax, [esp+494h+var_100]

.text:10001ACF                 add     ebx, ecx

.text:10001AD1                 add     ebx, edx

.text:10001AD3                 add     ebx, esi

.text:10001AD5                 push    ebx

.text:10001AD6                 push    offset a08lx    ; "%08lX"

.text:10001ADB                 push    eax             ; char *

.text:10001ADC                 call    _sprintf        ; 将DWORD数据格式化为字符串,也就是最终的注册码

.text:10001AE1                 mov     edx, [esp+4A0h+arg_4]

.text:10001AE8                 lea     ecx, [esp+4A0h+var_100]

.text:10001AEF                 push    8               ; size_t

.text:10001AF1                 push    ecx             ; 真注册码

.text:10001AF2                 push    edx             ; 假注册码

.text:10001AF3                 call    _strncmp        ; 真假注册码比较(内存注册机的话,直接拦截这里就可以了)

.text:10001AF8                 add     esp, 18h

.text:10001AFB                 lea     ecx, [esp+494h+obj_3]

.text:10001AFF                 test    eax, eax

.text:10001B01                 pop     edi

.text:10001B02                 pop     esi

.text:10001B03                 pop     ebp

.text:10001B04                 pop     ebx

.text:10001B05                 jnz     short loc_10001B2C ; 根据析构,可以看出有两个对象,和一个对象数组(包含4个对象)

.text:10001B07                 call    W_sub_10001BC0  ; 析构函数,用于析构数组

.text:10001B0C                 lea     ecx, [esp+484h+obj_1]

.text:10001B10                 call    W_sub_10004120  ; 析构函数

.text:10001B15                 lea     ecx, [esp+484h+obj_0]

.text:10001B19                 call    W_sub_10004120

.text:10001B1E                 mov     eax, 1

.text:10001B23                 add     esp, 484h

.text:10001B29                 retn    8

至此,这款软件的具体注册流程就明了了,看起来好像只要是把上面的函数给还原出来注册机就完成了,当初我也是这么想的,准备大干一场,干着干着就把自己整懵逼了

2.代码还原

原本打算只还原重要的算法函数,从后往前一步一步的推理是哪一步计算出了什么数据,但是这一层一层的往上溯源,大致还原了一个类(其中很多成员函数也没有还原),感觉越搞越复杂,直到搞到又发现一个与注册相关的类,就彻底放弃了从后往前逆推的想法了

由于两个注册相关的类之间有千丝万缕的关系,并且成员函数一层层的嵌套,不再是只还原重要的算法函数就能解决的。无奈只能硬着头皮从头一个一个成员函数的逆,推测各个数据成员之间的关系

由于还原完代码后,发现算法规模有些大(小菜没见过世面),就不一一列举了。说一下类大致框架吧(具体的函数实现见工程代码)

这里列一下,这两个类的数据成员和成员函数,开始时打算为每个成员函数规范的命名的,到了后来发现越来越多,并且一些成员函数的功能实在是不好界定,索性直接使用了IDA中命名风格

类1:CMyObj的类声明

class CMyObj

{

public:

    int    m_nLen;                            //实际使用的长度

    DWORD* m_pdwAry;                          //数组首地址

    int    m_nBufferLen;                      //缓冲区的长度

    int    m_nCount;                          //引用计数

public:

    CMyObj();                                 //sub_10003D30

    CMyObj(int* pInt, int nLen); 

    void Release();                           //sub_10003760

    void sub_10003CE0(int a2);

    //对象拷贝

    void sub_10003D00(CMyObj* pObj);

    //取数组中指定下标的数值

    DWORD sub_10003720(DWORD dwIndex);

    //比较两个对象是否一样

    int sub_10003AB0(CMyObj* pObj);  

    //对对象的数组重新计算并赋值

    BOOL sub_10003B60();                                 

    //对数组的指定元素进行赋值

    void sub_100037E0(DWORD dwIndex, int nNum);

    //扩展数组的空间

    void sub_10003780(DWORD dwLen);

    //根据对象(参数),经过一系列运算赋值给this对象数组

    DWORD sub_10003C80(CMyObj* pObj);  

    //根据对象(参数),经过一系列运算赋值给this对象数组

    int sub_10003C10(CMyObj* pObj);    

    //重新计算并写入对象的int数组

    void sub_10003B20();      

    //计算出数组1

    void sub_10003D90(CMyObj* pObj1, CMyObj* pObj2, CMyObj* pObj3);  

    //算法函数,经过一些列运算,最终返回一个运算结果

    DWORD sub_10003A70();    

    DWORD sub_10003D50(CMyObj* pObj1, CMyObj* pObj2);

    //一个很复杂的函数

    DWORD sub_10003860(CMyObj* pObj1, CMyObj* pObj2, DWORD dwNum);

    //判断m_nLen是否为0

    BOOL sub_10003A30();

    BOOL sub_10003A40(DWORD dwIndex);

    DWORD sub_10003BA0(DWORD dwNum);

};

类2:CMyObj_1的类声明

有些全局的函数,被我放到了CMyObj_1中,定义为了静态成员函数

class CMyObj_1

{

public:

    CMyObj_1();

    CMyObj_1(int nNum);                   //sub_10004070            

    CMyObj_1& operator=(CMyObj_1 &obj);   //sub_100040B0

    CMyObj_1* sub_10004070(int nNum);

    //对m_pObj->m_pdwAry数组进行赋值

    void sub_10003F40(DWORD* pdwAry, DWORD dwLength);  

    //根据引用计数决定是否重新new一个对象

    void sub_10003FB0();

    //对象拷贝

    CMyObj_1* sub_100040D0(CMyObj_1* pObj);

    //引用计数-1

    void sub_10004120();

    //关键算法函数

    CMyObj_1* sub_10003330(CMyObj_1* pObj1, CMyObj_1* pObj2);         

    //对m_pObj->m_dwAry进行运算赋值

    CMyObj_1* sub_10004200(CMyObj_1* pObj);

    int sub_10004000(CMyObj_1* pObj);

    CMyObj_1* sub_10004150(CMyObj_1* pObj);

    CMyObj_1* sub_100048C0(CMyObj_1* pObj);

    CMyObj_1* sub_10004BD0(CMyObj_1* pObj1, CMyObj_1* pObj2, CMyObj_1* pObj3);

    CMyObj_1* sub_10004AE0(CMyObj_1* pObj1, CMyObj_1* pObj2, CMyObj_1* pObj3);

    CMyObj_1* sub_10004A60(CMyObj_1* pObj1, CMyObj_1* pObj2);

    //复制指定长度的数组到pDwordAry

    void sub_10003F80(DWORD* pDwordAry, DWORD dwLen);

    //对象数组中的所有对象引用计数-1

    void sub_10001BC0();

    //对第一个参数的m_pObj->m_dwAry进行运算赋值

    static CMyObj_1* sub_10004340(CMyObj_1* pObj1, CMyObj_1* pObj2, CMyObj_1* pObj3);

    static CMyObj_1* sub_100043C0(CMyObj_1* pObj1, CMyObj_1* pObj2, CMyObj_1* pObj3);

    static CMyObj_1* sub_100045D0(CMyObj_1* pObj1, CMyObj_1* pObj2, CMyObj_1* pObj3);

    static BOOL sub_10003EE0(CMyObj_1* pObj1, CMyObj_1* pObj2);

    //计算出数组1

    static CMyObj_1* sub_10004460(CMyObj_1* pObj1, CMyObj_1* pObj2, CMyObj_1* pObj3);

    static BOOL sub_10003F20(CMyObj_1* pObj1, CMyObj_1* pObj2);

    static CMyObj_1* sub_10004520(CMyObj_1* pObj1, CMyObj_1* pObj2, CMyObj_1* pObj3);

    static CMyObj_1* sub_10004CB0(CMyObj_1* pObj1, CMyObj_1* pObj2, CMyObj_1* pObj3, CMyObj_1* pObj4);

    static BOOL sub_10003F00(CMyObj_1* pObj1, CMyObj_1* pObj2);

    static CMyObj_1* sub_100042C0(CMyObj_1* pObj1, CMyObj_1* pObj2, CMyObj_1* pObj3);

public:

    CMyObj *m_pObj;    

    int     m_Unknow;  //未知用途的一个数据成员(只有一处用到还是很奇怪的用法)

};

贴出_appsys_check()函数的大致实现

限于篇幅原因,其中还有很多代码未被列出具体的函数实现请参考工程文件

#include "stdafx.h"

#include "MyObj.h"

#include "MyObj_1.h"

#include <windows.h>

#include <stdio.h>

#include <stdlib.h>

DWORD g_dwAry_0[4] = { 0x804376CB, 0xAAB4E161, 0xEA9F6A95, 0xEED77AD4 };

DWORD g_dwAry_1[4] = { 0xA9E66FC7, 0x79EC73B0, 0x4E89AE6F, 0x506D7783 };

DWORD g_dwAry_2[8] = { 0xB2555CCD, 0xEE7D1C00, 0x6F08E3AA, 0x37218D51,

                       0x159BBBD7, 0x634AE19A, 0x819CA6AF, 0x4B0977A2 };

int main()

{

    BYTE szAppName[MAXBYTE] = "Ultra Video Splitter";

    BYTE szUserName[MAXBYTE] = { 0 };    //用户名

    BYTE szBuffer[MAXBYTE] = { 0 };

    char szKeyBuffer[MAXBYTE] = { 0 };

    BYTE szTmp[MAXBYTE] = { 0 };

    DWORD dwAry[8] = { 0 };

    //输入用户名

    printf("User Name:");

    scanf("%s", szUserName);

    //填充用户名中间的非法字符

    for (int i = 0; i < MAXBYTE; ++i)

    {

        BYTE ch = szUserName[i];

        if (ch != ' ' && ch && (ch < '0' || ch > '9') && (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))

        {

            szUserName[i] = i;

        }

    }

    //将用户名和软件名相加得到的字符串放到szBuffer中

    for (int i = 0; i < MAXBYTE; ++i)

    {

        szBuffer[i] = szUserName[i] + szAppName[i];

    }

    CMyObj_1 obj_0(0);

    CMyObj_1 obj_1(0);

    //对象数组:obj_3、obj_4、obj_5、obj_6

    CMyObj_1 ObjAry[4] = { CMyObj_1(0), CMyObj_1(0), CMyObj_1(0), CMyObj_1(0) };

    //对m_pObj->m_pdwAry数组进行赋值

    ObjAry[2].sub_10003F40(g_dwAry_1, 4);

    ObjAry[3].sub_10003F40(g_dwAry_0, 4);

    CMyObj_1 obj_2(0x10001);

    //对象拷贝

    ObjAry[1].sub_100040D0(&obj_2);

    obj_2.sub_10004120();

    ObjAry[0].sub_10003F40(g_dwAry_2, 8);

    memset(szTmp, 0, 0x20);

    int nLen = strlen((char*)szBuffer);

    if (nLen != 0)

    {

        for (int i = 0; i < nLen; ++i)

        {

            szTmp[i] = szBuffer[i];

        }

    }

    memset(dwAry, 0, sizeof(dwAry));

    //将字符串(szTmp[])对应的16进制转为DWORD数组(dwAry[])

    for (int i = 0; i < 8; i++)

    {

        DWORD dwV10 = szTmp[4 * i + 1] + (szTmp[4 * i] << 8);

        DWORD dwV11 = szTmp[4 * i + 2];

        dwAry[i] = szTmp[4 * i + 3] + ((dwV11 + (dwV10 << 8)) << 8);

    }

    //对m_pObj->m_pdwAry数组进行赋值

    obj_0.sub_10003F40(dwAry, 8);

    //关键的算法函数

    CMyObj_1* pObj = ObjAry[0].sub_10003330(&obj_2, &obj_0);

    obj_1.sub_100040D0(pObj);

    //引用计数-1

    obj_2.sub_10004120();

    //复制指定长度的数组到dwAry

    obj_1.sub_10003F80(dwAry, 8);

    dwAry[6] ^= dwAry[0] + dwAry[5];

    dwAry[7] ^= dwAry[1] + dwAry[2] + dwAry[4] + dwAry[3];

    sprintf(szKeyBuffer, "%08X", dwAry[1] + dwAry[2] + dwAry[3] + dwAry[4] + dwAry[7] + dwAry[0] + dwAry[5] + dwAry[6]);

    printf("Key:%s\r\n", szKeyBuffer);

    //释放空间(引用计数-1)

    ObjAry[0].sub_10001BC0();

    obj_1.sub_10004120();

    obj_0.sub_10004120();

    system("pause");

    return 0;

}

晒图环节


献上一组注册信息:
用户名:www.pediy.com
注册码:B23A1EB5

未解决的问题:

1.该软件中对于类对象有一些很奇葩的使用方法在sub_100048C0()函数中,当时看了好久也没明白类对象数组中为什么穿插了一个DWORD的标志值,对于此我是强转char*数组解决的

2.至于CMyObj_1中的有一个暂定为m_Unknow的数据成员,看了好久也没有看出来具体作用

感谢科锐!

上传的附件 UltraVideoSplitterKeyGen.zip

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功