Assembly Series Part 1 – Preparation Work

Add the author’s WeChat to join the discussion group:

Assembly Series Part 1 - Preparation Work

Introduction

The previously mentioned “Spear and Shield: The Eternal Cyber Warfare Series” is no longer available, as it may overlap with my work. Recently, I have been learning assembly language, and one of the results of my studies is the article “C2 Implant Assembly Implementation Process and Detection Countermeasures Research.” Since most assembly tutorials on the market are obscure and difficult to understand, I believe it is necessary to write a series of articles from a beginner’s perspective to learn assembly language, which can help everyone and reinforce my own knowledge. This series will start from scratch to learn assembly language through a Windows x64 C2 implant project. Of course, I will selectively omit certain parts to prevent malicious use; I will remove critical command executions, as the goal is to learn assembly, not to write malware. I also want to avoid any legal risks. Those who know me are aware that I am quite cautious, haha. Now, let’s begin the main content.

Objectives of This Article

Understand what assembly language is, configure the development environment for assembly, and, following the traditional learning approach, run a “Hello World” program.

What is Assembly Language?

Programming languages have different levels of abstraction:

  • Python/Java (High-level languages): A single line of code can accomplish many tasks, close to human thinking.
  • C/C++: Lower level, can directly manipulate memory and pointers.
  • Assembly (Low-level language): Each instruction corresponds to a simple CPU operation.
  • Machine Code: Binary (010101…), the instructions executed directly by the CPU.

Note: Don’t come to me looking for grammatical errors; some say C/C++ is also a high-level language, but I am just abstracting for easier understanding.

In simple terms: Assembly is the human-readable language closest to machine code.

Same Task, Different Languages

Objective: Calculate 5 + 10Python (High-level language):

result = 5 + 10

C Language (Intermediate):

int result = 5 + 10;

Assembly (Low-level):

mov rax, 5      ; Move 5 into the RAX register
add rax, 10     ; RAX = RAX + 10
                ; Result is in RAX

Machine Code (Executed by the computer):

48 C7 C0 05 00 00 00    ; mov rax, 5
48 83 C0 0A             ; add rax, 10

Overview of My Project Series

C/S Architecture, Commands Issued by Server Executed by Implant:

Assembly Series Part 1 - Preparation Work

Working Principle

Server (You can write a script in Python as the server; don’t ask me why, thanks to the large model, it can be done quickly):

  1. Listen on port 4444
  2. Wait for client connection
  3. Send commands
  4. Receive results

Client (This Project):

  1. Connect to the server
  2. Receive commands
  3. Execute commands
  4. Return results
  5. Repeat…

Overall Process:

Assembly Series Part 1 - Preparation Work

Installing Development Tools

There are two essential tools, and an editor that can be installed optionally

  1. NASM: Assembler (.asm → .obj)
  2. MSVC: Linker (.obj → .exe)

1. Install NASM

NASM = Netwide Assembler (Assembler)

Download:

  • https://www.nasm.us/pub/nasm/releasebuilds/
  • Select the latest version <span>win64.zip</span>
  • For example:<span>nasm-2.16.01-win64.zip</span>

Installation:

1. Unzip to C:\nasm
2. Add to PATH:
   - Win key → Search "Environment Variables"
   - System Variables → Path → Edit
   - New → Enter C:\nasm
   - Confirm

Verification:

nasm -v
# Should display the version number

2. Install Visual Studio

Some may wonder why such a heavy tool is needed for writing assembly?

  • Provides <span>link.exe</span> linker
  • Provides Windows SDK
  • Provides C runtime library

Download:

  • https://visualstudio.microsoft.com/downloads/
  • Visual Studio 2022 Community (Free)

Installation:

1. Run the installer
2. Check "Desktop development with C++"
3. Install

Verification:

# Open x64 Native Tools Command Prompt for VS 2022
# Or run manually:
"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"

link
# Should display linker help information

Editor

You can choose any editor; if you prefer, using Notepad is fine, but I recommend using Sasm or Vscode. For example, Sasm can automate building and running, supports x86 and x64 architectures, and supports NASM, GAS, FASM, MASM. However, this tutorial uses NASM. People online generally say it is because it is closer to human syntax and cross-platform, but I think it is just because everyone is using it, and language, syntax, and assembler are just tools; there is no need to get too hung up on them. The main thing is to understand the concepts. For reverse learning, what you see in IDA and Ghidra is quite similar to this; I have included a screenshot below:

Assembly Series Part 1 - Preparation Work

1.4 First Assembly Program

Hello World

Create <span>hello.asm</span>:

global main
extern printf, ExitProcess

section .data
    msg db "Hello world!", 13, 10, 0

section .text
default rel

main:
    push rbp
    mov rbp, rsp
    sub rsp, 32

    lea rcx, [msg]
    call printf

    mov rcx, 0
    call ExitProcess

    add rsp, 32
    pop rbp
    ret

Code Explanation

For now, I will provide a simple explanation of what each part does; you can choose to read it or not. A more detailed explanation of assembly basics will be provided later, but if you’re interested, you can take a look at this:

Part 1: Declarations

global main              ; Export main function
extern printf, ExitProcess  ; Use external functions

Important Note: Differences Between Assembly and C/C++

In C/C++, using external functions requires including header files first:

#include <stdio.h>     // Must declare function prototypes before use
printf("Hello"); 

But in assembly,you do not need header files; you can directly declare using <span>extern</span>, andonly link them later by specifying the library through linker parameters <span>/defaultlib:libcmt.lib</span>; you only need to know the function name without needing to know the parameter types (but you must adhere to calling conventions when calling).

Principle:

Assembly Series Part 1 - Preparation Work

Part 2: Data Segment

section .data
    msg db "Hello from Assembly!", 13, 10, 0
    ; db = define byte (define byte)
    ; 13, 10 = \r\n (newline)
    ; 0 = string terminator

Part 3: Code Segment

section .text
default rel     ; Use relative addressing

main:
    push rbp        ; Save stack frame
    mov rbp, rsp    ; Establish new stack frame
    sub rsp, 32     ; Allocate shadow space (required by Windows x64)

    lea rcx, [msg]  ; First parameter = address of msg
    call printf     ; Call printf

    mov rcx, 0      ; Exit code = 0
    call ExitProcess

    add rsp, 32     ; Release stack space
    pop rbp         ; Restore stack frame
    ret

Compiling and Running

Manual Compilation (Understanding the Process)

# 1. Initialize MSVC environment
"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"

# 2. Assemble
nasm -f win64 hello.asm -o hello.obj

# 3. Link (this step links the implementation of external functions)
link /subsystem:console /entry:main /out:hello.exe hello.obj ^
     kernel32.lib ucrt.lib libcmt.lib legacy_stdio_definitions.lib ^
     /IGNORE:4210
# The role of the libraries:
#   kernel32.lib                 - Windows core API (ExitProcess, etc.)
#   ucrt.lib                     - Universal C Runtime (stdio low-level functions)
#   libcmt.lib                   - C runtime library (printf, etc.)
#   legacy_stdio_definitions.lib - Compatibility support for printf
# /IGNORE:4210 - Ignore CRT initialization warnings (we directly use /entry:main)
   
# 4. Run
hello.exe

Tip: Manually typing commands helps understand the compilation process. If you find it cumbersome, you can use the general compilation script in Appendix A.

You should see

Hello from Assembly!

1.5 Understanding the Compilation Process

Assembly Series Part 1 - Preparation Work

NASM: Translates assembly → machine code (external symbols are retained as references)

LINK: Assembles into a complete program, adds startup code, and resolves external symbols

Linking Process of External Functions

C/C++ Method:
#include <stdio.h>  
Checks function prototypes at compile time; the compiler knows what parameters printf needs, and links the implementation from the library.

Assembly Method:
extern printf  
Just tells the assembler "this is external"; you are responsible for passing parameters according to calling conventions (the compiler does not check), and links the implementation from libcmt.lib.

Key Points:

  • C/C++ header files = function prototype declarations (checked at compile time)
  • Assembly’s extern = symbol reference declaration (not checked, more flexible but also more dangerous)
  • The actual function implementations are in .lib files, linked in only at link time

Note: I have written about special issues like functions with the same name in different libraries in my own articles, but to avoid confusion, I won’t elaborate here; I find this WeChat editor a bit uncomfortable.

1.6 Project Compilation

View build.bat

The project’s <span>build.bat</span> is similar to the above, but:

  • Compiles multiple .asm files
  • Links all .obj files

Core Part:

nasm -f win64 asm\main.asm -o asm\main.obj
nasm -f win64 asm\util.asm -o asm\util.obj
nasm -f win64 asm\network.asm -o asm\network.obj
...

link /out:main.exe asm\main.obj asm\util.obj asm\network.obj ... ^
     /defaultlib:ws2_32.lib /defaultlib:libcmt.lib ...

Compile the Project

cd C:\Users\Administrator\Desktop\asm
.uild.bat

If successful, it will generate <span>main.exe</span>.

1.7 Common Issues

Q1: Cannot find nasm command

A: Not added to PATH; re-add the environment variable and restart the terminal.

Q2: Cannot find link.exe

A: You need to run <span>vcvars64.bat</span> to initialize the environment:

"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"

Or run it in the “x64 Native Tools Command Prompt”.

Q3: Link error: unresolved external symbol

  • Check if all .obj files are included
  • Check if the required libraries are linked (e.g., <span>ws2_32.lib</span>)

Q4: Runtime crash

  • Check if shadow space is allocated (<span>sub rsp, 32</span>)
  • Check if the stack is correctly restored (<span>add rsp, 32</span>)

Next Chapter Preview

Chapter 2: Basic Knowledge of Assembly

Before analyzing the project, let’s learn the basics:

  • What are registers?
  • The 5 most commonly used instructions
  • Calling conventions
  • Using the stack

Build a solid foundation to understand the project!

Appendix A: General Compilation Script

Design Concept

To allow a script to compile various different .asm files (some require network libraries, some require GUI libraries), I use the method of declaring dependencies in the source code; this is not a universal practice, I am just being lazy and found this method. I am not sure how others do it, so please don’t ask me~

Three Usage Modes

Mode 1: Do not write @libs (use basic libraries)

; Do not write @libs comment, automatically use basic libraries
global main
extern printf, ExitProcess
...

Link:<span>kernel32.lib ucrt.lib libcmt.lib legacy_stdio_definitions.lib</span>

Mode 2: Add extra libraries (basic libraries + extra libraries)

; @libs: ws2_32.lib
; Add network library in addition to basic libraries

global main
extern printf, WSAStartup, ExitProcess
...

Link:<span>kernel32.lib ucrt.lib libcmt.lib legacy_stdio_definitions.lib ws2_32.lib</span>

Mode 3: Use only specified libraries (use ! to disable basic libraries)

; @libs: !user32.lib kernel32.lib
; ! at the beginning means "only use these libraries, do not use basic libraries"

global main
extern MessageBoxA, ExitProcess
...

Link:<span>user32.lib kernel32.lib</span>

Mode 4: Completely do not link libraries (advanced usage)

; @libs: !
; Only ! without a library name, do not link any libraries at all

global _start
; Directly use syscall, do not call any library functions
...

Do not link any libraries

build.bat Script

Create <span>build.bat</span> (place it in the root directory of the working directory):

@echo off
setlocal enabledelayedexpansion

REM Enable ANSI color support (Windows 10+)
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do set "ESC=%%b"

REM Define color codes
set "RESET=%ESC%[0m"
set "RED=%ESC%[91m"
set "GREEN=%ESC%[92m"
set "YELLOW=%ESC%[93m"
set "BLUE=%ESC%[94m"
set "CYAN=%ESC%[96m"
set "GRAY=%ESC%[90m"

echo %CYAN%==================================================
echo General Assembly Compilation Script
echo Usage: build.bat ^<filename.asm^>
echo Example: build.bat hello.asm  (supports Tab completion)
echo.
echo Add dependency declarations at the beginning of the .asm file:
echo   ; @libs: ws2_32.lib kernel32.lib
echo ==================================================%RESET%
echo.

if "%~1"=="" (
    echo %RED%[!] Error: Missing parameter%RESET%
    echo Usage: build.bat ^<filename.asm^>
echo Example: build.bat hello.asm
    exit /b 1
)

set "ASMFILE=%~1"
set "BASENAME=%~n1"
set "VCVARS=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"

echo %YELLOW%[*] Checking source file...%RESET%
if not exist "%ASMFILE%" (
    echo %RED%[!] Error: Cannot find %ASMFILE%%RESET%
    exit /b 1
)
echo %GRAY%    Source file: %ASMFILE%%RESET%
echo.

echo %YELLOW%[*] Initializing MSVC environment...%RESET%
call "%VCVARS%" >nul 2>&1
if errorlevel 1 (
    echo %RED%[!] Error: Cannot find Visual Studio%RESET%
    echo %RED%    Please modify the VCVARS path in the script%RESET%
    exit /b 1
)
echo %GRAY%    MSVC environment loaded%RESET%
echo.

echo %YELLOW%[*] Reading library dependencies...%RESET%
set "LIBS="
for /f "tokens=2* delims=:" %%a in ('findstr /i ";@libs:" "%ASMFILE%"') do (
    set "LIBS=%%a"
    goto :libs_found
)
:libs_found

set "BASIC_LIBS=kernel32.lib ucrt.lib libcmt.lib legacy_stdio_definitions.lib"

if not "!LIBS!"=="" (
    for /f "tokens=*" %%b in ("!LIBS!") do set "LIBS=%%b"

    set "FIRST_CHAR=!LIBS:~0,1!"
    if "!FIRST_CHAR!"=="!" (
        set "CUSTOM_LIBS=!LIBS:~1!"
        for /f "tokens=*" %%c in ("!CUSTOM_LIBS!") do set "CUSTOM_LIBS=%%c"

        if "!CUSTOM_LIBS!"=="" (
            echo %BLUE%    Mode: Disable all libraries%RESET%
            set "ALL_LIBS="
        ) else (
            echo %BLUE%    Mode: Use only specified libraries%RESET%
            echo %GRAY%    Libraries: !CUSTOM_LIBS!%RESET%
            set "ALL_LIBS=!CUSTOM_LIBS!"
        )
    ) else (
        echo %BLUE%    Mode: Basic libraries + extra libraries%RESET%
        echo %GRAY%    Extra libraries: !LIBS!%RESET%
        set "ALL_LIBS=!BASIC_LIBS! !LIBS!"
    )
) else (
    echo %BLUE%    Mode: Use basic libraries%RESET%
    set "ALL_LIBS=!BASIC_LIBS!"
)
echo.

echo %YELLOW%[*] Compiling %ASMFILE%...%RESET%
nasm -f win64 "%ASMFILE%" -o "%BASENAME%.obj"
if errorlevel 1 (
    echo %RED%[!] Compilation failed%RESET%
    exit /b 1
)
echo %GREEN%    ✓ Compilation successful%RESET%
echo.

echo %YELLOW%[*] Linking %BASENAME%.obj...%RESET%
if "!ALL_LIBS!"=="" (
    link /subsystem:console /entry:main /out:"%BASENAME%.exe" "%BASENAME%.obj" ^
         /machine:X64 /IGNORE:4210 >nul 2>&1
) else (
    link /subsystem:console /entry:main /out:"%BASENAME%.exe" "%BASENAME%.obj" ^
         !ALL_LIBS! /machine:X64 /IGNORE:4210 >nul 2>&1
)
if errorlevel 1 (
    echo %RED%[!] Linking failed, detailed errors below:%RESET%
    echo.
    if "!ALL_LIBS!"=="" (
        link /subsystem:console /entry:main /out:"%BASENAME%.exe" "%BASENAME%.obj" /machine:X64 /IGNORE:4210
    ) else (
        link /subsystem:console /entry:main /out:"%BASENAME%.exe" "%BASENAME%.obj" !ALL_LIBS! /machine:X64 /IGNORE:4210
    )
    del "%BASENAME%.obj" 2>nul
    exit /b 1
)
echo %GREEN%    ✓ Linking successful%RESET%
echo.

del "%BASENAME%.obj" 2>nul

echo %GREEN%========================================
echo [+] Compilation successful!
echo [+] Generated file: %BASENAME%.exe
echo ========================================%RESET%
echo.
echo %CYAN%Run the program: %BASENAME%.exe%RESET%
echo.
echo %YELLOW%[*] Program automatic run result:%RESET%
echo %GRAY%----------------------------------------%RESET%
%BASENAME%.exe
echo %GRAY%----------------------------------------%RESET%

Cleanup Script (Optional)

Create <span>clean.bat</span>:

@echo off
echo [*] Cleaning up compilation artifacts...
del /q *.obj *.exe 2>nul
echo [+] Cleanup complete

I have deleted a lot of content that I feel is not suitable for beginners~ I will keep it for my own research. I will leave two exercises:

  1. Modify hello.asm to make the program output your name, for example:
Hello, I am Zhang San!
  1. Multi-line output, write a program to output 3 lines of text:
Welcome to learn assembly language!
This is the second line
This is the third line

3. Create a simple program and use the general compilation script in Appendix A to compile it.

This concludes the first article~ I will supplement it later. Those in the group know that I have written about 13 articles in this series~ I will release them when I have time. Everyone remember to like and follow, give me a little motivation to keep going~ If no one is watching, I will stop updating~

Leave a Comment