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

View File

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

View File

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