V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
这是一个专门讨论 idea 的地方。

每个人的时间,资源是有限的,有的时候你或许能够想到很多 idea,但是由于现实的限制,却并不是所有的 idea 都能够成为现实。

那这个时候,不妨可以把那些 idea 分享出来,启发别人。
Evi1m0
V2EX  ›  奇思妙想

12306 售票网站新版验证码识别对抗

  •  1
     
  •   Evi1m0 · 2015-03-17 18:24:50 +08:00 · 7251 次点击
    这是一个创建于 3540 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前文

    近段时间 12306 订票网站验证码升级为用户识别图像内容,然后选取符合条件的图片为验证码,比如这样:

    http://ww1.sinaimg.cn/large/c334041bgw1eq8ijbaf75j209806xaaj.jpg

    不少媒体新闻大呼抢票工具集体失效、12306终极验证码等新闻,这种验证码的推出有好同样也有坏处:机器识别困难,同样人眼识别也轻松不到哪里去。

    用这种方式作为验证码最大的担忧就是怕脚本或人工对其图片进行爬虫遍历,然后将所有的图片保存后与关键字进行对比并关联入库,当然前提是这些图片都是静态的。

    12306 验证码究竟是静态还是动态,昨晚对这个疑问进行了实践:http://linux.im/2015/03/17/12306-captcha-md5-go.html ,简单的说测试后发现这整张图片是在服务器后端动态生成的,所以不难理解为什么生成验证码页面时会比较慢。

    同样上午我们又进行了第二个实践,将整张验证码中的八张图像拆分为8张小图然后进行感知hash处理,获得样本总数72225张,不重复的图库为15478张,重复最高为869次,绘制成图如下:

    http://ww1.sinaimg.cn/large/c334041bgw1eq8v4upbm6j217o0l0q5m.jpg

    既然不是静态的图像(对比过近10w条图像hash),那我们就不浪费功夫爬取静态图片进行数据关联入库了,但我们仍然需要“破”掉这个验证码,没有什么理由。

    最后,下文出现的所有的片段代码将会开源,无需担心。

    关键字识别

    验证码流程:

    • 验证码提问
    • 选择答案(多选)

    例如上面的验证码图,他是一整张图片,识别其关键字首先要对关键字区域进行图像截取,随后识别成文字。

    这里使用 Python 的 PIL 图像处理库来进行区域的选择:

    def imgCut():
        pic_file = downloadImg()
        pic_path = "./12306_pic/%s.jpg" % pic_file
        pic_text_path = './12306_pic/%s_text.jpg' % pic_file
        pic_obj = Image.open(pic_path)
        box = (120,0,290,25)
        region = pic_obj.crop(box)
        region.save(pic_text_path)
        print '[*] Picture Text Picture: {}'.format(pic_text_path)
        return pic_path, pic_text_path
    

    imgGut函数首先会下载这张验证码大图(其中包括提示字、关键字、8张图片等),然后保存至 ./12306_pic/ 目录进行存储,随后使用 PIL 库对图像的 (120,0,290,25) 区域切割,也就是获取关键字图像区域。

    http://ww3.sinaimg.cn/large/c334041bgw1eq8uap1ub5j20hp0cjq43.jpg

    现在我们已经能够将验证码下载并切割出想要的关键字区域了,下面我们要识别关键字,然后转换为文本文字。

    使用一些开源的光学字符识别模块应该就能进行识别,但这不方便使用者运行,所以我选择了一款在线网站OCR识别,他能够对你上传的图像(我们刚刚切割好的图像)进行文字识别转换,当然准确率并没有那么高,一定得记住这一点!

    这里贴出部分代码,功能实现(传入图像返回关键字的文本内容):

    upload_pic_url = "http://cn.docs88.com/pdftowordupload2.php"
    filename_tmp = filename.split('/')[-1]
    pic_text_content = open(filename).read()
    para = {'Filename': filename_tmp,
           'sourcename': filename_tmp,
           'sourcelanguage': 'cn',
           'desttype': 'txt',
           'Upload': 'Submit Query',}
    upload_pic = requests.post(upload_pic_url, data=para, files={"Filedata" : open(filename, 'rb')})
    text_result_url = 'http://cn.docs88.com/' + upload_pic.content[3:]
    text_result = requests.get(text_result_url)
    return text_result.content
    

    我们运行试试效果:

    [+] Download Picture: https://kyfw.12306.cn/otn/passcode...
    [*] Picture Text Picture: ./12306_pic/1426580454_text.jpg
    [*] Text: 衬 衫
    
    [+] Download Picture: https://kyfw.12306.cn/otn/passcod...
    [*] Picture Text Picture: ./12306_pic/1426580454_text.jpg
    [*] Text: )帽子
    
    [+] Download Picture: https://kyfw.12306.cn/otn/passcod...
    [*] Picture Text Picture: ./12306_pic/1426580454_text.jpg
    [*] Text: 春联
    

    效果还不错,足够我们测试使用,还记得他的准确率吗?

    巧妙的图像识别

    之前关于图像识别我在 Buzz 发表过相关文章:使用CloudSight API进行图像识别的Python脚本,这次我们不使用这个脚本,原因是虽然识别准确度较高但速度略慢,所以我并不是很钟爱这一套,恰巧知乎上有位朋友写了一篇利用百度识图来进行图像识别的文章及代码,Google识图当然也不错,但刚好在这我们会用到,所以不必纠结。

    1. 分割验证码图像
    2. 丢进百度识图API函数
    3. 返回百度识图结果

    横向两行,每行四个,然后对其进行图像识别并返回:

    dict_list = {}
    count = 0
    for y in range(2):
        for x in range(4):
            count += 1
            im2 = get_sub_img(pic_path, x, y)
            result = baidu_stu_lookup(im2)
            dict_list[count] = result
            print (y,x), result
    

    其中函数因文章长度原因暂不在这贴出,识别效果如下:

    (0, 0) 冰雕|建筑夜景
    (0, 1) 炸暑条|快餐
    (0, 2) 灯塔|高塔
    (0, 3) 汉堡|麦当劳薯条|开店
    (1, 0) 运动外套|防护服|运动服
    (1, 1) 银灰色|手机|移动版
    (1, 2) 标书制作|规划
    (1, 3) 手机
    

    好,现在我们能够识别出关键字,也能识别出验证码8个图像了,我们还需要机器帮助我们确认,究竟选择哪几个图。

    可能是它

    前面两次提到使用的 OCR 在线识别准确度并没有那么高,所以为了方便程序能够聪明的帮我们思考这道选择题,我们进行结果伪分词对比。

    首先将关键字进行拆分,然后循环对比结果,这样就能将未准确识别的文字忽略并识别相应识图结果,这里我将8个图像结果按照1-8区分,第一行从左到右(1-4),第二行(5-8):

    if captcha_text.strip() > 2:
        print '\n[*] Maybe the result of the:'
        maybe_result = []
        for v in dict_list:
            for c in range(len(unicode(captcha_text.strip(), 'utf8'))):
                text = unicode(captcha_text, 'utf8')[c]
                if text in dict_list[v]:
                    _str_res = '%s --- %s' % (v, dict_list[v])
                    maybe_result.append(_str_res)
        for r in list(set(maybe_result)):
            print r
    else:
        print '[-] False'
    

    好了,这样一来就算识别率没有那么高我们也能尽可能的将答案寻找出来了,看下效果:

    http://ww1.sinaimg.cn/large/c334041bgw1eq8ux0jpkoj20yz0qrn3f.jpg

    http://ww1.sinaimg.cn/large/c334041bgw1eq8uxfmz78j20p90igdj7.jpg

    未结束

    结束了吗? 其实没有。

    我们使用脚本进行了大量的测试,成功率可喜的足够令一些邪恶的人做些事儿了,但验证码对抗一直在进行,当然也越来越有趣:)

    文中完整代码链接:https://gist.github.com/Evi1m0/fbbdb1ba7c66cc4e1bb2

    32 条回复    2015-03-26 15:10:40 +08:00
    RIcter
        1
    RIcter  
       2015-03-17 18:25:45 +08:00   ❤️ 1
    想法以及实现很棒,就是 lz 头像挫了点
    wxg4net
        2
    wxg4net  
       2015-03-17 19:00:26 +08:00
    今天我也考虑这样做了呢,可有人比我动手早呢... 学习
    scusjs
        3
    scusjs  
       2015-03-17 19:13:41 +08:00   ❤️ 1
    正想at蘑菇君的,结果发现是本人发的,囧。。。
    jacy
        4
    jacy  
       2015-03-17 19:19:15 +08:00
    我这的已经出现两个词和不止8张图的情况了
    spance
        5
    spance  
       2015-03-17 19:31:10 +08:00 via iPhone   ❤️ 3
    不要为黑牛党铺路做贡献好不好。

    12306那么拼是为的什么?

    12306是便民公众服务,不要动不动就对抗,到头来损害的是大家的利益。
    FrankFang128
        6
    FrankFang128  
       2015-03-17 19:31:10 +08:00 via Android
    只要一直换验证码策略让别人来不及就好了
    Jelen
        7
    Jelen  
       2015-03-17 19:32:40 +08:00
    真棒!
    zhaiduo
        8
    zhaiduo  
       2015-03-17 19:38:56 +08:00 via Android
    要是图片加不同字体文字,就跪了
    zhouqian
        9
    zhouqian  
       2015-03-17 21:26:56 +08:00
    @spance +1
    aiguozhedaodan
        10
    aiguozhedaodan  
       2015-03-17 22:00:33 +08:00
    @spance +1
    社会的运行成本只会越来越高,我觉得这不是发扬geek精神的地方
    Cee
        11
    Cee  
       2015-03-17 22:08:47 +08:00
    @RIcter 沒有你的萌。
    Dongdong36
        12
    Dongdong36  
       2015-03-17 22:14:55 +08:00
    诶,12306也不容易
    xingzhi
        13
    xingzhi  
       2015-03-17 22:18:09 +08:00
    @spance 我能理解你的想法。

    但我个人认为,这正能促进了社会(技术)的进步么,把思路公布出来,促使12306去解决它,总比这思路被黄牛利用好。 唯一不妥的是应该学习漏洞公布流程,先修复后公布,但这也不算漏洞啦。
    ysz1996
        14
    ysz1996  
       2015-03-17 22:19:17 +08:00 via Android
    @spance
    @zhouqian
    @aiguozhedaodan 弄个后门什么的
    binux
        15
    binux  
       2015-03-17 22:40:35 +08:00
    @spance 就这样都能『破解』的,黄牛做不出来吗?别小看黄牛了。
    spance
        16
    spance  
       2015-03-17 23:17:27 +08:00
    @zhouqian
    @aiguozhedaodan
    @xingzhi
    就是,12306也不容易,所以多希望大家研究研究机器和真人的分辨技术,研究、开源、分享它才是我们要做的事情。还有那么多的劳动人民买不到票,而我们却在“研究着”没有营养的http post image的代码,这是何等的窘境啊。

    @binux
    不是怀疑黄牛的能力,我们没有能力打压黄牛,但我们有能力做的就是不为黄牛事业添砖加瓦。
    decken
        17
    decken  
       2015-03-17 23:31:36 +08:00 via Android
    网易火车票新版更新说可以识别了 但是现在是维护期 无法测试
    decken
        18
    decken  
       2015-03-17 23:34:22 +08:00 via Android
    我错了。只是支持显示出来而已
    不过应该也快了吧
    WildCat
        19
    WildCat  
       2015-03-17 23:36:38 +08:00 via iPhone
    kava
        20
    kava  
       2015-03-17 23:39:41 +08:00
    12306还是取消网上卖票算了,窗口排队更公平。
    omi4399
        21
    omi4399  
       2015-03-17 23:54:54 +08:00
    @RIcter 楼主这头像明显是巨硬 Xbox 虚拟人物截图,他们家的虚拟人物都很挫,不要在意这些细节。
    zddhub
        22
    zddhub  
       2015-03-18 09:00:35 +08:00 via iPhone
    会不会有人屏蔽百度识图
    DT27
        23
    DT27  
       2015-03-18 09:46:37 +08:00
    国家资源就这么被楼主浪费了。。。
    schezuk
        24
    schezuk  
       2015-03-18 09:47:52 +08:00
    @spance -1
    技术无过错,特别是对于孱弱的验证码,以『漏洞攻防』来揭露其不可靠非常有意义。
    goodan
        25
    goodan  
       2015-03-18 09:48:48 +08:00
    关于这个问题。
    如果分析和破解能够促进12306改进,那去分析、研究肯定是好的。
    但是谁知道天刹的12306的技术到底足不足以改进,或者他们的态度如何。。就很难估量。
    kinhit
        26
    kinhit  
       2015-03-18 11:52:42 +08:00
    买票难,归根结底还是在特殊的人流高峰期,社会资源和社会需求的严重不平衡造成的。在牛逼的验证码,也解决不了本质问题,记得去台湾座高铁的时候,人家的体验就和做地铁一样,什么验证码的无所谓。
    ulic95
        27
    ulic95  
       2015-03-18 12:03:44 +08:00
    高铁网还在50%的状态,再过几年吧。
    comanboy
        28
    comanboy  
       2015-03-18 12:05:01 +08:00
    @kinhit 這能和台灣比嗎?台灣才多少人,大陸這是億萬級別的人,在同一時間段,進行流動啊!當然像你所說的是社会资源和社会需求的严重不平衡造成的,但最主要的原因還是,區域發展不平衡造成的,你像平常,很多火車專列都是有空座位的。
    caiych
        29
    caiych  
       2015-03-18 13:47:14 +08:00
    @spance
    我觉得楼主这样的做法没有问题。
    对一个不合理的解决方案提出意见的方式也只能是这样了,不然估计会被评价为嘴巴选手或者You can you up之类。(当然就算提出了这个方案的不合理之处也会被说You can you up,这就是另一回事了)

    也许提交到乌云上可能更好一点,虽然确实已经有人这么做了。
    luckylion
        30
    luckylion  
       2015-03-18 13:52:51 +08:00
    这种只能识别单色背景图片,如果背景负责一样识别不出来
    chilaoqi
        31
    chilaoqi  
       2015-03-18 13:56:05 +08:00
    看了新闻之后 我第一反映就是前段时间不是在这里发了一个图片识别的API么?还是很准的。
    xlrtx
        32
    xlrtx  
       2015-03-26 15:10:40 +08:00
    好用的验证码插件这么多..为什么一定要自己写一个..
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1151 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 18:13 · PVG 02:13 · LAX 10:13 · JFK 13:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.