初次见穷举的表演是jackozoo 的帖子《小试金枪: jackozoo 2009热身版 !! 》
http://bbs.pediy.com/showthread.php?t=97046&page=1
里面ccfer兄和海风兄的思路是我以往从未见过的(接触加解密快三个月了,请不要笑我孤陋寡闻,呵呵~),特别是ccfer兄的方法更是让我觉得匪夷所思,对比海风兄的dll注入方法,他的效率提高很多,但是我个人还是觉得海风兄的dll注入方法实现还是较为方便,至少不用手动去更改或将出现的某些偏移地址,有一定的通用性,呵呵,个人拙见。
http://bbs.pediy.com/showthread.php?t=97307,这个帖子是天易兄的详细解释说明和实例应用,
上面的例子是算法在一个fuction中计算对比序列号的穷举方法,最好的适用范围如下:
{
getitemdlgtext();
……
……
bool result = check_nameserial();
……
……
}
在穷举的时候,只要构筑好环境,直接call check_nameserial,然后比较返回结果eax就可以了。
但是还有很多情况是这样子的:
{
getitemdlgtext(); 或者 updatedata(true);
……
switchname();
……
switchserial();
……
checkname();
……
checkserial();
……
……
}
或者
{
getitemdlgtext(); 或者 updatedata(true);
……
……
……
……
}
出现上述情况的话(这种情况也是比较多见的),直接call 算法函数 可能就有点不太适用了,因此我考虑直接call整个片段,即把需要call的内容开始到结束作为一个函数即可以了(只要注意堆栈平衡)。 依然以jackozoo 的帖子《小试金枪: jackozoo 2009热身版 !! 》中的CM为实例,具体实现步骤如下:
const char injectionwindow[MAX_CHAR] = "ACG TRIAL Membership Crackme v1.0";
BOOL callFullFucResult(char *name, char *key,DWORD wnd)
{
BOOL bResult = 0;
__asm
{
pushad;//把所有寄存器保存起来,即保存现场
xor eax, eax; //以下的寄存器数据都是根据程序的实际情况来写的。即模拟调用函数时的状态
xor ecx, ecx; //特别注意ecx,在类函数中,往往为this指针
mov edx, 0x00000022;
xor ebx, ebx;
;// mov esp, 0x0013FA50; //这两个由函数自动分配,无需更改
;// mov ebp, 0x0013FAA0;
mov esi, wnd; //dlg窗口句柄 bb0cbe
mov edi, 0x0013FADC;
push offset _returnResult; //算法调用函数的返回值,先放到offset这边
push 0x00401020; //算法调用的函数地址
retn;
_returnResult:
mov bResult, eax;
popad;//恢复所有寄存器,即恢复现场
}
return bResult;
}
__declspec(dllexport) void checkresult(void)
{
HWND pWnd = NULL;
int len = strlen(wantname);
int i;
char cc;
memset(name, 0, MAX_CHAR);
memset(key, 0, MAX_CHAR);
strcpy(name, "feng");
strcpy(key, "ujkt");
#if _ENABLE_MSG_
char strWnd[MAX_CHAR] = _TEXT("NULL");
#endif
pWnd =::FindWindow(NULL,injectionwindow);
if(pWnd == NULL) //先找到输入name和serial的dlg窗口
{
MessageBoxEx(NULL,_TEXT("没有找到注入的窗口句柄"),_TEXT("测试结果"),MB_OK,0);
return;
}
#if 1
for(i = 0; i < len; i++) //name
{
name[i] = wantname[i];
SetDlgItemText(pWnd,0x3e9,name);//写入name,0x3e9为name输入框的ID
for(cc = 'a'; cc < 'z'; cc++)
{
key[i] = cc;
SetDlgItemText(pWnd,0x3ea,key);//写入name,0x3ea为serial输入框的ID
#if _ENABLE_MSG_
sprintf(strWnd,"测试次数 == %d",cc);
MessageBoxEx(NULL,strWnd,_TEXT("测试次数结果"),MB_OK,0);
#endif
if(callFullFucResult(name, key,(DWORD)pWnd))
{
MessageBox(NULL, name, key, NULL);
return;//break; //找到一个就返回好了
}
}
}
#endif
MessageBox(NULL, name, key, NULL);
return;
}
//---------------------------------------------------------------------
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
myTestFun();
checkresult();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
上面对CM的算法还是依据海风兄的源代码(所有的思路都是建立在对海风兄的那个源码的理解上,谢谢海风兄的共享! 听说他快结婚了,在此先恭喜他 :))
以上思路的主要注意点:
1,对注入环境的模拟,用ollydbg多跟踪应该就可得出。
2,穷举算法的选择,应该对CM的流程和算法(包括CM的一些限制)有一定的了解,然后选择最快的算法,这是穷举最关键的地方!
3,对CM需要做部分更改,以使堆栈平衡。
附件为此dll源码和我自己编写的dll注入工具,只要对dll进行相应修改就可以实现对有可能穷举实现的CM实现穷举了,呵呵~~我用2007年的CM做过实验,有很多可以穷举实现。。:)
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!