Browse Source

fix(QMCv2): overflow error in js decoder

(cherry picked from commit 191ac6a932efb290f49e2824839db20ac6ff47ca)
20230320
MengYX 3 years ago
parent
commit
976077e3e1
No known key found for this signature in database GPG Key ID: E63F9C7303E8F604
  1. 2
      src/decrypt/qmc.test.ts
  2. 8
      src/decrypt/qmc_cipher.test.ts
  3. 7
      src/decrypt/qmc_cipher.ts
  4. 2
      src/decrypt/qmc_key.test.ts
  5. 1
      testdata/mflac_rc4_key.bin
  6. 1
      testdata/mflac_rc4_key_raw.bin
  7. BIN
      testdata/mflac_rc4_raw.bin
  8. BIN
      testdata/mflac_rc4_suffix.bin
  9. BIN
      testdata/mflac_rc4_target.bin

2
src/decrypt/qmc.test.ts

@ -18,7 +18,7 @@ function loadTestDataDecoder(name: string): {
} }
test('qmc: real file', async () => { test('qmc: real file', async () => {
const cases = ['mflac0_rc4', 'mflac_map', 'mgg_map', 'qmc0_static']; const cases = ['mflac0_rc4', 'mflac_rc4', 'mflac_map', 'mgg_map', 'qmc0_static'];
for (const name of cases) { for (const name of cases) {
const { clearText, cipherText } = loadTestDataDecoder(name); const { clearText, cipherText } = loadTestDataDecoder(name);
const c = new QmcDecoder(cipherText); const c = new QmcDecoder(cipherText);

8
src/decrypt/qmc_cipher.test.ts

@ -69,7 +69,7 @@ test('map cipher: real file', async () => {
}); });
test('rc4 cipher: real file', async () => { test('rc4 cipher: real file', async () => {
const cases = ['mflac0_rc4']; const cases = ['mflac0_rc4', 'mflac_rc4'];
for (const name of cases) { for (const name of cases) {
const { key, clearText, cipherText } = loadTestDataCipher(name); const { key, clearText, cipherText } = loadTestDataCipher(name);
const c = new QmcRC4Cipher(key); const c = new QmcRC4Cipher(key);
@ -81,7 +81,7 @@ test('rc4 cipher: real file', async () => {
}); });
test('rc4 cipher: first segment', async () => { test('rc4 cipher: first segment', async () => {
const cases = ['mflac0_rc4']; const cases = ['mflac0_rc4', 'mflac_rc4'];
for (const name of cases) { for (const name of cases) {
const { key, clearText, cipherText } = loadTestDataCipher(name); const { key, clearText, cipherText } = loadTestDataCipher(name);
const c = new QmcRC4Cipher(key); const c = new QmcRC4Cipher(key);
@ -93,7 +93,7 @@ test('rc4 cipher: first segment', async () => {
}); });
test('rc4 cipher: align block (128~5120)', async () => { test('rc4 cipher: align block (128~5120)', async () => {
const cases = ['mflac0_rc4']; const cases = ['mflac0_rc4', 'mflac_rc4'];
for (const name of cases) { for (const name of cases) {
const { key, clearText, cipherText } = loadTestDataCipher(name); const { key, clearText, cipherText } = loadTestDataCipher(name);
const c = new QmcRC4Cipher(key); const c = new QmcRC4Cipher(key);
@ -105,7 +105,7 @@ test('rc4 cipher: align block (128~5120)', async () => {
}); });
test('rc4 cipher: simple block (5120~10240)', async () => { test('rc4 cipher: simple block (5120~10240)', async () => {
const cases = ['mflac0_rc4']; const cases = ['mflac0_rc4', 'mflac_rc4'];
for (const name of cases) { for (const name of cases) {
const { key, clearText, cipherText } = loadTestDataCipher(name); const { key, clearText, cipherText } = loadTestDataCipher(name);
const c = new QmcRC4Cipher(key); const c = new QmcRC4Cipher(key);

7
src/decrypt/qmc_cipher.ts

@ -119,7 +119,7 @@ export class QmcRC4Cipher implements QmcStreamCipher {
// ignore if key char is '\x00' // ignore if key char is '\x00'
if (!value) continue; if (!value) continue;
const next_hash = (this.hash * value) & 0xffffffff; const next_hash = (this.hash * value) >>> 0;
if (next_hash == 0 || next_hash <= this.hash) break; if (next_hash == 0 || next_hash <= this.hash) break;
this.hash = next_hash; this.hash = next_hash;
@ -174,7 +174,8 @@ export class QmcRC4Cipher implements QmcStreamCipher {
// Calculate the number of bytes to skip. // Calculate the number of bytes to skip.
// The initial "key" derived from segment id, plus the current offset. // The initial "key" derived from segment id, plus the current offset.
const skipLen = (offset % QmcRC4Cipher.SEGMENT_SIZE) + this.getSegmentKey(offset / QmcRC4Cipher.SEGMENT_SIZE); const skipLen =
(offset % QmcRC4Cipher.SEGMENT_SIZE) + this.getSegmentKey(Math.floor(offset / QmcRC4Cipher.SEGMENT_SIZE));
// decrypt the block // decrypt the block
let j = 0; let j = 0;
@ -192,7 +193,7 @@ export class QmcRC4Cipher implements QmcStreamCipher {
private getSegmentKey(id: number): number { private getSegmentKey(id: number): number {
const seed = this.key[id % this.N]; const seed = this.key[id % this.N];
const idx = ((this.hash / ((id + 1) * seed)) * 100.0) | 0; const idx = Math.floor((this.hash / ((id + 1) * seed)) * 100.0);
return idx % this.N; return idx % this.N;
} }
} }

2
src/decrypt/qmc_key.test.ts

@ -16,7 +16,7 @@ function loadTestDataKeyDecrypt(name: string): {
} }
test('key dec: real file', async () => { test('key dec: real file', async () => {
const cases = ['mflac_map', 'mgg_map', 'mflac0_rc4']; const cases = ['mflac_map', 'mgg_map', 'mflac0_rc4', 'mflac_rc4'];
for (const name of cases) { for (const name of cases) {
const { clearText, cipherText } = loadTestDataKeyDecrypt(name); const { clearText, cipherText } = loadTestDataKeyDecrypt(name);
const buf = QmcDeriveKey(cipherText); const buf = QmcDeriveKey(cipherText);

1
testdata/mflac_rc4_key.bin

@ -0,0 +1 @@
pUtyvqr0TgAvR95mNmY7DmNl386TsJNAEIz95CEcgIgJCcs28686O7llxD5E74ldn70xMtd5cG58TA5ILw09I8BOTf5EdHKd6wwPn689DUK13y3Req6H0P33my2miJ5bQ2AA22B8vp4V0NJ3hBqNtFf7cId48V6W51e1kwgu1xKKawxe9BByT92MFlqrFaKH32dB2zFgyd38l2P1outr4l2XLq48F9G17ptRz4W8Loxu28RvZgv0BzL26Ht9I2L5VCwMzzt7OeZ55iQs40Tr6k81QGraIUJj5zeBMgJRMTaSgi19hU5x5a08Qd662MbFhZZ0FjVvaDy1nbIDhrC62c1lX6wf70O45h4W42VxloBVeZ9Sef4V7cWrjrEjj3DJ5w2iu6Q9uoal2f4390kue42Um5HcDFWqv3m56k6O89bRV424PaRra1k9Cd2L56IN2zfBYqNo2WP5VC68G8w1hfflOY0O52h4WdcpoHSjZm4b35N7l47dT4dwEXj1U4J5

1
testdata/mflac_rc4_key_raw.bin

@ -0,0 +1 @@
cFV0eXZxcjAF/IXJ9qJT1u5C3S5AgY9BoVtIQNBKfxQMt5hH7BF36ndIJGV5L6qw5h4G0IOIOOewdHmMCNfKJftHM4nv3B0iRlSdqJKdL08wO3sV0v8eZk0OiYAlxgseGcBquQWYS/0b5Lj/Ioi2NfpOthAY9vUiRPnfH3+7/2AJGudHjj4Gg1KkpPW3mXIKbsk+Ou9fhrUqs873BCdsmI6qRmVNhOkLaUcbG6Zin3XU0WkgnnjebR43S8N4bw5BTphFvhy42QvspnD7Ewb1tVZQMQ2N1s38nBjukdfCB9R6aRwITOvg2U7Lr0RjLpbrIn6A6iVilpINjK4VptuKUTlpDXQwgCjoqeHQaHNCWgYpdjB69lXn8km/BfzK7QyDbh0VgTikwAHF9tvPhin3AIDRcU0xsaWYKURRfJelX3pSN495ADlhXdEKL/+l60hVnY7t6iCMxJL3lOtdGtdUYUGUCc76PB1fX+0HTWCcfcwvXTEdczr9J1h2yTeJNqFQ5pNy8vX7Ws8k7vDQVFkw4llZjPhb0kg9aDNePTNIKSGwy/7eofrcUQlC9DI+qqqwQ5abA/93fNsPq6XU3uwawnrbBsdz8DDdjJiEDI7abkPIDIfr/uR0YzgBxW90t5bt6xAtuW+VSYAM7kGxI3RZTl7JgOT60MLyIWkYASrRhRPMGks8zL10ED/4yGTEB1nt

BIN
testdata/mflac_rc4_raw.bin

Binary file not shown.

BIN
testdata/mflac_rc4_suffix.bin

Binary file not shown.

BIN
testdata/mflac_rc4_target.bin

Binary file not shown.
Loading…
Cancel
Save