JS逆向之基础定位技巧

篇幅有限

完整内容及源码关注公众号:ReverseCode,发送

当我们拿到一个网站时,首先就是抓包定位加密参数的实现,本文将通过常用的定位方案结合实际案例完成对加密参数的分析。

搜索关键参数

这是最常见也是最简单的定位方案,F12打开网站控制台后,Ctrl+Shift+F打开搜索面板,比如搜索password参数或者submit函数

password:`,`password=`,`password =`请求url,搜索方法`var submit`或者`function submit`或者`submit:

to8to

图片

搜索password位置太多,由于抓包请求是new_login.php

图片

在Element面板中搜索new_login.php,ctrl+shift+f 搜索loginCheck

图片

该方法中jq('#rsa_userNum').val(rsaString(password));,调用rsaString方法加密password

function rsaString(str) {
    return encodeURIComponent(RSAUtilszb.encryptfun(str));
}

进入encryptfun定义的js中,rsa加密最少2000行,该方法不过163行,拷贝该js通过编程猫专用工具中的JS调试工具,加载代码,报错引用错误: window 未定义,添加var window = this;,报错引用错误: JSEncrypt 未定义,添加原js中var JSEncrypt = JSEncryptExports.JSEncrypt;,报错引用错误: JSEncryptExports 未定义,搜索var JSEncryptExports,将var JSEncryptExports = {};添加到JS调试工具,报错类型错误: JSEncrypt is not a constructor

图片

尝试打上断点,但是每次都不能进入断点,说明肯定是动态加载的js,且每次刷新js后缀会有时间戳。勾选Disable cache,打开fiddler抓包,将js拷贝到本地实现http欺骗,选中该请求点击AutoResponder-Add Rule下拉选择Find a File,找到本地保存的js并开启规则

图片

由于每次js请求地址不一样,使用正则匹配regex:https://static\.to8to\.com/gb_js/to8torsaszb\.js\?_=\d+并保存规则重新发起请求https://static.to8to.com/gb_js/to8torsaszb.js?_=1628128571412,使用本地js欺骗网络请求js

图片
图片

将整个js格式化找到之前报错JSEncrypt is not a constructor是从上面的压缩的js中export出来的

图片

将上面压缩的代码添加到编程猫的JS调试工具中加载代码,报错引用错误: navigator 未定义,添加var navigator = {},报错引用错误: window 未定义,添加var window =this,因为如果用window ={}报错ASN1 未定义,而用this则可以拿到当前js中所有的变量函数。

图片

dom元素事件监听

通过控制台的Elements中的Event Listeners逐个排除按钮的Remove节点,直到最后一个Event Listeners使按钮无效,拿到该按钮真正生效的js位置。

中烟新商盟

图片

以下通过dom元素事件监听实现对该j_mcmm加密参数逻辑定位分析。

图片

以上通过尽可能多的地方打上断点,监听元素事件定位到jsmain-9826b285f8fad5a5.js,左下角格式化js后添加断点,在js页面ctrl查看所有变量值

图片

鼠标悬停,或者控制台打印出来,点击进入方法声明时打上断点,为同一行中的函数打上断点,F8单步调试,完成加密参数的定位

图片

xhr断点

通过定位发包函数跟栈,复制网址请求路径到Sources下的XHR/fetch Breakpoints,支持正则。

七麦数据

图片

通过关键加密参数analysis搜索无果,尝试在Sources中加入XHR断点,以请求路径作为断点内容

图片

XHR断点后追溯调用栈,查看每个调用栈的出入参是否包含加密后的analysis,直到进入Promise异步l.request,单步调试到n.then(t.shift(), t.shift()),then作为Promise的异步函数,promise.then(onCompleted, onRejected);,而shift()通过逐条调用t中的方法,参数是上一个方法的返回值,同时删除该方法,相当于队列先进先出。控制台打印t,逐个方法进入打上断点

图片

l.prototype.request中暂时还没生成analysis加密参数,逐个进入t方法中

图片

执行完r().interceptors.request.use该方法后生成的a就是analysis,观察该代码中的逻辑完成加密分析。a=(0,n.cv)((0,n.oZ)(r, l))作为逗号表达式,由上图分析n.cv和n.oZ是函数,r和l是变量,可以还原为n.cv(n.oz(r,l))

通过控制台获取n中的函数

window = global;
window.document = {
    cookie: ''  // 这边带上自己的cookie
}
function i(e) {
    var t, a = (t = "",
        ["66", "72", "6f", "6d", "43", "68", "61", "72", "43", "6f", "64", "65"].forEach((function (e) {
                t += unescape("%u00" + e)
            }
        )),
        t);
    return String[a](e)
}
function s() {
    return unescape("861831832863830866861836861862839831831839862863839830865834861863837837830830837839836861835833".replace(/8/g, "%u00"))
}
var n = {
    oZ: function g(e, t) {
        t || (t = s());
        for (var a = (e = e.split("")).length, n = t.length, o = "charCodeAt", r = 0; r < a; r++)
            e[r] = i(e[r][o](0) ^ t[(r + 10) % n][o](0));
        return e.join("")
    },
    cv: function h(e) {
        return function (e) {
            try {
                return btoa(e)
            } catch (t) {
                return Buffer.from(e).toString("base64")
            }
        }(encodeURIComponent(e).replace(/%([0-9A-F]{2})/g, (function (e, t) {
                return i("0x" + t)
            }
        )))
    },
    ej: function u(e) {
        var t, a = new RegExp("(^| )" + e + "=([^;]*)(;|$)");
        return (t = document.cookie.match(a)) ? unescape(t[2]) : null
    }
}

由于btoa本质就是base64加密,通过引入CryptoJS.pad.js后

function base64(data) {
 var wordArray = CryptoJS.enc.Utf8.parse(data);
 var base64_data = CryptoJS.enc.Base64.stringify(wordArray);
 return base64_data
}
var n = {
    oZ: function g(e, t) {
        t || (t = s());
        for (var a = (e = e.split("")).length, n = t.length, o = "charCodeAt", r = 0; r < a; r++)
            e[r] = i(e[r][o](0) ^ t[(r + 10) % n][o](0));
        return e.join("")
    },
    cv: function h(e) {
        return function (e) {
            try {
                return base64(e)
            } catch (t) {
                return Buffer.from(e).toString("base64")
            }
        }(encodeURIComponent(e).replace(/%([0-9A-F]{2})/g, (function (e, t) {
                return i("0x" + t)
            }
        )))
    },
    ej: function u(e) {
        var t, a = new RegExp("(^| )" + e + "=([^;]*)(;|$)");
        return (t = document.cookie.match(a)) ? unescape(t[2]) : null
    }
}

将try/catch中逻辑还原

var l = "00000008d78d46a"
var d = "@#"
var e = {
    // url: "/rank/indexPlus/brand_id/1",
    url: "/rank/indexPlus/brand_id/" + pg,    // 1.免费榜 0.付费 2.畅销榜
    baseURL: "https://api.qimai.cn",
}
var c = {
    default: function On(e) {
        this._init(e)
    }
}
var u = "synct"
var t = (0, n.ej)(u);
var m = "syncd"
var f = f = c.default.prototype.difftime = -(0, n.ej)(m) || +new Date - 1e3 * t
var o = +new Date - (f || 0) - 1515125653845
var r = []
r = r.sort().join(""), r = (0, n.cv)(r), r += d + e.url.replace(e.baseURL, ""), r += d + o, r += d + 1,
a_ = (0, n.cv)((0, n.oZ)(r, l))
return a_
图片

Initiator栈追踪

Network下的发包请求的Initiator,如jquery堆栈的顶层断点(可能会请求多次,找到发包请求时进入的断点),重新请求找到堆栈中属于目标网站的js格式化断点。

升学e网通

登录抓包,打开Initiator,进入堆栈顶层定位的代码行

图片

打上断点,查看右侧调用栈,逐个方法往底层去调用,直到react库js找到了preLogin,找到出现password的位置,打上断点

图片

再次登录时,进入断点,找到password

图片

进入加密方法中,aes加密

图片

打开WT-JS中的Crypto类复制key和iv,输出以HEX的十六进制格式,对比结果是标准的AES加密。

图片

基于base64或十六进制的AES加解密实现见aes.js

图片

长房集团

图片

搜索j_password后打断点,重新登录

图片

进入desEncrypt中,大致加密完成逻辑就在该函数中

图片

加密逻辑中首先根据SECURITYKEY.get()获取到key,首先通过请求后端拿到str,判断加密类型是否为aes后截取字符串通过toHexString转成十六进制拿到key和iv和security

图片

整理完逻辑扣出js报错CryptoJS is not defined,点击进入CryptoJS.AES.encrypt扣出来aes.js源码

function getdes(encodeType) {
    // 请求"/resource/js/session.jsp?_=1628210376229"返回
    var str = "E55A433905551AC39DB3165591D9CD74";
    if (encodeType == null || encodeType == 'aes') {
        if (str.length < 32) {
            str += "abcdefghijklmnopqrstuvwxyz1234567890"
        }
        str = str.toUpperCase();
        var key = {};
        key.key = str.substring(0, 16);
        key.iv = str.substring(16, 32);
        key.security = "\u4435\u5320\u4d35"
    } else {
        if (str.length < 16) {
            str += "abcdefghijklmnopqrstuvwxyz"
        }
        str = str.toUpperCase();
        var key = {};
        key.key = toHexString(str.substring(0, 8));
        key.iv = toHexString(str.substring(8, 16));
        key.security = "\u4445\u5320\u4d45"
    }
    return key
}
function getPwd(value, type) {
    var keyObj = {};
    if (type == null || "aes" == type.toLowerCase()) {
        keyObj = getdes()
        value = CryptoJS.AES.encrypt(value, CryptoJS.enc.Utf8.parse(keyObj.key), {
            iv: CryptoJS.enc.Utf8.parse(keyObj.iv)
        }).toString()
    } else {
        keyObj = getdes()('des');
        value = CryptoJS.DES.encrypt(value, CryptoJS.enc.Hex.parse(keyObj.key), {
            iv: CryptoJS.enc.Hex.parse(keyObj.iv)
        })
    }
    return keyObj.security + value
}
图片

安装编程猫插件

  • fiddler 版本必须 >= v4.6.3,复制Fiddler 编程猫专用插件到fiddler程序目录下的Scripts目录中示例: C:\Program Files (x86)\Fiddler2\Scripts

爱奇艺

  1. 覆盖原函数
  function xxx(){
      console.log("1111")
  }
  var xxx_ = xxx;
  xxx = function(){
      console.log("2222")
  }
  window.alert = function(){console.log("?")}
  console.clear = function(){console.log("?")}
  setInterval = function(){}
  1. Object.defineProperty替换对象属性(getter.setter)
  (function () {
      var a = "";
      Object.defineProperty(document, 'cookie', {
          set: function (val) {
              console.log('Hook捕获到cookie设置->', val);
              a = val;
              return val;
          },
          get: function(){
              return a;
          }
      });
  })();
  document.cookie = "1"  // 设置
  document.cookie  // 获取
  1. hook的时机在控制台注入的hook,刷新网页就失效了,过滤Network的js找到第一个加载的js,右键Open in Sources panel格式化,第一行断点,不过有些cookie可能异步可能在html中js生成,在控制台中注入以上hook,清除cookie,手动注入hook,控制台中找到VM虚拟机找到我们的hook的js打上断点,,每次hook都会经过set,右侧就可以查看调用栈,追溯cookie的来源与加密方式。(有可能注入hook的时机会晚于部分异步请求或者html中的js)
图片
  1. 利用fiddler代理所有请求替换响应,编程猫专用工具注入hook
图片
(function () {
    'use strict';
    Object.defineProperty(document, 'cookie', {
        set: function (val) {
            if (val.indexOf("__dfp") != -1) {
                debugger;
            }
            console.log('Hook捕获到cookie设置->', val);
            return val;
        }
    });
})();
图片

接下来查看调用栈,最终保存到window.name中。

(function () {
    'use strict';
    var a = "";
    Object.defineProperty(window, 'name', {
        set: function (val) {
            debugger;
            a = val;
            console.log('Hook捕获到cookie设置->', val);
            return val;
        }, 
        get: function(){
            return a;
        }
    });
})();

重新进入iqiyi,断点完成hook定位从而可以根据调用栈分析cookie的生成逻辑。

本文由博客群发一文多发等运营工具平台 OpenWrite 发布

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容