JS逆向入门案例-Trip国际版携c Phantom-Token(补环境Jvmp)-12

文章目录

    • 概要
    • 整体架构流程
    • 技术细节
    • 小结

概要

提示:仅供学习,不得用做商业交易,如有侵权请及时联系

逆向:Trip国际版携c

URL:aHR0cHM6Ly9oay50cmlwLmNvbS9ob3RlbHM=

目的:参数 Phantom-Token 补环境

在这里插入图片描述

接口:aHR0cHM6Ly9oay50cmlwLmNvbS9odGxzL2dldEhvdGVsTGlzdD94LXRyYWNlSUQ9MTczMzQ0OTExMjQ3OS4wOWJlblgyMXNGcEEtMTczMzQ1MDc3OTY5OS0xMTI5Mjc1NTM0

整体架构流程

提示:挂代理补环境并验证参数
1、找到参数加密位置–搜索–断点
在这里插入图片描述
2、分析加密入口和明文
在这里插入图片描述
加密内容就是载荷数据(表单数据)
3、进入方法内,扣下js
在这里插入图片描述
发现跟携程的token如出一辙,都是这种格式的vmp
4、将vmp复制到本地,挂上代理
在这里插入图片描述

	function setProxyArr(proxyObjArr) {
	    for (let i = 0; i < proxyObjArr.length; i++) {
	        const handler = `{
	      get: function(target, property, receiver) {
	        console.log("方法:", "get  ", "对象:", "${proxyObjArr[i]}", "  属性:", property, "  属性类型:", typeof property, ", 属性值:", target[property], ", 属性值类型:", typeof target[property]);
	        return target[property];
	      },
	      set: function(target, property, value, receiver) {
	        console.log("方法:", "set  ", "对象:", "${proxyObjArr[i]}", "  属性:", property, "  属性类型:", typeof property, ", 属性值:", value, ", 属性值类型:", typeof target[property]);
	        return Reflect.set(...arguments);
	      }
	    }`;
	        eval(`try {
	            ${proxyObjArr[i]};
	            ${proxyObjArr[i]} = new Proxy(${proxyObjArr[i]}, ${handler});
	        } catch (e) {
	            ${proxyObjArr[i]} = {};
	            ${proxyObjArr[i]} = new Proxy(${proxyObjArr[i]}, ${handler});
        }`);
    }
}

5、接下来,缺啥补啥
在这里插入图片描述

缺window,我们知道浏览器的window表示全局,而node里面的全局是global和globalThis,那么我们到浏览器看看window有没有这俩个

在这里插入图片描述

我们发现有globalThis

在这里插入图片描述

	CanvasRenderingContext2D = function () { };
	HTMLCanvasElement = function () { };
	navigator = {}
	screen = {}
	document = {}
	setProxyArr(['window','navigator','screen','document','CanvasRenderingContext2D','HTMLCanvasElement'])

在这里插入图片描述

我们发现缺少createElement,在js中dom进行该操作实际上就是在创建标签,所以我们要捕抓它到底创建了什么标签

	document = {
	    createElement: function (res) { 
	        console.log("创建标签:", res);
	    }
}

补完这些我们发现基本环境就已经通了

在这里插入图片描述
6、接下来我们就可以调用signature方法生成Phantom-Token参数值,直接复制o.data表单

	var data = {
	    "guideLogin": "T",
	    "search": {
	        "sessionId": "913eaa06-9980-ce5e-e3e2-5b2b46e3222c",
	        "preHotelCount": 12,
	        "preHotelIds": [
	            115996086,
	            456133,
	            345032,
	            6490490,
	            425224,
	            371241,
	            374627,
	            6398678,
	            474480,
	            447883,
	            109336017,
	            78037552
	        ],
	        "checkIn": "20241206",
	        "checkOut": "20241207",
	        "sourceFromTag": "",
	        "filters": [
	            {
	                "filterId": "17|1",
	                "value": "1",
	                "type": "17",
	                "subType": "2",
	                "sceneType": "17"
	            },
	            {
	                "filterId": "80|0|1",
	                "value": "0",
	                "type": "80",
	                "subType": "2",
	                "sceneType": "80"
	            },
	            {
	                "filterId": "29|1",
	                "value": "1|2",
	                "type": "29"
	            }
	        ],
	        "pageCode": 10320668148,
	        "location": {
	            "geo": {
	                "countryID": 1,
	                "provinceID": 0,
	                "cityID": 30,
	                "districtID": 0,
	                "oversea": false
	            },
	            "coordinates": []
	        },
	        "pageIndex": 2,
	        "pageSize": 10,
	        "needTagMerge": "T",
	        "roomQuantity": 1,
	        "orderFieldSelectedByUser": false,
	        "hotelId": 0,
	        "hotelIds": [],
	        "lat": 22.536674277945078,
	        "lng": 114.06165334380388,
	        "tripWalkDriveSwitch": "T",
	        "resultType": "CT",
	        "nearbyHotHotel": {},
	        "recommendTimes": 0,
	        "crossPromotionId": "",
	        "travellingForWork": false
	    },
	    "batchRefresh": {
	        "batchId": "",
	        "batchSeqNo": 0
	    },
	    "queryTag": "NORMAL",
	    "mapType": "MAPBOX",
	    "extends": {
	        "crossPriceConsistencyLog": "",
	        "NewTaxDescForAmountshowtype0": "B",
	        "TaxDescForAmountshowtype2": "T",
	        "MealTagDependOnMealType": "T",
	        "MultiMainHotelPics": "T",
	        "enableDynamicRefresh": "T",
	        "isFirstDynamicRefresh": "T",
	        "ExposeBedInfos": "F",
	        "TaxDescRemoveRoomNight": "",
	        "priceMaskLoginTip": "",
	        "NeedHotelHighLight": ""
	    },
	    "head": {
	        "platform": "PC",
	        "clientId": "1733449112479.09benX21sFpA",
	        "bu": "ibu",
	        "group": "TRIP",
	        "aid": "",
	        "sid": "",
	        "ouid": "",
	        "caid": "",
	        "csid": "",
	        "couid": "",
	        "region": "HK",
	        "locale": "zh-HK",
	        "timeZone": "8",
	        "currency": "HKD",
	        "p": "89018333664",
	        "pageID": "10320668148",
	        "deviceID": "PC",
	        "clientVersion": "0",
	        "frontend": {
	            "vid": "1733449112479.09benX21sFpA",
	            "sessionID": "1",
	            "pvid": "2"
	        },
	        "extension": [
	            {
	                "name": "cityId",
	                "value": "30"
	            },
	            {
	                "name": "checkIn",
	                "value": "2024/12/06"
	            },
	            {
	                "name": "checkOut",
	                "value": "2024/12/07"
	            },
	            {
	                "name": "region",
	                "value": "HK"
	            }
	        ],
	        "tripSub1": "",
	        "qid": "275606394200",
	        "pid": "4bd353a1-5c1c-45a8-ab88-113ad7b1b866",
	        "hotelExtension": {},
	        "cid": "1733449112479.09benX21sFpA",
	        "traceLogID": "b9fc81848e5e2",
	        "ticket": "",
	        "href": "脱米处理",
	        "deviceConfig": "M"
	    }
	}
	
	console.log(window.signature(data));

继续运行

在这里插入图片描述

	cookie:复制浏览器的cookie
	userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'

在这里插入图片描述
在这里插入图片描述

	crypto = {
	    randomUUID:function randomUUID() {
	        const hexDigits = '0123456789abcdef';
	        let uuid = '';
	    
	        for (let i = 0; i < 36; i++) {
	            if (i === 8 || i === 13 || i === 18 || i === 23) {
	                uuid += '-';
	            } else if (i === 14) {
	                uuid += '4';
	            } else if (i === 19) {
	                uuid += hexDigits[(Math.floor(Math.random() * 4) + 8)];
	            } else {
	                uuid += hexDigits[Math.floor(Math.random() * 16)];
	            }
	        }
	        return uuid;
    }
}

在这里插入图片描述

	webdriver:false

在这里插入图片描述
这里我们看到,dom创建了一个canvas标签,那么为什么这里直接new HTMLCanvasElement对象呢,因为创建canvas对象的原型就是它
在这里插入图片描述
在这里插入图片描述

我们也给代理上,getContext原型链上的方法,创建上下文

在这里插入图片描述

	HTMLCanvasElement = function () {
	    this.getContext = function () {
	        console.log('上下文:',arguments[0])
	    }
};

在这里插入图片描述

	CanvasRenderingContext2D = function () { };
	twod = new CanvasRenderingContext2D();
	HTMLCanvasElement = function () {
	    this.getContext = function () {
	        console.log('上下文:', arguments[0])
	        if (arguments[0] === '2d') {
	            return twod
	        }
	    }
	};

我们创建的2d对象的原型就是CanvasRenderingContext2D

在这里插入图片描述
在这里插入图片描述

canvas对象中的setAttribute在js中就是设置属性和属性值的

	HTMLCanvasElement = function () {
    this.getContext = function () {
        console.log('上下文:', arguments[0])
        if (arguments[0] === '2d') {
            return twod
        }
    };
    this.setAttribute = function () {
        console.log('设置属性:', arguments[0], arguments[1])
    };
};

在这里插入图片描述

该方法在js就拿到一个图片的base64的值,这个我们可以通过浏览器进行hook,拿到该值

		HTMLCanvasElement.prototype.toDataURL_ = HTMLCanvasElement.prototype.toDataURL;
		HTMLCanvasElement.prototype.toDataURL = function () {
		    console.log("toDataURL被劫持");
		    debugger;
		    return HTMLCanvasElement.prototype.toDataURL_.apply(this, arguments);
		}

这里我就直接粘贴出来

	number = 1
	HTMLCanvasElement = function () {
	    this.getContext = function () {
	        console.log('上下文:', arguments[0])
	        if (arguments[0] === '2d') {
	            return twod
	        }
	    };
	    this.setAttribute = function () {
	        console.log('设置属性:', arguments[0], arguments[1])
	    };
	    this.toDataURL = function toDataURL() {
	        if (number === 1) {
	            number += 1
	            return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAAAeCAYAAABHenA+AAAAAXNSR0IArs4c6QAAAMNJREFUeF7t00ENAAAIAzHm3zQu7lUMLGm4nSNAIBNYtmSIAIETnCcgEAoILsQ2RUBwfoBAKCC4ENsUAcH5AQKhgOBCbFMEBOcHCIQCgguxTREQnB8gEAoILsQ2RUBwfoBAKCC4ENsUAcH5AQKhgOBCbFMEBOcHCIQCgguxTREQnB8gEAoILsQ2RUBwfoBAKCC4ENsUAcH5AQKhgOBCbFMEBOcHCIQCgguxTREQnB8gEAoILsQ2RUBwfoBAKCC4ENsUgQen2QAfWCC+5AAAAABJRU5ErkJggg=='
	        }
	        if (number === 2) {
	            return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAAA5JREFUGFdjZIACRhgDAAAtAANNw4UTAAAAAElFTkSuQmCC'
	        }
	    }
	};

这里我们再运行发现报错了,往上找发现CanvasRenderingContext2D去调用了prototype属性,CanvasRenderingContext2D.prototype也需要挂上代理

在这里插入图片描述

在这里插入图片描述

这里就出现了这个属性并没有

	CanvasRenderingContext2D = function () {
	    this.fillRect = function fillRect() { };
	    this.fillText = function fillText() { };
	};
	CanvasRenderingContext2D.prototype.fillRect = function fillRect() { };
	CanvasRenderingContext2D.prototype.fillText = function fillText() { };

在这里插入图片描述

运行发现有报错了,这里其实就是检测原型的toString方法

CanvasRenderingContext2D = function () {
    this.fillRect = function fillRect() { };
    this.fillText = function fillText() { };
};
CanvasRenderingContext2D.prototype.fillRect = function fillRect() { };
CanvasRenderingContext2D.prototype.fillText = function fillText() { };
CanvasRenderingContext2D.prototype.fillRect = function fillRect() { };
CanvasRenderingContext2D.prototype.fillText = function fillText() { };
CanvasRenderingContext2D.prototype.fillRect.toString = function toString() {
    return 'function fillRect() { [native code] }'
}
CanvasRenderingContext2D.prototype.fillText.toString = function toString() {
    return 'function fillText() { [native code] }'
}

twod = new CanvasRenderingContext2D();
number = 1
HTMLCanvasElement = function () {
    this.getContext = function getContext() {
        console.log('上下文:', arguments[0])
        if (arguments[0] === '2d') {
            return twod
        }
    };
    this.setAttribute = function setAttribute() {
        console.log('设置属性:', arguments[0], arguments[1])
    };
    this.toDataURL = function toDataURL() {
        if (number === 1) {
            number += 1
            return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAAAeCAYAAABHenA+AAAAAXNSR0IArs4c6QAAAMNJREFUeF7t00ENAAAIAzHm3zQu7lUMLGm4nSNAIBNYtmSIAIETnCcgEAoILsQ2RUBwfoBAKCC4ENsUAcH5AQKhgOBCbFMEBOcHCIQCgguxTREQnB8gEAoILsQ2RUBwfoBAKCC4ENsUAcH5AQKhgOBCbFMEBOcHCIQCgguxTREQnB8gEAoILsQ2RUBwfoBAKCC4ENsUAcH5AQKhgOBCbFMEBOcHCIQCgguxTREQnB8gEAoILsQ2RUBwfoBAKCC4ENsUgQen2QAfWCC+5AAAAABJRU5ErkJggg=='
        }
        if (number === 2) {
            return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAAA5JREFUGFdjZIACRhgDAAAtAANNw4UTAAAAAElFTkSuQmCC'
        }
    }
};
HTMLCanvasElement.prototype.getContext = function getContext() { };
HTMLCanvasElement.prototype.setAttribute = function setAttribute() { };
HTMLCanvasElement.prototype.toDataURL = function toDataURL() { };
HTMLCanvasElement.prototype.getContext.toString = function toString() {
    return 'function getContext() { [native code] }'
}
HTMLCanvasElement.prototype.setAttribute.toString = function toString(res, res1) {
    return 'function setAttribute() { [native code] }'
}
HTMLCanvasElement.prototype.toDataURL.toString = function toString(res, res1) {
    return 'function toDataURL() { [native code] }'
}
HTMLCanvasElement.prototype.toString = function toString() {
    return '[object HTMLCanvasElement]'
}
HTMLCanvasElement.toString = function toString() {
    return 'function HTMLCanvasElement() { [native code] }'
}

然后我们发现不报错了,继续

在这里插入图片描述

screen = {
    colorDepth: 24,
    width: 1536,
    height: 864,
    availWidth: 1536,
    availHeight: 864,
    pixelDepth: 24,
}
navigator = {
    language: 'zh-CN',
    platform: 'Win32',
    webdriver:false,
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
}

7、最后我们运行看一下
在这里插入图片描述

好家伙居然失败了,肯定检测了啥,我们突然想到了它上面检测了原型链我方法,那么createElement是不是也被检测了

	HTMLDocument = function HTMLDocument() { };
	HTMLDocument.prototype.createElement =  function createElement() {
	    if (res === "canvas") {
	        return canvas
	    }
	}
	HTMLDocument.prototype.createElement.toString = function toString() {
	    return 'function createElement() { [native code] }'
	}
	HTMLDocument.prototype.toString = function toString() { return '[object HTMLDocument]'; }
	document = {cookie:浏览器打印}

8、我们发现成功了
在这里插入图片描述
在这里插入图片描述

技术细节

提示:细节

  1. 需要注意代理的对象不能漏
  2. 仔细观察缺少了什么
  3. 分析检测点

小结

提示:学习交流群:v:wzwzwz0613

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值