[原创]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的数据成员,看了好久也没有看出来具体作用
感谢科锐!
