【JS逆向学习】快乐学堂登陆接口(自定义DES加密、ddddocr验证码识别)

逆向目标
  • 网址:https://www.91118.com/Passport/Account/Login
  • 接口:https://www.91118.com/passport/Account/LoginPost
  • 参数:
    • pass
    • r
逆向过程

输入手机号、密码、验证码 点击登陆,多试几次,然后观察并比较不通请求参数有哪些变化,其中 ckcode 是验证码
在这里插入图片描述

逆向分析

先使用关键词 pass 搜索,匹配项太多,直接从启动器入口进去跟栈分析
在这里插入图片描述
调试如下
在这里插入图片描述
可以发现参数 r就是一个随机值 Math.random()pass 跟栈进去如下
在这里插入图片描述
加解密的方法都有了

var _key = 'k1fsa01v';
var _iv = 'k1fsa01v';
function encryptByDES(message) {
    var keyHex = CryptoJS.enc.Utf8.parse(_key);
    var encrypted = CryptoJS.DES.encrypt(message, keyHex, {
        iv: CryptoJS.enc.Utf8.parse(_iv),
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    });
    return encrypted.toString();
}
function decryptByDES(ciphertext) {
    var keyHex = CryptoJS.enc.Utf8.parse(_key);
    var decrypted = CryptoJS.DES.decrypt({
        ciphertext: CryptoJS.enc.Base64.parse(ciphertext)
    }, keyHex, {
        iv: CryptoJS.enc.Utf8.parse(_iv),
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    });
    return decrypted.toString(CryptoJS.enc.Utf8);
}

看着像是 DES 对称加密,使用标准库 crypto-js 来加密看看结果是否一致


var CryptoJS = require("crypto-js");
// 定义加密密钥,使用Utf8编码解析密钥字符串
const SECRET_KEY = CryptoJS.enc.Utf8.parse("k1fsa01v");
// 定义加密向量IV,使用Utf8编码解析IV字符串
const SECRET_IV = CryptoJS.enc.Utf8.parse("k1fsa01v");

/**
 * 加密函数,接受一个数据参数,返回加密后的字符串
 * @param {string|object} data - 需要加密的数据,可以是字符串或对象
 * @returns {string} 加密后的数据字符串
 */
function encrypt(data) {
	// 如果数据是对象类型,则尝试将其转换为字符串
	if (typeof data === "object") {
		try {
			data = JSON.stringify(data);
		} catch (e) {
			// 如果转换过程中发生错误,打印错误信息并返回
			console.log(e);
			return;
		}
	}
	// 将数据转换为Utf8格式的密文
	const dataHex = CryptoJS.enc.Utf8.parse(data);
	// 使用AES算法加密数据
	const encrypted = CryptoJS.AES.encrypt(dataHex, SECRET_KEY, {
		iv: SECRET_IV,
		mode: CryptoJS.mode.CBC, // 使用CBC模式
		padding: CryptoJS.pad.Pkcs7, // 使用Pkcs7填充
	});
	// 返回加密后的密文字符串
	return encrypted.toString();
}

console.log(encrypt("a123456"));
// 输出如下
>> T6fdfWw2zktEch0jKSJkmA==

我们再看网站加密结果

encryptByDES("a123456")
// 输出如下
>> bb3mlkFBqqo=

很明显网站对加密做了改造,我们跟进去把整个加密扣出来
在这里插入图片描述
我们把整个文件的 js code 全部copy出来执行并打印 console.log(CryptoJS) 输出如下

{
  lib: {
    Base: {
      extend: [Function: extend],
      create: [Function: create],
      init: [Function: init],
      mixIn: [Function: mixIn],
      clone: [Function: clone]
    },
    WordArray: {
      init: [Function: init],
      toString: [Function: toString],
      concat: [Function: concat],
      clamp: [Function: clamp],
      clone: [Function: clone],
      random: [Function: random],
      '$super': [Object]
    },
    BufferedBlockAlgorithm: {
      reset: [Function: reset],
      _append: [Function: _append],
      _process: [Function: _process],
      clone: [Function: clone],
      _minBufferSize: 0,
      init: [Function (anonymous)],
      '$super': [Object]
    },
    Hasher: {
      cfg: [Object],
      init: [Function: init],
      reset: [Function: reset],
      update: [Function: update],
      finalize: [Function: finalize],
      blockSize: 16,
      _createHelper: [Function: _createHelper],
      _createHmacHelper: [Function: _createHmacHelper],
      '$super': [Object]
    },
    Cipher: {
      cfg: [Object],
      createEncryptor: [Function: createEncryptor],
      createDecryptor: [Function: createDecryptor],
      init: [Function: init],
      reset: [Function: reset],
      process: [Function: process],
      finalize: [Function: finalize],
      keySize: 4,
      ivSize: 4,
      _ENC_XFORM_MODE: 1,
      _DEC_XFORM_MODE: 2,
      _createHelper: [Function: _createHelper],
      '$super': [Object]
    },
    StreamCipher: {
      _doFinalize: [Function: _doFinalize],
      blockSize: 1,
      init: [Function (anonymous)],
      '$super': [Object]
    },
    BlockCipherMode: {
      createEncryptor: [Function: createEncryptor],
      createDecryptor: [Function: createDecryptor],
      init: [Function: init],
      '$super': [Object]
    },
    BlockCipher: {
      cfg: [Object],
      reset: [Function: reset],
      _doProcessBlock: [Function: _doProcessBlock],
      _doFinalize: [Function: _doFinalize],
      blockSize: 4,
      init: [Function (anonymous)],
      '$super': [Object]
    },
    CipherParams: {
      init: [Function: init],
      toString: [Function: toString],
      '$super': [Object]
    },
    SerializableCipher: {
      cfg: [Object],
      encrypt: [Function: encrypt],
      decrypt: [Function: decrypt],
      _parse: [Function: _parse],
      init: [Function (anonymous)],
      '$super': [Object]
    },
    PasswordBasedCipher: {
      cfg: [Object],
      encrypt: [Function: encrypt],
      decrypt: [Function: decrypt],
      init: [Function (anonymous)],
      '$super': [Object]
    }
  },
  enc: {
    Hex: { stringify: [Function: stringify], parse: [Function: parse] },
    Latin1: { stringify: [Function: stringify], parse: [Function: parse] },
    Utf8: { stringify: [Function: stringify], parse: [Function: parse] },
    Base64: {
      stringify: [Function: stringify],
      parse: [Function: parse],
      _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
    }
  },
  algo: {
    MD5: {
      _doReset: [Function: _doReset],
      _doProcessBlock: [Function: _doProcessBlock],
      _doFinalize: [Function: _doFinalize],
      clone: [Function: clone],
      init: [Function (anonymous)],
      '$super': [Object]
    },
    EvpKDF: {
      cfg: [Object],
      init: [Function: init],
      compute: [Function: compute],
      '$super': [Object]
    },
    DES: {
      _doReset: [Function: _doReset],
      encryptBlock: [Function: encryptBlock],
      decryptBlock: [Function: decryptBlock],
      _doCryptBlock: [Function: _doCryptBlock],
      keySize: 2,
      ivSize: 2,
      blockSize: 2,
      init: [Function (anonymous)],
      '$super': [Object]
    },
    TripleDES: {
      _doReset: [Function: _doReset],
      encryptBlock: [Function: encryptBlock],
      decryptBlock: [Function: decryptBlock],
      keySize: 6,
      ivSize: 2,
      blockSize: 2,
      init: [Function (anonymous)],
      '$super': [Object]
    }
  },
  MD5: [Function (anonymous)],
  HmacMD5: [Function (anonymous)],
  EvpKDF: [Function (anonymous)],
  mode: {
    CBC: {
      init: [Function (anonymous)],
      '$super': [Object],
      Encryptor: [Object],
      Decryptor: [Object]
    }
  },
  pad: { Pkcs7: { pad: [Function: pad], unpad: [Function: unpad] } },
  format: {
    OpenSSL: { stringify: [Function: stringify], parse: [Function: parse] }
  },
  kdf: { OpenSSL: { execute: [Function: execute] } },
  DES: { encrypt: [Function: encrypt], decrypt: [Function: decrypt] },
  TripleDES: { encrypt: [Function: encrypt], decrypt: [Function: decrypt] }
}

然后把具体加密的那段代码也拷贝过来执行

var _key = 'k1fsa01v';
var _iv = 'k1fsa01v';
function encryptByDES(message) {
    var keyHex = CryptoJS.enc.Utf8.parse(_key);
    var encrypted = CryptoJS.DES.encrypt(message, keyHex, {
        iv: CryptoJS.enc.Utf8.parse(_iv),
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    });
    return encrypted.toString();
}
function decryptByDES(ciphertext) {
    var keyHex = CryptoJS.enc.Utf8.parse(_key);
    var decrypted = CryptoJS.DES.decrypt({
        ciphertext: CryptoJS.enc.Base64.parse(ciphertext)
    }, keyHex, {
        iv: CryptoJS.enc.Utf8.parse(_iv),
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    });
    return decrypted.toString(CryptoJS.enc.Utf8);
}
console.log(encryptByDES('a123456'));

结果报错了:

(base) qin@wuanjuns-MacBook-Pro jsprojects % node test.js 
/Users/qin/dev/jsprojects/jsprojects/test.js:527
              var b = a.createEncryptor;
                        ^
TypeError: Cannot read properties of undefined (reading 'createEncryptor')

因为我们是全扣的代码,不存在少扣的问题,这个时候把这段代码拷贝到浏览器里执行发现正常输出了
在这里插入图片描述
大家回想一下在具体的加密代码那个页面是不是还有一段 html 代码,我们去看看是什么东西

(function () {
    document.write("<script language=\"javascript\" type=\"text/javascript\" src=\"//assets.91118.com/js/rollups/tripledes.js\"></script><script language=\"javascript\" type=\"text/javascript\" src=\"//assets.91118.com/js/components/mode-ecb.js\"></script>");
})();

代码比较简单,就是一个自执行函数,引入了两个 js scripttripledes.js就是定义 CryptoJS 的文件,我们看另外一个 mode-ecb.js 是什么

CryptoJS.mode.ECB = (function () {
    var ECB = CryptoJS.lib.BlockCipherMode.extend();

    ECB.Encryptor = ECB.extend({
        processBlock: function (words, offset) {
            this._cipher.encryptBlock(words, offset);
        }
    });

    ECB.Decryptor = ECB.extend({
        processBlock: function (words, offset) {
            this._cipher.decryptBlock(words, offset);
        }
    });

    return ECB;
}());

补充了 CryptoJSECB mode,我们在刚才代码中补上这段代码再执行结果如下

>> node test.js
>> bb3mlkFBqqo=

返回了正确的加密后的结果
另外,在接口请求参数中还有一个验证码参数 ckcode ,这个验证码是一个 img 元素,直接请求网址https://www.91118.com/Passport/Account/Login 并解析返回的 html 内容即可拿到 img 这里我门就不做赘述了,像这种简单的验证码推荐大家使用免费的验证码识别库 ddddocr(https://github.com/sml2h3/ddddocr),这里直接给出识别代码

# -*- coding: utf-8 -*-
import ddddocr

ocr = ddddocr.DdddOcr()

image = open("code.jpg", "rb").read()
result = ocr.classification(image)
print(result)

在这里插入图片描述
识别成功

逆向结果

到这里整个逆向过程就全部结束了,整个流程走下来就两点

  1. 扣代码——CryptoJS加密模块,尤其是后面那个 CryptoJS.mode.ECB,真的是很有迷惑性;
  2. 验证码识别——这个没有什么可讲的,直接上 ddddocr 硬怼就行了
  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诗雅颂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值