diff --git a/src/decrypt/qmc.test.ts b/src/decrypt/qmc.test.ts index 9fee8ee..73156bd 100644 --- a/src/decrypt/qmc.test.ts +++ b/src/decrypt/qmc.test.ts @@ -18,7 +18,7 @@ function loadTestDataDecoder(name: string): { } 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) { const { clearText, cipherText } = loadTestDataDecoder(name); const c = new QmcDecoder(cipherText); diff --git a/src/decrypt/qmc_cipher.test.ts b/src/decrypt/qmc_cipher.test.ts index 4c574f7..0f1e0b7 100644 --- a/src/decrypt/qmc_cipher.test.ts +++ b/src/decrypt/qmc_cipher.test.ts @@ -69,7 +69,7 @@ test('map 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) { const { key, clearText, cipherText } = loadTestDataCipher(name); const c = new QmcRC4Cipher(key); @@ -81,7 +81,7 @@ test('rc4 cipher: real file', async () => { }); test('rc4 cipher: first segment', async () => { - const cases = ['mflac0_rc4']; + const cases = ['mflac0_rc4', 'mflac_rc4']; for (const name of cases) { const { key, clearText, cipherText } = loadTestDataCipher(name); const c = new QmcRC4Cipher(key); @@ -93,7 +93,7 @@ test('rc4 cipher: first segment', async () => { }); test('rc4 cipher: align block (128~5120)', async () => { - const cases = ['mflac0_rc4']; + const cases = ['mflac0_rc4', 'mflac_rc4']; for (const name of cases) { const { key, clearText, cipherText } = loadTestDataCipher(name); 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 () => { - const cases = ['mflac0_rc4']; + const cases = ['mflac0_rc4', 'mflac_rc4']; for (const name of cases) { const { key, clearText, cipherText } = loadTestDataCipher(name); const c = new QmcRC4Cipher(key); diff --git a/src/decrypt/qmc_cipher.ts b/src/decrypt/qmc_cipher.ts index 14c0a50..d0d3683 100644 --- a/src/decrypt/qmc_cipher.ts +++ b/src/decrypt/qmc_cipher.ts @@ -119,7 +119,7 @@ export class QmcRC4Cipher implements QmcStreamCipher { // ignore if key char is '\x00' 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; this.hash = next_hash; @@ -174,7 +174,8 @@ export class QmcRC4Cipher implements QmcStreamCipher { // Calculate the number of bytes to skip. // 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 let j = 0; @@ -192,7 +193,7 @@ export class QmcRC4Cipher implements QmcStreamCipher { private getSegmentKey(id: number): number { 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; } } diff --git a/src/decrypt/qmc_key.test.ts b/src/decrypt/qmc_key.test.ts index da392cc..2ec1e96 100644 --- a/src/decrypt/qmc_key.test.ts +++ b/src/decrypt/qmc_key.test.ts @@ -16,7 +16,7 @@ function loadTestDataKeyDecrypt(name: string): { } 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) { const { clearText, cipherText } = loadTestDataKeyDecrypt(name); const buf = QmcDeriveKey(cipherText); diff --git a/testdata/mflac_rc4_key.bin b/testdata/mflac_rc4_key.bin new file mode 100644 index 0000000..8f1104c --- /dev/null +++ b/testdata/mflac_rc4_key.bin @@ -0,0 +1 @@ +pUtyvqr0TgAvR95mNmY7DmNl386TsJNAEIz95CEcgIgJCcs28686O7llxD5E74ldn70xMtd5cG58TA5ILw09I8BOTf5EdHKd6wwPn689DUK13y3Req6H0P33my2miJ5bQ2AA22B8vp4V0NJ3hBqNtFf7cId48V6W51e1kwgu1xKKawxe9BByT92MFlqrFaKH32dB2zFgyd38l2P1outr4l2XLq48F9G17ptRz4W8Loxu28RvZgv0BzL26Ht9I2L5VCwMzzt7OeZ55iQs40Tr6k81QGraIUJj5zeBMgJRMTaSgi19hU5x5a08Qd662MbFhZZ0FjVvaDy1nbIDhrC62c1lX6wf70O45h4W42VxloBVeZ9Sef4V7cWrjrEjj3DJ5w2iu6Q9uoal2f4390kue42Um5HcDFWqv3m56k6O89bRV424PaRra1k9Cd2L56IN2zfBYqNo2WP5VC68G8w1hfflOY0O52h4WdcpoHSjZm4b35N7l47dT4dwEXj1U4J5 \ No newline at end of file diff --git a/testdata/mflac_rc4_key_raw.bin b/testdata/mflac_rc4_key_raw.bin new file mode 100644 index 0000000..1633371 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/testdata/mflac_rc4_raw.bin b/testdata/mflac_rc4_raw.bin new file mode 100644 index 0000000..6f02875 Binary files /dev/null and b/testdata/mflac_rc4_raw.bin differ diff --git a/testdata/mflac_rc4_suffix.bin b/testdata/mflac_rc4_suffix.bin new file mode 100644 index 0000000..3762b4c Binary files /dev/null and b/testdata/mflac_rc4_suffix.bin differ diff --git a/testdata/mflac_rc4_target.bin b/testdata/mflac_rc4_target.bin new file mode 100644 index 0000000..f99e219 Binary files /dev/null and b/testdata/mflac_rc4_target.bin differ