善意提醒

如果您打开本站很慢,布局排版混乱,并且看不到图片,那么可能是因为您还没有掌握用科学的方法上网的本领。

2012-10-25

头一次埋彩蛋哩

在改某个 Bug 的时候,需要通过已知的 HWND 来判断该子窗口是不是一个 DateTimePicker 控件。办法很简单,只要给这个子窗口发一个 DateTimePicker 控件的特定消息,如果返回值正确,那么就认为猜对了。

但是在选择这个「特定消息」的时候,我犯了难。DateTimePicker 支持的消息本来就不多。为了避免对正在编辑的数据带来扰动,只能选择 GETXXX 之类的消息。并且这个消息还得有定义良好的返回值,用于鉴别消息是不是发对了人。

一开始我选的是 DTM_GETSYSTEMTIME,这货能返回当前编辑的时间。但我发现控件一旦响应了这个消息,键盘输入就像按过回车一样被 COMMIT 过了,导致年份之类的多位数字根本输不完整。回头看了看 DTM_GETMCCOLOR,又无法确定返回值是不是能够鉴别出来。有个 DTM_GETDATETIMEPICKERINFO 倒是看上去挺好,可惜只支持 VISTA 往上。最后我选择了 DTM_GETRANGE。

DTM_GETRANGE 可以返回设计者在 IDE 上给 DateTimePicker 控件定下的最大/最小值。所以我只要把最小值设一个特定的日子,然后看看返回值正不正确就 OK。实验下来对输入也没有扰动,是个很好的选择。那么,日子选哪一天呢?

……我敲入了 1989-06-04

2012-10-19

一个关于浮点运算的坑

这两天遇到一个很奇怪的问题,后来通过排除法终于把故障点缩小到一句代码,但还是很奇怪:

某个同事以前用 VC 写了一个 DLL 用于提供某类通用计算,相当于一个计算模块,由我这边写 BCB 程序来调用。不过在这次的问题中发现,一旦线程调用过 pow() 这个 C 库函数来计算过一个非整数指数的幂,那么这个计算模块接下来同样的参数再次用就会得出不一样的结果。调用过 pow() 之前是一种结果,调用之后是另一种结果,现象相当稳定。

诡异就诡异在 pow() 的返回值既没有错,也没有被采用过。比如仅仅一句:
pow(2.0, 3.1);
函数返回值根本没有用来干过任何事情,相当于直接丢掉了。那么按道理来说这行代码应该不会对之前或之后的代码造成任何影响。但它确确实实影响了。
然而指数如果是整数,就不会,比如:
pow(2.0, 3.0);

那个写 DLL 的同事对此也是一头雾水。怀疑点一度被放在 VC / BCB 身上,因为它们的 CRT 不一样。但因为正好有一个测试工具,稍加改造便可以针对计算过程输出详细的结果报表,因此用来比较了一下,发现了问题:出问题的地方,有两个本来应该用来比较的 double 型输入数据正好是相等的。在 pow() 调用之前,它们的确被判断为相等。但调用 pow() 之后,计算结果显示它们被判断为不相等了。

这种事情对于常写浮点运算相关代码的程序员而言是很容易引起警惕的。浮点数不能直接用 == 之类来比较,必须去判断两数相减的绝对值是否小于某个精度。所以将这个测试结果提交给写 DLL 的同事之后,很快就定位并解决了问题。
但这里我更关注的是以下几个问题:

  1. pow(2.0, 3.0) 和 pow(2.0, 3.1) 的不同,使我相信 CRT 肯定对前者作了优化。这种事情,想得通,但后果可能是个坑。
  2. 很明显,CRT 在 pow() 被调用之后,处理浮点数时的行为模式改变了。通过观察 _statusfp() 的返回值,我发现调用前为 0,调用后变成了 0x20。但这个 0x20 是一个 Undocumented 的东西,哪怕是最新的 MSDN 上也找不到。并且我通过 _fpreset() 将状态字变回了 0,但计算模块仍然会出错,说明应该还有别的东西也被改了。这个坑是不是 VC 和 BCB 联合挖的,目前还不知道。
  3. 如果不知道有这个坑,就可能导致一些大麻烦。在特定的代码逻辑中,这个问题很难通过黑盒测试发现。它可能在很长一段时间内都能工作得很好,直到某一次有个用户算了一个指数带小数点的幂,然后……一切就不一样了。这简直就是逻辑炸弹嘛!让我想起了《深渊上的火》里面可怜的蓝荚和绿茎。
  4. MSDN 真心不是完全靠得住的。

在本文最后,还要对提供过重要帮助的 Libin Yan 表示感谢!并感谢所有关注和评论过这个 PO 的 G+ 网友!

2012-10-12

黑客故事 2:关于弱口令设备的闲话

家庭路由器都有 HTTP 的管理界面的。不过我估计给这些人家布网络的人大概都很不耐烦,因为那些用着 admin / admin 这样密码的路由器,80 或者 8080 端口就这样开在 WAN 口上,在自己附近的网段上完全是一扫一大片。另外,早期家庭路由器大概厂商这边投入也不够,大部分都偷懒把 PPPoE 密码写在了网页上。据我当时看下来,什么网件、D-Link、腾达、贝尔,统统中招。唯一做了防范措施的,是 TP-Link,这导致我之后买路由器时几乎没有考虑过别的任何牌子。

话说这 PPPoE 密码我轻易拿到了一大批。要是到了坏人手里头,互联星空就有得赚了。不过我系好人呀,好人我只是去电信营业厅看了看大家的住址,从来没用来干过坏事。不过就算不知道 PPPoE 的密码,也能干坏事。断网什么的都是小事。当年还没有 360,一台没打过补丁的 Ghost 机器被 DMZ 暴露在外网,会出现什么情况?

这些年,中国电信大力推广它的定制路由器。无论是管理密码还是无线安全都得到了加强,从这方面我觉得还是应该予以正面的肯定。不过我还是嫌它太烦,让它下岗了。用刀能自杀,也能防身。既然我不是小白,就不需要给我学生剪刀玩吧。

专业级的设备,比如机架式的路由器、防火墙,铁皮壳子,比家用路由器那种塑料玩意儿容易让人觉得可靠。呵呵,看起来可靠,于是某些人就不设密码,23 端口开着就往外网上放。哎呀,这种设备要是被扫到那能怪谁?其实我很多次都怀疑这些统统都是蜜罐。23端口开着,弱口令,轻易就能拿到最高权限。哦,被我第一个找到?这世上有这么好的事情?

当然,从这些设备上的情况看来,它们只是被当作傻瓜型设备在使用。SSH 没开就不用说了,连私钥都没有,说明 SSH 从来就没开起来用过。日志也是只开了默认的 syslogd。有个单位的硬件千兆防火墙连一条 IP 过滤规则都没有配,完全当路由器在用,下面接的 PC 却只有五台。但即使这样,我也只是逛了逛,浏览并学习了一下这些网络设备的命令和界面,除了清掉日志停掉 syslogd,不敢做任何改动。没必要自己给自己找麻烦,对吧?

我想说的是,这些设备,完全可能成为僵尸网络的一部分。不要以为只有电脑才能被僵尸网络控制。防火墙不好好设置,比 PC 还残,关键是你根本想不到它会成为威胁的一部分。为了维护自己网络的安全,你可能还离不开它,但真正的内鬼其实就是它。甚至也许都不是防火墙、路由器。交换机?也有可能。但问一句,你们单位有摄像头吗?

黑客故事 1:图形验证码上的 SQL 注入点

有一次,遇到有人在街上发宣传材料。本以为是什么餐馆开张之类,拿回家一看才发现是个类似网络传销的东西。我当然无意参加,但既然是「网络」传销,就必定得有网站和服务器,于是打开电脑看了看。

网站挺简单,就是一些宣传页。但我觉得肯定得有后台系统。果然没多久就被我找到了,PHP + MySQL 做的。登录输用户名+密码,另外还有图形验证码,没有 SQL 注入漏洞。看来开发者对登录这块还是相当注意的。又看了几个页面,还是没有注入点。我想撤,但又有点儿不甘心。向这种非法组织的服务器下手,基本上是没有法律风险的。他们显然不敢报警。所以我又回到登录界面上。在看 HTML 代码的时候,图形验证码那部分吸引了我。

我发现他们的图形验证码没有用 Session 来控制,用的是 URL 传参,GET 方式。也就是说在 URL 中给出了一个数字,然后服务器上返回一个生成的验证码图片。我在表单中也找到了那个数字,试着刷新了一下页面,数字加了 1。用旧的 URL 还是能拿回原来那幅图片,但一旦该图片中的验证码被「使用」过,就不能再拿到了。

这意味着什么?这意味着验证码与这个数字之间有某种联系,某种对应关系。这个联系可能是 Hash 出来的,但根据现象,更可能是存下来的。我决定赌一把。果然,验证码图片的 URL 存在 SQL 注入点。每次读出来的是存起来的四个字母,然后交给 PHP 生成图片。

也许开发者认为,就算被注入,入侵者面对着四个字母的图片也干不了什么破坏。但他可能忘了有 MID 这个函数。我利用这个漏洞确认了 admin 这个用户名确实存在,也拿到了 Hash 过的密码。不过接下来我收手了。我想再潜伏一段时间再动手。结果一个月之后,我再去看的时候,这个服务器已经不在了,网站也不在了。大概被警方打掉了吧,可惜!

在这个故事里面,我想强调的是,SQL 注入每一个地方都要防!哪怕再小的漏洞,也能造成危害,一个都不能疏忽。