[HWS-01] ── The Secret Life of Backdoors

Actually it’s the hardware that allows you to hack something the hard way

Learn it with me

I’ve decided to do something a little different with this series. It’s not just going to be a bunch of blog posts. Nope! I’ve decided to turn this into a full course—complete with videos, blogs, projects, and challenges.

Honestly, it will challenge me more than it offers something to you. I’m trying to learn by doing what I love. But the goal isn’t just to read a book and blog about it. I want to be practical and output as much as I take in.

Hopefully, you’ll enjoy the journey… I mean, at the end of it, you’ll see me hoping that you enjoyed it. I’m not assuming you’ll read all of this, am I? You might identify as not reading this blog—just as I identify as a penguin 🐧.

My approach

I’m taking a different approach here. As my name suggests, I’m a Muslim. Actually, a Muslim who supports Palestine, and I’ll weave that into this series. You can expect a combination of Islamic stories, History, and related projects alongside the usual technical topics I dive into it.

اللَّهُمَّ حُلَّ بَيْنَهُمْ وَبَيْنَ مَنْ عَادَاهُمْ، اَللَّهُمَّ أَلْهِمْهُمْ رُشْدَهُمْ وَافْتَحْ عَلَيْهِمْ أَبْوَابَ الحِكْمَةِ وَالأَمَانَ، وَاْلعَفْوِ وَاْلغُفْرَانِ

Here’s how the journey will go:

Throughout this whole journey, I’ll be building projects I genuinely enjoy. I’ll also be solving CTFs and replicating them, hosting the source code on each blog so you can understand it. And of course, I’ll be recording YouTube videos and embedding them in each blog post.

The “Hack” to the “Hacker”

Before diving into anything, I like to study what it actually is. What does it imply to me? What kind of hat am I putting on here (at least, it’s not a kippah 😄), and what does it mean to others?

When I first started, I asked myself: “What does hack even mean?” As someone who doesn’t trust history much, I found one story I actually liked:

“A person who enjoys exploring the details of programmable systems and > how to stretch their capabilities, as opposed to most users, who prefer to > learn only the minimum necessary.”

That’s how the Jargon File defines it, and honestly, I like it. It really describes me.

That’s what I aim to do—learn, apply, fail, and apply again after failing (probably a couple more times). So, this blog may be just another failed success.

Another piece of chip

Apart from the title of this blog which implies that it’s going to be about hardware security and hacking, which actually it is to some extent.

I can’t just learn how to hack something without learning what is it that I am trying to hack, in this case, the hardware.

How to create your own computer

Actually, it’s a pretty easy task depending on what level of abstraction you are looking from, for example, creating it from the very bottom up including a ton of knowledge and working hours to make the smallest set of transistors, busses, and each logical gate which is way out of our scope, my scope is to understand how the hardware works so I can build up a computer from whatever components available given they’re usable for such a task

so to build one we need first to understand what makes a computer and what actually defines one to be

“A computer is a machine that can be programmed to automatically carry out sequences of arithmetic or logical operations.”

and that’s actually true if we disregard the scale, the power and speed of the operations everything around us becomes a computer in one sense or another and that might include but is not limited to, of course, your TV, smartwatch, toothbrush if you care so much to get a programmable one per se, or even a toaster.

but that’s the normal perspective of what makes a computer to the normal user or consumer, which we are not in this case, what makes a computer at least for me, is anything that YOU use because if it exists it means it’s hackable and if you use it, it means you leave a part of your personality in your preferences and daily routine using that device which means more fun for me or Us for that matter.

but all of these perspectives doesn’t shadow the full truth of what a computer actually is under, it actually consists of 5 basic components that make up any computer if applied to its design

The Von Neumann model:

See, all electronic devices kind of have these 5 pillars, let’s say your earbuds or airpods whatever air pollutes your ear, includes input in the shape of microphone, output as for the speaker, and working memory which might be very tiny as it only saves some data to be output by the speaker and permanent which holds its core instructions.

The Central Processing Unit

The CPU or in other words, the brain of every device, this is where all the calculations happen, not really, but mostly it is. and actually, it doesn’t do the calculations as we perceive them in our real world of maths of physics, it actually uses a different simpler system simpler for the computers not us actually which is binary that is zeros and ones

it is the one that computes the arithmetic, logic, control, and input/output operations as instructed by the programs that are on the computer.

and the components that make it up consist of the Control Unit (CU), The Arithmetic Logic Unit (ALU), and some registers that enable it to process data instructions.

Actually, let’s talk about some components that are either in the CPU or is closely interfering with it so we kind of build a cloud of knowledge around the current topic

Name Description
Random Access Memory (RAM) This is the main memory where The CPU fetches instructions and data it is a volatile memory meaning that it loses the data in it when the power is off, that’s why automatic save was invented so whenever the power goes out you don’t actually lose everything
Control Unit (CU) This is the component that controls the flow of data in the CPU. it interprets the instructions from the program and signal to each specific part of the CPU to do its part of the instruction
Memory Management Unit This unit manages the translation of the virtual memory addresses to physical memory addresses and ensure that Its process is separated into a contained and different memory space
Cache

This includes the L1, L2, L3 Cache which are the smallest and fastest memory in your computer, they are located near the CPU cores only differ in size and speed.

    • L1 is the smallest and the fastest
    • L2 is slightly larger and slower
    • L3 is the largest and the slowest
    The cache is actually used to save the frequently accessed data to speed up the process rather than storing it and retrieving it from the memory
Registers These are small, fast storages located within the CPU used to hold data temporarily during execution and includes
    • A & B registers
      • these are general-purpose registers used for various operations
    • Accumulator
      • A special purpose register used for arithmetic and logic operations
    • Instruction Pointer (IP)
      • Holds the address of the next instruction to be executed in memory
    • Instruction Register
      • Holds the current instruction being executed

How does the CPU actually work

the CPU goes through a simple loop that is referred to as the fetch-decode-execute cycle to process instruction and this can be broken down into many sub-steps that would help to enhance the processing of a certain task. This cycle includes the following stages: searching for the instruction within the memory, interpreting the instruction to know what needs to be done with it, performing the necessary action and, if necessary, storing the results back in memory.

Fetch stage:

At the fetch stage of the cycle the Program Counter (PC) contains the address of the next instruction to be executed. Instruction is also fetched from the memory location referenced by the content of the PC and after that, the fetched instruction is located in the IR. Afterwards, the PC is incremented to the next instruction to enable the CPU to prepare for the next cycle’s operation.

Decode Stage:

In the decode stage, the CU takes the meaning of an instruction that is stored in the IR. The CU produces control signals that enable the other parts of the CPU to perform the required activities like loading data from memory to perform ARITHMETIC LOGIC UNIT ALU operations by the instructions to be executed.

Execute Stage:

In the execute stage, the arithmetic logic unit ALU carries out an arithmetic or logical operation according to the decoded instruction. If the instruction is a data transfer type, the CPU loads from or stores to the main memory. In control-type operations including the jump or branch instructions, the PC is updated to correspond to the new instructional sequence.

Store Stage:

In the store stage, the completion of the executed instruction is written back to the memory or registers based on the CPU; this is important to keep the outcome available for other instructions or processing.

The Binary system

This numbering system was meant to make it easier on the computer hardware to do its calculations, it is as fast as the transistor could change it’s state from one state to another and actually that MHZ that you find next to each processor is kind of a unit that measures this speed but on an upper level of abstraction

and as an example to further understand this, the letter “A” that you see everywhere and can recognize it is actually saved in the computer as 01000001

but why exactly do we need it to be only 0’s and 1’s, it’s because each and the single component in the computer actually consists of millions of transistors that work by either holding current or not holding it, full or empty, on or off, true or false. whatever you like to call it and that was the reason for calling it bi in binary.

it means that it takes 8 transistors to process that one single uppercase letter in your computer

A ”speed” which is used to indicate clock frequency. It really means: million cycles per second. The more MHZ, the more data operations can be performed per second.

it really measures the number of operations that can be done per given unit of time, which is if we break it down

this is actually a very top-level view of what’s happening inside, but it works, for now until we know what we don’t later.

and to make it even more engaging, we’ll create a very minimal emulation of a CPU using the C programming language.

The C-PU Emulator

Part 1: Header Files and Macros

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define MEMORY_SIZE 8
#define REGISTER_NUMBER 4

Part 2: CPU Struct Definition

// Define a type alias 'CPU' for the struct
typedef struct {
  uint8_t memory[MEMORY_SIZE];        // Memory array of 8 bytes
  uint8_t registers[REGISTER_NUMBER]; // Array of 4 registers
  uint8_t register_number;            // Register number to use in operations
  uint8_t opcode;                     // Current instruction opcode
  uint8_t memory_operand;             // Operand specifying a memory address
  uint8_t immediate_value;            // Immediate value for operations
  uint8_t program_counter; // Program counter to track the current instruction
  uint8_t destination_register; // Destination register for certain instructions
} CPU;

Part 3: Reset Function

/*
 * reset_cpu - Resets the CPU state to its initial values
 * @cpu: Pointer to the CPU struct
 */
void reset_cpu(CPU *cpu) {
  for (int i = 0; i < MEMORY_SIZE; i++) {
    cpu->memory[i] = 0;
  }
  for (int j = 0; j < REGISTER_NUMBER; j++) {
    cpu->registers[j] = 0;
  }
  cpu->program_counter = 0;
  cpu->destination_register = 0;

  // Output the reset status with improved formatting
  printf("\n[INFO] CPU has been reset successfully."
         "\n");
}

Part 4: Print CPU State Function

/*
 * print_cpu_state - Prints the current state of the CPU, including memory,
 * registers, and program counter
 * @cpu: Pointer to the CPU struct
 */
void print_cpu_state(const CPU *cpu) {
  printf("\n──── CPU STATE ────\n");
  printf("Memory State:\n");
  for (int i = 0; i < MEMORY_SIZE; i++) {
    printf("  Memory[%d]: %d\n", i, cpu->memory[i]);
  }

  printf("\nRegister State:\n");
  for (int j = 0; j < REGISTER_NUMBER; j++) {
    printf("  Register[%d]: %d\n", j, cpu->registers[j]);
  }

  printf("\nProgram Counter: %d\n", cpu->program_counter);
  printf("─────────────────────\n");
}

Part 5: Execute Instruction Function

/*
 * execute_instruction - Executes the current instruction based on the opcode
 * @cpu: Pointer to the CPU struct
 */
void execute_instruction(CPU *cpu) {
  printf("[EXEC] Executing Opcode: 0x%02X\n", cpu->opcode);
  switch (cpu->opcode) {
    case 0x00: // LDR - Load value from memory into a register
      cpu->registers[cpu->register_number] = cpu->memory[cpu->memory_operand];
      printf("[LDR] Loaded Memory[%d] into Register[%d]: %d\n",
             cpu->memory_operand, cpu->register_number,
             cpu->registers[cpu->register_number]);
      cpu->program_counter++;
      break;

    case 0x01: // ADD - Add value from memory to a register
      printf("[ADD] Adding Memory[%d] (%d) to Register[%d] (%d)\n",
             cpu->memory_operand, cpu->memory[cpu->memory_operand],
             cpu->register_number, cpu->registers[cpu->register_number]);
      cpu->registers[cpu->register_number] += cpu->memory[cpu->memory_operand];
      cpu->program_counter++;
      break;

    // Additional cases...

    case 0x06: // HLT - Halt the CPU
      printf("[HALT] CPU halted.\n");
      exit(0);
      break;

    default: // Unknown opcode
      printf("[ERROR] Unknown Instruction Opcode: 0x%02X\n", cpu->opcode);
      break;
  }
}

Part 6: Main Function

/*
 * main - Main function to run the CPU simulation
 */
int main(void) {
  // Declare and initialize the CPU
  CPU cpu;
  reset_cpu(&cpu); // Reset the CPU state at the start

  FILE *file;
  file = fopen("asm.b", "rb");

  uint8_t command[MAX_COMMAND_LENGTH];

  if (file == NULL) {
    printf("[ERROR] Could not open assembly file.\n");
    return 1;
  }

  while (fread(command, sizeof(uint8_t), 1, file) == 1) {
    cpu.opcode = command[0];

    if (cpu.opcode == 0x05 || cpu.opcode == 0x09) {
      execute_instruction(&cpu);
    } else if (cpu.opcode == 0x0A || cpu.opcode == 0x0B) {
      fread(&cpu.register_number, sizeof(uint8_t), 1, file);
      execute_instruction(&cpu);
    } else if (cpu.opcode == 0x0C) {
      fread(&cpu.register_number, sizeof(uint8_t), 1, file);
      fread(&cpu.destination_register, sizeof(uint8_t), 1, file);
      execute_instruction(&cpu);
    } else if (cpu.opcode == 0x06) {
      execute_instruction(&cpu);
    } else if (cpu.opcode == 0x07) {
      fread(&cpu.memory_operand, sizeof(uint8_t), 1, file);
      fread(&cpu.immediate_value, sizeof(uint8_t), 1, file);
      execute_instruction(&cpu);
    } else {
      fread(&cpu.register_number, sizeof(uint8_t), 1, file);
      fread(&cpu.memory_operand, sizeof(uint8_t), 1, file);
      execute_instruction(&cpu);
    }
  }

  fclose(file);
  print_cpu_state(&cpu); // Print the final CPU state

  return 0;
}

Download the code