From e925fe11554fc7fbe349b0f7a23dfb435696a311 Mon Sep 17 00:00:00 2001 From: Chung Chi Yeung Date: Sat, 28 Feb 2026 21:17:28 +0000 Subject: [PATCH 1/2] Made some small changes in encoder.py to make it more verbose --- .gitignore | 1 + calc.html => Examples/calc.html | 0 example.html => Examples/example.html | 0 .../exampleCoffeeHouse.html | 114 +++++++++--------- decoder.py | 1 + encoder.py | 34 ++++-- 6 files changed, 81 insertions(+), 69 deletions(-) rename calc.html => Examples/calc.html (100%) rename example.html => Examples/example.html (100%) rename exampleCoffeeHouse.html => Examples/exampleCoffeeHouse.html (99%) diff --git a/.gitignore b/.gitignore index f056d4c..1294ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ __pycache__/* python-*.exe build/* dist/* +Encoded/* decoder*.spec encoder*.spec \ No newline at end of file diff --git a/calc.html b/Examples/calc.html similarity index 100% rename from calc.html rename to Examples/calc.html diff --git a/example.html b/Examples/example.html similarity index 100% rename from example.html rename to Examples/example.html diff --git a/exampleCoffeeHouse.html b/Examples/exampleCoffeeHouse.html similarity index 99% rename from exampleCoffeeHouse.html rename to Examples/exampleCoffeeHouse.html index ad30cd7..1d95ab7 100644 --- a/exampleCoffeeHouse.html +++ b/Examples/exampleCoffeeHouse.html @@ -1,58 +1,58 @@ - - - - The Coffee House - - - - -
- -
-
-
-

About Us

-

We are a cozy coffee shop located in the heart of the city. Our mission is to provide our customers with high-quality coffee, baked goods, and a welcoming atmosphere.

- The Coffee House -
- -
-

Contact Us

-
- -
- -
- -
- -
-
-
- - + + + + The Coffee House + + + + +
+ +
+
+
+

About Us

+

We are a cozy coffee shop located in the heart of the city. Our mission is to provide our customers with high-quality coffee, baked goods, and a welcoming atmosphere.

+ The Coffee House +
+ +
+

Contact Us

+
+ +
+ +
+ +
+ +
+
+
+ + \ No newline at end of file diff --git a/decoder.py b/decoder.py index 8a0356d..3ff1708 100644 --- a/decoder.py +++ b/decoder.py @@ -38,6 +38,7 @@ def parse_decoding_arguments(): parser.add_argument('input_html_file', type=str, help='Input HTML file') parser.add_argument('output_file', type=str, help='Output file') parser.add_argument('--cyberchef', action='store_true', help='Save CyberChef output to a file') + parser.add_argument('--step_mode', help='Decoder will do one step at a time and show what happens at each step') return parser.parse_args() def decode_base64(encoded_content): diff --git a/encoder.py b/encoder.py index d452dde..d95d8cf 100644 --- a/encoder.py +++ b/encoder.py @@ -17,9 +17,13 @@ def read_file(input_file, mode='rb', encoding=None): content = f.read() return content -def write_file(output_file, content, mode='w', encoding=None): - with open(output_file, mode, encoding=encoding) as f: +def write_file(output_file, content, mode='w', encoding=None): + encoded_dir = "Encoded" + output_file_path = f"{encoded_dir}/{output_file}" + with open(output_file_path, mode, encoding=encoding) as f: f.write(content) + print(f"Encoded content written to: {output_file_path}") + f.close() def gzip_content(content): compressed = gzip.compress(content) @@ -83,18 +87,21 @@ def gzip_wrap_in_html(encoded_content): def random_encoding(content): html_content = content.decode('utf-8') encoding_steps = random.randint(1, 10) + encoding_patterns = { + 'None': ('base64', 'unicode', 'uri'), + 'base64': ('unicode', 'uri'), + 'unicode': ('base64', 'uri'), + 'uri': ('base64', 'unicode') + } prev_encoding = None - prev_prev_encoding = None - + encoding_flow = [] + for _ in range(encoding_steps): - encoding_types = ['base64', 'unicode', 'uri'] - if prev_encoding == 'unicode' and prev_prev_encoding != 'uri': - encoding_type = random.choice(['base64', 'unicode']) - elif prev_encoding == 'uri' and prev_prev_encoding != 'unicode': - encoding_type = random.choice(['base64', 'uri']) + if prev_encoding is None: + encoding_type = random.choice(encoding_patterns.get('None')) else: - encoding_type = random.choice(encoding_types) - + encoding_type = random.choice(encoding_patterns.get(prev_encoding)) + if encoding_type == 'base64': content = encode_base64(content) html_content = base64_wrap_in_html(content) @@ -108,8 +115,11 @@ def random_encoding(content): html_content = unicode_wrap_in_html(content) content = html_content.encode('utf-8') - prev_prev_encoding = prev_encoding prev_encoding = encoding_type + encoding_flow.append(encoding_type) + + print(f"Total encoding steps: {encoding_steps}") + print(f"Random encoding flow: original -> {' -> '.join(encoding_flow)}") return html_content def main(args): From 696123ff0d7e0dc77bc7ec56048b0a53fd868b11 Mon Sep 17 00:00:00 2001 From: Chung Chi Yeung Date: Sun, 1 Mar 2026 00:14:40 +0000 Subject: [PATCH 2/2] Redirect output files to directories for better file management, add simple step mode for viewing decode process one step at a time --- .gitignore | 1 + decoder.py | 40 +++++++++++++++++++++++++++++++++------- encoder.py | 1 + requirements.txt | 3 ++- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 1294ee8..f52d357 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,6 @@ python-*.exe build/* dist/* Encoded/* +Decoded/* decoder*.spec encoder*.spec \ No newline at end of file diff --git a/decoder.py b/decoder.py index 3ff1708..0510fe8 100644 --- a/decoder.py +++ b/decoder.py @@ -6,6 +6,7 @@ import gzip import io from urllib.parse import unquote +from readchar import readkey, key import magic @@ -22,7 +23,9 @@ def read_file(input_file, mode='rb', encoding=None): return content def write_file(output_file, content, mode='w', encoding=None): - with open(output_file, mode, encoding=encoding) as f: + decoded_dir = "Decoded" + decoded_file_path = f"{decoded_dir}/{output_file}" + with open(decoded_file_path, mode, encoding=encoding) as f: f.write(content) class CustomHTMLParser(HTMLParser): @@ -38,7 +41,7 @@ def parse_decoding_arguments(): parser.add_argument('input_html_file', type=str, help='Input HTML file') parser.add_argument('output_file', type=str, help='Output file') parser.add_argument('--cyberchef', action='store_true', help='Save CyberChef output to a file') - parser.add_argument('--step_mode', help='Decoder will do one step at a time and show what happens at each step') + parser.add_argument('--step_mode', action='store_true', help='Decoder will do one step at a time and show what happens at each step') return parser.parse_args() def decode_base64(encoded_content): @@ -86,10 +89,19 @@ def create_cyberchef_ops_json(encoding_steps): return json.dumps(cyberchef_ops, indent=2) -def decode_random_encoding(script_content): +def step_mode(decoded_content_snippet, current_encoding_step, next_encoding_step): + print(f"\nCurrent encoding step: {current_encoding_step} -> {next_encoding_step}") + print(f"Decoded content snippet (first 500 characters): \n{decoded_content_snippet[:500]}") + print("Press ENTER key to proceed to the next decoding step...") + while True: + k = readkey() + if k == key.ENTER: + break + +def decode_random_encoding(script_content, step_mode_flag): decoded_content = script_content counters = {"base64": 0, "unicode": 0, "uri": 0, "gzip": 0} - encoding_steps = [] + encoding_steps = ["Encoded content"] while True: updated = False @@ -100,6 +112,8 @@ def decode_random_encoding(script_content): decoded_str = decode_base64(b64_match.group(2)).decode('utf-8') decoded_content = decoded_content.replace(b64_match.group(0), decoded_str) counters["base64"] += 1 + if step_mode_flag: + step_mode(decoded_str, ''.join(encoding_steps[-1:]), "base64") encoding_steps.append("base64") updated = True @@ -111,6 +125,8 @@ def decode_random_encoding(script_content): if decoded_str != encoded_str: decoded_content = decoded_content.replace(unicode_match.group(0), decoded_str) counters["unicode"] += 1 + if step_mode_flag: + step_mode(decoded_str, ''.join(encoding_steps[-1:]), "uricode") encoding_steps.append("unicode") updated = True else: @@ -119,6 +135,8 @@ def decode_random_encoding(script_content): if decoded_uri_str != encoded_str: decoded_content = decoded_content.replace(unicode_match.group(0), decoded_uri_str) counters["uri"] += 1 + if step_mode_flag: + step_mode(decoded_str, ''.join(encoding_steps[-1:]), "uri") encoding_steps.append("uri") updated = True except Exception as e: @@ -130,10 +148,14 @@ def decode_random_encoding(script_content): decoded_str = decode_and_unzip_base64(gzip_match.group(1)) decoded_content = decoded_content.replace(gzip_match.group(0), decoded_str) counters["gzip"] += 1 + if step_mode_flag: + step_mode(decoded_str, ''.join(encoding_steps[-1:]), "gzip") encoding_steps.append("gzip") updated = True if not updated: + print("decoded_content snippet:", decoded_content[:200]) # Print the first 200 characters of the decoded content + print("\nNo more encodings detected. Decoding complete.") break for key, value in counters.items(): @@ -145,7 +167,9 @@ def decode_random_encoding(script_content): def scan_for_mime_types(output_file): mime = magic.Magic(mime=True) - with open(output_file, "rb") as f: + decoded_dir = "Decoded" + decoded_file_path = f"{decoded_dir}/{output_file}" + with open(decoded_file_path, "rb") as f: content = f.read() mime_type = mime.from_buffer(content) print(f"MIME Type: {mime_type}") @@ -187,7 +211,9 @@ def scan_for_mime_types(output_file): def scan_for_script_tags(output_file): mime = magic.Magic(mime=True) - with open(output_file, "rb") as f: + decoded_dir = "Decoded" + decoded_file_path = f"{decoded_dir}/{output_file}" + with open(decoded_file_path, "rb") as f: content = f.read() mime_type = mime.from_buffer(content) @@ -215,7 +241,7 @@ def scan_for_script_tags(output_file): def decode_main(args): script_content = extract_script_content(args.input_html_file) - decoded_content, encoding_steps = decode_random_encoding(script_content) + decoded_content, encoding_steps = decode_random_encoding(script_content, args.step_mode) write_file(args.output_file, decoded_content, mode='w', encoding='utf-8') scan_for_mime_types(args.output_file) scan_for_script_tags(args.output_file) diff --git a/encoder.py b/encoder.py index d95d8cf..068c258 100644 --- a/encoder.py +++ b/encoder.py @@ -150,3 +150,4 @@ def main(args): if __name__ == '__main__': main(parse_arguments()) + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index bc3665e..8b0d76a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ argparse -python-magic \ No newline at end of file +python-magic +readchar \ No newline at end of file