Added: Harman AIR 51 unpacker for binarydata with custom key

This commit is contained in:
Jindra Petřík
2024-08-18 22:10:21 +02:00
parent 87aea2a2e1
commit c7c4ab8fc0
20 changed files with 219 additions and 40 deletions

View File

@@ -47,9 +47,22 @@ public class HarmanBinaryDataEncrypt {
/**
* Encrypts data.
* @param data Data to encrypt
* @param key Key
* @return Encrypted data
*/
public static byte[] encrypt(byte[] data) {
public static byte[] encrypt(byte[] data, String key) {
byte[] customKey = null;
if (key != null) {
customKey = new byte[16];
int keyLen = key.length();
for(int i = 0; i < 16; ++i) {
customKey[i] = (byte)key.charAt(i % keyLen);
}
}
byte[] result;
try {
SecureRandom random = new SecureRandom();
@@ -69,7 +82,7 @@ public class HarmanBinaryDataEncrypt {
byte[] ivBytes = getIv(hashBytes, random1, random2);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
SecretKeySpec keySpec = new SecretKeySpec(customKey != null ? customKey : keyBytes, "AES");
byte[] dataPadded = new byte[(int) encryptedDataLen];
random.nextBytes(dataPadded);
System.arraycopy(data, 0, dataPadded, 0, data.length);
@@ -153,12 +166,25 @@ public class HarmanBinaryDataEncrypt {
/**
* Decrypts data.
* @param data Encrypted data
* @param key
* @return Decrypted data
*/
public static byte[] decrypt(byte[] data) {
public static byte[] decrypt(byte[] data, String key) {
if (data.length < 32) {
return null;
}
byte[] customKey = null;
if (key != null) {
customKey = new byte[16];
int keyLen = key.length();
for(int i = 0; i < 16; ++i) {
customKey[i] = (byte)key.charAt(i % keyLen);
}
}
long encryptedLen = data.length;
long encryptedLenXorHash = unpack(data, 0);
long decryptedLenXorRandom1 = unpack(data, 4);
@@ -181,7 +207,7 @@ public class HarmanBinaryDataEncrypt {
byte[] ivBytes = getIv(hashBytes, random1, random2);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
SecretKeySpec keySpec = new SecretKeySpec(customKey != null ? customKey : keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
@@ -203,8 +229,8 @@ public class HarmanBinaryDataEncrypt {
*/
public static void main(String[] args) throws IOException {
byte[] data = new byte[]{'A', 'B', 'C'};
byte[] encrypted = encrypt(data);
byte[] decrypted = decrypt(encrypted);
byte[] encrypted = encrypt(data, null);
byte[] decrypted = decrypt(encrypted, null);
if (!Arrays.equals(data, decrypted)) {
throw new RuntimeException("Cannot encrypt/decrypt");
}

View File

@@ -47,9 +47,9 @@ public class HarmanAirPacker implements Packer {
}
@Override
public boolean decrypt(InputStream is, OutputStream os) throws IOException {
public boolean decrypt(InputStream is, OutputStream os, String key) throws IOException {
byte[] encryptedData = Helper.readStream(is);
byte[] decryptedData = HarmanBinaryDataEncrypt.decrypt(encryptedData);
byte[] decryptedData = HarmanBinaryDataEncrypt.decrypt(encryptedData, null);
if (decryptedData == null) {
return false;
}
@@ -58,9 +58,9 @@ public class HarmanAirPacker implements Packer {
}
@Override
public boolean encrypt(InputStream is, OutputStream os) throws IOException {
public boolean encrypt(InputStream is, OutputStream os, String key) throws IOException {
byte[] data = Helper.readStream(is);
byte[] encryptedData = HarmanBinaryDataEncrypt.encrypt(data);
byte[] encryptedData = HarmanBinaryDataEncrypt.encrypt(data, null);
if (encryptedData == null) {
return false;
}
@@ -77,4 +77,9 @@ public class HarmanAirPacker implements Packer {
public String getIdentifier() {
return "harmanair";
}
@Override
public boolean usesKey() {
return false;
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.packers;
import com.jpexs.decompiler.flash.harman.HarmanBinaryDataEncrypt;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.helpers.Helper;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Harman AIR SDK packer.
*
* @author JPEXS
*/
public class HarmanAirPackerWithKey implements Packer {
@Override
public Boolean suitableForBinaryData(DefineBinaryDataTag dataTag) {
if (dataTag.binaryData.getLength() < 32) {
return false;
}
return null;
}
@Override
public Boolean suitableForData(byte[] data) {
if (data.length < 32) {
return false;
}
return null;
}
@Override
public boolean decrypt(InputStream is, OutputStream os, String key) throws IOException {
byte[] encryptedData = Helper.readStream(is);
byte[] decryptedData = HarmanBinaryDataEncrypt.decrypt(encryptedData, key);
if (decryptedData == null) {
return false;
}
os.write(decryptedData);
return true;
}
@Override
public boolean encrypt(InputStream is, OutputStream os, String key) throws IOException {
byte[] data = Helper.readStream(is);
byte[] encryptedData = HarmanBinaryDataEncrypt.encrypt(data, key);
if (encryptedData == null) {
return false;
}
os.write(encryptedData);
return true;
}
@Override
public String getName() {
return "Harman AIR SDK with custom key";
}
@Override
public String getIdentifier() {
return "harmanair_key";
}
@Override
public boolean usesKey() {
return true;
}
}

View File

@@ -42,7 +42,7 @@ public class MochiCryptPacker16Bit implements Packer {
}
@Override
public boolean decrypt(InputStream is, OutputStream os) throws IOException {
public boolean decrypt(InputStream is, OutputStream os, String key) throws IOException {
byte[] payload = Helper.readStream(is);
if (!handleXor(payload)) {
return false;
@@ -103,7 +103,7 @@ public class MochiCryptPacker16Bit implements Packer {
}
@Override
public boolean encrypt(InputStream is, OutputStream os) throws IOException {
public boolean encrypt(InputStream is, OutputStream os, String key) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DeflaterOutputStream def = new DeflaterOutputStream(baos);
Helper.copyStreamEx(is, def);
@@ -133,4 +133,10 @@ public class MochiCryptPacker16Bit implements Packer {
public String getIdentifier() {
return "mochicrypt16";
}
@Override
public boolean usesKey() {
return false;
}
}

View File

@@ -42,7 +42,7 @@ public class MochiCryptPacker32Bit implements Packer {
}
@Override
public boolean decrypt(InputStream is, OutputStream os) throws IOException {
public boolean decrypt(InputStream is, OutputStream os, String key) throws IOException {
byte[] payload = Helper.readStream(is);
if (!handleXor(payload)) {
return false;
@@ -103,7 +103,7 @@ public class MochiCryptPacker32Bit implements Packer {
}
@Override
public boolean encrypt(InputStream is, OutputStream os) throws IOException {
public boolean encrypt(InputStream is, OutputStream os, String key) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DeflaterOutputStream def = new DeflaterOutputStream(baos);
Helper.copyStreamEx(is, def);
@@ -133,4 +133,9 @@ public class MochiCryptPacker32Bit implements Packer {
public String getIdentifier() {
return "mochicrypt32";
}
@Override
public boolean usesKey() {
return false;
}
}

View File

@@ -53,21 +53,23 @@ public interface Packer {
*
* @param is Data to unpack
* @param os Stream to write unpacked data to
* @param key Key
* @return True if it was unpacked correctly, False if it is not suitable
* for unpacking or an error happened.
* @throws IOException On I/O error
*/
public boolean decrypt(InputStream is, OutputStream os) throws IOException;
public boolean decrypt(InputStream is, OutputStream os, String key) throws IOException;
/**
* Pack the data
*
* @param is Data to pack
* @param os Stream to write packed data to
* @param key Key
* @return True if packed successfully, False if error happened.
* @throws IOException On I/O error
*/
public boolean encrypt(InputStream is, OutputStream os) throws IOException;
public boolean encrypt(InputStream is, OutputStream os, String key) throws IOException;
/**
* Human readable name of this packer
@@ -82,4 +84,10 @@ public interface Packer {
* @return Identifier of this packer
*/
public String getIdentifier();
/**
* Checks whether the packer uses encryption key.
* @return True if key is used
*/
public boolean usesKey();
}

View File

@@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.configuration.CustomConfigurationKeys;
import com.jpexs.decompiler.flash.configuration.SwfSpecificCustomConfiguration;
import com.jpexs.decompiler.flash.packers.HarmanAirPacker;
import com.jpexs.decompiler.flash.packers.HarmanAirPackerWithKey;
import com.jpexs.decompiler.flash.packers.MochiCryptPacker16Bit;
import com.jpexs.decompiler.flash.packers.MochiCryptPacker32Bit;
import com.jpexs.decompiler.flash.packers.Packer;
@@ -68,6 +69,9 @@ public class DefineBinaryDataTag extends CharacterTag implements BinaryDataInter
@Internal
public Packer usedPacker;
@Internal
public String packerKey;
@Internal
private PackedBinaryData sub;
@@ -75,7 +79,8 @@ public class DefineBinaryDataTag extends CharacterTag implements BinaryDataInter
private static final Packer[] PACKERS = {
new MochiCryptPacker16Bit(),
new MochiCryptPacker32Bit(),
new HarmanAirPacker()
new HarmanAirPacker(),
new HarmanAirPackerWithKey()
};
public static Packer[] getAvailablePackers() {
@@ -103,10 +108,10 @@ public class DefineBinaryDataTag extends CharacterTag implements BinaryDataInter
}
@Override
public boolean unpack(Packer packer) {
public boolean unpack(Packer packer, String key) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (!packer.decrypt(new ByteArrayInputStream(binaryData.getRangeData()), baos)) {
if (!packer.decrypt(new ByteArrayInputStream(binaryData.getRangeData()), baos, key)) {
return false;
}
} catch (IOException ex) {
@@ -114,6 +119,7 @@ public class DefineBinaryDataTag extends CharacterTag implements BinaryDataInter
}
sub = new PackedBinaryData(swf, this, new ByteArrayRange(baos.toByteArray()));
usedPacker = packer;
packerKey = key;
return true;
}
@@ -135,7 +141,7 @@ public class DefineBinaryDataTag extends CharacterTag implements BinaryDataInter
String packerAdd = "";
BinaryDataInterface binaryData = this;
if (usedPacker != null) {
unpack(usedPacker);
unpack(usedPacker, packerKey);
if (sub != null) {
is = new ByteArrayInputStream(sub.getDataBytes().getRangeData());
binaryData = sub;
@@ -233,7 +239,7 @@ public class DefineBinaryDataTag extends CharacterTag implements BinaryDataInter
sub.pack();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (!usedPacker.encrypt(new ByteArrayInputStream(sub.getDataBytes().getRangeData()), baos)) {
if (!usedPacker.encrypt(new ByteArrayInputStream(sub.getDataBytes().getRangeData()), baos, packerKey)) {
return false;
}
} catch (IOException ex) {
@@ -282,4 +288,9 @@ public class DefineBinaryDataTag extends CharacterTag implements BinaryDataInter
public String getClassExportFileName(String className) {
return className;
}
@Override
public String getPackerKey() {
return packerKey;
}
}

View File

@@ -43,9 +43,10 @@ public interface BinaryDataInterface extends Exportable {
/**
* Unpacks the data.
* @param packer Packer
* @param key Key
* @return True if the data was unpacked
*/
public boolean unpack(Packer packer);
public boolean unpack(Packer packer, String key);
/**
* Detects the packer.
@@ -57,6 +58,12 @@ public interface BinaryDataInterface extends Exportable {
* @return Used packer
*/
public Packer getUsedPacker();
/**
* Gets the used packer key.
* @return Packer key
*/
public String getPackerKey();
/**
* Sets the data bytes.

View File

@@ -43,6 +43,7 @@ public class PackedBinaryData implements TreeItem, BinaryDataInterface {
private PackedBinaryData sub;
private Packer usedPacker;
private SWF innerSwf;
private String packerKey;
/**
* Constructor.
@@ -57,10 +58,10 @@ public class PackedBinaryData implements TreeItem, BinaryDataInterface {
}
@Override
public boolean unpack(Packer packer) {
public boolean unpack(Packer packer, String key) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (!packer.decrypt(new ByteArrayInputStream(data.getRangeData()), baos)) {
if (!packer.decrypt(new ByteArrayInputStream(data.getRangeData()), baos, key)) {
return false;
}
} catch (IOException ex) {
@@ -68,6 +69,7 @@ public class PackedBinaryData implements TreeItem, BinaryDataInterface {
}
sub = new PackedBinaryData(swf, this, new ByteArrayRange(baos.toByteArray()));
usedPacker = packer;
packerKey = key;
return true;
}
@@ -159,7 +161,7 @@ public class PackedBinaryData implements TreeItem, BinaryDataInterface {
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (!usedPacker.encrypt(new ByteArrayInputStream(sub.getDataBytes().getRangeData()), baos)) {
if (!usedPacker.encrypt(new ByteArrayInputStream(sub.getDataBytes().getRangeData()), baos, packerKey)) {
return false;
}
} catch (IOException ex) {
@@ -254,4 +256,9 @@ public class PackedBinaryData implements TreeItem, BinaryDataInterface {
parts.add(0, binaryData.getClassExportFileName(className));
return String.join("_", parts);
}
@Override
public String getPackerKey() {
return packerKey;
}
}