Thursday, May 30, 2013

飞常准用户客户端协议


原链接:https://sskaje.me/index.php/2013/05/verizhun/
UPDATE[2013-05-30]: 隐去具体密钥,释放文章

飞常准(http://www.veryzhun.com/)是一款国内比较好用的航班信息追踪服务/软件,官方莫名其妙地提供了两个版本的Android客户端,而且入口都在首页上。
不过也正好感谢这俩客户端,抓到了两个不同的签名密钥。

dex2jar + JD-GUI 发现签名算法部分的签名串是在一个 libsmm.so 的文件里,找SAI同学把两个版本的so都反编译了,一个15k的一个24k的,IDA Pro反编译的伪代码里边,分别有如下片段

  v7 = xxx;
  v8 = xxx;
  v9 = xxx;
  v10 = xxx;
  v11 = aRtw8121X0csX4[8];

。。。
完整文章:飞常准用户客户端协议

Monday, July 9, 2012

AppShopper 导入Appstore 历史购买记录

Link: http://sskaje.me/index.php/2012/07/appshopper-%e5%af%bc%e5%85%a5appstore-%e5%8e%86%e5%8f%b2%e8%b4%ad%e4%b9%b0%e8%ae%b0%e5%bd%95/

功能页面:http://sskaje.me/sskaje/appstore2appshopper/ (执行速度很慢,建议下载源码自己跑CLI)。
页面源码 http://sskaje.me/sskaje/appstore2appshopper/?s ,各路代码在 https://github.com/sskaje/appstore 。

以前写的iTunes Gift Card AutoRedeemer在 http://sskaje.me/index.php/2012/03/itunes-gift-card-autoredeemer/,这个版本把代码集成进去了,但是没测能不能用,因为现在自己没需求了。

 iTunes Appstore这边没啥可说的,抓包就是了。

关于Appshopper,一开始的实现方案是使用他家的iOS App,用Charles Proxy抓包拿到完整https的协议(关于Charles Proxy的破解点这里),前一天晚上看了下他家网站提交数据的参数。
发现如果需要把某个App添加到wishlist/myapp里,app id能直接拿到,但是还需要查到一个updateid。这个updateid从他家网页实在不好查,虽然可以匹配,但是流程会很麻烦,好在他家iOS App有个detail的接口,能拿到包括 updateid, wantit, ownit 这样的字段。这样实现起来,就逐个app地去查updateid和是否已经添加到wishlist/myapp,若未添加则执行添加。效率堪忧。

随便瞅了瞅他家网站,看到已经给了个导入工具。.Net 写的程序,研究了下,使用的是本地iTunes库,不过用了一个新的批量接口,
POST结构如下:
app_id,unix_timestamp
app_id,unix_timestamp
...
唯一不爽的是,似乎只能导入到myapp 里

一个效果图:

Monday, March 19, 2012

iTunes Gift Card AutoRedeemer

blogger 纯人肉同步
Link: http://sskaje.me/index.php/2012/03/itunes-gift-card-autoredeemer/

应该没人会有需求,具体应用需求来源不说了。

最开始一直想研究itunes的下载协议,但是中间因为某些原因,先搞了这个。

基本只是实现了登录、load redeem页(有form token)、提交请求(pc端有校验,相当于确认过程,需要输入apple id和密码)。
具体代码在http://sskaje.me/sskaje/code/itunes/auto_redeemer.php,敏感字段已移除或xx掉了,细节没处理,姑且看个协议大概。

用法:
 php auto_redeemer.php XXXXXXXXXXXX
 php auto_redeemer.php itunes.gift.card.list
 php auto_redeemer.php
后两种使用文件,每行一个code,第三种情况默认取itunes.log。

至于下载协议,由于有个签名字段,一直没搞定,半吊子逆向能力,等有空再研究,一直有个设想还没去验证。

Sunday, February 26, 2012

由一个扯淡需求引出的关于Charles Proxy的破解

原文链接:https://sskaje.me/2012/02/crack-charles-proxy/

UPDATE[2013.01.04]: Keygen for Charles Proxy: https://sskaje.me/2014/01/keygen-charles-proxy/
其实是想研究iTunes的协议然后写个限免自动下载工具的,之前的技术调研没看到网上有现成的实现,所以只能靠自己研究。
Wireshark抓到Client Hello的包能看到两个域,se.itunes.apple.com和p46-buy.itunes.apple.com,p46这个之前试了下,p后边的数字可以从2到50,不过只是半折法试的不保证准确。
。。。

Tuesday, January 24, 2012

12306刷票记

新blog:sskaje.me
本文地址:http://sskaje.me/index.php/2012/01/12306bot/

我也记不清啥时候动了写bot刷票这个念头的。原因很简单,我一直认为作为一个以代码谋生的不合格程序员,只有把生产工具用好,才能增加自己存在的价值。

首先说明一下主要开发环境:Windows 7,PHP 5.3,php_curl。

翻到了 第一条关于刷票的微博,附了图

很不低调地炫耀。

要刷票,首先自然得熟悉目标系统,所谓踩点。firefox+firebug,抓了一个标准流程的请求:登录、查票、订票。确认订单一开始没敢点,怕会有什么影响,后来去注册了几个测试号,然后尝试了确认订单的操作。流程本身不复杂,但是提交参数有点太多,一步一步来。

回到图1,登录,其实核心在验证码。

1 验证码识别
登录的验证码处理起来很简单,图在 https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=lrand,提供一个示例。
这个验证码结构一直没变,字体比较规则,无变形,直接就想到了tesseract,上一次用这个的时候是2010年12月初为了刷顺丰快递的订单追踪。

流程很简单:
1 下载图片;
2 转tiff(使用了ImageMagick的convert);
3 tesseract识别;
4 读取识别文件;
5 简单判断识别输出是否合法;
6 拼登录请求。

代码里留了个 decaptcha_valid()的方法,不过只判断了是不是4位数字+字母。
写完第一版代码后,清理了一些个人信息,把代码发给了@也云,不过当时毕竟比较早,他没怎么看,但是后期验证码识别这块提供了一些改进,包括:
1 replace识别输出里的空字符,因为由于字符间距不确定,tesseract偶尔会识别出空格;
2 训练tesseract,优化识别效果;
3 这次tesseract用的是v3,已经支持多种图片格式,而我还是按上次使用的经验convert jpeg 2 tiff,这次直接省去这个过程;
4 最关键的,登录验证码在登录请求提交后并不会删除上次访问验证码图片生成的session字段,也就是说,只需要识别一次验证码,后续只需要暴力提交即可。

按4调整了验证码识别方法,增加了人肉输入的支持。使用system('out.jpg')直接利用Windows 7 CMD的高级功能,同时发现了Win7自带的图片查看器system执行后程序可以继续往下走而不必关掉图片查看器,这样可以alt+tab切换cmd和图片查看器,确认人肉识别的输入是否正确。

上述逻辑在1月19号之前也同样适用于确认订单的验证码,那天早起发现这块儿验证码需要每次请求了,幸好之前人肉识别的改造增加了配置开关,很轻松地就完成了改动(虽然19号刷票无一成功,但是20号回家客车上刷5张票轻松到手)。

2 查票
查票的过程其实没啥特别可以说的,简单拼个请求,查票就是了。不过返回值是需要处理的,处理的依据,从页面看,就是查票结果输出里的“预订”按钮。

预订的参数就是页面输出里的getSelect()这个js调用的参数,最初的结构我从另外一条微博里找到了例子。
查票输出
正如页面上所示,结构是“车次号#历时(分钟单位)#发车时间#某个ID#始发站编号#目标站编号”,这个参数由页面解析后带入实际的预订请求。 不过后来某一天,这个参数变了,一个输出示例是“Z67#11:26#20:06#2400000Z6705#BXP#NCG#07:32#北京西#南昌#10175000003030800001404860000160895000001017503001”。同样按上边的解释,结构是“车次号#历时#发车时间#某个ID#始发站编号#目标站编号#到站时间#某个带入ypInfoDetail字段的参数”,@也云同学研究了一下最后这个字段,认定是“余票信息”的意思,对我们没有特别的意义,可无视(后续也证实了)。

这里可能有用的信息包括上述的“某个ID”。而查票的时候也需要输出里的始发站和目标站编号,这个编号可以从 这个地址里找到 。

3 订票 && 提交订单
订票使用了查票的输出参数,提交url是 https://dynamic.12306.cn/otsweb/order/querySingleAction.do?method=submutOrderRequest。明显有个Typo,而且这里名字是“提交订单”。不管那么多,说技术细节。

这个使用查票参数构造了一个POST请求,如果提交成功,服务器端会发一个302,重定向到一个新的页面 https://dynamic.12306.cn/otsweb/order/confirmPassengerAction.do?method=init。

流程很简单,curl实现只需要开 CURLOPT_FOLLOWLOCATION 即可。但是这个地方卡了我很久,直到12月初的某天才查出来为什么每次提交在这里总是出现 HTTP 500,虽然还是不知道原因,但是补上了几个http头,一切ok了(具体看代码)。

订票输出的页面,会有formtoken,参数名是 org.apache.struts.taglib.html.TOKEN。有点Web编程经验就应该能知道是干啥的。不多说,参数请求必须带上,所以正则匹配,无技术含量。
确认订单页只是拼参数,补上验证码识别就ok,无技术含量。

第一个细节,这个页面是填身份证的页面,入口有两个,订票 和 确认订单失败(后期确认订单失败不再直接回到该页面,不过验证码错误的应该还是会回来),所以确认订单的错误处理是可以考虑增加的。这个一开始代码就加上了,不过后来@也云改的lite版把这个流程改为 提交订单 + 确认订单,两步走,确认失败直接重订票。

第二个细节,验证码URL不一样了,https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=randp,不过输出还是一样的。

第三个细节,最关键的,某个晚上睡前突然意识到的问题。回到form token上,我们可以观察到这个系统很多地方都会有form token,包括但不限于确认订单、支付订单、撤销订单等等。但是我意识到一个问题,提交订单这个过程可能会很慢,慢到一个不能忍的状态,尤其是高峰期。而,用户中心的表单里,也有form token,而且参数名完全一样,那么,这两个token会是同一个session字段吗?
接下来做的事情是改造form token的获取方法,改从用户中心的页面匹配,仅作测试。测试结果很乐观,从用户中心匹配到的token能作为确认订单token,而且这个请求自然会比从提交订单拿到的页面匹配要快,于是匹配token的方法可以优化了。(但是16号早上,订单系统似乎出问题了,各种慢,未支付订单页面刷不出来,支付订单也失败了,3张票扣了我1.3k RMB,没出票,极度想粗口!!!)

说到第三细节,如果你顺着读下来,可能会觉得有疑问:不是有很多参数要从页面匹配吗?如果不去拿提交订单后的页面,匹配变量,怎么确认订单?
答案是:你所需要的参数从你拿到查票输出的时候就已经足够了。你所需要的车次相关的信息,查票的输出已经能满足所有字段的需求;你所需要的订票人信息,以及所需坐席,你自己是已经知道了的。而,经过观察,查票输出里除了余票信息字段会变,其他的都不变!!!所以,你只需要在确定要刷票的前一天晚上,做一下准备工作,查查你要刷票的车次的信息,第二天拿着刷票便是了!!!
具体不多说,看代码便知。

到此,基本能说的都说了。

后边还有一些其他的细节优化。
支付
最初的支付表单是在一个独立的页面上,从未完成订单里选择订单,点支付,弹出的一个新页面匹配几个参数再submit,输出页面只有一个简单的form,body onload的时候触发了submit操作。也就是说,只需要拿下这个页面完成支付即可,这个最早@也云实验成功了,后来我有几次支付也用这个方法完成。过了几天,支付的表单在未完成订单点支付后的页面里直接写死了,于是新的lite版bot直接匹配了页面表单,存了个独立页面。
完后,弹出IE,支付。
本来想做招行的手机支付自动化的,后来实在懒了,自己订票的需求满足了,没心做这种优化。

登录
登录是件苦力活,经常就人满了,频率高了IP也会被封。所以登录成功一次得好好珍惜。
做法是: CURLOPT_COOKIEFILE + CURLOPT_COOKIEJAR。
脚本执行结束后,手工改 cookie jar文件,把expiration时间改长点。
但是由于经常性在登录完成后ctrl+c,没法触发__destruct()的调用,按@也云的建议,改成了登录完成后触发一次curl close操作并改expiration,执行结束后的继续保留。


源码
/bot.php
/config/test.php

执行参数 php bot.php 0 test 2>err.txt
只保证win下可用,soff改过一个mac下可用的版本,没找他要。
test 是 config/ 下的 test.php
0 是 test.php 里的tickets_info字段的索引,其实叫train_info更合适

然后为了骚扰自己,加了个vbs文件,内容很简单,取名messagebox.vbs,这段内容感谢@linxinsnow
' Message box Script Set objArgs = WScript.Arguments For I = 0 to objArgs.Count - 1 msgbox objArgs(I) Next

写在最后
虽然我经常在微博XY,但是一直没公开源码。最终可用的代码也只有也云和soff拿到了。
一直不开源或者不发布的原因很简单,我不希望我的代码成为别人系统“有压力”的理由。我也看到了几个网上放出来的刷票工具或者js解决方案,但是,越多人知道这个,就意味着越多人会用你的“刀”“杀”铁道部。
或许我想的有点多。

感谢也云同学对本年度刷票的支持。感谢soff提供的验证码识别优化方案,虽然效果还不如我们的。

Saturday, December 31, 2011

纪事:2010-2011


工作第三年过半,如果从实习开始算,也已经满了三年了。
2009年底,正式工作刚半年,在BITUNION上替版主掺和了年终总结的活动,然后2010年。今年,不知觉忘了这事,前不久去联盟职场版看了眼,efni已经开了新的活动。没曾想,这一无聊之举也成了版面的小传统了。
翻看了一眼09年写的总结,时间过得真快。10年,才到新公司半年,犹豫了一下最后没写。于是这回得从2010年开始回忆了。
2010年初,继续以尴尬的身份在前公司混着,先是09年11月在微博产品的正式上线后离开,然后在那层好几个产品线转了一圈,**产品的准上线,**产品的夭折,然后经历了人生中的第一次项目被砍、部门被砍,最后尴尬地回到了微博。紧接着,离职潮,虽然总共就4个人,整个组就这样先后离开了前公司。
决定离开之前,犹豫了很久。很迷茫,不知道自己该做什么,该学什么,不知道微博适不适合我,也不知道下一份工作在哪儿。只知道,4个人的小团队散了,很难找到一个理由一个人留下;微博或许很好但是不适合我,找不到归属感。6月初甚至更早的时候,Sunshow同学一次电话一次bg欲拉我入伙,犹豫着没给答复,只是说6月底给结论。soff那儿当时也有可以做的项目,没心思做。只是在那儿纠结着。直到有一天xkl同学告诉我她确认要离职了,纠结了好一会儿然后在msn上跟她说出了我的打算,顿时心里轻松了不少。
那时学校里的事情总算有了个结论,压在心里的几块石头少了一块最大的。然后给几个领导简单地写了个邮件说了自己想离职的事情,紧接着被耗子找着聊了两次。依然记得当时说了,趁自己年轻,还有机会犯错,再过几年就彻底没机会了。然后没多久,就离职了。(感谢高成同学替我折腾了好久离职的手续。)
然后7月第二个周一,换了个新的环境,从零开始。
开始的半年,完全站在旁观者的位置以学习的状态看待要做的内容,因为真的不知道要做什么,模模糊糊的有个目标却不知道有什么需求。搭积木似的堆需求、堆模块,堆了小半年,新项目的上线时间也从一开始的8月变成10月,12月,第二年……最后被告知农历年后要上线,这才开始上粘合剂,把项目搭了起来。
2010年后半年,还在中关村,除了加班还是加班。小team初成型,公司也从10多个人变成20个,30个;挤着一个2M的破铁通宽带,经历过一次QQ群被屏蔽;无数次吃饭时被紧急电话告知线上系统挂了;为了躲酒翘了第一次大FB;集体加班叫小豆外卖,中午的午饭上楼,免费饮料,定期的毛毛虫,还有偶尔出现的地铁口的外卖烤肉串,有双夹,以及各种抱怨午饭难吃然后下楼买的炒饼;有午间的魔兽,有PES、CS;还有那6点准点关掉和周末从来不开的空调,天热的时候真想抱着电脑去电梯里干活……
2010年,偶然的机会,开始尝试接触抗战史,于是开始读书学习,加班之余释放压力。2010年,无数次折腾,用了两年的nokia终于告急,找萧剑风借了个HTC Magic凑合了半年,然后自己买了个iphone,也第一次有了信用卡。2010年,用了两年半的笔记本被变成了台式机,然后换了个新的,也是2010年,买了台5k的联想本带回去送给了爹娘……
然后2010年结束了,2011年1月10号搬到了亮马桥,开始从每天100米变成每天地铁+公交,也开始了寻觅午餐的痛苦生活。
年前,百般无奈买了全价机票,准备从南昌再折腾一次,然后也云同学告诉我弄到了两张软卧,于是happy地省了小1k回家了。
年后回来,开始紧张的上线前准备工作。上线从2011年02月21日02时开始,翻看着当时的微博来了段流水账般的回忆:
2011年02月20日凌晨2点,新系统上线前的预演。等待是漫长的,从19日中午开始。翻看着那时候的微博,似乎当时正在替28同学干活,折腾一个LFI的东西,未果,然后只能去睡觉。那个时候可能是qatang吧,不知道拿啥放着哈狗帮的东西,本来椅子就难受,然后耳旁还各种噪声,怎么着都没睡着。无聊,刷微博,看球,然后3点多看球睡着了,直到5点半,才撤回去睡觉。
20日睡到中午,吃完午饭,买了无数红牛,备着晚上上线前用。看着当时的微博,完全不知道自己在干啥,只知道喝了三瓶红牛,还困得不行睡着了。
21日凌晨2点开始执行上线流程,一切都很顺利,出乎意料的顺利。白天没回去。上午在公司趴桌上睡觉,没睡好,但是也没人骚扰说系统不可用,直到下午,充值系统各种掉单,然后线上查bug,查得死去活来,终于搞定。晚上11点,去雍和宫的金鼎轩吃了夜宵,也许是晚饭。
22日下午才去的公司,结果晚上临下班系统出问题,被迫把一个op cache的组件下了,然后又发现另外的问题,陪着俩人熬到4点多,中间又出去买了红牛,继续喝红牛睡觉。
…………
然后后来,工作似乎开始进入了新的篇章,稳定而又不平淡。开始踢球了,开始跟着一群人喝酒了,各种吃,各种玩。见证着公司的发展,也很享受现在这种团队的氛围。
2011年,有久等不至的302和731,也有难见空车的十号线;有令人发愁的午餐,也有高喊着减肥各处海吃的晚饭;有兰特伯爵的德国啤酒,也有门口的山西面;有科荟路的脆骨,也有簋街的烤串;有每周一次的欢乐足球,也有不再掺和的香山;有雨后天晴的秦皇岛,也有青山绿水的雾灵山;有无数新面孔的加入,也有不少老面孔的难再见。
也许真是太过怀念2010,总觉得2011没那么丰富多彩,但是亲眼见证着公司的成长,产品的上线,这一年,值得的。

--------------------------------
拖了好几天,最终决定舍弃一些不该说的话,凑合了这篇不疼不痒的两年回忆,权当总结这两年吧。
不善言辞亦不习惯从言语上表达感谢,但是最后,感谢本文提及和未提及的所有人。

Saturday, October 23, 2010

live essential 2011 messenger msgsres.dll ad-free beta

这个纯粹是为了凑发博数量:P

上一篇记录的东西大体没问题,一开始用的时候的确广告都没了,但是突然我发现聊天窗口的广告又出现了。。。于是各种囧。。。

这次简单地复了一遍流程,改一点测一点。

log如下:
1195 
 找到 SSConstrainer,layoutpos=top -> layoutpos=none
  主窗体广告消失。


1192
 adbannercont, layout=FillLayout() -> layoutpos=none

  聊天窗口动画广告还在,关掉动画后的文字广告已经不在了

 找到 adbannerregiontrident,删除 layout=FillLayout()


下载地址:http://sinaurl.cn/h40Mhk
有问题请sina微博 @sskaje
感谢 Messenger Plus!,Resource Hacker,微盘/SAE 的存储

Thursday, October 21, 2010

Windows Live Essential 2011 Messenger 去广告

上篇相关的文章:http://blog.sskaje.name/2009/09/msn.html

等了好久的uib decoder,终于在Messenger Plus! 4.90.0.392 中拿到了(获取前边已经有了,但是一直没敢再次尝试恐怖的live essential)。

操作流程差不多,区别如下:
这次有3个bin文件,920 4010 "Data_1192.bin",923 4010 "Data_1195.bin",925 4010 "Data_1197.bin"。

1195 修改:找到 SSConstrainer,layoutpos=top -> layoutpos=none
1192和1197大体相同都作三个修改:
1) adbannercont,该行 layout=FillLayout() -> layoutpos=none
2) 上边 layoutpos=none
3) 跟 2 同一行,去掉 layout=FillLayout()

保存,替换就行。
这次语言都是1033,直接替换就好。二进制文件可以 mail 我 sskaje (at) gmail dot com

================
这次在1192/1197上折腾死了,唉,唉

Tuesday, August 31, 2010

Problems with tracing location in callback-ed functions/methods

Problems with tracing location in callback-ed functions/methods

Exceptions thown in a function/method been called via 'call_user_func' or 'call_user_func_array', have key 'file' and 'line' disappeared in their first element (array[0]) from Exception::getTrace().
getTrace());
}

function callback() {
 call_user_func('test');
}

try {
 callback();
} catch (Exception $e) {
 var_dump($e->getTrace());
}
?>

G:\wwwroot\Test\Exception>php trace_func.php
#0  test() called at [G:\wwwroot\Test\Exception\trace_func.php:8]
array(1) {
  [0]=>
  array(4) {
    ["file"]=>
    string(40) "G:\wwwroot\Test\Exception\trace_func.php"
    ["line"]=>
    int(8)
    ["function"]=>
    string(4) "test"
    ["args"]=>
    array(0) {
    }
  }
}
#0  test()
#1  call_user_func(test) called at [G:\wwwroot\Test\Exception\trace_func.php:14]
#2  callback() called at [G:\wwwroot\Test\Exception\trace_func.php:18]
array(3) {
  [0]=>
  array(2) {
    ["function"]=>
    string(4) "test"
    ["args"]=>
    array(0) {
    }
  }
  [1]=>
  array(4) {
    ["file"]=>
    string(40) "G:\wwwroot\Test\Exception\trace_func.php"
    ["line"]=>
    int(14)
    ["function"]=>
    string(14) "call_user_func"
    ["args"]=>
    array(1) {
      [0]=>
      string(4) "test"
    }
  }
  [2]=>
  array(4) {
    ["file"]=>
    string(40) "G:\wwwroot\Test\Exception\trace_func.php"
    ["line"]=>
    int(18)
    ["function"]=>
    string(8) "callback"
    ["args"]=>
    array(0) {
    }
  }
}
Similar result given by debug_print_backtrace(), is this a bug or an unexpected feature ? XD

Wednesday, August 25, 2010

foreach 赋值引用 + refcount 引发的一个问题

第一个问题,封装了 mysqli_stmt 的bind_param方法,为了实现 $stmt->execute(array(typelist, var1, var2, ...)); 的操作。
<?php
  $values[0] = $info[0];
  foreach ($info[1] as $k=>$v) {
   $fields[] = "`{$k}`=?";
   $values[] = & $v;
  }

这个操作的结果显然是错误的。于是有了个下边的测试:
<?php
$array = array(
1,2,3,4,5
);

$to0 = array();
foreach ($array as $k=>$v) {
 $to0[$k] = & $v;
}
var_dump($to0);


$to1 = array();
foreach ($array as $k=>$v) {
 $to1[$k] = & $v;
 unset($v);
}
var_dump($to1);


$to2 = array();
foreach ($array as $k=>&$v) {
 $to2[$k] = & $v;
}
var_dump($to2);

/*
Output:

G:\wwwroot\Test>php reference3.php
array(5) {
  [0]=>
  &int(5)
  [1]=>
  &int(5)
  [2]=>
  &int(5)
  [3]=>
  &int(5)
  [4]=>
  &int(5)
}
array(5) {
  [0]=>
  &int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
  [4]=>
  int(5)
}
array(5) {
  [0]=>
  &int(1)
  [1]=>
  &int(2)
  [2]=>
  &int(3)
  [3]=>
  &int(4)
  [4]=>
  &int(5)
}

*/


第一个问题结束
---------------------------------------------------------

第二个问题,
最上边代码后来的版本是这样的:
<?php
  $values[0] = $info[0];
  foreach ($info[1] as $k=>&$v) {
   $fields[] = "`{$k}`=?";
   $values[] = & $v;
  }
  unset($info);

  $stmt = $db->prepare($query);
  $ret = $stmt->execute($values);

结果传递多于一个字段时,execute报错。
假设foreach 循环结束后,$values 内容是:
array(5) {
  [0]=>
  &int(1)
  [1]=>
  &int(2)
  [2]=>
  &int(3)
  [3]=>
  &int(4)
  [4]=>
  &int(5)
}
而在execute()的时候,却变成了
array(5) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
  [4]=>
  &int(5)
}

除了最后一个元素外,其他的引用都消失了。。。
在几个点放上debug_zval_dump,一看便明白了。

foreach 循环结束的时候,$values 数组涉及到引用的元素,除了最后一个 refcount 为3,其余均为2. 因为对于其他的元素,$info 和 $values 两个数组的元素都对目标进行了引用,所以refcount为2,而最后一个,因为 foreach 的临时变量 $v 还对这个有引用,所以 refcount 是3.
当执行了 unset($info) 的操作后,所有的refcount均 -1,于是变成了 11112。
这个时候var_dump,自然会发现只有最后一个元素还处于引用的状态。

Monday, August 16, 2010

第一次完整流程体验wep破解

一个白天都在折腾bt4,直到下午才把usb persistent live 做好。
大致步骤:
live cd/dvd 启动 bt4;
1个4GB U盘,fdisk 分区成 2+2,fat32+ext3,激活1;
mount -o loop bt4 dvd iso, 复制内容到fat分区;
grub-install --no-floppy --root-directory=/PATH/TO/FIRST/PARTITION /dev/DEVICENAME;
done;
印象中就这几步,具体可以看看bt4官网的tutorials

破解过程其实都还简单,手头没教程,就只能自己折腾。
一开始遇到问题,所以后来先把 realtek的驱动装了,不过后来想想 iw scan时 nl80211 not found 的问题,应该不用装驱动;
iwlist wlan0 scanning,找到bssid,因为目标是在windows下就确定了的,所以根据essid,找到bssid,也就是mac,记下来就行;
airodump-ng 。。。。。 参数不记得了= =, --ivs --write test ..... 反正有一个 filter 用是 bssid,需要指定interface(最后一个参数),第一次抓了11466个包,不成,后来重搞,抓了20000+个,搞定了;
aircrack-ng 。。。。。 参数也不记得了,有个 -a 1 指定wep,有个指定ivs文件,最后结果大致入下:

                                                 Aircrack-ng 1.1 r1738


                                  [00:00:07] Tested 108182 keys (got 20290 IVs)

   KB    depth   byte(vote)
    0    1/ 33   13(25648) 4F(25392) 43(25280) 25(24912) D0(24836) 47(24800) AE(24696) 76(24544)
    1    0/ 11   57(27220) C4(26432) F8(26416) E3(25616) FB(24620) F6(24588) 04(24472) AC(24292)
    2    0/  1   92(29236) B8(25916) 84(25172) 8D(25092) 4E(24980) E6(24684) B1(24476) 41(23960)
    3    6/ 33   46(24548) 7C(24512) A6(24364) 72(24216) EA(24152) 14(23968) 27(23884) 64(23448)
    4    8/ 10   08(24404) C2(24332) 1E(24284) 11(24256) A7(24176) 30(24108) FC(24072) 02(23888)

                         KEY FOUND! [ 13:57:92:46:80 ]
        Decrypted correctly: 100%

Wednesday, July 21, 2010

乱七八糟

这是一篇给 @xklxkl 的作业。
实在不知道写什么,只能是想到啥写啥。
不要试图从中找逻辑,根本就没有逻辑。。。。。。

ID: sskaje

这个小标题应该是什么。。。:
1 尊重其他文化,但绝不愿主动尝试接受。
2 我是懒人,怎么简单怎么来,什么好用用什么,怎么好用怎么来。
--------------------------------------------------------------------------
写代码:
这是当前的吃饭工具,所以多写点,不过也只是列表。
非科班出身,从没指望做得多专业。不会数据结构和算法,不会软件工程,啥都不会各种不会,更多只是以知识面取胜,关键问题是没人会管知识面,尴尬。
曾经接触过的各种语言,我也不知道咋排序:
C:
曾经:学过,写过点简单的东西,OpenSSL相关;
现状:忘光了,除了基本语法。想捡起来,但不知道用来干嘛。
C++:
曾经:上课没好好学,曾经嵌入式课封装过一个简单的mysql client的lib;
现状:抱着抵制C++的想法,我只会回答“全忘了”。
ASM:
曾经:从win32逆向开始入手学汇编,只会x86的语法,at&t的都是现查,但是总共也没几次;
现状:还能简单地逆一些初级的crackme或一些注册验证简单的win32应用程序,coding是没戏的了,Ollydbg无敌,IDA Pro万能。
Perl:
曾经:三年前学过一点,但是后边不知道干啥,于是就忘了;
现状:语法应该还能记得一丁点吧。
Python:
曾经:第一次见应该快4年了,也只是很初级地奔着几个库查了点资料发现没有任何成果就放弃了;
现状:呃。。。
Matlab:
曾经:伟大的Matlab,信号系统必备,只是我实验课都是把考试系统黑了拿别人的答案改的 :x;
现状:用不上啊,为啥要学。
Java:
曾经:没学过,但是看过一点,写个第一次应用应该是三年前拿下某个高中的服务器的时候,测试写入文件能否执行的时候,不过当时报了语法错误,至少证明解析了;
现状:一年前反编译Zend Studio 7的注册部分,把算法跟出来,写了个keygen,但是,还是属于啥都不会的那种。
JS:
曾经:还算看得比较多吧;
现状:让我写可以,但是基本啥都不会,很多都需要现查。
Ruby/Erlang/Lua/VBS/VBA/VB/ASP:
曾经:看过;
现状:不会。
PHP:
曾经:从第一次接触到现在也快4年了吧,不曾尝试主动看C源码,但是尝试过自己Windows下从头开始编译,大致了解点,写PHP完全因为简单省事;
现状:快被逼进死胡同了。
SQL:
曾经:只说mysql,也算是写过sp,trigger,function的了;
现状:手册无敌。
Bat/Shell Script:
曾经:Dos下不会批处理还咋混——于是没混好;Shell Script没学过
现状:能写简单的,不指望有多好的逻辑。
[a-zA-Z]+ML:
曾经:你觉得这是语言么?
现状:你觉得这是语言么?
CSS:
曾经:同上;
现状:同上。
小结:
我本不想用代码吃饭,结果现在沦落到此地步,怎一个凄惨了得。然后,写代码的原因是目的性极强的,当初只是为了玩hts,各种诡异的题目不靠编程根本没法搞,不会写代码,搞安全也没戏。
有明确的目的,有阶段性的成就感,才有发展的动力啊。
-----------------------------------------------------------------
网络安全:
自认为是做安全出身,于是坚决抵制把安全当主业。
所谓安全,更多的只是一个看问题的“新”的角度。于是我总是习惯于换各种角度看问题,但是鉴于模拟信号没法完整遍历,所以,从不指望自己能面面俱到,想到哪儿算哪儿。
----------------------------------------------------------------
其他:
也没啥了。做安全的时候接触过实验室仅有的几台设备;学校的时候,简单研究过dns协议,于是做了个现在看来用户体验极差的ddns服务;前不久刚把hts的irc题目搞完了,于是又重新看了点irc的东西;……
----------------------------------------------------------------
总结:
做的东西实在太杂了,整理起来也太费劲,于是经常就想到什么写什么。
大二进的实验室,所以有时候接触的东西比身边的人多很多,同时也被一群人骂没情商,lol。
学校里混论坛比较多,做安全的时候也有自己的小圈子,于是各种原因认识了学校里形形色色的人,但是和身边的人反倒关系处得不好。现在依旧挂着论坛管理运维的职务,偶尔去学校网络中心也去顺道提供安全相关“咨询服务”。
修电脑的事情已经坚决不做了。
很喜欢做点自己想做的事情,但是尴尬的是现在不知道该做什么,不知道什么是自己真正想做的有兴趣的。于是就有了这个“暑假”。


-----------------------------------------------------------------

我是真不想看自己的简历了。。。。。。

Monday, June 28, 2010

HackThisSite: Programming 6 Bypass the image captcha

这是07年题目发布一来,解决耗时最长的一题,从07年年初到现在,这一晃。。。

题目地址在 http://www.hackthissite.org/missions/prog/6/

列表说明如下:
Bypass the image captcha

Bypass the image captcha This level is about OCR. Write a program which is able to read all the characters in a given image, and let it beat a captcha automatically. 

Difficulty rating: harder   Take this challenge!

题目大致是一个用 js+css 渲染的页面,上边画了一些文字,可以看这里
PHP Imagick 使用手记 这个是三年前学 php imagick 的时候做的,思路待会儿一并讲了。需要注册才能下附件,但是校外貌似没法注册 XD.
那就直接去上边给的关卡链接看吧。

画出来的页面,字符按渐近线排序。从最内顺时针方向,每旋转10度一个字符;相邻两个文字到旋转中心之间的距离是有一定比例关系的。
于是思路有两个:
1 画图 + OCR
图片绘制很简单,html代码直接可以重写成php代码,然后生成图片就是了。但是唯一的麻烦是文字的识别。
一般OCR预处理图片的时候,都会对字符进行矫正,但是毕竟斜着的字符处理起来还是很麻烦,所以基本思路是“旋转”。取的是水平和垂直的四栏数据,旋转10度,再取,再旋转。。。
由于拿到的数据本身已经做过一些round类似的处理,整数数值已经不精确,不合适地旋转会让偏差更大。所以旋转的策略应该是,每次都对*原图*旋转,第一次旋转0°,第二次10°。。。
这样总比拿上次的结果再转10°的效果好。。。

剩下就是定位,然后截框,圈字符,识别。。。。
最早按这个思路做,然后折腾了好久识别率都不高。。。
(其实是当时截框的定位计算出了错,以后有时间可以再重新实现)

2 直接字符坐标定位
每个字符都有特定的组成,例如 1 有三个横线, 2 有一个弧线和两个横线。。。
关键的一点,弧线的弧度是不受旋转影响而产生误差的。所以有弧线的字符很容易判定出来。


不管哪种思路,第一步都是差不多的:
第一步,定位所谓的旋转中心。直接字面理解即可,或者把这个发散线当一个圆,那就是圆心。
有一个重要的参数,是上边提到的相邻两个字符中间位置到圆心距离的比例,1.005,不用问为什么,反正就是这个数了。这样一来,测得最内圈是99像素左右,最外圈的是 100 * 1.005^252 ~= 351 像素。

定义的数据结构如下:
Line {
int x1,
int y1,
int x2,
int y2
}
Arc {
int x,
int y,
int radius,
int sangle,
int anglelength
}
对于Line,(x1,y1) 和 (x2,y2) 之间的位置关系是不确定的,所以预处理最好调一下顺序。
对于Arc,这里是按照逆时针方向画的弧线,所有都是用角度单位表示,x,y是圆心坐标。

预处理第一步,根据定义的数据结构,可以遍历取到x方向和y方向的最小和最大的坐标,计入 edge 中。
所以有圆心位置为:
x方向: edge[x][max] - (edge[x][max] - edge[x][min]) / (1.005^18 + 1)
y方向: edge[y][max] - (edge[y][max] - edge[y][min]) / (1.005^18 + 1)

由于题目中的坐标已经不是精确值,所以这里建议不要再作 round 处理。


*从现在开始,以上述第二种思路来分析*。
第二步,根据圆心坐标,计算每个字符的中心位置。因为旋转角度是确定的,所以这个也很容易。
第三步,遍历所有的Line 和 Arc,根据中点位置,判定基于圆心笛卡尔坐标系的角度,进而可以分辨出是在哪条射线方向上,分组整理。
第四步,对每组的Line和Arc,再判定距离圆心的位置,就可以定位大致是第几层的字符。
第五步,按组、层识别字符。

后边的思路都很简单,最恶心的就是字符的识别,特征提取。
上边说了,有弧线的地方很容易搞定。真正开发中也的确如此。但是对于没有弧线的位置,于是就得恶心了,关键还得考虑排重和缺信息的情况。

Anyway, 这个题目已经搞定。只是由于识别率的不稳定,就在那儿直接循环暴力重试。。。

不过,第一次用 php 的 goto,真方便 XD

Sunday, June 13, 2010

leviathan.intruded.net

上午把这个过了,最容易的一台机器
ssh: leviathan.intruded.net
port: 10101
username: level1
password: leviathan

密码列表(blog里隐掉了)
level1: leviathan
level2: ********
level3: ********
level4: ********
level5: ********
level6: ********
level7: ********
level8: ********
----------------------------------
1
cat .backup/bookmarks.html | grep --color pass
<DT><A HREF="http://nahtaivel.intruded.net/passwordus.html" TEMP: "AFeSdWEf"ADD_DATE="1155384634" LAST_CHARSET="ISO-8859-1" ID="rdf:#$2wIU71">password to level2</A>
直接访问
********
----------------------------------
2
/wargame/check
下载下来打开,看到 sex secret god love
这个就是密码,验证,打开一个shell

level2@leviathan:/wargame$ ./check
password: sex secret god love
sh-3.1$ id
uid=1001(level2) gid=1001(level2) euid=1002(level3) groups=1001(level2)
********
----------------------------------
3
level3@leviathan:/wargame$ ln -s /home/level4/.passwd /tmp/file.log
level3@leviathan:/wargame$ ./prog
********
这个纯粹是猜出来的
直接运行提示 Cannot find /tmp/file.log
/tmp是可写不可读的状态

----------------------------------
4
/wargame
ida pro打开,直接伪代码看
int __cdecl do_stuff()
{
  int v1; // [sp+11Dh] [bp-Bh]@1
  int v2; // [sp+121h] [bp-7h]@1
  __int16 v3; // [sp+125h] [bp-3h]@1
  char v4; // [sp+127h] [bp-1h]@1
  char v5; // [sp+1Dh] [bp-10Bh]@1

  v1 = dword_8048737;
  v2 = dword_804873B;
  v3 = word_804873F;
  v4 = byte_8048741;
  fgets(&v5, 256, (FILE *)stdin);
  if ( strcmp(&v5, (const char *)&v1) )
  {
    puts("bzzzzzzzzap. WRONG");
  }
  else
  {
    puts("[You've got shell]!");
    seteuid(0x3ECu);
    system("/bin/sh");
  }
  return 0;
}

点开v1
.rodata:08048737 dword_8048737   dd 706C6E73h            ; DATA XREF: do_stuff+9 r
.rodata:0804873B dword_804873B   dd 746E6972h            ; DATA XREF: do_stuff+11 r
.rodata:0804873F word_804873F    dw 0A66h                ; DATA XREF: do_stuff+19 r
mysql出场了


mysql> select 0x706C6E73;
+------------+
| 0x706C6E73 |
+------------+
| plns       |
+------------+
1 row in set (0.00 sec)

mysql> select 0x746E6972;
+------------+
| 0x746E6972 |
+------------+
| tnir       |
+------------+
1 row in set (0.00 sec)

mysql> select 0x66;
+------+
| 0x66 |
+------+
| f    |
+------+
1 row in set (0.00 sec)

mysql> select reverse('ftnirplns');
+----------------------+
| reverse('ftnirplns') |
+----------------------+
| snlprintf            |
+----------------------+
1 row in set (0.00 sec)
密码是 ********

----------------------------------
5
这关是个算法题?
~/.Trash/bin

Ida pro 打开
int __cdecl main(char a1)
{
  int result; // eax@2
  FILE *v2; // eax@1
  int  r; // [sp+40h] [bp+0h]@1
  int v4; // [sp+3Ch] [bp-4h]@1
  char *v5; // [sp+30h] [bp-10h]@1
  FILE *v6; // [sp+20h] [bp-20h]@1
  unsigned int v7; // [sp+24h] [bp-1Ch]@3
  char v8; // [sp+2Fh] [bp-11h]@4
  signed int v9; // [sp+28h] [bp-18h]@4
  char *v10; // [sp+18h] [bp-28h]@11

  v4 =  r;
  v5 = &a1;
  v2 = fopen("/home/level6/.passwd", (const char *)&unk_80485E8);
  v6 = v2;
  if ( v2 )
  {
    fgets(buf, 256, v6);
    v7 = 0;
    while ( 1 )
    {
      v10 = buf;
      if ( v7 >= strlen(buf) - 1 )
        break;
      v8 = buf[v7];
      v9 = 0;
      while ( v9 <= 7 )
      {
        if ( v8 >= 0 )
          putchar(48);
        else
          putchar(49);
        v8 *= 2;
        ++v9;
      }
      putchar(32);
      ++v7;
    }
    result = putchar(10);
  }
  else
  {
    result = -1;
  }
  return result;
}
执行的结果是
00110110 01101100 01111001 01110110 01001100 01011000 01000011 01000001 00001010

做了个字典:
#include 
#include 

int main(char a1)
{
    char *buf = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int i=0;
    int t=0;
    char c;
    int j=0;

    for (; i<strlen(buf); i++, j++) {
        c = buf[i];
        printf("%c : ", c);
        t = 0;
        while ( t <= 7 ) {
            if ( c >= 0) {
                putchar('0');
            } else {
                putchar('1');
            }

            c *= 2;
            t++;
        }
        if (j%4 == 3) {
            printf(" \n");
        } else {
            printf(" \t");
        }
    }
}
/*
0 : 00110000    1 : 00110001    2 : 00110010    3 : 00110011
4 : 00110100    5 : 00110101    6 : 00110110    7 : 00110111
8 : 00111000    9 : 00111001    0 : 00110000    a : 01100001
b : 01100010    c : 01100011    d : 01100100    e : 01100101
f : 01100110    g : 01100111    h : 01101000    i : 01101001
j : 01101010    k : 01101011    l : 01101100    m : 01101101
n : 01101110    o : 01101111    p : 01110000    q : 01110001
r : 01110010    s : 01110011    t : 01110100    u : 01110101
v : 01110110    w : 01110111    x : 01111000    y : 01111001
z : 01111010    A : 01000001    B : 01000010    C : 01000011
D : 01000100    E : 01000101    F : 01000110    G : 01000111
H : 01001000    I : 01001001    J : 01001010    K : 01001011
L : 01001100    M : 01001101    N : 01001110    O : 01001111
P : 01010000    Q : 01010001    R : 01010010    S : 01010011
T : 01010100    U : 01010101    V : 01010110    W : 01010111
X : 01011000    Y : 01011001    Z : 01011010
*/
查表
00110110 01101100 01111001 01110110 01001100 01011000 01000011 01000001 00001010
********
----------------------------------
6
signed int __cdecl main(char a1)
{
  int  r; // [sp+230h] [bp+0h]@1
  int v3; // [sp+22Ch] [bp-4h]@1
  char *v4; // [sp+224h] [bp-Ch]@1
  char *v5; // [sp+18h] [bp-218h]@1
  int v7; // [sp+24h] [bp-20Ch]@5
  int v8; // [sp+1Ch] [bp-214h]@6

  v3 =  r;
  v4 = &a1;
  v5 = &a1;
  if ( a1 <= 1 )
  {
    puts("*** File Printer ***");
    printf("Usage: %s filename\n", **((_DWORD **)v5 + 1));
    return -1;
  }
  if ( access(*(const char **)(*((_DWORD *)v5 + 1) + 4), 0) )
  {
    puts("You cant have that file...");
    return 1;
  }
  snprintf((char *)&v7, 0x1FFu, "/bin/cat %s", *(_DWORD *)(*((_DWORD *)v5 + 1) + 4));
  system((const char *)&v7);
  return v8;
}
/var/tmp下有几个symlink,但是直接读还是不行。看了看命令的构造,很显然,需要从文件名上动手。
本想自己弄,结果发现/var/tmp下有了

level6@leviathan:/var/tmp$ /wargame/printfile ./\|cat\ /home/level7/.passwd
/bin/cat: ./: Is a directory
********

----------------------------------
7
level7@leviathan:/wargame$ ./sphinx
usage: ./sphinx <4 digit code>
level7@leviathan:/wargame$ ./sphinx 1111
Wrong
第一眼,想写个代码遍历 - -|||

IDA Pro

int __cdecl main(char a1)
{
  int result; // eax@5
  int  r; // [sp+30h] [bp+0h]@1
  int v3; // [sp+2Ch] [bp-4h]@1
  char *v4; // [sp+24h] [bp-Ch]@1
  char *v5; // [sp+10h] [bp-20h]@1
  int v6; // [sp+20h] [bp-10h]@1

  v3 =  r;
  v4 = &a1;
  v5 = &a1;
  v6 = 7123;
  if ( a1 != 2 )
  {
    printf("usage: %s<4 digit code>\n", **((_DWORD **)v5 + 1));
    exit(-1);
  }
  if ( atoi(*(const char **)(*((_DWORD *)v5 + 1) + 4)) == v6 )
  {
    seteuid(0x3EFu);
    result = system("/bin/sh");
  }
  else
  {
    result = puts("Wrong");
  }
  return result;
}

level7@leviathan:/$ /wargame/sphinx 7123
sh-3.1$ id
uid=1006(level7) gid=1006(level7) euid=1007(level8) groups=1006(level7)
脑残了,居然不相信7123是密码

啊啊啊啊a

********
----------------------------------

level8@leviathan:~$ cat CONGRATULATIONS
Well Done, you seem to have used *nix system before, now try something more serious. Your completion string is "Unix is easy!!!".

Saturday, June 12, 2010

使用mklink修改chrome/firefox缓存目录到ramdisk

Windows 7下,mklink为cmd的内部命令,参数如下

Creates a symbolic link.

MKLINK [[/D] | [/H] | [/J]] Link Target

/D Creates a directory symbolic link. Default is a file
symbolic link.
/H Creates a hard link instead of a symbolic link.
/J Creates a Directory Junction.
Link specifies the new symbolic link name.
Target specifies the path (relative or absolute) that the new link
refers to.



创建Ramdisk,分区符定为 Z:
建立目录 Z:\Chrome\Cache。
Administrator模式开启CMD,进入 C:\Users\sskaje\AppData\Local\Google\Chrome\User Data\Default
执行
mklink /J Cache z:\Chrome\Cache


social.microsoft.com的一帖子说,Junction 和 Symlink 区别在于,前者系统认为是一个实际目录,后者认为是一个快捷方式,但是貌似这个说法还不是很正确,没空研究,爱怎怎地吧。

效率考虑,我ramdisk的数据不往硬盘上同步,这就意味着重启系统,Z盘的这些目录就被干掉了。
于是开机就需要执行一次mkdir建目录。

代码如下

SET RAMDISKDRV=Z:

SET CHROME_CACHE_DIR="%USERPROFILE%\AppData\Local\Google\Chrome\User Data\Default\Cache"
SET CHROME_RAMDISK_DIR="%RAMDISKDRV%\Chrome\Cache"

rd /s /q %CHROME_CACHE_DIR%
mkdir %CHROME_RAMDISK_DIR%
mklink /J %CHROME_CACHE_DIR% %CHROME_RAMDISK_DIR%


如果嫌这样麻烦,可以改Chrome的启动参数,加 --user-data-dir=Z:\Chrome\Cache

Firefox直接 用 about:config 添加 browser.cache.disk.parent_directory 指到 Z:\Firefox 即可。

Friday, April 30, 2010

autossh + plink

/root/bin/plinkssh.sh:

#!/bin/sh
export AUTOSSH_PATH=`which plink`
`which autossh` -M 5122 -N -R 12345:127.0.0.1:22 -pw blahblah root@10.***.***.*** 2>&1 >/dev/null &
chattr +i /root/bin/plinkssh.sh
/etc/rc.local:
/bin/sh /root/bin/plinkssh.sh

Monday, March 29, 2010

关于Libmemcached 编译

上一篇相关的,/2010/02/libmemcached-undefined-reference-to.html
这个是在CentOS 5.4上的编译。

但是这次是在 CentOS 4.6 上。GCC 版本 3.4.6。用上篇提供的方法,依旧是错。
这里测试了 0.37 和 0.38 两个版本。
编译参数如下:
./configure  --with-libevent-prefix=/usr/local/libevent --with-memcached --disable-64bit CFLAGS="-O3 -march=i686"


CCLD   clients/memslap
clients/ms_conn.o(.text+0x24b): In function `ms_conn_close':
clients/ms_conn.c:685: undefined reference to `__sync_fetch_and_sub'
clients/ms_conn.o(.text+0xc8e): In function `ms_setup_conn':
clients/ms_conn.c:380: undefined reference to `__sync_fetch_and_add'
clients/ms_conn.o(.text+0x11af): In function `ms_reconn_socks':
clients/ms_conn.c:1051: undefined reference to `__sync_fetch_and_add'
clients/ms_conn.o(.text+0x1351): In function `ms_verify_value':
clients/ms_conn.c:1808: undefined reference to `__sync_fetch_and_add'
clients/ms_conn.o(.text+0x13f8):clients/ms_conn.c:1767: undefined reference to `__sync_fetch_and_add'
clients/ms_conn.o(.text+0x17f4): In function `ms_build_udp_headers':
clients/ms_conn.c:194: undefined reference to `__sync_fetch_and_add'
clients/ms_conn.o(.text+0x1d46):clients/ms_conn.c:919: more undefined references to `__sync_fetch_and_add' follow
collect2: ld returned 1 exit status
make[2]: *** [clients/memslap] Error 1
make[2]: Leaving directory `/data2/space/libmemcached-0.38'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/data2/space/libmemcached-0.38'
make: *** [all] Error 2


和上次的错误略有不同。但是出错都是在 memslap 这个文件的编译上。
往上翻了翻,看到,所有的Warnings和Errors都是在 clients 下的文件编译上,而libmemcached 和 libhashkit 没问题。

于是,解决方案自然是把 clients 下所有的应用程序的编译干掉。
一开始直接去改configure出来的Makefile,太费劲了。于是只得去改 Makefile.am 然后去 automake 重新生产 Makefile.in
但是 CentOS 4.6 和 5.4 的两个系统的 autoconf 都是 2.59 的版本,改过的 Makefile.am
autoconf
aclocal.m4:20: warning: this file was generated for autoconf 2.63.
You have another version of autoconf.  It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically `autoreconf'.
configure.ac:16: error: Autoconf version 2.62 or higher is required
aclocal.m4:530: AM_INIT_AUTOMAKE is expanded from...
m4/pandora_canonical.m4:246: PANDORA_CANONICAL_TARGET is expanded from...
configure.ac:16: the top level
autom4te: /usr/bin/m4 failed with exit status: 63


换到一台 Ubuntu Lucid Lynx 的机器上,autoconf 2.69, 修改 Makefile.am,注释掉了
include clients/include.am
include tests/include.am
include example/include.am
include support/include.am


重新 aclocal; autoreconf; automake .
打包扔到开发机上,编译顺利完成。


另外,下午验证了,libmemcached 0.38 + pecl memcached 1.0.1 在 CentOS 4.6上也出现了连接无法结束的情况/2010/03/tests-about-using-tokyotyrant-with-pecl.html

Tests about using tokyotyrant with pecl memcached.

CentOS: 5.4
PHP: 5.2.13
Apache: 2.2.14
TokyoTyrant: 1.1.39

libmemcached pecl memcached result
0.37 1.0.0 works perfectly.

0.37 1.0.1 works perfectly on cli mode
zend_mm_heap corrupted in apache log(non-debug php compilation)
apache SegFaults (php --enable-debug)

0.37 1.0.2(?) works perfect.
======================================================================

0.38 1.0.0 Cannot disconnect after script execution

0.38 1.0.1 Cannot disconnect after script execution

0.38 1.0.2(?) Cannot disconnect after script execution


1.0.2(?) is the github version from http://github.com/tricky/php-memcached by Teddy(commit 078544ae226313dbdf56775bc004136c7e6b6e28).

Friday, March 12, 2010

php 处理 <? 的错误。

刚才 @iammutex 同学写 prepare 方法的时候,把一个数组dump出来,结果发现var_dump输出被截断。
大致是 array('uid=?', 'fid<?'); 的

写了个测试代码
<?php
$array = array('1', '2', '3<? 1+1', '4>111?');
var_dump($array);


猜猜输出是啥

cli 模式下:

0> /data4/space/bin/php test.php
array(4) {
[0]=>
string(1) "1"
[1]=>
string(1) "2"
[2]=>
string(7) "3<? 1+1"
[3]=>
string(6) "4>111?"
}


嗯,没有问题。

但是。。。 Apache + php ...

array(4) {
[0]=>
string(1) "1"
[1]=>
string(1) "2"
[2]=>
string(7) "3111?"
}



LOL ...

PS: 测试环境 php5.3.1 + apache 2.2.14
看到这个的人来验证一下其他的版本或者其他的httpd吧。

pecl memcached 处理默认参数就是一悲剧

这错误也怪我,之前在这个扩展的不同的地方栽了一下,后来还没注意。
代码贴在了 pastebin.com 。。。 可怜的 pastebin.ca,也在墙外了。。。

虽然贴了代码,还是有必要简要提一下,要不然哪天pastebin.com也在墙外了。
<?php
class spdLibMemcached {
private $mc;
public function __construct($persistent_id=null) {
$this->mc = new Memcached($persistent_id);
}
public function addServer($host, $port, $weight=null) {
return $this->mc->addServer($host, $port, $weight);
}
public function getServerList() {
return $this->mc->getServerList();
}
}


$a = new memcached;
$b = new memcached;
$a->addServer('10.1.0.55', 22201);
$b->addServer('10.1.3.52', 11211);

$c = new spdlibmemcached;
$d = new spdlibmemcached;
$c->addServer('10.1.0.55', 22201);
$d->addServer('10.1.3.52', 11211);

var_dump(
$a->getServerList(),
$b->getServerList(),
$c->getServerList(),
$d->getServerList()
);

你会发现 $c 和 $d 取得的结果是 。。。 两个值。。。。

刚才还去麻烦小天天看代码,也是没看出问题。

最后索性继续精简代码,写了个最简单的例子,居然是好的。一细看:
new Memcached; 和 new Memcached(null);
根本不是一回事。。。

于是构造的时候必须

<?php
class spdLibMemcached {
private $mc;
public function __construct($persistent_id=null) {
if ($persistent_id === null) {
$this->mc = new Memcached;
} else {
$this->mc = new Memcached($persistent_id);
}
}
}



继续改其他的去。。。