This commit is contained in:
dela
2026-02-21 18:27:49 +08:00
parent 0ac4b23f07
commit 5dc86ccfbf
270 changed files with 49508 additions and 4636 deletions

View File

@@ -2,14 +2,24 @@
* Payload Builder - Assembling the Final Form
*
* Takes all our crafted components and stitches them into
* the exact JSON structure hCaptcha expects.
* the exact structure hCaptcha expects.
*
* From h.html source, the payload object (s) contains:
* {v, sitekey, host, hl, motionData, n, c, rqdata, pst, pd, pdc, pem...}
*
* IMPORTANT: The 'c' field is included in the payload object but gets
* REMOVED before encryption. The encrypted body is then packed as:
* msgpack.encode([JSON.stringify(c), encrypted_payload_without_c])
*/
export class PayloadBuilder {
/**
* Build the getcaptcha request payload
*
* The returned object includes 'c' - the caller is responsible for
* cloning and deleting 'c' before encryption (matching h.html behavior).
*/
static build({ siteKey, host, n, c, motionData }) {
static build({ siteKey, host, n, c, motionData, rqdata = '' }) {
const now = Date.now();
return {
@@ -20,50 +30,43 @@ export class PayloadBuilder {
// Challenge response
n, // Proof of work from hsw.js
c: JSON.stringify(c), // Config from checksiteconfig
// c field — will be stripped before encryption,
// then used separately in body packing as msgpack([c_string, encrypted])
c: typeof c === 'string' ? c : JSON.stringify(c),
// Motion telemetry
motionData: JSON.stringify(motionData),
motionData: typeof motionData === 'string'
? motionData
: JSON.stringify(motionData),
// Timestamps
prev: {
// Additional fields from h.html source
rqdata, // Request data from checksiteconfig
pst: false, // Previous success token
// Performance / detection data
pd: JSON.stringify({
si: now - 5000, // Script init
ce: now - 4500, // Challenge end
cs: now - 4000, // Challenge start
re: now - 500, // Response end
rs: now - 1000, // Response start
}),
pdc: JSON.stringify({}), // Performance data cached
pem: JSON.stringify({}), // Performance event map
// Previous state
prev: JSON.stringify({
escaped: false,
passed: false,
expiredChallenge: false,
expiredResponse: false,
},
// Widget metadata
d: PayloadBuilder._generateWidgetData(host, now),
// Response type
pst: false, // Previous success token
}),
};
}
/**
* Generate widget embedding data
*/
static _generateWidgetData(host, timestamp) {
return {
gt: 0, // Widget type
ct: timestamp - 1000, // Creation time
fc: 1, // Frame count
ff: false, // First frame
// Fake performance metrics
pd: {
si: timestamp - 5000, // Script init
ce: timestamp - 4500, // Challenge end
cs: timestamp - 4000, // Challenge start
re: timestamp - 500, // Response end
rs: timestamp - 1000, // Response start
},
};
}
/**
* Build form-encoded payload (alternative format)
* Build form-encoded payload (alternative format for non-encrypted requests)
*/
static buildFormData(data) {
const params = new URLSearchParams();