声明:请支持使用正版软件,尊重原作者的劳动成果,此文只做技术交流,如有他人利用,产生后果,不负任何责任。
最近破解一款会务管理软件,用PEid侦查外壳,发现是是穿山甲的 外壳,
但是还是无法显示出完整的信息出来
可以发现 Upx0,UPX1的节段,说明,加了双层外壳,还有一层UPX的压缩壳,首先用OD打开这个软件
看到开头是pushad,然后按F8,到达
压缩外壳,接下来我们就用esp定律来脱壳,在esp寄存器上 右键---数据窗口跟随
接下来在内存窗口右键----断点---硬件访问-------word字方式
然后F9运行,到达下图的位置,看到一个一个jmp,明显这里是跳转到,第二个压缩外壳的入口点,然后F8
F8后到达一个pushad,这是另外的外壳的入口点,我们可以dump进程,也可以继续,我们首先试试dump他看看,是否是UPX的入口
dump进程
把dump的进程保存下来,我们用PEID查看
这下正常了,显示出了软件信息,delphi写的,现在只剩下UPX的外壳,可以用UPX脱壳机直接脱壳。
我们就这上上个步骤继续,不再中途dump,我们继续F9后到达,一个jmp调转,可能是跳到入口点,也可能是第三层壳,F8
F8后到达发现到达入口点,现在我们可以完全dump ,外壳至此全部脱掉
用PEID检查发现,外壳已经全部脱掉
请继续关注第二部份,破解和逆向分析算法。
第二步分析注册码或者暴力破解
delphi写的软件我们可以直接用Dede来逆向分析。
打开软件登录
可以看到试用版弹出此对话框,只能显示4个人的信息,不提供导出,于是我们可以破解掉,取消限制,并且不单出此对话框。
我们可以查找这段字符窜,
找到了该字符窜地址在08e5a28,于是双击
于是看到call unpacked.0043d914,可以猜测这里call是调用那个试用版信息提示的对话框,我们把这里的指令nop掉后,信息不再弹出来
nop后的结果,运行后不再弹出提示
由于试用版有功能限制,右边只能显示4个人,我开始破解
找到关键点就可以破解掉,发现每次点击刷新按钮时,每次会重新查询插入节点,于是猜测在刷新按钮中会只插入4个,于是查看dede 的返汇编信息,发现主窗口为TfrmMain
找到refresh按钮的事件,
发现只调用了一个函数
我们找到该函数然后双击 进入
发现函数里没什么特殊之处的操作,唯一特殊的地方就是
查询数据库时有个top 4的限制,这会不会就是 软件限制的原因,可以看到地址为008e8c04,于是在od的命令行里输入dd 0x8e8c04
右键长型------Asicii地址转换后
于是我们把中间top 4改成空,即ascii为0x20,修改后为
于是可以保存修改后的软件,
点击全部复制,关闭后,令存在一个exe.
我们重新打开修改后的软件,发现可以全部显示人员的信息了。
OK,完成
第三部分算法分析
第三层就是分析算法了,要求生成机器码
以这个为例子,我们可以分析这个如何计算出来,通过大量的分析后我们发现该类为Tfrm_getuserinfo
对应的
双击btn_okClick,为点击OK的事件,地址为0x0087AE0C,于是我们在Od里断下改地址。ctrl+g 输入 0x0087AE0C到达该地址双击下断点
然后点击OK按钮
打开ida pro ,反汇编该exe,找到0x87AE0C地址的函数
按F5,即可显示出伪C代码出来
int __usercall Tfrm_getuserinfo_btn_okClick
{
int v2; // ebx@1
const CHAR *v3; // ST08_4@2
const CHAR *v4; // eax@2
int v5; // ST0C_4@3
int v6; // ecx@3
int v7; // edx@3
int v9; // edx@4
int v10; // ecx@4
int v11; // ecx@4
int v12; // edx@4
int v13; // ecx@4
int v14; // ecx@4
int v15; // [sp-10h] [bp-2Ch]@1
int (*v16)(); // [sp-Ch] [bp-28h]@1
int *v17; // [sp-8h] [bp-24h]@1
int v18; // [sp-4h] [bp-20h]@1
int v19; // [sp+0h] [bp-1Ch]@1
int v20; // [sp+4h] [bp-18h]@1
int v21; // [sp+8h] [bp-14h]@1
int v22; // [sp+Ch] [bp-10h]@1
int (*v23)(); // [sp+10h] [bp-Ch]@1
int v24; // [sp+14h] [bp-8h]@1
int v25; // [sp+18h] [bp-4h]@3
int v26; // [sp+1Ch] [bp+0h]@1
v23 = 0;
v22 = 0;
v21 = 0;
v20 = 0;
v19 = 0;
v18 = a2;
v2 = a1;
v17 = &v26;
v16 = loc_87AF3E;
v15 = *MK_FP(__FS__, 0);
*MK_FP(__FS__, 0) = &v15;
Controls__TControl__GetText(*(_DWORD *)(a1 + 784), &v23);
Sysutils__Trim(v23, &v24);
if ( sub_40519C(v24) >= 5 )
{
sub_879E60(&v25);
v5 = v25;
Controls__TControl__GetText(*(_DWORD *)(v2 + 784), &v19);
Sysutils__Trim(v19, &v20);
System____linkproc___LStrCatN(&v21, 3, v6, v18, v17, v16, v15, v5, &str____87[1], v20);
LOWORD(v7) = -8747;
sub_877D90(v21, v7, &v22);
Controls__TControl__SetText(*(_DWORD *)(v2 + 788), v22);
Controls__TControl__Show(*(_DWORD *)(v2 + 760));
sub_47ABF4(*(_DWORD *)(v2 + 768));
(*(void (**)(void))(**(_DWORD **)(v2 + 788) + 196))();
Stdctrls__TCustomEdit__SelectAll(*(_DWORD *)(v2 + 788));
}
else
{
v3 = (const CHAR *)System____linkproc___LStrToPChar(off_9171D4);
v4 = (const CHAR *)System____linkproc___LStrToPChar(off_9171D8);
j_MessageBoxA_0(0, v4, v3, 0x30u);
(*(void (__stdcall **)(int, int (*)(), int *, int, int, int))(**(_DWORD **)(v2 + 784) + 196))(
v15,
v16,
v17,
v18,
v19,
v20);
}
v9 = v21;
v10 = (int)v23;
*MK_FP(__FS__, 0) = v21;
v23 = loc_87AF45;
System____linkproc___LStrClr(&v19, v9, v10);
System____linkproc___LStrArrayClr(&v20, 3, v11);
System____linkproc___LStrClr(&v23, v12, v13);
return System____linkproc___LStrArrayClr(&v24, 2, v14);
}
看下来这两个函数比较特殊
sub_40519C(v24) >= 5 该函数计算长度,函数前面有GetText说明是得到Edit控件的字符窜的,然后计算长度是否>5,大于5才计算机器码
sub_879E60()函数计算机器码
于是我们在od中 单步F8,运行到该函数事,,前面的函数都是获得字符窜的,进入该函数
可以看到有个Call MessageBox,这个就是当输入的名字小于5时就不用跳转到下面的call 0x879e60,直接运行到这个messagebox,然后运行到jmp unp.0087AF06而不执行计算机器码,F7 进入call 0x879e60
F7进入后
打开IDA 进入 0x879e60函数
F5之后
int __fastcall sub_879E60(int a1)
{
int v1; // esi@1
int v2; // ST04_4@1
int v3; // edx@3
int v4; // ebx@5
int v5; // edx@5
int v6; // ecx@5
int v7; // edx@5
int v8; // ebx@5
int v9; // edx@5
int v10; // ecx@5
int v11; // ST08_4@5
int v12; // ST04_4@5
int v13; // ecx@5
int (*v15)(); // ecx@6
int v16; // [sp-Ch] [bp-44h]@1
int (*v17)(); // [sp-8h] [bp-40h]@1
int (*v18)(); // [sp-4h] [bp-3Ch]@1
int v19; // [sp+0h] [bp-38h]@5
int v20; // [sp+8h] [bp-30h]@5
int v21; // [sp+Ch] [bp-2Ch]@5
int v22; // [sp+10h] [bp-28h]@5
int v23; // [sp+14h] [bp-24h]@3
int v24; // [sp+18h] [bp-20h]@1
int v25; // [sp+1Ch] [bp-1Ch]@1
int v26; // [sp+20h] [bp-18h]@1
int v27; // [sp+24h] [bp-14h]@1
int v28; // [sp+28h] [bp-10h]@5
int v29; // [sp+2Ch] [bp-Ch]@1
int v30; // [sp+30h] [bp-8h]@1
int v31; // [sp+34h] [bp-4h]@1
int v32; // [sp+38h] [bp+0h]@1
v1 = a1;
v18 = (int (*)())&v32;
v17 = loc_879FED;
v16 = *MK_FP(__FS__, 0);
*MK_FP(__FS__, 0) = &v16;
sub_8799A8(0, &v31, &v30);
Sysutils__Trim(v31, &v27);
Idglobal__RightStr(v27, 10, &v29);
v2 = 15 - sub_40519C(v29);//计算长度
Sysutils__Trim(v30, &v25);
System____linkproc___LStrCopy(&v26);
System____linkproc___LStrCat(&v29, v26);
Sysutils__Trim(v29, &v24);
if ( v24 )
{
System____linkproc___LStrAsg(v1, v29);
}
else
{
sub_879D00(0, &v23);
Sysutils__Trim(v23, &v29);
if ( sub_40519C(v29) <= 9 ));//计算长度
{
LOBYTE(v3) = 1;
v4 = sub_878094(off_877E9C, v3, 0);
sub_8789B4(v4, &v22);
System____linkproc___LStrCat(&v29, v22);
System__TObject__Free(v4, v5, v6);
LOBYTE(v7) = 1;
v8 = sub_879034(off_878E6C, v7);
sub_879894(v8, &v28);
System__TObject__Free(v8, v9, v10);
v11 = v29;
System____linkproc___LStrCopy(&v21);
v12 = v21;
System____linkproc___LStrCopy(&v20);
System____linkproc___LStrCatN(&v29, 3, v13, v19, v18, v17, v16, v11, v12, v20);
System____linkproc___LStrCopy(v1);
}
else
{
System____linkproc___LStrAsg(v1, v29);
}
}
v15 = v18;
*MK_FP(__FS__, 0) = v16;
v18 = loc_879FF4;
return System____linkproc___LStrArrayClr(&v20, 12, v15);
}
发现一个这个函数sub_8799A8(0, &v31, &v30);
我们先F8走一遍,一边走一边观察堆栈,
F8运行后,我们查看堆栈发现,有如下
两串字符窜,经验丰富的人,是觉得很熟悉的字符窜,像硬件id号,于是我们重新进入该函数,
分析是打开磁盘物理设备,然后询问id值,上面的汇编语言翻译成c的代码就是,
//计算获得磁盘物理设备的id
int __fastcall GetDiskInCode(int nphysicalDriver,PSENDCMDOUTPARAMS pSCOP)
{
DWORD BytesReturned = 0; // [sp+30h] [bp-224h]@8
HANDLE hDevice = NULL; // [sp+34h] [bp-220h]@3
try
{
if (g_dwServicePackNumber == 2 )
{
if ( nphysicalDriver )
hDevice = CreateFileA("////.//PhysicalDrive1", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);//
// 0xc0000000 == |Access = GENERIC_READ|GENERIC_WRITE
//
//
//
// 0x3u == ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
//
//
// 0x3u = Mode = OPEN_EXISTING
//
//
else
hDevice = CreateFileA("////.//PhysicalDrive0", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
}
else
{
if (g_dwServicePackNumber == 1 )
{
//sub_879C38(&v12);
return NULL;
}
}
DWORD pdwBytesReturned = 0;
SENDCMDINPARAMS pSCIP ={0};
if ( hDevice != (HANDLE)-1 )
{
pSCIP.cBufferSize = IDENTIFY_BUFFER_SIZE;
pSCIP.irDriveRegs.bSectorCountReg = 1;
pSCIP.irDriveRegs.bSectorNumberReg =1;
pSCIP.irDriveRegs.bDriveHeadReg = 0xA0;
pSCIP.irDriveRegs.bCommandReg =0xec;
pSCIP.cBufferSize = IDENTIFY_BUFFER_SIZE;
if(DeviceIoControl( hDevice,
SMART_RCV_DRIVE_DATA,
(LPVOID)&pSCIP,
sizeof(SENDCMDINPARAMS) - 1,
(LPVOID)pSCOP,
sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1,
&pdwBytesReturned, NULL))
{
}
}
if (hDevice)
{
CloseHandle(hDevice);
hDevice = NULL;
}
}
catch (...)
{
}
return NULL;
}
当运行获得物理磁盘设备id结束后运行到 00879b30地址,ebp所在的地址就是上个参数的栈地址就是(SENDCMDOUTPARAMS*)pSCOP)的地址
00879B30 . 8D9D 00FEFFFF lea ebx,dword ptr ss:[ebp-0x200] ========(SENDCMDOUTPARAMS*)pSCOP)->bBuffer地址
00879B36 . 8D43 14 lea eax,dword ptr ds:[ebx+0x14] =======((char*)(SENDCMDOUTPARAMS*)pSCOP)->bBuffer)+0x14
00879B39 . BA 14000000 mov edx,0x14 =======这个为字符窜大小0x14
00879B3E . E8 31FEFFFF call 复件_unp.00879974 ======去除字符窜两边的空格,即Trim函数
00879B43 . 8D43 14 lea eax,dword ptr ds:[ebx+0x14] eax = ((char*)(SENDCMDOUTPARAMS*)pSCOP)->bBuffer)+0x14
00879B46 . 83C0 14 add eax,0x14 eax[0x14] = '/0'
00879B49 . C600 00 mov byte ptr ds:[eax],0x0 eax[0x14] = '/0'
00879B4C . 8D43 36 lea eax,dword ptr ds:[ebx+0x36] =======((char*)(SENDCMDOUTPARAMS*)pSCOP)->bBuffer)+0x36
00879B4F . BA 28000000 mov edx,0x28 大小0x28
00879B54 . E8 1BFEFFFF call 复件_unp.00879974 然后Trim去空格 buffer+0x36后的字符窜,大小0x28
00879B59 . 8D43 36 lea eax,dword ptr ds:[ebx+0x36] eax = ((char*)(SENDCMDOUTPARAMS*)pSCOP)->bBuffer)+0x36
00879B5C . 83C0 28 add eax,0x28 eax[0x28] ='/0'
00879B5F . C600 00 mov byte ptr ds:[eax],0x0 eax[0x28] ='/0'
00879B62 . 8B85 E8FDFFFF mov eax,dword ptr ss:[ebp-0x218]
00879B68 . 8D53 36 lea edx,dword ptr ds:[ebx+0x36]
00879B6B . E8 64B5B8FF call 复件_unp.004050D4 取出((char*)(SENDCMDOUTPARAMS*)pSCOP)->bBuffer)+0x36
大小为 0x28的字符串
00879B70 . 8B85 ECFDFFFF mov eax,dword ptr ss:[ebp-0x214]
00879B76 . 8D53 14 lea edx,dword ptr ds:[ebx+0x14]
00879B79 . E8 56B5B8FF call 复件_unp.004050D4 取出((char*)(SENDCMDOUTPARAMS*)pSCOP)->bBuffer)+0x14 大小为 0x14的字符串
00879B7E . 8B8D ECFDFFFF mov ecx,dword ptr ss:[ebp-0x214]
00879B84 . 8B09 mov ecx,dword ptr ds:[ecx] 恢复前面被设置为'/0'的原始字符,保证堆栈平衡
00879B86 . 8B95 E8FDFFFF mov edx,dword ptr ss:[ebp-0x218]
00879B8C . 8B12 mov edx,dword ptr ds:[edx] 恢复被前面设置为'/0'的原始字符,保证堆栈平衡
00879B8E . 8D85 B0FDFFFF lea eax,dword ptr ss:[ebp-0x250]
00879B94 . E8 4FB6B8FF call 复件_unp.004051E8
00879B99 . 83BD B0FDFFFF>cmp dword ptr ss:[ebp-0x250],0x0 判断得到的字符窜是否为空
00879BA0 . 0F9585 E7FDFF>setne byte ptr ss:[ebp-0x219]
00879BA7 > 33C0 xor eax,eax
00879BA9 . 5A pop edx
00879BAA . 59 pop ecx
00879BAB . 59 pop ecx
00879BAC . 64:8910 mov dword ptr fs:[eax],edx //异常try catch
00879BAF . 68 CC9B8700 push 复件_unp.00879BCC
00879BB4 > 8D85 B0FDFFFF lea eax,dword ptr ss:[ebp-0x250]
00879BBA . BA 02000000 mov edx,0x2
00879BBF . E8 2CB3B8FF call 复件_unp.00404EF0
00879BC4 . C3 retn
00879BC5 .- E9 7AABB8FF jmp 复件_unp.00404744
00879BCA .^ EB E8 jmp X复件_unp.00879BB4
00879BCC . 8A85 E7FDFFFF mov al,byte ptr ss:[ebp-0x219]
00879BD2 . 5B pop ebx
00879BD3 . 8BE5 mov esp,ebp
00879BD5 . 5D pop ebp
00879BD6 . C3 retn
翻译过来就好是
int __fastcall sub_879974(char* a1, unsigned int a2)
{
int result;
char v3;
char* v4 = a1;
result = (a2 >> 1) - 1;
if ( result >= 0 )
{
result = a2 >> 1;
do
{
v3 = *(byte *)v4;
*(byte *)v4 = *(byte *)(v4 + 1);
*(byte *)(v4 + 1) = v3;
v4 += 2;
--result;
}
while ( result );
}
return result;
}
int GetCode(int nphysicalDriver,char* pCode1,char* pCode2)
{
char pSCOP[sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE] = {0};
GetDiskInCode(nphysicalDriver,(PSENDCMDOUTPARAMS)pSCOP);
sub_879974((char*)(((SENDCMDOUTPARAMS*)pSCOP)->bBuffer+0x14),0x14);
char* p0 = (char*)(((SENDCMDOUTPARAMS*)pSCOP)->bBuffer+0x14);
char pTemp = p0[0x14];
p0[0x14] = 0;
if (pCode2)
{
strcpy(pCode2,p0);
}
p0[0x14] = pTemp;
sub_879974((char*)(((SENDCMDOUTPARAMS*)pSCOP)->bBuffer+0x36),0x28);
CHAR* p1 = (char*)(((SENDCMDOUTPARAMS*)pSCOP)->bBuffer+0x36);
pTemp = p1[0x28];
p1[0x28] = 0;
if (pCode1)
{
strcpy(pCode1,p1);
}
p1[0x28] = pTemp;
return 0;
}
至此 call 复件_unp.008799A8函数就结束了,达到两个字符窜,
接下来再继续看
0087AE84 |. E8 D7EFFFFF CALL XJMeetin.00879E60 获得物理id,并计算出两个字符串,上面已经 分析
0087AE89 |. FF75 FC PUSH DWORD PTR SS:[EBP-4] 即字符串 堆栈 SS:[0012F34C]=03928AE4, (ASCII "AYU7559244WDC W")
0087AE8C |. 68 54AF8700 PUSH XJMeetin.0087AF54 ; ASCII 字符串"#*"
0087AE91 |. 8D55 E4 LEA EDX,DWORD PTR SS:[EBP-1C] 用户名存放地址[EBP-1c]
0087AE94 |. 8B83 10030000 MOV EAX,DWORD PTR DS:[EBX+310] 获得大小
0087AE9A |. E8 1DF8BFFF CALL XJMeetin.0047A6BC 获得输入的用户名
0087AE9F |. 8B45 E4 MOV EAX,DWORD PTR SS:[EBP-1C] eax =EBP-1C 即存放用户名的地址,输入的“wanjun”
0087AEA2 |. 8D55 E8 LEA EDX,DWORD PTR SS:[EBP-18] 下面调用函数操作字符窜后的返回地址
0087AEA5 |. E8 7AF1B8FF CALL XJMeetin.0040A024 Trim函数,去掉输入字符串两边的空格
0087AEAA |. FF75 E8 PUSH DWORD PTR SS:[EBP-18] 保存得到的去掉空格的新字符串,入栈准备作为参数
0087AEAD |. 8D45 EC LEA EAX,DWORD PTR SS:[EBP-14] 下面调用函数操作字符窜后的返回地址,是个参数
0087AEB0 |. BA 03000000 MOV EDX,3 操作字符串的个数,是个参数
0087AEB5 |. E8 A2A3B8FF CALL XJMeetin.0040525C 字符串连接函数,把上面三个字符串连接起来,结果放入EBP-14
0087AEBA |. 8B45 EC MOV EAX,DWORD PTR SS:[EBP-14] 保存函数地址,作为下一个函数的参数
0087AEBD |. 8D4D F0 LEA ECX,DWORD PTR SS:[EBP-10] 下一个函数的参数,作为操作后的返回地址
0087AEC0 |. 66:BA D5DD MOV DX,0DDD5 下一个函数参数,作为加密的密钥
0087AEC4 |. E8 C7CEFFFF CALL XJMeetin.00877D90 加密计算该字符窜,第一次加密
0087AEC9 |. 8B55 F0 MOV EDX,DWORD PTR SS:[EBP-10] 加密后结果
0087AECC |. 8B83 14030000 MOV EAX,DWORD PTR DS:[EBX+314]
翻译后成c语言的结果为
int CJMeetingRegisterDlg::GetMachineCode(char* pMachineCode)
{
char cMachineCode[128] = {0};
CalculateMachineCode(cMachineCode);
char user[MAX_PATH] ={0};
m_editUserName.GetWindowTextA(user,MAX_PATH);
CString csUser = user;
csUser.Trim();
CString csMachine = cMachineCode;
csMachine += "#*";
csMachine += user;
DWORD dwCode = 0xddd5;
char OutBuffer[128] = {0};
crypt1(csMachine.GetBuffer(0),dwCode,OutBuffer);
crypt2(OutBuffer,1,3,pMachineCode);
return 0;
}
现在我们来看 CALL XJMeetin.00877D90 函数 即crypt1(csMachine.GetBuffer(0),dwCode,OutBuffer);函数
00877D28 /$ 53 PUSH EBX
00877D29 |. 56 PUSH ESI
00877D2A |. 57 PUSH EDI
00877D2B |. 55 PUSH EBP
00877D2C |. 51 PUSH ECX
00877D2D |. 8BF1 MOV ESI,ECX
00877D2F |. 8BFA MOV EDI,EDX
00877D31 |. 8BD8 MOV EBX,EAX
00877D33 |. 8BC6 MOV EAX,ESI
00877D35 |. 8BD3 MOV EDX,EBX
00877D37 |. E8 E4D1B8FF CALL XJMeetin.00404F20
00877D3C |. 8B06 MOV EAX,DWORD PTR DS:[ESI]
00877D3E |. E8 59D4B8FF CALL XJMeetin.0040519C 计算长度是否大于0
00877D43 |. 66:85C0 TEST AX,AX 判断长度
00877D46 |. 76 40 JBE SHORT XJMeetin.00877D88 不大于0这跳转到877D88
00877D48 |. 66:890424 MOV WORD PTR SS:[ESP],AX
00877D4C |. 66:BB 0100 MOV BX,1 作为循环计数的
00877D50 |> 8BC6 /MOV EAX,ESI ESI保存传进来的字符窜地址,地址赋给EAX
00877D52 |. E8 9DD6B8FF |CALL XJMeetin.004053F4
00877D57 |. 0FB7EB |MOVZX EBP,BX
00877D5A |. 8B16 |MOV EDX,DWORD PTR DS:[ESI]
00877D5C |. 8A542A FF |MOV DL,BYTE PTR DS:[EDX+EBP-1] 把 字符串的 第[ESP-1]字符赋给DL,第一开始是第0个字符,以后 依次加一
00877D60 |. 0FB7CF |MOVZX ECX,DI DI,即传进来的0xddd5,下次这个数值会改变,把DI的字节变成DWORD,即字节高位 填0,赋给ECX
00877D63 |. C1E9 08 |SHR ECX,8 ECX DWORD数值 右移3位
00877D66 |. 32D1 |XOR DL,CL 然后异或DL 即上面字符串的冒一位字符和ECX的前面计算的低位CL值,结果放入 DL
00877D68 |. 885428 FF |MOV BYTE PTR DS:[EAX+EBP-1],DL 把结果放入返回地址中
00877D6C |. 8B06 |MOV EAX,DWORD PTR DS:[ESI]
00877D6E |. 0FB64428 FF |MOVZX EAX,BYTE PTR DS:[EAX+EBP-1]
00877D73 |. 66:03F8 |ADD DI,AX DI操作数在此处改变,DI+ 前面计算的DL值
00877D76 |. 66:69C7 6DCE |IMUL AX,DI,0CE6D 然后 DI 乘以 0xCE6D,结果给AX
00877D7B |. 66:05 BF58 |ADD AX,58BF 然后AX再加上0x58BF
00877D7F |. 8BF8 |MOV EDI,EAX 把得到的结果给EDI,至此DI值改变,继续下次使用
00877D81 |. 43 |INC EBX EBX计数加一,计算字符串的下一位
00877D82 |. 66:FF0C24 |DEC WORD PTR SS:[ESP] ESP计数减一
00877D86 |.^ 75 C8 /JNZ SHORT XJMeetin.00877D50 调转到上面继续下一位字符串计算
00877D88 |> 5A POP EDX
00877D89 |. 5D POP EBP
00877D8A |. 5F POP EDI
00877D8B |. 5E POP ESI
00877D8C |. 5B POP EBX
00877D8D /. C3 RETN
crypt1到此计算结束
翻译成c代码
int __fastcall crypt1(IN char* inMachineCode,int code,char* OutMachineCode)
{
if (OutMachineCode && inMachineCode)
{
strcpy(OutMachineCode,inMachineCode);
int nLength = strlen(inMachineCode);
int nI =0;
if (nLength)
{
DWORD wd_shr = code;
while(nLength)
{
byte byte_code = OutMachineCode[nI];
//为了方便直接用汇编写
__asm
{
push ecx
push edx
push edx
push eax
push edi
push esi
mov edi,wd_shr
mov esi,dword ptr [OutMachineCode]
add esi,dword ptr [nI]
mov dl,byte ptr[esi]
mov ecx,wd_shr
shr ecx,8
xor dl,cl
//赋值给原数组
mov byte ptr [esi],dl
pop esi
movzx eax,dl
add di,ax
imul ax,di,0xce6d
add ax,0x58bf
mov wd_shr,eax
pop edi
pop eax
pop edx
pop edx
pop ecx
inc nI
dec nLength
}
}
}
}
}
crypt1函数结束最后得出的字符串为 03928B38 5943A59C 湧CY
03928B3C 739AA5F0 馥歴
03928B40 F72BFB9E 烕+
03928B44 4F9418AC ?擮
03928B48 8A9B4722 "G泭
03928B4C 000C0F20 ..
我们继续crypt2的函数
00877C8C /$ 55 PUSH EBP
00877C8D |. 8BEC MOV EBP,ESP
00877C8F |. 6A 00 PUSH 0
00877C91 |. 6A 00 PUSH 0
00877C93 |. 6A 00 PUSH 0
00877C95 |. 53 PUSH EBX
00877C96 |. 56 PUSH ESI
00877C97 |. 8BDA MOV EBX,EDX
00877C99 |. 8BF0 MOV ESI,EAX
00877C9B |. 33C0 XOR EAX,EAX
00877C9D |. 55 PUSH EBP
00877C9E |. 68 1A7D8700 PUSH XJMeetin.00877D1A
00877CA3 |. 64:FF30 PUSH DWORD PTR FS:[EAX]
00877CA6 |. 64:8920 MOV DWORD PTR FS:[EAX],ESP
00877CA9 |. 8D45 FC LEA EAX,DWORD PTR SS:[EBP-4]
00877CAC |. 8BD6 MOV EDX,ESI
00877CAE |. E8 B1D2B8FF CALL XJMeetin.00404F64 这些都是Delphi的分配空间复制字符窜的函数
00877CB3 |. 8BC3 MOV EAX,EBX
00877CB5 |. E8 12D2B8FF CALL XJMeetin.00404ECC 这些都是Delphi的分配空间复制字符窜的函数
00877CBA |. EB 3D JMP SHORT XJMeetin.00877CF9 分配复制失败就不继续操作
00877CBC |> 8D45 F4 /LEA EAX,DWORD PTR SS:[EBP-C] 下面字符窜操作函数的返回地址
00877CBF |. 50 |PUSH EAX
00877CC0 |. B9 03000000 |MOV ECX,3
00877CC5 |. BA 01000000 |MOV EDX,1
00877CCA |. 8B45 FC |MOV EAX,DWORD PTR SS:[EBP-4] 传进来的crypt1函数加密的字符串,赋给eax作为 函数参数
00877CCD |. E8 2AD7B8FF |CALL XJMeetin.004053FC 从第0位开始,一直循环到最后一位,每次截取获得三个字节的 字符串,
00877CD2 |. 8B45 F4 |MOV EAX,DWORD PTR SS:[EBP-C] 截取的结果放入EBP-C的地址
00877CD5 |. 8D55 F8 |LEA EDX,DWORD PTR SS:[EBP-8] 下面加密函数的返回地址
00877CD8 |. E8 63FDFFFF |CALL XJMeetin.00877A40 计算该三个字符窜所对应的加密码
00877CDD |. 8B55 F8 |MOV EDX,DWORD PTR SS:[EBP-8] 计算后的 所得的字符串
00877CE0 |. 8BC3 |MOV EAX,EBX
00877CE2 |. E8 BDD4B8FF |CALL XJMeetin.004051A4 把上面计算的结果重新分配地址,复制,作为 所有计算出的字符串的拼接 地址
00877CE7 |. 8D45 FC |LEA EAX,DWORD PTR SS:[EBP-4]
00877CEA |. B9 03000000 |MOV ECX,3
00877CEF |. BA 01000000 |MOV EDX,1
00877CF4 |. E8 43D7B8FF |CALL XJMeetin.0040543C 把每次循环计算的结果拼接起来,最后得到一个总的字符串
00877CF9 |> 837D FC 00 CMP DWORD PTR SS:[EBP-4],0
00877CFD |.^ 75 BD /JNZ SHORT XJMeetin.00877CBC
00877CFF |. 33C0 XOR EAX,EAX
00877D01 |. 5A POP EDX
00877D02 |. 59 POP ECX
00877D03 |. 59 POP ECX
00877D04 |. 64:8910 MOV DWORD PTR FS:[EAX],EDX
00877D07 |. 68 217D8700 PUSH XJMeetin.00877D21
00877D0C |> 8D45 F4 LEA EAX,DWORD PTR SS:[EBP-C]
00877D0F |. BA 03000000 MOV EDX,3
00877D14 |. E8 D7D1B8FF CALL XJMeetin.00404EF0
00877D19 /. C3 RETN
00877D1A .- E9 25CAB8FF JMP XJMeetin.00404744
00877D1F .^ EB EB JMP SHORT XJMeetin.00877D0C
00877D21 . 5E POP ESI
00877D22 . 5B POP EBX
00877D23 . 8BE5 MOV ESP,EBP
00877D25 . 5D POP EBP
00877D26 . C3 RETN
//下面这个就是 crypt2中加密三个字符串的函数看起来比较复杂,实际上很简单
00877A40 /$ 53 PUSH EBX
00877A41 |. 56 PUSH ESI
00877A42 |. 83C4 E4 ADD ESP,-1C
00877A45 |. 8BF2 MOV ESI,EDX
00877A47 |. 8BD8 MOV EBX,EAX
00877A49 |. 33C0 XOR EAX,EAX
00877A4B |. 890424 MOV DWORD PTR SS:[ESP],EAX
00877A4E |. 8BC3 MOV EAX,EBX
00877A50 |. E8 47D7B8FF CALL XJMeetin.0040519C
00877A55 |. 50 PUSH EAX
00877A56 |. 8BC3 MOV EAX,EBX
00877A58 |. E8 3FD9B8FF CALL XJMeetin.0040539C
00877A5D |. 8D5424 04 LEA EDX,DWORD PTR SS:[ESP+4]
00877A61 |. 59 POP ECX
00877A62 |. E8 65B3B8FF CALL XJMeetin.00402DCC
00877A67 |. 8BC3 MOV EAX,EBX
00877A69 |. E8 2ED7B8FF CALL XJMeetin.0040519C
00877A6E |. 48 DEC EAX ; Switch (cases 1..3)
00877A6F |. 74 0F JE SHORT XJMeetin.00877A80
00877A71 |. 48 DEC EAX
00877A72 |. 74 7B JE SHORT XJMeetin.00877AEF
00877A74 |. 48 DEC EAX
00877A75 |. 0F84 22010000 JE XJMeetin.00877B9D
00877A7B |. E9 05020000 JMP XJMeetin.00877C85
00877A80 |> 8D4424 04 LEA EAX,DWORD PTR SS:[ESP+4] ; Case 1 of switch 00877A6E
00877A84 |. 8B1424 MOV EDX,DWORD PTR SS:[ESP]
00877A87 |. 81E2 3F000080 AND EDX,8000003F
00877A8D |. 79 05 JNS SHORT XJMeetin.00877A94
00877A8F |. 4A DEC EDX
00877A90 |. 83CA C0 OR EDX,FFFFFFC0
00877A93 |. 42 INC EDX
00877A94 |> 8A92 2C719100 MOV DL,BYTE PTR DS:[EDX+91712C]
00877A9A |. 8850 01 MOV BYTE PTR DS:[EAX+1],DL
00877A9D |. C600 01 MOV BYTE PTR DS:[EAX],1
00877AA0 |. 8D5424 04 LEA EDX,DWORD PTR SS:[ESP+4]
00877AA4 |. 8D4424 08 LEA EAX,DWORD PTR SS:[ESP+8]
00877AA8 |. E8 57BAB8FF CALL XJMeetin.00403504
00877AAD |. 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+C]
00877AB1 |. 8B1424 MOV EDX,DWORD PTR SS:[ESP]
00877AB4 |. C1EA 06 SHR EDX,6
00877AB7 |. 81E2 3F000080 AND EDX,8000003F
00877ABD |. 79 05 JNS SHORT XJMeetin.00877AC4
00877ABF |. 4A DEC EDX
00877AC0 |. 83CA C0 OR EDX,FFFFFFC0
00877AC3 |. 42 INC EDX
00877AC4 |> 8A92 2C719100 MOV DL,BYTE PTR DS:[EDX+91712C]
00877ACA |. 8850 01 MOV BYTE PTR DS:[EAX+1],DL
00877ACD |. C600 01 MOV BYTE PTR DS:[EAX],1
00877AD0 |. 8D5424 0C LEA EDX,DWORD PTR SS:[ESP+C]
00877AD4 |. 8D4424 08 LEA EAX,DWORD PTR SS:[ESP+8]
00877AD8 |. B1 02 MOV CL,2
00877ADA |. E8 F5B9B8FF CALL XJMeetin.004034D4
00877ADF |. 8D5424 08 LEA EDX,DWORD PTR SS:[ESP+8]
00877AE3 |. 8BC6 MOV EAX,ESI
00877AE5 |. E8 56D6B8FF CALL XJMeetin.00405140
00877AEA |. E9 96010000 JMP XJMeetin.00877C85
00877AEF |> 8D4424 04 LEA EAX,DWORD PTR SS:[ESP+4] ; Case 2 of switch 00877A6E
00877AF3 |. 8B1424 MOV EDX,DWORD PTR SS:[ESP]
00877AF6 |. 81E2 3F000080 AND EDX,8000003F
00877AFC |. 79 05 JNS SHORT XJMeetin.00877B03
00877AFE |. 4A DEC EDX
00877AFF |. 83CA C0 OR EDX,FFFFFFC0
00877B02 |. 42 INC EDX
00877B03 |> 8A92 2C719100 MOV DL,BYTE PTR DS:[EDX+91712C]
00877B09 |. 8850 01 MOV BYTE PTR DS:[EAX+1],DL
00877B0C |. C600 01 MOV BYTE PTR DS:[EAX],1
00877B0F |. 8D5424 04 LEA EDX,DWORD PTR SS:[ESP+4]
00877B13 |. 8D4424 08 LEA EAX,DWORD PTR SS:[ESP+8]
00877B17 |. E8 E8B9B8FF CALL XJMeetin.00403504
00877B1C |. 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+C]
00877B20 |. 8B1424 MOV EDX,DWORD PTR SS:[ESP]
00877B23 |. C1EA 06 SHR EDX,6
00877B26 |. 81E2 3F000080 AND EDX,8000003F
00877B2C |. 79 05 JNS SHORT XJMeetin.00877B33
00877B2E |. 4A DEC EDX
00877B2F |. 83CA C0 OR EDX,FFFFFFC0
00877B32 |. 42 INC EDX
00877B33 |> 8A92 2C719100 MOV DL,BYTE PTR DS:[EDX+91712C]
00877B39 |. 8850 01 MOV BYTE PTR DS:[EAX+1],DL
00877B3C |. C600 01 MOV BYTE PTR DS:[EAX],1
00877B3F |. 8D5424 0C LEA EDX,DWORD PTR SS:[ESP+C]
00877B43 |. 8D4424 08 LEA EAX,DWORD PTR SS:[ESP+8]
00877B47 |. B1 02 MOV CL,2
00877B49 |. E8 86B9B8FF CALL XJMeetin.004034D4
00877B4E |. 8D5424 08 LEA EDX,DWORD PTR SS:[ESP+8]
00877B52 |. 8D4424 10 LEA EAX,DWORD PTR SS:[ESP+10]
00877B56 |. E8 A9B9B8FF CALL XJMeetin.00403504
00877B5B |. 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+C]
00877B5F |. 8B1424 MOV EDX,DWORD PTR SS:[ESP]
00877B62 |. C1EA 0C SHR EDX,0C
00877B65 |. 81E2 3F000080 AND EDX,8000003F
00877B6B |. 79 05 JNS SHORT XJMeetin.00877B72
00877B6D |. 4A DEC EDX
00877B6E |. 83CA C0 OR EDX,FFFFFFC0
00877B71 |. 42 INC EDX
00877B72 |> 8A92 2C719100 MOV DL,BYTE PTR DS:[EDX+91712C]
00877B78 |. 8850 01 MOV BYTE PTR DS:[EAX+1],DL
00877B7B |. C600 01 MOV BYTE PTR DS:[EAX],1
00877B7E |. 8D5424 0C LEA EDX,DWORD PTR SS:[ESP+C]
00877B82 |. 8D4424 10 LEA EAX,DWORD PTR SS:[ESP+10]
00877B86 |. B1 03 MOV CL,3
00877B88 |. E8 47B9B8FF CALL XJMeetin.004034D4
00877B8D |. 8D5424 10 LEA EDX,DWORD PTR SS:[ESP+10]
00877B91 |. 8BC6 MOV EAX,ESI
00877B93 |. E8 A8D5B8FF CALL XJMeetin.00405140
00877B98 |. E9 E8000000 JMP XJMeetin.00877C85
00877B9D |> 8D4424 04 LEA EAX,DWORD PTR SS:[ESP+4] ; Case 3 of switch 00877A6E
00877BA1 |. 8B1424 MOV EDX,DWORD PTR SS:[ESP]
00877BA4 |. 81E2 3F000080 AND EDX,8000003F
00877BAA |. 79 05 JNS SHORT XJMeetin.00877BB1
00877BAC |. 4A DEC EDX
00877BAD |. 83CA C0 OR EDX,FFFFFFC0
00877BB0 |. 42 INC EDX
00877BB1 |> 8A92 2C719100 MOV DL,BYTE PTR DS:[EDX+91712C]
00877BB7 |. 8850 01 MOV BYTE PTR DS:[EAX+1],DL
00877BBA |. C600 01 MOV BYTE PTR DS:[EAX],1
00877BBD |. 8D5424 04 LEA EDX,DWORD PTR SS:[ESP+4]
00877BC1 |. 8D4424 08 LEA EAX,DWORD PTR SS:[ESP+8]
00877BC5 |. E8 3AB9B8FF CALL XJMeetin.00403504
00877BCA |. 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+C]
00877BCE |. 8B1424 MOV EDX,DWORD PTR SS:[ESP]
00877BD1 |. C1EA 06 SHR EDX,6
00877BD4 |. 81E2 3F000080 AND EDX,8000003F
00877BDA |. 79 05 JNS SHORT XJMeetin.00877BE1
00877BDC |. 4A DEC EDX
00877BDD |. 83CA C0 OR EDX,FFFFFFC0
00877BE0 |. 42 INC EDX
00877BE1 |> 8A92 2C719100 MOV DL,BYTE PTR DS:[EDX+91712C]
00877BE7 |. 8850 01 MOV BYTE PTR DS:[EAX+1],DL
00877BEA |. C600 01 MOV BYTE PTR DS:[EAX],1
00877BED |. 8D5424 0C LEA EDX,DWORD PTR SS:[ESP+C]
00877BF1 |. 8D4424 08 LEA EAX,DWORD PTR SS:[ESP+8]
00877BF5 |. B1 02 MOV CL,2
00877BF7 |. E8 D8B8B8FF CALL XJMeetin.004034D4
00877BFC |. 8D5424 08 LEA EDX,DWORD PTR SS:[ESP+8]
00877C00 |. 8D4424 10 LEA EAX,DWORD PTR SS:[ESP+10]
00877C04 |. E8 FBB8B8FF CALL XJMeetin.00403504
00877C09 |. 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+C]
00877C0D |. 8B1424 MOV EDX,DWORD PTR SS:[ESP]
00877C10 |. C1EA 0C SHR EDX,0C
00877C13 |. 81E2 3F000080 AND EDX,8000003F
00877C19 |. 79 05 JNS SHORT XJMeetin.00877C20
00877C1B |. 4A DEC EDX
00877C1C |. 83CA C0 OR EDX,FFFFFFC0
00877C1F |. 42 INC EDX
00877C20 |> 8A92 2C719100 MOV DL,BYTE PTR DS:[EDX+91712C]
00877C26 |. 8850 01 MOV BYTE PTR DS:[EAX+1],DL
00877C29 |. C600 01 MOV BYTE PTR DS:[EAX],1
00877C2C |. 8D5424 0C LEA EDX,DWORD PTR SS:[ESP+C]
00877C30 |. 8D4424 10 LEA EAX,DWORD PTR SS:[ESP+10]
00877C34 |. B1 03 MOV CL,3
00877C36 |. E8 99B8B8FF CALL XJMeetin.004034D4
00877C3B |. 8D5424 10 LEA EDX,DWORD PTR SS:[ESP+10]
00877C3F |. 8D4424 14 LEA EAX,DWORD PTR SS:[ESP+14]
00877C43 |. E8 BCB8B8FF CALL XJMeetin.00403504
00877C48 |. 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+C]
00877C4C |. 8B1424 MOV EDX,DWORD PTR SS:[ESP]
00877C4F |. C1EA 12 SHR EDX,12
00877C52 |. 81E2 3F000080 AND EDX,8000003F
00877C58 |. 79 05 JNS SHORT XJMeetin.00877C5F
00877C5A |. 4A DEC EDX
00877C5B |. 83CA C0 OR EDX,FFFFFFC0
00877C5E |. 42 INC EDX
00877C5F |> 8A92 2C719100 MOV DL,BYTE PTR DS:[EDX+91712C]
00877C65 |. 8850 01 MOV BYTE PTR DS:[EAX+1],DL
00877C68 |. C600 01 MOV BYTE PTR DS:[EAX],1
00877C6B |. 8D5424 0C LEA EDX,DWORD PTR SS:[ESP+C]
00877C6F |. 8D4424 14 LEA EAX,DWORD PTR SS:[ESP+14]
00877C73 |. B1 04 MOV CL,4
00877C75 |. E8 5AB8B8FF CALL XJMeetin.004034D4
00877C7A |. 8D5424 14 LEA EDX,DWORD PTR SS:[ESP+14]
00877C7E |. 8BC6 MOV EAX,ESI
00877C80 |. E8 BBD4B8FF CALL XJMeetin.00405140
00877C85 |> 83C4 1C ADD ESP,1C ; Default case of switch 00877A6E
00877C88 |. 5E POP ESI
00877C89 |. 5B POP EBX
00877C8A /. C3 RETN
翻译过来就是
byte g_code[64] =
{
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 即对应着“ABCDEFGHIGKLMN.............”字符串码表,一共64个
0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56,
0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F
};
int __fastcall crypt3(IN char* inMachineCode,OUT char* OutMachineCode)
{
if(inMachineCode && OutMachineCode)
{
int nLength = strlen(inMachineCode)-1; //计算循环截取三个字节码的 长度,也许组后传进来是2个字节或者一个字节的长度的字节数组,长度-1
if (nLength) >0
{
nLength--; 再减一
if (nLength) >0
{
nLength--; 再减一
if (nLength == 0) 如果最后 ==0,这是传进来为 3位的情况
{//进入
DWORD dwCode = ((*((DWORD*)inMachineCode))); 把传进来的字节数组转化成DWORD类型的 DWORD数值 //if (dwCode)
{
int nCode = 0;
byte byteCode1 = g_code[dwCode%64]; g_code数组的长度为64里面存储的是一窜字符串码表
分别除以64求余数,找到对应的数组相应位上字符作为第一位
OutMachineCode[nCode] = byteCode1;
nCode =1;
byte byteCode2 = g_code[ (dwCode>>6)%64]; 移位后再除以64得到余数,得到第2,3,4位
OutMachineCode[nCode] = byteCode2;
nCode =2;
byte byteCode3 = g_code[ (dwCode>>12)%64];
OutMachineCode[nCode] = byteCode3;
nCode =3;
byte byteCode4 = g_code[ (dwCode>>18)%64];
OutMachineCode[nCode] = byteCode4; 最后连接起来得到一个四位的字符串
}
}
}
else这是传进来为 2位的情况,最后得到的是三位的返回字符串
{
DWORD dwCode = ((*((DWORD*)inMachineCode)));
//if (dwCode)
{
int nCode = 0;
byte byteCode1 = g_code[dwCode%64];
OutMachineCode[nCode] = byteCode1;
nCode =1;
byte byteCode2 = g_code[ (dwCode>>6)%64];
OutMachineCode[nCode] = byteCode2;
nCode =2;
byte byteCode3 = g_code[ (dwCode>>12)%64];
OutMachineCode[nCode] = byteCode3;
}
}
}
else这是传进来为 1位的情况,最后得到的是2位的返回字符串
{
DWORD dwCode = ((*((DWORD*)inMachineCode)));
//if (dwCode)
{
int nCode = 0;
byte byteCode1 = g_code[dwCode%64];
OutMachineCode[nCode] = byteCode1;
nCode =1;
byte byteCode2 = g_code[ (dwCode>>6)%64];
OutMachineCode[nCode] = byteCode2;
}
}
}
return 0;
}
至此最后所有所得字符窜连接起来所得到的字符串为机器码,我们完整起来看看
int CJMeetingRegisterDlg::GetMachineCode(char* pMachineCode)
{
char cMachineCode[128] = {0};
CalculateMachineCode(cMachineCode);
char user[MAX_PATH] ={0};
m_editUserName.GetWindowTextA(user,MAX_PATH);
CString csUser = user;
csUser.Trim();
CString csMachine = cMachineCode;
csMachine += "#*";
csMachine += user;
DWORD dwCode = 0xddd5;
char OutBuffer[128] = {0};
crypt1(csMachine.GetBuffer(0),dwCode,OutBuffer);
crypt2(OutBuffer,1,3,pMachineCode);
return 0;
}
我们随便输入一个名字wnjunnihao,最后得到的结果和软件中的一样
剩下的工作分析注册码注册的方法类似,下面不再赘述。