[转]由一张不科学的图片说起

【转载本文不写出处的,男的木丁丁,女的满脸都是丁丁】

出自http://chichou.0ginr.com/blog/328

昨晚上偶然看到个比较坑爹的日志,正文有一张图片,在好友动态列表查看的时候可以显示自己(不是发日志的好友,是你自己)的头像和QQ号码、昵称。正文的文字称,这是“本年度最给力神奇魔力日志(转载会看到你最熟悉的身影)”,并且在文章底部附加了一个发广告的QQ号码。

由于这张坑爹图片的存在,文章转发量短短三天内过千。这招可比那些疯狂加群发广告的来的更有创意和杀伤力,转载传播图片(包括日志当中的广告)的人几乎是全网用户(跟所谓病毒营销差不多了)。好奇的话可以看这篇日志最初来源:hXXp://user.qzone.qq.com/732678621/blog/1363502247

一看就知道是检测referer的把戏。早在几年前BBS还在流行的时候,很多人设置的签名图具有天气预报、客户端信息(浏览器、操作系统、IP所在地之类)、随机笑话、倒计时等五花八门的功能。这都得益于服务器端脚本的图像处理功能。而客户端的检测则是基于HTTP请求中的UserAgent和Referer等信息。

但是印象中QQ空间为了防止referer潜在的安全问题和防止图片被防盗链下了很大功夫,凡是发表到QQ空间的日志,正文都会把引用到的所有第三方图片资源缓存到腾讯的云端上。所以直接在日志正文中引用的图片,是不会提交REFER到我们的服务器脚本上的。

更新:有人发现只要响应HTTP请求的时候输出状态200,但不输出任何内容,腾讯就不会缓存这张图。

文中特别称,“请转载后用电脑进入个人中心看”。为什么要特别说明是“个人中心”呢?我刷新了好久的动态,终于看到了图片所说的效果。页面生成的DOM代码为:

1
2
3
 onload="QZFL.media.reduceImage(0,400,300,{trueSrc:'http:\/\/qq.sennvwu.com\/qzone\/do.php',callback:function(img,type,ew,eh,o){var _h = Math.floor(o.oh/o.k),_w = Math.floor(o.ow/o.k);if(_w<=ew && _h>=eh){var p=img.parentNode;p.style.width=_w+'px';p.style.height=_h+'px';}}})"
 width="400">

原来QQ空间还是会显示源地址的图片的,仅限于在“个人中心”。这时候请求图片附带的HTTP_REFERER的值为

http://user.qzone.qq.com/123456789/infocenter

号码就是这样提取到的。如果REFERER不满足条件,这个php将在header中发送Location跳转到同一目录下的no.png。

那么后台是如何取到QQ头像、昵称等信息的呢?我Google到了一个腾讯的WebService接口:

http://base.qzone.qq.com/fcg-bin/cgi_get_portrait.fcg?uins=123456789

不需要任何凭证信息即可获取uins指定的QQ号码的头像、昵称信息,返回的格式为JSON。另外上面的图片还有一个显示地理位置和ISP的功能,这个就比较常见了。我找到了一个比较好用的接口,来自TB:

http://ip.taobao.com/service/getIpInfo.php?ip=127.0.0.1

格式同样也是JSON。

接下来实现这个效果就比较简单了,通过REFERER检测用户的QQ号码,然后在后台下载头像、昵称等信息,用GD函数绘制上图片,返回客户端。

我也折腾了一个‘神奇图片“发到空间,居然捉弄了一群人。下面是php语言的实现代码。为了减少后端的流量,对下载的头像做了缓存处理:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
error_reporting(0);
ob_start();
header('Content-Type: image/png');
define('IMG_NO', "no.png"); #刚开始显示的提示信息
define('IMG_BACKGROUND', "background.png");
define('IMG_WIDTH', 400);
define('IMG_HEIGHT', 128);
define('FONT_NAME', "AdobeHeitiStd-Regular.otf"); #字体文件名
define('CACHE_PATH', rtrim(realpath("./cache"), '/').'/'); #缓存目录
define('CACHE_EXPIRE', 60*60); #缓存时间,单位秒
#(!is_dir(CACHE_PATH) && is_writable(CACHE_PATH)) || die;
/*
 $remote: 远程URL
 $local: 本地缓存路径
 $expire: 过期时间。为-1时,永久不更新缓存
*/
function load_from_cache($remote, $local, $expire = CACHE_EXPIRE, $as_path = false) {
 //过滤潜在的危险字符
 $local = preg_replace("/[.\/\\\?\*\'\"\|\:\<\>]/", "_", $local);
 $cache = CACHE_PATH.$local;
 //查找缓存
 if(file_exists($cache) && ($expire = -1 || filemtime($cache) - time() < $expire))
 return $as_path ? $cache : file_get_contents($cache);
 //文件不存在或缓存过期,重新下载
 $content = file_get_contents($remote);
 file_put_contents($cache, $content);
 return $as_path ? $cache : $content;
}
/*
 返回客户端信息。
*/
function client_info() {
 $ip = ($_SERVER["HTTP_VIA"] && $_SERVER["HTTP_X_FORWARDED_FOR"] ?
 $_SERVER["HTTP_X_FORWARDED_FOR"] : $_SERVER["REMOTE_ADDR"]);
 $info = explode('"', load_from_cache($url.$ip, $ip, -1));
 $string = $info[7].$info[23].$info[31].$info[47];
 return json_decode('"'.$string.'"');
}
$referer = $_SERVER['HTTP_REFERER'];
$pattern = "/http:\/\/user.qzone.qq.com\/(\d+)\/infocenter/";
if(preg_match($pattern, $referer, $matches)) {
 //获取QQ号码
 $uin = $matches[1];
 $info = explode('"', load_from_cache(
 $avatar = $info[3];
 $nickname = iconv("GBK", "UTF-8//IGNORE", $info[5]);
 $client = client_info();
//重点来了,生成图片
 try{
 $im = imagecreatefrompng(IMG_BACKGROUND);
//绘制头像
 $avatar_file = load_from_cache($avatar, $uin.".jpg", 60*60*24, true);
 $im_avatar = imagecreatefromjpeg($avatar_file);
 imagecopymerge($im, $im_avatar, 14, 14, 0, 0, 100, 100, 100);
 imagedestroy($im_avatar);
//绘制文字
 $blue = imagecolorallocate($im, 0, 0x99, 0xFF);
$white = imagecolorallocate($im, 0xFF, 0xFF, 0xFF);
$texts = array(
 array(12, 148, 40, $white, $uin),
 array(18, 125, 70, $blue, $nickname),
 array(16, 125, 100, $blue, $client)
 );
foreach($texts as $key=>$value) {
 imagettftext($im, $value[0], 0, $value[1], $value[2], $value[3], FONT_NAME,
 mb_convert_encoding($value[4], "html-entities", "utf-8")); //解决乱码问题
 }
imagepng($im);
 imagedestroy($im);
header("Content-Length: ".ob_get_length());
 ob_end_flush();
 } catch (Exception $e) {
//die($e->getMessage());
 $error = true;
 }
} else {
 $error = true;
}
//更新:在这里直接停止输出即可防止图片被缓存
die();
/*
if($error){
 header('Content-Length: '.filesize(IMG_NO));
 echo file_get_contents(IMG_NO);
}
*/

WORDPRESS把缩进都吞掉了,看上去非常不爽。下面还是直接给出我的打包吧,连图片也有。不过字体文件太大而且有版权纠纷,请自行准备。

QZone Trick

 

CC BY-NC-SA 4.0 [转]由一张不科学的图片说起 by 桔子小窝 is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

发表回复

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据