• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jiangxincode / ApkToolBoxGUI / #1147

30 Aug 2025 08:56AM UTC coverage: 3.018% (-0.004%) from 3.022%
#1147

push

jiangxincode
Merge branch 'master' of https://github.com/jiangxincode/ApkToolBoxGUI

248 of 8217 relevant lines covered (3.02%)

0.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/edu/jiangxin/apktoolbox/convert/protobuf/unsupervised/ProtobufDecoder.java
1
package edu.jiangxin.apktoolbox.convert.protobuf.unsupervised;
2

3
import com.google.protobuf.WireFormat;
4
import org.apache.commons.lang3.ArrayUtils;
5
import org.json.JSONObject;
6

7
import java.math.BigInteger;
8
import java.util.ArrayList;
9
import java.util.HashMap;
10
import java.util.List;
11
import java.util.Map;
12

13

14
public class ProtobufDecoder {
×
15

16
    private static final String VALID_DATA = "valid_data";
17

18
    private static final String LEFT_OVER_DATA = "left_over_data";
19

20
    private static final String KEY_BYTE_RANGE = "byteRange";
21

22
    private static final String KEY_FIELD_NUMBER = "fieldNumber";
23

24
    private static final String KEY_TYPE = "type";
25

26
    private static final String KEY_VALUE = "value";
27

28
    /**
29
     * 解码字节数组
30
     *
31
     * @param bytes
32
     * @return
33
     */
34
    public static String bytesDecoder(byte[] bytes) {
35
        Map<String, Object> map = decodeProto(bytes);
×
36
        List<Map<String, Object>> validItems = (List<Map<String, Object>>) map.get(VALID_DATA);
×
37
        List<JSONObject> result = new ArrayList<>();
×
38
        for (Map<String, Object> validItem : validItems) {
×
39
            DecoderResult protobufPart = getProtobufPart(validItem);
×
40
            JSONObject jsonObject = ProtobufUtils.getJsonObject(protobufPart);
×
41
            result.add(jsonObject);
×
42
        }
×
43
        JSONObject jsonObject = new JSONObject();
×
44
        jsonObject.put(VALID_DATA, result);
×
45
        jsonObject.put(LEFT_OVER_DATA, map.get(LEFT_OVER_DATA));
×
46
        return jsonObject.toString(2);
×
47
    }
48

49
    /**
50
     * 解码Proto数据结构
51
     *
52
     * @param buffer
53
     * @return
54
     */
55
    private static Map<String, Object> decodeProto(byte[] buffer) {
56
        BufferReader reader = new BufferReader(buffer);
×
57
        List<Object> parts = new ArrayList<>();
×
58
        reader.trySkipGrpcHeader();
×
59

60
        try {
61
            while (reader.leftBytes() > 0) {
×
62
                reader.checkpoint();
×
63

64
                List<Integer> byteRange = new ArrayList<>();
×
65
                byteRange.add(reader.getOffset());
×
66
                int indexType = reader.readVarInt().intValue();
×
67
                int type = indexType & 0b111;
×
68
                int fieldNumber = indexType >> 3;
×
69

70
                Object value;
71
                if (type == WireFormat.WIRETYPE_VARINT) {
×
72
                    value = reader.readVarInt();
×
73
                } else if (type == WireFormat.WIRETYPE_LENGTH_DELIMITED) {
×
74
                    int length = reader.readVarInt().intValue();
×
75
                    value = reader.readBuffer(length);
×
76
                } else if (type == WireFormat.WIRETYPE_FIXED32) {
×
77
                    value = reader.readBuffer(4);
×
78
                } else if (type == WireFormat.WIRETYPE_FIXED64) {
×
79
                    value = reader.readBuffer(8);
×
80
                } else {
81
                    throw new RuntimeException("Unknown type: " + type);
×
82
                }
83
                byteRange.add(reader.getOffset());
×
84

85
                Map<String, Object> part = new HashMap<>();
×
86
                part.put(KEY_BYTE_RANGE, byteRange);
×
87
                part.put(KEY_FIELD_NUMBER, fieldNumber);
×
88
                part.put(KEY_TYPE, type);
×
89
                part.put(KEY_VALUE, value);
×
90
                parts.add(part);
×
91
            }
×
92
        } catch (Exception e) {
×
93
            reader.resetToCheckpoint();
×
94
        }
×
95

96
        Map<String, Object> result = new HashMap<>();
×
97
        result.put(VALID_DATA, parts);
×
98
        result.put(LEFT_OVER_DATA, ByteUtil.bytesToHex(reader.readBuffer(reader.leftBytes())));
×
99

100
        return result;
×
101
    }
102

103
    /**
104
     * 解码protobuf格式的数据
105
     *
106
     * @param part
107
     * @return
108
     */
109
    private static DecoderResult getProtobufPart(Map<String, Object> part) {
110
        DecoderResult result = new DecoderResult();
×
111
        result.setByteRange((List<Integer>) part.get(KEY_BYTE_RANGE));
×
112
        result.setFieldNumber((int) part.get(KEY_FIELD_NUMBER));
×
113
        int type = (int) part.get(KEY_TYPE);
×
114
        Object value = part.get(KEY_VALUE);
×
115
        switch (type) {
×
116
            case WireFormat.WIRETYPE_VARINT:
117
                String valueStr = value.toString();
×
118
                result.setContent(decodeVarintParts(valueStr));
×
119
                break;
×
120
            case WireFormat.WIRETYPE_LENGTH_DELIMITED:
121
                byte[] bytes = (byte[]) value;
×
122
                Map<String, Object> decoded = decodeProto(bytes);
×
123
                String leftOverData = (String) decoded.get(LEFT_OVER_DATA);
×
124
                if (bytes != null && bytes.length > 0 && leftOverData != null && leftOverData.length() == 0) {
×
125
                    List<Map<String, Object>> decodedParts = (List<Map<String, Object>>) decoded.get(VALID_DATA);
×
126
                    List<DecoderResult> subResults = new ArrayList<>();
×
127
                    for (Map<String, Object> decodedPart : decodedParts) {
×
128
                        DecoderResult protobufPart = getProtobufPart(decodedPart);
×
129
                        subResults.add(protobufPart);
×
130
                    }
×
131
                    result.setSubResults(subResults);
×
132
                } else {
×
133
                    Map<String, Object> map = decodeStringOrBytes(bytes);
×
134
                    result.setContent((String) map.get(KEY_VALUE));
×
135
                }
136
                break;
×
137
            case WireFormat.WIRETYPE_FIXED64:
138
                bytes = (byte[]) value;
×
139
                List<Map<String, Object>> fixed64Result = decodeFixed64(bytes);
×
140
                result.setContent(JSONObject.valueToString(fixed64Result));
×
141
                break;
×
142
            case WireFormat.WIRETYPE_FIXED32:
143
                bytes = (byte[]) value;
×
144
                List<Map<String, Object>> fixed32Result = decodeFixed32(bytes);
×
145
                result.setContent(JSONObject.valueToString(fixed32Result));
×
146
                break;
×
147
            default:
148
                break;
149
        }
150
        result.setType(type);
×
151
        return result;
×
152
    }
153

154

155
    /**
156
     * 解码为Fixed32格式数据
157
     *
158
     */
159
    private static List<Map<String, Object>> decodeFixed32(byte[] value) {
160
        float floatValue = ByteUtil.bytesToFloat(value);
×
161
        int intValue = ByteUtil.bytesToInt(value);
×
162
        int uintValue = ByteUtil.bytesToInt(value);
×
163

164
        List<Map<String, Object>> result = new ArrayList<>(3);
×
165
        Map<String, Object> map1 = new HashMap<>(2);
×
166
        map1.put(KEY_TYPE, "Int");
×
167
        map1.put(KEY_VALUE, intValue);
×
168
        result.add(map1);
×
169

170
        if (intValue != uintValue) {
×
171
            Map<String, Object> map2 = new HashMap<>(2);
×
172
            map2.put(KEY_TYPE, "Unsigned Int");
×
173
            map2.put(KEY_VALUE, uintValue);
×
174
            result.add(map2);
×
175
        }
176
        Map<String, Object> map3 = new HashMap<>(2);
×
177
        map3.put(KEY_TYPE, "Float");
×
178
        map3.put(KEY_VALUE, floatValue);
×
179
        result.add(map3);
×
180
        return result;
×
181
    }
182

183

184
    /**
185
     * 解码为Fixed64格式数据
186
     *
187
     */
188
    private static List<Map<String, Object>> decodeFixed64(byte[] value) {
189
        double floatValue = ByteUtil.bytesToDouble(value);
×
190
        BigInteger intValue = new BigInteger(ByteUtil.bytesToHex(value), 16);
×
191
        BigInteger uintValue = twoComplements(intValue);
×
192

193
        List<Map<String, Object>> result = new ArrayList<>(3);
×
194
        Map<String, Object> map1 = new HashMap<>(2);
×
195
        map1.put(KEY_TYPE, "Int");
×
196
        map1.put(KEY_VALUE, intValue.toString());
×
197
        result.add(map1);
×
198

199
        if (!intValue.equals(uintValue)) {
×
200
            Map<String, Object> map2 = new HashMap<>(2);
×
201
            map2.put(KEY_TYPE, "Unsigned Int");
×
202
            map2.put(KEY_VALUE, uintValue.toString());
×
203
            result.add(map2);
×
204
        }
205
        Map<String, Object> map3 = new HashMap<>(2);
×
206
        map3.put(KEY_TYPE, "Double");
×
207
        map3.put(KEY_VALUE, floatValue);
×
208
        result.add(map3);
×
209
        return result;
×
210
    }
211

212

213
    /**
214
     * 解码为字符串或16进制字符串
215
     *
216
     */
217
    private static Map<String, Object> decodeStringOrBytes(byte[] value) {
218
        Map<String, Object> result = new HashMap<>(2);
×
219
        if (ArrayUtils.isEmpty(value)) {
×
220
            result.put(KEY_TYPE, "string|bytes");
×
221
            result.put(KEY_VALUE, "");
×
222
            return result;
×
223
        }
224
        try {
225
            result.put(KEY_TYPE, "string");
×
226
            result.put(KEY_VALUE, hexStrToStr(ByteUtil.bytesToHex(value), "utf-8"));
×
227
        } catch (Exception e) {
×
228
            result.put(KEY_TYPE, "bytes");
×
229
            result.put(KEY_VALUE, ByteUtil.bytesToHex(value));
×
230
        }
×
231
        return result;
×
232
    }
233

234

235
    /**
236
     * 解码为Varint格式数据
237
     *
238
     */
239
    private static String decodeVarintParts(String value) {
240
        List<Map<String, Object>> result = new ArrayList<>(3);
×
241
        StringBuilder sb = new StringBuilder();
×
242
        BigInteger intVal = new BigInteger(value);
×
243
        Map<String, Object> map1 = new HashMap<>(2);
×
244
        sb.append("[as uint:");
×
245
        sb.append(intVal);
×
246
        sb.append("]");
×
247
        result.add(map1);
×
248

249
        BigInteger signedIntVal = VarintUtils.interpretAsSignedType(intVal);
×
250
        if (!signedIntVal.equals(intVal)) {
×
251
            Map<String, Object> map2 = new HashMap<>(2);
×
252
            sb.append("[as sint:");
×
253
            sb.append(signedIntVal);
×
254
            sb.append("]");
×
255
            result.add(map2);
×
256
        }
257
        return sb.toString();
×
258
    }
259

260

261
    /**
262
     * 补码
263
     *
264
     * @param uintValue
265
     * @return
266
     */
267
    private static BigInteger twoComplements(BigInteger uintValue) {
268
        if (uintValue.compareTo(new BigInteger("7fffffffffffffff", 16)) > 0) {
×
269
            return uintValue.subtract(new BigInteger("10000000000000000", 16));
×
270
        } else {
271
            return uintValue;
×
272
        }
273
    }
274

275
    /**
276
     * 将16进制字符串转字符串
277
     *
278
     * @param string
279
     * @param charsetName
280
     * @return
281
     */
282
    private static String hexStrToStr(String string, String charsetName) {
283
        if (string == null || string.equals("")) {
×
284
            return null;
×
285
        }
286
        byte[] baKeyword = new byte[string.length() / 2];
×
287
        for (int i = 0; i < baKeyword.length; i++) {
×
288
            try {
289
                baKeyword[i] = (byte) (0xff & Integer.parseInt(string.substring(i * 2, i * 2 + 2), 16));
×
290
            } catch (Exception e) {
×
291
                e.printStackTrace();
×
292
            }
×
293
        }
294
        try {
295
            string = new String(baKeyword, charsetName);
×
296
        } catch (Exception e1) {
×
297
            e1.printStackTrace();
×
298
        }
×
299
        return string;
×
300
    }
301
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc