js加密算法自动逆向

拦截加解密框架函数,获取密钥、IV等关键数据

// ==UserScript==
// @name         js加密算法自动逆向
// @description  拦截加解密框架函数,获取密钥、IV等关键数据
// @author       ejfkdev
// @namespace    @ejfkdev
// @version      1.0
// @license      MIT
// @match        https://*/*
// @match        http://*/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

function arraysEqual(a, b) {
  if (a === b) return true;
  if (!Array.isArray(a) || !Array.isArray(b)) return false;
  if (a.length !== b.length) return false;
  for (let i = 0; i < a.length; i++) {
    if (a[i] !== b[i]) return false;
  }
  return true;
}

const containsAllUnique = (A, B) =>
  Array.isArray(A) && Array.isArray(B) &&
  [...new Set(B)].every(x => A.includes(x));

function wordArrayToHex(t) {
  const { words, sigBytes } = t;
  const u8 = new Uint8Array(sigBytes);
  for (let n = 0; n < sigBytes; n++) {
    u8[n] = (words[n >>> 2] >>> (24 - (n % 4) * 8)) & 0xff;
  }
  return Array.from(u8, b => b.toString(16).padStart(2, "0")).join("");
}

function wordArrayToBase64(wordArray) {
  const { words, sigBytes } = wordArray;
  const bytes = new Uint8Array(sigBytes);

  for (let i = 0; i < sigBytes; i++) {
    bytes[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
  }

  // 内置函数 btoa 只能处理字符串,所以要先转成 binary string
  let binary = String.fromCharCode(...bytes);
  return btoa(binary);
}

function utf8Stringify(wordArray) {
  const { words, sigBytes } = wordArray;
  const bytes = new Uint8Array(sigBytes);

  // 提取有效字节
  for (let i = 0; i < sigBytes; i++) {
    bytes[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
  }

  try {
    return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
  } catch {
    return ""
  }
}

function detectAlgorithm(algo) {
  if (!algo) return "Unknown";

  // DES/3DES/AES 区分
  if (algo.blockSize === 2 && algo.keySize === 2 && algo.ivSize === 2) return "DES";
  if (algo.blockSize === 2 && algo.keySize === 6 && algo.ivSize === 2) return "TripleDES";
  if (algo.blockSize === 4 && algo.ivSize === 4) return "AES";

  // 可扩展更多算法特征
  return "Unknown";
}

function detectPadding(pad) {
  if (!pad || !pad.pad) return "Unknown";

  const len = pad.pad.length;
  if (len === 2) {
    // Pkcs7 / AnsiX923 / Iso10126 都是 2 参数,进一步根据函数名字或 toString 内容
    const fnStr = pad.pad.toString();
    if (fnStr.includes("Pkcs7")) return "Pkcs7";
    if (fnStr.includes("AnsiX923")) return "AnsiX923";
    if (fnStr.includes("Iso10126")) return "Iso10126";
    return "Pkcs7"; // 默认
  }
  if (len === 1) return "ZeroPadding";
  return "Unknown";
}

function detectMode(mode, algo) {
  if (!mode) return "Unknown";

  const s = mode.$super;
  if (!s) return "Unknown";

  // ECB:没有 ivSize 或 iv = undefined
  if (!algo.ivSize || algo.ivSize === 0) return "ECB";

  // 根据内部方法区分 CBC / CFB / OFB / CTR
  if (s._doCryptBlock && s.createEncryptor && s.createDecryptor) {
    // 进一步区分
    if (s.hasOwnProperty('processBlock')) return "CFB";
    if (s.ivSize && s._doCryptBlock.length === 3) return "CBC";
    return "OFB"; // fallback
  }
  return "Unknown";
}

const getLog = (ctx) => {
  const { rule, self, thisArg, argArray, caller } = ctx;
  rule?.encrypt(ctx)
  let encryptSource;
  try {
    encryptSource = rule?.encryptSource(ctx)
  } catch { }
  return ({ plaintext, cipherText }) => {
    const table = {
      ...(rule.lib !== undefined ? { lib: rule.lib } : {}),
      ...(rule.mode ? { mode: rule.mode(ctx) } : {}),
      ...(rule.padding ? { padding: rule.padding(ctx) } : {}),
      ...(rule.key ? { key: rule.key(ctx) } : {}),
      ...(rule.keyHex ? { keyHex: rule.keyHex(ctx) } : {}),
      ...(rule.keyBase64 ? { keyBase64: rule.keyBase64(ctx) } : {}),
      ...(rule.iv ? { iv: rule.iv(ctx) } : {}),
      ...(rule.ivHex ? { ivHex: rule.ivHex(ctx) } : {}),
      ...(rule.ivBase64 ? { ivBase64: rule.ivBase64(ctx) } : {}),
      ...(rule.algorithm ? { algorithm: rule.algorithm(ctx) } : {}),
      ...(plaintext ? { plaintext: rule?.plaintext({ ...ctx, plaintext }) ?? plaintext } : {}),
      ...(cipherText ? { cipherText: rule?.cipherText?.({ ...ctx, cipherText }) ?? cipherText } : {}),
    };
    console.table(table)
    encryptSource && console.log(encryptSource)
  }
}

const findTargetCaller = (rootCaller, targetSeq) => {
  function tryMatch(node, seqIndex) {
    if (!node) return null;
    if (node.name !== targetSeq[seqIndex]) return null;


    if (seqIndex === targetSeq.length - 1) {
      // 匹配完成,返回最后一个节点的 caller
      return node.caller || null;
    }
    return tryMatch(node.caller, seqIndex + 1);
  }
  function search(node) {
    if (!node) return null;
    const match = tryMatch(node, 0);
    if (match) return match;
    // 从下一个 caller 开始重新尝试
    return search(node.caller);
  }
  return search(rootCaller);
}

const rules = [{
  lib: 'CryptoJS',
  hook: 'apply',
  argsLength: 3,
  funcName: '',
  thisKeys: ['init', '$super', 'cfg', '_xformMode', '_key', '_data', '_nDataBytes', '_mode'],
  argsKeys: [[], ['words', 'sigBytes'], ['init', '$super']],
  chain: ['create', 'createEncryptor', 'encrypt', 'encrypt'],
  key: ({ rule, self, thisArg, argArray }) => argArray[1]?.toString({ stringify: utf8Stringify }),
  keyHex: ({ rule, self, thisArg, argArray }) => argArray[1]?.toString({ stringify: wordArrayToHex }),
  keyBase64: ({ rule, self, thisArg, argArray }) => argArray[1]?.toString({ stringify: wordArrayToBase64 }),
  iv: ({ rule, self, thisArg, argArray }) => argArray[2]?.iv?.toString({ stringify: utf8Stringify }),
  ivHex: ({ rule, self, thisArg, argArray }) => argArray[2]?.iv?.toString({ stringify: wordArrayToHex }),
  ivBase64: ({ rule, self, thisArg, argArray }) => argArray[2]?.iv?.toString({ stringify: wordArrayToBase64 }),
  algorithm: ({ rule, self, thisArg, argArray }) => detectAlgorithm(argArray[2]?.algorithm),
  padding: ({ rule, self, thisArg, argArray }) => detectPadding(argArray[2]?.padding),
  // mode: ({ rule, self, thisArg, argArray }) => argArray[2]?.mode,
  plaintext: ({ rule, self, thisArg, argArray, plaintext }) => plaintext?.toString({ stringify: utf8Stringify }),
  cipherText: ({ rule, self, thisArg, argArray, cipherText }) => cipherText?.toString(),
  encrypt: ({ rule, self, thisArg, argArray, encrypt }) => encrypt && (window.__encrypt__ = (plaintext) => encrypt(plaintext).toString()),
  encryptSource: ({ rule, self, thisArg, argArray, caller }) => findTargetCaller(caller, rule.chain),
  return: ({ returnValue, log, rule, self, thisArg, argArray }) => {
    const finalize_original = thisArg.finalize;
    rule?.encrypt({ returnValue, log, rule, self, thisArg, argArray, encrypt: finalize_original.bind(thisArg) })
    thisArg.finalize = (...args) => {
      const plaintext = args[0];
      const cipherText = finalize_original.apply_original(thisArg, args);
      log({ plaintext, cipherText });
      return cipherText
    }
    return returnValue;
  },
}]

function getCallerDepth(args, depth = 1) {
  try {
    let callee = args?.callee
    while (callee && depth--) {
      callee = callee.caller
    }
    return callee
  } catch {
  }
}

Function.prototype.apply_original = Function.prototype.apply_original ?? Function.prototype.apply;
Function.prototype.call_original = Function.prototype.call_original ?? Function.prototype.call;

// 覆盖 apply
Function.prototype.apply = function (thisArg, argArray) {
  let returnValue = this.apply_original(thisArg, argArray)
  try {
    argArray = argArray ?? []
    const caller = getCallerDepth(arguments, 32)
    if (caller) return returnValue
    const self = this;
    const rule = rules.find(rule =>
      rule.hook === 'apply' &&
      argArray.length === rule.argsLength &&
      this.name === rule.funcName &&
      containsAllUnique(Object.keys(thisArg ?? 0), rule.thisKeys) &&
      Array.from(argArray).every((arg, i) => containsAllUnique(Object.keys(arg), rule.argsKeys[i])));
    if (!rule) return returnValue;
    const log = getLog({ rule, self, thisArg, argArray, caller: arguments?.callee })
    if (rule.return) returnValue = rule.return({ returnValue, log, rule, self, thisArg, argArray });
    else log(returnValue);
  } catch (error) {
    console.error('apply 错误:', error);
  }
  return returnValue;
};

Function.prototype.call = function (thisArg, ...argArray) {
  let returnValue = this.call_original(thisArg, ...argArray)
  try {
    const caller = getCallerDepth(arguments, 32)
    if (caller) return returnValue
    let rule;
    const self = this;
    for (const r of rules) {
      if (argArray.length ===
        r.hook === 'call' &&
        r.argsLength &&
        this.name === r.funcName &&
        containsAllUnique(Object.keys(thisArg ?? 0), r.thisKeys) &&
        Array.from(argArray).every((arg, i) => containsAllUnique(Object.keys(arg), r.argsKeys[i]))) {
        rule = r;
        break;
      }
    }
    if (!rule) return returnValue;
    const log = getLog({ rule, self, thisArg, argArray, caller: arguments?.callee })
    if (rule.return) returnValue = rule.return({ returnValue, log, rule, self, thisArg, argArray });
    else log(returnValue);
  } catch (error) {
    console.error('call 错误:', error);
  }
  return returnValue;
};

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址