一、开局 p,V%wG M
昨晚没事干,打算写个QQ农场外挂玩玩。后面的事情还不知道要怎么干,但前面第一步是肯定要的,就是用软件自动登录QQ空间。 uHsLlfTn
研究了QQ空间登录页面http://qzone.qq.com/,发现它有两种登录方式,第一种是快速登录,这个很简单,但必须先运行并登录了QQ软件后,才能利用这个功能登录QQ空间网页,最终放弃了这种方法,转向第二种方法--普通登录模式,就是通过输入QQ号、密码、验证码来登录QQ空间网页。要实现软件自动登录本来很简单,可是QQ密码在登录前,必须用一定的算法,把QQ密码加密成32字节的字符串。看到哪些算法,头真有点大了。今晚有空的话,跟踪一下试试。 d8uDSy
S"VO@)d
二、密码算法 MgP&9
刚才跟踪了一下密码算法,好复杂,Javascript算法如下: ;PB_ @Zg
function preprocess(A){ QE8 `nMf
var B=""; ?@z/#3b
B+=A.verifycode.value; K_AdMXF9
B=B.toUpperCase(); //这里的B的值,是用户填的验证码大写字符串 `M*jrkM]x
A.p.value=md5(md5_3(A.p.value)+B); //验证了一下,md5函数,是常规的32位md5算法 //A.p.value是用户原来填的未加密的QQ密码, ?yxQs=&-q~
//md5(md5_3(A.p.value)+B) 整个函数最终求得加密后的密码。 kS35X)-
//结果例如7C8B01F18E8EF15C5DA45EA59DC73934 ySB0"bl
//问题简化为md5_3这个算法了。 <@# g2b
return true u(fZ^
} W mbIz[un
W"*2,R[}%
+*w}H 0Z
function md5_3(B){ //看这里,这是难关。参数B是原来未加密的QQ密码。 "G-h8IN^O
var A=new Array; Vhh=GJ
A=core_md5(str2binl(B),B.length*chrsz); //又是调用另外一个算法,搞这么复杂干嘛尼.... ML MetRP
A=core_md5(A,16*chrsz); //还绕 Kj"X!-
A=core_md5(A,16*chrsz); //再绕 dC8}Ttc}
return binl2hex(A) //绕个没完! O_ZYm{T[7
} 6bc\ )n`
4;W{#jk
function md5(A){ //这个是常规md5算法,不鸟他,太简单了。 UlXxG|
return hex_md5(A) //一样的md5算法,等同,不鸟他。 WR`NISSp
} }#u #m.
*Ow2,{Nn
function hex_md5(A){ //还是常规md5算法... YvcV801Go
return binl2hex(core_md5(str2binl(A),A.length*chrsz)) //太暴露啦 @&E IH,c
} n}[S
\>pm (gF
function b64_md5(A){ L'S,=NYXY
return binl2b64(core_md5(str2binl(A),A.length*chrsz)) .gmS1ju
} ZAU#^bEQB
'7PaJj=Nx
function str_md5(A){ P_N F;v5 v
return binl2str(core_md5(str2binl(A),A.length*chrsz)) |%F,n2
} Y1I)w^}:
_p%n%Oce
function hex_hmac_md5(A,B){ <{bxOr+
return binl2hex(core_hmac_md5(A,B)) .kg 3>*
} =WW5H\?
cPuXy e
function b64_hmac_md5(A,B){ 2;WbXc!#!
return binl2b64(core_hmac_md5(A,B)) ^ex\S8j
} &xN+a{&
+"<+JRI(M5
function str_hmac_md5(A,B){ (~zu4^9w
return binl2str(core_hmac_md5(A,B)) X1`3KqK<9
} 5X)M)"rq;V
<JWU@A-.y
function md5_vm_test(){ k Alx m{
return hex_md5("abc")=="900150983cd24fb0d6963f7d28e17f72" )^{}ov
} [M~tH *4"
// 狂晕的时刻到了,核心算法core_md5来了.... ftxL-7y %
function core_md5(K,F){K[F>>5]|=128<<((F)%32);K[(((F+64)>>>9)<<4)+14]=F;var J=1732584193;var I=-271733879;var H=-1732584194;var G=271733878;for(var C=0;C<K.length;C+=16){var E=J;var D=I;var B=H;var A=G;J=md5_ff(J,I,H,G,K[C+0],7,-680876936);G=md5_ff(G,J,I,H,K[C+1],12,-389564586);H=md5_ff(H,G,J,I,K[C+2],17,606105819);I=md5_ff(I,H,G,J,K[C+3],22,-1044525330);J=md5_ff(J,I,H,G,K[C+4],7,-176418897);G=md5_ff(G,J,I,H,K[C+5],12,1200080426);H=md5_ff(H,G,J,I,K[C+6],17,-1473231341);I=md5_ff(I,H,G,J,K[C+7],22,-45705983);J=md5_ff(J,I,H,G,K[C+8],7,1770035416);G=md5_ff(G,J,I,H,K[C+9],12,-1958414417);H=md5_ff(H,G,J,I,K[C+10],17,-42063);I=md5_ff(I,H,G,J,K[C+11],22,-1990404162);J=md5_ff(J,I,H,G,K[C+12],7,1804603682);G=md5_ff(G,J,I,H,K[C+13],12,-40341101);H=md5_ff(H,G,J,I,K[C+14],17,-1502002290);I=md5_ff(I,H,G,J,K[C+15],22,1236535329);J=md5_gg(J,I,H,G,K[C+1],5,-165796510);G=md5_gg(G,J,I,H,K[C+6],9,-1069501632);H=md5_gg(H,G,J,I,K[C+11],14,643717713);I=md5_gg(I,H,G,J,K[C+0],20,-373897302);J=md5_gg(J,I,H,G,K[C+5],5,-701558691);G=md5_gg(G,J,I,H,K[C+10],9,38016083);H=md5_gg(H,G,J,I,K[C+15],14,-660478335);I=md5_gg(I,H,G,J,K[C+4],20,-405537848);J=md5_gg(J,I,H,G,K[C+9],5,568446438);G=md5_gg(G,J,I,H,K[C+14],9,-1019803690);H=md5_gg(H,G,J,I,K[C+3],14,-187363961);I=md5_gg(I,H,G,J,K[C+8],20,1163531501);J=md5_gg(J,I,H,G,K[C+13],5,-1444681467);G=md5_gg(G,J,I,H,K[C+2],9,-51403784);H=md5_gg(H,G,J,I,K[C+7],14,1735328473);I=md5_gg(I,H,G,J,K[C+12],20,-1926607734);J=md5_hh(J,I,H,G,K[C+5],4,-378558);G=md5_hh(G,J,I,H,K[C+8],11,-2022574463);H=md5_hh(H,G,J,I,K[C+11],16,1839030562);I=md5_hh(I,H,G,J,K[C+14],23,-35309556);J=md5_hh(J,I,H,G,K[C+1],4,-1530992060);G=md5_hh(G,J,I,H,K[C+4],11,1272893353);H=md5_hh(H,G,J,I,K[C+7],16,-155497632);I=md5_hh(I,H,G,J,K[C+10],23,-1094730640);J=md5_hh(J,I,H,G,K[C+13],4,681279174);G=md5_hh(G,J,I,H,K[C+0],11,-358537222);H=md5_hh(H,G,J,I,K[C+3],16,-722521979);I=md5_hh(I,H,G,J,K[C+6],23,76029189);J=md5_hh(J,I,H,G,K[C+9],4,-640364487);G=md5_hh(G,J,I,H,K[C+12],11,-421815835);H=md5_hh(H,G,J,I,K[C+15],16,530742520);I=md5_hh(I,H,G,J,K[C+2],23,-995338651);J=md5_ii(J,I,H,G,K[C+0],6,-198630844);G=md5_ii(G,J,I,H,K[C+7],10,1126891415);H=md5_ii(H,G,J,I,K[C+14],15,-1416354905);I=md5_ii(I,H,G,J,K[C+5],21,-57434055);J=md5_ii(J,I,H,G,K[C+12],6,1700485571);G=md5_ii(G,J,I,H,K[C+3],10,-1894986606);H=md5_ii(H,G,J,I,K[C+10],15,-1051523);I=md5_ii(I,H,G,J,K[C+1],21,-2054922799);J=md5_ii(J,I,H,G,K[C+8],6,1873313359);G=md5_ii(G,J,I,H,K[C+15],10,-30611744);H=md5_ii(H,G,J,I,K[C+6],15,-1560198380);I=md5_ii(I,H,G,J,K[C+13],21,1309151649);J=md5_ii(J,I,H,G,K[C+4],6,-145523070);G=md5_ii(G,J,I,H,K[C+11],10,-1120210379);H=md5_ii(H,G,J,I,K[C+2],15,718787259);I=md5_ii(I,H,G,J,K[C+9],21,-343485551);J=safe_add(J,E);I=safe_add(I,D);H=safe_add(H,B);G=safe_add(G,A)}if(mode==16){return Array(I,H)}else{return Array(J,I,H,G)}} f|q/2}Bqb
Ph[MXb:*
//真不想再看了,TX真是鸟事多磨,想搞死谁啊? .5G`Y
function md5_cmn(F,C,B,A,E,D){return safe_add(bit_rol(safe_add(safe_add(C,F),safe_add(A,D)),E),B)} Mi8)r_l%O
function md5_ff(C,B,G,F,A,E,D){return md5_cmn((B&G)|((~B)&F),C,B,A,E,D)}function md5_gg(C,B,G,F,A,E,D){return md5_cmn((B&F)|(G&(~F)),C,B,A,E,D)}function md5_hh(C,B,G,F,A,E,D){return md5_cmn(B^G^F,C,B,A,E,D)}function md5_ii(C,B,G,F,A,E,D){return md5_cmn(G^(B|(~F)),C,B,A,E,D)}function core_hmac_md5(C,F){var E=str2binl(C);if(E.length>16){E=core_md5(E,C.length*chrsz)}var A=Array(16),D=Array(16);for(var B=0;B<16;B++){A=E^909522486;D=E^1549556828}var G=core_md5(A.concat(str2binl(F)),512+F.length*chrsz);return core_md5(D.concat(G),512+128)}function safe_add(A,D){var C=(A&65535)+(D&65535);var B=(A>>16)+(D>>16)+(C>>16);return(B<<16)|(C&65535)}function bit_rol(A,B){return(A<<B)|(A>>>(32-B))}function str2binl(D){var C=Array();var A=(1<<chrsz)-1;for(var B=0;B<D.length*chrsz;B+=chrsz){C[B>>5]|=(D.charCodeAt(B/chrsz)&A)<<(B%32)}return C}function binl2str(C){var D="";var A=(1<<chrsz)-1;for(var B=0;B<C.length*32;B+=chrsz){D+=String.fromCharCode((C[B>>5]>>>(B%32))&A)}return D}function binl2hex(C){var B=hexcase?"0123456789ABCDEF":"0123456789abcdef";var D="";for(var A=0;A<C.length*4;A++){D+=B.charAt((C[A>>2]>>((A%4)*8+4))&15)+B.charAt((C[A>>2]>>((A%4)*8))&15)}return D}function binl2b64(D){var C="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var F="";for(var B=0;B<D.length*4;B+=3){var E=(((D[B>>2]>>8*(B%4))&255)<<16)|(((D[B+1>>2]>>8*((B+1)%4))&255)<<8)|((D[B+2>>2]>>8*((B+2)%4))&255);for(var A=0;A<4;A++){if(B*8+A*6>D.length*32){F+=b64pad}else{F+=C.charAt((E>>6*(3-A))&63)}}}return F};/* |xGv00|cb468f26d34c538c82b625c4af8e5397 */ bKCE;Wu:G
呵呵,看来QQ的程序员,还真不是饭桶,为了一个登录验证,花了不少功夫呵。不过,既然有算法,再怎么复杂,都是可解的,只是时间问题而已。 Fk/I (Q
有点困,先猪一下再说.... WDIin6u-
baII !ks
三、问题简化 Th9V8Rg+E
上面的密码问题,关键在于md5(md5_3("原密码")+"验证码") 这里,可以看出,只要密码固定,md5_3("原密码")就是固定的,变的在验证码这里。而md5算法,用.Net来实现的话,就只是一行代码而已。既然这样,我就懒得重写QQ的算法了,直接到网页里Javascript一下,把我的密码md5_3算出来,问题马上解决了,呵呵...当然,如果要公开发行的话,也容易,在软件里做个小小的静态网页,让用户先把自已的密码md5_3算出来,再复制到软件里,就万事大吉了。 [Xu8~c X
VB.NET里实现md5算法非常简单: R|\eBnfI
System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile("原文", "md5") c]|vg=W
====2009.8.21补==据牛人赐教,并经验证,md5_3可用以下算法实现(C#)========= TI7$J #
static string MD5_3(string arg) .pUB.l$)
{ Qjj }k)
System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); o7!A(Eu
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(arg); aA|{r/.10K
buffer = md5.ComputeHash(buffer); TRi#
buffer = md5.ComputeHash(buffer); z[c8W@OJ
buffer = md5.ComputeHash(buffer); U7g`R@
return BitConverter.ToString(buffer).Replace("-", "").ToUpper(); ;-u]@35
} }U_^zQfaj
MeBTc&S<
static string MD5(string arg) e#;43=/Ia
{ (10t,n$
System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); fF0K].
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(arg); ejV`W7U
buffer = md5.ComputeHash(buffer); :5~Dca_iU4
return BitConverter.ToString(buffer).Replace("-", "").ToUpper(); QIkFX.^
} P}Ig6^[m\
==== md5_3算法 ========= Wmd@%K
En&7e
四、验证码 GhfhR^P
验证码也不难,把图片采下来,显示到窗口,让用户用肉眼看后,把验证码填到软件里面,就解决了。当然,半手工操作而已。如果要用软件自动识别,太麻烦,而且TX把算法变一下,又认不了,不好。还有一点,采验证码图片的时侯,也要同时采页面的Session(形如:verifysession=75600974d636457da9c095026cd3034ee2f6d81301ec95d5d51576a91320012b4c109d94a61e3c43;)提交表单的时侯会用到,这是网页登录最基本的要求。 DL bP$&o
hD5@PeLh
五、提交表单 [ 6VM4l"
前面准备的事情干完了,接下来,就easy啦,把表单的名字和要提交的值记下来(就是Html里面<form > </form>中间的一堆<Input ...>),表单值要用urlencode加密一下,然后构造成一个字符串“表单名1=表单值1&表单名2=表单值2&.....”,再转为字节数组,用流的方式提交给action页面,就OK了。注意同时要送出原来的session。 : _Y^o
如果是vb.net的话,用HttpWebRequest、HttpWebResponse两个小东东就搞定了。下面的事情太简单,不说了。 l^R:W#*+U
OK,QQ空间软件半自动登录,就搞定了。接下来,就是破解农场FLASH游戏啦。这个暂时还没什么兴趣,以后要搞的话,再说了。。。 TE&E f$h
uO1^Q;F
六、暴破农场 auN8M.
想到破解FLASH就抓狂,任重而道远矣。不过,就算搞不定,看看也好嘛。找了个“硕思闪客精灵”,把农场的Swf用它反编译一下,看看,里面的代码还真多,还是要赞一下TX,人家程序员,哪可真不是光吃饭不干活的。 wrJQkven-
浏览了一下,暂时还看不出门道,不过倒是看到了农场的发展方向,估计牧场在不久的将来就会开张了,里面已经有“鸡蛋”、“兔仔”、“羊毛”、“牛奶”,还有动物的食物等等,现在还没开放,是因为他们还没搞好。 &H# l*
一些相关的线索: 3\ajnd|
1、农场初始化资料XML数据页面:http://appimg.qq.com/happyfarm/module/ini.xml z&!o1u q
2、农场操作的主要程序,在Main.Swf文件里的FarmCommand包里,比如: z]g#2xD2
摘果实的代码:fr.postRequest("mod=farmlandstatus&act=scrounge", _loc_3, harvestAllFn); *><j(uz!
偷果实:fr.postRequest(LocalData.api_steal + "mod=farmlandstatus&act=scrounge", _loc_3, harvestAllFn); lg-`zV3
浇水:fr.postRequest("mod=farmlandstatus&act=water", _loc_6, waterFn); wa[J\lW
放虫:fr.postRequest("mod=farmlandstatus&act=pest", _loc_5, addBugFn); a.u{b&+9
种植:fr.postRequest("mod=farmlandstatus&act=planting", _loc_5, plantingFn); + q2\3REzx
种草:fr.postRequest("mod=farmlandstatus&act=scatterSeed", _loc_6, addWeedFn); 2 br>{^T
施肥:fr.postRequest("mod=farmlandstatus&act=fertilize", _loc_5, fertilizeFn); :6C R~p
除草:fr.postRequest("mod=farmlandstatus&act=clearWeed", _loc_6, clearWeedFn); )`k+Oyvi<
杀虫:fr.postRequest("mod=farmlandstatus&act=spraying", _loc_7, clearBugFn); m?Jnb\0
铲除:executeScarify(place); :zC=JvKT
呵呵,看来,破解比原来想的容易多了..... U#Kw+slM
TNND,只顾着写这个东西,忘了去收果实了,刚才切过去一看,损失惨重啊!!!狂晕!2009.8.21 20:43 k!qOE\%B
fX:G;vYn
七、捉包 eG1A7n'6W
看破解的代码是有必要的,可以知道数据的编码方法。有关的算法,在FLASH源代码中都可以找到。这里不贴出。接下来就是看看要怎么跟QQ农场服务器打交道了,一样的,可以通过破解FLASH来取得各种方法,这里再介绍另外一个捷径:网络捉包。 8f?o?c|
介绍一个软件:Wireshark,强大的网络捉包工具,绝对经典的东西,下载地址:http://www.wireshark.org/download.html。经过捉包,可以看清楚各种操作的实际网络请求方法。例如种草,就是向主机 happyfarm.qzone.qq.com 的 /***.php?mod=farmlandstatus&act=scatterSeed 提交形如 fName=...&place=...&farmKey=...&tName=...&farmTime=...&ownerId=... 这样的数据。其它类同。 M9Z9s11{H
用某个QQ账号读取自已地里的数据如: ?fU{?nI}>p
{"farmlandStatus":[{"a":7,"b":1,"c":0,"d":0,"e":1,"f":0,"g":0,"h":1,"i":100,"j":0,"k":0,"l":0,"m":0,"n":[],"o":0,"p":[],"q":1249789896,"r":1249791412},{"a":7,"b":1,"c":0,"d":0,"e":1,"f":0,"g":0,"h":1,"i":100,"j":0,"k":0,"l":0,"m":0,"n":[],"o":0,"p":[],"q":1249789896,"r":1249805007},{"a":7,"b":1,"c":0,"d":0,"e":1,"f":0,"g":0,"h":1,"i":100,"j":0,"k":0,"l":0,"m":0,"n":[],"o":0,"p":[],"q":1249789898,"r":1249791420},{"a":7,"b":1,"c":0,"d":0,"e":1,"f":0,"g":0,"h":1,"i":100,"j":0,"k":0,"l":0,"m":0,"n":[],"o":0,"p":[],"q":1249789895,"r":1249791411},{"a":7,"b":1,"c":0,"d":0,"e":1,"f":0,"g":0,"h":1,"i":100,"j":0,"k":0,"l":0,"m":0,"n":[],"o":0,"p":[],"q":1249789897,"r":1249796459},{"a":7,"b":1,"c":0,"d":0,"e":1,"f":0,"g":0,"h":1,"i":100,"j":0,"k":0,"l":0,"m":0,"n":[],"o":0,"p":[],"q":1249789898,"r":1249813131},{"a":7,"b":1,"c":0,"d":0,"e":1,"f":0,"g":0,"h":1,"i":100,"j":0,"k":0,"l":0,"m":0,"n":[],"o":0,"p":[],"q":1249789894,"r":1249791409}],"items":{"1":{"itemId":1},"2":{"itemId":2}},"exp":5189,"a":1,"b":1249637140,"c":0,"task":{"taskId":8,"taskFlag":1},"user":{"pf":0}}