您的位置:首页 > 其它

160个练手CrackMe-004

2017-10-06 00:27 453 查看
1、准备工作

无壳,Delphi编写,工具:DarkDe4(Delphi反汇编工具,定位事件地址)



2、OD载入运行,搜索字符串。



双击定位



0045803B为关键跳转。

[esi+0x30C] == 0x85 才提示注册成功,向上查看哪个地方有赋值操作。

在Panel1DbClick函数中发现赋值操作。

00457EFE  |.  C786 0C030000>mov dword ptr ds:[esi+0x30C],0x85




而给[esi+0x30C]赋值的条件是[esi+0x30C] == 0x3E,继续查看哪个地方写入了0x3E。
编辑框的chkcode函数中发现赋值操作。



而赋值条件是Call CKme.00403C。

到此知道了整个注册流程:

     a.输入正确的Key

     b.单击图片框

     c.双击图片框,会显示图片,提示注册成功



单步运行到00457D35时,数据窗口中跟随edx就可以看到正确的Key了:“黑头Sun Brird8dseloffc-012-OK123”

此时可以分析内存关系,可以写内存注册机了。

3、内存注册机

从[ebx + 0x318]开始,向上层函数查。

00457C56  |.  8BD8          mov ebx,eax                               ; ebx的值由eax得来,eax是上层函数传的参数
00428185   .  8B83 C4010000 mov eax,dword ptr ds:[ebx+0x1C4]          ; eax <- [ebx+0x1C4]
00428181   .  8BD8          mov ebx,eax                               ; ebx <- eax
此处省略几步。。。
004281FC   .  53            push ebx
004281FD   .  56            push esi
004281FE   .  8BF2          mov esi,edx
00428200   .  8BD8          mov ebx,eax                               ; ebx <- eax 到这里猜想eax可能就没变化了。所以先试试。此时eax=0x02275028
00428202   .  8BD6          mov edx,esi
00428204   .  8BC3          mov eax,ebx                               ;  这一层相关的是eax,  eax <- ebx, 再往上看
姑且把eax的值当做基值。

内存注册机:

#include <iostream>
#include <windows.h>
using namespace std;

int main(){
int pid;
int Base_adr = 0x02275028;                                        //基值
int Key_adr;
int tmp_adr;
char key[50] = {0};
int num = 0;
unsigned char tmp;

printf("PID:");
scanf("%d", &pid);

HANDLE hProcess = OpenProcess(2035711, false, pid);
if(hProcess == NULL)
cout << "Error!" << endl;
else{
ReadProcessMemory(hProcess, (LPCVOID)(Base_adr+0x1C4), &tmp_adr, 4, NULL);     //第一层关系
ReadProcessMemory(hProcess, (LPCVOID)(tmp_adr+0x318), &Key_adr, 4, NULL);      //正确的Key的地址
ReadProcessMemory(hProcess, (LPCVOID)Key_adr, &tmp, 1, NULL);
while(tmp != 0 && num < 50){                                                   //循环取值,取到0表示结束
key[num++] = tmp;
ReadProcessMemory(hProcess, (LPCVOID)Key_adr+num, &tmp, 1, NULL);
}

}
//    printf("%X\n", Key_adr);
printf("%s\n", key);                                                               //当前name正确的key

return 0;
}


尝试了一下。




输入正确的Key后,单击双击操作后显示图片。

但这个内存注册机只对分析出基值的程序有效,上面所谓的基值新开一个程序就变了。好像和下面这个模块有关。



如果这个地址显示为02230000,则上面代码中基值应该是0x02235028。        022C0000则基值为0x022C5028 。暂时不知道解决办法。有机会再解决。

补:

查了一波资料,可以吧内存注册机当做一个小的调试器,以Debug方式创建进程,在Key漏出来的地方下断点,然后读取相关寄存器的值。

源码:

#include <iostream>
#include <windows.h>
using namespace std;

int main() {
char exePath[100] = {"C:\\CKme.exe"};
int int3_adr = 0x00457D35; // 设置断点的地方
unsigned char CC = 0xCC;
unsigned char E8 = 0xE8;
STARTUPINFO si;
PROCESS_INFORMATION pi;
DEBUG_EVENT devent;
CONTEXT context;
char key[50] = {0};
char tmp = 0;
int num = 0;
// LPCONTEXT lpContext;

ZeroMemory( &si, sizeof(si));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW;
// si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.wShowWindow = SW_SHOW;
ZeroMemory( &pi, sizeof(pi) );

// cout << "File Path:";
// scanf("%s", exePath);

if(CreateProcess(exePath,
NULL, NULL, NULL,
FALSE,
DEBUG_PROCESS,
NULL, NULL, &si, &pi)){ // 以Debug方式创建进程
cout << "OK" << endl;
while(TRUE){
if(WaitForDebugEvent(&devent, INFINITE)){ //等待Debug事件
// cout << "Lai MI" << endl;
switch(devent.dwDebugEventCode){ // 过滤事件
case CREATE_PROCESS_DEBUG_EVENT:
// cout << pi.dwProcessId << endl;
WriteProcessMemory(pi.hProcess, (LPVOID)int3_adr, &CC, 1, NULL); //进程被创建时下断点
break;
case EXCEPTION_DEBUG_EVENT:
// SuspendThread(pi.hThread);
if(devent.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT){ // 判断异常信息(int 3 断点信息为0x80000003)
SuspendThread(pi.hThread); // 暂停线程
context.ContextFlags = CONTEXT_CONTROL; // 此Flags表示get或set Eip,Ebp...等寄存器
if(GetThreadContext(pi.hThread, &context)){
cout << "Eip:" << hex << context.Eip << endl;
if(context.Eip != int3_adr + 1){ // 判断异常的地方是否是我们想要的地方
// cout << hex << devent.u.Exception.ExceptionRecord.ExceptionCode << endl;
ResumeThread(pi.hThread); // 其他地方的断点直接恢复线程
}else{
context.ContextFlags = CONTEXT_INTEGER; // 此Flags表示get或set Eax,Ebx...等寄存器
if(GetThreadContext(pi.hThread, &context)){ // 这里我们要get Edx的值,Edx指向了注册码
cout << "Edx:" << hex << context.Edx << endl;
ReadProcessMemory(pi.hProcess, (LPCVOID)context.Edx, &tmp, 1, NULL);
while(tmp != 0 && num < 50){
key[num++] = tmp;
ReadProcessMemory(pi.hProcess, (LPCVOID)(context.Edx+num), &tmp, 1, NULL);
}
cout << "This is the registration code:";
cout << key << endl; //输出注册码
}
WriteProcessMemory(pi.hProcess, (LPVOID)int3_adr, &E8, 1, NULL); // 还原断点
context.ContextFlags = CONTEXT_CONTROL;
context.Eip -= 1;
SetThreadContext(pi.hThread, &context); // 还原 Eip
ResumeThread(pi.hThread); // 恢复线程
}
}
}
break;
}
ContinueDebugEvent(devent.dwProcessId, devent.dwThreadId, DBG_CONTINUE); // 继续Debug事件
}else{
cout << "Error";
cout << GetLastError();
return 0;
}
}
}else{
cout << "Error";
cout << GetLastError();
}

return 0;
}

效果图:程序运行起来后先随便几个字符。



4、追码

00457C40  /.  55            push ebp                                 ;  chkcode 真正判断函数
00457C41  |.  8BEC          mov ebp,esp
00457C43  |.  51            push ecx
00457C44  |.  B9 05000000   mov ecx,0x5
00457C49  |>  6A 00         /push 0x0
00457C4B  |.  6A 00         |push 0x0
00457C4D  |.  49            |dec ecx
00457C4E  |.^ 75 F9         \jnz XCKme.00457C49
00457C50  |.  51            push ecx
00457C51  |.  874D FC       xchg [local.1],ecx
00457C54  |.  53            push ebx
00457C55  |.  56            push esi
00457C56  |.  8BD8          mov ebx,eax
00457C58  |.  33C0          xor eax,eax
00457C5A  |.  55            push ebp
00457C5B  |.  68 3D7E4500   push CKme.00457E3D
00457C60  |.  64:FF30       push dword ptr fs:[eax]
00457C63  |.  64:8920       mov dword ptr fs:[eax],esp
00457C66  |.  8BB3 F8020000 mov esi,dword ptr ds:[ebx+0x2F8]         ;  name的长度?
00457C6C  |.  83C6 05       add esi,0x5
00457C6F  |.  FFB3 10030000 push dword ptr ds:[ebx+0x310]
00457C75  |.  8D55 F8       lea edx,[local.2]
00457C78  |.  8BC6          mov eax,esi
00457C7A  |.  E8 85FEFAFF   call CKme.00407B04
00457C7F  |.  FF75 F8       push [local.2]                           ;  参数1:str(len(name)+5)
00457C82  |.  FFB3 14030000 push dword ptr ds:[ebx+0x314]            ;  参数2:'dseloffc-012-OK'
00457C88  |.  8D55 F4       lea edx,[local.3]
00457C8B  |.  8B83 D4020000 mov eax,dword ptr ds:[ebx+0x2D4]
00457C91  |.  E8 B2B6FCFF   call CKme.00423348                       ;  获取name
00457C96  |.  FF75 F4       push [local.3]                           ;  参数3:name
00457C99  |.  8D83 18030000 lea eax,dword ptr ds:[ebx+0x318]         ;  参数4:目标地址
00457C9F  |.  BA 04000000   mov edx,0x4                              ;  参数5:4
00457CA4  |.  E8 93BFFAFF   call CKme.00403C3C                       ;  连接字符串
00457CA9  |.  33D2          xor edx,edx
00457CAB  |.  8B83 F4020000 mov eax,dword ptr ds:[ebx+0x2F4]


这一段还不是太明白,只知道大致效果是这样。

写注册机:

name = input("Name:")
key = '黑头Sun Bird' + str(len(name)+5) + 'dseloffc-012-OK' + name
print('Key:', key, sep='')
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  反汇编