Malware development for authorised red-team and detection-engineering work. Use when building a custom implant, loader, or payload for an engagement, or when producing samples for defensive testing. Covers the development pipeline, capability selection, and the legal and operational constraints.
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
SSkills DirectorySkills Directory
Is that skill safe to install?
Are you the author of Malware Development Workflow?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/harezadmm-malware-development-workflow)
---
name: malware-development-workflow
description: >-
Malware development for authorised red-team and detection-engineering work. Use when building
a custom implant, loader, or payload for an engagement, or when producing samples for
defensive testing. Covers the development pipeline, capability selection, and the legal and
operational constraints.
---
# SKILL: Malware Development Workflow
> **AI LOAD INSTRUCTION**: "Malware development" in an engagement means **building tooling the
> target cannot detect and the client can remove**. This skill frames it as an engineering
> pipeline with an authorisation gate, not a technique list. The output of the pipeline is
> usually a *detection artefact* — a sample the defender can write rules against. Read §1 before
> any code is written; the authorisation question is not optional.
## 0. RELATED ROUTING
- [edr-bypass-techniques](../edr-bypass-techniques/SKILL.md) — the detection layer you are targeting
- [c2-infrastructure-and-channel-design](../c2-infrastructure-and-channel-design/SKILL.md) — the network half
- [attack-execution-atomic-tests](../attack-execution-atomic-tests/SKILL.md) — validating that detection works
- [malware-development-pipeline](../../core-subjects/malware-development-pipeline.md) — doctrine
- [malware-types](../../core-subjects/malware-types.md) — taxonomy
- [memory-forensics-volatility](../memory-forensics-volatility/SKILL.md) — how analysts pull it apart
---
## 1. THE AUTHORISATION GATE
Custom malware is the highest-liability artefact a tester produces. Before writing a line:
| Requirement | Why |
|---|---|
| Written scope naming **malware development** explicitly | a pentest authorisation does not automatically cover writing implants |
| Named target environment | determines capabilities and constraints |
| **Deployment restrictions** — lab only, or production? | determines whether anti-analysis features are even legal |
| Removal procedure agreed in advance | the client must be able to uninstall what you install |
| Samples handling — does the client want them? | a sample is itself a controlled artefact |
| Signing / attribution agreement | unsigned custom code will be flagged by their own AV; the SOC must expect it |
**The deployment question is the one that matters.** A loader developed for a lab validation is
legitimate engineering. The same loader deployed into a production network where it can
replicate, persist, or damage data crosses into a different category entirely — and in many
jurisdictions, a criminal one, regardless of the contract.
---
## 2. THE PIPELINE
```
requirements → capability selection → payload → loader → channel → validation → handover
│ │ │ │ │ │ │
what must it minimum viable the work how it how it prove it detection
do & not do capability set being done hides talks works artefacts
```
**Requirements come first, and they constrain downward.** A requirement like "must not write to
disk" eliminates half the loader design space before you start. Most failed development
projects skipped this and built capability they then could not deploy.
**The pipeline terminates in handover, not in a working implant.** The deliverable is the
*understanding* — detection artefacts, a removal procedure, and a documented gap. An implant that
works and is never handed over is a liability with no return.
---
## 3. CAPABILITY SELECTION
Build only what the objective requires. Every added capability adds detection surface and
liability.
| Capability | Adds | Detection cost |
|---|---|---|
| Execute a command | minimal | low — but the command line is logged |
| Interactive shell | session management | medium — beacon pattern |
| File transfer | bandwidth | low-medium |
| Persistence | survival across reboot | **high** — registry, service, task all monitored |
| Credential access | lateral movement | **very high** — see [credential-access-atomic-tests](../credential-access-atomic-tests/SKILL.md) |
| Lateral movement | reach | high — its own detection discipline |
| Self-propagation | reach | **unacceptable** — this is a worm, and it is out of scope in every legitimate engagement |
| Anti-analysis (VM detection, debugger detection) | analysis resistance | medium — and it blocks the client's own IR team |
| Anti-forensics (log deletion, timestomping) | cover | high — and it destroys the evidence the client needs |
**Self-propagation and anti-forensics are the two red lines.** The first turns an assessment into
an incident. The second destroys your own deliverable, because the client's responders need the
logs you deleted to understand what happened.
**Anti-VM and anti-debug features deserve suspicion.** They prevent the client's blue team from
analysing the sample you handed them — which is the point of the handover. Build them only if
the scope explicitly requires evading an automated sandbox, and document it.
---
## 4. PAYLOAD AND LOADER
Separate the two. The payload does the work; the loader gets it running undetected. They have
different lifecycles.
| Loader concern | Options |
|---|---|
| Where the payload lives before execution | encrypted in the binary, fetched from C2, in a resource section, in the registry, in an ADS |
| How it is decrypted | key derived from a host property (harder), or embedded (easier, weaker) |
| How memory is allocated | avoid private RWX — use module stomping or a legitimate-backed region |
| How execution begins | APC injection, thread creation, callback, hijack a legitimate call |
| What the call stack looks like | **the strongest modern signal** — indirect syscalls and stack spoofing exist for this |
**The payload should be replaceable without rebuilding the loader.** In a long engagement the
payload will need changes; a loader that hardcodes it forces a redeploy each time, and every
deployment is a detection opportunity.
**Host-derived keys are meaningfully better.** A key derived from hostname, domain SID, or an
installed application's data means the sample found on a sandbox does not decrypt, while the
one on the target does. This is a design choice with no runtime cost.
---
## 5. VALIDATION BEFORE DEPLOYMENT
Never deploy an unvalidated implant. Validation is local, cheap, and catches the failures that
would otherwise become incidents.
| Check | Method |
|---|---|
| Does it run at all? | local VM matching the target OS build |
| Does the loader work from a clean state? | fresh snapshot, no dev tools present |
| Does the payload survive reboot (if persistence is required)? | reboot cycle |
| Does the C2 channel connect from **inside** a similarly-restricted network? | equivalent egress policy |
| Does it get caught by the target EDR product? | the actual product, the actual version |
| Does cleanup actually remove everything? | **snapshot the VM before, restore after, diff** |
| Does it crash the host? | run the full operation and observe |
**The cleanup diff is the step people skip.** Snapshot, install, run, uninstall, restore, and
compare the filesystem/registry. Anything remaining is a persistence mechanism the client cannot
remove — an incident waiting to happen.
---
## 6. THE DETECTION ARTEFACT
The primary output for a mature client. For every capability implemented, produce what the
defender needs to catch it.
| Artefact | Content |
|---|---|
| Behavioural description | what it does, in defender's language — process, registry, file, network |
| Host indicators | file paths, registry keys, mutex names, service names |
| Network indicators | domains, URI patterns, request shape, beacon interval and jitter |
| Sample | the binary itself, hashed, if the client wants it |
| Detection opportunity | **the specific telemetry that would have caught it, and whether it was enabled** |
| Removal procedure | exact steps, verified by the cleanup diff in §5 |
**If the client's EDR caught it, that is a positive finding and should be reported as one.** A
technique that fails is a validated control, and the report should say so with the same rigour
as a gap.
---
## 7. EVIDENCE STANDARD
| Item | Why |
|---|---|
| the authorisation clause covering malware development | the artefact is otherwise unlawful |
| the capability list, with everything deliberately excluded | shows scope discipline |
| the target environment (OS build, EDR product **and version**) | validation is version-specific |
| the cleanup diff result | proves the implant is removable |
| every indicator the client needs to block and detect | the deliverable |
| whether the client's controls caught it | the finding, in either direction |
| the sample's hash and storage location | chain of custody |
---
## 8. REMEDIATION REFERENCE
1. **Attack surface reduction rules** — block the execution vectors loaders depend on (Office spawning shells, credential theft from LSASS, untrusted executables from email/web).
2. **Memory protection-change monitoring** — alert on RWX allocation in unbacked memory; removes the cheapest loader technique.
3. **Call-stack validation in the EDR** — the strongest single signal against userland-evading loaders.
4. **Application allowlisting** — a loader that cannot execute cannot load; this is the most durable control.
5. **Enable the telemetry the whole pipeline relies on** — without it, every "gap" found is really a logging gap.
6. **Retain samples and run them against your own stack** periodically — the client's detection capability decays as new techniques appear.
7. **Remove the persistence surface you do not need** — fewer mechanisms available means fewer mechanisms to detect and fewer to clean.
---
## 9. CONFIRMING THE FINDING
A sample's behaviour claim is worth exactly as much as **the byte that causes it**. This table is what
separates an observed effect from a hypothesis about what a sample "probably does".
| Step | Question | What it proves |
|---|---|---|
| 1 | Is the sample's **SHA-256** recorded, and is it the analysed bytes? | a behaviour claim without a hash is unreproducible and unsafe to share |
| 2 | Was the analysis performed in an **isolated environment with no real network**? | the safety precondition, and it bounds every network observation |
| 3 | Is the behaviour **correlated to a specific feature** (a string, a config key, a code branch)? | the difference you will use for the control |
| 4 | Was the **control run**: the same sample with that feature **stripped or patched**? | the behaviour must DISAPPEAR |
| 5 | Is the **observable** a concrete artefact (a file, a registry key, a process, a packet)? | an LLM's narration of behaviour is not an observable |
| 6 | Was the sample's **execution actually reached** - not merely unpacked or statically matched? | a string in a binary is not executed behaviour |
| 7 | Is the **network claim** bounded: was the sink real, faked, or merely referenced? | a hardcoded URL is not a C2 that answered |
**A hash, an isolated environment, a feature-to-behaviour link, and a stripped control that removes the
behaviour.** "The sample is likely a loader" is a hypothesis; the control run is the finding.
---
## 10. EXECUTION PRIMITIVES
This domain executes as a **differential on a sample's own bytes.** Every step is a procedure, and the
control pair is the sample against the same sample with the causal feature removed.
### 9.1 The hash, and the isolation, before anything executes
```bash
echo "=== 1. identify the sample BY HASH, and record the analysis environment ==="
sha256sum sample.bin | tee sample.sha256
file sample.bin
cat <<'ID'
RECORD, BEFORE EXECUTION:
the SHA-256, and the SIZE <- the identity of the analysed bytes
the FILE TYPE, and any container <- a ZIP-wrapped script is not a PE
the SOURCE, and the date obtained
the ANALYSIS ENVIRONMENT: a VM or sandbox, NO BRIDGED NETWORK, a snapshot you can revert to,
and the VM's own identifiers (a sample can detect a sandbox and behave differently)
AND THE ISOLATION CLAIM MUST BE SPECIFIC: 'no real network' means either a host-only adapter with
an INETSIM or a fake-DNS sink, or no adapter at all. STATE WHICH - it changes what a network
observation means.
ID
echo
cat <<'NET'
THE NETWORK FIXTURE DECIDES WHAT YOU CAN CLAIM:
NO ADAPTER AT ALL -> you can observe an ATTEMPT (a failed connect) but not a reply
HOST-ONLY + INETSIM -> you can observe the REQUEST and serve a FAKE response
A REAL SINK YOU OWN -> you can observe a real interaction, and you must own the sink
A HARDCODED URL IN THE BINARY PROVES INTENT, NOT CONTACT. The contact is proven by the request
ARRIVING at your sink, with the timestamp, and it is the only thing that proves it.
NET
```
**A hardcoded URL proves intent, not contact** — contact is proven by the request arriving at a sink you
own, and the isolation claim must state which network fixture was used.
### 9.2 The causal link, and the control that removes the behaviour
```bash
echo "=== 2. THE CONTROL PAIR: the same sample, the causal feature removed ==="
cat <<'PAIR'
THE FINDING IS A DIFFERENCE BETWEEN TWO RUNS OF THE SAME SAMPLE:
A. the sample AS-IS -> the behaviour OBSERVED
B. the SAME sample with its causal feature PATCHED/STRIPPED
-> the behaviour ABSENT
AND B IS WHAT PROVES CAUSATION. Without B, the behaviour could be:
- the loader or the OS itself (a shell that touched a file the sample merely read)
- the sandbox's own activity (an agent that phoned home)
- COINCIDENCE, which is very common in a noisy sandbox
HOW TO BUILD B, in order of preference:
1. PATCH THE BYTE: the config key, the URL string, the branch condition. RECORD THE OFFSET AND
THE BEFORE/AFTER BYTES, and the patched sample's OWN SHA-256.
2. FLIP AN ENVIRONMENT INPUT: a fake DNS answer, a disabled service, a missing dependency. This
is a SOFTER control, because it changes the environment as well as the sample.
3. A REFERENCE SAMPLE: a known-benign binary of similar shape. The weakest control, but it
establishes the baseline noise floor.
RUN EACH THREE TIMES. A BEHAVIOUR THAT APPEARS IN ONE OF THREE RUNS IS NOISE, INCLUDING IN B.
PAIR
echo
echo "=== 3. the unpacking and the code reachability, which most reports skip ==="
cat <<'REACH'
STATIC MATCH IS NOT EXECUTION. THE LADDER, AND STOP AT THE STEP YOU REACHED:
1. STRING PRESENT 'a URL is in the binary' -> intent, and nothing more
2. CODE PRESENT the routine that uses it exists -> capability, still not execution
3. CODE REACHED a debugger or a trace shows the routine executing
4. EFFECT OBSERVED the request left, the file was written, the key was set
5. CAUSATION PROVEN the patched sample does NOT do it <- THE FINDING
LADDER STEPS 1 AND 2 ARE THE ONES THAT GET WRITTEN UP AS FINDINGS AND ARE NOT. Say which step you
reached, and treat everything above it as a hypothesis.
AND THE UNPACKING: if the sample is packed, the BEHAVIOUR IS THE UNPACKED CODE'S. Record how you
unpacked it and the unpacked artefact's hash, because the packer's behaviour is not the payload's.
REACH
```
**The patched sample not doing it is the proof of causation** — and ladder steps 1 and 2 (string present,
code present) are the ones written up as findings that are not.
### 9.3 Safety, attribution, and the end-to-end harness
```bash
echo "=== SAFETY AND ATTRIBUTION, which bound every claim here ==="
cat <<'SAFE'
A SAMPLE CAN CHANGE THE ENVIRONMENT IT IS ANALYSED IN:
- it can DETECT the sandbox (timing, artefacts, a missing user) and behave differently
- it can PERSIST, and a non-reverted snapshot carries that into the next analysis
- it can ATTACK THE ANALYST: a weaponised document, a VM escape, a network attack on your sink
THEREFORE EVERY REPORT STATES:
- the isolation, specifically, and whether the sandbox indicators were present
- a REVERT BETWEEN RUNS, and the snapshot's identity
- that the network sink was one YOU OWN, and its address
AND ATTRIBUTION: a sample's behaviour says NOTHING about who wrote it. Do not attribute an actor
from a sample's features. Language strings, timestamps, and toolmarks are WEAK, and they are
shared by many. If the report needs attribution, that is a different discipline with different
evidence, and the sample analysis does not supply it.
SAFE
echo
echo "=== end-to-end harness ==="
python3 - <<'PY'
print("=== MALWARE ANALYSIS ACCEPTANCE CHECKLIST ===")
CHECKS = [
("the sample's SHA-256 and size are recorded, and they identify the analysed bytes",
"a behaviour claim without a hash is unreproducible and unsafe to share"),
("the file type and any container are identified",
"a ZIP-wrapped script is not a PE, and the analysis differs"),
("the ISOLATION is stated specifically, including the network fixture used",
"'no real network' means different things and bounds different claims"),
("the sandbox was REVERTED between runs, and the snapshot identity is recorded",
"a sample can persist, and a dirty snapshot poisons the next analysis"),
("the CODE REACHABILITY step reached is named, from string-present to causation-proven",
"a string in a binary is not executed behaviour"),
("the CONTROL ran: the causal feature was patched or stripped, and the behaviour DISAPPEARED",
"without the control, the behaviour may be the loader, the sandbox, or coincidence"),
("the control is recorded to the BYTE (offset, before/after, the patched sample's hash)",
"the control must itself be reproducible"),
("each run was repeated THREE times, and a one-in-three behaviour is called noise",
"sandboxes are noisy, and a single observation proves nothing"),
("a network claim is backed by a request ARRIVING at a sink you own, with a timestamp",
"a hardcoded URL proves intent, not contact"),
("if the sample was packed, the unpacking method and the unpacked artefact's hash are recorded",
"the loader's behaviour is not the payload's"),
("the network sink was one you own, and its address is stated",
"attacking a third party from an analysis is not authorised by the sample's behaviour"),
("NO ATTRIBUTION is made from the sample's features",
"strings, timestamps, and toolmarks are weak and shared by many"),
]
for n, how in CHECKS: print(" [ ] %-74s -> %s" % (n, how))
print()
print("=== THE REPORT SHAPE ===")
print(" identity : the hash, the size, the type, and the source")
print(" isolation : the environment, the network fixture, the revert, the sandbox indicators")
print(" ladder : the code-reachability step reached, and everything above it as hypothesis")
print(" control : the patched sample, its bytes, its hash, and the behaviour that disappeared")
print(" network : the request at your sink, with its timestamp; or the attempt, if no fixture")
print(" limits : the sandbox-detection risk, and no attribution")
PY
```
---
## 11. RELATED SIBLINGS - LOAD TOGETHER
- [memory-forensics-volatility](../memory-forensics-volatility/SKILL.md) - the endpoint evidence a behaviour claim is checked against
- [steganography-techniques](../steganography-techniques/SKILL.md) - the carrier a sample may deliver through
- [anti-debugging-techniques](../anti-debugging-techniques/SKILL.md) - the sample's own analysis-evasion layer
- [code-obfuscation-deobfuscation](../code-obfuscation-deobfuscation/SKILL.md) - the unpacking step before behaviour is attributable
- [data-breach-correlation-workflows](../data-breach-correlation-workflows/SKILL.md) - why a sample's features do not attribute an actor