crownx-decoder 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ Metadata-Version: 2.4
2
+ Name: crownx-decoder
3
+ Version: 1.0.0
4
+ Summary: Protobuf raw and AES decoder CLI tool
5
+ Author: CrownX
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: pycryptodome
9
+ Dynamic: author
10
+ Dynamic: description
11
+ Dynamic: description-content-type
12
+ Dynamic: requires-dist
13
+ Dynamic: requires-python
14
+ Dynamic: summary
15
+
16
+ # Crownx Decoder
17
+
18
+ Protobuf & AES Decoder CLI Tool.
19
+
20
+ ## Installation
21
+ ```bash
22
+ pip install crownx-decoder
@@ -0,0 +1,7 @@
1
+ # Crownx Decoder
2
+
3
+ Protobuf & AES Decoder CLI Tool.
4
+
5
+ ## Installation
6
+ ```bash
7
+ pip install crownx-decoder
File without changes
@@ -0,0 +1,638 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Protobuf Decoder - JSON Output Version
4
+ Decodes a raw protobuf message given as hex or base64 text, and prints
5
+ the result as beautifully formatted JSON. Nested messages are expanded automatically.
6
+ """
7
+
8
+ import base64
9
+ import struct
10
+ import argparse
11
+ import json
12
+ import sys
13
+ import re
14
+ import os
15
+ from datetime import datetime
16
+ from Crypto.Cipher import AES
17
+ from Crypto.Util.Padding import unpad
18
+
19
+ # ---------------------------------------------------------------------------
20
+ # Wire types (same constants as protobufDecoder.js)
21
+ # ---------------------------------------------------------------------------
22
+ class TYPES:
23
+ MSG_LEN_DELIMITER = -1
24
+ VARINT = 0
25
+ FIXED64 = 1
26
+ LENDELIM = 2
27
+ FIXED32 = 5
28
+
29
+ # ---------------------------------------------------------------------------
30
+ # Input parsing (hexUtils.js)
31
+ # ---------------------------------------------------------------------------
32
+ def is_hex(s):
33
+ return bool(s) and all(c in "0123456789abcdef" for c in s) and len(s) % 2 == 0
34
+
35
+
36
+ def parse_input(input_str, force_base64=False):
37
+ normalized = re.sub(r"\s", "", input_str)
38
+ normalized_hex = re.sub(r"0x", "", normalized, flags=re.IGNORECASE).lower()
39
+ if not force_base64 and is_hex(normalized_hex):
40
+ return bytes.fromhex(normalized_hex)
41
+ else:
42
+ # pad base64 if needed
43
+ padded = normalized + "=" * (-len(normalized) % 4)
44
+ return base64.b64decode(padded)
45
+
46
+
47
+ def buffer_to_pretty_hex(b):
48
+ return " ".join(f"{c:02x}" for c in b)
49
+
50
+
51
+ def buffer_le_to_be_hex(b):
52
+ output = ""
53
+ for v in b:
54
+ h = f"{v:x}"
55
+ if len(h) == 1:
56
+ h = "0" + h
57
+ output = h + output
58
+ return output
59
+
60
+ # ---------------------------------------------------------------------------
61
+ # Varint decoding (varintUtils.js)
62
+ # ---------------------------------------------------------------------------
63
+ def decode_varint(buffer, offset):
64
+ result = 0
65
+ shift = 0
66
+ byte = 0
67
+ start = offset
68
+ while True:
69
+ if offset >= len(buffer):
70
+ raise IndexError("Index out of bound decoding varint")
71
+ byte = buffer[offset]
72
+ offset += 1
73
+ result += (byte & 0x7F) << shift
74
+ shift += 7
75
+ if byte < 0x80:
76
+ break
77
+ return {"value": result, "length": offset - start}
78
+
79
+ # ---------------------------------------------------------------------------
80
+ # Signed/twos-complement helpers (intUtils.js)
81
+ # ---------------------------------------------------------------------------
82
+ def interpret_as_signed_type(n):
83
+ """ZigZag decode (protobuf sint semantics)."""
84
+ if n % 2 == 0:
85
+ return n // 2
86
+ else:
87
+ return -((n + 1) // 2)
88
+
89
+ def interpret_as_twos_complement(n, bits):
90
+ """If the top bit within `bits` is set, interpret as a negative
91
+ two's-complement number of that bit width."""
92
+ sign_bit = (n >> (bits - 1)) & 1
93
+ if sign_bit == 1:
94
+ return n - (1 << bits)
95
+ return n
96
+
97
+ # ---------------------------------------------------------------------------
98
+ # BufferReader (protobufDecoder.js)
99
+ # ---------------------------------------------------------------------------
100
+ class BufferReader:
101
+ def __init__(self, buffer):
102
+ self.buffer = buffer
103
+ self.offset = 0
104
+ self.saved_offset = 0
105
+
106
+ def read_varint(self):
107
+ result = decode_varint(self.buffer, self.offset)
108
+ self.offset += result["length"]
109
+ return result["value"]
110
+
111
+ def read_buffer(self, length):
112
+ self.check_bytes(length)
113
+ result = self.buffer[self.offset:self.offset + length]
114
+ self.offset += length
115
+ return result
116
+
117
+ def try_skip_grpc_header(self):
118
+ backup_offset = self.offset
119
+ if self.left_bytes() >= 5 and self.buffer[self.offset] == 0:
120
+ self.offset += 1
121
+ length = struct.unpack_from(">I", self.buffer, self.offset)[0]
122
+ self.offset += 4
123
+ if length > self.left_bytes():
124
+ self.offset = backup_offset
125
+
126
+ def left_bytes(self):
127
+ return len(self.buffer) - self.offset
128
+
129
+ def check_bytes(self, length):
130
+ available = self.left_bytes()
131
+ if length > available:
132
+ raise ValueError(
133
+ f"Not enough bytes left. Requested: {length} left: {available}"
134
+ )
135
+
136
+ def checkpoint(self):
137
+ self.saved_offset = self.offset
138
+
139
+ def reset_to_checkpoint(self):
140
+ self.offset = self.saved_offset
141
+
142
+ # ---------------------------------------------------------------------------
143
+ # Core decode (decodeProto in protobufDecoder.js)
144
+ # ---------------------------------------------------------------------------
145
+ def decode_proto(buffer, parse_delimited=False, skip_grpc_header=False, scan_embedded=True):
146
+ reader = BufferReader(buffer)
147
+ parts = []
148
+
149
+ if skip_grpc_header:
150
+ reader.try_skip_grpc_header()
151
+
152
+ proto_buf_msg_length = 0
153
+ proto_buf_msg_end = 0
154
+
155
+ # ---------------------------------------------------------------
156
+ # Normal protobuf parser
157
+ # ---------------------------------------------------------------
158
+ while reader.left_bytes() > 0:
159
+ reader.checkpoint()
160
+ field_start = reader.offset
161
+
162
+ try:
163
+ if parse_delimited and proto_buf_msg_end == reader.offset:
164
+ proto_buf_msg_length = reader.read_varint()
165
+ proto_buf_msg_end = reader.offset + proto_buf_msg_length
166
+
167
+ parts.append({
168
+ "index": -1,
169
+ "type": TYPES.MSG_LEN_DELIMITER,
170
+ "value": proto_buf_msg_length,
171
+ })
172
+
173
+ index_type = reader.read_varint()
174
+
175
+ type_ = index_type & 0b111
176
+ index = index_type >> 3
177
+
178
+ # -------------------------------------------------------
179
+ # Invalid protobuf field number
180
+ # -------------------------------------------------------
181
+ if index <= 0:
182
+ raise ValueError("Invalid protobuf field number")
183
+
184
+ # -------------------------------------------------------
185
+ # VARINT
186
+ # -------------------------------------------------------
187
+ if type_ == TYPES.VARINT:
188
+ value = reader.read_varint()
189
+
190
+ # -------------------------------------------------------
191
+ # FIXED64
192
+ # -------------------------------------------------------
193
+ elif type_ == TYPES.FIXED64:
194
+ value = reader.read_buffer(8)
195
+
196
+ # -------------------------------------------------------
197
+ # LENGTH DELIMITED
198
+ # -------------------------------------------------------
199
+ elif type_ == TYPES.LENDELIM:
200
+ length = reader.read_varint()
201
+
202
+ if length < 0 or length > reader.left_bytes():
203
+ raise ValueError(
204
+ f"Invalid length-delimited size: {length}"
205
+ )
206
+
207
+ value = reader.read_buffer(length)
208
+
209
+ # -------------------------------------------------------
210
+ # START GROUP
211
+ # -------------------------------------------------------
212
+ elif type_ == 3:
213
+ group_start = reader.offset
214
+ depth = 1
215
+
216
+ while reader.left_bytes() > 0 and depth > 0:
217
+ tag = reader.read_varint()
218
+ wire = tag & 0x07
219
+
220
+ if wire == 3:
221
+ depth += 1
222
+
223
+ elif wire == 4:
224
+ depth -= 1
225
+
226
+ if depth == 0:
227
+ break
228
+
229
+ elif wire == 0:
230
+ reader.read_varint()
231
+
232
+ elif wire == 1:
233
+ reader.read_buffer(8)
234
+
235
+ elif wire == 2:
236
+ length = reader.read_varint()
237
+
238
+ if length < 0 or length > reader.left_bytes():
239
+ raise ValueError(
240
+ "Invalid length inside group"
241
+ )
242
+
243
+ reader.read_buffer(length)
244
+
245
+ elif wire == 5:
246
+ reader.read_buffer(4)
247
+
248
+ else:
249
+ raise ValueError(
250
+ f"Invalid wire type inside group: {wire}"
251
+ )
252
+
253
+ if depth != 0:
254
+ raise ValueError("Unterminated protobuf group")
255
+
256
+ group_end = reader.offset
257
+ value = buffer[group_start:group_end]
258
+
259
+ # -------------------------------------------------------
260
+ # END GROUP
261
+ # -------------------------------------------------------
262
+ elif type_ == 4:
263
+ raise ValueError("Unexpected END_GROUP")
264
+
265
+ # -------------------------------------------------------
266
+ # FIXED32
267
+ # -------------------------------------------------------
268
+ elif type_ == TYPES.FIXED32:
269
+ value = reader.read_buffer(4)
270
+
271
+ else:
272
+ raise ValueError(f"Unknown wire type: {type_}")
273
+
274
+ parts.append({
275
+ "index": index,
276
+ "type": type_,
277
+ "value": value,
278
+ })
279
+
280
+ except (ValueError, IndexError, struct.error):
281
+ reader.offset = field_start
282
+ break
283
+
284
+ leftover = reader.read_buffer(reader.left_bytes())
285
+
286
+ result = {
287
+ "parts": parts,
288
+ "leftOver": leftover,
289
+ }
290
+
291
+ # ---------------------------------------------------------------
292
+ # Embedded protobuf scan
293
+ # ---------------------------------------------------------------
294
+ parsed_bytes = len(buffer) - len(leftover)
295
+
296
+ has_bad_wire = any(
297
+ p.get("type") in (3, 4)
298
+ for p in parts
299
+ )
300
+
301
+ poor_parse = (
302
+ not parts
303
+ or has_bad_wire
304
+ or parsed_bytes < max(16, len(buffer) // 2)
305
+ or len(leftover) > max(16, len(buffer) // 2)
306
+ )
307
+
308
+ if scan_embedded and poor_parse and len(buffer) > 1:
309
+
310
+ best = None
311
+
312
+ # -----------------------------------------------------------
313
+ # Search every byte offset for a coherent protobuf message
314
+ # -----------------------------------------------------------
315
+ for start in range(1, len(buffer)):
316
+
317
+ try:
318
+ tag_info = decode_varint(buffer, start)
319
+
320
+ first_tag = tag_info["value"]
321
+ first_wire = first_tag & 0x07
322
+ first_field = first_tag >> 3
323
+
324
+ if first_field <= 0:
325
+ continue
326
+
327
+ # Avoid false START_GROUP / END_GROUP candidates
328
+ if first_wire in (3, 4):
329
+ continue
330
+
331
+ except (ValueError, IndexError, struct.error):
332
+ continue
333
+
334
+ # -------------------------------------------------------
335
+ # Parse candidate
336
+ # -------------------------------------------------------
337
+ candidate = decode_proto(
338
+ buffer[start:],
339
+ parse_delimited=False,
340
+ skip_grpc_header=False,
341
+ scan_embedded=False,
342
+ )
343
+
344
+ candidate_parts = candidate.get("parts", [])
345
+ candidate_left = candidate.get("leftOver", b"")
346
+
347
+ if not candidate_parts:
348
+ continue
349
+
350
+ # Reject candidates containing group wire types
351
+ if any(
352
+ p.get("type") in (3, 4)
353
+ for p in candidate_parts
354
+ ):
355
+ continue
356
+
357
+ candidate_buffer_length = len(buffer) - start
358
+
359
+ consumed = (
360
+ candidate_buffer_length
361
+ - len(candidate_left)
362
+ )
363
+
364
+ if consumed <= 0:
365
+ continue
366
+
367
+ # Require a meaningful protobuf sequence
368
+ if consumed < 32:
369
+ continue
370
+
371
+ if len(candidate_parts) < 3:
372
+ continue
373
+
374
+ # -------------------------------------------------------
375
+ # Candidate score
376
+ #
377
+ # Priority:
378
+ # 1. No leftover
379
+ # 2. Most consumed bytes
380
+ # 3. Most fields
381
+ # 4. Earliest offset
382
+ # -------------------------------------------------------
383
+ no_leftover = len(candidate_left) == 0
384
+
385
+ score = (
386
+ 1 if no_leftover else 0,
387
+ consumed,
388
+ len(candidate_parts),
389
+ -start,
390
+ )
391
+
392
+ if best is None or score > best["score"]:
393
+ best = {
394
+ "score": score,
395
+ "offset": start,
396
+ "decoded": candidate,
397
+ }
398
+
399
+ # -----------------------------------------------------------
400
+ # Use best embedded protobuf candidate
401
+ # -----------------------------------------------------------
402
+ if best is not None:
403
+ result = best["decoded"]
404
+
405
+ result["embeddedOffset"] = best["offset"]
406
+
407
+ return result
408
+
409
+ # ---------------------------------------------------------------------------
410
+ # Part decoders (protobufPartDecoder.jsx)
411
+ # ---------------------------------------------------------------------------
412
+ def decode_fixed32(value):
413
+ int_value = struct.unpack_from("<i", value)[0]
414
+ uint_value = struct.unpack_from("<I", value)[0]
415
+ float_value = struct.unpack_from("<f", value)[0]
416
+
417
+ result = [{"type": "int", "value": int_value}]
418
+ if int_value != uint_value:
419
+ result.append({"type": "uint", "value": uint_value})
420
+ result.append({"type": "float", "value": float_value})
421
+ return result
422
+
423
+
424
+ def decode_fixed64(value):
425
+ double_value = struct.unpack_from("<d", value)[0]
426
+ uint_value = int(buffer_le_to_be_hex(value), 16)
427
+ int_value = interpret_as_twos_complement(uint_value, 64)
428
+
429
+ result = [{"type": "int", "value": str(int_value)}]
430
+ if int_value != uint_value:
431
+ result.append({"type": "uint", "value": str(uint_value)})
432
+ result.append({"type": "double", "value": double_value})
433
+ return result
434
+
435
+
436
+ def decode_varint_parts(value):
437
+ result = []
438
+ uint_val = int(value)
439
+ result.append({"type": "uint", "value": str(uint_val)})
440
+
441
+ for bits in (8, 16, 32, 64):
442
+ int_val = interpret_as_twos_complement(uint_val, bits)
443
+ if int_val != uint_val:
444
+ result.append({"type": f"int{bits}", "value": str(int_val)})
445
+
446
+ signed_int_val = interpret_as_signed_type(uint_val)
447
+ if signed_int_val != uint_val:
448
+ result.append({"type": "sint", "value": str(signed_int_val)})
449
+
450
+ return result
451
+
452
+
453
+ def decode_string_or_bytes(value):
454
+ if not len(value):
455
+ return {"type": "string|bytes", "value": ""}
456
+ try:
457
+ decoded_str = value.decode("utf-8")
458
+
459
+ # 1. Agar string me valid JSON formatted text hai, toh use JSON object me parse karein
460
+ trimmed = decoded_str.strip()
461
+ if (trimmed.startswith("{") and trimmed.endswith("}")) or (trimmed.startswith("[") and trimmed.endswith("]")):
462
+ try:
463
+ parsed_json = json.loads(trimmed)
464
+ return {"type": "json", "value": parsed_json}
465
+ except json.JSONDecodeError:
466
+ pass
467
+
468
+ # 2. Check karein ki string me sirf printable characters hain ya garbage control characters bhi hain
469
+ is_printable = True
470
+ for char in decoded_str:
471
+ o = ord(char)
472
+ # Control characters (ASCII < 32) ko exclude karein (except tab, newline, carriage return)
473
+ if o < 32 and o not in (9, 10, 13):
474
+ is_printable = False
475
+ break
476
+
477
+ if is_printable:
478
+ return {"type": "string", "value": decoded_str}
479
+ else:
480
+ # Agar control characters hain toh garbage string ke bajay binary hex return karein
481
+ return {"type": "bytes", "value": buffer_to_pretty_hex(value)}
482
+
483
+ except UnicodeDecodeError:
484
+ return {"type": "bytes", "value": buffer_to_pretty_hex(value)}
485
+
486
+ # ---------------------------------------------------------------------------
487
+ # JSON Formatting Transformer
488
+ # ---------------------------------------------------------------------------
489
+ def process_decoded_to_dict(decoded):
490
+ """Recursively converts the raw parsed decoded dict into a clean JSON-serializable dictionary."""
491
+ out_parts = []
492
+
493
+ for part in decoded["parts"]:
494
+ t = part["type"]
495
+ out_part = {
496
+ "field": part["index"] if part["index"] != -1 else None
497
+ }
498
+
499
+ # Remove field key if it is None (e.g., MSG_LEN_DELIMITER)
500
+ if out_part["field"] is None:
501
+ del out_part["field"]
502
+
503
+ if t == TYPES.VARINT:
504
+ out_part["type"] = "varint"
505
+ out_part["content"] = {d["type"]: d["value"] for d in decode_varint_parts(part["value"])}
506
+
507
+ elif t == TYPES.FIXED64:
508
+ out_part["type"] = "fixed64"
509
+ out_part["content"] = {d["type"]: d["value"] for d in decode_fixed64(part["value"])}
510
+
511
+ elif t == TYPES.FIXED32:
512
+ out_part["type"] = "fixed32"
513
+ out_part["content"] = {d["type"]: d["value"] for d in decode_fixed32(part["value"])}
514
+
515
+ elif t == TYPES.LENDELIM:
516
+ raw = part["value"]
517
+ nested = decode_proto(raw)
518
+ # Exactly same nested check condition
519
+ if len(raw) > 0 and len(nested["leftOver"]) == 0 and len(nested["parts"]) > 0:
520
+ out_part["type"] = "protobuf"
521
+ out_part["content"] = process_decoded_to_dict(nested)
522
+ else:
523
+ dec_str = decode_string_or_bytes(raw)
524
+ out_part["type"] = dec_str["type"]
525
+ out_part["content"] = dec_str["value"]
526
+
527
+ elif t == TYPES.MSG_LEN_DELIMITER:
528
+ out_part["type"] = "message_delimiter"
529
+ out_part["content"] = {"length": part["value"]}
530
+
531
+ else:
532
+ out_part["type"] = "unknown"
533
+ out_part["content"] = buffer_to_pretty_hex(part.get("value", b""))
534
+
535
+ out_parts.append(out_part)
536
+
537
+ result = {"parts": out_parts}
538
+
539
+ if len(decoded["leftOver"]) > 0:
540
+ result["leftOver"] = buffer_to_pretty_hex(decoded["leftOver"])
541
+
542
+ return result
543
+
544
+ # ---------------------------------------------------------------------------
545
+ # AES Decryption Helper
546
+ # ---------------------------------------------------------------------------
547
+ def try_aes_decrypt(ciphertext):
548
+ key = b'Yg&tc%DEuh6%Zc^8'
549
+ iv = b'6oyZDr22E3ychjM%'
550
+ try:
551
+ cipher = AES.new(key, AES.MODE_CBC, iv)
552
+ # Decrypt karega aur PKCS7 padding remove karega
553
+ decrypted = unpad(cipher.decrypt(ciphertext), AES.block_size)
554
+ return decrypted
555
+ except Exception:
556
+ # Agar decrypt/unpad fail ho jaye to None return karega
557
+ return None
558
+
559
+ # ---------------------------------------------------------------------------
560
+ # CLI
561
+ # ---------------------------------------------------------------------------
562
+ def main():
563
+ parser = argparse.ArgumentParser(
564
+ description="Decode a raw protobuf message (hex or base64) and print it as JSON."
565
+ )
566
+ parser.add_argument(
567
+ "data", nargs="?", help="Hex or base64 encoded protobuf bytes. Reads stdin if omitted."
568
+ )
569
+ parser.add_argument(
570
+ "--base64", action="store_true", help="Force interpreting input as base64."
571
+ )
572
+ parser.add_argument(
573
+ "--delimited", action="store_true",
574
+ help="Parse input as a stream of length-delimited messages (e.g. gRPC streaming)."
575
+ )
576
+ parser.add_argument(
577
+ "--grpc", action="store_true",
578
+ help="Try to skip a leading gRPC framing header (1 flag byte + 4 byte length)."
579
+ )
580
+ args = parser.parse_args()
581
+
582
+ raw_input = args.data if args.data is not None else sys.stdin.read()
583
+ if raw_input is None or raw_input.strip() == "":
584
+ print(json.dumps({"error": "No input provided."}, indent=2))
585
+ sys.exit(1)
586
+
587
+ try:
588
+ buffer = parse_input(raw_input, force_base64=args.base64)
589
+ except Exception as e:
590
+ print(json.dumps({"error": f"Failed to parse input as hex or base64: {e}"}, indent=2))
591
+ sys.exit(1)
592
+
593
+ # Pehle normal try karega
594
+ decoded = decode_proto(buffer, parse_delimited=args.delimited, skip_grpc_header=args.grpc)
595
+
596
+ # Firr KEY (AES) se try karega
597
+ decrypted_buffer = try_aes_decrypt(buffer)
598
+ if decrypted_buffer is not None:
599
+ decoded_aes = decode_proto(decrypted_buffer, parse_delimited=args.delimited, skip_grpc_header=args.grpc)
600
+ # Agar AES wali decoding zyada valid lagti hai (leftOver kam hai ya normal parts zero the)
601
+ if len(decoded_aes["leftOver"]) <= len(decoded["leftOver"]) or not decoded["parts"]:
602
+ decoded = decoded_aes
603
+ decoded["is_aes_decrypted"] = True
604
+
605
+ # Convert exactly parsed logic into a neat JSON dict and print it
606
+ json_data = process_decoded_to_dict(decoded)
607
+
608
+ # Agar AES se decode hua hai toh JSON me indicator add kar dega
609
+ if decoded.get("is_aes_decrypted"):
610
+ json_data["is_aes_decrypted"] = True
611
+
612
+ print(json.dumps(json_data, indent=2, ensure_ascii=False))
613
+
614
+ # Auto-save decoded JSON sequentially to /sdcard/#DECODED/
615
+ try:
616
+ dir_path = "/sdcard/#DECODED"
617
+ os.makedirs(dir_path, exist_ok=True)
618
+ date_str = datetime.now().strftime("%d-%m-%Y")
619
+
620
+ index = 1
621
+ while True:
622
+ file_name = f"decoded{index}_{date_str}.json"
623
+ full_path = os.path.join(dir_path, file_name)
624
+ if not os.path.exists(full_path):
625
+ break
626
+ index += 1
627
+
628
+ with open(full_path, "w", encoding="utf-8") as f:
629
+ json.dump(json_data, f, indent=2, ensure_ascii=False)
630
+ f.write(f"\n\ninput = {raw_input.strip()}\n")
631
+ print(f"\n[+] Saved to: {full_path}")
632
+ except Exception as e:
633
+ print(f"\n[-] Failed to auto-save file to /sdcard: {e}")
634
+
635
+
636
+ if __name__ == "__main__":
637
+ main()
638
+
@@ -0,0 +1,22 @@
1
+ Metadata-Version: 2.4
2
+ Name: crownx-decoder
3
+ Version: 1.0.0
4
+ Summary: Protobuf raw and AES decoder CLI tool
5
+ Author: CrownX
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: pycryptodome
9
+ Dynamic: author
10
+ Dynamic: description
11
+ Dynamic: description-content-type
12
+ Dynamic: requires-dist
13
+ Dynamic: requires-python
14
+ Dynamic: summary
15
+
16
+ # Crownx Decoder
17
+
18
+ Protobuf & AES Decoder CLI Tool.
19
+
20
+ ## Installation
21
+ ```bash
22
+ pip install crownx-decoder
@@ -0,0 +1,10 @@
1
+ README.md
2
+ setup.py
3
+ crownx_decoder/__init__.py
4
+ crownx_decoder/decoder.py
5
+ crownx_decoder.egg-info/PKG-INFO
6
+ crownx_decoder.egg-info/SOURCES.txt
7
+ crownx_decoder.egg-info/dependency_links.txt
8
+ crownx_decoder.egg-info/entry_points.txt
9
+ crownx_decoder.egg-info/requires.txt
10
+ crownx_decoder.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ dec = crownx_decoder.decoder:main
@@ -0,0 +1 @@
1
+ pycryptodome
@@ -0,0 +1 @@
1
+ crownx_decoder
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,20 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="crownx-decoder",
5
+ version="1.0.0",
6
+ author="CrownX",
7
+ description="Protobuf raw and AES decoder CLI tool",
8
+ long_description=open("README.md", encoding="utf-8").read(),
9
+ long_description_content_type="text/markdown",
10
+ packages=find_packages(),
11
+ install_requires=[
12
+ "pycryptodome",
13
+ ],
14
+ entry_points={
15
+ "console_scripts": [
16
+ "dec = crownx_decoder.decoder:main", # Isse terminal me 'dec' command register hoga
17
+ ],
18
+ },
19
+ python_requires=">=3.7",
20
+ )