mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
603 字
2 分钟
某保网逆向
2026-06-04

某保网逆向#

目标地址:aHR0cHM6Ly9mdXd1Lm5oc2EuZ292LmNuL25hdGlvbmFsSGFsbFN0LyMvc2VhcmNoL2RydWctZGlyZWN0b3J5

一、明确目标#

通过翻页,我们可以看见这个包,它的响应如下,对数据做了个加密。

image-20260604173319051

我们来看看请求头和表单。

image-20260604174016493

image-20260604174049221

很明显,这里有很多参数,但是强校验的只有signData,encData。但是处于学习目的,我们要把所有参数都整出来。

二、请求头参数逆向#

采用直接搜索的方式,定位到如下几个位置。

image-20260604174307186

在这里,我们能拿到大部分需要的参数,在node里复现一下。

function get_headers() {
return {
"x-tif-nonce": get_x_nonce(),
"x-tif-paasid": undefined,
"x-tif-signature": get_signature(),
"x-tif-timestamp": x_timestamp(),
"x-tingyun": get_tingyun(),
}
}

signature是一个sha256算法,传入的是时间戳和get_x_nonce返回值。

function x_timestamp() {
return Math.ceil((new Date).getTime() / 1e3)
}
function get_x_nonce() {
var e, t, n, i = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", r = "0123456789";
return e = o(6, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"),
t = o(1, i),
n = o(1, r),
t + n + e;
function o(e, t) {
e = e || 32;
for (var n = "", i = 0; i < e; i++)
n += t.charAt(Math.ceil(1e3 * Math.random()) % t.length);
return n
}
}
function get_signature() {
s = x_timestamp()
h = get_x_nonce()
f = s + h + s;
return CryptoJS.SHA256(f).toString();
}
function get_tingyun() {
function t(t) {
return t < 0 ? NaN : t <= 30 ? 0 | Math.random() * (1 << t) : t <= 53 ? (0 | Math.random() * (1 << 30)) + (0 | Math.random() * (1 << t - 30)) * (1 << 30) : NaN
}
function e(t, e) {
for (var n = t.toString(16), r = e - n.length, i = "0"; r > 0; r >>>= 1,
i += i)
1 & r && (n = i + n);
return n
}
r = 'c=B|4Nl_NnGbjwY'
function G(n) {
var r = "-";
return n && (r = ""),
e(t(32), 8) + r + e(t(16), 4) + r + e(16384 | t(12), 4) + r + e(32768 | t(14), 4) + r + e(t(48), 12)
}
n = G(!0).substring(0, 16);
return r += ";x=" + n
}

第38行的r是个前缀,网页上定位如下。

image-20260604175349931

把缺失的函数补一下,请求头基本就这样能够拿到了。

三、表单参数逆向#

表单是个大对象,里面重要的是时间戳和encdata。

网站上定位到,把他扣下来。

image-20260604175655286

function get_encdata(t) {
return function (e) {
for (var t = e.data.data && JSON.stringify(e.data.data), n = "", i = 0; i < t.length; i++) {
var r = t.charAt(i)
, o = t.charCodeAt(i);
n += o > 127 ? "\\u" + o.toString(16).padStart(4, "0") : r
}
var a = k(n);
e.data.appCode && e.data.appCode !== p && (p = e.data.appCode);
var s = x(p, m)
, l = S(s, a);
return l.toUpperCase()
}(t)
}

接着补缺少的函数。

其中e这个对象我们可以在网站上寻找一下,不然from函数要再扣很多东西。

e来自于这个自执行函数的传参。

image-20260604181225416

function S(t, n) {
var i = 16 - parseInt(n.length % 16);
n = n.concat(new Array(i).fill(i));
var r = m.encrypt(n, t);
return e.from(r).toString("hex")
}
function k(e) {
var t, n, i = new Array;
t = e.length;
for (var r = 0; r < t; r++)
(n = e.charCodeAt(r)) >= 65536 && n <= 1114111 ? (i.push(n >> 18 & 7 | 240),
i.push(n >> 12 & 63 | 128),
i.push(n >> 6 & 63 | 128),
i.push(63 & n | 128)) : n >= 2048 && n <= 65535 ? (i.push(n >> 12 & 15 | 224),
i.push(n >> 6 & 63 | 128),
i.push(63 & n | 128)) : n >= 128 && n <= 2047 ? (i.push(n >> 6 & 31 | 192),
i.push(63 & n | 128)) : i.push(255 & n);
return i
}
function x(e, t) {
return k(S(k(e.substr(0, 16)), k(t)).toUpperCase().substr(0, 16))
}
m = 'NMVFVILMKT13GEMD3BKPKCTBOQBPZR2P'
p = 'T98HPCGN5ZVVQBS8LZQNOAEXVI9GYHKQ'

m和p都是硬编码,这个自己多试几次就能发现。

接着我们处理这两个

var r = m.encrypt(n, t);
return e.from(r).toString("hex")

s往上找,可以发现是个大模块。

image-20260604175925662

这里其实差不多能发现是webpack,我们直接把整个文件拿下来,放到mywebpack.js然后做一些手脚。

image-20260604180557524

放一个loader加载器,劫持一下它的。

webpack整整70000多行,有大量vue等环境,我们可以补些环境,让它能顺利执行。如果你不想不环境,也可以把需要的模块手动抠出来,大概100多个模块。

环境代码如下

window = globalThis;
delete globalThis.navigator
createConstructor('HTMLCollection', true, [], {},);
createConstructor('EventTarget', true, [], {
addEventListener:function(){},
removeEventListener:function(){}
});
createConstructor('Node', true, [], {}, 'EventTarget');
createConstructor('WindowProperties', true, [], {}, 'EventTarget')
createConstructor('Window', true, [], {}, 'WindowProperties');
createConstructor('Navigator', true, [], {});
createConstructor('Screen', true, [], {}, 'EventTarget');
createConstructor('History', true, [], {});
createConstructor('Location', true, [], {});
createConstructor('Element', true, [], {
getAttribute: function(name) {
return name === 'href' ? _href : null;
},
setAttribute:function(a){
console.log("setAttribute创建",a)
}
},'Node');
createConstructor('HTMLElement', true, [], {},"Element");
createConstructor('HTMLHtmlElement', true, [], {},"HTMLElement");
createConstructor('HTMLAnchorElement', true, [], {},"HTMLElement");
createConstructor('Document', true, [], {
createEvent:function(event){
return {
timestamp:0
}
},
getElementsByTagName:function(name){
console.log("getElementsByTagName————",name)
if (name === 'script') {
var arr = [];
for (var i = 0; i < 10; i++) {
arr.push({
tagName: 'SCRIPT',
src: '',
innerHTML: '',
readyState: 'complete',
type: 'text/javascript',
getAttribute: function(attr) { return null; },
setAttribute: function() {}
});
}
arr.length = 10;
arr.item = function(i) { return this[i] || null; };
return arr;
}
},
createElement:function(name){
console.log("createElement————",name)
if(name==='a'){
return new HTMLAnchorElement({
tagName: 'A',
href: '',
protocol: '',
host: '',
hostname: '',
port: '',
pathname: '/',
search: '',
hash: '',
setAttribute: function(name, value) {
console.log("a.setAttribute:", name, '=', value);
if (name === 'href') {
_href = value;
// ✅ 用 URL 解析
try {
var url = new URL(value, window.location.href || 'https://fuwu.nhsa.gov.cn');
this.href = url.href;
this.protocol = url.protocol.replace(/:$/, '');
this.host = url.host;
this.hostname = url.hostname;
this.port = url.port;
this.pathname = url.pathname.charAt(0) === '/' ? url.pathname : '/' + url.pathname;
this.search = url.search ? url.search.replace(/^\?/, '') : '';
this.hash = url.hash ? url.hash.replace(/^#/, '') : '';
} catch(e) {
this.href = value;
}
} else {
this['_' + name] = value;
}
},
getAttribute: function(name) {
return name === 'href' ? _href : (this['_' + name] || null);
}
},null,"fatdog")
}
}
}, 'Node');
createConstructor('HTMLDocument', true, [], {}, 'Document');
createConstructor('Storage', true, [], {
getItem: function(key) {
console.log('localStorage.getItem获取', key);
return this[key] || null;
},
setItem: function(key, value) {
console.log('localStorage.setItem设置', key, value);
this[key] = String(value);
},
removeItem: function(key) {
console.log('localStorage.removeItem删除', key);
if (this[key]) delete this[key];
},
clear: function() {
this.__storage__ = {};
}
});
window.window = window.self =window.top = window
Object.setPrototypeOf(window, Window.prototype);
location =new Location({
"ancestorOrigins": {},
"href": "https://fuwu.nhsa.gov.cn/nationalHallSt/#/search/drug-directory?code=174000&message=serverUrl%20is%20null&gbFlag=true",
"origin": "https://fuwu.nhsa.gov.cn",
"protocol": "https:",
"host": "fuwu.nhsa.gov.cn",
"hostname": "fuwu.nhsa.gov.cn",
"port": "",
"pathname": "/nationalHallSt/",
"search": "",
"hash": "#/search/drug-directory?code=174000&message=serverUrl%20is%20null&gbFlag=true"
},null,'fatdog')
document = new HTMLDocument({
documentElement:new HTMLHtmlElement({},null,'fatdog'),
querySelector:function(a){
if(a==='base'){
return null;
}
},
cookie:'amap_local=320500',
"location":location,
implementation:{}
},null,"fatdog")
navigator = new Navigator({
userAgent:'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36',
product:'Gecko',
appName:'Netscape',
},null,"fatdog")
screen=new Screen({
},null,"fatdog")
history=new History({
pushState:function(n, r, i) {
var o;
o = e.apply(this, arguments);
try {
Wa(t)
} catch (a) {}
return o
}
},null,'fatdog')
localStorage = new Storage({},null,'fatdog')
// window.location = watch(location,"window.location")
// window.navigator = watch(navigator,'window.navigator')
// window.screen = watch(screen,'window.screen')
// window.history = watch(history,'window.history')
// window.document = watch(document,'window.document')
// window.localStorage = watch(localStorage,"window.localStorage")
// window = watch(window,"window")
_log = console.log
console.log = function(){}

补到这里就能出值了,但是还会报错,我们可以用process.exit(0)截断。

四、解密函数#

function dec_data(t) {
var n = e.from(t.data.data.encData, "hex")
, i = function (t, n) {
var i = sm4.decrypt(n, t)
, r = i[i.length - 1];
return i = i.slice(0, i.length - r),
e.from(i).toString("utf-8")
}(x(p, m), n);
return JSON.parse(i)
}

这个直接拿就行,定位就在encdata的下面,里面的函数基本在前面补齐了,用的SM4解密。

main.js做如下处理

try {
const loader = require('./mywebpack.js');
······
}
catch (e) {
console.log = _log; // 恢复日志
console.log(JSON.stringify({error: e.message}));
process.exit(0);
}
// main.js 最后加上:
process.on('exit', function () {
process.exitCode = 0; // 强制退出码为 0
});

这样可以让vue环境静默,不需要多补环境或者手动去拿模块。

最后那个退出码是为了阻止execjs报错。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

某保网逆向
https://fatdog.20060113.xyz/posts/moubaowang/
作者
神秘大胖狗
发布于
2026-06-04
许可协议
MIT

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00