分享
 
 
 

Linux下的透明代理技术

王朝system·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

想像这么一个场景你想截获(hijack)一个本地的出口连接(LOCALOUT)或者转发的连接(PREROUTING),对这个连接的两个方向的内容做修改,比如:1、将这个连接连接到远程socks代理(在通讯头部加上socks通信协议部分)2、对这个连接进行记录(用于协议分析)3、任何你能想到的折腾方式,比如我们叫他tcpgrep,:)。

那么我们该如何做呢?

一、获得连接

首先,我们要能获得这个连接,很容易想到的是用iptables连接REDIRECT方法(和DNAT类似),如果你做过squid的透明代理,应该会觉得很熟悉。

#iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports 8010

当然,你应该忽略一些不感兴趣的连接,可以单独建立一个chains,然后配和-j RETURN就可以做到。如果你熟悉iptables,这个应该很容易做到,我就不多说了。

现在呢?

nc -l -p 8010

(注:BSD style 的nc,如果你是GNU nc,用nc -l 8888)

然后nc www.baidu.com 80

很好,连接已经被第一个nc hijack了。但是,问题来了,他要去哪里呢?

二、获得ORGINAL DESTINALTION

这里就是透明代理技术的关键所在了。

0、socks代理方式的这个方式并不是我们所需要的东西,因为这个需要涉及到用户程序的修改。因为用户需要将目的地址发送给服务器(按照socks协议),如果用户程序本身没有考虑支持socks协议那么就没有直接的办法了。

1、方法一:做一个专用内核模块

我曾经做过一个,在http://s5snake.gro.clinux.org有相关信息,是专门用于socks[45]的东西。不过在kernel 2.6.9以后因为设计上的问题,对于LOCALOUT的连接就失效了,现在我已经不维护这个老的模块了。

这种方法太吃力,而且调试困难,需要同时维护netfilter/iptables的内核态模块和用户态模块,所以不推荐再使用了。

2、方法二: PRELOAD方式

通过PRELOAD一个库,修改socks,connect函数的调用来达到目的。

tsocks就是这么做的(http://tsocks.sourceforge.net/)

这样的方式非常像windows下的sockscap32程序,但是有个最大的问题是,只能在本机使用,对转发的连接就不适用了。

3、方式三:SO_ORIGINAL_DST

SO_ORIGINAL_DST是一个socket参数(SOL_IP层的)。

调用方式如下:

getsockopt (clifd, SOL_IP, SO_ORIGINAL_DST, &orig_addr, &sin_size);

clifd是hijack到的客户socket,orig_addr是sockaddr_in结构的参数,sin_size=sizeof(sockaddr_in).

返回0如果成功,-1失败。

如果成功orig_addr将是客户真正需要去的方向(恩……撒个小谎,后面你会看到)。先给段代码吧:

/*

* Simple "Hello, World!" server

* patch: demonstrate SO_ORIGINAL_DST

*/

#include <stdio.h /* */

#include <stdlib.h /* exit() */

#include <string.h /* memset(), memcpy() */

#include <sys/utsname.h /* uname() */

#include <sys/types.h

#include <sys/socket.h

/* socket(), bind(),listen(), accept(),getsockopt() */

#include <linux/netfilter_ipv4.h

#include <netinet/in.h

#include <arpa/inet.h

#include <netdb.h

#include <unistd.h /* fork(), write(), close() */

char message[BUFSIZ];

const int BACK_LOG = 5;

int main(int argc, char *argv[])

{

int serverSocket = 0,

on = 0,

port = 0,

status = 0,

childPid = 0;

struct hostent *hostPtr = NULL;

char

hostname[80] = "";

struct sockaddr_in serverName = { 0 };

struct sockaddr_in originDst

= { 0 };

socklen_t

sin_size

= sizeof(originDst);

if (2 != argc)

{

fprintf(stderr, "Usage: %s portnum\n",

argv[0]);

exit(1);

}

port = atoi(argv[1]);

serverSocket = socket(PF_INET, SOCK_STREAM,

IPPROTO_TCP);

if (-1 == serverSocket)

{

perror("socket()");

exit(1);

}

on = 1;

status = setsockopt(serverSocket, SOL_SOCKET,

SO_REUSEADDR,

(const char *) &on, sizeof(on));

if (-1 == status)

{

perror("setsockopt(...,SO_REUSEADDR,...)");

}

{

struct linger linger = { 0 };

linger.l_onoff = 1;

linger.l_linger = 30;

status = setsockopt(serverSocket,

SOL_SOCKET, SO_LINGER,

(const char *) &linger,

sizeof(linger));

if (-1 == status)

{

perror("setsockopt(...,SO_LINGER,...)");

}

}

/*

* find out who I am

*/

status = gethostname(hostname,

sizeof(hostname));

if (-1 == status)

{

perror("gethostname()");

exit(1);

}

hostPtr = gethostbyname(hostname);

if (NULL == hostPtr)

{

perror("gethostbyname()");

exit(1);

}

(void) memset(&serverName, 0,

sizeof(serverName));

(void) memcpy(&serverName.sin_addr,

hostPtr-h_addr,

hostPtr-h_length);

serverName.sin_addr.s_addr=htonl(INADDR_ANY);

serverName.sin_family = AF_INET;

serverName.sin_port = htons(port);

status = bind(serverSocket,

(struct sockaddr *) &serverName,

sizeof(serverName));

if (-1 == status)

{

perror("bind()");

exit(1);

}

status = listen(serverSocket, BACK_LOG);

if (-1 == status)

{

perror("listen()");

exit(1);

}

for (;;)

{

struct sockaddr_in clientName = { 0 };

int slaveSocket;

socklen_t clientLength =

sizeof(clientName);

(void) memset(&clientName, 0,

sizeof(clientName));

slaveSocket = accept(serverSocket,

(struct sockaddr *) &clientName,

&clientLength);

if (-1 == slaveSocket)

{

perror("accept()");

exit(1);

}

childPid = fork();

switch (childPid)

{

case -1: /* ERROR */

perror("fork()");

exit(1);

case 0: /* child process */

close(serverSocket);

if (-1 == getpeername(slaveSocket,

(struct sockaddr *) &clientName,

&clientLength))

{

perror("getpeername()");

}

else

{

if(getsockopt( slaveSocket, SOL_IP, SO_ORIGINAL_DST, &originDst, &sin_size) == 0){

printf("new connection:%s,%u",inet_ntoa(clientName.sin_addr),ntohs(clientName.sin_port));

printf("-%s,%u\n",inet_ntoa(originDst.sin_addr),ntohs(originDst.sin_port));

}else{

perror("getsockopt SO_ORIGINAL_DST:");

}

}

do{

read(slaveSocket,message,BUFSIZ);

write(1,message,strlen(message));

write(slaveSocket, message,strlen(message));

}while(message[0]);

close(slaveSocket);

exit(0);

default:

}

}

return 0;

}

编译运行前,记得

#iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports [端口号]

运行后,如果一切正常,那么你是很幸运,如果得到的是服务器运行的地址和端口,那么你很不幸,你很可能用的是2.6.9-2.6.12之间的

内核,很明显这是一个BUG,见:http://patchwork.netfilter.org/netfilter-devel/patch.pl?id=2676

那么怎么办呢?

1、升级到2.6.13的内核,2.6.13已经合并了上面的那个patch。

2、降级到低版本的内核,前提是有SO_ORIGINAL_DST选项,并测试是否正常

3、手动分析/proc/net/ip_conntrack文件,个人分析过可行性,并认为这种方法是一种可行的补救措施,不过一直没有动手写_=_!

三、恩……随你怎么玩吧

用poll或者select做中间人,做tcp-grep游戏(s/microsft/gnu/g,哈哈),分析QQ协议,等等等等。写个插件式的框架会更好的:)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有