关于12306第三方软件检测研究

转载请注明出处:http://blog.csdn.net/tang9140/article/details/42869269

首先申明下,本文章纯作为个人喜好的技术性研究,请不要用于非法操作盈取不正当利益,你懂的。

问题引出

大概在2014年12月的17、18号,大量针对12306的刷票软件出现了非法请求或‘使用第三方购票软件’提示,并且验证码识别出错。后面得知12306为了对恶意抢票软件、插件进行遏制与防控,通过手段能识别出抢票软件和用户购票的行为差别。

那么问题来了。。。,我就很好奇,他们通过什么手段做到的呢。

补充下,我之前是有做过JAVA版的抢票软件,仅做学习用途,没其它目的。在12306加了第三方软件检测后,我的刷票软件同样出现了‘非法请求’提示,连登录都进不去。于是引发了我的思考。

抢票软件本质是什么?

我个人认为抢票软件无非是用机器人(实际上所谓的机器人就是一个软件,这里只是形象表述)代替实体人去购买车票。大家都知道机器反应快,因此在同时开抢的前提下,肯定是机器比实体人快,这也是为什么黄牛党能买到车票,而你买不到车票的原因。继续往下思考,为什么机器人能够代替实体人去进行买票的一系列操作?这就引出了下一个问题。

用户在浏览器上购买车票的一系列操作,从技术角度看,本质上发生了什么?

大家都知道,12306网站提供的是基于B/S架构的WEB服务,是建立在http协议之上的服务。http协议是典型的请求-应答模式的协议,是无状态的协议。实际上用户在浏览器上的所有操作,最终都是依托浏览器发送请求到服务端,服务端接收到请求后进行相应的业务处理并将响应结果返回到浏览器端,浏览器再显示给用户。更具体点说,浏览器接收用户的各种事件(例如鼠标单击事件,键盘输入事件)后,然后在后台发送http请求到服务端,同时浏览器后台会接收到服务端的响应内容并展示为HTML页面。

更一般化,对于服务器来说,只要收到的请求是符合HTTP协议的就会进行处理,它不关心请求是通过浏览器发送过来的还是刷票软件发送过来的(浏览器本身也是一个软件,一般操作系统都自带浏览器)。从上面分析可以看出,只要第三方软件去完全模拟浏览器发送符合规范的HTTP请求,WEB服务器就会当作合法的请求并进行处理。那么问题又来了,为什么刷票软件会出现“非法请求”提示呢,这不跟你刚才的分析有矛盾吗?这个嘛,其实不矛盾。请注意我刚才说的是“完全模拟浏览器”,之前作为学习版的抢票软件并没有严格按照浏览器方式去发送HTTP请求,这也就导致12306能通过一些技术手段检测出非法的请求。那么,引出了我们的终极问题

12306怎么进行技术检测的,怎么区别正常请求跟非法请求?

在回答这个问题前,大家先要了解下HTTP协议。HTTP请求消息分为四部分:请求行、请求头、空行、可选的请求消息体;共有八种请求方法,最常用的就两种:GET请求和POST请求。GET请求将参数带在URL后面(没有消息体),而POST请求将参数带在消息体中。请求头中可能包含Cookie信息等。回到正题,12306对于非法请求检测无非对三方面进行检查,即请求头、Cookie、请求参数。

12306检测三方面:

1、请求头

在模拟HTTP请求时,需要注意请求头的顺序。经过本人测试,如果登录请求的‘Cookie请求头’放置在‘Connection请求头’后面时,会提示‘非法请求’

2、Cookie

Cookie请求头同样要注意顺序,先是JSESSIONID(中间其它cookie)最后是BIGipServerotn,current_captcha_type

3、请求参数

还是顺序的问题,请严格参照浏览器发送请求参数的顺序进行发送。除此之外,12306在登录和提交订单时还增加了动态key验证。你需要先获取到动态js文件的url地址,然后访问该js文件内容,提取出其中的key值,并用该js文件中的加密算法对key加密后形相应的value值。

通过上述三方面的检测,就能够发现一些非法请求。当然有第三方软件检测,也就有反检测。在12306推出这一系列第三方软件检测、监控及更换验证码(我相信有三套不同的验证码)后的数小时,一些刷票软件就进行了破解。其实本人觉得所谓的第三方软件检测完全是治标不治本,就好像游戏中的反外挂检测一样,在强大的外挂研发人员看来,完全不堪一击。不管你加了多少次验证,不管你怎么更换验证规则,都会很快的被破解。我倒觉得,与其把时间花在怎么反外挂上,还不如内部提供外挂,借鉴游戏的做法,12306倒不如内部提供一键抢票功能,不需要经过选车次,选乘客,提交下单验证码,再次确认等复杂过程。直接按照用户事先设定好的规则一键抢票(需要保证用内部一键抢票功能比外部刷票软件抢到票的概率要高),这样还来得实在些,你怎么看呢。支持的赞个

码字不易,源头来自http://blog.csdn.net/tang9140。

另附上关于KEY加密算法的JAVA版实现代码,感兴趣的同学可以看下

public class DynamicJsUtil {

    private static String keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

    static class Base32 {

        private static int delta = 0x9E3779B8;

        public static String longArrayToString(int[] data, boolean includeLength) {
            int length = data.length;
            int n = (length - 1) << 2;
            if (includeLength) {
                int m = data[length - 1];
                if ((m < n - 3) || (m > n))
                    return null;
                n = m;
            }

            StringBuilder sb = new StringBuilder("");
            for (int i = 0; i < length; i++) {
                int i0 = data[i] & 0xff;
                int i8 = data[i] >>> 8 & 0xff;
                int i16 = data[i] >>> 16 & 0xff;
                int i24 = data[i] >>> 24 & 0xff;
                if (i0 != 0)
                    sb.append((char)i0);
                if (i8 != 0)
                    sb.append((char)i8);
                if (i16 != 0)
                    sb.append((char)i16);
                if (i24 != 0)
                    sb.append((char)i24);
            }

            String result;
            if (includeLength) {
                result = sb.substring(0, n);
            }
            else
                result = sb.toString();
            return result;
        }

        public static int[] stringToLongArray(String str, boolean includeLength) {
            int length = str.length();
            int arrsize = length % 4 == 0 ? length / 4 : length / 4 + 1;
            int[] result = new int[arrsize];
            for (int i = 0; i < length; i += 4) {
                if (i + 4 > length) {
                    int char8 = i + 1 >= length ? 0 : str.charAt(i + 1) << 8;
                    int char16 = i + 2 >= length ? 0 : str.charAt(i + 1) << 16;
                    int char24 = i + 3 >= length ? 0 : str.charAt(i + 1) << 24;
                    result[i >> 2] = str.charAt(i) | char8 | char16 | char24;
                }
                else
                    result[i >> 2] =
                        str.charAt(i) | str.charAt(i + 1) << 8 | str.charAt(i + 2) << 16 | str.charAt(i + 3) << 24;
            }
            if (includeLength) {
                int[] newArr = new int[arrsize + 1];
                System.arraycopy(result, 0, newArr, 0, arrsize);
                newArr[arrsize] = length;
                result = newArr;
            }
            return result;
        }

        public static String encrypt(String str, String key) {
            if (str == "") {
                return "";
            }
            int[] v = stringToLongArray(str, true);
            int[] k = stringToLongArray(key, false);
            if (k.length < 4) {
                int[] newArr = new int[4];
                System.arraycopy(k, 0, newArr, 0, k.length);
                k = newArr;
            }
            int n = v.length - 1;
            int z = v[n], y = v[0];
            int mx, e, p, sum = 0;
            int q = 6 + 52 / (n + 1);
            while (0 < q--) {
                sum = sum + delta & 0xffffffff;
                e = sum >>> 2 & 3;
                for (p = 0; p < n; p++) {
                    y = v[p + 1];
                    mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
                    z = v[p] = v[p] + mx & 0xffffffff;
                }
                y = v[0];
                mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
                z = v[n] = v[n] + mx & 0xffffffff;
            }
            return longArrayToString(v, false);
        };
    }

    public static String encode32(String input) {
        input = escape(input);
        StringBuilder output = new StringBuilder();
        int length = input.length();
        int chr1, chr2, chr3;
        int enc1, enc2, enc3, enc4;
        int i = 0;
        do {
            chr1 = input.charAt(i++);
            enc1 = chr1 >> 2;
            output.append(keyStr.charAt(enc1));

            if (i < length) {
                chr2 = input.charAt(i++);
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                output.append(keyStr.charAt(enc2));

                if (i < length) {
                    chr3 = input.charAt(i++);
                    enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                    enc4 = chr3 & 63;
                    output.append(keyStr.charAt(enc3)).append(keyStr.charAt(enc4));
                }
                else {
                    enc3 = ((chr2 & 15) << 2) | (0 >> 6);
                    output.append(keyStr.charAt(enc3)).append(keyStr.charAt(64));
                }
            }
            else {
                enc2 = ((chr1 & 3) << 4) | (0 >> 4);
                output.append(keyStr.charAt(enc2)).append(keyStr.charAt(64)).append(keyStr.charAt(64));
            }
        } while (i < length);
        return output.toString();
    }

    static String bin216(String s) {
        s += "";
        String output = "";
        int l = s.length();
        for (int i = 0; i < l; i++) {
            char c = s.charAt(i);
            String temp = Integer.toString(c, 16);
            output += temp.length() < 2 ? "0" + temp : temp;
        }
        return output;
    }

    public static String escape(String src) {
        char j;
        StringBuffer tmp = new StringBuffer(src.length() * 2);
        for (int i = 0; i < src.length(); i++) {
            j = src.charAt(i);
            if (Character.isDigit(j) || Character.isLowerCase(j) || Character.isUpperCase(j))
                tmp.append(j);
            else if (j < 256) {
                if (j == '*' || j == '@' || j == '-' || j == '_' || j == '+' || j == '.' || j == '/') {
                    tmp.append(j);
                }
                else {
                    tmp.append("%");
                    if (j < 16)
                        tmp.append("0");
                    tmp.append(Integer.toString(j, 16).toUpperCase());
                }
            }
            else {
                tmp.append("%u");
                tmp.append(Integer.toString(j, 16));
            }
        }
        return tmp.toString();
    }

    /**
     * 获取动态加密value值
     * @param key
     * @return
     */
    public static String getRandomParamValue(String key) {
        return encode32(DynamicJsUtil.bin216(Base32.encrypt("1111", key)));
    }
}
时间: 2024-10-07 14:33:11

关于12306第三方软件检测研究的相关文章

基于深度学习的目标检测研究进展

前言 开始本文内容之前,我们先来看一下上边左侧的这张图,从图中你看到了什么物体?他们在什么位置?这还不简单,图中有一个猫和一个人,具体的位置就是上图右侧图像两个边框(bounding-box)所在的位置.其实刚刚的这个过程就是目标检测,目标检测就是"给定一张图像或者视频帧,找出其中所有目标的位置,并给出每个目标的具体类别". 目标检测对于人来说是再简单不过的任务,但是对于计算机来说,它看到的是一些值为0~255的数组,因而很难直接得到图像中有人或者猫这种高层语义概念,也不清楚目标出现在

基于虚拟数据的行人检测研究(Recognition in-the-Tail:Training Detectors for Unusual Pedestrians with Synthetic Imposters)

Paper Link : https://arxiv.org/pdf/1703.06283 Github: https://github.com/huangshiyu13/RPNplus 摘要:这篇paper探索了如何用虚拟数据或者叫做人工生成的数据对行人检测进行辅助的方式.通过Unity3D产生虚拟数据,然后用RPN进行训练,再在真是数据上进行finetue,能提高检测器的鲁棒性.

车道线检测文献解读系列(一) 基于机器视觉的高速车道标志线检测算法的研究_李晗

作者背景 基于机器视觉的高速车道标志线检测算法的研究_李晗 东北大学车辆工程硕士学位论文 2006年 [GB/T 7714]李晗. 基于机器视觉的高速车道标志线检测算法的研究[D]. 东北大学, 2006. DOI:10.7666/d.y852642.` 论文结构一览 预处理 灰度化 [亮点]模式判别 选择日间模式还是夜间模式: 在每个检测周期开始时,首先判断采用日间模式还是夜间模式工作.摄像机视野中的上半部分为天空背景,天空亮度可以显著区分日间和夜间环境.由于天空的颜色为蓝离,日间天空的蓝色分

行人检测

最近一直在看行人检测的论文,对目前的行人检测做大概的介绍. 行人检测具有极其广泛的应用:智能辅助驾驶,智能监控,行人分析以及智能机器人等领域.从2005年以来行人检测进入了一个快速的发展阶段,但是也存在很多问题还有待解决,个人觉得主要还是在性能和速度方面还不能达到一个权衡. 1.行人检测的现状(大概可以分为两类) (1).基于背景建模:利用背景建模方法,提取出前景运动的目标,在目标区域内进行特征提取,然后利用分类器进行分类,判断是否包含行人: 背景建模目前主要存在的问题:(背景建模的方法总结可以

智能视频监控中的遗留物或搬移物检测

智能视频监控中的遗留物或搬移物检测 [email protected] http://blog.csdn.net/kezunhai 遗留物或搬移物检测是智能视频监控中的一项基本功能,基本上是智能视频监控领域的必备功能.然而,在实际应用中漏报或误判率依然很高.常见的遗留物或搬移物检测算法主要分为两类,一类是先检测,再根据检测前景在场景中的停留时间来判定是否为遗留物或搬移物:另一类,则是先检测,然后采用跟踪方法来判断是否为遗留物或搬移物,该类方法由于目标跟踪本身存在的难点,在实际应用的场景受到了很大

行人检测(Pedestrian Detection)资源整理

一.论文 综述类的文章: [1] D. Geronimo, and A. M.Lopez. Vision-based Pedestrian Protection Systems for Intelligent Vehicles, BOOK, 2014. [2] P.Dollar, C. Wojek,B. Schiele, et al. Pedestrian detection: an evaluation of the state of the art [J].IEEE Transactions

行人检测1(总结)

最近一直在看行人检测的论文,对目前的行人检测做大概的介绍. 行人检测具有极其广泛的应用:智能辅助驾驶,智能监控,行人分析以及智能机器人等领域.从2005年以来行人检测进入了一个快速的发展阶段,但是也存在很多问题还有待解决,个人觉得主要还是在性能和速度方面还不能达到一个权衡. 1.行人检测的现状(大概可以分为两类) (1).基于背景建模:利用背景建模方法,提取出前景运动的目标,在目标区域内进行特征提取,然后利用分类器进行分类,判断是否包含行人: 背景建模目前主要存在的问题:(背景建模的方法总结可以

cs231n学习笔记-CNN-目标检测

1. 基本概念 1)CNN:Convolutional Neural Networks 2)FC:Fully Connected 3)IoU:Intersection over Union (IoU的值定义:Region Proposal与Ground Truth的窗口的交集比并集的比值,如果IoU低于0.5,那么相当于目标还是没有检测到) 4)ICCV:International Conference on Computer Vision 5)R-CNN:Region-based Convol

行人检测 综述 (6)

由于课程作业,总结下近期国内关于行人检测的文献,虽然是2013 2014年写的,但是综述的内容还都是经典的东西.作为浏览回顾就行. 许腾,黄铁军,田永鸿 . 车载视觉系统中的行人检测技术综述[J]. 中国图象图形学报,2013,18( 4) : 359 -367. 本文对 2005 年以来该技术中最重要的两个环节---感兴趣区域分割以及目标识别的研究现状进行综述. 1 ROIs ( regions of interest) 分隔 ROIs的分隔方法主要有这五类:1. 基于运动 2. 基于立体视觉