亲宝软件园·资讯

展开

记一次:Windows的Socket编程学习和分析过程

Hookcc 人气:1

Socket编程依赖于:WS2_32.dll

 

--- 服务端 ---

1、导入我们需要的函数
#incldue <windows.h> //#include<WinSock2.h> #pragma comment(lib,"ws2_32.lib")



2、初始化(指定要使用的socket版本)
WSADATA ws = {0}; /*WSAStartup 微软MSDN:https:/https://img.qb5200.com/download-x/docs.microsoft.com/en-us/previous-versions/aa921082(v=msdn.10)?redirectedfrom=MSDN
	参数1:版本号  调用者可以使用的Windows套接字最高支持。高位字节指定次要版本号(修订)。低位字节指定主要版本号。
	参数2:存储socket相关的信息
	返回值;成功返回0/
WSAStartuo(MAKEWORD(2,2),&ws); //MAKEWORD 是一个宏用于将其拆成2个word ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))





3、创建socket
/*socket 微软msdn:https:/https://img.qb5200.com/download-x/docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-socket
参数1:地址簇类型,ip地址类型 参数2:socket的类型,数据是以何种方式传输 参数3:协议类型 返回值:成功返回新的socket,失败返回INVALID_SOCKET*/

SOCKET sk = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);





4、设置协议地址族信息(要连接或绑定的ip端口信息)
微软MSDN:https:/https://img.qb5200.com/download-x/docs.microsoft.com/en-us/windows/win32/api/winsock/ns-winsock-sockaddr_in
SOCKADDR_IN sever_addr = {0}; sever_addr.sin_family = AF_INET; //地址族类型 sever_addr.sin_port = htons(8888); //端口 server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //地址

htons函数:intel cpu存储数据的方式采用的是:Littke-endian方式(高位在右,低位在左),而例如ibm早期cpu采用的则是:Big-endian(高位在左,低位在右),而网络协议一致采取Big-endian的方式存储数据,而htons就是将Littke转化为Big的函数
inet_addr函数:将字符串地址转化为某一种数字格式
inet_ntoa函数:将数字格式的地址转化为字符串
需要注意的是描述地址族(莫名其妙的概念)信息本应使用大小为16字节的sockaddr结构体,但是因为该结构体除了sin.family,其余全是存储在一个数组中的,这样赋值难免麻烦,所以才有了SOCKADDR_IN

sockaddr:  
  struct sockaddr {
    u_short sa_family;
    char sa_data[14];};

 

SOCKADDR_IN:
  struct sockaddr_in {

  short sin_family;
  u_short sin_port;
  struct in_addr sin_addr;
  char sin_zero[8];
};





5、绑定(将scocket与地址族联起来
/*bind 参数1:未绑定的socket 参数2:地址族信息地址 参数3:地址族信息的大小 返回值:成功返回0,失败返回SOCKET_ERROR*/
bind(sk,(sockaddr*)&server_addr,sizeof(sockaddr))




6、监听
/*listen
	参数1:已绑定未监听的socket
	参数2:等待连接的队伍最大长度 SOMAXCONN表示自动设置合理值
	返回值:失败返回SOCKET_ERROR,成功返回0*/
listen(sk,SOMAXCONN);




7、接收连接(等待连接)
/*接收连接:accetp 参数1:已监听的socket 参数2 out :建立连接的客户端信息 参数3:传递时包含参数2的大小,返回时包含返回地址的实际长度 返回值:成功就返回已连接的套接字,否则返回INVALID_SOCKET失败信息*/
SOCKADDR_IN cline_addr = {0};
DWORD len = sizeof(SOCKADDR_IN);
SOCKET _cline_socket = accetp(sk,(sockaddr*)&cline_addr,(int*)&len);

accetp函数:这是一个阻塞函数,在有连接前就会等待于此





8、通信
微软MSDN:https:/https://img.qb5200.com/download-x/docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv /*通信recv(接收) send(发送) 参数1;建立连接的socket 参数2: 接数或发送数据的缓冲区 参数3: 缓冲区长度 参数4: 发送或接数数据的方式 返回值:成功返回接数或返回的字节大小,连接正常断开返回0,其他情况返回相应的错误代码*/
char recvbuff[256] = {0};
while(true)
{
  memset(recvbuff,0,256);
  DWORD recvlen = recv(_cline_socket,recvbuff,256,0); //默认为0表示全接收,并结束函数
  if(recvlen > 0)
  {
    printf("%s\r\n",recvlen);
  }else
  {
    switch(recvlen)
    {
      case 0:
        closesocket(_cline_socket); //关闭socket连接
        break;
    }
  }
}
WSACleanup(); //释放ws2_32.dll


--- 客户端 ---
客户端相较于服务端,只需要连接即可进行通信

1、导入需要的函数

#incldue <windows.h> //#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

2、创建scoket

SOCKET sk = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);


3、创建地址族描述信息
SOCKADDR_IN sever_addr = {0};
sever_addr.sin_family = AF_INET; //地址族类型
sever_addr.sin_port = htons(8888); //端口
server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //地址

4、连接到服务端

/*connect
  参数1:未连接的socket
  参数2:连接的议地址族信息(连接的服务端信息)
  参数3:参数2的长度
  返回值:成功返回0,否则返回SOCKET_ERROR*/
connect(sk,(sockadd*)&server_addr,sizeof(SOCKADDR_IN));

5、通信
  char sendbuff[256] = "Hello.";
  DWORD Num = send(sk,sendbuff,256,0); ////如果返回值小于len参数,但是这是一个非阻塞套接字就表示没有发生错误
  if(NUm == SOCKET_ERR)
  {
    printf("%s\r\n",“连接断开”);
    closesocket(sk);
    WSACleeanup();
  }




软件界面:
  

 

根据分析找到了其回调函数:并得到其处理过程函数:

.text:004010F9                 cmp     [ebp+arg_4], 111h ; 判断是否是WM_COMMADN消息
.text:00401100                 jnz     short loc_40116E
.text:00401102                 mov     ecx, [ebp+arg_8]
.text:00401105                 and     ecx, 0FFFFh     ; 获取wparam参数的低4位,也就是控件id
.text:0040110B                 mov     [ebp+var_8], ecx
.text:0040110E                 mov     edx, [ebp+arg_C]
.text:00401111                 mov     [ebp+hWnd], edx
.text:00401114                 cmp     [ebp+var_8], 3EAh ; 判断是否登录按钮
.text:0040111B                 jnz     short loc_40116E
.text:0040111D                 mov     esi, esp
.text:0040111F                 push    0               ; bEnable
.text:00401121                 mov     eax, [ebp+hWnd]
.text:00401124                 push    eax             ; hWnd
.text:00401125                 call    ds:EnableWindow
.text:0040112B                 cmp     esi, esp
.text:0040112D                 call    __chkesp
.text:00401132                 call    sub_40100F      ; 处理函数
.text:00401137                 test    eax, eax
.text:00401139                 jz      short loc_401159
.text:0040113B                 xor     ecx, ecx
.text:0040113D                 mov     cl, byte_41AEB0
.text:00401143                 cmp     ecx, 77h
.text:00401146                 jz      short loc_401159 ; 返回值为0就重新恢复button,否则就退出程序
.text:00401148                 mov     esi, esp
.text:0040114A                 push    0               ; uExitCode
.text:0040114C                 call    ds:ExitProcess

 

sub_40100F函数内核心代码如下:将计算的随机数和用户名长度,密码长度按每个1字节写入到[ebp-0x348]这块内存,之后将用户名和密码也复制到这一块内存
0129751D  |.  6A 00         push 0x0
0129751F  |.  E8 7CFEFFFF   call crackmen.timet_stdio_output::positiona>;  获取时间戳
01297524  |.  83C4 04       add esp,0x4
01297527  |.  50            push eax
01297528  |.  E8 D6B5FFFF   call crackmen.01292B03
0129752D  |.  83C4 04       add esp,0x4
01297530  |.  E8 E8A0FFFF   call crackmen.0129161D                      ;  获取随机数
01297535  |.  25 FF000080   and eax,0x800000FF
0129753A  |.  79 07         jns short crackmen.01297543
0129753C  |.  48            dec eax
0129753D  |.  0D 00FFFFFF   or eax,-0x100
01297542  |.  40            inc eax
01297543  |>  8885 A7FCFFFF mov byte ptr ss:[ebp-0x359],al              ;  把随机数给0x359
01297549  |.  BA 01000000   mov edx,0x1
0129754E  |.  6BC2 00       imul eax,edx,0x0
01297551  |.  8A8D B0FCFFFF mov cl,byte ptr ss:[ebp-0x350]
01297557  |.  888C05 B8FCFF>mov byte ptr ss:[ebp+eax-0x348],cl          ;  用户名的长度给0x348
0129755E  |.  BA 01000000   mov edx,0x1
01297563  |.  c1e2 00       shl edx,0x0
01297566  |.  8A85 ACFCFFFF mov al,byte ptr ss:[ebp-0x354]
0129756C  |.  888415 B8FCFF>mov byte ptr ss:[ebp+edx-0x348],al          ;  把密码给长度给0x348+0x1
01297573  |.  B9 01000000   mov ecx,0x1
01297578  |.  D1E1          shl ecx,1
0129757A  |.  8A95 A7FCFFFF mov dl,byte ptr ss:[ebp-0x359]
01297580  |.  88940D B8FCFF>mov byte ptr ss:[ebp+ecx-0x348],dl          ;  把随机数给0x348+0x2
01297587  |.  8B85 B0FCFFFF mov eax,[local.212]
0129758D  |.  50            push eax
0129758E  |.  8D4D C0       lea ecx,[local.16]
01297591  |.  51            push ecx
01297592  |.  8D95 BBFCFFFF lea edx,dword ptr ss:[ebp-0x345]
01297598  |.  52            push edx
01297599  |.  E8 0DBDFFFF   call crackmen.012932AB                      ;  将用户名复制到 [ebp-0x345]
0129759E  |.  83C4 0C       add esp,0xC
012975A1  |.  8B85 ACFCFFFF mov eax,[local.213]
012975A7  |.  50            push eax
012975A8  |.  8D4D 84       lea ecx,[local.31]
012975AB  |.  51            push ecx
012975AC  |.  8B95 B0FCFFFF mov edx,[local.212]
012975B2  |.  8D8415 BBFCFF>lea eax,dword ptr ss:[ebp+edx-0x345]
012975B9  |.  50            push eax
012975BA  |.  E8 ECBCFFFF   call crackmen.012932AB                      ;  将密码复制到 [ebp+用户名长度-0x345]

当上面步骤执行完后:从[ebp-0x348]这一块内存,就存储了如下格式的数据:04 03 77 61 61 61 61 66 66 66  前3个字节分别是:随机数,用户名长度,密码长度,后面的就是用户名和密码

 

 

对[ebp-0x348]进行简单的加密

012975C2  |.  8B8D ACFCFFFF mov ecx,[local.213]  ;获取用户名长度
012975C8  |.  8B95 B0FCFFFF mov edx,[local.212] ;//获取密码长度
012975CE  |.  8D440A 03     lea eax,dword ptr ds:[edx+ecx+0x3]  //得到整个[ebp-0x348]内存的长度
012975D2  |.  8985 A8FCFFFF mov [local.214],eax                         ;  liack0x214存储的就是整个用户名长度+整个密码长度+0x3(随机数,用户名长度值,密码长度值+)
012975D8  |.  C785 9CFCFFFF>mov [local.217],0x0                         ;  217作为循环变量使用i
012975E2  |.  EB 0F         jmp short crackmen.012975F3
012975E4  |>  8B8D 9CFCFFFF /mov ecx,[local.217]
012975EA  |.  83C1 01       |add ecx,0x1    ;i++
012975ED  |.  898D 9CFCFFFF |mov [local.217],ecx
012975F3  |>  8B95 9CFCFFFF  mov edx,[local.217]
012975F9  |.  3B95 A8FCFFFF |cmp edx,[local.214]                        ;  如果i大于等于214([ebp-0x348]缓冲区的总长度)就跳出循环
012975FF  |.  7D 23         |jge short crackmen.01297624
01297601  |.  8B85 9CFCFFFF |mov eax,[local.217]
01297607  |.  0FBE8C05 B8FC>|movsx ecx,byte ptr ss:[ebp+eax-0x348]      ;  取1字节进行加密
0129760F  |.  81F1 A6000000 |xor ecx,0xA6                               ;  
01297615  |.  8B95 9CFCFFFF |mov edx,[local.217]
0129761B  |.  888C15 B8FCFF>|mov byte ptr ss:[ebp+edx-0x348],cl         ;  将加密后的值写回
01297622  |.^ EB C0         \jmp short crackmen.012975E4

 

发送数据到服务端:

01297624  |> \6A 00         push 0x0                                    ; /Flags = 0
01297626  |.  8B85 A8FCFFFF mov eax,[local.214]                         ; |
0129762C  |.  50            push eax                                    ; |DataSize = A (10.)
0129762D  |.  8D8D B8FCFFFF lea ecx,[local.210]                         ; |
01297633  |.  51            push ecx                                    ; |Data = 00000003
01297634  |.  8B95 80FCFFFF mov edx,[local.224]                         ; |
0129763A  |.  52            push edx                                    ; |Socket = 0x4
0129763B  |.  E8 84050000   call crackmen.tring_output_adapter<char> ><>; \send


接收服务端返回的数据,并进行解密:

01297654  |.  6A 00         push 0x0                                    ; /Flags = 0
01297656  |.  68 F4010000   push 0x1F4                                  ; |BufSize = 1F4 (500.)
0129765B      8D85 88FDFFFF lea eax,dword ptr ss:[ebp-0x278]            ;  [ebp-0x278]作为接收数据的缓冲区
01297661  |.  50            push eax                                    ; |Buffer = 0000000A
01297662  |.  8B8D 80FCFFFF mov ecx,[local.224]                         ; |
01297668  |.  51            push ecx                                    ; |Socket = 0x3
01297669  |.  E8 50050000   call crackmen.<char,__crt_stdio_output::str>; \recv
0129766E  |.  8985 A0FCFFFF mov [local.216],eax                         ;  局部变量:loacal.216是接收的数据长度
01297674  |.  C785 9CFCFFFF>mov [local.217],0x0                         ;  217作为循环值i
0129767E  |.  EB 0F         jmp short crackmen.0129768F ;循环开始
01297680  |>  8B95 9CFCFFFF /mov edx,[local.217]
01297686  |.  83C2 01       |add edx,0x1 ;i++
01297689  |.  8995 9CFCFFFF |mov [local.217],edx
0129768F  |>  8B85 9CFCFFFF  mov eax,[local.217]
01297695  |.  3B85 A0FCFFFF |cmp eax,[local.216]                        ;  i < revc()
0129769B  |.  7D 1F         |jge short crackmen.012976BC
0129769D  |.  8B8D 9CFCFFFF |mov ecx,[local.217]
012976A3  |.  0FB6940D 88FD>|movzx edx,byte ptr ss:[ebp+ecx-0x278]      ;  从[ebp-278]取一个字节,并解密
012976AB  |.  83F2 6E       |xor edx,0x6E                               ;  解密
012976AE  |.  8B85 9CFCFFFF |mov eax,[local.217]
012976B4  |.  8890 F0AE3501 |mov byte ptr ds:[eax+eeam_output_adapter<w>;  0x135AEF0 存储解密的数据内存地址0x135AEF0
012976BA  |.^ EB C4         \jmp short crackmen.01297680

根据解密后的数据进行验证:
012976BC  |> \B9 01000000   mov ecx,0x1
012976C1  |.  C1E1 02       shl ecx,0x2                                 ;  ecx = 0x4
012976C4  |.  0FB691 F0AE35>movzx edx,byte ptr ds:[ecx+eeam_output_adap>;  从解密的数据中+0x4 取1byte 是否为0xd5
012976CB  |.  81FA D5000000 cmp edx,0xD5
012976D1  |.  0F85 9E000000 jnz crackmen.01297775 ;1297775结束位置
012976D7  |.  B8 01000000   mov eax,0x1
012976DC  |.  6BC8 0E       imul ecx,eax,0xE
012976DF  |.  0FB691 F0AE35>movzx edx,byte ptr ds:[ecx+eeam_output_adap>;  从解密的数据中+0xe 取1byte
012976E6  |.  0FB685 A7FCFF>movzx eax,byte ptr ss:[ebp-0x359]           ;  取之前获取的随机数
012976ED  |.  3BD0          cmp edx,eax                                 ;  判断是否合之前的获取的随机数相等
012976EF  |.  0F85 80000000 jnz crackmen.01297775
012976F5  |.  B9 01000000   mov ecx,0x1
012976FA  |.  6BD1 0A       imul edx,ecx,0xA
012976FD  |.  0FB682 F0AE35>movzx eax,byte ptr ds:[edx+eeam_output_adap>;  从解密的数据中+0xA 取1byte并+0x3,判断结果是否为0x16
01297704  |.  83C0 03       add eax,0x3                                 ;  +=3
01297707  |.  83F8 16       cmp eax,0x16                                ;  判断是否为0x16
0129770A  |.  75 69         jnz short crackmen.01297775
0129770C  |.  B9 01000000   mov ecx,0x1
01297711  |.  6BD1 23       imul edx,ecx,0x23                           ;  从解密的数据中+0x23 取1byte 是否为0bd
01297714  |.  0FB682 F0AE35>movzx eax,byte ptr ds:[edx+eeam_output_adap>
0129771B  |.  3D BD000000   cmp eax,0xBD
01297720  |.  75 53         jnz short crackmen.01297775
01297722  |.  B9 01000000   mov ecx,0x1
01297727  |.  6BD1 3C       imul edx,ecx,0x3C
0129772A  |.  0FB682 F0AE35>movzx eax,byte ptr ds:[edx+eeam_output_adap>;  从解密的数据中+0x3c 取1byte 是否为0x43
01297731  |.  83F8 43       cmp eax,0x43
01297734  |.  75 3F         jnz short crackmen.01297775
01297736  |.  B9 01000000   mov ecx,0x1
0129773B  |.  6BD1 42       imul edx,ecx,0x42
0129773E  |.  0FB682 F0AE35>movzx eax,byte ptr ds:[edx+eeam_output_adap>;  从解密的数据中+0x42 取1byte 是否为0xc6
01297745  |.  3D C6000000   cmp eax,0xC6
0129774A  |.  75 29         jnz short crackmen.01297775
0129774C  |.  8BF4          mov esi,esp
0129774E  |.  6A 00         push 0x0                                    ; /Style = MB_OK|MB_APPLMODAL
01297750  |.  68 58A03501   push crackmen.0135A058                      ; |Title = "Crackne net-2"
01297755  |.  68 68A03501   push crackmen.0135A068                      ; |Text = "Registration successful !"
0129775A  |.  8B0D 80C63501 mov ecx,dword ptr ds:[0xnions'::`2'::_Optio>; |
01297760  |.  51            push ecx                                    ; |hOwner = 00000003
01297761  |.  FF15 D0D13501 call dword ptr ds:[<&USER32.MessageBoxA>]   ; \MessageBoxA

加载全部内容

相关教程
猜你喜欢
用户评论