whalibmob 5.5.67 → 5.5.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +54 -1
- package/lib/Attestation.js +15 -7
- package/lib/Registration.js +5 -2
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -90,7 +90,19 @@ function enableWireTrace() {
|
|
|
90
90
|
// reformat its lines on the way out. Anything that is not a pino record is
|
|
91
91
|
// passed through byte-for-byte.
|
|
92
92
|
const stamp = () => new Date().toISOString().slice(11, 23);
|
|
93
|
-
|
|
93
|
+
// Traffic arrives while readline owns the bottom line, so writing straight
|
|
94
|
+
// out overwrites the prompt and leaves the two interleaved. Clear the line
|
|
95
|
+
// first and redraw the prompt after, the same way the CLI's own event
|
|
96
|
+
// handlers do.
|
|
97
|
+
const trace = (s) => {
|
|
98
|
+
const live = _rl && process.stdout.isTTY;
|
|
99
|
+
if (live) {
|
|
100
|
+
readline.cursorTo(process.stdout, 0);
|
|
101
|
+
readline.clearLine(process.stdout, 0);
|
|
102
|
+
}
|
|
103
|
+
_origStderrWrite(s + '\n');
|
|
104
|
+
if (live) _rl.prompt(true);
|
|
105
|
+
};
|
|
94
106
|
|
|
95
107
|
// Route the library's internal [DBG] stream through pino at full verbosity.
|
|
96
108
|
// pino writes newline-delimited JSON straight to fd 1 via sonic-boom, which
|
|
@@ -162,6 +174,47 @@ function enableWireTrace() {
|
|
|
162
174
|
}
|
|
163
175
|
}
|
|
164
176
|
|
|
177
|
+
// The Noise handshake never goes through sendNode — sendNode refuses to run
|
|
178
|
+
// until the channel is secured, and the ClientHello/ClientFinish frames are
|
|
179
|
+
// written straight to the TCP socket. Wrap that socket as soon as connect()
|
|
180
|
+
// creates it so the handshake is visible too, then step aside once the
|
|
181
|
+
// channel is up and the stanza-level hooks take over.
|
|
182
|
+
const HS_OUT = ['ClientHello', 'ClientFinish'];
|
|
183
|
+
const HS_IN = ['ServerHello'];
|
|
184
|
+
const origConnect = NoiseSocket.prototype.connect;
|
|
185
|
+
NoiseSocket.prototype.connect = function (...args) {
|
|
186
|
+
const result = origConnect.apply(this, args);
|
|
187
|
+
const sock = this.socket;
|
|
188
|
+
if (sock && !sock.__waTraced) {
|
|
189
|
+
sock.__waTraced = true;
|
|
190
|
+
let outN = 0, inN = 0;
|
|
191
|
+
trace(`\n${C.out}${stamp()} ──▶ TCP CONNECT ${sock.remoteAddress || ''}${C.off}`);
|
|
192
|
+
|
|
193
|
+
const origWrite = sock.write.bind(sock);
|
|
194
|
+
sock.write = (chunk, ...rest) => {
|
|
195
|
+
if (!this.secured && Buffer.isBuffer(chunk)) {
|
|
196
|
+
const name = HS_OUT[outN++] || 'handshake frame';
|
|
197
|
+
trace(`\n${C.out}${stamp()} ──▶ NOISE ${name} (${chunk.length} bytes)${C.off}`);
|
|
198
|
+
trace(` ${C.dim}${chunk.toString('hex')}${C.off}`);
|
|
199
|
+
}
|
|
200
|
+
return origWrite(chunk, ...rest);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
sock.on('data', (d) => {
|
|
204
|
+
if (this.secured) return;
|
|
205
|
+
// Label positionally, but say so when the reply is plainly not a Noise
|
|
206
|
+
// frame — a proxy or captive portal answering in ASCII is worth reading
|
|
207
|
+
// as text rather than staring at its hex.
|
|
208
|
+
const ascii = isPrintable(d);
|
|
209
|
+
const name = ascii ? 'unexpected plaintext reply' : (HS_IN[inN++] || 'handshake frame');
|
|
210
|
+
trace(`\n${C.in}${stamp()} ◀── NOISE ${name} (${d.length} bytes)${C.off}`);
|
|
211
|
+
trace(` ${C.dim}${d.toString('hex').slice(0, 1024)}${C.off}`);
|
|
212
|
+
if (ascii) trace(` ${C.dim}as text: ${JSON.stringify(d.toString('utf8').slice(0, 400))}${C.off}`);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
216
|
+
};
|
|
217
|
+
|
|
165
218
|
const origSendNode = NoiseSocket.prototype.sendNode;
|
|
166
219
|
NoiseSocket.prototype.sendNode = function (node) {
|
|
167
220
|
try { logStanza('OUT', node); } catch (_) {}
|
package/lib/Attestation.js
CHANGED
|
@@ -158,19 +158,27 @@ async function iosAppAttest(noisePubB64) {
|
|
|
158
158
|
// ENC envelope, so the Play Integrity nonce is derived from the store (a stable
|
|
159
159
|
// per-session value), NOT from the ENC body. pushToken comes from the optional
|
|
160
160
|
// push client — empty by default.
|
|
161
|
+
// A field with no value is omitted from the body rather than sent empty.
|
|
162
|
+
// buildForm drops null and keeps '', and the registration server validates the
|
|
163
|
+
// shape of what it receives: an empty push_code is a malformed push_code, and
|
|
164
|
+
// it answers with bad_param/bad_format naming that field. An absent field has
|
|
165
|
+
// nothing to validate, which is what an unattested client should send.
|
|
166
|
+
// Real values are still sent whenever a Frida attestation server supplies them.
|
|
167
|
+
const orNull = (v) => (v === '' || v === undefined ? null : v);
|
|
168
|
+
|
|
161
169
|
async function attestationFields(device, nonceB64, opts) {
|
|
162
170
|
opts = opts || {};
|
|
163
|
-
const pushToken = opts.pushToken
|
|
171
|
+
const pushToken = orNull(opts.pushToken);
|
|
164
172
|
|
|
165
173
|
if (device && device.os === 'android') {
|
|
166
174
|
const a = await androidIntegrity(nonceB64 || '');
|
|
167
175
|
return [
|
|
168
|
-
'gpia', a.gpia,
|
|
169
|
-
'_gg', a.gg,
|
|
170
|
-
'_gi', a.gi,
|
|
171
|
-
'_gp', a.gp,
|
|
172
|
-
'_ge', a.ge,
|
|
173
|
-
'_ga', a.ga,
|
|
176
|
+
'gpia', orNull(a.gpia),
|
|
177
|
+
'_gg', orNull(a.gg),
|
|
178
|
+
'_gi', orNull(a.gi),
|
|
179
|
+
'_gp', orNull(a.gp),
|
|
180
|
+
'_ge', orNull(a.ge),
|
|
181
|
+
'_ga', orNull(a.ga),
|
|
174
182
|
'push_token', pushToken
|
|
175
183
|
];
|
|
176
184
|
}
|
package/lib/Registration.js
CHANGED
|
@@ -424,7 +424,9 @@ function getRequestVerificationCodeParameters(store, method, meta, device, attem
|
|
|
424
424
|
'manage_call_permission', 'false',
|
|
425
425
|
'clicked_education_link', 'false',
|
|
426
426
|
'aid', '',
|
|
427
|
-
|
|
427
|
+
// Omitted unless a push client supplies a real code — an empty value is
|
|
428
|
+
// rejected as bad_format, an absent field is not validated at all.
|
|
429
|
+
'push_code', null
|
|
428
430
|
];
|
|
429
431
|
}
|
|
430
432
|
|
|
@@ -434,7 +436,8 @@ function getRequestVerificationCodeParameters(store, method, meta, device, attem
|
|
|
434
436
|
'sim_mcc', meta.mcc,
|
|
435
437
|
'sim_mnc', meta.mnc,
|
|
436
438
|
'jailbroken', '0',
|
|
437
|
-
|
|
439
|
+
// Omitted unless a push client supplies a real code (see above).
|
|
440
|
+
'push_code', null,
|
|
438
441
|
'cellular_strength', '1'
|
|
439
442
|
];
|
|
440
443
|
}
|