记一次:Windows的Socket编程学习和分析过程
Hookcc 人气:1Socket编程依赖于: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
加载全部内容