Add dynamic analysis lab and enhance documentation

- Introduced a new lab on dynamic analysis and debugging using GDB, covering breakpoints, memory examination, and practical CTF challenges for runtime behavior analysis.
- Updated the highlighting guide with examples for image captions and C code formatting to improve clarity and consistency.
- Added multiple images to support the new lab content, enhancing visual learning and engagement.
This commit is contained in:
Z. Cliffe Schreuders
2025-09-30 16:58:45 +01:00
parent 07bae2fe03
commit 5ccab8163c
14 changed files with 415 additions and 0 deletions

View File

@@ -617,6 +617,19 @@ For proper CSS styling, image captions should be formatted as follows:
- Use italics (`*text*`) for the caption
- Identify existing captions by looking for descriptive text that explains what the image shows (often incomplete sentences or phrases), rather than assuming any text following an image is a caption
**Example:**
```markdown
![][image-11]
#### **Examining the contents of a memory address containing an unsigned integer**
```
**After (correct):**
```markdown
![][image-11]
*Examining the contents of a memory address containing an unsigned integer*
```
### C Code Formatting
When working with C programming content, ensure all C code is properly formatted:

View File

@@ -0,0 +1,383 @@
---
title: "Dynamic Analysis and Debugging"
author: ["Thalita Vergilio", "Tom Shaw", "Z. Cliffe Schreuders"]
license: "CC BY-SA 4.0"
description: "Learn dynamic malware analysis using GDB debugger, including breakpoints, memory examination, register analysis, and CTF challenges for runtime behavior analysis."
overview: |
Dynamic analysis and debugging play a pivotal role in the field of cybersecurity and malware analysis. In this lab, you will delve into the world of dynamic malware analysis, a critical practice for understanding how malicious software behaves at runtime. You will learn how to use the GNU Debugger (GDB) to dissect and monitor a program's execution, even when you don't have access to its source code. This is particularly valuable for cybersecurity professionals and malware analysts, as it enables them to identify and analyze malicious behavior, ultimately contributing to the development of effective security measures.
Throughout this lab, you will gain a comprehensive understanding of GDB, from setting breakpoints and examining memory locations to analyzing registers and making informed decisions about program execution. You will explore practical examples, such as setting breakpoints at specific locations in the code, examining memory content, and identifying crucial information like passwords. By the end of this lab, you will have the skills necessary to conduct dynamic analysis and debug potentially malicious programs effectively, providing invaluable insights into their runtime behavior and enhancing your expertise in the realm of cybersecurity and malware analysis.
In your home directory you will find some binaries that you need to reverse engineer in order to determine the password that the program expects. Once you have found the password, run the program and enter the password to receive the flag.
tags: ["gdb", "dynamic-analysis", "debugging", "malware-analysis", "breakpoints", "memory-analysis", "ctf"]
categories: ["software_and_malware_analysis"]
lab_sheet_url: "https://docs.google.com/document/d/1D2iVBmK35JTbfHez6qhuMyXpK7o7pr1_efbd2Ry7eUg/edit?usp=sharing"
type: ["ctf-lab", "lab-sheet"]
difficulty: "intermediate"
cybok:
- ka: "MAT"
topic: "Malware Analysis"
keywords: ["analysis techniques", "analysis environments", "DYNAMIC ANALYSIS"]
---
## Introduction {#introduction}
Having learned how to perform static malware analysis using Ghidra, we now turn our attention to dynamic malware analysis, which involves executing suspicious programs in a secure and controlled environment to gather information about their runtime behaviour. As we have seen, Ghidra offers very advanced features to help us reverse engineer binary code. However, one of its known weaknesses up until release 10.0.0 (June 2021\) had always been the lack of a built-in debugger. In order to observe the behaviour of suspicious malware as it executes, we are going to learn how to use a new tool this lab: the GNU Debugger (GDB).
Please note that, for this module, we are going to use the standard standalone version of GDB. It is worth pointing out, however, that both GDB and WinDbg have recently been integrated into Ghidra, so you may wish to experiment with them outside of this lab. Whichever way you choose to work in the future, the functionality is the same, as are the commands and shortcuts you learn in this lab.
## Creating a Safe Execution Environment {#creating-a-safe-execution-environment}
Since dynamic malware analysis involves executing potentially malicious programs that could potentially harm your host system and/or network, it should only be carried out in a safe environment. For this module, we take care of this for you by providing learning challenges based on non-malicious binaries and giving you safe VMs to investigate real malware samples.
Looking ahead into your future career as a malware analyst, however, it is important that you familiarise yourself with different ways in which you can create a secure environment for dynamic malware analysis. This is covered in this labs recommended reading, and you should aim to have at least theoretical knowledge of the options available.
## Basic Dynamic Analysis {#basic-dynamic-analysis}
Typically, after performing initial static analysis of a suspicious executable, you would proceed to basic dynamic analysis, where you execute the program in a controlled environment while monitoring how it interacts with the host system and network to understand its runtime behaviour. The table below describes the different levels at which monitoring can be configured.
| Monitoring Level | Description |
| :---- | :---- |
| Processes | Monitor the processes running in the host machine to see if any new processes are created as a result of executing the program. |
| File System | Observe how the malware interacts with the file system \- are any new files created or existing files deleted/modified? |
| Registry | On Windows systems, check if new registry entries are created or existing ones modified. |
| Network | Monitor network traffic for activity generated by the program. Check if the program is listening on any open ports. |
## Advanced Dynamic Analysis {#advanced-dynamic-analysis}
Although very informative, basic dynamic analysis involves executing the program as a black box while monitoring it externally. A more powerful approach would allow you to control every step of the programs execution while monitoring its internal state in real-time. This can be achieved by using a debugger.
You may have come across high-level graphical debuggers such as the ones which come as standard in most browsers or IDEs. They allow you to set breakpoints, step through the execution of a program line by line, and even modify dynamic values on the fly. Low-level debuggers such as GDB are pretty similar and, once you become familiar with them, you will see that the functionality available is pretty much the same. Two main differences to note are:
* instead of stepping through the source code line by line, you will step through the assembly code, instruction by instruction;
* while a lot of debuggers give you a graphical user interface, GDB is terminal-based, so it will look a little different from what you are used to.
Before we run GDB for the first time, we will change some configuration settings so our programs are disassembled using the Intel notation (GDB defaults to AT\&T).
==action: Run:==
```bash
vi ~/.gdbinit
```
==action: Add the following line:==
```
set disassembly-flavor intel
```
==action: Save and close the file.==
Now we are ready to start working through our first example.
==action: Create a file called simple-if-else.c and enter the following code:==
```c
int main (void) {
int i = 5;
int j = 0;
if (i > 3) {
j = i;
} else {
return 1;
}
return 0;
}
```
This is the same program we used in a previous lab\.
==action: Compile your code using gcc:==
```bash
gcc -m32 simple-if-else.c -o simple-if-else
```
==action: Now run it in gdb:==
```bash
gdb ./simple-if-else
```
You should see the gdb prompt as in the image below:
![][image-2]
*Running a program in GDB*
Let's use GDB to disassemble the program's main function.
==action: Run:==
```bash
disassemble main
```
![][image-3]
*Main function disassembled in GDB*
### Breakpoints {#breakpoints}
You can use the break command to set a breakpoint. You can pass it a function name, a memory address, or even an offset from the function start.
==action: Let's set a breakpoint at the start of main().== You will typically start your dynamic analysis by breaking at the start of main() in order to watch the program's execution from the start.
==action: Type:==
```bash
break main
```
==action: Now run the program:==
```bash
run
```
![][image-4]
*Setting a breakpoint at the start of main()*
The program executed and stopped at the breakpoint. However, the default layout does not show you a lot of information.
==action: Change to the assembly layout:==
```bash
layout asm
```
![][image-5]
*GDB showing ASM layout*
Note how your view is now split: the top pane shows the disassembled code, with breakpoints indicated on the left, while the bottom pane shows the GDB console.
==action: Change to the registers layout:==
```bash
layout regs
```
![][image-6]
*GDB showing REGS layout*
This gives you an additional pane on top where you can monitor the general-purpose registers.
If we look at the disassembled code, we can see that there is a cmp instruction on line \<main \+ 30\>, followed by a jle on \<main \+ 34\>. Lets set a breakpoint before the jle to check the results of the cmp instruction.
==action: Type:==
```bash
break *main+34
```
Remember: when we set a breakpoint in GDB, the programs execution stops BEFORE the line is executed.
==action: To get a list of all the breakpoints you set, type:==
```bash
info break
```
![][image-7]
*GDB showing list of breakpoints*
Now that we have our second breakpoint in place, ==action: let's continue the program's execution.==
==action: Type:==
```bash
continue
```
> Tip: As a shortcut, you can simply type c. For other shortcuts and a cheat-sheet of the most commonly used commands, check the reference card: [GDB Quick Reference](https://users.ece.utexas.edu/~adnan/gdb-refcard.pdf). Alternatively (and we do recommend you do this), you can create your own.
When the execution stops at our second breakpoint, we can examine the EFLAGS register to determine whether the jump will be taken or not based on the result of the preceding cmp operation. Remember the jumps table we studied in Week 4? You can use that as a reference.
According to the table, a jle jump will be taken if ZF=1 or SF=1. ==action: You can either type:==
```bash
info registers eflags
```
or look at the EFLAGS register information on the top pane if you are using the REGS layout.
![][image-8]
*GDB showing EFLAGS register*
As we can see, only the Interrupt Enabled (IF) flag is set. This is to be expected, since we are debugging. We can deduce that the jump was not taken. Lets check if we were correct.
==action: Advance the execution by stepping exactly one instruction.==
==action: Type:==
```bash
si
```
Confirm that the jump was indeed not taken
> Tip: You will get plenty of practice with breakpoints in this week's CTF exercises. Don't forget to take notes and keep a table/list of helpful commands at hand, at least until you've memorised them.
### Examining Memory Locations {#examining-memory-locations}
Another very useful feature of GDB (and one which you will be using a lot) is being able to examine memory locations to read the data stored in them.
==action: If you are still running the previous program in GDB, stop it by typing:==
```bash
quit
```
==action: Create a file called simple-types.c and enter the following code:==
```c
#include <stdio.h>
int main(void) {
char c = 'a';
int i = 5;
float f = 1.5f;
char *name = "Rampage";
printf("%s\n", name);
return 0;
}
```
==action: Compile your code using gcc:==
```bash
gcc -m32 simple-types.c -o simple-types
```
==action: Now run it in gdb:==
```bash
gdb ./simple-types
```
==action: Add a breakpoint to the start of main().==
==action: Change the layout to ASM.==
==action: Run the program.==
Note a call to the puts() function. This is your printf() call in the original C code.
==action: Add a breakpoint on that line and continue the execution so the program stops there.==
![][image-9]
*GDB running the simple-types program*
We are now going to examine the memory locations where we stored our local variables and try to read the data in them. The first local variable that appears in the code, in line \<main+28\>, appears to be 1 byte in size. It is referenced as an offset of EBP: ebp-0x9.
==action: Type:==
```bash
x $ebp-0x9
```
> Note: The x command defaults to hexadecimal the first time you run it. Thereafter, it defaults to the last data type used. The best approach to avoid confusion is to explicitly tell the command how you would like to read your data.
> Tip: Note that you wont always know what type of data is stored in an address, so be prepared to try a few different formats.
==action: Type:==
```bash
x /c $ebp-0x9
```
We are now telling the x command to read the data as an ASCII character. The output makes more sense:
![][image-10]
*Examining the contents of a memory address containing a single character*
Following the example above, lets check what is stored in our next local variable: ebp-0x10.
==action: Type:==
```bash
x /d $ebp-0x10
```
![][image-11]
*Examining the contents of a memory address containing an unsigned integer*
The /d specifier reads the data as an unsigned decimal.
The next local variable, ebp-0x14, is of type float. For the sake of precision, we will specify the size of the data as a word (2 bytes), as well as the format.
==action: Type:==
```bash
x /fw $ebp-0x14
```
![][image-12]
*Examining the contents of a memory address containing a floating-point number*
> Action: Read the data stored in the remaining local variable using an appropriate format specifier.
> Hint: you will need to use x to read the data **as an** **address**, then use x again to read the data at the address returned **as a string**. Feel free to consult the GDB Quick Reference.
## CTF Challenges {#ctf-challenges}
Now that we have had a brief introduction to GDB, lets jump straight into this weeks CTF challenges. There are 5 challenges this week, designed to teach you different skills in GDB as you progress through them. There is often more than one way in which you can achieve the same results \- feel free to explore them.
### General tips {#general-tips}
> Flag: For all the challenges, once you have found the password, run the program again **outside of GDB** to get your flag.
> Tip: If at any point your console layout looks jumbled, with the text you type overwriting the text on the screen, hit Ctrl + L to fix it.
Remember to use:
| run | execute the program from within GDB |
| :---- | :---- |
| `si` | “step into” the next assembly instruction |
| `ni` | “step over” the next assembly instruction (dont step into functions) |
| `fin` | “step out” of the function |
| `c` | continue, or “play” |
### GdbIntro {#gdbintro}
> Hint: This is a nice guided introduction to GDB. Once you have identified the correct call to strcmp(), check the values pushed to the stack just before the function is called.
### GdbRegs {#gdbregs}
> Hint: An alternative to the instructions provided is to add a breakpoint at the end of the retval_in_rax() function and use the REGS layout to display the registers. Another option is to use "info registers" or "info registers" + the name of the register you are interested in. Remember that RAX is the 64-bit version of EAX.
### GdbSetmem {#gdbsetmem}
> Hint: Set a breakpoint on the line that does the cmp. When the code stops there, find out what values are being compared. Change one of them using the set command so the jump is not taken and the password is printed on the screen.
### GdbPractice {#gdbpractice}
> Hint: Put a breakpoint at the start of pwdFunction(). When the execution stops, examine the value in EAX. Remember to read it as a string.
### GdbParams {#gdbparams}
> Hint: Once you have identified the function you are interested in, break just before the function is called and examine its arguments (the strings pushed to the stack). The password is one of them.
## Conclusion {#conclusion}
At this point you have:
* Been introduced to dynamic malware analysis and learned the importance of performing this type of analysis in a secure environment;
* Understood the difference between basic and advanced dynamic analysis;
* Learned how to use GDB to step through a programs execution at assembly level, without having access to the source code;
* Solved practical CTF challenges and found 5 more flags\!
Well done\!
GDB is a powerful tool for performing dynamic analysis on malware for which the source code is not available. Now that you have completed this labs work, you are ready to take on more advanced debugging challenges.
[image-1]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-1.png
[image-2]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-2.png
[image-3]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-3.png
[image-4]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-4.png
[image-5]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-5.png
[image-6]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-6.png
[image-7]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-7.png
[image-8]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-8.png
[image-9]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-9.png
[image-10]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-10.png
[image-11]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-11.png
[image-12]: {{ site.baseurl }}/assets/images/software_and_malware_analysis/7_dynamic/image-12.png

View File

@@ -1395,6 +1395,25 @@ mark, .highlight-text{
font-size: 1em;
margin-right: 0.5rem;
float: left;
font-family: "Source Code Pro", Monaco, monospace !important;
}
// Add icon above c code blocks
.language-c::before {
content: ".c ";
font-size: 1em;
margin-right: 0.5rem;
float: left;
font-family: "Source Code Pro", Monaco, monospace !important;
}
// Add icon above c code blocks
.language-nasm::before {
content: "asm ";
font-size: 1em;
margin-right: 0.5rem;
float: left;
font-family: "Source Code Pro", Monaco, monospace !important;
}
.nav-link {

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB