akamai3.0逆向分析超详细过程(包含AST解混淆部分)

一. 流程分析

目标网站:‘aHR0cHM6Ly93d3cuZGhsLmNvbS8=’

  1. 请求首页的搜索接口’/cn-zh/home/tracking.html’,会返回一段htnl,其中包含某个重要的js文件的url,这步先固定首页内容,使相关js文件等内容不变
  2. 提取第一步返回的jsurl,发送get请求,此时返回一段js代码及相关cookie,得使用这段返回的js代码生成我们重要的sensor_data参数
  3. 使用第二步生产的sensor_data参数及相关cookie对第一步提取的jsurl发送post请求,这里返回重要的_abck cookie
  4. 使用第三步的cookie去请求数据接口/utapi
    解题方式分2种,第一补环境,第二,扣纯算,此处围绕第二种扣纯算的目的(不包含tls指纹部分)
    此处也是我一步一步学习解AST的过程,如有不正确之处或者有什么好的建议,欢迎各位大佬批评指出!文章有时间会一点一点写,同时会直接发布半成品,如果还是半成品请忽视。
    当然,第一种补环境也会把我的补环境代码贴出来,当前补环境的问题:1. 返回的_abck参数虽然返回的是-1,但是也能正常请求拿到结果
    2.连续请求大概20次后就开始返回428了,过一段时间又好了,换ip应该会好点,还有个原因,要换浏览器指纹,这个是个很深的话题,朋友们各显神通吧,哈哈哈!!!由于是网站练习,没去换ip,这里把补环境的代码贴出来仅供参考,这篇文章主要是扣纯算。

1.1 补环境全部代码

说明:每个akamai的网站环境都有细微差别,当前只适应最上面的目标网站,请知悉!
首先放请求成功的截图:
在这里插入图片描述
在这里插入图片描述
可以看到在连续请求到第22次时返回428了,过一段时间就好了,感觉换ip可以过,不一定是tls的原因,这里没继续测试换ip那些了。下面是全部环境代码(去除了代理方法watch,大家替换成自己的就行):
tips:我用另一套补环境框架,补的跟这个不一样,按理说input标签需要补齐,补上另一套环境input标签内容:
在这里插入图片描述

(()=>{
    const origin_log = console.log;
    console_log = function(){
        return origin_log(...arguments)
    }
})();;;

let _toString = Function.prototype.toString;
Function.prototype.toString = function () {
    console_log(`[Hook] 获取函数源代码: ${this.name}`, this);
    // debugger
    return _toString.call(this);
};



Object.getOwnPropertyDescriptor_ = Object.getOwnPropertyDescriptor;
Object.getOwnPropertyDescriptor = function(obj, prop) {
    console_log(`[Hook] 获取属性描述符: ${String(prop)}`, obj);
    return Object.getOwnPropertyDescriptor_(obj, prop);
};safeFunction(Object.getOwnPropertyDescriptor);


Object.getPrototypeOf_ = Object.getPrototypeOf;
Object.getPrototypeOf = function(target) {
    var val = Object.getPrototypeOf_(target);
    val = watch(val,"getPrototypeOf原型");
    return val;
};safeFunction(Object.getPrototypeOf);



window = globalThis;
window.innerHeight = 1080;
window.outerHeight = 1080;
window.innerWidth = 1920;
window.outerWidth = 1920;
window.chrome = watch({},"window.chrome");
window.PointerEvent = makeFunction("PointerEvent");
window.addEventListener  = makeFunction("addEventListener");
window.DeviceOrientationEvent = makeFunction("DeviceOrientationEvent");
window.DeviceMotionEvent = makeFunction("DeviceMotionEvent");
window.TouchEvent = makeFunction("TouchEvent");
window.XMLHttpRequest = makeFunction("XMLHttpRequest");
window._sdTrace = '<init/>';
window.XMLHttpRequest.prototype.withCredentials = makeFunction("withCredentials");
window.XMLHttpRequest.prototype.open = makeFunction("open");
window.XMLHttpRequest.prototype.send = makeFunction("send");
window.HTMLElement = makeFunction("HTMLElement");
window.RTCPeerConnection = makeFunction("RTCPeerConnection");
window.webkitRTCPeerConnection = makeFunction("webkitRTCPeerConnection");
window.speechSynthesis = watch({getVoices:function (){return []},onvoiceschanged:function (){console_log("========notification onvoiceschanged 参数:",arguments);debugger}},"speechSynthesis");
window.PushManager = makeFunction("PushManager");
window.Notification = makeFunction("Notification");
window.TypeError = makeFunction("TypeError");
window.Symbol = makeFunction("Symbol");
window.Document = makeFunction("Document");
Document.prototype.hasPrivateToken = function () {
    console.log("==== document.hasPrivateToken 的参数是:", arguments);
    return true;
};
window.XPathResult = makeFunction("XPathResult");
window.FileReader = makeFunction("FileReader");
window.toString = function () {return "[object Window]"};
window.Symbol.toStringTag = "Symbol(Symbol.toStringTag)";
window.Symbol.iterator    = "Symbol(Symbol.iterator)";
location = {
    "ancestorOrigins": {},
    "href": "https://www.dhl.com/cn-zh/home/tracking/tracking-supply-chain.html?submit=1&tacking-id=1232343&tracking-id=66668888",
    "origin": "https://www.dhl.com",
    "protocol": "https:",
    "host": "www.dhl.com",
    "hostname": "www.dhl.com",
    "port": "",
    "pathname": "/cn-zh/home/tracking/tracking-supply-chain.html",
    "search": "?submit=1&tacking-id=1232343&tracking-id=66668888",
    "hash": "",
    toString: function () {
        return this.href;
    }
}
document = {
    location: location,
    currentScript:watch({
        // src:'https://www.dhl.com/XU21sK/Efhd/xFt/d42/aFbbLvihRfk/fuEaSkQw6Xppkcm9/VElEcw/axAfRk/Z3aRw',
        src:'_js_url',
        type:"text/javascript",
        toString: function () {
            return '[object HTMLScriptElement]';
        }
    },"currentScript"),
    getElementsByTagName: function(tag_name) {
        if (tag_name === "input") {
            return watch([],"getElementsByTagName_input");
        }
        debugger
    },
    getElementById: function(tag_name) {
        debugger
    },
    createElement: function(tag_name) {
        if (tag_name === "span") {
            return watch({
                style: watch({},"createElement_span_style"),
                nodeName:"SPAN"
            },"createElement_span");
        }
        if (tag_name === "p") {
            return watch({
                nodeType: 1,
            },"createElement_p")
        }
        if (tag_name === "div") {
            return watch({
                getElementsByTagName: function(tag_name) {
                    console_log("dopcument getElementsByTagName 监听事件:", tag_name)
                },
                ATTRIBUTE_NODE:2,
                baseURI:'https://www.dhl.com/cn-zh/home/tracking.html?submit=1&tacking-id=1232343&tracking-id=66668888',
            },"createElement_div");
        }

    },
    hidden:false,
    webkitHidden:false,
    addEventListener: function(event, callback) {
        console_log("dopcument addEventListener 监听事件:", event)
    },
    // cookie:'cookieDisclaimer=seen; OnetrustActiveGroups=%2CC0001%2C; OptanonConsent=isGpcEnabled=0&datestamp=Tue+Jul+08+2025+16%3A23%3A36+GMT%2B0800+(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4)&version=202411.1.0&browserGpcFlag=0&isIABGlobal=false&hosts=&consentId=da571e86-74b4-4620-b21d-b349c740f02b&interactionCount=1&isAnonUser=1&landingPath=https%3A%2F%2Fwww.dhl.com%2Fcn-zh%2Fhome%2Ftracking.html%3Fsubmit%3D1%26tacking-id%3D1232343%26tracking-id%3D66668888&groups=C0001%3A1%2CC0002%3A0%2CC0003%3A0%2CC0004%3A0; _abck=EDED0D701CA86B136225FCB7F826C17C~-1~YAAQLVCcJGZ2D8aXAQAABiAi6Q7XFIhT1VAnOGoGAhvwLwO1gCOovc97gGrNMhDcmK1fDBmBIUOPKJONfZU2RLJ+kcprqhBk+7JH5D6FFpyY9nTAethQ3Mu83ri4ZlT++tLRhO+YdbTdjYta/NnGshOrOxN4rtAWiDN7VAL1TLBO+pxawZ9jnMW7wvEhrzDiwahihiRbpLhXJpvsstRswyS3/TsLuc3zfPsvbiNR8pW3rfDU4sr0otpa1un+/mp/FMUtnNvFUl7MKu3KvKeONqqx34fhK/8WX5UIAwTTq+qdwGeAooHJR/0xMz+ljQxIjWnluPl/ZLN01NUojNfkFyidWCST3c6oAxh7H0JsJ5oqzzPAF6zrx0Y9/pwhzqiqHW70MNJY7q1K6zZlrzwD/1OIGqL9kxwa2v75Gxam/Dlmxgg57uqZIP2rW9iZYqzMz9pCNL4ii8wE+jbM~-1~-1~-1; ak_bmsc=7A5690B1A0A6FCB62DC237C3BF276904~000000000000000000000000000000~YAAQLVCcJGd2D8aXAQAABiAi6RwRIwQcp8voNYKCsoKI/ZCdypntosRXVmHUaxBo5yzClUoDmzjbxs3S9tTXeflge7DYRn1PVdqpWJ/2CWZHnA01hc3wOEE9/v8eIlSSiMe1OW5HBOXmg4co3qQUkaD1H3fyBWiavE0QuFt6jyVgrLmqEihIvNJWdSOimAYRV5+EcxVH+C+c0BZLn6YMs2W4jw5TZJwhM/A7bAuseOdvYdCwNE3eL4eo/c4OVJC9/fvxSPCkLPpBbNUJizdvBNKMtBPrke7J2+/+F8wrrrE6quQcWcZ7pUsShHur6o8pYj9SklqPaUe3o5UeyLP0kEfqUAccYoA7Jus99mq+cMO4m8/3VZqnfW4EDeFKE1v05w==; bm_sz=657CF8C3F8885860AB52367BF9B4FAC4~YAAQLVCcJGh2D8aXAQAABiAi6RwJsFGM14y1pIAEYv3TOoFFqZepTvpZuCQ/DaE5JC83bYNywAScgnish6HfWwsPLPFPkNi/QZqrY3QNp+hyTH1II9fiF8Du/WSIjE/fflCXguxxoqwy7aorxjjdVlKvt857HciRUVVhEha5tEr2en/juRuZQ8Q/pkHIZ0ceFDgv/xr/J3T7dh6RWAVId39TP8xAGl6G1NkwuMdB0Cetg0fiYVQ7XC7mzGzwptb4jfITObCHC/v1NTgxB1D4OvczF/xkgpjHYFdDYiJFLMeuiDGDKDX9nakRWxZf2ASqIMdmWqszhscfEHEPZ3S7FLUG9zFDF6vHiplbwOiQSk5JdZX7wbm9aMwxzuRsS8yAYeEk8xhHRTduGbmuk+QKp2M9iCfyZwV06NCFVpoLeKx6tnJ74XNZVizChA==~3356211~3163193',
    cookie:'__cookie_value',
    URL:'https://www.dhl.com/cn-zh/home/tracking.html?submit=1&tacking-id=1232343&tracking-id=66668888',
    body:watch({clientHeight:1080},"document.body"),
    head: watch({},"document.head"),
    documentElement: watch({
        getAttribute:function (){
            if (arguments[0] === "webdriver" || arguments[0] === "driver" || arguments[0] === "selenium") {
                return null;
            }
            console_log("=============document documentElement getAttribute 的参数是:", arguments)
        },
        toString: function () {
            return "[object HTMLHtmlElement]";
        },
        clientHeight:1080
    },"document.documentElement"),
    appendChild: function(tag_name) {
        console_log("dopcument appendChild 监听事件:", tag_name)
    },
    toString: function () {
        return "[object HTMLDocument]";
    },


}
navigator = {
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
    productSub: '20030107',
    language: 'zh-CN',
    product: 'Gecko',
    // plugins:[watch({},"navigator.plugins[0]"),watch({},"navigator.plugins[1]"),watch({},"navigator.plugins[2]"),watch({},"navigator.plugins[3]"),watch({},"navigator.plugins[4]")],
    plugins:watch({length:5},"navigator.plugins"),
    onLine: true,
    vibrate:makeFunction("vibrate"),
    // getBattery: makeFunction("getBattery"),  //***
    getBattery:function (){
        return Promise((resolve, reject)=>{
            var info = {}
            info = watch(info,"getBattery.info")
            resolve(info)
        })
    },  //***
    credentials: watch({},"navigator.credentials"),
    bluetooth: watch({},"navigator.bluetooth"),
    storage: watch({},"navigator.storage"),
    getGamepads: makeFunction("getGamepads"),
    hardwareConcurrency: 16,
    mediaDevices: watch({},"navigator.mediaDevices"),
    permissions:watch({
        query:function (){
            return Promise((resolve, reject)=>{
            })
        }
    },"navigator.permissions"),
    registerProtocolHandler:makeFunction("registerProtocolHandler"),
    requestMediaKeySystemAccess: makeFunction("requestMediaKeySystemAccess"),
    sendBeacon: makeFunction("sendBeacon"),
    serviceWorker: watch({},"navigator.serviceWorker"),
    webkitTemporaryStorage: watch({},"navigator.webkitTemporaryStorage"),
    maxTouchPoints: 0,
    webdriver: false,
    toString: function () {
        return "[object Navigator]";
    },


}
screen = {
    width: 1920,
    height: 1080,
    availWidth: 1920,
    availHeight: 1080,
    colorDepth: 24,
    pixelDepth: 24,

}
_localStorage = {}
localStorage = {
    setItem: function(key, value) {
        console_log("localStorage setItem:", key, "=", value)
        _localStorage[key] = value;
    },
    removeItem: function(key) {
        console_log("localStorage removeItem:", key)
        delete _localStorage[key];
    },
    toString: function () {
        return "[object Storage]";
    }
}
sessionStorage = {
    setItem: function(key, value) {
        console_log("sessionStorage setItem:", key, "=", value)
        _localStorage[key] = value;
    },
    removeItem: function(key) {
        console_log("sessionStorage removeItem:", key)
        delete _localStorage[key];
    },
}

// setTimeout = makeFunction("setTimeout");
// setInterval = makeFunction("setInterval");
setTimeout = function (){};
setInterval = function (){};

window.navigator = navigator;
window.screen = screen;
window = watch(window,"window");
document = watch(document,"document");
navigator = watch(navigator,"navigator");
location = watch(location,"location");
screen = watch(screen,"screen");
localStorage = watch(localStorage,"localStorage");
sessionStorage = watch(sessionStorage,"sessionStorage");

;;
require("./work2")
function get_sensor_data(){
    return window.sensor_data;
}

当前状态:半成品,差个mst的vdc参数逆向,这里我不想扣vmp的纯算,大家有没有什么好的办法啊,求探讨,文章最后把这个vdc的分析情况说明一下!

二. AST解混淆akamai

2.1 常量替换

说明:代码中有很多参数是用字母表示的,比如od=1,aa = 2… 而这些字母在执行某个函数后初始化好了,当前作用域下可用,在后续扣算法的时候会严重阻扰我们扣算法的体验,因为有大量的这些参数,不还原的话扣起来很麻烦。
在这里插入图片描述
如图,加密的js里第一个自执行方法是浏览器兼容的一个方法,这个可以忽略,第二段自执行方法就是我截图的(js会变,但是整体的结构基本固定),第一个R3是判断浏览器和node环境的,这个可以忽略。第二个和第三个函数里都是初始化这些变量的,还有其他好多地方有变量初始化的操作,开始还原这些变量,把他们代表的真正数字含义还原到js里。
开始写ast插件:

let changliang_obj = {};
let changliang_str = "";
const eval_constant1 = {
  FunctionDeclaration(path) {
    const functionBody = path.node.body;

    if (types.isBlockStatement(functionBody)) {
      // 遍历函数体内的所有语句
      functionBody.body.forEach((statement, stmtIndex) => {
        // 处理表达式语句
        if (types.isExpressionStatement(statement)) {
          const expr = statement.expression;
          if (types.isSequenceExpression(expr)) {
            expr.expressions.forEach((subExpr, seqIndex) => {
              // 只处理赋值表达式
              if (types.isAssignmentExpression(subExpr)) {
                try {
                  // 生成右侧表达式的代码
                  const rightCode = generator(subExpr.right).code;

                  // 安全执行表达式(使用沙箱)
                  const result = eval(rightCode);

                  // 创建新的常量节点替换右侧表达式
                  const resultNode = types.valueToNode(result);

                  // 正确替换节点(直接修改 AST 节点)
                  subExpr.right = resultNode;

                  console.log(`已替换: ${subExpr.left.name} = ${result}`);
                  // changliangcode += `${subExpr.left.name} = ${result};`
                  changliang_obj[subExpr.left.name] = result;
                  // path.remove(); 移除函数  此处我就不移除了
                } catch (error) {
                  console.error(`处理赋值失败: ${error.message}`);
                }
              }
            });
          }

        }
      });
    }
  }
};

// 执行遍历
traverse(ast, eval_constant1);
UV = 8;lO = 7;lW = 5;c6 = 0;SM = 10;UQ = 4;U3 = 1;pw = 3;OY = 6;ZP = 9;G = 2;zQ = 4;XG = 0;Pf = 10;JO = 5;HR = 1;H8 = 8;FG = 7;g0 = 9;zY = 6;Q8 = 2;s2 = 3;UV = 8;lO = 7;lW = 5;c6 = 0;SM = 10;UQ = 4;U3 = 1;pw = 3;OY = 6;ZP = 9;G = 2;jM = 48;VF = 60;Lf = 15;lf = 38;OY = 27;RO = 26;v0 = 46;d = 65536;J = 37;hH =
 32;w2 = 40;j2 = 41;YO = 18;TF = 21;OO = 42;Xg = 53;PY = 11;lC = 20;zG = 45;SU = 55;Nf = 34;CF = 51;LY = 54;dR = 52;b = 49;KY = 19;MC = 30;NC = 14;XR = 39;CR = 25;MH = 23;l9 = 36;Bl = 57;NM = 12;V8 = 62;F0 = 47;OM = 56320;t2 = 17;NG = 59;DR = 50;lM = 1024;AO = 44;CY = 55296;V = 56;KC = 31;KF = 13;JH = 65535;Z8 = 16;zQ = 4;XG = 0;Pf = 10;JO = 5;HR = 1;H8 = 8;FG = 7;g0 = 9;zY = 6;Q8 = 2;s2 = 3;

traverse(ast, eval_constant1);
for (const [key, value] of Object.entries(changliang_obj)) {
  changliang_str += `${key} = ${value};`;
}
console.log(changliang_str)


const replaceConstants = {
  Identifier(path) {
    const varName = path.node.name;
    // 检查是否是需要替换的常量
    if (changliang_obj.hasOwnProperty(varName)) {
      // 关键判断:排除赋值表达式左侧的变量(左值)
      const isAssignmentLeft = path.parentPath.isAssignmentExpression() &&
                             path.parent.left === path.node;

      // 排除更新表达式的左值(如 a++、a--)
      const isUpdateExpression = path.parentPath.isUpdateExpression() &&
                               path.parent.argument === path.node;

      if (isAssignmentLeft || isUpdateExpression) {
        // 跳过左值,不替换
        return;
      }

      // 排除函数参数、局部变量(只替换全局常量)
      const binding = path.scope.getBinding(varName);
      if (!binding || binding.kind === 'hoisted') {
        try {
          path.replaceWith(types.numericLiteral(changliang_obj[varName]));
          console.log(`已替换变量 ${varName}${changliang_obj[varName]}`);
        } catch (error) {
          console.error(`替换变量 ${varName} 失败:`, error.message);
        }
      }
    }
  }
};

// 执行替换并生成代码
traverse(ast, replaceConstants);

还原前后对比
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/96bfa259664d4372b6c48ccc22e0c62a.png
中间的变量提取出来,下次在执行即可解决因函数执行顺序问题导致还原不全。

2.2 函数解混淆

继续观察js加密的情况
在这里插入图片描述
akamai类似这种函数调用嵌套执行的有很多,常规解混淆需要把这些函数扣出来,扣到能执行为止比如图片的send,此处我们不能采取常规解混淆方式,不然有点累,我们采用逗号表达式去改写js代码。首先提取加密函数的特征,以下是需要提取的函数。

hU()[gn(Dl)](n0, XPQ, jn)
sA()[hN(dA)].call(null, bE, OZ, Qr, Fv, Yf)
cA()[QU(nr)].apply(null, [JH, pG])
bWQ[Ul(typeof cA()[QU(kD)], zG('', [][[]])) ? cA()[QU(Rf)](tj, QH) : cA()[QU(II)](CtQ, mdQ)]
/*
拿第一个hU()[gn(Dl)](n0, XPQ, jn)来说(其他一样)
hU()[gn(Dl)](n0, XPQ, jn)   处理成----》   obj['hU()[gn(Dl)](n0, XPQ, jn)'] = hU()[gn(Dl)](n0, XPQ, jn),obj['hU()[gn(Dl)](n0, XPQ, jn)']

*/

在js的最上面定义一个obj = {},用来存放解密前后的值映射。在此之前不得不说经过ast解密后的代码或多或少都格式化了,代码里有些做了tostring检测,需要hook出来,返回未格式化之前的代码,hook代码如下:

let _toString = Function.prototype.toString;
Function.prototype.toString = function() {
    console.log(this.name);
    //debugger
    if (this.name === "YFZmKgEATx") {
        return "xxxxx替换成你的";
    }
    if (this.name === "zsMITAREXv") {
        return "xxxx";
    }
    return _toString.call(this);
}

接着来写ast代码还原字符串

const obj_func = {
    CallExpression: {
        exit: function(path) {
            let node = path.node;
            if (types.isMemberExpression(node.callee, { computed:  true }) && types.isCallExpression(node.callee.object) && types.isIdentifier(node.callee.object.callee) && types.isCallExpression(node.callee.property) && types.isIdentifier(node.callee.property.callee)
            ||  types.isMemberExpression(node.callee, { computed:  false}) && types.isMemberExpression(node.callee.object) && types.isCallExpression(node.callee.object.object) && types.isIdentifier(node.callee.object.object.callee) && types.isIdentifier(node.callee.property)
            ){
                console.log(path.toString());
                let key = path.toString();
                //构建逗号表达式 obj['hU()[gn(Dl)](n0, XPQ, jn)'] = hU()[gn(Dl)](n0, XPQ, jn),obj['hU()[gn(Dl)](n0, XPQ, jn)']
                let new_node = types.sequenceExpression([
                    types.assignmentExpression("=",
                        types.memberExpression(
                            types.identifier("obj"),
                            types.stringLiteral(key),
                            computed = true
                        ),
                        node
                    ),
                    types.memberExpression(
                        types.identifier("obj"),
                        types.stringLiteral(key),
                        computed = true
                    )
                ])
                path.replaceWith(new_node);
                path.skip();
            }
        }
    },
    ConditionalExpression:function (path){
        let node = path.node;
        if(types.isCallExpression(node.test) && node.test.arguments.length === 2 && types.isIdentifier(node.test.callee) && types.isCallExpression(node.consequent) && types.isCallExpression(node.alternate)){
            let new_node = types.sequenceExpression([
                types.assignmentExpression("=",
                    types.memberExpression(
                        types.identifier("obj")
                        ,types.stringLiteral(path.toString())
                        ,computed=true
                    )
                    ,node
                ),
            types.memberExpression(
                        types.identifier("obj")
                        ,types.stringLiteral(path.toString())
                        ,computed=true
                    )
            ])
            // console.log(path.toString(),'---->',generator(new_node).code);
            path.replaceWith(new_node);
            path.skip();
        }
    }
}

traverse(ast, obj_func);

然后将解密后的js代码替换到网页里,让网页自动捕获解密函数后的值,看网页效果:
在这里插入图片描述
在控制台copy(obj)复制到代码里,后面继续替换。
在这里插入图片描述
现在我们拿到了代码里解密后的结果,接下来我们拿解密后的结果替换解密前的函数,开始写AST:

const obj_func2 = {
    CallExpression: {
        exit: function(path) {
            let node = path.node;
            if (types.isMemberExpression(node.callee, { computed:  true }) && types.isCallExpression(node.callee.object) && types.isIdentifier(node.callee.object.callee) && types.isCallExpression(node.callee.property) && types.isIdentifier(node.callee.property.callee)
            ||  types.isMemberExpression(node.callee, { computed:  false}) && types.isMemberExpression(node.callee.object) && types.isCallExpression(node.callee.object.object) && types.isIdentifier(node.callee.object.object.callee) && types.isIdentifier(node.callee.property)
            ){
                if (obj[path.toString()] !== undefined){
                    path.replaceInline(types.valueToNode(obj[path.toString()]))
                }
            }
        }
    },
    ConditionalExpression:function (path){
        let node = path.node;
        if(types.isCallExpression(node.test) && node.test.arguments.length === 2 && types.isIdentifier(node.test.callee) && types.isCallExpression(node.consequent) && types.isCallExpression(node.alternate)){
            if (obj[path.toString()] !== undefined){
                path.replaceInline(types.valueToNode(obj[path.toString()]))
            }
        }
    }
}
traverse(ast, obj_func2);

将解密后的js替换到网页,见效果:
在这里插入图片描述
能够正常发包(有时候会触发另一套请求逻辑,因为会对解密函数调用的次数做验证,如果是这种,解密函数只能用于方便分析对比用。扣纯算得用混淆的代码去分析,这里没问题我就继续用解密后的js去分析扣纯算,看后面结果如何,因为经常会变,后面能不能成功我也不知道,我是写一点记录一点)

2.3 开始扣纯算——主流程

看上一张图,K8Q就是我们需要的sensor_data,我们从这里扣,先把主流程走通,纯算js正式开始,最前面放 2.1 常量替换 解混淆后的常量,因为后面函数里有大量的值引用,我们最前面已经处理好了。
在这里插入图片描述
在这里插入图片描述
对所有出现J8Q的地方打上断点。看是怎么一步一步赋值过来的。
在这里插入图片描述
跟踪到J8Q图片最后出现的位置就是刚才发包的地方的J8Q,从最上面扣起,其他参数先固定,主流程算法先扣。
在这里插入图片描述
主流程这里相关参数先写死和浏览器一致,看输出结果,和浏览器一致就行,这步主要扣jA函数,jA函数有许多case,我们只扣我们需要的部分,其他的删除,改名jA_31。
在这里插入图片描述
在这里插入图片描述
此时和浏览器值一致,继续扣。扣rH函数在这里插入图片描述
在这里插入图片描述
验证本地和浏览器值的一致性:
在这里插入图片描述
继续扣WVQ函数,此函数是生成版本信息的参数,扣完验证和浏览器生成的结果对比。
在这里插入图片描述
至此主函数扣完,开始扣之前写死的参数。

2.4 固定写死的参数逆向过程

首先分析Qz = [ 3355188, 1662293] //先固定***,替换写死的参数,找到Qz生成的位置:var Qz = GWQ || Fp(); 这里是处理cookie bm_sz的。
在这里插入图片描述
那么这里直接将bm_sz传参就好了,此处x2的函数可不扣了;
在这里插入图片描述
这里的Qz参数一个长度为2的数组处理好了,跟cookie bm_sz相关。
再来看重头戏kPQ参数的生成逻辑。
在这里插入图片描述

2.5 ver参数生成

在这里插入图片描述
在这里插入图片描述

ver实际上是结婚新的 js写死的,暂时写死(感觉可以写死)

2.6 ajr参数逆向

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
通过对比发现Cj方法不用扣了按照那几个参数组成对象即可。即:
在这里插入图片描述
这里看gVQ参数:
在这里插入图片描述
发现gVQ实际上是din参数,这里先写死。后面再单独来分析din参数。

在这里插入图片描述
扣DtQ函数时,其他的case删掉只留本身需要的。
在这里插入图片描述
在扣DtQ函数时发现这里一直在循环,值好眼熟,就是din的值,退回去直接看结果。
在这里插入图片描述
实际上就是取din的值,我们自己取值就行,不用扣函数了。
在这里插入图片描述
跟页面对得上,ok,deviceData处理好了。再来分析X6Q
在这里插入图片描述
是一个时间差,补上就行。
在这里插入图片描述
再来处理A1函数:
在这里插入图片描述

在这里插入图片描述
验证,这里有个随机数,形式上一样就行了,就不hook随机数了,太麻烦了,哈哈哈,基本上问题不大,ajr参数搞定。

2.7 mst 参数逆向

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
试了几次(包括换js),这些参数没变,可以写死,继续分析不能写死的参数。
有个时间参数sts注意一下,是最开始定义的IV[“window”].bmak[“startTs”]。
在这里插入图片描述
在这里插入图片描述
分析dd2,应该是可以写死的,由于很简单,扣下来也无妨。
Cj(BQ, [“dd2”, TWQ]),实际上就是这个TWQ。
在这里插入图片描述
后面要扣的只有dvc了,其他参数可不处理。
在这里插入图片描述

2.8 mst之 dvc参数

这里是一段jsvmp,放在后面分析

2.9 din参数逆向

在这里插入图片描述
在这里插入图片描述
开始扣c3Q函数。
在这里插入图片描述

在这里插入图片描述
扣到这里时,页面上看会比较明显,这2个值一样。
在这里插入图片描述
为什么会一样呢,我第一遍做的时候直接赋值相等,后面发现错了,审图分析
在这里插入图片描述
继续往下扣:
在这里插入图片描述
在这里插入图片描述
发现bM(RUQ, z5Q)这个函数的作用是把RUQ对象调换了一下顺序,那这个方法我们不用扣了,自己实现(借助GPT)
首先拿RUQ对象的key的顺序js如下:

// 遍历RUQ数组,提取每个对象的唯一key
const keys = RUQ.map(item => {
  // 每个对象只有一个key,取第一个即可
  return Object.keys(item)[0];
});

console.log(keys);
// 输出:["ua", "xag", "nps", "nal", "nap", "npl", "pha", "wdr", "dau", "hz1", "tsd", "asw", "ash", "swi", "she", "wiw", "wih", "wow", "adp", "ucs", "ran", "hal", "ibr"]

剩下的js是重新排序输出:

// 给定的目标顺序
const targetOrder = ['adp', 'nal', 'hz1', 'xag', 'dau', 'wih', 'ua', 'ucs', 'nap', 'ibr', 'npl', 'nps', 'wiw', 'tsd', 'hal', 'swi', 'pha', 'wow', 'ran', 'she', 'ash', 'wdr', 'asw'];

// 1. 构建 key 到对象的映射(快速查找)
const keyToObjectMap = {};
RUQ.forEach(obj => {
  const key = Object.keys(obj)[0]; // 每个对象只有一个 key
  keyToObjectMap[key] = obj;
});

// 2. 按目标顺序重新排列数组
const orderedRUQ = [];

// 先添加目标顺序中存在的对象
targetOrder.forEach(key => {
  if (keyToObjectMap.hasOwnProperty(key)) {
    orderedRUQ.push(keyToObjectMap[key]);
    delete keyToObjectMap[key]; // 移除已添加的对象,避免重复
  }
});

// 3. 处理目标顺序中没有,但 RUQ 中存在的额外对象(可选:放在末尾)
Object.values(keyToObjectMap).forEach(remainingObj => {
  orderedRUQ.push(remainingObj);
});

// 输出结果
console.log(orderedRUQ);

关于vdc参数

vdc参数就是这个hM函数执行的结果,其中第二个入参就是ajr
在这里插入图片描述
hM函数进去后如下:
在这里插入图片描述
这里是一段vmp,不管咋样,打死不想扣vmp的纯算,太浪费时间了,各位大佬有什么好办法,求指导!
以下的js是我解混淆后的js,大家如果分析这个网站,可以直接把js内容替换成我下面这个,那么就可以直接搜这个hM函数打断点分析这个参数了!!(见附件,太大了,放不进来)

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值