上午的时候,收到 梦不见的梦 的一条 qq 消息,说出现了授权问题。
看了下提示域名,大概率就是 tm 百度地图弹的,那个域名做了个足迹应用就这么放着。
就在上个月自己更换 ssl 证书的时候还一切正常,结果现在来了这么一出。本来就是个人开发者,纯自用的东西,还经常收到百度的电话让升级企业认证,之前就是不小心升级了,结果一年要五万的授权费用。
我 tm 就自己玩的,还需要花钱,真 tm 服了。看来这戏破玩意儿都完犊子之后,最后能玩的也就只剩下天地图了。
刚开始是以为嵌入的问题,看了下嵌入页面都会提示:
各种提示信息:
【d45a31】未获取商用授权,平台资源与服务稳定性受限;详情信息请前往:https://lbs.baidu.com/faq/search?id=314&title=908
并且后来发现,不单纯是弹窗在地图的贴图上也会出现授权提示,不得不说。这狗皮膏药贴的有水平。
对应的 js:
地址:
https://api.map.baidu.com/?qt=cen&b=7597813.822562976%2C687420.7063757228%3B15519477.822562976%2C7879996.706375723&l=5&ie=utf-8&oue=1&fromproduct=jsapi&ak=BxlnBNX55clLsUHVFZlaukyJesN5F5VI&callback=BMapGL._rd._cbk28033&v=gl&seckey=hNJCxM58roJGqVuMKRuYPEZfDZ%2FdhlL4Pp7JxBsDoLNUSc3QN6CRIbBdAJ%2FOt7zPayXwFYMsbLGx0%2BZUValnOg%3D%3D%2ChNJCxM58roJGqVuMKRuYPEZfDZ_dhlL4Pp7JxBsDoLPlabo3s2HGFnIPFXI8e1esM9-LzywgHJdkZjwHcr89ZaWfdPB6XAgd7DE4lcFgxfu8J0x_GywX0u2he7lW2roGgTrZQMyK7kcSPMHFwMyFrx45ktBewEco-xsnR-zdIctg5lIvP6h-iihbsl6ehY9AbrnGwefDt0BtO6gKCedJ8PeXNIUPh2rilmKFv6PZRd0&timeStamp=1763012808396&sign=07d57e493b29
内容:
/**/BMapGL._rd._cbk28033 && BMapGL._rd._cbk28033({"result":{"b":"7597813.822562976,687420.7063757228;15519477.822562976,7879996.706375723","callback":"BMapGL._rd._cbk28033","catalogID":0,"count":0,"current_null":1,"db":0,"error":503,"error_msg":"未获取商用授权,平台资源与服务稳定性受限;详情信息请前往:https://lbs.baidu.com/faq/search?id=314\u0026title=908","fromproduct":"jsapi","ie":"utf-8","jump_back":0,"l":"5","op_gel":0,"oue":"1","popup":1,"qt":"cen","requery":"","res_l":-1,"res_x":"0.000000","res_y":"0.000000","return_query":"","seckey":"hNJCxM58roJGqVuMKRuYPEZfDZ/dhlL4Pp7JxBsDoLNUSc3QN6CRIbBdAJ/Ot7zPayXwFYMsbLGx0+ZUValnOg==,hNJCxM58roJGqVuMKRuYPEZfDZ_dhlL4Pp7JxBsDoLPlabo3s2HGFnIPFXI8e1esM9-LzywgHJdkZjwHcr89ZaWfdPB6XAgd7DE4lcFgxfu8J0x_GywX0u2he7lW2roGgTrZQMyK7kcSPMHFwMyFrx45ktBewEco-xsnR-zdIctg5lIvP6h-iihbsl6ehY9AbrnGwefDt0BtO6gKCedJ8PeXNIUPh2rilmKFv6PZRd0","sign":"07d57e493b29","spec_dispnum":0,"time":0,"timeStamp":"1763012808396","total":0,"tp":0,"type":11,"v":"gl","wd":"","wd2":"","what":"","where":""},"current_city":{"code":0,"geo":"","level":0,"name":"","sup":0,"sup_bus":0,"sup_business_area":0,"sup_lukuang":0,"sup_subway":0,"type":0,"up_province_name":""},"hot_city":["北京市|131","上海市|289","广州市|257","深圳市|340","成都市|75","天津市|332","南京市|315","杭州市|179","武汉市|218","重庆市|132"]})
地址:
https://api.map.baidu.com/?qt=verify&v=gl&type=webgl&ak=BxlnBNX55clLsUHVFZlaukyJesN5F5VI&time=1763012794639&callback=BMapGL.bmapVerifyCbk
内容:
/**/BMapGL.bmapVerifyCbk && BMapGL.bmapVerifyCbk({"error":503,"error_msg":"未获取商用授权,平台资源与服务稳定性受限;详情信息请前往:https://lbs.baidu.com/faq/search?id=314\u0026title=908","popup":1})
我这个破玩意儿就是纯粹个玩具啊,你何苦这么狠心呢?!
对于这个东西,其实我也没啥好办法,刚开始登录百度 lbs 地图后台提示账号要年审。结果进行账号年审之后依然提示这个错误:
这尼玛就离谱了啊,既然你年审不能解决,那就直接 hook 大法:
<!-- 拦截百度地图弹窗相关请求和Hook方法 -->
<script>
(function() {
// 1. 拦截 fetch 请求
const originalFetch = window.fetch;
window.fetch = function(...args) {
const url = args[0];
if (typeof url === 'string' && url.includes('api.map.baidu.com')) {
// 检查是否是弹窗相关的请求
if (url.includes('qt=verify') || url.includes('qt=cen')) {
console.log('拦截百度地图弹窗请求:', url);
// 返回一个模拟的成功响应,避免弹窗
return Promise.resolve(new Response(JSON.stringify({
error: 0,
error_msg: "",
popup: 0,
result: {
error: 0,
popup: 0
}
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
}));
}
}
return originalFetch.apply(this, args);
};
// 2. 拦截 XMLHttpRequest
const originalXHROpen = XMLHttpRequest.prototype.open;
const originalXHRSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
this._url = url;
if (typeof url === 'string' && url.includes('api.map.baidu.com')) {
if (url.includes('qt=verify') || url.includes('qt=cen')) {
console.log('拦截百度地图弹窗XHR请求:', url);
// 标记为已拦截,在send时处理
this._intercepted = true;
}
}
return originalXHROpen.apply(this, [method, url, ...rest]);
};
XMLHttpRequest.prototype.send = function(...args) {
if (this._intercepted) {
// 模拟成功响应
Object.defineProperty(this, 'status', { value: 200, writable: false });
Object.defineProperty(this, 'statusText', { value: 'OK', writable: false });
Object.defineProperty(this, 'responseText', {
value: JSON.stringify({
error: 0,
error_msg: "",
popup: 0,
result: {
error: 0,
popup: 0
}
}),
writable: false
});
Object.defineProperty(this, 'readyState', { value: 4, writable: false });
// 触发事件
if (this.onreadystatechange) {
this.onreadystatechange();
}
if (this.onload) {
this.onload();
}
return;
}
return originalXHRSend.apply(this, args);
};
// 3. 拦截 JSONP 回调(百度地图使用JSONP)
const originalCreateElement = document.createElement;
const originalAppendChild = Node.prototype.appendChild;
const originalInsertBefore = Node.prototype.insertBefore;
document.createElement = function(tagName, ...rest) {
const element = originalCreateElement.apply(this, [tagName, ...rest]);
// 处理div元素,防止创建弹窗
if (tagName.toLowerCase() === 'div') {
const originalSetAttribute = element.setAttribute;
const originalAddClass = element.classList?.add;
// Hook setAttribute,检查可能用于弹窗的属性
element.setAttribute = function(name, value) {
if (typeof value === 'string' && (
value.includes('商用授权') ||
value.includes('business_accredit') ||
value.includes('lbs.baidu.com') ||
(name === 'class' && (value.includes('dialog') || value.includes('modal') || value.includes('popup')))
)) {
console.log('拦截百度地图弹窗元素创建:', name, value);
// 不设置属性,或者设置为隐藏
if (name === 'style') {
return originalSetAttribute.call(this, name, 'display:none !important;');
}
return; // 不设置属性
}
return originalSetAttribute.apply(this, arguments);
};
// Hook classList.add
if (element.classList && originalAddClass) {
const classListAdd = element.classList.add;
element.classList.add = function(...tokens) {
for (let token of tokens) {
if (typeof token === 'string' && (
token.includes('dialog') ||
token.includes('modal') ||
token.includes('popup') ||
token.includes('alert')
)) {
console.log('拦截百度地图弹窗class添加:', token);
continue; // 跳过这个class
}
}
return classListAdd.apply(this, tokens);
};
}
// Hook appendChild,如果添加了包含授权相关文本的子元素,则隐藏
const originalDivAppendChild = element.appendChild;
element.appendChild = function(child) {
if (child && child.textContent) {
const text = child.textContent;
if (text.includes('商用授权') ||
text.includes('business_accredit') ||
text.includes('lbs.baidu.com') ||
text.includes('未完成商用授权')) {
console.log('拦截百度地图弹窗内容添加');
// 隐藏元素而不是添加
if (child.style) {
child.style.display = 'none';
}
}
}
return originalDivAppendChild.apply(this, arguments);
};
}
if (tagName.toLowerCase() === 'script') {
const originalSetAttribute = element.setAttribute;
const originalSetProperty = Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, 'src')?.set;
// Hook setAttribute
element.setAttribute = function(name, value) {
if (name === 'src' && typeof value === 'string' && value.includes('api.map.baidu.com')) {
// 拦截所有包含callback参数的JSONP请求(包括缩放等操作触发的请求)
// 只要包含callback参数就拦截,确保所有百度地图API回调都被处理
if (value.includes('callback=')) {
console.log('拦截百度地图JSONP请求 (setAttribute):', value);
value = modifyJsonpCallback(value);
}
}
return originalSetAttribute.apply(this, arguments);
};
// Hook src 属性设置
if (originalSetProperty) {
Object.defineProperty(element, 'src', {
set: function(value) {
if (typeof value === 'string' && value.includes('api.map.baidu.com')) {
// 拦截所有包含callback参数的JSONP请求
if (value.includes('callback=')) {
console.log('拦截百度地图JSONP请求 (src property):', value);
value = modifyJsonpCallback(value);
}
}
originalSetProperty.call(this, value);
},
get: function() {
return this.getAttribute('src');
},
configurable: true
});
}
// 标记这个script元素,以便在添加到DOM时检查
element._isBaiduMapScript = true;
}
return element;
};
// Hook appendChild 和 insertBefore,在添加到DOM前最后检查
Node.prototype.appendChild = function(child) {
if (child && child._isBaiduMapScript && child.src) {
// 拦截所有包含callback的百度地图请求
if (child.src.includes('api.map.baidu.com') && child.src.includes('callback=')) {
// 确保已经修改了回调
if (!child.src.includes('_baidu_map_popup_blocker_')) {
child.src = modifyJsonpCallback(child.src);
}
}
}
return originalAppendChild.apply(this, arguments);
};
Node.prototype.insertBefore = function(newNode, referenceNode) {
if (newNode && newNode._isBaiduMapScript && newNode.src) {
// 拦截所有包含callback的百度地图请求
if (newNode.src.includes('api.map.baidu.com') && newNode.src.includes('callback=')) {
// 确保已经修改了回调
if (!newNode.src.includes('_baidu_map_popup_blocker_')) {
newNode.src = modifyJsonpCallback(newNode.src);
}
}
}
return originalInsertBefore.apply(this, arguments);
};
// 修改JSONP回调的辅助函数
function modifyJsonpCallback(url) {
// 匹配 callback=xxx 或 callback=xxx.xxx.xxx 格式
return url.replace(
/callback=([^&]+)/,
(match, callbackName) => {
// 创建一个包装函数,修改返回数据
const wrapperName = '_baidu_map_popup_blocker_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
const callbackPath = callbackName.split('.');
// 创建包装回调函数
window[wrapperName] = function(data) {
// 深度修改所有可能的弹窗相关字段
function removePopupFields(obj) {
if (!obj || typeof obj !== 'object') return;
// 修改当前对象的字段
if (obj.popup !== undefined) {
obj.popup = 0;
}
if (obj.error !== undefined && obj.error === 503) {
obj.error = 0;
obj.error_msg = "";
}
// 递归处理嵌套对象
for (let key in obj) {
if (obj.hasOwnProperty(key) && typeof obj[key] === 'object') {
removePopupFields(obj[key]);
}
}
}
if (data && typeof data === 'object') {
removePopupFields(data);
}
// 调用原始回调
let callback = window;
for (let i = 0; i < callbackPath.length; i++) {
if (callback && callback[callbackPath[i]]) {
callback = callback[callbackPath[i]];
} else {
callback = null;
break;
}
}
if (callback && typeof callback === 'function') {
try {
callback(data);
} catch(e) {
console.error('Error calling original callback:', e);
}
}
// 延迟清理包装函数,确保回调已执行
setTimeout(function() {
try {
delete window[wrapperName];
} catch(e) {}
}, 1000);
};
return 'callback=' + wrapperName;
}
);
}
// 4. Hook BMapGL 相关方法(如果已经加载)
function hookBMapGLMethods() {
if (window.BMapGL) {
// Hook bmapVerifyCbk 回调
if (window.BMapGL.bmapVerifyCbk) {
const originalVerifyCbk = window.BMapGL.bmapVerifyCbk;
// 深度修改弹窗字段的辅助函数
function removePopupFieldsDeepVerify(obj) {
if (!obj || typeof obj !== 'object') return;
if (obj.popup !== undefined) obj.popup = 0;
if (obj.error !== undefined && obj.error === 503) {
obj.error = 0;
obj.error_msg = "";
}
for (let key in obj) {
if (obj.hasOwnProperty(key) && typeof obj[key] === 'object' && obj[key] !== null) {
removePopupFieldsDeepVerify(obj[key]);
}
}
}
window.BMapGL.bmapVerifyCbk = function(data) {
if (data && typeof data === 'object') {
removePopupFieldsDeepVerify(data);
}
return originalVerifyCbk ? originalVerifyCbk.call(this, data) : undefined;
};
}
// Hook 动态回调(如 _rd._cbk28033)
if (window.BMapGL._rd) {
const originalRd = window.BMapGL._rd;
// Hook _cbk 方法(用于创建动态回调)
if (originalRd._cbk) {
const originalCbk = originalRd._cbk;
// 深度修改弹窗字段的辅助函数(局部定义)
function removePopupFieldsDeepLocal(obj) {
if (!obj || typeof obj !== 'object') return;
if (obj.popup !== undefined) obj.popup = 0;
if (obj.error !== undefined && obj.error === 503) {
obj.error = 0;
obj.error_msg = "";
}
for (let key in obj) {
if (obj.hasOwnProperty(key) && typeof obj[key] === 'object' && obj[key] !== null) {
removePopupFieldsDeepLocal(obj[key]);
}
}
}
originalRd._cbk = function(callbackName) {
const originalCallback = originalCbk.call(this, callbackName);
if (typeof originalCallback === 'function') {
return function(data) {
if (data && typeof data === 'object') {
removePopupFieldsDeepLocal(data);
}
return originalCallback.call(this, data);
};
}
return originalCallback;
};
}
// 使用 Proxy 拦截所有动态创建的回调属性
try {
// 深度修改弹窗字段的辅助函数(用于Proxy)
function removePopupFieldsDeepProxy(obj) {
if (!obj || typeof obj !== 'object') return;
if (obj.popup !== undefined) obj.popup = 0;
if (obj.error !== undefined && obj.error === 503) {
obj.error = 0;
obj.error_msg = "";
}
for (let key in obj) {
if (obj.hasOwnProperty(key) && typeof obj[key] === 'object' && obj[key] !== null) {
removePopupFieldsDeepProxy(obj[key]);
}
}
}
const rdProxy = new Proxy(originalRd, {
set: function(target, prop, value) {
if (typeof prop === 'string' && prop.startsWith('_cbk') && typeof value === 'function') {
// 包装回调函数
target[prop] = function(data) {
if (data && typeof data === 'object') {
removePopupFieldsDeepProxy(data);
}
return value.call(this, data);
};
return true;
}
target[prop] = value;
return true;
}
});
window.BMapGL._rd = rdProxy;
} catch(e) {
console.log('Proxy not supported, using fallback method');
}
}
// 深度修改弹窗字段的辅助函数
function removePopupFieldsDeep(obj) {
if (!obj || typeof obj !== 'object') return;
// 修改当前对象的字段
if (obj.popup !== undefined) {
obj.popup = 0;
}
if (obj.error !== undefined && obj.error === 503) {
obj.error = 0;
obj.error_msg = "";
}
// 递归处理嵌套对象
for (let key in obj) {
if (obj.hasOwnProperty(key) && typeof obj[key] === 'object' && obj[key] !== null) {
removePopupFieldsDeep(obj[key]);
}
}
}
// 定期检查并Hook新创建的回调函数
const callbackMonitor = setInterval(function() {
if (window.BMapGL && window.BMapGL._rd) {
// 检查所有以_cbk开头的属性
for (let key in window.BMapGL._rd) {
if (key.startsWith('_cbk') && typeof window.BMapGL._rd[key] === 'function') {
const originalCbk = window.BMapGL._rd[key];
// 如果还没有被Hook过
if (!originalCbk._hooked) {
window.BMapGL._rd[key] = function(data) {
if (data && typeof data === 'object') {
removePopupFieldsDeep(data);
}
return originalCbk.call(this, data);
};
window.BMapGL._rd[key]._hooked = true;
console.log('Hook BMapGL callback:', key);
}
}
}
}
// 也检查BMapGL上的其他回调
if (window.BMapGL) {
for (let key in window.BMapGL) {
if ((key.includes('cbk') || key.includes('Cbk') || key.includes('callback') || key.includes('Callback'))
&& typeof window.BMapGL[key] === 'function' && !window.BMapGL[key]._hooked) {
const originalCbk = window.BMapGL[key];
window.BMapGL[key] = function(data) {
if (data && typeof data === 'object') {
removePopupFieldsDeep(data);
}
return originalCbk.call(this, data);
};
window.BMapGL[key]._hooked = true;
console.log('Hook BMapGL callback:', key);
}
}
}
}, 100); // 更频繁的检查(100ms)
// 30秒后停止监控(给足够时间处理所有回调)
setTimeout(function() {
clearInterval(callbackMonitor);
}, 30000);
}
}
// 5. 监听百度地图API加载完成
const checkInterval = setInterval(function() {
if (window.BMapGL) {
hookBMapGLMethods();
clearInterval(checkInterval);
}
}, 100);
// 6. 全局拦截弹窗显示方法
// 拦截 alert
const originalAlert = window.alert;
window.alert = function(message) {
if (typeof message === 'string' && (
message.includes('商用授权') ||
message.includes('lbs.baidu.com') ||
message.includes('business_accredit') ||
message.includes('未完成商用授权')
)) {
console.log('拦截百度地图授权弹窗 (alert):', message);
return;
}
return originalAlert.apply(this, arguments);
};
// 拦截 confirm
const originalConfirm = window.confirm;
window.confirm = function(message) {
if (typeof message === 'string' && (
message.includes('商用授权') ||
message.includes('lbs.baidu.com') ||
message.includes('business_accredit') ||
message.includes('未完成商用授权')
)) {
console.log('拦截百度地图授权弹窗 (confirm):', message);
return true; // 返回true避免阻塞
}
return originalConfirm.apply(this, arguments);
};
// 拦截 prompt
const originalPrompt = window.prompt;
window.prompt = function(message, defaultText) {
if (typeof message === 'string' && (
message.includes('商用授权') ||
message.includes('lbs.baidu.com') ||
message.includes('business_accredit')
)) {
console.log('拦截百度地图授权弹窗 (prompt):', message);
return null;
}
return originalPrompt.apply(this, arguments);
};
// 7. 拦截可能用于显示弹窗的方法
window.addEventListener('load', function() {
// 拦截可能用于显示弹窗的全局函数
const popupKeywords = ['商用授权', 'business_accredit', 'lbs.baidu.com', '未完成商用授权'];
// 监控DOM变化,移除包含授权相关文本的元素
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1) { // Element node
const text = node.textContent || '';
const className = node.className || '';
const id = node.id || '';
// 检查是否包含授权相关文本
if (popupKeywords.some(keyword =>
text.includes(keyword) ||
className.includes(keyword) ||
id.includes(keyword)
)) {
console.log('检测到百度地图弹窗元素,正在移除:', node);
// 立即移除或隐藏
if (node.parentNode) {
node.style.display = 'none';
// 延迟移除,避免影响其他功能
setTimeout(function() {
if (node.parentNode) {
node.parentNode.removeChild(node);
}
}, 100);
}
}
}
});
});
});
// 开始观察DOM变化
observer.observe(document.body, {
childList: true,
subtree: true
});
// 定期检查并移除弹窗元素
const popupChecker = setInterval(function() {
const allElements = document.querySelectorAll('*');
allElements.forEach(function(el) {
const text = el.textContent || '';
const className = el.className || '';
const id = el.id || '';
const style = window.getComputedStyle(el);
// 检查是否是可见的弹窗元素
if (popupKeywords.some(keyword =>
(text.includes(keyword) || className.includes(keyword) || id.includes(keyword)) &&
style.display !== 'none' &&
(style.position === 'fixed' || style.position === 'absolute') &&
(parseInt(style.zIndex) > 1000 || style.zIndex === 'auto')
)) {
console.log('检测到百度地图弹窗,正在移除:', el);
el.style.display = 'none';
setTimeout(function() {
if (el.parentNode) {
el.parentNode.removeChild(el);
}
}, 100);
}
});
}, 500);
// 30秒后停止检查
setTimeout(function() {
clearInterval(popupChecker);
}, 30000);
});
})();
</script>
实际效果:
不过既然是 hook,存在一定的稳定性问题,有可能会失效。
等哪天彻底被恶心够了,就直接换地图 sdk 了!






15 comments
哈哈,用魔法来打败魔法吧
是哒,暂时先别恶心我就行,😂
破解了。不错~
主要是吃相太难看了
虽然不懂这是什么,但是我从来不碰百度的东西,太恶心了这公司。
的确不咋地
技术流真厉害!国内的厂,不论大小,吃相大都不好看
是的 一点不错
怪不得人家都说免费的东西才是最贵的
都是阴性成本
用 apache Echart 就没问题哦,开源的
这玩意儿太不专业了
我当时用地图就是因为他的一个消息提示转用的高德
百度这个司马玩意儿就不是个东西
现在有个国家什么部门的地图API可以免费用