热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

百度登录加密协议分析(下)

上一篇百度登录加密协议分析(上)主要讲解了codestring,gid,token,rsakey等参数的产生。好了,废话不多说,咱们进入今天的主题,咱们接着上一篇的内容往下讲解,最

  上一篇百度登录加密协议分析(上)主要讲解了codestring,gid,token,rsakey等参数的产生。好了,废话不多说,咱们进入今天的主题,咱们接着上一篇的内容往下讲解,最后还剩三个字段 callback,password,ppui_logintime

技术分享

  第三部分:    

  分析第一次post已经产生,第二次post内容发生变化的字段
    callback
    password
    ppui_logintime
  
   通过之前的分析,可以了解到callback 可能没啥用,所以放到后面再分析。
   一般来说password的是最难分析的,所以也放到后面分析。
    
  3.1  接下来分析ppui_logintime,依旧是搜索ppui_logintime,在下面链接中找到了ppui_logintime的出处
    http://passport.bdimg.com/passApi/js/login_tangram_a829ef5.js
技术分享
  
  找到了 timeSpan: ‘ppui_logintime‘,接着搜索timespan
 
技术分享
 
  
   找到了 r.timeSpan = (new Date).getTime() - e.initTime,
 
   接着搜索initTime技术分享
 
  咱们看一下_initApi什么时候调用的,通过搜索找到以下代码:
  
_eventHandler: function() {
    var e,
        t = {
            focus: function(t) {
                var n = e.fireEvent(‘fieldFocus‘, {
                    ele: this
                });
                n && (this.addClass(e.constant.FOCUS_CLASS), this.removeClass(e.constant.ERROR_CLASS), baidu(e.getElement(t + ‘Label‘)).addClass(e.constant.LABEL_FOCUS_CLASS))
            },
            blur: function(t) {
                var n = e.fireEvent(‘fieldBlur‘, {
                    ele: this
                });
                n && (this.removeClass(e.constant.FOCUS_CLASS), baidu(e.getElement(t + ‘Label‘)).removeClass(e.constant.LABEL_FOCUS_CLASS))
            },
            mouseover: function() {
                var t = e.fireEvent(‘fieldMouseover‘, {
                    ele: this
                });
                t && this.addClass(e.constant.HOVER_CLASS)
            },
            mouseout: function() {
                var t = e.fireEvent(‘fieldMouseout‘, {
                    ele: this
                });
                t && this.removeClass(e.constant.HOVER_CLASS)
            },
            keyup: function() {
                e.fireEvent(‘fieldKeyup‘, {
                    ele: this
                })
            }
        },
        n = {
            focus: {
                userName: function() {
                    e.config.loginMerge && e.getElement(‘loginMerge‘) && (e.getElement(‘loginMerge‘).value = ‘true‘, e.getElement(‘isPhone‘).value = ‘‘)
                },
                password: function() {
                    e._getRSA(function(t) {
                        e.RSA = t.RSA,
                            e.rsakey = t.rsakey
                    })
                },
                verifyCode: function() {}
            },
            blur: {
                userName: function() {},
                password: function(t) {
                    var n = this.get(0).value;
                    n.length && e.validate(t)
                },
                verifyCode: function(t) {
                    var n = this.get(0).value;
                    n.length && e.validate(t)
                }
            },
            change: {
                userName: function() {
                    var t = this.get(0).value;
                    e._loginCheck(t)
                },
                verifyCode: function() {}
            },
            click: {
                verifyCodeChange: function(t, n) {
                    e.getElement(‘verifyCode‘).value = ‘‘,
                        e._doFocus(‘verifyCode‘),
                        e.getVerifyCode(),
                        n.preventDefault()
                }
            },
            keyup: {
                verifyCode: function() {
                    var t = e.getElement(‘verifyCode‘),
                        n = baidu(t);
                    t.value && 4 == t.value.length ? e._asyncValidate.checkVerifycode.call(e, {
                        error: function(t) {
                            n.addClass(e.constant.ERROR_CLASS),
                                e.setGeneralError(t.msg)
                        },
                        success: function() {
                            n.removeClass(e.constant.ERROR_CLASS),
                                e.clearGeneralError()
                        }
                    }) : e.$hide(‘verifyCodeSuccess‘)
                }
            },
            submit: function(t) {
                e.submit(),
                    t.preventDefault()
            }
        };
    return {
        entrance: function(i) {
            e = this;
            var r = (baidu(i.target), i.target.name);
            if (!r && i.target.id) {
                var o = i.target.id.match(/\d+__(.*)$/);
                o && (r = o[1])
            }
            r && (t.hasOwnProperty(i.type) && t[i.type].apply(baidu(i.target), [
                r,
                i
            ]), n.hasOwnProperty(i.type) && (‘function‘ == typeof n[i.type] && n[i.type].apply(baidu(i.target), [
                i
            ]), n[i.type].hasOwnProperty(r) && n[i.type][r].apply(baidu(i.target), [
                r,
                i
            ])), e.initialized || ‘focus‘ != i.type || e._initApi())
        }
    }
}(),
  通过分析上面的js代码可以看出来,发生点击,内容改变,按键按下等事件可能会调用initApi()。
 
  通过上面的代码我们可以知道ppui_logintime是从你输入登录信息,一直到你点击登录按钮提交的这段时间,
 
  因此我们通过之前的抓包,直接把ppui_logintime的值保存下来即可。
  
  3.2 接着咱们分析callback,看看这字段到底是干什么用的(最后发现没啥用,和上一篇得出来的推断差不多)。搜索callback,红色标注的地方是不是和post出去的内容有重复。
 
  技术分享
    
callback =‘bd__cbs__‘+Math.floor(2147483648 *Math.random()).toString(36)
   这个时候callback的生成当时也就确定了
 
  3.3 最后分析password的加密方式:搜索password,发现敏感内容。 
  http://passport.bdimg.com/passApi/js/login_tangram_a829ef5.js
 
技术分享
 
技术分享
 
  通过下断点,动态调试可以知道password,是通过公钥pubkey对密码进行加密,最后输出进行base64编码
上图的xn()就是在进行base64编码。
 
   如果大家对Javascript的RSA加密不熟悉,可以推荐看一下                         https://github.com/travist/jsencrypt/blob/master/src/jsencrypt.js。
等你看完了这个开源项目,你会发现,百度使用的RSA加密函数和上面的连命名几乎一样,这也就是为什么能这么快分析出RSA加密的原因。
 
  3.4  如何使用python进行RSA加密
采用的是RSA加密方式:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
password = xxxxxxxx
with open(pub.pem) as f:
    pubkey = f.read()
    rsakey = RSA.importKey(pubkey)
    cipher = PKCS1_v1_5.new(rsakey)
    cipher_text = base64.b64encode(cipher.encrypt(password))
    print cipher_text

  3.5 由于之前安装了pyv8,所以不把gid,callback等js函数翻译成python了,翻译过来也很简单,如果你电脑上没装pyv8,就试着翻译一下。

    function callback(){
        return ‘bd__cbs__‘+Math.floor(2147483648 * Math.random()).toString(36)

    }
    function gid(){
        return ‘xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx‘.replace(/[xy]/g, function (e) {
        var t = 16 * Math.random() | 0,
        n = ‘x‘ == e ? t : 3 & t | 8;
        return n.toString(16)
        }).toUpperCase()

    }

  3.6 似乎还有验证码没说,其实就是两个链接,一个是获取验证码的链接,一个是检测验证码是否正确的链接。验证码获取很简单,这里就不详细说了。下面我会把整个登录的源代码,贴在下面有兴趣的,可以去玩一下。

总结: 

  下面我用python模拟了一下登录,使用了requests和pyv8(其实想偷懒),代码如下:

#coding:utf-8
import base64
import json
import re
from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA
import PyV8

import requests
import time

if __name__==__main__:
    s = requests.Session()
    s.get(http://yun.baidu.com)
    js=‘‘‘
    function callback(){
        return ‘bd__cbs__‘+Math.floor(2147483648 * Math.random()).toString(36)

    }
    function gid(){
        return ‘xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx‘.replace(/[xy]/g, function (e) {
        var t = 16 * Math.random() | 0,
        n = ‘x‘ == e ? t : 3 & t | 8;
        return n.toString(16)
        }).toUpperCase()

    }
    ‘‘‘
    ctxt = PyV8.JSContext()
    ctxt.enter()
    ctxt.eval(js)
    ###########获取gid#############################3
    gid = ctxt.locals.gid()
    ###########获取callback#############################3
    callback1 = ctxt.locals.callback()
    ###########获取token#############################3
    tokenUrl="https://passport.baidu.com/v2/api/?getapi&tpl=netdisk&subpro=netdisk_web&apiver=v3"              "&tt=%d&class=login&gid=%s&logintype=basicLogin&callback=%s"%(time.time()*1000,gid,callback1)

    token_response = s.get(tokenUrl)
    pattern = re.compile(r"token"\s*:\s*"(\w+)")
    match = pattern.search(token_response.text)
    if match:
        token = match.group(1)

    else:
        raise Exception
    ###########获取callback#############################3
    callback2 = ctxt.locals.callback()
    ###########获取rsakey和pubkey#############################3
    rsaUrl = "https://passport.baidu.com/v2/getpublickey?token=%s&"              "tpl=netdisk&subpro=netdisk_web&apiver=v3&tt=%d&gid=%s&callback=%s"%(token,time.time()*1000,gid,callback2)
    rsaResponse = s.get(rsaUrl)
    pattern = re.compile("\"key\"\s*:\s*‘(\w+)‘")
    match = pattern.search(rsaResponse.text)
    if match:
        key = match.group(1)
        print key

    else:
        raise Exception
    pattern = re.compile("\"pubkey\":‘(.+?)‘")
    match = pattern.search(rsaResponse.text)
    if match:
        pubkey = match.group(1)
        print pubkey

    else:
        raise Exception
    ################加密password########################3
    password = xxxxxxx‘#填上自己的密码
    pubkey = pubkey.replace(\\n,\n).replace(\\,‘‘)
    rsakey = RSA.importKey(pubkey)
    cipher = PKCS1_v1_5.new(rsakey)
    password = base64.b64encode(cipher.encrypt(password))
    print password
    ###########获取callback#############################3
    callback3 = ctxt.locals.callback()
    data={
        apiver:v3,
        charset:utf-8,
        countrycode:‘‘,
        crypttype:12,
        detect:1,
        foreignusername:‘‘,
        idc:‘‘,
        isPhone:‘‘,
        logLoginType:pc_loginBasic,
        loginmerge:True,
        logintype:basicLogin,
        mem_pass:on,
        quick_user:0,
        safeflg:0,
        staticpage:http://yun.baidu.com/res/static/thirdparty/pass_v3_jump.html,
        subpro:netdisk_web,
        tpl:netdisk,
        u:http://yun.baidu.com/,
        username:xxxxxxxxx,#填上自己的用户名
        callback:parent.+callback3,
        gid:gid,ppui_logintime:71755,
        rsakey:key,
        token:token,
        password:password,
        tt:%d%(time.time()*1000),


    }
    ###########第一次post#############################3
    post1_respOnse= s.post(https://passport.baidu.com/v2/api/?login,data=data)
    pattern = re.compile("codeString=(\w+)&")
    match = pattern.search(post1_response.text)
    if match:
    ###########获取codeString#############################3
        codeString = match.group(1)
        print codeString

    else:
        raise Exception
    data[codestring]= codeString
    #############获取验证码###################################
    verifyFail = True
    while verifyFail:
        genimage_param = ‘‘
        if len(genimage_param)==0:
            genimage_param = codeString

        verifycodeUrl="https://passport.baidu.com/cgi-bin/genimage?%s"%genimage_param
        verifycode = s.get(verifycodeUrl)
        #############下载验证码###################################
        with open(verifycode.png,wb) as codeWriter:
            codeWriter.write(verifycode.content)
            codeWriter.close()
        #############输入验证码###################################
        verifycode = raw_input("Enter your input verifycode: ");
        callback4 = ctxt.locals.callback()
        #############检验验证码###################################
        checkVerifycodeUrl=https://passport.baidu.com/v2/?                         checkvcode&token=%s                         &tpl=netdisk&subpro=netdisk_web&apiver=v3&tt=%d                         &verifycode=%s&codestring=%s                         &callback=%s%(token,time.time()*1000,verifycode,codeString,callback4)
        print checkVerifycodeUrl
        state = s.get(checkVerifycodeUrl)
        print state.text
        if state.text.find(u验证码错误)!=-1:
            print 验证码输入错误...已经自动更换...
            callback5 = ctxt.locals.callback()
            changeVerifyCodeUrl = "https://passport.baidu.com/v2/?reggetcodestr"                                   "&token=%s"                                   "&tpl=netdisk&subpro=netdisk_web&apiver=v3"                                   "&tt=%d&fr=login&"                                   "vcodetype=de94eTRcVz1GvhJFsiK5G+ni2k2Z78PYRxUaRJLEmxdJO5ftPhviQ3/JiT9vezbFtwCyqdkNWSP29oeOvYE0SYPocOGL+iTafSv8pw"                                   "&callback=%s"%(token,time.time()*1000,callback5)
            print changeVerifyCodeUrl
            verifyString = s.get(changeVerifyCodeUrl)
            pattern = re.compile("verifyStr"\s*:\s*"(\w+)")
            match = pattern.search(verifyString.text)
            if match:
            ###########获取verifyString#############################3
                verifyString = match.group(1)
                genimage_param = verifyString
                print verifyString

            else:
                verifyFail = False
                raise Exception

        else:
            verifyFail = False
    data[verifycode]= verifycode
    ###########第二次post#############################3
    data[ppui_logintime]=81755
    post2_response = s.post(https://passport.baidu.com/v2/api/?login,data=data)
    if post2_response.text.find(err_no=0)!=-1:
        print 登录成功

    else:
        print 登录失败

  我把整个代码上传到git上了:https://github.com/qiyeboy/baidulogin.git,大家可以star和fork。


  今天的分享就到这里,如果大家觉得还可以呀,记得推荐呦。


欢迎大家支持我公众号:   
 
技术分享

本文章属于原创作品,欢迎大家转载分享,禁止修改文章的内容。尊重原创,转载请注明来自:七夜的故事 http://www.cnblogs.com/qiyeboy/

  
  

百度登录加密协议分析(下)


推荐阅读
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
author-avatar
semb
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有