---
title: 'Reverse Engineering Fundamentals'
source: 'https://academia.sh/en/courses/attack-techniques/reverse-engineering-fundamentals'
course: 'Attack Techniques and the Threat Landscape'
language: en
updated: '2026-09-26T03:02:49+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Actually measures, with a string scan, how many indicators can be extracted from a binary'
    - 'Counts which analysis step each countermeasure breaks and how many items it hides'
    - "Separates out the items that cannot be extracted by any means and shows defense's remaining resource"
---

# Reverse Engineering Fundamentals

Measuring reverse engineering's place in defense: the total defense value of twenty fictional indicators being thirty-eight, twelve items coming out of the raw binary through a string scan, symbol stripping together with string hiding bringing those twelve down to zero, three items being impossible to extract in any deployment and visible only in the victim's own record, and the chain columns of the course's twenty-two lessons being gathered into a single table.

The previous lesson counted an item reaching the endpoint but never asked what that item was.
Most of the time, all the defense has in hand is that item: no source, no documentation, no
known author. Reverse engineering is the work of extracting from that item an **indicator the
defense can use**.

This lesson takes the class from the other end of the measurement. In the eight lessons so far,
the chain belonged to the attacker and the defense was the one breaking it. Here the chain
**belongs to the defense**: acquiring the sample, doing static extraction, doing dynamic
observation, converting the indicators into a rule, and deploying the rule to the fleet. The
breaking side is the countermeasures. Throughout the measurement, no real sample, real tool, or
real analysis step is written; the binary in this lesson is **generated by itself**, and the
scan is actually run.

## Three Types of Indicator, One Type of Gap

The items that can be extracted from a binary are not all one type. Items of **string** and
**symbol** type exist as text inside the binary; a scan gives them up directly. Items of
**behavior** type are not written into the binary; they are only seen once the item is run. A
fourth type cannot be extracted by any means at all: the item does not carry that information, it
gets it from the **victim's own environment**.

This fourth type is this lesson's actual point. No matter how detailed an analysis report is, it
cannot say what the item's address on the target network is, what a derived session key is, or
which organization was targeted. Those items are only seen in the **victim's own record**.

**TA33:** the indicator inventory is fictional; `defense` is the item's value for a detection
rule. **TA34:** the binary is actually generated; the items carrying text are written into
regions, the rest of the region is filled with bytes from the generator. **TA35:** the string
scan collects printable byte sequences of four and longer. **TA36:** the defense's analysis
chain is five steps, and each countermeasure breaks one step.

```js
// tm/binary.mjs — a fictional binary item and the indicators inside it (model). No real
// malware, no real sample, and no real analysis tool; the binary is generated here.
export const INDICATOR = [
  { name: "config-key",        type: "string", text: "measurement.setting.threshold", defense: 2 },
  { name: "component-label",   type: "string", text: "counter-collector-1", defense: 1 },
  { name: "send-interval",     type: "string", text: "send-interval=900", defense: 2 },
  { name: "filename-pattern",  type: "string", text: "temp-measurement-data", defense: 3 },
  { name: "retry-limit",       type: "string", text: "retry-limit-threshold", defense: 1 },
  { name: "build-label",       type: "string", text: "build-2026-04-11", defense: 1 },
  { name: "version-label",     type: "string", text: "settings-signature-v3", defense: 1 },
  { name: "func-connect",      type: "symbol", text: "connect", defense: 2 },
  { name: "func-collectData",  type: "symbol", text: "collectData", defense: 2 },
  { name: "func-writeLog",     type: "symbol", text: "writeLog", defense: 1 },
  { name: "func-timer",        type: "symbol", text: "setTimer", defense: 2 },
  { name: "func-readConfig",   type: "symbol", text: "readConfig", defense: 1 },
  { name: "startup-order",           type: "behavior", defense: 3 },
  { name: "file-write-pattern",      type: "behavior", defense: 3 },
  { name: "timing-interval",         type: "behavior", defense: 2 },
  { name: "restart-response",        type: "behavior", defense: 2 },
  { name: "resource-usage-profile",  type: "behavior", defense: 1 },
  { name: "target-network-address",  type: "unextractable", defense: 3 },
  { name: "session-key",             type: "unextractable", defense: 3 },
  { name: "victim-identity",         type: "unextractable", defense: 2 },
];

function generator(seed) {
  let s = seed >>> 0;
  return () => (s = (s * 1664525 + 1013904223) >>> 0) & 255;
}

export function generateBinary({ stripSymbols = false, hideStrings = false, pack = false } = {}) {
  const r = generator(20260804);
  const byte = [];
  const place = (m) => { for (const c of m) byte.push(c.charCodeAt(0)); byte.push(0); };
  const fill = (n) => { for (let i = 0; i < n; i++) byte.push(r() | 128); };  // unprintable

  fill(24);                                                 // header region
  for (const g of INDICATOR.filter((x) => x.type === "symbol")) {
    if (stripSymbols || pack) fill(g.text.length + 1); else place(g.text);
  }
  fill(32);                                                 // code region
  for (const g of INDICATOR.filter((x) => x.type === "string")) {
    if (pack) fill(g.text.length + 1);
    else if (hideStrings) { for (const c of g.text) byte.push(c.charCodeAt(0) ^ 90); byte.push(0); }
    else place(g.text);
  }
  fill(24);
  return Uint8Array.from(byte);
}

export function scanStrings(b, minLen = 4) {
  const found = [];
  let s = "";
  for (const x of b) {
    if (x >= 32 && x <= 126) s += String.fromCharCode(x);
    else { if (s.length >= minLen) found.push(s); s = ""; }
  }
  if (s.length >= minLen) found.push(s);
  return found;
}

export const write = (g, ...s) => console.log(s.map((v, i) =>
  (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join(""));
```

```js
// tm/measure.mjs — how many indicators come out of the binary, how many the countermeasures
// hide, which item cannot come out at any deployment. The string scan is actually run.
import { INDICATOR, generateBinary, scanStrings, write } from "./binary.mjs";

const TYPE = ["string", "symbol", "behavior", "unextractable"];
const value = (k) => k.reduce((t, g) => t + g.defense, 0);

console.log(`${INDICATOR.length} fictional indicators, total defense value ${value(INDICATOR)}\n`);

const A = [-14, 9, 12, 22];
console.log("1. indicator types");
write(A, "type", "count", "defense", "extraction path");
const PATH = { string: "string scan", symbol: "string scan",
  behavior: "dynamic observation", unextractable: "none" };
for (const t of TYPE) {
  const k = INDICATOR.filter((g) => g.type === t);
  write(A, t, k.length, value(k), PATH[t]);
}

const extracted = (option) => {
  const found = scanStrings(generateBinary(option));
  return INDICATOR.filter((g) => g.text && found.includes(g.text));
};

const DEPLOYMENT = [
  ["no countermeasure", {}],
  ["symbol stripping", { stripSymbols: true }],
  ["string hiding", { hideStrings: true }],
  ["symbol stripping + string hiding", { stripSymbols: true, hideStrings: true }],
  ["packing", { pack: true }],
];
const B = [-36, 9, 11, 11, 17];
console.log("\n2. what the string scan extracts (real run)");
write(B, "deployment", "count", "defense", "hidden", "remaining share");
const raw = extracted({});
for (const [name, s] of DEPLOYMENT) {
  const c = extracted(s);
  write(B, name, c.length, value(c), raw.length - c.length,
    `${((value(c) / value(INDICATOR)) * 100).toFixed(1)}%`);
}
console.log(`the binary is ${generateBinary({}).length} bytes (same across all deployments; only ` +
  `the content changes). Of the ${raw.length} items extracted from the raw binary, ` +
  `${raw.filter((g) => g.type === "string").length} are strings, ` +
  `${raw.filter((g) => g.type === "symbol").length} are symbols.`);

const STEP = ["sample-acquisition", "static-extraction", "dynamic-observation",
  "rule-conversion", "fleet-deployment"];
const COUNTERMEASURE = [
  { name: "symbol-stripping", breaks: 2, hides: ["symbol"] },
  { name: "string-hiding", breaks: 2, hides: ["string"] },
  { name: "packing", breaks: 2, hides: ["string", "symbol"] },
  { name: "debugger-detection", breaks: 3, hides: ["behavior"] },
  { name: "conditional-execution", breaks: 3, hides: ["behavior"] },
];
const C = [-24, 10, 20, 10, 12];
console.log("\n3. countermeasure-step mapping");
write(C, "countermeasure", "breaks", "hides type", "count", "defense");
for (const o of COUNTERMEASURE) {
  const k = INDICATOR.filter((g) => o.hides.includes(g.type));
  write(C, o.name, `step ${o.breaks}`, o.hides.join("+"), k.length, value(k));
}
console.log(`${STEP.length}-step analysis chain: ${STEP.join(" -> ")}`);

const D = [-34, 10, 12, 17];
console.log("\n4. remaining indicators (countermeasures cumulative)");
write(D, "deployment", "count", "defense", "remaining share");
const remaining = (types) => INDICATOR.filter((g) => !types.includes(g.type) && g.type !== "unextractable");
for (const [name, t] of [["none", []], ["static hiding", ["string", "symbol"]],
  ["dynamic hiding", ["behavior"]], ["both together", ["string", "symbol", "behavior"]]]) {
  const k = remaining(t);
  write(D, name, k.length, value(k), `${((value(k) / value(INDICATOR)) * 100).toFixed(1)}%`);
}
const cant = INDICATOR.filter((g) => g.type === "unextractable");
console.log(`items unextractable in any deployment: ${cant.length}, defense value ` +
  `${value(cant)} (${((value(cant) / value(INDICATOR)) * 100).toFixed(1)}%); ` +
  `these items are only seen in the victim's own record`);
```

```
20 fictional indicators, total defense value 38

1. indicator types
type              count     defense       extraction path
string                7          11           string scan
symbol                5           8           string scan
behavior              5          11   dynamic observation
unextractable         3           8                  none

2. what the string scan extracts (real run)
deployment                              count    defense     hidden  remaining share
no countermeasure                          12         19          0            50.0%
symbol stripping                            7         11          5            28.9%
string hiding                               5          8          7            21.1%
symbol stripping + string hiding            0          0         12             0.0%
packing                                     0          0         12             0.0%
the binary is 280 bytes (same across all deployments; only the content changes). Of the 12 items extracted from the raw binary, 7 are strings, 5 are symbols.

3. countermeasure-step mapping
countermeasure              breaks          hides type     count     defense
symbol-stripping            step 2              symbol         5           8
string-hiding               step 2              string         7          11
packing                     step 2       string+symbol        12          19
debugger-detection          step 3            behavior         5          11
conditional-execution       step 3            behavior         5          11
5-step analysis chain: sample-acquisition -> static-extraction -> dynamic-observation -> rule-conversion -> fleet-deployment

4. remaining indicators (countermeasures cumulative)
deployment                             count     defense  remaining share
none                                      17          30            78.9%
static hiding                              5          11            28.9%
dynamic hiding                            12          19            50.0%
both together                              0           0             0.0%
items unextractable in any deployment: 3, defense value 8 (21.1%); these items are only seen in the victim's own record
```

## What the Scan Extracts

The second output is a real run: printable byte sequences are collected in the generated
280-byte binary and matched against the inventory. **12 items** come out of the raw binary — 7
strings, 5 symbols — and their defense value is 19, **half** of the inventory's total value.

Countermeasures bring this number down step by step. Symbol stripping brings 12 down to 7,
string hiding brings it to 5. When the two are applied together, or when a single packing step
is used, the scan gives **zero items**. When the number drops to zero, the binary is still 280
bytes and still does exactly the same job; the only thing that changes is whether a static scan
can read anything out of it.

This is the measured form of the most common misconception in analysis reports: **a scan coming
back empty does not show the item is harmless — it shows that scan did not work on that item.**
The same binary, the same behavior, a different presentation.

## Which Step Each Countermeasure Breaks

The third output maps the five countermeasures to the defense's analysis chain. Three of them
break the **second step** — static extraction. Two of them break the **third step** — dynamic
observation.

None of them break the first step. Acquiring the sample is the link countermeasures cannot reach:
if the item has reached the endpoint, it is in the defense's hands. The fourth and fifth steps —
rule conversion and fleet deployment — are also outside the countermeasures' scope; their speed
depends on the defense's own operation.

A conclusion fitting this course's measure follows from this. The defense's analysis chain has
**two breakable links**, and both are in the middle. The start and end of the chain are links
the attacker cannot reach. The choice of countermeasure looks at which step the defense has less
backup for: static extraction's backup is dynamic observation; dynamic observation has no
backup.

## The Unextractable Item

The fourth output counts the remaining indicators. With no countermeasure at all, 17 items and a
defense value of 30 are extractable. With static hiding applied, 5 items and a value of 11
remain; with dynamic hiding applied, 12 items and a value of 19. With both applied together,
**nothing at all** comes out of the binary itself.

The last line is this lesson's answer. Three items — target network address, session key, and
victim identity — cannot be extracted in any deployment, and their defense values total 8,
**21.1%** of the inventory. These items never exist in the binary at all; the item gets them
from the environment it runs in.

The conclusion is this: reverse engineering gives the defense a set of indicators, but **never
all of them**, and against countermeasures, the part it gives can drop all the way to zero. The
victim's own records are, for this reason, not an alternative to analysis but its complement:
the last three items exist only there, and two of the five highest-defense-value items are in
that set.

## Summary

- The items that can be extracted from a binary are four types: string and symbol via static
  scan, behavior seen only at runtime, and a fourth type that cannot be extracted by any means.
- A string scan on the 280-byte fictional binary extracted 12 items (7 strings, 5 symbols),
  defense value 19 — half the inventory's total value.
- Symbol stripping brought the extracted item count down to 7, string hiding to 5; either
  applying both together or packing alone brought it to zero. The binary is the same size and
  the same function; only readability changes.
- Three of the five countermeasures break the analysis chain's second step, two break its third
  step. The first, fourth, and fifth steps are links countermeasures cannot reach.
- Three items cannot be extracted in any deployment; their defense value is 8, 21.1% of the
  inventory. These items are only seen in the victim's own record, and they are a complement to
  analysis, not an alternative to it.

## Course Wrap-Up

Across twenty-two lessons, a single question was asked: at which link can this chain be broken,
and what does breaking it late cost. The table below gathers each lesson's three columns in
order; every number is read from its own lesson.

| Lesson | Chain step | Breakable link | Cost of late breaking |
|---|---|---|---|
| Threat Actors | 8 steps | 8 thresholds, 1 level passes | 64 items |
| Advanced Persistent Threat | 8 steps, 330 days | 5 monitored, 2 alert | 48 items |
| Attack Lifecycle | 8 steps | 5 controls, 3 empty steps | 20.14 items |
| Adversary Behavior Classifications | 8 steps, 10,800 chains | 72 chains unbreakable | 1.39 items average |
| Diamond Model | 8 steps, 6 links | infrastructure at step 8 | 41 items |
| Known and Unknown Threats | 8 controls | 7 in known, 5 in unknown | 19.69 items |
| Fundamentals of Social Engineering | 6 steps | 4 monitored, earliest at 3rd | 9 items |
| Phishing Types | 5 steps | earliest at step 2 | 11 items |
| Other Channels | 5 steps | 4 in email, 0 in voice call | 11 items |
| Physical Techniques | 3 chains | first step unmonitored | 7, 5, 7 items |
| Domain Spoofing | registration chain | step 1 if monitoring exists | 6 items gain |
| Watering Hole Attacks | 6 steps | 3rd in 3 sources, 6th in 2 pages | 12 items |
| Awareness Programs | 5 links | notification breaks the 3rd link | 5 items |
| Web Application Risks | 39 steps, 9 chains | control at 17 steps | 480 items |
| Injection Attacks | 6 steps | step 2, at 9/12 places | 480 items |
| Cross-Site Scripting | 5, 6, and 4 steps | 3 compositions do not break | 340 sessions |
| Request Forgery | 5 + 5 steps | 3rd in browser, 1st on server | 267 operations, 60 internal endpoints |
| Path Traversal | 5 steps | step 2, identifier mapping | 30 items, 41 sensitivity |
| Buffer Overflow | 5 steps | step 1, language class | 6 neighboring fields |
| Password Attacks | 6 steps | step 3, threshold's scale | 190 accounts |
| Drive-By Downloads | 6 conditions | 3rd condition, early boundary | 73 endpoints |
| Reverse Engineering Fundamentals | 5 steps, analysis | step 2, countermeasure | 8 defense value |

The table requires naming a single rule: **an attack is not a moment, it is a sequence of steps
with preconditions.** No row is a single event; every row has interlinked steps, and every step
has a condition that must hold. The defense's job, for this reason, is not to break the chain —
the chain can already be broken in many places. The defense's job is to find the **earliest
breakable link**, write down its legitimate-work cost, and count how many places that link is
actually deployed.

All twenty-two rows show this only on paper. Step sequences were modeled, controls were mapped
to steps, coverage was counted, and cost was written down — all on data structures we wrote
ourselves. Paper cannot show whether a defense actually holds; only **an authorized person
trying it** can show that. That trial's rules, how its scope is defined, where to stop, and in
what form findings get reported were never covered in this course at all. The next course —
**Security Tools and Penetration Testing** — measures exactly that.
