1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-09-01 03:55:47 +02:00

Huawei: Improved error handling for bin file parsing.

This commit is contained in:
Me7c7 2024-08-30 10:53:26 +03:00 committed by José Rebelo
parent 2aafad46eb
commit 1c19283192
3 changed files with 65 additions and 37 deletions

View File

@ -7,15 +7,23 @@ import java.util.Arrays;
public class HuaweiBinAppParser {
public static class HuaweiBinAppParseError extends Exception {
public HuaweiBinAppParseError(String str) {
super(str);
}
}
public static class AppFileEntry {
public String filename;
public String path;
public byte[] content;
}
private static final int signHeaderLen = 32;
private static final byte[] signMagic = "hw signed app ".getBytes();
private static final byte[] signVersion = "1000".getBytes();
private static final byte BIN_APP_MAGIC = (byte) 0xbe;
private static final int SIGN_HEADER_LEN = 32;
private static final byte[] SIGN_MAGIC = "hw signed app ".getBytes();
private static final byte[] SIGN_VERSION = "1000".getBytes();
private String packageName;
@ -26,59 +34,76 @@ public class HuaweiBinAppParser {
}
public byte[] getEntryContent(String filename) {
for(AppFileEntry en: entries) {
if(en.filename.equals(filename)) {
for (AppFileEntry en : entries) {
if (en.filename.equals(filename)) {
return en.content;
}
}
return null;
}
private byte[] readData(ByteBuffer data) {
private byte[] readData(ByteBuffer data) throws Exception {
long len = data.getLong();
byte[] newPayload = new byte[(int)len];
data.get(newPayload, 0, (int)len);
if (len > Integer.MAX_VALUE || len <= 0) {
throw new HuaweiBinAppParseError("Invalid data length");
}
byte[] newPayload = new byte[(int) len];
data.get(newPayload, 0, (int) len);
return newPayload;
}
private String readString(ByteBuffer data) {
int len = data.getInt();
private String readStringInternal(ByteBuffer data, int len) throws Exception {
byte[] newPayload = new byte[len];
data.get(newPayload, 0, len);
return new String(newPayload, StandardCharsets.UTF_8);
}
private String readString(ByteBuffer data) throws Exception {
int len = data.getInt();
if (len <= 0 || len > data.remaining()) {
throw new HuaweiBinAppParseError("Invalid string length");
}
return readStringInternal(data, len);
}
private String readEmptyString(ByteBuffer data) throws Exception {
int len = data.getInt();
if (len < 0 || len > data.remaining()) {
throw new HuaweiBinAppParseError("Invalid string length");
}
return readStringInternal(data, len);
}
private int getSignLen(byte[] in) {
byte[] signatureHeader = Arrays.copyOfRange(in, in.length - signHeaderLen, in.length);
byte[] signatureHeader = Arrays.copyOfRange(in, in.length - SIGN_HEADER_LEN, in.length);
ByteBuffer signatureHeaderBuf = ByteBuffer.wrap(signatureHeader);
byte[] magic = new byte[signMagic.length];
signatureHeaderBuf.get(magic, 0, signMagic.length);
if(!Arrays.equals(signMagic, magic))
byte[] magic = new byte[SIGN_MAGIC.length];
signatureHeaderBuf.get(magic, 0, SIGN_MAGIC.length);
if (!Arrays.equals(SIGN_MAGIC, magic))
return 0;
byte[] version = new byte[signVersion.length];
signatureHeaderBuf.get(version, 0, signVersion.length);
if(!Arrays.equals(signVersion, version))
byte[] version = new byte[SIGN_VERSION.length];
signatureHeaderBuf.get(version, 0, SIGN_VERSION.length);
if (!Arrays.equals(SIGN_VERSION, version))
return 0;
//NOTE: we need only signature size.
return signatureHeaderBuf.getInt();
}
public void parseData(byte[] in) throws Exception {
//NOTE: Binary app file signed. We should avoid to read this signature.
int signLen = getSignLen(in);
ByteBuffer data = ByteBuffer.wrap(in);
byte magic = data.get();
if(magic != (byte)0xbe){
throw new Exception("Invalid magic");
if (magic != BIN_APP_MAGIC) {
throw new HuaweiBinAppParseError("Invalid magic");
}
this.packageName = readString(data);
while(data.remaining() > signLen) {
while (data.remaining() > signLen) {
AppFileEntry ent = new AppFileEntry();
ent.filename = readString(data);
ent.path = readString(data);
ent.path = readEmptyString(data);
ent.content = readData(data);
entries.add(ent);
}

View File

@ -72,8 +72,8 @@ public class HuaweiInstallHandler implements InstallHandler {
GenericItem installItem = new GenericItem();
if (helper.getWatchfacePreviewBitmap() != null) {
installItem.setPreview(helper.getWatchfacePreviewBitmap());
if (helper.getPreviewBitmap() != null) {
installItem.setPreview(helper.getPreviewBitmap());
}
installItem.setName(description.title);
@ -116,8 +116,8 @@ public class HuaweiInstallHandler implements InstallHandler {
GenericItem installItem = new GenericItem();
if (helper.getWatchfacePreviewBitmap() != null) {
installItem.setPreview(helper.getWatchfacePreviewBitmap());
if (helper.getPreviewBitmap() != null) {
installItem.setPreview(helper.getPreviewBitmap());
}
installItem.setName(config.bundleName);

View File

@ -48,7 +48,7 @@ public class HuaweiFwHelper {
private byte fileType = 0;
String fileName = "";
Bitmap watchfacePreviewBitmap;
Bitmap previewBitmap;
HuaweiWatchfaceManager.WatchfaceDescription watchfaceDescription;
HuaweiAppManager.AppConfig appConfig;
Context mContext;
@ -83,7 +83,6 @@ public class HuaweiFwHelper {
try {
final UriHelper uriHelper = UriHelper.get(uri, this.mContext);
InputStream inputStream = uriHelper.openInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@ -96,10 +95,12 @@ public class HuaweiFwHelper {
}
buffer.flush();
byte[] hap_data = buffer.toByteArray();
byte[] appData = buffer.toByteArray();
inputStream.close();
HuaweiBinAppParser app = new HuaweiBinAppParser();
app.parseData(hap_data);
app.parseData(appData);
byte[] config = app.getEntryContent("config.json");
if(config == null)
@ -107,20 +108,22 @@ public class HuaweiFwHelper {
appConfig = new HuaweiAppManager.AppConfig(new String(config));
fileName = app.getPackageName() + "_INSTALL"; //TODO: INSTALL or UPDATE suffix
fw = hap_data;
fw = appData;
fileSize = fw.length;
byte[] icon = app.getEntryContent("icon_small.png");
if(icon != null) {
watchfacePreviewBitmap = BitmapFactory.decodeByteArray(icon, 0, icon.length);
previewBitmap = BitmapFactory.decodeByteArray(icon, 0, icon.length);
}
return true;
} catch (FileNotFoundException e) {
LOG.error("The watchface file was not found.", e);
LOG.error("The app file was not found.", e);
} catch (IOException e) {
LOG.error("General IO error occurred.", e);
} catch (HuaweiBinAppParser.HuaweiBinAppParseError e) {
LOG.error("Error parsing app File", e);
} catch (Exception e) {
LOG.error("Unknown error occurred.", e);
}
@ -164,7 +167,7 @@ public class HuaweiFwHelper {
watchfaceDescription = new HuaweiWatchfaceManager.WatchfaceDescription(xmlDescription);
if (watchfacePackage.fileExists("preview/cover.jpg")) {
final byte[] preview = watchfacePackage.getFileFromZip("preview/cover.jpg");
watchfacePreviewBitmap = BitmapFactory.decodeByteArray(preview, 0, preview.length);
previewBitmap = BitmapFactory.decodeByteArray(preview, 0, preview.length);
}
byte[] watchfaceZip = watchfacePackage.getFileFromZip("com.huawei.watchface");
@ -203,8 +206,8 @@ public class HuaweiFwHelper {
return isWatchface() || isAPP();
}
public Bitmap getWatchfacePreviewBitmap() {
return watchfacePreviewBitmap;
public Bitmap getPreviewBitmap() {
return previewBitmap;
}
public HuaweiWatchfaceManager.WatchfaceDescription getWatchfaceDescription() {