逆向实战小记——某ToB商城网站分析学习

aHR0cHM6Ly9tLmRvbmdmYW5nLmNvbS8=

特别声明

++注意!!!!实例仅作为学习案例,禁止其他个人以及团体做谋利用途!!!++

技术点

1、sm2

2、sm4

网页分析

第一步:分析请求和响应

通过分析可以知道,请求参数响应内容 都被加密,请求headers 中也存在加密参数**code。**通过加密的信息可复制到在线加解密工具验证是否为猜测的base64或者sha256。经过猜测和验证,上述编码加密类型均不是

第二步:找切入口,开始逆向

1、post请求的url, 添加到XHR中,刷新页面,重新请求

2、重新请求后,观察在哪里截断(停止)

分析堆栈,我个人习惯是从上往下看(怎么看看个人习惯),目的是分析请求都经过了哪些函数,图上框出的地方,可以看出,这一步参数已经被加密,且headers中的code也生成了,那么继续向下分析。

在t.exports的上一个函数中,可以看到没有显示参数和headers的code,那初步认为参数加密和headers的code 在d.request到t.exports 这个过程生成。

3、分析d.request处断点

这一步断点停住后,需要F10自己一步一步调试,需要观察t中的url是否为我们分析请求url, 如果不是就F8跳入下一断点,如果是我们分析的url, 观察t 中的headers 和data。 能够看到data未被加密,且headers中没有code,因此d.request 断点是正确的。

F10一步一步调试,会进入到Vr 函数中,这里有个for 循环,持续F10会很痛苦,因此,这个时候可以将下一个断点,打在var n 这一行,这样F8 跳入下一个断点,既不会绕在循环中也不会跳到莫名的断点处(注意:之所以将断点 添加在 var n 这一行,因为循环中并没有可以跳转到其他函数的操作)

上述跳出循环后,继续F10,会遇到Vn 函数,此时又是一个循环,此时断点可以加在if 的上一行,原理和上述差不多

第三步:处理逆向核心------请求参数(加密前)

按照上述F10一步一步调试,很快就跳转到上图位置。经过分析可以看到实际上,未加密的请求参数中新增了一个参数magicaValue。 而magicaValue值来自x, 也就是Object (w.d)(), 通过验证可以了解到Object (w.d)() 是uuid4, magicaValue是由uuid4+时间戳组成。

第四步:处理逆向核心------headers的code

分析可以看到 code 其实是sm2.doEncrypt(front+时间戳,"固定值") 生成。

第五步:处理逆向核心------params加密(请求参数(加密后))

可以看出来 _ 是请求参数的明文,经过h.a.encodeNoKey(明文参数(字符串), front+时间戳)

第六步:处理逆向核心------响应内容

响应内容排查加密方式,可以search 一下responseText,上图框出的地方进行断点,这块不做详细的描述,如果新手可以把认为的可能性都打上断点

可以看出加密内容解析出明文,使用了一个sm4Code ,这个参数在响应headers中出现了,使用sm2.doDecrypt(sm4code, 固定值), Object(x.a) 可以看出是sm4的加密

第七步:结合AI整理关于sm2, sm4 的加密函数

需要将 2dcfb47.js和3c010f5.js 文件保存在本地。将图上在文件中加密的函数贴出来给AI,让AI整理出可以在node环境中运行的代码(这个过程需要反复验证,以及更换提示词,有些模型会认为这个行为不合规拒绝整理)。

复制代码
// get_code
const { sm2 } = require("sm-crypto");

function buildEncryptedHeader(t) {
  const E = "front" + t; //
  const publicKey = "04fcd2cf3649a************************dbe5e48df"; // 需要自己找
  console.log(E)
  return {
    encryptionType: "SM",
    code: sm2.doEncrypt(E, publicKey),
    randomKey: E
  };
}
function code(t){
    return buildEncryptedHeader(t).code
}
// **************************************************************************************
// get_params
/* =========================
 * Base64
 * ========================= */
function bytesToBase64(bytes) {
  return Buffer.from(bytes).toString("base64");
}

function base64ToBytes(base64) {
  return Array.from(Buffer.from(base64, "base64"));
}

/* =========================
 * UTF-8 编解码
 * ========================= */
function utf8Bytes(str) {
  const bytes = [];
  for (let i = 0; i < str.length; i++) {
    let code = str.charCodeAt(i);

    if (code >= 0x10000 && code <= 0x10ffff) {
      bytes.push((code >> 18) | 0xf0);
      bytes.push(((code >> 12) & 0x3f) | 0x80);
      bytes.push(((code >> 6) & 0x3f) | 0x80);
      bytes.push((code & 0x3f) | 0x80);
    } else if (code >= 0x800) {
      bytes.push((code >> 12) | 0xe0);
      bytes.push(((code >> 6) & 0x3f) | 0x80);
      bytes.push((code & 0x3f) | 0x80);
    } else if (code >= 0x80) {
      bytes.push((code >> 6) | 0xc0);
      bytes.push((code & 0x3f) | 0x80);
    } else {
      bytes.push(code & 0xff);
    }
  }
  return bytes;
}

function bytesToUtf8(bytes) {
  let out = "";
  for (let i = 0; i < bytes.length; i++) {
    const bin = bytes[i].toString(2);
    const prefix = bin.match(/^1+?(?=0)/);

    if (prefix && bin.length === 8) {
      const len = prefix[0].length;
      let store = bytes[i].toString(2).slice(7 - len);
      for (let j = 1; j < len; j++) {
        store += bytes[i + j].toString(2).slice(2);
      }
      out += String.fromCharCode(parseInt(store, 2));
      i += len - 1;
    } else {
      out += String.fromCharCode(bytes[i]);
    }
  }
  return out;
}

/* =========================
 * SM4
 * ========================= */
function rotl(x, n) {
  return ((x << n) | (x >>> (32 - n))) >>> 0;
}

const SBOX = [
  0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
  0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
  0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
  0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
  0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
  0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
  0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
  0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
  0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
  0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
  0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
  0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
  0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
  0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
  0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
  0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
];

const FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc];

const CK = [
  0x00070e15,0x1c232a31,0x383f464d,0x545b6269,
  0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
  0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,
  0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,
  0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,
  0x30373e45,0x4c535a61,0x686f767d,0x848b9299,
  0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,
  0x10171e25,0x2c333a41,0x484f565d,0x646b7279
];

function tau(a) {
  return (
    ((SBOX[(a >>> 24) & 0xff] << 24) >>> 0) |
    (SBOX[(a >>> 16) & 0xff] << 16) |
    (SBOX[(a >>> 8) & 0xff] << 8) |
    SBOX[a & 0xff]
  ) >>> 0;
}

function L(x) {
  return (x ^ rotl(x, 2) ^ rotl(x, 10) ^ rotl(x, 18) ^ rotl(x, 24)) >>> 0;
}

function LPrime(x) {
  return (x ^ rotl(x, 13) ^ rotl(x, 23)) >>> 0;
}

function bytesToWord(bytes, offset) {
  return (
    ((bytes[offset] || 0) << 24) |
    ((bytes[offset + 1] || 0) << 16) |
    ((bytes[offset + 2] || 0) << 8) |
    (bytes[offset + 3] || 0)
  ) >>> 0;
}

function wordToBytes(word, out, offset) {
  out[offset] = (word >>> 24) & 0xff;
  out[offset + 1] = (word >>> 16) & 0xff;
  out[offset + 2] = (word >>> 8) & 0xff;
  out[offset + 3] = word & 0xff;
}

function expandKey(keyBytes, mode) {
  const MK = [
    bytesToWord(keyBytes, 0),
    bytesToWord(keyBytes, 4),
    bytesToWord(keyBytes, 8),
    bytesToWord(keyBytes, 12)
  ];

  const K = [
    (MK[0] ^ FK[0]) >>> 0,
    (MK[1] ^ FK[1]) >>> 0,
    (MK[2] ^ FK[2]) >>> 0,
    (MK[3] ^ FK[3]) >>> 0
  ];

  const rk = new Array(32);

  for (let i = 0; i < 32; i++) {
    const tmp = tau((K[1] ^ K[2] ^ K[3] ^ CK[i]) >>> 0);
    const next = (K[0] ^ LPrime(tmp)) >>> 0;
    rk[i] = next;
    K[0] = K[1];
    K[1] = K[2];
    K[2] = K[3];
    K[3] = next;
  }

  if (mode === 0) {
    rk.reverse();
  }

  return rk;
}

function cryptBlock(blockBytes, roundKeys) {
  const X = [
    bytesToWord(blockBytes, 0),
    bytesToWord(blockBytes, 4),
    bytesToWord(blockBytes, 8),
    bytesToWord(blockBytes, 12)
  ];

  for (let i = 0; i < 32; i++) {
    const tmp = tau((X[1] ^ X[2] ^ X[3] ^ roundKeys[i]) >>> 0);
    const next = (X[0] ^ L(tmp)) >>> 0;
    X[0] = X[1];
    X[1] = X[2];
    X[2] = X[3];
    X[3] = next;
  }

  const out = new Array(16);
  wordToBytes(X[3], out, 0);
  wordToBytes(X[2], out, 4);
  wordToBytes(X[1], out, 8);
  wordToBytes(X[0], out, 12);
  return out;
}

function zeroPad16(bytes) {
  const out = bytes.slice();
  while (out.length < 16) out.push(0);
  return out;
}

/* =========================
 * encodeNoKey / decodeNoKey
 * ========================= */
const DEFAULT_KEY = "PNb1v6JvtVH3QQHa8a";

function encodeNoKey(e, t) {
  if (!e) return "";

  if (t == null) {
    t = DEFAULT_KEY;
  }

  const input = utf8Bytes(e);
  const keyBytes = utf8Bytes(t).slice(0, 16);
  const roundKeys = expandKey(keyBytes, 1);

  let encrypted = [];
  for (let i = 0; i < input.length; i += 16) {
    const block = zeroPad16(input.slice(i, i + 16));
    encrypted = encrypted.concat(cryptBlock(block, roundKeys));
  }

  return bytesToBase64(encrypted);
}

function decodeNoKey(base64Text) {
  if (!base64Text) return "";

  const input = base64ToBytes(base64Text.replace(/(\r\n|\n|\r)/gm, ""));
  const keyBytes = utf8Bytes(DEFAULT_KEY).slice(0, 16);
  const roundKeys = expandKey(keyBytes, 0);

  let decrypted = [];
  for (let i = 0; i < input.length; i += 16) {
    decrypted = decrypted.concat(cryptBlock(input.slice(i, i + 16), roundKeys));
  }

  const text = bytesToUtf8(decrypted);
  const lastBrace = text.lastIndexOf("}");
  return lastBrace === -1 ? text : text.slice(0, lastBrace + 1);
}

function decodeNoKey2(base64Text, t) {
  if (!base64Text) return "";

  const input = base64ToBytes(base64Text.replace(/(\r\n|\n|\r)/gm, ""));
  const keyBytes = utf8Bytes(t).slice(0, 16);
  const roundKeys = expandKey(keyBytes, 0);

  let decrypted = [];
  for (let i = 0; i < input.length; i += 16) {
    decrypted = decrypted.concat(cryptBlock(input.slice(i, i + 16), roundKeys));
  }

  const text = bytesToUtf8(decrypted);
  const lastBrace = text.lastIndexOf("}");
  return text.slice(0, lastBrace + 1);
}

/* =========================
 * SM2 请求头 code
 * ========================= */
const PUBLIC_KEY =
  "04fcd2cf3649a************************dbe5e48df"; // 需要自己找
 
function buildEncryptedRequest(data, t) {
  const E = "front" + t;//Date.now();
  const raw = typeof data === "string" ? data : JSON.stringify(data);

  return {
    headers: {
      encryptionType: "SM",
      code: sm2.doEncrypt(E, PUBLIC_KEY)
    },
    data: {
      params: encodeNoKey(raw, E)
    },
    debug: {
      randomKey: E,
      raw
    }
  };
}

function uuid4() {
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
    const r = Math.random() * 16 | 0;
    const v = c === "x" ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

function params(keyword, pageNo, t){
  const payload = {
    keyword: keyword,
    pageSize: 20,
    pageNo: 1,
    goodsType: [],
    goodSource: "sc",
    magicaValue: uuid4()+"-"+Date.now()
  };
  console.log(payload)
  const req = buildEncryptedRequest(payload, t);
  return req.data.params
}

//*******************************************************************************
// get_sm4Code 加密的e 也是n
const { sm2 } = require('sm-crypto');
function get_e(defaultCipher) {
  // 默认参数
  // const defaultCipher = '714b29006b449d028876167a437f2394fe02481a2b1586313a979387d3360065e46de83566fabc0ca4dd549cb6132eb1200839a933e4657631db760a074f8abdb36bb532d3bf9f83f5f779ead6ab6eff00081c404ce2a0e31c3d790f620efaf92bcfaeb4780bdd75c4c9caf378ac7f08';
  const defaultPrivateKey = '0c90e64bfc64*************0c1321e20f4'; // 需要自己找
  const defaultCipherMode = 1;
  const defaultOutput = 'string';

  // 从命令行参数获取值
  const cipher = process.argv[2] || defaultCipher;
  const privateKey = process.argv[3] || defaultPrivateKey;
  const cipherMode = process.argv[4] !== undefined ? parseInt(process.argv[4]) : defaultCipherMode;
  const output = process.argv[5] || defaultOutput;
  return sm2.doDecrypt(cipher, privateKey, cipherMode, {output})

}

//*******************************************************************************
// get_res
const { sm4 } = require("sm-crypto");

function normalizeHexKey(key) {
  if (typeof key !== "string") {
    throw new Error("key must be a string");
  }

  if (/^[0-9a-fA-F]{32}$/.test(key)) {
    return key.toLowerCase();
  }

  if (key.length === 16) {
    return Buffer.from(key, "utf8").toString("hex");
  }

  throw new Error(`invalid key: ${key}`);
}

class SM4Compat {
  constructor({ key, mode = "ecb", cipherType = "base64" }) {
    this.key = normalizeHexKey(key);
    this.mode = mode;
    this.cipherType = cipherType;
  }

  decrypt(text) {
    let cipherHex;

    if (this.cipherType === "base64") {
      cipherHex = Buffer.from(text, "base64").toString("hex");
    } else {
      cipherHex = text;
    }

    return sm4.decrypt(cipherHex, this.key, {
      mode: this.mode,
      padding: "pkcs#7",
      output: "string"
    });
  }

  encrypt(text) {
    const cipherBytes = sm4.encrypt(text, this.key, {
      mode: this.mode,
      padding: "pkcs#7",
      output: "array"
    });

    if (this.cipherType === "base64") {
      return Buffer.from(cipherBytes).toString("base64");
    }

    return Buffer.from(cipherBytes).toString("hex");
  }
}
function r(text, e) {
  return new SM4Compat({
    key: e,
    mode: "ecb",
    cipherType: "base64"
  }).decrypt(text);
}
相关推荐
程序员小远1 小时前
Python自动化测试框架及工具详解
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
sleven fung2 小时前
MinerU与BabelDOC与KTransformers与OpenAI API库
开发语言·python·ai·langchain
小毛驴8502 小时前
spring-boot-maven-plugin,maven-compiler-plugin 功能对比
java·python·maven
萤萤七悬2 小时前
【Python笔记】AI帮实现CLI工具-使用argparse.ArgumentParser接收命令参数
开发语言·笔记·python
郑洁文4 小时前
基于Python的Web命令执行漏洞自动化检测系统
前端·python·网络安全·自动化
yingjie1104 小时前
Scanpy vs Seurat 深度对比:Python 与 R 的单细胞分析框架谁更强?
开发语言·python·r语言·生物信息学·单细胞转录组·seurat·scanpy
包子BI大数据4 小时前
3.openclaw小龙虾简单版安装教程
人工智能·python·ai
程序大视界4 小时前
【Python系列课程】Pandas(四):数据统计与排序——describe、sort_values、sample
开发语言·python·pandas
Cthy_hy5 小时前
Python算法竞赛:排列组合核心用法
开发语言·python·算法