博文

目前显示的是 2012的博文

gcc中获知表达式的类型

有时候我们想知道一个东西它具体是什么类型。比如,pthread_t,它到底是个整数呢,还是个指针呢,还是什么其它东西呢?幸运的是,gcc的abi中有一个函数可以帮我们的忙。#include <iostream>#include <typeinfo>#include <cxxabi.h>#include <stdlib.h>#include <pthread.h>usingnamespacestd; intmain(int argc, char* argv[]){ int status; pthread_t thr; char* realname = abi::__cxa_demangle(typeid(thr).name(), 0, 0, &status); std::cout << realname << std::endl; free(realname); return0; } # g++ -o t test.cpp -g3 -Wall # ./tunsigned long另外,我们还可以用它来检查下数字和字符串常量是什么类型。比如0xFFFFFFFF是什么类型?#include <iostream>#include <typeinfo>#include <cxxabi.h>#include <stdlib.h>#include <pthread.h>usingnamespacestd; intmain(int argc, char* argv[]){ int status; char* realname = abi::__cxa_demangle(typeid(0xFFFFFFFF).name(), 0, 0, &status); std::cout << realname << std::endl; free(realname); return0; } 还是很有趣的.比如0x0FFFFFFF的类型是int,而0xFFFFFFFF的类型就是unsigned int。Integer literals基本规则:以0开头的一定是8进制
以…

LLVM 5.0发布,附下载地址及安装办法

官方文档: http://releases.llvm.org/5.0.0/docs/CMake.html

下载地址:
http://llvm.org/releases/5.0.0/

如果你没有找到与你系统对应的二进制包,可按下述步骤编译及安装:
下载源代码 mkdir llvm
cd llvm

aria2c -x 5 -j 5 -k 1M http://releases.llvm.org/5.0.0/llvm-5.0.0.src.tar.xz
aria2c -x 5 -j 5 -k 1M http://releases.llvm.org/5.0.0/cfe-5.0.0.src.tar.xz
aria2c -x 5 -j 5 -k 1M http://releases.llvm.org/5.0.0/compiler-rt-5.0.0.src.tar.xz
aria2c -x 5 -j 5 -k 1M http://releases.llvm.org/5.0.0/lldb-5.0.0.src.tar.xz
aria2c -x 5 -j 5 -k 1M http://releases.llvm.org/5.0.0/lld-5.0.0.src.tar.xz
aria2c -x 5 -j 5 -k 1M http://releases.llvm.org/5.0.0/clang-tools-extra-5.0.0.src.tar.xz
aria2c -x 5 -j 5 -k 1M http://releases.llvm.org/5.0.0/polly-5.0.0.src.tar.xz
aria2c -x 5 -j 5 -k 1M http://releases.llvm.org/5.0.0/openmp-5.0.0.src.tar.xz

解压缩
for filename in *.xz; do tar -Jxf $filename ; done

mv cfe-5.0.0.src llvm-5.0.0.src/tools/clang
mv compiler-rt-5.0.0.src llvm-5.0.0.src/projects/compiler-rt
mv clang-tools-extra-5.0.0.src llvm-5.0.0.src/tools/clang/tool…

LLVM初体验

LLVM是一套新的C/C++工具链,用来取代GNU的gcc、g++、binutils(如ar、objdump等)。目前XCode 4已经把默认的编译器换成了gcc 4.2+LLVM。即用gcc 4.2做前端,生成中间代码,然后用LLVM把中间代码转换成二进制。LLVM项目还有一个子项目叫clang,是C/C++编译器的前端,用起来就跟gcc/g++基本一样。在编译速度和最终二进制码的执行效率上来说,LLVM已经勉强可以与GCC匹敌。这个只能看各处的评测报告。但是在实际可用性上来说,目前还比较惨。GNU有一套C++的标准库,libstdc++。这个必须要打补丁后才能被LLVM/clang编译过去。于是在fedora 17上,clang连一个最最简单的C++的hello world都编译不过去。编译不过的主要原因是: 1. gcc的很多内置关键字,需要clang支持。 2. gcc在对C++11的支持上远胜于LLVM,于是它就可以大胆的在libstdc++的代码中使用很多语言新特性。所以,在这个问题上,LLVM会长期处于被动、被拖累的状态。据说等clang 3.2(这周)发布后对libstdc++的支持会好点。于是Apple开了一个新项目,libc++来取代libstdc++。目前这个已经用在了Mac OS X上。但是对Linux用户来说,我觉得这是个噩梦。两套C++标准库啊…. 肯定不能混联。不信你把一个libstdc++的string*传递给libc++中试一试…… 那么做发行版的怎么办呢?只有放弃libc++。总之,如果没有用到太多C++11的标准库,在公司内部的小项目试用LLVM我觉得蛮不错的。起码错误信息更友善一点,节省些开发时间。如果现在的项目是用autotools/cmake管理的话,那么切换起来很简单。设置下CC/CXX这两个环境变量即可。Apple看来是对gcc恨之入骨,等XCode 5发布的时候,估计不会再支持gcc了。

vmware安装程序的神奇BUG!

我想用虚拟机装一个Mac OS X,据说vmware workstation 9.0新添了对Mac OS X的支持,于是我赶紧卸载8.0安装9.0试一试。我从http://www.crsky.com/soft/1863.html 上下载了zip包,解压缩得到VMware-workstation-full-9.0.1-894247.exe。这是安装程序,我嫌名字太长,就改名为vmware.exe。然后双击安装,告诉我,“setup has detected vmware software running on this machine”。不让我装!然后我在命令行下运行vmware.exe /? ,得到参数列表。发现可以通过“VMware.exe /d /v /f2 D:\1.log”这样打开调试日志。重新运行,浏览日志后发现,20121217141611:INFO *** Executing CCheckRunningProcessesOperation::Execute
20121217141611:INFO CCheckRunningProcessesOperation::Execute: checking for running process: 'vmware-vmx.exe'
20121217141611:INFO CProcess::isRunning: Could not find running process: vmware-vmx.exe
20121217141611:INFO CCheckRunningProcessesOperation::Execute: checking for running process: 'vmware-vmx-debug.exe'
20121217141611:INFO CProcess::isRunning: Could not find running process: vmware-vmx-debug.exe
20121217141611:INFO CCheckRunningProcessesOperation::Execute: checking for running process: 'vmware-vmx-stats.exe'

Tcp Fast Open and Linux 3.7

今天Linux 3.7发布了,一个引人注目的特性是,包含了完整的对TCP Fast Open(TFO)的支持,client端以及server端。传统的TCP需要经过3次握手才能建立。假设我们要从服务器上用http协议下载一个图片C->S : SYNS->C : SYN+ACKC->S : ACK+ http get requestS->S : http response所以一个最简单的HTTP请求也需要2个round-trip time才能完成。比如从北京到加州的round-trip time大约是200ms左右,2 round-trip time 就是400ms左右。无论如何增加带宽,都无济于事。Google给出了一个解决方案,让第一个SYN包就携带数据,于是在一个RTT之内就能得到response。但是,这个看似很小的改动,实际应用起来却很不容易。首先,目前只有Linux支持了TFO,先给你的Linux升级kernel吧。其次,server和client的代码都得改。server在listen之前要设置TFO选项。int qlen = 5;
setsockopt(sfd, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen));client就复杂多了。去掉connect调用,send/write方法要改成sendto或者sendmsg。sendto(sfd, data, data_len, MSG_FASTOPEN,
(struct sockaddr *) &server_addr, addr_len);看完TFO的paper后,作为一个搞后台开发的程序员,我当时首先想的是,如果MySQL也启用TFO的话……我不知道你们怎么用MySQL的。JAVA程序员习惯用长连接,而PHP程序员习惯用短连接,我之前在某浪公司工作的时候,那边则是强烈要求使用短连接。短连接的原因是MySQL是按Connection分配内存资源(而非Query),短连接能提高内存利用率。但是另一方面,谁都知道,每次都打开、关闭TCP连接,进行身份认证,设置变量,会造成不必要的消耗,增大延迟。否则你告诉我,JAVA程序员干嘛非得去用连接池?但是TFO要适用在MySQL上还必须得修改MySQL协议。因…

密码能不用明文传输吗?

我发现居然还有很多,每天有几千万人次访问的网站,登录密码居然是明文在网上传输。没有https,也没有CHAP。tudou.com就是其中之一。还有某网站,虽然把登录页换成了https,但是它支持以第三方帐号登录。登录绑定页又是http。无语。能更山寨点吗?呼唤立法限制!!!

Hadoop unable to create new native thread

这已经是我第N次遇到这个问题了。我的leader是一个做事很小心的人,每次用机器的时候都生怕用坏了,任务不敢多开,进程不敢多启,而我恰恰相反,喜欢一次开几十、上百个进程让它们去跑。于是就这样了:2012-12-05 16:59:50,649 ERROR org.apache.hadoop.hdfs.server.datanode.DataNode: DatanodeRegistration(10.4.1.26:50010, storageID=DS-1495479526-10.4.1.26-50010-1351698048013, infoPort=50075, ipcPort=50020):DataXceiveServer: Exiting due to:java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:691)
at org.apache.hadoop.hdfs.server.datanode.DataXceiverServer.run(DataXceiverServer.java:133)
at java.lang.Thread.run(Thread.java:722)2012-12-05 16:59:50,650 INFO org.apache.hadoop.hdfs.server.datanode.DataNode: PacketResponder 1 for block blk_-7993801208181269223_29312 terminating
2012-12-05 16:59:50,650 INFO org.apache.hadoop.hdfs.server.datanode.DataNode: Receiving block blk_-5613326197766122812_29314 src: /10.4.1.26:50409 dest: /10.4.1.26:50010
2012-12-05 16:59:50,709 INFO org.apache.h…

code snippet: 计算HDFS中文件的md5值

org.apache.hadoop.fs.FSDataInputStream os = dfs.open(outputFileName); byte[] buffer = newbyte[1024 * 1024 * 2]; MessageDigest md5 = MessageDigest.getInstance("MD5"); try { while (true) { int bytesRead = os.read(buffer); if (bytesRead <= -1) break; elseif(bytesRead > 0){ md5.update(buffer, 0, bytesRead); } } } finally { os.close(); } byte[] result = md5.digest(); String hexString = DatatypeConverter.printHexBinary(result); Hadoop其实本身给每个文件都存的有checksum,请参见MD5MD5CRC32FileChecksum这个类。但是那个跟md5sum计算出来的md5值并不一样。这是个串行化版本,求更好的实现。

prgmr的硬盘损坏导致客户的VPS数据丢失事件

prgmr.inc是美国加州的一家IaaS服务商,主要是提供基于xen的虚拟机。这个公司本来只有一个人,现在扩张到大概有2-3个。一共有70多台Dom0主机,并承诺99.5%的可用性(实际都在99.9%左右),这些主机主要都由Luke Crawford一人维护,所以我很佩服他的运维能力。但是天有不测风云。这周,有一台Dom0机器(boutros)的硬盘突然坏了。这个机器一共8块硬盘,做的是raid10。结果一次性坏掉了3块(4块?),恰好有两块硬盘是属于同一个Mirror,于是就惨了,RAID整个坏掉处于不可用的状态,只能手工做数据恢复。具体的经过可见这里:http://wiki.prgmr.com/mediawiki/index.php/20121124boutros_post-mortem(困。先睡了,过几天再更新详细的经过)

用Bloom Filter的方式统计网络流量

背景:我现在在一个网站工作,每天都有很多网络爬虫和恶意攻击。我想根据http访问日志统计一下每个IP每天的访问次数,然后大于1万的都认为是机器人。现在寻求一个高效且实时的算法解决这个问题。最简单的做法,就是用一个map来记录所有IP的访问次数。那么这可能会需要几百兆的内存。有一个更好的办法,可以在O(1)的空间复杂度中解决这个问题。算法:我们用一个m 乘 k 的 二维数组来存放所有的计数值。此外,我们还需要m个两两独立的散列函数,每个散列函数将输入(即IP地址)散列到[0,k)范围内的整数。伪代码如下:int count[m][k]; int c=INT_MAX; Object input ; // the ip;for(int i=0;i!=m;++i){ int index=hash(i,input); //用第i个hash函数对input做hash。 c= std::min(c,++count[i][index]); } if(c>10000) printf("catch one robot"); 由代码可以看出,每来一个input的时候,会同时增加m个计数器的值。一个更好的改进是,只增加这m个当中值最小的那个。例如7、5、4 变成 7、5、5。但是7、3、3必须变成7、4、4而不是7、4、3。分析:这是Bloom Filter的一个变种。原始的Bloom Filter算法中,hash对应的是0/1这样的一个bit。而此处把bit改成了一个整数。和Bloom Filter一样,它也存在假阳性的问题,就是,有些IP明明没有访问那么多次,但是我以为它有。降低假阳性率的方式就是提高m和k的数值。同时,和Bloom Filter一样,hash函数的选择也很关键。如果你把hash函数看成是带参数的随机变量,那么它应该尽可能的在值域中均匀、且相互独立。相关问题:问题一:来自《编程之美》这本书,号称是微软面试题。假设我给你的是员工的签到签退记录,里面每一行是时间戳和员工号。每个人应该每天有2次记录。已知今天有人只签了一次(只有一人!),我想让你查一下这个人是谁。要求,空间复杂度是O(1),时间复杂度是O(n)。问题二:原题中,假如我要统计的不是访问次数,而是in/out流量。找出流量最大的IP,该怎么办?

MySQL一个奇怪的IO问题

有一台机器,OS是CentOS release 5.8(2.6.18-308.1.1.el5.centos.plus), MySQL是5.5.27,通过RPM安装。表全是InnoDB(除了mysql的系统表)。MySQL所在的文件系统是xfs。下面这个SQL语句,执行一个多小时多没执行完:select count(*) from (select sum(count) as sum from top_no_ds_query_youku where query_date between '2012-11-01' and '2012-11-20'group by query) as temp_table;cpu负载不高,io wait在12.5%左右。更奇怪的是,有2-3条这样的SQL语句一起执行的时候,io wait会到30%左右,于是整个操作系统会变得特别慢,慢到sshd不接受新的连接。这个太要命了。但是现有的ssh连接可以clone,于是我顺利的用现有的ssh连接clone之后kill掉query。在kill之前我用vmstat看了一下,发现大约有9个进程处于uninterruptible sleep,running队列的长度只有1左右。然后呢,我找了另外一台机器做mysql slave。无RAID,SATA硬盘。内存也比master小,只有8G。OS是fedora 17,kernel是3.6.3-1,文件系统是ext4。MySQL是5.5.28。于是我就把master的my.cnf复制过去,改了一下innodb的buffer pool的大小和server id,其它都没动,然后启动,和主库进行replication。replication结束后,我在从库上测试这个SQL语句,结果只花了20秒就执行完了。期间IO非常正常,在0.4%以下。IO写入量高峰期在60MB/s左右,tps最高在600左右。这算是BUG吗?谁的BUG呢?

又遇到Pig的一个Bug

(这篇blog没啥阅读价值,纯粹工作郁闷吐槽)测试数据,是一个文本文件,每行都是一个整数。[app_admin@test14 data]$ more /tmp/data/data1.txt
1
2
3
4
5
6测试脚本如下:a = load '/tmp/data/data1.txt' USING PigStorage() as (id);
g = group a by id;
d = foreach g {
b1 = FILTER a by id > 1;
b2 = FILTER a by id > 2;
b3 = FILTER a by id > 3;
GENERATE COUNT(b1.id) as c1 ,COUNT(b2.id) as c2 ;
}然后我们看看d的schema:grunt> describe d;
2012-11-20 18:21:33,598 [main] WARN org.apache.pig.PigServer - Encountered Warning IMPLICIT_CAST_TO_INT 6 time(s).
d: {id: bytearray}完全错了。正确的结果是d: {c1: long,c2: long}。原因是因为我写了一行废代码(就是上面标红的那个)。我进行了过滤操作,但是之后没有引用b3这个alias。然后pig就因此产生一个完全错误的schema。然后我去jira上搜了一下,上周已经有人发现这个Bug了:https://issues.apache.org/jira/browse/PIG-2970pig的nested foreach 的BUG超级多,尽量能避则避吧!

Hadoop与jvm gc策略

我最近我新上了几台hadoop机器,kernel用的比较新,是3.x的。然后发现负载常常在30-100左右,程序总是在gc,实际效率很低,这让我很苦恼。其实按常规的来说,把堆加大,加到不需要频繁gc即可。可是我后来发现不是堆大小的问题。post一个gc.log:1.069: [GC 31872K->5284K(122304K), 0.0088550 secs]
6.494: [GC 31690K->4514K(122304K), 0.0099710 secs]
6.508: [GC 33335K->32774K(122304K), 0.0100570 secs]
6.593: [GC 64647K->59923K(154176K), 0.0108310 secs]
6.604: [Full GC 59923K->59653K(200640K), 0.0508330 secs]
6.987: [GC 132863K->107292K(200640K), 0.0085310 secs]
6.995: [Full GC 107292K->106832K(267392K), 0.0401220 secs]
7.183: [GC 170576K->107056K(307264K), 0.0082470 secs]
7.533: [GC 215728K->135600K(322944K), 0.0074970 secs]
12.723: [GC 253360K->173598K(385024K), 0.0124620 secs]
12.735: [Full GC 173598K->173123K(465856K), 0.0724350 secs]
18.332: [GC 352903K->239422K(465728K), 0.0249970 secs]
18.357: [Full GC 239422K->238927K(557312K), 0.1160660 secs]
18.822: [GC 418706K->267380K(621440K), 0.0175090 secs]
19.325: [GC 511412K->267452K(621312K), 0.0022710 secs]
19.780: [GC 51…

2012-10-24

本周最大的变化是我们team终于有了第三台服务器。我需要用它运行hadoop,虽然硬盘只有100GB,但是有总比没有好。这周的主要工作是做搜索的点击日志分析。在做这些日志分析工作之前,我一直以为统计程序的瓶颈是在IO上,所以就一门心思的学习各种列存储技术、对压缩算法做benchmark等等。但是实际运行的时候发现瓶颈总是在CPU上。对hadoop程序做profiling并非易事,所以我现在主要靠手动使用jstack做抽样,猜测哪些函数是热点。我最近对linkedin的glu玩的越来越转了。首先是安装脚本这块儿,写多了也就熟了,所以没啥问题了。其次,glu虽然号称有监控的功能,但是实际上glu agent server只提供了一个ScheduledExecutorService罢了,其它什么都没做,全靠你去发挥。从某种角度来看,glu也可以看做是一个分布式计算框架。它的task分为两种,只执行一次的(如install),和定期执行的(ScheduledExecutorService)。那么最简单的监控功能,就是往ScheduledExecutorService扔一个task,定期检测下load、disk usage啥的。整个工作的核心是如何搜集系统信息,可惜,java在搜集操作系统信息方面的功能太弱,应用层利用jmx倒是很方便。所以就看你的监控侧重什么了。linkedin glu里面用的groovy是1.7.x的,比较老。不仅不支持jdk 7(exception的构造函数不一样多),而且Grape也不大好使。我想用它抓mysql jdbc的jar,没成功。想手动把这个jar包加到当前这个脚本中的classpath中,也没成功。jar包放到~/.groovy/lib目录中,也没成功。最终我放弃了。改成由glu script启动一个bash脚本,这个bash脚本设置classpath并执行一个新的groovy脚本。glu的static model文件是一个json,我现在深深的觉得在生产环境中这玩意儿绝对不该手写。因为随着机器数增多这玩意儿很快就会膨胀到数千行。所以,glu本身应该只是一个基础框架。各个企业应该写自己的脚本来生成static model文件,然后调用glu console-cli 上传、部署。glu把cli工具做那么完善就是为了便于其他人二次开发。

解决mp3 tag乱码的问题

不知道你们现在都从哪里下载音乐专辑? 我是从港台的论坛上,下载BT文件,然后交给QQ旋风下载。但是,有个问题,他们那边在制作mp3的时候都是采用繁体中文,很多mp3到了我机器上,tag信息都是乱码(如歌手名),只有文件名不是乱码。出现乱码的原因倒不是简体字、繁体字,而是因为他们在mp3中采用了BIG5编码,而不是GBK或者unicode。为了消除乱码,最彻底的办法就是把所有的tag信息都换成utf-16编码。那么在任何操作系统下都可以正确的解析。这里需要用到两个软件:微软的applocale。可惜从2004年之后就没更新了,在win7/8安装过程中可能遇到困难,具体请google。mp3tag。一个免费软件,随处都可以下载到。解决办法:用applocale以繁体中文运行mp3tag这个软件,然后在mp3tag中切换至歌曲所在目录,然后Ctrl+A,Ctrl+S即可。

Howto: 在windows 7或8中彻底禁用IPv6

今天我发现我的电脑上有很多ipv6的连接,尤其是skydrive,这个吃流量的大户,竟然走的是ipv6!!! 很明显,我现在的ISP没有提供IPv6接入,所以实际上它走的是6to4 tunnel,于是网络延迟就很高了。禁用ipv6的办法:打开注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP6\Parameters下面新建一个名为DisabledComponents的DWORD值,value=0xffffffff 。打开服务管理器,services.msc。找到一个名为IP Helper的服务,禁用它。重启。附带说一句,如果你的这台电脑有公网IP的话,那么默认情况下windows会帮你启用ipv6 6to4 tunnel,于是你就可以直接访问ipv6.google.com而不担心被reset了。 所以,要不要禁用ipv6请自行考虑。

HIV的感染数

我今天刚发现我之前对CDC的传染病发病及死亡统计理解有误。 卫生部每年都会发一个统计报告,披露上一年全国法定传染病发病及死亡人数。如http://www.chinacdc.cn/tjsj/fdcrbbg/201202/t20120216_57473.htm 死亡人数中,占大头的毫无疑问是AIDS,去年是9224/15264=60.5%。 但是我今天在http://www.phsciencedata.cn/ 看统计数据的时候发现,上面那个统计报告中,AIDS的发病和死亡人数仅仅是指AIDS病人,不含HIV携带者。HIV是导致AIDS的病毒,从感染HIV到发展成AIDS,平均要经过8-9年的潜伏期。HIV携带者同样具有很高的病毒传播性,所以,从传染性疾病的预防和控制角度来说,HIV的“发病数”,要比AIDS的发病数更有意义。去年HIV的新发感染人数大约是4.8万,加上AIDS,大约就是7万左右。每年有这么多人被宣布“死刑”或“死缓”,不得不说这很可怕。另外,每年HIV的死亡人数和AIDS差不多,但是却没有被列入法定传染病发病及死亡人数表中,不得不说,这很误导人。 据卫生部的估计,存活的HIV感染者及AIDS病人大约78万,因此全人群中HIV的感染率大概是万分之六左右(假设总人口13亿)。虽然HIV的最主要传播途径是异性性行为,但是为了防止不必要的恐慌,还是需要说明一下:暗娼虽然是高危人群,但是HIV感染率在1%以下。如果已知性行为的某一方是HIV感染者,那么一次性行为导致另一方感染HIV的概率是0.1%-3.3%。女性比男性更易感染。(虽然HIV感染者中男性占了绝大多数,这个矛盾关系,自己体会吧)口交、接吻不传染。以上均指的是异性间的性行为。

用msmtp代替系统自身的sendmail

sendmail是一个漏洞奇多、配置超级麻烦的东西,所以很多系统管理员都把它禁用了。但是如此一来,如果crontab脚本执行出错,就只有天知地知了。sendmail有很多轻量级的替代,我之前一直在用ssmtp,但是这东西已经停止维护了,我在google 搜它的源代码都搜不到。于是我就只好找其它的替代,于是就找到了msmtp:http://msmtp.sourceforge.net/。安装:下载解压之后,./configure --prefix=/usr --libdir=/usr/lib64 --sysconfdir=/etcmakemake install即可。配置如果你像我这样,编译的时候加上了--sysconfdir=/etc,那么全局的配置文件就在/etc下面,名为msmtprc。这是我的配置:文件: /etc/msmtprcdefaults tls off logfile /var/log/msmtp.log account default host mail.tudou.com from xxxx@tudou.com domain tudou.com auth login user sunchangming password xxxx 很多SMTP服务器现在支持TLS加密。如果要启用TLS,那么把tls off改成tls on tls_trust_file /etc/ssl/certs/ca-bundle.crt 让mail命令使用msmtp而不是sendmail这个很简单,在/etc/mail.rc的末尾加一行set sendmail="/usr/bin/msmtp"让crontab用msmtp打开/etc/sysconfig/crond,设置一下CRONDARGS的值CRONDARGS="-m '/usr/bin/msmtp -t'"请注意这里一定要加-t。否则msmtp是从command line arg而不是stdin读取收件人列表。我之前就是因为没有加这个,而导致cron老是报告Sep 24 18:06:01 a02 crond[8023]: (app_admin) MAIL (mailed 52 bytes of output but got status 0x0040 )独家秘方哦…

一次数学式的抓虫经历

我们这有一个http service,每次应答的时候,它会生一个长度为8的随机字符串发给client。我们假设这个字符串是不会重复的,所以我们把它作为request id来唯一标识一个http request。但是后来做日志分析的时候,我们经常发现这个字符串重复了。 然后我去翻代码,发现有段代码有问题:staticchar[] alpha = ("0123456789ABCDEFGHIJKLMNOPQRSTUVWXZY" +"0123456789abcdefghijklmnopqrstuvwxzy").toCharArray(); static String getRandStr(int length){ char[] ret = newchar[length]; for (int i = 0; i != length; ++i) { int pos = ThreadLocalRandom.current().nextInt(alpha.length); ret[i] = alpha[pos]; } returnnew String(ret); } 我第一眼就发现,0123456789被写了两遍。但是问题是,这究竟是BUG的真正原因吗? 我们这个页面每天要处理200万次请求,如果是从0-9、a-z、A-Z这62个字符随机,那么有效的取值范围是\( 62^8 \),约等于200万亿,根据我之前blog中介绍的公式 \(p = 1- e ^ {-\frac{m(m-1)}{2n}} \) 那么可计算出产生冲突的概率(p)小于百分之一。 但是上面的代码写错了,导致0-9的出现概率增大了一倍,为0.28。我不知道这种情况下该怎么算冲突概率,于是我就想用信息熵的方式模拟,len = 10 *.2777777778 + 52*(1-.2777777778)= 40.3。然后用40.3代替上面的62,计算出来冲突概率是25%。然后我写代码模拟了一下,publicstaticvoidmain(String[] args){ java.util.HashSet results = new java.util.HashSet(); int badcount = 0; for (…

2012-09-12

昨天下午突然被通知,周四搬到村里去上班。说实话,我真的很沮丧。一是某公司有强制加班(不是因为工作没完成)又不付加班费的习惯,二是上班大大的远了,且不说现在的房子何时到期,总之在中关村附近想找个交通方便又住的舒服的地方,很难。最近的工作重心依然放在kafka和hadoop上。为了让调试代码方便,我自己改了改kafka的SimpleConsumer的代码,让它支持socks代理。但是SimpleConsumer是针对单个broker的单个consumer的。如何从zookeeper中获取topic的partition信息,我还不甚清楚。我之前以为如果有两个broker,那么每个topic会自动分摊到这两个broker上,现在看来似乎不是的。(update: 这是kafka的bug https://issues.apache.org/jira/browse/KAFKA-278)elephant-bird 让我耗了很久时间。我本来是写了一个程序,利用elephant-bird的ProtobufBlockWriter写入hadoop,然后拿com.twitter.elephantbird.pig.load.ProtobufPigLoader 载入,然后再在pig中dump出来。但是执行dump的时候,每次都是在准备阶段就失败了。map/reduce的task count都是0,最终的输出也是空。而且,我看不到任何错误日志。愤怒!最后我看了看代码,ProtobufPigLoader是从LzoBaseLoadFunc继承的,于是我感觉似乎是要求这个file必须用lzo压缩。好吧,开始搞lzo的东西,可惜了,hadoop-gpl-compression 在windows下似乎是编译不过去的。liblzo2可以。由于压缩效果还是挺明显的,所以我觉得不压缩是不道德的,于是就乖乖的去折腾hadoop-gpl-compression 了。这东西在CentOS的官方的yum源里面就有,如果自己编译的话,hadoop-gpl-compression 要求liblzo2非得编译成动态库,不能是静态库。另外这个库还必须放在LIB_LIBRARY_PATH中,光用java.library.path指定还不行。于是我乖乖的去求运维人员了,我没root权限。随着把lzo搞定,在pig中dump终于成功…

2012-09-07

半个月前,我准备接手一件事情:现在某项目有一套类似于google analytics的东西,前端js就不说了,它会发起一个http请求给nginx,然后统计人员用python脚本去分析nginx的access log。整个系统用一台机器就搞定了。然后我打算把它做的更好一点,用自己写的一个http server代替nginx,然后它会主动吐日志给一个message queue,这个message queue是分布式的,另一端接hadoop,用map-reduce并行的从queue载入数据。现在这个http server刚刚写完部署上去了,各种问题。首先,我的程序没有root权限,所以在8080端口监听的。我让OP把80端口转过来,于是他就给装了一个nginx。我说iptables不行吗?他说这样不好。 我估计是因为这个nginx有什么监控和过滤功能。结果就是,我从socket拿不到client ip了。不仅如此,http header中也没找到x-forward-for之类的字段。各种扯皮啊……来回查。折腾了1天多了,下周继续去看。有些事情看着明明是一个非常简单的小事,但是实际做的时候,工期远远超过预期。唉…… 今天刚实现了对比“If-Modified-Since”和文件的最后修改时间而在适当的条件下返回304的功能。我现在只能处理GET请求,而且只能返回304或者200、404。剩下主要就是在hadoop中怎么存、怎么写统计脚本的问题。我准备学下pig,恰好发现twitter做了一些关于protocol buffers on hadoop的工作https://github.com/kevinweil/elephant-bird 我觉得对我会很有用。linkedin kafka的官方文档太少了,我自己写了一些,准备下个月做成pdf放出来。

通过代理访问hadoop

我有一套hadoop集群,部署在外网的服务器上。很遗憾的是,这些机器不能从办公区的网络直接访问。必须先ssh到一个跳板机,然后再ssh过去。之前的我做法是,部署两套,内网一套,公网一套。但是这样对我来说,维护成本无疑很大。因为这些hadoop就我一个人用啊!好在hadoop支持socks。只需要在core-site.xml中加上<property><name>hadoop.socks.server</name><value>127.0.0.1:5800</value></property><property><name>hadoop.rpc.socket.factory.class.default</name><value>org.apache.hadoop.net.SocksSocketFactory</value></property>就可以了。这是因为hadoop用的是java.io的Socket接口,而不是java.nio。而java.io是利用socket factory来创建socket,允许别人通过创建新的factory来支持socks代理、http代理等等。并且JDK内置了socks代理的支持。我觉得这种做法很值得参考。剩下的事情就交给ssh -D好了.

令人郁闷的maven

今天我用maven编译一个程序的时候,死活过不去。老说jms的jar包下载不下来。[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.407s
[INFO] Finished at: Wed Sep 05 17:38:58 CST 2012
[INFO] Final Memory: 3M/15M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project logconsumer: Could not resolve depende
ncies for project com.sunchangming:logconsumer:jar:0.0.1-SNAPSHOT: The following
artifacts could not be resolved: javax.jms:jms:jar:1.1, com.sun.jdmk:jmxtools:j
ar:1.2.1, com.sun.jmx:jmxri:jar:1.2.1: Could not transfer artifact javax.jms:jms
:jar:1.1 from/to java.net (https://maven-repository.dev.java.net/nonav/repositor
y): No connector available to access repository java.net (https://maven-reposito
ry.dev.java.net/nonav/repository) of type legacy using the available factories W
agonRepositoryConnectorFactory -> [Help…

2012-08-27

首先抱怨一下,google code越来越不好用了,被某防火墙封锁的厉害,老连不上。我想把东西往github上迁移,但是,git我还一直用不习惯,比如没有像TortoiseSVN这么方便的图形化的diff工具。虽然有个TortoiseGit项目,但是貌似不怎么stable。最近我被调去做搜索了,所以之前做的关于flash p2p的事情就暂且放一放。一下子又进入到了一个新领域,有很多可以学习的。我比较后悔为什么没有一毕业的时候就来做搜索,因为它的技术门槛很高,并且充满了挑战,无论是算法还是OS层面的优化。我刚才在想,假如,我重新回到完美去做网游。除了之前我想的那些关于基础框架(如IO库、xdb)的改善外,还有很多新的产品可以做。比如分布式日志搜集以及实时分析,比如对角色信息做实时索引。打开一个页面,就像在淘宝搜商品一样,搜“北京的”、“18-23岁”、“性别女”、“水瓶座”、“单身”、“10级以上”等等,然后找到合适的就去搭讪。对于运营人员来说,这个也可以作为很实用的运维工具,比如搜索不正常的玩家。然后上周末看了看如何使用javascript做访问统计。主要是看google analytics。可惜它的代码是处理过的,函数名什么的都已经不见了。好在ga.js代码不长。昨天顺便在我们自己的某页面上发现了一个XSS漏洞。哎,对用户的每个输入都得小心啊。土豆已正式退市。部门合并正在如火如荼的进行着。我不知道出于何种理由让Gary选择了youku而不是其它一家业务重合度不那么大的公司。现如今土豆已经退市,我也很想问老古,youku从这次合并中究竟得到了什么?总之在我看来,现在是对两个公司的中层管理人员最大挑战的时候,是否能把合适的人放在合适的位置上让他们依然去做自己擅长或者喜欢的东西?

准备山寨一个Google Analytics

不用Google Analytics的原因很简单:我需要把数据存在自己的服务器上,然后按我的需求做统计。所以我需要重新山寨一套Google Analytics,大体上来讲,基本流程是:页面加track codetrack code搜集完信息后,向后台的http服务器发请求通过http服务器的access log来分析关于页面的track code如何工作,在GA的网站上有一些介绍,其中关于如何搜集浏览器事件我还不大清楚,得去看看GA的代码,反正都是open source的,多花点时间,总能搞清楚。http server这里我准备这样:给nginx写一个module,首先在内存中维护一个local log buffer,大约200条。然后nginx连kafka。kafka可以视为是一个Java Message Service,但是它的生产者是push模型,而消费者是pull模型。当local log buffer满了就往kafka提交一次。kafka此时可以视为是一个临时文件存储,它存储最近7天的日志。kafka的另外一端是hadoop(虽然我的大领导很不喜欢它),把kafka作为一种特殊的数据源,为它实现一个input format,然后运行map reduce将日志从kafka中拽到HDFS中。下一步就是在HDFS中对日志做分析了。我现在比较犹豫的是到底是给nginx写module,还是自己写一个http server处理请求。因为我对nginx不熟悉,贸然下手给它写代码也许会死的很难看。反正我的请求量也不高,每秒也就几千,用libevent 单线程就能搞定,实在不行多开几个进程/机器。用nginx的好处是我的那些静态文件(如ga.js)能够一并放给nginx处理。你说呢?

Windows下用stat函数得到的文件size可能不准

下面这段测试代码用于在TEMP目录下创建一个文件,然后随便写入一点数据char* tempdir=getenv("TEMP"); std::ostringstream oss; oss<<tempdir<<"\\1.txt"; std::string filename=oss.str(); FILE* f=fopen(filename.c_str(),"wb"); char buf[]="abcdfdfdf"; fwrite(buf,1,sizeof(buf),f); fflush(f); Sleep(10000000); fclose(f); 如果你有VC,可以编译试一下。运行这段代码,然后开一个命令行窗口,用dir命令获取这个文件的大小。D:\Users\cm>dir %TEMP%\1.txt在我机器上(Windows 7 64bit),每次的测试结果都是发现这个文件的大小是0。如果你自己写一个程序,用_stat(或FindFirstFileEx)函数去拿文件大小,发现也是0。但是,如果,此时对这个文件做一点读操作,比如用more命令查看下这个文件,然后再获取文件大小,那么就一切正常了。发现这个问题是因为,我在用leveldb的时候,发现虽然我通过fwrite函数写了log文件,然后flush了,但是接着用_stat函数去拿文件大小的时候,文件大小是0。而碰巧,跑TESTS的目录恰好在%TEMP%目录下,否则也不会这么糟了。解决办法:用GetFileSizeEx获取文件大小,而不是_stat。

程序中如何启用openssl的AES-NI ?

昨天我贴了一个测试报告,openssl在启用了AES-NI后,做AES加解密的时候,效率有8-9倍的提升。但是当我自己写了一个程序去测试的时候,结果却并非如此。 首先,openssl有两套接口,一套EVP的(与具体算法无关),一套较低level的,针对特定算法的。 EVP的例子如下:EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init(&ctx); ENGINE_load_builtin_engines(); ENGINE* engine=ENGINE_by_id("aesni"); if(engine==NULL){ printf("aesni not found\n"); } EVP_EncryptInit_ex(&ctx,EVP_aes_128_cbc(),engine,test_key_128,test_init_vector); int out; EVP_CipherUpdate(&ctx,output,&out, test_plain_text, inputlen); 需要特别说明的是:在openssl 1.0.0中,新添了一个叫做aesni的engine,只有当你指定了使用这个engine的时候,才会使用CPU的AES指令。但是到了openssl 1.0.1之后,这个engine被去除掉了,变成运行期自动监测是否使用AESNI。1.0.1版本中EVP_aes_128_cbc()这个函数的实现如下:
extern unsigned int OPENSSL_ia32cap_P[2];
#define AESNI_CAPABLE (OPENSSL_ia32cap_P[1]&(1\<\<(57-32))) const EVP_CIPHER *EVP_aes_128_cbc(void) { return AESNI_CAPABLE?&aesni_128_cbc:&aes_128_cbc; }
除了EVP,还有一套低级接口如下:intAES_set_encrypt_key(constunsignedchar *userKey, constint bits, AES_KEY…

Intel AES NI性能测试

老大派我去找找看有没有什么便宜好用的AES硬件加速卡,于是我就想先试一试Intel CPU自身的硬件加速功能。 我的测试环境是CentOS release 5.7 (Final),所用的CPU是Intel(R) Xeon(R) CPU X5675 @ 3.07GHz。不得不说,这么好的一个CPU配这么老的一个操作系统真是可惜了。 首先,拿系统自带的openssl作测试, $ openssl speed -evp aes-128-cbc
Doing aes-128-cbc for 3s on 16 size blocks: 16189439 aes-128-cbc's in 3.00s
Doing aes-128-cbc for 3s on 64 size blocks: 4972096 aes-128-cbc's in 3.00s
Doing aes-128-cbc for 3s on 256 size blocks: 1280673 aes-128-cbc's in 3.00s
Doing aes-128-cbc for 3s on 1024 size blocks: 321781 aes-128-cbc's in 3.00s
Doing aes-128-cbc for 3s on 8192 size blocks: 40365 aes-128-cbc's in 3.00s
OpenSSL 0.9.8e-fips-rhel5 01 Jul 2008
built on: Sat Aug 13 18:21:50 EDT 2011
options:bn(64,64) md2(int) rc4(ptr,int) des(idx,cisc,16,int) aes(partial) blowfish(ptr2)
compiler: gcc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DKRB5_MIT -I/usr/kerberos/include -DL_ENDIAN -DTERMIO -Wall -DMD32_REG_T=int -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOUR…

我对开源的看法

有这么一种观点:程序员要想提高技术水平,多读开源代码、多参与社区讨论与开发就好了。我以前很赞同,现在发现不是这样。panpan和xuhui对我的批评是,多看看技术以外的东西。我的理解是这样,很多东西它之所以这么做,不是因为技术上是最优的,而是因为工期、领导的旨意、需求变更留下的历史问题等等。这些原因是无法通过阅读代码而得知的,我只能看到实现细节,却不能知道作者的设计意图。像Qt、leveldb这样的项目,名为开源,但是它并没有采用开放式的开发。只是这个公司将其技术成果共享出来了而已,中间的过程完全不可得知。举个例子,Nokia和Intel说要合伙做一个叫Meego的操作系统。Intel说好啊,我对Linux熟,我来优化底层系统,Nokia你来搞QT及上层软件。结果Intel很郁闷的发现,Nokia的Meego项目组有两套BUG管理系统,一套是对内的,只有Nokia自己的员工可以访问(intel的不行),一套是对外的,放在社区里让任何人都可以去提BUG。然后Intel就怒了,你这让我们怎么合作?Nokia也很委屈,因为对任何一个手机厂商来说,未上市的手机的硬件细节是很重要的商业机密,这些东西很容易通过BUG管理系统中的日志文件和coredump流露出来。所以,大多数软件厂商最终选择的都是Nokia这样的做法。很多时候要想清楚自己的目的,是让项目尽快完工,还是追求纯技术?如果是后者,不如老老实实在学校或者研究所呆着。做项目最重要的不是解决一个多么大的技术问题,而是尽可能的把所有技术困难都绕开,然后再解决那些不得不解决的。这时便会引申出一个重要原则:Simple is the best。读代码跟读小说不一样。写小说的人心里一定是有读者在的。所以每拿起一本古典小说的时候,就仿佛穿越了时空在跟一个已经死去的人对话。而代码不一样,代码主要还是给机器读的,以及给自己读。举个例子,如果你对C++的模板推导很擅长,你会因为这门技术太晦涩,有人有可能看不懂,而不用吗?所以如果想通过代码猜透作者的设计企图,不管能不能做到,总之为其花费的代价实在是太高。那么有别的方法吗? 有啊! 跳到这个公司去,中午跟作者一起吃个饭,随便套两句,就出来了。对我自己来说,很有幸能在一流的互联网公司工作。如果我想去百度、腾迅、阿里中的任何一家,应该都是有机会的。所以,总有比我资深的老员工,如果有幸遇到一个比…

线性同余发生器的参数如何选取?

我们平时所用的伪随机数生成器(PRNGs)主要有两种:线性同余发生器(Linear Congruence Generator)和反馈位移寄存器法(Feedback Shift Register)。LCG线性同余发生器是通过这样的递推函数产生随机序列:x=(a*x+c)%M (x,a,c,M都是非负整数)这样产生的随机数序列,一定是有周期的,且小于等于M。在实际应用中,当然希望周期越大越好。我在我的笔记本电脑上测试,生成20亿个随机数只需要20秒左右。如果遇上需要大量随机数的程序,很快就会将这个生成器的"随机性"耗尽。如果以下三个条件都满足,则可以让该随机数序列的周期等于Mc和M互质。 对于M的任何一个质因子P,a-1能被P整除。 如果4是M的因子,则a除以4余1。(这个定理的证明非常复杂,请参考数论的书)在计算机系统中,M通常选择是2的整数倍,如\( 2^{48} \),\( 2^{32} \)。M通常由计算机的字长所限,于是在M给定的情况下,如何选择a和c,则很关键。可以证明,如果M是2的整数次幂,如果a和c满足以下条件,则该序列的周期等于Ma除以4余1。 c除以2余1。(这个证明过程非常显然,请自推一下)下面假设通过x=(a*x+c)%M得到的数列为\( \{X_n\} \),那么我们希望它满足[0,M-1]之间的等可能分布。那么它的概率密度函数为:$$ \begin{equation} \rm P(X_n = i ) = \left\{ \begin{array}{ll} \frac{1}{M}, & i=0,1,\dots,M-1 \\ 0, & other \end{array} \right. \end{equation} $$于是它的数学期望应该为:$$ E(X_n) = \sum_{i=0}^{M-1}iP\{X_n=i\} = \frac{M-1}{2} $$方差应该为:$$ Var(X_n) = E(X_n^2)-(E(X_n))^2 = \frac{1}{M}\sum _{i=0}^{M-1} \left( i- \frac{M-1}{2} \right) ^{2} = \frac{M^2}{12}-\frac{1}{12}$$下面计算\( X_n \)的1阶自相关系数,$$ \rho = \frac…

NAT连通性测试工具以及Flash P2P中的NAT穿透原理

图片
由于公网IP有限,NAT几乎是无处不在。比如我们在家里,牵一个ADSL,用Modem拨号得到一个公网IP,然后在Modem后面再接一个路由使得多个设备能同时上网。路由会有一个公网IP一个私网IP,然后家里的其它设备都用的是私网IP。此时路有器就要完成一个很重要的职责:对于进出它的包做网络地址转换。NAT是在传输层及以上做的,传输层最主要的2个协议是TCP和UDP,下面只考虑UDP。对于UDP而言,每个包都有很基本的4个要素:src ip、src port、dst ip、dst port。根据在做NAT的时候是否保留src ip和src port,可以把NAT分为这么三种:Cone: 将src ip映射到一个固定的IP,并且将src port映射到一个固定的Port,无论dst ip和dst port是什么。假如我从192.168.0.2:5000,通过路由器发一个UDP包给66.66.88.88:4000端口,而路由器把这个包的src ip和src port翻译成了173.245.73.182:5000。那么在此后一段时间内,无论我从192.168.0.2:5000往外面的任意IP、任意端口发包,src ip、src port都会被翻译成173.245.73.182:5000。Single IP address, symmetric:将src ip映射到一个固定的IP,将src port映射到一个随机的port,但是保证对于相同的(dst ip,dst port), src port始终相同。(否则双方没法通话啊,回来的包回给哪个端口呢?)举例:假如我从192.168.0.2:5000,通过路由器发一个UDP包给66.66.88.88:4000端口,而路由器把这个包的src ip和src port翻译成了173.245.73.182:5000。那么在此后一段时间内,无论我从192.168.0.2:5000往外面的任意IP、任意端口发包,src ip都会被翻译成173.245.73.182,但是src port嘛,可就说不准了。Multiple IP address, symmetric:与上面类似,但是src ip可能会被映射到多个IP中的一个。最典型的就是假如你的网关做了双线接入,那么你访问电信的资源就会走电信的那个IP出去,你访问网通的资源就会走网通的IP出去…

用FLIRT和tamarin的代码自动识别flash里面的函数

图片
首先,一定要装vs 2008。我本来装的是vs 2010,效果非常差。然后装python 2.7,一定要把python的目录加入到PATH中。假设tamarin的源代码目录是D:ostamarin-redux-fb079918582ccd utils
wget ftp://ftp.mozilla.org/pub/js/tamarin/builds/asc/latest/asc.jar
cd ..core
set ASC=../utils/asc.jar
python builtin.py -config CONFIG::VMCFG_FLOAT=false
cd ..shell
python shell_toplevel.py打开D:\ostamarin-redux-fb079918582cplatformwin32avmplus2008.sln,编译。给coreErrorConstants.cpp加BOM。把avmplus项目的Treat Warnings as Errors去掉,然后编译然后用flair做符号> D:\flairpcf avmplus.lib avmplus.pat
avmplus.lib: skipped 1731, total 14180> D:\flairsigmake avmplus.pat avmplus.sig
avmplus.sig: modules/leaves: 8012/4207, COLLISIONS: 407
See the documentation to learn how to resolve collisions.删掉冲突,再做,然后apply上。效果还行update 2014-03-05:

MMgc: The Garbage Collector of Flash Player

MMgc是Flash Player中所使用的memory management garbage collector。MMgc不仅是用来回收ActionScript中的对象,它也用来管理Flash Player中的所有动态内存分配、释放请求,Flash Player VM的C++代码中也有很多对象是交给MMgc自动回收的。所以,从理论上来说,MMgc可以从Flash Player的代码中分离出来,作为一个单独的GC库,给所有C++程序用。在MMgc中,堆上的对象被分为两种,Managed和UnManaged。Managed的就是交给GC去管理,用户只管new,不用delete,但是你也可以手动delete。UnManaged的就是传统的C++中的那些对象,必须手动的new/delete。所有Managed对象必须从class GCObject(或其子类)继承。这个类增加了new和delete方法:class GCObject{ staticvoid *operatornew(size_t size, GC *gc, size_t extra); staticvoid *operatornew(size_t size, GC *gc); staticvoidoperatordelete(void *gcObject); }; 假如一个类从GCObject继承,例如class MyPoint2D : public MMgc::GCObject { int x; int y; }; 那么我们既可以像以前那样直接new:MyPoint2D* p = new MyPoint2D(); //这样得到的对象是不受GC管理的。也可以传递一个gc对象给new:MyPoint2D* p = new (gc) MyPoint2D(); //这样得到的对象受GC管理。不需要delete后面这种的语法看起来很像带placeholder的new,但其实不是。从Managed对象指向Managed对象,要加一个write barrier ,叫DWB。class MyOtherManagedObject : public MMgc::GCObject { DWB(MyPoint2D*) object; }; 对于那些不受GC管理…

A small hack on Adobe FMS

我发现Adobe FMS的程序中包含很多日志输出代码,这些日志信息对于理解RTMFP协议非常有用。例如它会把收发到的每个user data chunk中的AMF对象打印出来,如2012-06-12 17:32:07 8328 (d)0000000 rtmfp send message, session: 08F2F008 flow: 08F22EA0 rel: (-2,-2) kMsgCmd idByte=20 streamId=0 time=0 trxId=1 kEncodingAMF0 _result cmdData=( kObjectType ( fmsVer= kStringType "FMS/4,5,0,297", capabilities= kNumberType 255, mode= kNumberType 1 ) ) arg0=( kObjectType ( level= kStringType "status", code= kStringType "NetConnection.Connect.Success", description= kStringType "Connection succeeded.", objectEncoding= kNumberType 3, data= kArrayType ( version= kStringType "4,5,0,297" ) ) ) -2012-06-12 17:32:07 8328 (i)2581173 rtmfp recv message, session: 08F2F008 flow: 05624900 kMsgCmdEx idByte=17 streamId=0 time=724 trxId=0 kEncodingAMF0 setPeerInfo cmdData=( kNullType ) arg0=( kStringType "192.168.15.1:53804" ) arg1=( kStringType "192.168.146.1:53804" ) arg2=( kStrin…

CVE-2012-2122: MySQL身份认证漏洞

我今天早上来上班,打开电脑,在seclists中看到一个很惊人的邮件: http://seclists.org/oss-sec/2012/q2/493 MySQL爆出了一个很大的安全漏洞,几乎影响5.1至5.5的所有版本。出问题的模块是登录时密码校验的部分(password.c),在知道用户名的情况下(如root),直接反复重试(平均大约256次)即可登入。不过,MySQL身份认证的时候是采用3元组,username,ip,password。如果client的IP在mysql.user表中找不到对应的,也无法登陆。 这个BUG实际上早在4月份就被发现了,今年5月7号,MySQL发布5.5.24的时候,修正了这个BUG。 漏洞分析: 出问题的代码如下my_bool check_scramble(const uchar *scramble_arg, constchar *message, const uint8 *hash_stage2){ SHA1_CONTEXT sha1_context; uint8 buf[SHA1_HASH_SIZE]; uint8 hash_stage2_reassured[SHA1_HASH_SIZE]; mysql_sha1_reset(&sha1_context); /* create key to encrypt scramble */ mysql_sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH); mysql_sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE); mysql_sha1_result(&sha1_context, buf); /* encrypt scramble */ my_crypt((char *) buf, buf, scramble_arg, SCRAMBLE_LENGTH); /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */ mysql_sha1_reset(&s…

CHAP、HMAC、HOTP、TOTP等等

去年CSDN密码泄露案公布之后,很多专家跳出来讨伐说他家的程序员太白痴居然还在数据库里面存明文。最近linkedin的事件出来之后,如何保存密码,这个问题又被拉出来谈。我认为,密码怎么保存与怎么在网上传输,这两个问题不能分开来谈。除非你已经有了安全的信道,如SSL,否则还是存明文为妙。mysql对其原始的CHAP协议做了些改进:
storedhash=sha1(passphrase)
reply=xor(passphrase, sha1(public_seed,storedhash)在网络上发送的是public_seed、reply,数据库里面存储的是storedhash。所以如果只是拿到了mysql.user表里的数据,而没有原始的passphrase,想要构造出reply,还是挺困难的。不过我估计这个算法是工程师自己想出来的,根本就没找安全专家讨论过,未必经得起推敲。今天我在我的手机上装了google authenticator,它主要是采用TOTP的方式进行身份验证。TOTP是HOTP的一个变种,把HOTP中的counter换成了时间,TOTP = Truncate(HMAC-SHA-1(K,T)) 。其中K是双方之间已经共享的一个密钥,而T则是当前时间除以步长(默认是30)得到的。用手机做这个的一个优势是,K的分发可以不走网络,而是通过二维码。我准备抽空好好看看SRP。

小说《玩笑》的主要人物列表

《玩笑》是米兰昆德拉发表的第一部长篇小说,我刚看完,写点读书笔记吧。小说的第一章大概发生在1964、1965年左右。该小说于1965年12月5日在布拉格完稿,在1967年春天在布拉格出版。路德维克:1947年秋天开始上大一。大二的时候遇到玛凯塔,喜欢她。1949年夏天寄给玛凯塔的一张明信片上写到:“乐观主义是人民的鸦片!健康精神是冒傻气!托洛茨基万岁!”。在他看来本是一句玩笑,但玛凯塔很认真的把这个卡片交给了组织。1949年秋天,路德维克因此被开除党籍、学籍。1950年开始,作为黑五类被派去俄斯特拉发的矿井中工作了五年。玛凯塔:路德维克大学时的女友、学妹,比路德维克小1岁,小1级。考茨卡:路德维克的大学里的一个年轻教师,虔诚的基督徒。大约1950年被迫离校,然后去西波希米亚地区的一个国营农场工作,1951年11月,在那里认识露茜。大约1952年,离开农场。1956年秋天,在布达格到布拉迪斯拉发的火车上再次遇到路德维克。路德维克给他找了份工作,去路德维克老家的一家医院的病毒科工作,给动物做实验。露茜*赛贝考娃:路德维克在俄斯特拉发的电影院门口遇见的一个姑娘,他很喜欢她。但是露茜因不愿跟路德维克ML,跑掉了。巴维尔*泽马内克:路德维克上学时的学生会干部,他牵头发起了对路德维克的批判,并提议将他开除。埃莱娜:电台记者,泽马内克的妻子。采访路德维克的时候被他勾引,随后被骗去路德维克老家的小旅馆开房。雅洛斯拉夫:大约是1930年出生。路德维克的中学同学,和路德维克曾一起组了个扬琴乐团。1948年,去布尔诺高等音乐学院上大学。大约2年后,辍学回家。芙拉丝塔:雅洛斯拉夫的妻子。符拉第米尔:雅洛斯拉夫的儿子。大约是1950年出生。阿莱克塞:和路德维克一起在俄斯特拉发工作的“黑五类”。在军营中吞药自杀。兹德娜:埃莱娜的女儿。历史背景:尤利乌斯·伏契克:1903-1943,捷克记者、作家。1942年被捕,后来在狱中写下了《绞刑架下的报告》。托洛茨基:苏联共产党领袖,斯大林当权时代,托洛茨基被视为反斯大林主义、革命叛徒。1945年8月,二战结束。1948年2月,捷共上台。1953年3月,斯大林去世。之后赫鲁晓夫上台。1956年,东欧骚乱。小说中多次提到,“即使是在1956年,人人都想把党抛弃的时候”1964年10月,赫鲁晓夫下台,勃列日涅夫上台。1968年1月,杜布切克任捷克斯洛伐克第…

2012-06-06

今天下载了Adobe Flash Media Server 的试用版,用PEID探测,没有壳。用IDA载入,相当的顺利。令我兴奋的是,它有很多代码与Adobe Flash Player是相同的,但是不同的是,Adobe Flash Media Server 编译的时候加RTTI了,于是看到类名的一刹那,瞬间揭开了我很多困惑。class type_info中存储的字符串是decorate之后的名字,如.PAVblah@@,如何把这样的名字转换成原来的形式,貌似并不简单。vc自带一个undname命令,我试了,不好使。Windows SDK中有UnDecorateSymbolName这个API,我准备明天试试,先SymInitialize,然后UnDecorateSymbolName。有一个难点是,如何找到type_list的root。关于这个转换规则,在http://www.agner.org/optimize/calling_conventions.pdf 到是有很详细的描述。

Diffie-Hellman算法的效率

我之前一直有个误解,以为DH的私钥的长度必须跟公钥一样。今天调试flash的时候发现,它的DH算法用的私钥长度竟然是128位,虽然它采用的同余群的p是1024位的。我然后翻了些资料,看DH算法对私钥的长度有何要求。从Sun JDK的代码来看,对私钥x只有一个要求,1 <= x <= p-2,那么也就是说只要它的长度小于1024就没有问题。BigInteger pMinus2 = p.subtract(BigInteger.valueOf(2)); do { // generate random x up to 2^lSize bits long x = new BigInteger(lSize, random); } while ((x.compareTo(BigInteger.ONE) < 0) ||((x.compareTo(pMinus2) > 0))); 其中无论是生KeyPair,还是计算secret,其中最耗时的无非是大整数的次幂再求余的运算,即BigInteger的modPow。 我想拿google的caliper测试一下DH的性能,结果,当private key的长度稍微大一点的时候,caliper就不干了,说Got no response。1 of 2 measurements complete: 50.0%.java.lang.RuntimeException: Got no response!
at com.google.caliper.runner.CaliperRun.measure(CaliperRun.java:234)
at com.google.caliper.runner.CaliperRun.run(CaliperRun.java:132)
at com.google.caliper.runner.CaliperMain.exitlessMain(CaliperMain.java:88)
at com.google.caliper.runner.CaliperMain.main(CaliperMain.java:58)
at com.google.caliper.runner.CaliperMain.main(CaliperMain.java:47)可能是因为运行的时间太长了,于是我就自己写代码…

Visual C++中的几种函数调用方式

(本文中所有汇编代码均采用Intel语法,即dest在左边)C++中的函数被编译成汇编代码的时候,必须遵循一定的规范,如参数怎么传递,栈指针怎么增减。Visual C++中,一共有5种情况:__cdecl__stdcall__fastcall__thiscall默认情况下,是__cdecl。__cdecl 和__stdcall的区别是:__cdecl是调用者清理栈,而__stdcall是被调用者清理栈。所以,理论来说,__cdecl生成的代码体积会更大。但是,对于varargs函数,由于被调用者并不知道参数的具体长度,所以这样的函数只能采用__cdecl。所有这四种方式,生成的函数都有固定的边界特征,__cdecl 和__stdcall以这样的模式开始:pushebp;保存ebpmovebp,esp;设置栈指针subesp,0C0h;为局部变量保留栈空间pushebx;保存在这个函数中可能用到的寄存器pushesi;保存在这个函数中可能用到的寄存器pushedi;保存在这个函数中可能用到的寄存器以这样的模式结束:popedi;恢复寄存器原先的值popesi;恢复寄存器原先的值popebx;恢复寄存器原先的值addesp,0C0h;与前面的sub esp,0C0h对应movesp,ebp;恢复栈指针popebp;恢复ebpret其中 “mov esp,ebp; pop ebp;”也可替换成一句“leave”指令。如果是__stdcall,最后一行的ret会变成ret0Ch这样。最后的那个数字代表从栈上弹出多少个字节,它应当等于函数参数的总大小。类的成员函数默认是采用__thiscall。它与__stdcall非常相似,区别是this指针会通过ecx传递。所以,如果在一个函数中发现ecx未被赋值就开始读它,那么多半是__thiscall。如pushebp;保存ebpmovebp, esp;设置栈指针pushecx;保存在这个函数中可能用到的寄存器pushebx;保存在这个函数中可能用到的寄存器pushesi;保存在这个函数中可能用到的寄存器movesi, ecx; 注意!!!ecx尚未被赋值就开始读了movebx, [esi+344h] ; this指针一般存放在esi中,并且在整个函数体内,esi尽量保持不变。另外再次强调,__thiscall末尾的ret语句要跟一…

Windows下调试ActiveX控件

VC较老的版本自带一个叫做ActiveX control test container的东西,从vs 2008开始,这个被移除了,改为以源代码的方式放在Samples里面发布。在VS的安装目录下有一个Samples目录,里面有一个VC2010Samples.zip,打开之后把C++/MFC/ole/TstCon解压缩出来并且编译即可。这个东西可以支持VBA脚本,于是就可以自动化测试ActiveX控件,比如Sub RunTest()
set ocx=TCForm.InsertControl("ShockwaveFlash.ShockwaveFlash.11","flash")
ocx.LoadMovie 0,"D:\Users\cm\doc\p2p\p2p.swf"
ocx.play
End Sub然后用tstcon.exe /D xxx.dsm 执行。但是我之前的用link.exe修改pe header的方法,似乎对ocx不好使了,所以用IDA调试ocx还是有些困难。另外,IDA似乎不支持加条件断点啊?

Howto:从C++对象指针得到类名

JAVA程序在运行的时候,有丰富的动态类型信息。而C++则困难的多。C++的运行时类型信息有3种实现方式:语言本身的RTTI、MFC的CObject、QT的moc。下面仅介绍前两种。 一、语言本身的RTTI: 如果是POD类型,如:class PodPoint{ public: int x; int y; }; 那么它和C语言中的struct没有什么区别。sizeof(PodPoint)=8。偏移值内容0x4y 别妄想能从它的对象指针中得到什么类型信息。 如果它有虚函数,那么就不一样了class PodPoint{ public: int x; int y; virtual ~PodPoint(){}; }; 在32位程序中,sizeof(PodPoint)=12。这是因为凡是有虚函数,就必须有vtable。所以PodPoint的实际布局就变成了这样:偏移值内容0指向vtable的指针4x8y如果编译的时候打开了RTTI(在vc2005及以上版本默认会打开),那么就很有意思了。在vtable[-1]的位置,是一个特殊的指针,指向RTTI Complete Object Locator,它的第12个字节开始,是一个指针,指向type_info对象。于是我就写了下面这样的代码:voidprintMyClassInfo(void *p){ type_info*** vtable=(type_info***)(*(int*)p); type_info** v1=vtable[-1]; type_info* v=v1[3]; printf("%s\n",v->name()); } 在有vtable的情况下,这个函数工作的非常好。 二、MFC的RTTI MFC中的大多数对象都从CObject继承而来,例如:class MyPoint:public CObject{ public: int x; int y; DECLARE_DYNAMIC(MyPoint) }; IMPLEMENT_DYNAMIC(MyPoint,CObject) 那么我们就可以通过调用CObject的virtual CRuntimeClass* GetRuntimeClass() …

Address Space Load Randomization以及如何禁用

图片
从Vista开始,Windows引入了一个新特性,叫Address Space Load Randomization(ASLR)。简单点说,就是将EXE/DLL的载入基址随机化。ASLR已经是一个非常普遍的特性,在几乎所有的现在操作系统中都引入了(如Linux、Windows Vista/7、iOS5)。但是ASLR对程序员来说可不是个好东西,明显增大了调试程序的难度。最近我在对Adobe Flash Player做逆向工程,发现我用ida启动调试的时候,经常会出现一个对话框,说"Rebasing program to xxxxx“。别听它说什么”Just a moment”,实际上要等很久很久才好。因为基址改变后,所有的地址都得重新计算,整个程序需要被重新分析一遍。用SysinternalsSuite中的Process Explorer(procxp.exe)可以查看一个进程是否启用了ASLR:通过修改PE文件的头部,可以禁用掉ASLR。强烈建议在对任何EXE/DLL做逆向工程前,先禁用掉它的ASLR选项,然后再往IDA/OllyDbg里面拖。下面用visual studio中自带的工具来查看并修改PE头:首先打开"Visual Studio 命令提示”,然后输入D:> dumpbin /headers flashplayer_11_sa_debug_32bit.exe就可以查看到这个PE文件的头部信息。输出很长,在OPTIONAL HEADER VALUES中有一项是”DLL characteristics“,如8140 DLL characteristics
Dynamic base
NX compatible
Terminal Server Aware如果它的值是8140,就说明打开了"Dynamic base”。用VC的链接器禁用掉它即可。D:> link /edit /dynamicbase:NO flashplayer_11_sa_debug_32bit.exe再 试一试吧!

联通iphone套餐对比

联通iphone套餐实在是让人眼花缭乱,到底选哪个好呢?本文的目的是为了给你拨开数字背后的秘密。如果我想买个iphone 4 8G,准备使用联通的网。那么现在有3种选择联通预存话费送手机联通购手机入网送话费在苹果的官网购机入网。我的思路是,首先分析我想要得到什么样的服务,然后计算我需要为之花多少钱。为了分析简单,下面假设我每月使用96套餐,并且打算使用2年左右,那么下面分析上述3个方案的总计支出方案1:联通预存话费送手机,合约价4999。选择96套餐,每月联通返91,所以我需要再付5块。总计支出4999+5*24=5119。方案2:裸机价格3999,每个月返38的话费,我需要再付(96-38)*24=1392元,总计支出3999+1392=5391。方案3:裸机价格3988。联通现在有1年期的入网送话费活动,一次存168,每月返28+14,带4的再加10块。总计支出:3988+(96-28-14-10)*24+168*2=5380,这里特别强调"带4的每月返10块"是因为,联通官网上剩下的号码都是些不怎么好的号码,一般都带4。有了上面的计算之后,下面介绍下我是怎么分析"预存话费送手机"这个表,还是以2年期为例:首先,购手机入网送话费不是针对Iphone特有的,这张表几乎对所有手机适用,而且联通和电信的这张表几乎完全一致。这意味着,如果联通如果要推出一个2年期的自购手机入网送话费活动,大致也是应该按照这张表返还。所以,我们要从"预存话费送手机"这张表中去除掉"购手机入网送话费"的影响。具体做法,首先从"购手机入网送话费"这个表中,找到对应的套餐,然后用"月送话费金额"*24,加到"预存话费送手机 "这张表的"优惠购机款"上,就是实际购机价格。6696126156优惠购机款3199279924992199月送话费金额26385062实际购机价3823371136003687经常有人说联通iphone的126套餐是鸡肋,这要看你怎么看了。从不同的角度分析,会得到不同的结果。不过这还没完。我们没算利息。以96套餐为例,假设年利率5%,那么方案1可得到4999*0.05*2+5*24*0.05=505.9元。方案2可…

DHPublicKey与byte[]转换的问题

背景:网络上经常采用Diffie-Hellman算法来交换密钥。通讯的双方首先共享2个公开数字:p和g。其中p是一个大质数,g通常等于2。则\( Z_p=\{a=g^n \pmod{p}, n \in N \} \) 构成一个整数群。密钥的生成方法:\(Z_p\)中选取一个随机数x,然后计算\( y=g^x \)。那么x,y构成一个keypair。x是私钥,y是公钥。那么每个人只需要把自己的公钥发出去,然后通信的时候选取一个对称加密算法,利用\(y^x\)作为通信的加密密钥即可。其中x是自己的私钥,y是对方的公钥。实际在用JAVA实现的时候:公钥和私钥可以通过java.security.KeyPairGenerator生成。//DH是算法名,SunJCE是Java Security API的Provider名,此处使用JDK自带的的SunJCE。 KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH","SunJCE"); 然后初始化KeyPairGenerator 这时需要传入一个java.security.spec.AlgorithmParameterSpec对象。kpg.initialize(MODP_GROUP2); MODP_GROUP2对象是这么来的:StringBuffer sb = new StringBuffer(); sb.append("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"); sb.append("29024E088A67CC74020BBEA63B139B22514A08798E3404DD"); sb.append("EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"); sb.append("E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"); sb.append("EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"); sb.appe…

Birthday Problem and Hash Collision

1月份的时候,我们公司的数据部门给我们做了一次统计学的培训。 那天参加培训的大概有30-40人,主讲当时提了这样一个问题,在参加培训的人当中,有两个人是同月同日生的概率是多少? 答案是“大于70%”。这个答案令当场的很多人吃惊。于是现场做了一下实验,确实找到了两个生日相同的人。这个问题如果从数学角度来分析,可以看成这样:有m个球,和n个桶。依次将这m个球扔入这n个桶中的随机一个。最终,有一个桶内至少有两个球的概率是多少?解答:首先,我们拿起一个球,随便扔到一个桶里。然后拿起第二个球,也扔到一个桶里。那么第二个球和第一个球被扔进同一个桶里的概率是\( \frac{1}{n} \),不在同一个桶里的概率是\( 1- \frac{1}{n} \)。假设前两个球不在同一个桶里,那么我们继续,拿起第三个球扔下去,它和前两个球不在同一个桶里的概率是\( 1- \frac{2}{n} \)。即,对于第k个球来说,假如前面k-1个球都在不同的桶里,那么第k个球被扔到一个新桶中的概率就是\( 1- \frac{k-1}{n} \)。于是,最终有一个桶内至少有两个球的概率就是$$ P =1 - (1- \frac{1}{n})(1- \frac{2}{n})(1- \frac{3}{n}) \dots (1- \frac{m-1}{n}) $$下面尝试给出一个更简单的近似值:首先,高等数学课上学过一个很重要的式子:当x远远小于1时,有\(e^x \approx 1 + x \)。于是,当m远远小于n的时候:$$ (1- \frac{1}{n})(1- \frac{2}{n})(1- \frac{3}{n}) \dots (1- \frac{m-1}{n}) \approx \Pi_{j=1}^{m-1}e^{-\frac{j}{n}} = e^{-\sum_{j=1}^{m-1}{\frac{j}{n}}} = e ^ {-\frac{m(m-1)}{2n}} $$生日问题其实在计算机算法中经常遇到:假设一个Hash Table有n个桶,需要插入m个元素,那么插入过程中发生hash冲突的概率是多大? \( 1- e ^ {-\frac{m(m-1)}{2n}} \)另外,假如我希望发生hash冲突的概率小于0.5,那么n至少应该为多大?求解$$ 1- e ^ {-\frac{m(…