---
toc: true
---
# Obedient Cat
To solve this challenge we had to download the flag from the shared link and cat it to get the contents.
```
wget https://mercury.picoctf.net/static/a5683698ac318b47bd060cb786859f23/flag
```
```
cat flag
```
# Warmed Up
To solve this challenge we have to convert the 0x3D which is a base 16 to a decimal (base 10). We will you the script I came up with hex2decimal.py to do the conversion.
```
def hex_to_decimal_binary():
"""Convert hexadecimal input to decimal and binary"""
print("Hex to Decimal & Binary Converter")
print("=================================")
while True:
# Get user input
hex_input = input("\nEnter a hexadecimal number (or 'quit' to exit): ").strip()
# Check if user wants to exit
if hex_input.lower() == 'quit':
print("Goodbye!")
break
# Validate input (should only contain 0-9, A-F, a-f)
if not all(c in '0123456789ABCDEFabcdef' for c in hex_input):
print("Error: Invalid hexadecimal input. Please use only 0-9, A-F.")
continue
try:
# Convert to decimal
decimal_value = int(hex_input, 16)
# Convert to binary (remove the '0b' prefix)
binary_value = bin(decimal_value)[2:]
# Format output with spaces for readability (optional)
# Group binary digits in sets of 4
binary_formatted = ' '.join([binary_value[i:i+4] for i in range(0, len(binary_value), 4)])
# Display results
print(f"\nHexadecimal: {hex_input.upper()}")
print(f"Decimal: {decimal_value}")
print(f"Binary: {binary_formatted}")
except ValueError:
print("Error: Invalid hexadecimal value. Please try again.")
# Run the converter
if __name__ == "__main__":
hex_to_decimal_binary()
```
```
python3 hex2decimal.py
Enter a hexadecimal number (or 'quit' to exit): 3D
Hexadecimal: 3D
Decimal: 61
Binary: 1111 01
Enter a hexadecimal number (or 'quit' to exit): quit
```
# ASCII Numbers
For this challenge we had to convert the `0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x35 0x63 0x31 0x31 0x5f 0x6e 0x30 0x5f 0x71 0x75 0x33 0x35 0x37 0x31 0x30 0x6e 0x35 0x5f 0x31 0x6c 0x6c 0x5f 0x74 0x33 0x31 0x31 0x5f 0x79 0x33 0x5f 0x6e 0x30 0x5f 0x6c 0x31 0x33 0x35 0x5f 0x34 0x34 0x35 0x64 0x34 0x31 0x38 0x30 0x7d ` to readable string. To solve this we will use the following script hexascii2string.py
```
def hex_ascii_to_string():
"""Convert hexadecimal ASCII input to readable string"""
print("Hex ASCII to String Converter")
print("=============================")
print("Enter hexadecimal values (with or without 0x prefix)")
print("Examples: 0x48 0x65 0x6C 0x6C 0x6F or 48 65 6C 6C 6F")
print("Type 'quit' to exit\n")
while True:
# Get user input
hex_input = input("Enter hex ASCII values: ").strip()
# Check if user wants to exit
if hex_input.lower() == 'quit':
print("Goodbye!")
break
if not hex_input:
print("Error: No input provided. Please enter hexadecimal values.")
continue
try:
# Split input into individual hex values
hex_values = hex_input.split()
# Convert each hex value to character
result_chars = []
for hex_val in hex_values:
# Remove '0x' prefix if present
if hex_val.startswith('0x'):
hex_val = hex_val[2:]
# Convert hex to decimal, then to character
decimal_val = int(hex_val, 16)
if 0 <= decimal_val <= 127: # Basic ASCII range
char = chr(decimal_val)
result_chars.append(char)
else:
print(f"Warning: Value 0x{hex_val.upper()} ({decimal_val}) is outside standard ASCII range")
result_chars.append('�') # Replacement character for invalid values
# Combine characters into final string
result_string = ''.join(result_chars)
# Display results
print(f"\nInput: {hex_input}")
print(f"Hex: {' '.join([f'0x{h.upper()}' for h in hex_values])}")
print(f"Decimal: {' '.join([str(int(h, 16)) for h in hex_values])}")
print(f"String: '{result_string}'")
print()
except ValueError as e:
print(f"Error: Invalid hexadecimal input - {e}")
print("Please enter valid hex values (e.g., '0x48 0x65' or '48 65')\n")
except Exception as e:
print(f"Unexpected error: {e}\n")
# Alternative function for single hex value input (like 0x3D)
def single_hex_to_char():
"""Convert a single hexadecimal value to its character representation"""
print("Single Hex to Character Converter")
print("=================================")
while True:
hex_input = input("\nEnter a single hex value (e.g., 0x3D): ").strip()
if hex_input.lower() == 'quit':
print("Goodbye!")
break
try:
# Remove '0x' prefix if present
if hex_input.startswith('0x'):
hex_clean = hex_input[2:]
else:
hex_clean = hex_input
# Convert to decimal and then to character
decimal_val = int(hex_clean, 16)
char = chr(decimal_val)
print(f"\nHex: {hex_input.upper()}")
print(f"Decimal: {decimal_val}")
print(f"Char: '{char}'")
except ValueError:
print("Error: Please enter a valid hexadecimal value (e.g., 0x3D, 41, 0x20)")
except Exception as e:
print(f"Error: {e}")
# Run the converter
if __name__ == "__main__":
print("Choose conversion mode:")
print("1. Multiple hex values to string")
print("2. Single hex value to character")
choice = input("\nEnter choice (1 or 2): ").strip()
if choice == "1":
hex_ascii_to_string()
elif choice == "2":
single_hex_to_char()
else:
print("Invalid choice. Running multiple values converter by default.")
hex_ascii_to_string()
```
```
python3 hexascii2string.py
Choose conversion mode:
1. Multiple hex values to string
2. Single hex value to character
Enter choice (1 or 2): 1
Hex ASCII to String Converter
=============================
Enter hexadecimal values (with or without 0x prefix)
Examples: 0x48 0x65 0x6C 0x6C 0x6F or 48 65 6C 6C 6F
Type 'quit' to exit
Enter hex ASCII values: 0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x35 0x63 0x31 0x31 0x5f 0x6e 0x30 0x5f 0x71 0x75 0x33 0x35 0x37 0x31 0x30 0x6e 0x35 0x5f 0x31 0x6c 0x6c 0x5f 0x74 0x33 0x31 0x31 0x5f 0x79 0x33 0x5f 0x6e 0x30 0x5f 0x6c 0x31 0x33 0x35 0x5f 0x34 0x34 0x35 0x64 0x34 0x31 0x38 0x30 0x7d
Input: 0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x35 0x63 0x31 0x31 0x5f 0x6e 0x30 0x5f 0x71 0x75 0x33 0x35 0x37 0x31 0x30 0x6e 0x35 0x5f 0x31 0x6c 0x6c 0x5f 0x74 0x33 0x31 0x31 0x5f 0x79 0x33 0x5f 0x6e 0x30 0x5f 0x6c 0x31 0x33 0x35 0x5f 0x34 0x34 0x35 0x64 0x34 0x31 0x38 0x30 0x7d
Hex: 0x0X70 0x0X69 0x0X63 0x0X6F 0x0X43 0x0X54 0x0X46 0x0X7B 0x0X34 0x0X35 0x0X63 0x0X31 0x0X31 0x0X5F 0x0X6E 0x0X30 0x0X5F 0x0X71 0x0X75 0x0X33 0x0X35 0x0X37 0x0X31 0x0X30 0x0X6E 0x0X35 0x0X5F 0x0X31 0x0X6C 0x0X6C 0x0X5F 0x0X74 0x0X33 0x0X31 0x0X31 0x0X5F 0x0X79 0x0X33 0x0X5F 0x0X6E 0x0X30 0x0X5F 0x0X6C 0x0X31 0x0X33 0x0X35 0x0X5F 0x0X34 0x0X34 0x0X35 0x0X64 0x0X34 0x0X31 0x0X38 0x0X30 0x0X7D
Decimal: 112 105 99 111 67 84 70 123 52 53 99 49 49 95 110 48 95 113 117 51 53 55 49 48 110 53 95 49 108 108 95 116 51 49 49 95 121 51 95 110 48 95 108 49 51 53 95 52 52 53 100 52 49 56 48 125
String: '{flag}'
```
# Picker I
For this challege we are told that the service provides a random number, but can it do anything else?
We'll download the sorce code to have a look at what's happening behind the scene.
```
wget https://artifacts.picoctf.net/c/515/picker-I.py
```
We can view the file using the cat command
```
import sys
def getRandomNumber():
print(4) # Chosen by fair die roll.
# Guaranteed to be random.
# (See XKCD)
def exit():
sys.exit(0)
def esoteric1():
esoteric = \
'''
int query_apm_bios(void)
{
struct biosregs ireg, oreg;
/* APM BIOS installation check */
initregs(&ireg);
ireg.ah = 0x53;
intcall(0x15, &ireg, &oreg);
if (oreg.flags & X86_EFLAGS_CF)
return -1; /* No APM BIOS */
if (oreg.bx != 0x504d) /* "PM" signature */
return -1;
if (!(oreg.cx & 0x02)) /* 32 bits supported? */
return -1;
/* Disconnect first, just in case */
ireg.al = 0x04;
intcall(0x15, &ireg, NULL);
/* 32-bit connect */
ireg.al = 0x03;
intcall(0x15, &ireg, &oreg);
boot_params.apm_bios_info.cseg = oreg.ax;
boot_params.apm_bios_info.offset = oreg.ebx;
boot_params.apm_bios_info.cseg_16 = oreg.cx;
boot_params.apm_bios_info.dseg = oreg.dx;
boot_params.apm_bios_info.cseg_len = oreg.si;
boot_params.apm_bios_info.cseg_16_len = oreg.hsi;
boot_params.apm_bios_info.dseg_len = oreg.di;
if (oreg.flags & X86_EFLAGS_CF)
return -1;
/* Redo the installation check as the 32-bit connect;
some BIOSes return different flags this way... */
ireg.al = 0x00;
intcall(0x15, &ireg, &oreg);
if ((oreg.eflags & X86_EFLAGS_CF) || oreg.bx != 0x504d) {
/* Failure with 32-bit connect, try to disconnect and ignore */
ireg.al = 0x04;
intcall(0x15, &ireg, NULL);
return -1;
}
boot_params.apm_bios_info.version = oreg.ax;
boot_params.apm_bios_info.flags = oreg.cx;
return 0;
}
'''
print(esoteric)
def win():
# This line will not work locally unless you create your own 'flag.txt' in
# the same directory as this script
flag = open('flag.txt', 'r').read()
#flag = flag[:-1]
flag = flag.strip()
str_flag = ''
for c in flag:
str_flag += str(hex(ord(c))) + ' '
print(str_flag)
def esoteric2():
esoteric = \
'''
#include "boot.h"
#define MAX_8042_LOOPS 100000
#define MAX_8042_FF 32
static int empty_8042(void)
{
u8 status;
int loops = MAX_8042_LOOPS;
int ffs = MAX_8042_FF;
while (loops--) {
io_delay();
status = inb(0x64);
if (status == 0xff) {
/* FF is a plausible, but very unlikely status */
if (!--ffs)
return -1; /* Assume no KBC present */
}
if (status & 1) {
/* Read and discard input data */
io_delay();
(void)inb(0x60);
} else if (!(status & 2)) {
/* Buffers empty, finished! */
return 0;
}
}
return -1;
}
/* Returns nonzero if the A20 line is enabled. The memory address
used as a test is the int $0x80 vector, which should be safe. */
#define A20_TEST_ADDR (4*0x80)
#define A20_TEST_SHORT 32
#define A20_TEST_LONG 2097152 /* 2^21 */
static int a20_test(int loops)
{
int ok = 0;
int saved, ctr;
set_fs(0x0000);
set_gs(0xffff);
saved = ctr = rdfs32(A20_TEST_ADDR);
while (loops--) {
wrfs32(++ctr, A20_TEST_ADDR);
io_delay(); /* Serialize and make delay constant */
ok = rdgs32(A20_TEST_ADDR+0x10) ^ ctr;
if (ok)
break;
}
wrfs32(saved, A20_TEST_ADDR);
return ok;
}
/* Quick test to see if A20 is already enabled */
static int a20_test_short(void)
{
return a20_test(A20_TEST_SHORT);
}
'''
print(esoteric)
while(True):
try:
print('Try entering "getRandomNumber" without the double quotes...')
user_input = input('==> ')
eval(user_input + '()')
except Exception as e:
print(e)
break
```
When we run the code we get a prompt that says try entering the getRandomNumber and once we enter it we get an output of 4.
```
python3 picker-I.py
Try entering "getRandomNumber" without the double quotes...
==> getRandomNumber
4
Try entering "getRandomNumber" without the double quotes...
==> quit
```
Looking at the source code we can see that the getRandomNumber is a funtion that prints 4
```
def getRandomNumber():
print(4) # Chosen by fair die roll.
# Guaranteed to be random.
# (See XKCD)
```
Looking at the code we can see another function called win
```
def win():
# This line will not work locally unless you create your own 'flag.txt' in
# the same directory as this script
flag = open('flag.txt', 'r').read()
#flag = flag[:-1]
flag = flag.strip()
str_flag = ''
for c in flag:
str_flag += str(hex(ord(c))) + ' '
print(str_flag)
```
So having this we can try using typing the win function to see if it'll return the flag.
```
python3 picker-I.py
Try entering "getRandomNumber" without the double quotes...
==> getRandomNumber
4
Try entering "getRandomNumber" without the double quotes...
==> win
[Errno 2] No such file or directory: 'flag.txt'
```
Awesome this means that if we know the funtion names we can just type the name of the function and it gets executed. Now Let's do the actual challenge and get the flag.
We will connect using `nc` command and type `win` to retrieve the flag.
```
nc saturn.picoctf.net 56137
Try entering "getRandomNumber" without the double quotes...
==> getRandomNumber
4
Try entering "getRandomNumber" without the double quotes...
==> win
0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x63 0x65 0x34 0x62 0x35 0x64 0x35 0x62 0x7d
Try entering "getRandomNumber" without the double quotes...
==> quit
```
We get the flag as hex we can decode it with the hexascii2string.py to get the flag
```
python3 hexascii2string.py
Choose conversion mode:
1. Multiple hex values to string
2. Single hex value to character
Enter choice (1 or 2): 1
Hex ASCII to String Converter
=============================
Enter hexadecimal values (with or without 0x prefix)
Examples: 0x48 0x65 0x6C 0x6C 0x6F or 48 65 6C 6C 6F
Type 'quit' to exit
Enter hex ASCII values: 0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x63 0x65 0x34 0x62 0x35 0x64 0x35 0x62 0x7d
Input: 0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x63 0x65 0x34 0x62 0x35 0x64 0x35 0x62 0x7d
Hex: 0x0X70 0x0X69 0x0X63 0x0X6F 0x0X43 0x0X54 0x0X46 0x0X7B 0x0X34 0x0X5F 0x0X64 0x0X31 0x0X34 0x0X6D 0x0X30 0x0X6E 0x0X64 0x0X5F 0x0X31 0x0X6E 0x0X5F 0x0X37 0x0X68 0x0X33 0x0X5F 0x0X72 0x0X30 0x0X75 0x0X67 0x0X68 0x0X5F 0x0X63 0x0X65 0x0X34 0x0X62 0x0X35 0x0X64 0x0X35 0x0X62 0x0X7D
Decimal: 112 105 99 111 67 84 70 123 52 95 100 49 52 109 48 110 100 95 49 110 95 55 104 51 95 114 48 117 103 104 95 99 101 52 98 53 100 53 98 125
String: 'picoCTF{flag}'
Enter hex ASCII values: quit
Goodbye!
```
We can automate this by using the python's pwn tools as shown in the code below:
```
#!/usr/bin/env python3
from pwn import *
import argparse
import re
def solve_with_debug(host, port):
try:
# Connect with verbose logging
context.log_level = 'debug' # Set to debug to see all communication
conn = remote(host, port)
# Receive initial data
initial_data = conn.recvuntil(b'==> ', timeout=5)
print("=== INITIAL DATA ===")
print(initial_data.decode())
print("====================")
# Send the win command
conn.sendline(b'win')
print("Sent: 'win'")
# Try to receive response with multiple approaches
response = b""
# Method 1: Try recvline with timeout
try:
for i in range(10): # Try to read up to 10 lines
line = conn.recvline(timeout=2)
if line:
response += line
print(f"Line {i+1}: {line.decode()}")
else:
break
except:
pass
# Method 2: If no lines, try raw recv
if not response:
try:
response = conn.recv(timeout=2)
print(f"Raw data: {response.decode()}")
except:
pass
# Method 3: If still nothing, the server might be waiting for more input
if not response:
print("No immediate response received. The server might be waiting.")
print("Trying to send empty line or 'exit' to trigger response...")
conn.sendline(b'') # Send empty line
try:
response = conn.recv(timeout=2)
print(f"After empty line: {response.decode()}")
except:
pass
# Convert response to string for analysis
response_str = response.decode('utf-8', errors='ignore')
print("\n=== FULL RESPONSE ANALYSIS ===")
print(f"Raw response: {response}")
print(f"As string: {response_str}")
# Try different patterns to find the flag
flag = None
# Pattern 1: Hex values (0xXX format)
hex_pattern = r'0x[0-9a-fA-F]{2}'
hex_values = re.findall(hex_pattern, response_str)
if hex_values:
flag = ''.join(chr(int(h[2:], 16)) for h in hex_values)
print(f"Flag from hex: {flag}")
# Pattern 2: Direct flag format (picoCTF{...})
flag_pattern = r'picoCTF\{[^}]+\}'
direct_flag = re.search(flag_pattern, response_str)
if direct_flag:
flag = direct_flag.group(0)
print(f"Direct flag found: {flag}")
# Pattern 3: Maybe it's already decoded
if not flag and 'pico' in response_str.lower():
print("Flag might be in plain text:")
print(response_str)
if flag:
print(f"\n SUCCESS! Flag: {flag}")
else:
print("\n Could not find flag. Possible issues:")
print("1. Server might have different command")
print("2. Flag might be in different format")
print("3. Server might need different approach")
# Try other possible commands
try_other_commands(conn)
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
finally:
conn.close()
def try_other_commands(conn):
"""
Try alternative commands if 'win' doesn't work
"""
print("\n=== TRYING ALTERNATIVE COMMANDS ===")
alternatives = ['getRandomNumber', 'exit', 'esoteric1', 'esoteric2', 'flag', 'print_flag']
for cmd in alternatives:
try:
print(f"Trying: {cmd}")
conn.sendline(cmd.encode())
response = conn.recv(timeout=2)
if response:
print(f"Response to '{cmd}': {response.decode()}")
# Check if this might contain flag
if b'pico' in response.lower() or b'0x' in response:
print(f" Possible flag response from '{cmd}'!")
break
conn.recvuntil(b'==> ', timeout=2) # Get back to prompt
except:
print(f"No response to '{cmd}'")
break
def interactive_mode(host, port):
"""
Full interactive mode to explore manually
"""
print("=== INTERACTIVE MODE ===")
print("Connect manually with: nc", host, port)
print("Then try these commands:")
print("- win")
print("- getRandomNumber")
print("- esoteric1")
print("- esoteric2")
print("- exit")
print("- flag")
print("- print_flag")
print()
print("Look for hex values (0xXX) or picoCTF{...} format")
def main():
parser = argparse.ArgumentParser(description='Solve picker-I challenge')
parser.add_argument('host', help='Target host')
parser.add_argument('port', type=int, help='Target port')
parser.add_argument('-i', '--interactive', action='store_true', help='Interactive mode')
args = parser.parse_args()
if args.interactive:
interactive_mode(args.host, args.port)
else:
solve_with_debug(args.host, args.port)
if __name__ == '__main__':
main()
```
```
python3 picker-I_solution.py saturn.picoctf.net 53028
[+] Opening connection to saturn.picoctf.net on port 53028: Done
[DEBUG] Received 0x40 bytes:
b'Try entering "getRandomNumber" without the double quotes...\n'
b'==> '
=== INITIAL DATA ===
Try entering "getRandomNumber" without the double quotes...
==>
====================
[DEBUG] Sent 0x4 bytes:
b'win\n'
Sent: 'win'
[DEBUG] Received 0x109 bytes:
b'0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x63 0x65 0x34 0x62 0x35 0x64 0x35 0x62 0x7d \n'
b'Try entering "getRandomNumber" without the double quotes...\n'
b'==> '
Line 1: 0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x63 0x65 0x34 0x62 0x35 0x64 0x35 0x62 0x7d
Line 2: Try entering "getRandomNumber" without the double quotes...
=== FULL RESPONSE ANALYSIS ===
Raw response: b'0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x63 0x65 0x34 0x62 0x35 0x64 0x35 0x62 0x7d \nTry entering "getRandomNumber" without the double quotes...\n'
As string: 0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x63 0x65 0x34 0x62 0x35 0x64 0x35 0x62 0x7d
Try entering "getRandomNumber" without the double quotes...
Flag from hex: picoCTF{flag}
🎉 SUCCESS! Flag: picoCTF{flag}
[*] Closed connection to saturn.picoctf.net port 53028
```
# Bit-O-Asm-1
For this challenge we are provided with an assembly dump and we are tasked with figuring out what is at the `eax` register.
```
wget https://artifacts.picoctf.net/c/509/disassembler-dump0_a.txt
```
```
cat disassembler-dump0_a.txt
<+0>: endbr64
<+4>: push rbp
<+5>: mov rbp,rsp
<+8>: mov DWORD PTR [rbp-0x4],edi
<+11>: mov QWORD PTR [rbp-0x10],rsi
<+15>: mov eax,0x30
<+20>: pop rbp
<+21>: ret
```
From the dump we can see the `0x30` being stored in the `eax` register. Let's decode using the hex2decimal.py script and get the decimal value as the flag.
```
python3 hex2decimal.py
Hex to Decimal & Binary Converter
=================================
Enter a hexadecimal number (or 'quit' to exit): 30
Hexadecimal: 30
Decimal: 48
Binary: 1100 00
Enter a hexadecimal number (or 'quit' to exit): quit
Goodbye!
```
# Bit-O-Asm-2
For this challenge we are provided with an assembly dump and tasked with identifying the value in the `eax` register.
```
wget https://artifacts.picoctf.net/c/510/disassembler-dump0_b.txt
```
```
cat disassembler-dump0_b.txt
<+0>: endbr64
<+4>: push rbp
<+5>: mov rbp,rsp
<+8>: mov DWORD PTR [rbp-0x14],edi
<+11>: mov QWORD PTR [rbp-0x20],rsi
<+15>: mov DWORD PTR [rbp-0x4],0x9fe1a
<+22>: mov eax,DWORD PTR [rbp-0x4]
<+25>: pop rbp
<+26>: ret
```
The PTR is a pointer to the specified address. As we can see the value of `edi` is store in the `rbp-0x14` address, the `rsi` is stored in the `rbp-0x20` address, the value `0x9fe1a` is stored in the `rbp-0x4` address and finally the value in the `rbp-0x4` is stored in the `eax` address. This means that `eax` holds the value `0x9fe1a`. So we can decode it using the hex2decimal.py script to obtain the flag.
```
python3 hex2decimal.py
Hex to Decimal & Binary Converter
=================================
Enter a hexadecimal number (or 'quit' to exit): 9fe1a
Hexadecimal: 9FE1A
Decimal: 654874
Binary: 1001 1111 1110 0001 1010
Enter a hexadecimal number (or 'quit' to exit): quit
Goodbye!
```
# Bit-O-Asm-3
For this challenge we are given an assembly dump and tasked to find out the value in the `eax` register.
```
wget https://artifacts.picoctf.net/c/530/disassembler-dump0_c.txt
```
```
cat disassembler-dump0_c.txt
<+0>: endbr64
<+4>: push rbp
<+5>: mov rbp,rsp
<+8>: mov DWORD PTR [rbp-0x14],edi
<+11>: mov QWORD PTR [rbp-0x20],rsi
<+15>: mov DWORD PTR [rbp-0xc],0x9fe1a
<+22>: mov DWORD PTR [rbp-0x8],0x4
<+29>: mov eax,DWORD PTR [rbp-0xc]
<+32>: imul eax,DWORD PTR [rbp-0x8]
<+36>: add eax,0x1f5
<+41>: mov DWORD PTR [rbp-0x4],eax
<+44>: mov eax,DWORD PTR [rbp-0x4]
<+47>: pop rbp
<+48>: ret
```
For this we can see that the value in `edi` is stored in the `rbp-0x14` address, `rsi` value is stored in `rbp-0x20` address, the value `0x9fe1a` is stored to the `rbp-0xc` address, `0x4` is stored to the `rbp-0x8` address, the value in the `rbp-0xc` which is `0x9fe1a` is stored to the eax, this value is then multiplied (imul in assembly means multiplication) by the value in `rbp-0x8` adress which is `0x4` and the result is stored to the `eax` register, `0x1f5` is then added to the result and the final result is stored to `eax`.
To simplify this `0x9fe1a` is multiplied by `0x4` and `0x1f5` is added to the result. The final result is the value in the `eax` address.
We'll use the hex2decimal.py script to get the corresponding decimal value and do the compution to get the final result as our flag.
```
python3 hex2decimal.py
Hex to Decimal & Binary Converter
=================================
Enter a hexadecimal number (or 'quit' to exit): 9fe1a
Hexadecimal: 9FE1A
Decimal: 654874
Binary: 1001 1111 1110 0001 1010
Enter a hexadecimal number (or 'quit' to exit): 4
Hexadecimal: 4
Decimal: 4
Binary: 100
Enter a hexadecimal number (or 'quit' to exit): 1f5
Hexadecimal: 1F5
Decimal: 501
Binary: 1111 1010 1
Enter a hexadecimal number (or 'quit' to exit): quit
Goodbye!
python3
Python 3.13.7 (main, Aug 15 2025, 12:34:02) [GCC 15.2.1 20250813] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> (654874 * 4) + 501
2619997
```
# Bit-O-Asm-4
For this challenge we are provided with an assembly dump and tasked to find the value in the `eax` register.
```
wget https://artifacts.picoctf.net/c/511/disassembler-dump0_d.txt
```
```
cat disassembler-dump0_d.txt
<+0>: endbr64
<+4>: push rbp
<+5>: mov rbp,rsp
<+8>: mov DWORD PTR [rbp-0x14],edi
<+11>: mov QWORD PTR [rbp-0x20],rsi
<+15>: mov DWORD PTR [rbp-0x4],0x9fe1a
<+22>: cmp DWORD PTR [rbp-0x4],0x2710
<+29>: jle 0x55555555514e <main+37>
<+31>: sub DWORD PTR [rbp-0x4],0x65
<+35>: jmp 0x555555555152 <main+41>
<+37>: add DWORD PTR [rbp-0x4],0x65
<+41>: mov eax,DWORD PTR [rbp-0x4]
<+44>: pop rbp
<+45>: ret
```
For this what is happening is `0x9fe1a` is stored to the `rbp-0x4` address, then a comparison is downe between the value in `rbp-0x4` and `0x2710` if is less than we jump to the address `<+37>: add DWORD PTR [rbp-0x4],0x65` where `0x65` is added to the value in `rbp-0x4` and that value is stored in `eax`. If from the comparison `rbp-0x4` is greater, `0x65` is subtracted from the value in `rbp-0x4`, the result is then stored as `eax`.
We'll use the hex2decimal.py script to decode decode the hex and get the decimal values for easier comparison.
```
python3 hex2decimal.py
Hex to Decimal & Binary Converter
=================================
Enter a hexadecimal number (or 'quit' to exit): 9fe1a
Hexadecimal: 9FE1A
Decimal: 654874
Binary: 1001 1111 1110 0001 1010
Enter a hexadecimal number (or 'quit' to exit): 2710
Hexadecimal: 2710
Decimal: 10000
Binary: 1001 1100 0100 00
Enter a hexadecimal number (or 'quit' to exit): 65
Hexadecimal: 65
Decimal: 101
Binary: 1100 101
Enter a hexadecimal number (or 'quit' to exit): quit
Goodbye!
```
From the decoding we can see that `0x9fe1a` that was in `rbp-0x4` is greater than the `0x2710`. So we will subtract the `101` from the `654874` so that we can get the flag. From the code we can see that after the subtraction, we jump to the `<main+41>` and the result of the subtraction is stored in the `eax`.
```
<+29>: jle 0x55555555514e <main+37>
<+31>: sub DWORD PTR [rbp-0x4],0x65
<+35>: jmp 0x555555555152 <main+41>
<+37>: add DWORD PTR [rbp-0x4],0x65
<+41>: mov eax,DWORD PTR [rbp-0x4]
```
```
python3
Python 3.13.7 (main, Aug 15 2025, 12:34:02) [GCC 15.2.1 20250813] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 654874 - 101
654773
```
# GDB Baby Step 1
For this challenge, we are given a binary and we are tasked with identifying the value in the `eax`.
```
wget https://artifacts.picoctf.net/c/512/debugger0_a
```
For this challenge we will use `gdb` command to analyze the binary.
```
gdb ./debugger0_a
GNU gdb (GDB) 16.3
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
93 commands loaded and 5 functions added for GDB 16.3 in 0.00ms using Python engine 3.13
Reading symbols from ./debugger0_a...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.archlinux.org>
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
(No debugging symbols found in ./debugger0_a)
gef➤
```
We can view the funtions in this binary by using `info functions`
```
gef➤ info functions
All defined functions:
Non-debugging symbols:
0x0000000000001000 _init
0x0000000000001030 __cxa_finalize@plt
0x0000000000001040 _start
0x0000000000001070 deregister_tm_clones
0x00000000000010a0 register_tm_clones
0x00000000000010e0 __do_global_dtors_aux
0x0000000000001120 frame_dummy
0x0000000000001129 main
0x0000000000001140 __libc_csu_init
0x00000000000011b0 __libc_csu_fini
0x00000000000011b8 _fini
gef➤
```
Let's disassemble the main function and see the `eax` register.
```
gef➤ disas main
Dump of assembler code for function main:
0x0000000000001129 <+0>: endbr64
0x000000000000112d <+4>: push rbp
0x000000000000112e <+5>: mov rbp,rsp
0x0000000000001131 <+8>: mov DWORD PTR [rbp-0x4],edi
0x0000000000001134 <+11>: mov QWORD PTR [rbp-0x10],rsi
0x0000000000001138 <+15>: mov eax,0x86342
0x000000000000113d <+20>: pop rbp
0x000000000000113e <+21>: ret
End of assembler dump.
gef➤
```
We can see that `eax` has the value `0x86342` assigned to it. We will use our script to decode the hex value.
```
python3 hex2decimal.py
Hex to Decimal & Binary Converter
=================================
Enter a hexadecimal number (or 'quit' to exit): 86342
Hexadecimal: 86342
Decimal: 549698
Binary: 1000 0110 0011 0100 0010
Enter a hexadecimal number (or 'quit' to exit): quit
Goodbye!
```
# GDB Baby Step 2
From this challenge we are given a binary file that we have to analyze and get the value in the `eax` registry.
```
wget https://artifacts.picoctf.net/c/520/debugger0_b
```
Let's check the functions in the binary.
```
gdb ./debugger0_b
GNU gdb (GDB) 16.3
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
93 commands loaded and 5 functions added for GDB 16.3 in 0.00ms using Python engine 3.13
Reading symbols from ./debugger0_b...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.archlinux.org>
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
(No debugging symbols found in ./debugger0_b)
gef➤ info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _init
0x0000000000401020 _start
0x0000000000401050 _dl_relocate_static_pie
0x0000000000401060 deregister_tm_clones
0x0000000000401090 register_tm_clones
0x00000000004010d0 __do_global_dtors_aux
0x0000000000401100 frame_dummy
0x0000000000401106 main
0x0000000000401150 __libc_csu_init
0x00000000004011c0 __libc_csu_fini
0x00000000004011c8 _fini
gef➤
```
Let's disassemble the main function and see what it contains.
```
gef➤ disas main
Dump of assembler code for function main:
0x0000000000401106 <+0>: endbr64
0x000000000040110a <+4>: push rbp
0x000000000040110b <+5>: mov rbp,rsp
0x000000000040110e <+8>: mov DWORD PTR [rbp-0x14],edi
0x0000000000401111 <+11>: mov QWORD PTR [rbp-0x20],rsi
0x0000000000401115 <+15>: mov DWORD PTR [rbp-0x4],0x1e0da
0x000000000040111c <+22>: mov DWORD PTR [rbp-0xc],0x25f
0x0000000000401123 <+29>: mov DWORD PTR [rbp-0x8],0x0
0x000000000040112a <+36>: jmp 0x401136 <main+48>
0x000000000040112c <+38>: mov eax,DWORD PTR [rbp-0x8]
0x000000000040112f <+41>: add DWORD PTR [rbp-0x4],eax
0x0000000000401132 <+44>: add DWORD PTR [rbp-0x8],0x1
0x0000000000401136 <+48>: mov eax,DWORD PTR [rbp-0x8]
0x0000000000401139 <+51>: cmp eax,DWORD PTR [rbp-0xc]
0x000000000040113c <+54>: jl 0x40112c <main+38>
0x000000000040113e <+56>: mov eax,DWORD PTR [rbp-0x4]
0x0000000000401141 <+59>: pop rbp
0x0000000000401142 <+60>: ret
End of assembler dump.
```
Let's make a break point at the ` 0x0000000000401141 <+59>: pop rbp` and run the program so that we can see the value at the `eax` register. To set a break point we will use the command `b *main+59` and then use the `run` command to execute the binary. The reason for adding the breakpoint at this position is so that the value in the `DWORD PTR [rbp-0x4]` to have been moved to the `eax` register.
```
gef➤ b *main+59
Breakpoint 1 at 0x401141
gef➤ run
```
After our program stops at the breakpoint, we need to observe the value in the `eax` register.
```
gef➤ info register eax
eax 0x4af4b 0x4af4b
gef➤
```
After that we use our python script to decode the value in the `eax` register.
```
python3 hex2decimal.py
Hex to Decimal & Binary Converter
=================================
Enter a hexadecimal number (or 'quit' to exit): 4af4b
Hexadecimal: 4AF4B
Decimal: 307019
Binary: 1001 0101 1110 1001 011
Enter a hexadecimal number (or 'quit' to exit): quit
Goodbye!
```
# GDB Baby Step 3
For this challenge, we are told that ` 0x2262c96b ` has been loaded to memory in the `main` function. We are tasked to examine the memory that the constant is loaded in using the GDB command `x/4xb addr`. The `4xb` represents;
* 4 - represent the count. Meaning the number of times we want to repeat the examine
* x - represent format of the result we want and for this case x represent hex
* b - is the size of memory we want to examine for this case its byte.
We will start by downloading the binary file and open it with gdb and do the examination.
```
wget https://artifacts.picoctf.net/c/531/debugger0_c
```
After Downloading and openning it with gdb, let's Observe the functions in the binary file provide.
```
gdb ./debugger0_c -q
GEF for linux ready, type `gef' to start, `gef config' to configure
93 commands loaded and 5 functions added for GDB 16.3 in 0.00ms using Python engine 3.13
Reading symbols from ./debugger0_c...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.archlinux.org>
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
(No debugging symbols found in ./debugger0_c)
gef➤ info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _init
0x0000000000401020 _start
0x0000000000401050 _dl_relocate_static_pie
0x0000000000401060 deregister_tm_clones
0x0000000000401090 register_tm_clones
0x00000000004010d0 __do_global_dtors_aux
0x0000000000401100 frame_dummy
0x0000000000401106 main
0x0000000000401130 __libc_csu_init
0x00000000004011a0 __libc_csu_fini
0x00000000004011a8 _fini
```
Let's disassemble the main function and see what it entails.
```
disas main
Dump of assembler code for function main:
0x0000000000401106 <+0>: endbr64
0x000000000040110a <+4>: push rbp
0x000000000040110b <+5>: mov rbp,rsp
0x000000000040110e <+8>: mov DWORD PTR [rbp-0x14],edi
0x0000000000401111 <+11>: mov QWORD PTR [rbp-0x20],rsi
0x0000000000401115 <+15>: mov DWORD PTR [rbp-0x4],0x2262c96b
0x000000000040111c <+22>: mov eax,DWORD PTR [rbp-0x4]
0x000000000040111f <+25>: pop rbp
0x0000000000401120 <+26>: ret
End of assembler dump.
```
From this we can see the variable we were told about. So we will have to create a break point after the variable has been loaded to memory and observe how it is stored in memory. So as we can see our value is stored in the `rbp-0x4` address which is later moved to the eax address.
So we will add a break point at main+25 then check the information at the eax address .
```
gef➤ b *main+25
Breakpoint 1 at 0x40111f
gef➤ r
```
```
gef➤ x/4xb $rbp-0x4
0x7fffffffe12c: 0x6b 0xc9 0x62 0x22
```
Since `eax` contains the actual data we can observe the value using `x/4xb $eax` but we will use `$rbp-0x4` since this is where the data is stored in this address.
If you try using `x/4xb $eax` you'll get an error as show below:
```
gef➤ x/4xb $eax
0x2262c96b: Cannot access memory at address 0x2262c96b
```
This error indicates that `0x2262c96b` is not an address but a value(data). For us to query we use the actual address where the value is store `DWORD PTR [rbp-0x4],0x2262c96b` the `rbp-0x4` address.
Happy Hacking !!