From be2f75f86bf4392ee7abc30cd738770e3473d36d Mon Sep 17 00:00:00 2001 From: Razvan Miclius Date: Sun, 21 Apr 2024 20:39:42 +0300 Subject: [PATCH] stack/functions: Add functions lab. Added functions lab based off old lab material. Ignored throwback exercise 0. Squashed commits into one. Signed-off-by: Razvan Miclius --- .../drills/tasks/print_rev_string/README.md | 59 ++++++ .../tasks/print_rev_string/solution/Makefile | 1 + .../solution/print_reverse_string.asm | 71 ++++++++ .../tasks/print_rev_string/support/Makefile | 25 +++ .../support/print_reverse_string.asm | 32 ++++ .../tasks/print_rev_string/utils/printf32.asm | 24 +++ .../functions/drills/tasks/rot13/README.md | 23 +++ .../drills/tasks/string_print/README.md | 8 + .../tasks/string_print/solution/Makefile | 1 + .../string_print/solution/print_string.asm | 23 +++ .../tasks/string_print/support/Makefile | 25 +++ .../string_print/support/print_string.asm | 21 +++ .../tasks/string_print/utils/printf32.asm | 24 +++ .../drills/tasks/string_print_len/README.md | 20 ++ .../tasks/string_print_len/solution/Makefile | 1 + .../solution/print_string_length.asm | 35 ++++ .../tasks/string_print_len/support/Makefile | 25 +++ .../support/print_string_length.asm | 32 ++++ .../tasks/string_print_len/utils/printf32.asm | 24 +++ .../functions/drills/tasks/to_upper/README.md | 17 ++ .../drills/tasks/to_upper/solution/Makefile | 1 + .../tasks/to_upper/solution/toupper.asm | 48 +++++ .../drills/tasks/to_upper/support/Makefile | 25 +++ .../drills/tasks/to_upper/support/toupper.asm | 39 ++++ .../drills/tasks/to_upper/utils/printf32.asm | 24 +++ .../guides/disassembling_c/README.md | 136 ++++++++++++++ .../guides/disassembling_c/support/Makefile | 17 ++ .../guides/disassembling_c/support/test.c | 24 +++ .../functions/guides/hello_world/README.md | 10 + .../guides/hello_world/support/Makefile | 25 +++ .../hello_world/support/hello_world.asm | 19 ++ .../guides/hello_world/utils/printf32.asm | 22 +++ chapters/stack/functions/media/stack.svg | 4 + chapters/stack/functions/reading/README.md | 172 ++++++++++++++++++ config.yaml | 23 +++ 35 files changed, 1080 insertions(+) create mode 100644 chapters/stack/functions/drills/tasks/print_rev_string/README.md create mode 120000 chapters/stack/functions/drills/tasks/print_rev_string/solution/Makefile create mode 100644 chapters/stack/functions/drills/tasks/print_rev_string/solution/print_reverse_string.asm create mode 100644 chapters/stack/functions/drills/tasks/print_rev_string/support/Makefile create mode 100644 chapters/stack/functions/drills/tasks/print_rev_string/support/print_reverse_string.asm create mode 100644 chapters/stack/functions/drills/tasks/print_rev_string/utils/printf32.asm create mode 100644 chapters/stack/functions/drills/tasks/rot13/README.md create mode 100644 chapters/stack/functions/drills/tasks/string_print/README.md create mode 120000 chapters/stack/functions/drills/tasks/string_print/solution/Makefile create mode 100644 chapters/stack/functions/drills/tasks/string_print/solution/print_string.asm create mode 100644 chapters/stack/functions/drills/tasks/string_print/support/Makefile create mode 100644 chapters/stack/functions/drills/tasks/string_print/support/print_string.asm create mode 100644 chapters/stack/functions/drills/tasks/string_print/utils/printf32.asm create mode 100644 chapters/stack/functions/drills/tasks/string_print_len/README.md create mode 120000 chapters/stack/functions/drills/tasks/string_print_len/solution/Makefile create mode 100644 chapters/stack/functions/drills/tasks/string_print_len/solution/print_string_length.asm create mode 100644 chapters/stack/functions/drills/tasks/string_print_len/support/Makefile create mode 100644 chapters/stack/functions/drills/tasks/string_print_len/support/print_string_length.asm create mode 100644 chapters/stack/functions/drills/tasks/string_print_len/utils/printf32.asm create mode 100644 chapters/stack/functions/drills/tasks/to_upper/README.md create mode 120000 chapters/stack/functions/drills/tasks/to_upper/solution/Makefile create mode 100644 chapters/stack/functions/drills/tasks/to_upper/solution/toupper.asm create mode 100644 chapters/stack/functions/drills/tasks/to_upper/support/Makefile create mode 100644 chapters/stack/functions/drills/tasks/to_upper/support/toupper.asm create mode 100644 chapters/stack/functions/drills/tasks/to_upper/utils/printf32.asm create mode 100644 chapters/stack/functions/guides/disassembling_c/README.md create mode 100644 chapters/stack/functions/guides/disassembling_c/support/Makefile create mode 100644 chapters/stack/functions/guides/disassembling_c/support/test.c create mode 100644 chapters/stack/functions/guides/hello_world/README.md create mode 100644 chapters/stack/functions/guides/hello_world/support/Makefile create mode 100644 chapters/stack/functions/guides/hello_world/support/hello_world.asm create mode 100644 chapters/stack/functions/guides/hello_world/utils/printf32.asm create mode 100644 chapters/stack/functions/media/stack.svg create mode 100644 chapters/stack/functions/reading/README.md diff --git a/chapters/stack/functions/drills/tasks/print_rev_string/README.md b/chapters/stack/functions/drills/tasks/print_rev_string/README.md new file mode 100644 index 00000000..fabaf8cf --- /dev/null +++ b/chapters/stack/functions/drills/tasks/print_rev_string/README.md @@ -0,0 +1,59 @@ +# Displaying the Reversed String + +In the file `print_rev_string.asm`, add the `reverse_string` function so that you have a listing similar to the one below: + +```Assembly +[...] +section .text +global main + +reverse_string: + push ebp + mov ebp, esp + + mov eax, dword [ebp+8] + mov ecx, dword [ebp+12] + add eax, ecx + dec eax + mov edx, dword [ebp+16] + +copy_one_byte: + mov bl, byte [eax] + mov byte [edx], bl + dec eax + inc edx + loopnz copy_one_byte + + inc edx + mov byte [edx], 0 + + leave + ret + +main: + push ebp + mov ebp, esp +[...] +``` + +> **IMPORTANT:** When copying the `reverse_string` function into your program, remember that the function starts at the `reverse_string` label and ends at the `main` label. The `copy_one_byte` label is part of the `reverse_string` function. + +The `reverse_string` function reverses a string and has the following signature: `void reverse_string(const char *src, size_t len, char *dst);`. This means that the first `len` characters of the `src` string are reversed into the `dst` string. + +Reverse the `mystring` string into a new string and display that new string. + +> **NOTE:** To define a new string, we recommend using the following construction in the data section: +> +> ```Assembly +> store_string times 64 db 0 +> ``` +> +> This creates a string of 64 zero bytes, enough to store the reverse of the string. +> The equivalent C function call is `reverse_string(mystring, ecx, store_string);`. We assume that the length of the string is calculated and stored in the `ecx` register. +> +> You cannot directly use the value of `ecx` in its current form. After the `printf` function call for displaying the length, the value of `ecx` is lost. To retain it, you have two options: +> +> 1. Store the value of the `ecx` register on the stack beforehand (using `push ecx` before the `printf` call) and then restore it after the `printf` call (using `pop ecx`). +> 2. Store the value of the `ecx` register in a global variable, which you define in the `.data` section. +> +> You cannot use another register because there is a high chance that even that register will be modified by the `printf` call to display the length of the string. diff --git a/chapters/stack/functions/drills/tasks/print_rev_string/solution/Makefile b/chapters/stack/functions/drills/tasks/print_rev_string/solution/Makefile new file mode 120000 index 00000000..a39be4f2 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/print_rev_string/solution/Makefile @@ -0,0 +1 @@ +../support/Makefile \ No newline at end of file diff --git a/chapters/stack/functions/drills/tasks/print_rev_string/solution/print_reverse_string.asm b/chapters/stack/functions/drills/tasks/print_rev_string/solution/print_reverse_string.asm new file mode 100644 index 00000000..626b09ac --- /dev/null +++ b/chapters/stack/functions/drills/tasks/print_rev_string/solution/print_reverse_string.asm @@ -0,0 +1,71 @@ +section .data + mystring db "This is my string", 0 + print_format db "String length is %d", 10, 0 + store_string times 64 db 0 + +section .text + +extern printf +extern puts +global main + +reverse_string: + push ebp + mov ebp, esp + + mov eax, dword [ebp+8] + mov ecx, dword [ebp+12] + add eax, ecx + dec eax + mov edx, dword [ebp+16] + +copy_one_byte: + mov bl, byte [eax] + mov byte [edx], bl + dec eax + inc edx + loopnz copy_one_byte + + inc edx + mov byte [edx], 0 + + leave + ret + +main: + push ebp + mov ebp, esp + + mov eax, mystring + xor ecx, ecx +test_one_byte: + mov bl, byte [eax] + test bl, bl + jz out + inc eax + inc ecx + jmp test_one_byte + +out: + ; save ecx's value since it can be changed by printf + push ecx + + push ecx + push print_format + call printf + add esp, 8 + + pop ecx + + push store_string + push ecx + push mystring + call reverse_string + add esp, 12 + + push store_string + call puts + add esp, 4 + + leave + ret diff --git a/chapters/stack/functions/drills/tasks/print_rev_string/support/Makefile b/chapters/stack/functions/drills/tasks/print_rev_string/support/Makefile new file mode 100644 index 00000000..93ae9b77 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/print_rev_string/support/Makefile @@ -0,0 +1,25 @@ +AS = nasm +CC = gcc +RM = rm + +SRCS := $(shell find . -name "*.asm") +OBJS := $(SRCS:.asm=.o) + +UTILSDIR := ../utils/ + +ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +CFLAGS ?= -Wall +LDFLAGS ?= -m32 -no-pie + +all: print_reverse_string + +print_reverse_string_: print_reverse_string.o + $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +%.o: %.asm + $(AS) $(ASFLAGS) $< -o $@ + +.PHONY: clean + +clean: + $(RM) -r *.o print_reverse_string diff --git a/chapters/stack/functions/drills/tasks/print_rev_string/support/print_reverse_string.asm b/chapters/stack/functions/drills/tasks/print_rev_string/support/print_reverse_string.asm new file mode 100644 index 00000000..52166833 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/print_rev_string/support/print_reverse_string.asm @@ -0,0 +1,32 @@ +%include "../utils/printf32.asm" + +section .data + mystring db "This is my string", 0 + +section .text + +extern puts +extern printf +global main + +main: + push ebp + mov ebp, esp + + mov eax, mystring + xor ecx, ecx +test_one_byte: + mov bl, byte [eax] + test bl, bl + je out + inc eax + inc ecx + jmp test_one_byte + +out: + PRINTF32 `[before]: %s\n[after]: \x0`, mystring + + ; TODO: print reverse string + + leave + ret diff --git a/chapters/stack/functions/drills/tasks/print_rev_string/utils/printf32.asm b/chapters/stack/functions/drills/tasks/print_rev_string/utils/printf32.asm new file mode 100644 index 00000000..0617f3d8 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/print_rev_string/utils/printf32.asm @@ -0,0 +1,24 @@ +; SPDX-License-Identifier: BSD-3-Clause + +;;; macro to use printf with 32bit parameters: +;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` +;;; escape \n and \x0 only work with backquotes +;;; - rest of parameters MUST be 32bit +;;; - gen purpose and flags are preserved +;;; - stack is cleaned +%macro PRINTF32 1-* + pushf + pushad + jmp %%endstr +%%str: db %1 +%%endstr: +%rep %0 - 1 +%rotate -1 + push dword %1 +%endrep + push %%str + call printf + add esp, 4*%0 + popad + popf +%endmacro diff --git a/chapters/stack/functions/drills/tasks/rot13/README.md b/chapters/stack/functions/drills/tasks/rot13/README.md new file mode 100644 index 00000000..542961bc --- /dev/null +++ b/chapters/stack/functions/drills/tasks/rot13/README.md @@ -0,0 +1,23 @@ +# Bonus: Rot13 + +Create and use a function that performs [rot13](https://rot13.com/) translation of a string. + +# Bonus: Rot13++ + +Implement `rot13` on an array of strings: the strings are contiguous in memory separated by the string terminator (`NULL`-byte, `0`). For example, `ana\0are\0mere\0` is an array of three strings. + +Apply `rot13` to alphabetical characters and replace the string terminator with a space (`' '`, blank, character `32`, or `0x20`). Thus, the initial string `ana\0are\0mere\0` will translate to `nan ner zrer`. + +> **NOTE:** To define the array of strings containing the string terminator, use a construction like: +> +> ```Assembly +> mystring db "ana", 0, "are", 0, "mere", 0 +> ``` + +> **NOTE:** You will need to know when to stop traversing the array of strings. The simplest way is to define a length variable in the `.data` section, like so: +> +> ```Assembly +> len dd 10 +> ``` +> +> where you either store the total length of the string (from the beginning to the last `NULL` byte) or the number of strings in the array. diff --git a/chapters/stack/functions/drills/tasks/string_print/README.md b/chapters/stack/functions/drills/tasks/string_print/README.md new file mode 100644 index 00000000..f4f9f8d7 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print/README.md @@ -0,0 +1,8 @@ +# Displaying a String + +To display a string, we can use the internal macro `PRINTF32`. Alternatively, we can use a function such as `puts`. In the file `print_string.asm`, displaying a string using the `PRINTF32` macro is implemented. + +Following the example of the `hello_world.asm` file, implement string display using `puts` as well. + + +If you're having difficulties solving this exercise, take a peek at [hello_world.asm](../../../guides/hello_world/). diff --git a/chapters/stack/functions/drills/tasks/string_print/solution/Makefile b/chapters/stack/functions/drills/tasks/string_print/solution/Makefile new file mode 120000 index 00000000..a39be4f2 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print/solution/Makefile @@ -0,0 +1 @@ +../support/Makefile \ No newline at end of file diff --git a/chapters/stack/functions/drills/tasks/string_print/solution/print_string.asm b/chapters/stack/functions/drills/tasks/string_print/solution/print_string.asm new file mode 100644 index 00000000..61ed7f5f --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print/solution/print_string.asm @@ -0,0 +1,23 @@ +%include "../utils/printf32.asm" + +section .data + mystring db "This is my string", 0 + +section .text + +extern puts +extern printf +global main +main: + push ebp + mov ebp, esp + + PRINTF32 `[PRINTF32]: %s\n[PUTS]: \x0`, mystring + + + push mystring + call puts + add esp, 4 + + leave + ret diff --git a/chapters/stack/functions/drills/tasks/string_print/support/Makefile b/chapters/stack/functions/drills/tasks/string_print/support/Makefile new file mode 100644 index 00000000..ef3bbea7 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print/support/Makefile @@ -0,0 +1,25 @@ +AS = nasm +CC = gcc +RM = rm + +SRCS := $(shell find . -name "*.asm") +OBJS := $(SRCS:.asm=.o) + +UTILSDIR := ../utils/ + +ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +CFLAGS ?= -Wall +LDFLAGS ?= -m32 -no-pie + +all: print_string + +print_string: print_string.o + $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +%.o: %.asm + $(AS) $(ASFLAGS) $< -o $@ + +.PHONY: clean + +clean: + $(RM) -r *.o print_string diff --git a/chapters/stack/functions/drills/tasks/string_print/support/print_string.asm b/chapters/stack/functions/drills/tasks/string_print/support/print_string.asm new file mode 100644 index 00000000..f707ef25 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print/support/print_string.asm @@ -0,0 +1,21 @@ +%include "../utils/printf32.asm" + +section .data + mystring db "This is my string", 0 + +section .text + +extern puts +extern printf +global main +main: + push ebp + mov ebp, esp + + PRINTF32 `[PRINTF32]: %s\n[PUTS]: \x0`, mystring + + + ; TODO: call puts on string + + leave + ret diff --git a/chapters/stack/functions/drills/tasks/string_print/utils/printf32.asm b/chapters/stack/functions/drills/tasks/string_print/utils/printf32.asm new file mode 100644 index 00000000..0617f3d8 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print/utils/printf32.asm @@ -0,0 +1,24 @@ +; SPDX-License-Identifier: BSD-3-Clause + +;;; macro to use printf with 32bit parameters: +;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` +;;; escape \n and \x0 only work with backquotes +;;; - rest of parameters MUST be 32bit +;;; - gen purpose and flags are preserved +;;; - stack is cleaned +%macro PRINTF32 1-* + pushf + pushad + jmp %%endstr +%%str: db %1 +%%endstr: +%rep %0 - 1 +%rotate -1 + push dword %1 +%endrep + push %%str + call printf + add esp, 4*%0 + popad + popf +%endmacro diff --git a/chapters/stack/functions/drills/tasks/string_print_len/README.md b/chapters/stack/functions/drills/tasks/string_print_len/README.md new file mode 100644 index 00000000..8c80266d --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print_len/README.md @@ -0,0 +1,20 @@ +# Displaying the Length of a String + +The program `print_string_len.asm` displays the length of a string using the `PRINTF32` macro. The calculation of the length of the `mystring` string occurs within the program (it is already implemented). + +Implement the program to display the length of the string using the `printf` function. + +At the end, you will have the length of the string displayed twice: initially with the `PRINTF32` macro and then with the external function call `printf`. + +> **NOTE:** Consider that the `printf` call is of the form `printf("String length is %u\n", len);`. You need to construct the stack for this call. +> +> The steps to follow are: +> +> 1. Mark the symbol `printf` as external. +> 2. Define the format string `"String length is %u", 10, 0`. +> 3. Make the function call to `printf`, i.e.: +> 1. Put the two arguments on the stack: the format string and the length. +> 2. Call `printf` using `call`. +> 3. Restore the stack. +> +> The length of the string is found in the `ecx` register. diff --git a/chapters/stack/functions/drills/tasks/string_print_len/solution/Makefile b/chapters/stack/functions/drills/tasks/string_print_len/solution/Makefile new file mode 120000 index 00000000..a39be4f2 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print_len/solution/Makefile @@ -0,0 +1 @@ +../support/Makefile \ No newline at end of file diff --git a/chapters/stack/functions/drills/tasks/string_print_len/solution/print_string_length.asm b/chapters/stack/functions/drills/tasks/string_print_len/solution/print_string_length.asm new file mode 100644 index 00000000..e6e5f542 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print_len/solution/print_string_length.asm @@ -0,0 +1,35 @@ +%include "../utils/printf32.asm" + +section .data + mystring db "This is my string", 0 + print_format db "String length is %d", 10, 0 + +section .text + +extern printf +global main + +main: + push ebp + mov ebp, esp + + mov eax, mystring + xor ecx, ecx +test_one_byte: + mov bl, byte [eax] + test bl, bl + jz out + inc eax + inc ecx + jmp test_one_byte + +out: + PRINTF32 `[PRINTF32]: %d\n[printf]: \x0`, ecx + + push ecx + push print_format + call printf + add esp, 8 + + leave + ret diff --git a/chapters/stack/functions/drills/tasks/string_print_len/support/Makefile b/chapters/stack/functions/drills/tasks/string_print_len/support/Makefile new file mode 100644 index 00000000..84018a12 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print_len/support/Makefile @@ -0,0 +1,25 @@ +AS = nasm +CC = gcc +RM = rm + +SRCS := $(shell find . -name "*.asm") +OBJS := $(SRCS:.asm=.o) + +UTILSDIR := ../utils/ + +ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +CFLAGS ?= -Wall +LDFLAGS ?= -m32 -no-pie + +all: print_string_length + +print_string_length: print_string_length.o + $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +%.o: %.asm + $(AS) $(ASFLAGS) $< -o $@ + +.PHONY: clean + +clean: + $(RM) -r *.o print_string_length diff --git a/chapters/stack/functions/drills/tasks/string_print_len/support/print_string_length.asm b/chapters/stack/functions/drills/tasks/string_print_len/support/print_string_length.asm new file mode 100644 index 00000000..5a2eb65e --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print_len/support/print_string_length.asm @@ -0,0 +1,32 @@ +%include "../utils/printf32.asm" + +section .data + mystring db "This is my string", 0 + +section .text + +extern puts +extern printf +global main + +main: + push ebp + mov ebp, esp + + mov eax, mystring + xor ecx, ecx +test_one_byte: + mov bl, byte [eax] + test bl, bl + je out + inc eax + inc ecx + jmp test_one_byte + +out: + PRINTF32 `[PRINTF32]: %d\n[printf]: \x0`, ecx + + ; TODO: print string length using printf + + leave + ret diff --git a/chapters/stack/functions/drills/tasks/string_print_len/utils/printf32.asm b/chapters/stack/functions/drills/tasks/string_print_len/utils/printf32.asm new file mode 100644 index 00000000..0617f3d8 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/string_print_len/utils/printf32.asm @@ -0,0 +1,24 @@ +; SPDX-License-Identifier: BSD-3-Clause + +;;; macro to use printf with 32bit parameters: +;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` +;;; escape \n and \x0 only work with backquotes +;;; - rest of parameters MUST be 32bit +;;; - gen purpose and flags are preserved +;;; - stack is cleaned +%macro PRINTF32 1-* + pushf + pushad + jmp %%endstr +%%str: db %1 +%%endstr: +%rep %0 - 1 +%rotate -1 + push dword %1 +%endrep + push %%str + call printf + add esp, 4*%0 + popad + popf +%endmacro diff --git a/chapters/stack/functions/drills/tasks/to_upper/README.md b/chapters/stack/functions/drills/tasks/to_upper/README.md new file mode 100644 index 00000000..37c8e0bf --- /dev/null +++ b/chapters/stack/functions/drills/tasks/to_upper/README.md @@ -0,0 +1,17 @@ +# Implementing the toupper Function + +We aim to implement the `toupper` function, which converts lowercase letters to uppercase. To do this, start with the `toupper.asm` file from the lab exercises archive and complete the body of the `toupper` function. + +The string used is `mystring`, and we assume it is a valid string. This string is passed as an argument to the `toupper` function when called. + +Perform the transformation in place; there is no need for another string. + +> **NOTE_** To convert a lowercase letter to uppercase, you need to **subtract** `0x20` from its value. This is the difference between lowercase and uppercase letters; for example, `a` is `0x61`, and `A` is `0x41`. You can see this in the [ASCII manual page](http://man7.org/linux/man-pages/man7/ascii.7.html). +> +> To read or write byte by byte, use the `byte [reg]` construction as seen in the implementation of determining the length of a string in the `print_string_length.asm` file, where `[reg]` is the pointer register storing the address of the string at that point. +> +> Stop when you reach the value `0` (`NULL` byte). For checking, you can use `test` as seen in the implementation of determining the length of a string in the `print-string-length.asm` file. + +## Bonus: toupper Only for Lowercase Letters + +Implement the `toupper` function so that the transformation occurs only for lowercase characters, not uppercase letters or other types of characters. diff --git a/chapters/stack/functions/drills/tasks/to_upper/solution/Makefile b/chapters/stack/functions/drills/tasks/to_upper/solution/Makefile new file mode 120000 index 00000000..a39be4f2 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/to_upper/solution/Makefile @@ -0,0 +1 @@ +../support/Makefile \ No newline at end of file diff --git a/chapters/stack/functions/drills/tasks/to_upper/solution/toupper.asm b/chapters/stack/functions/drills/tasks/to_upper/solution/toupper.asm new file mode 100644 index 00000000..6aa1137c --- /dev/null +++ b/chapters/stack/functions/drills/tasks/to_upper/solution/toupper.asm @@ -0,0 +1,48 @@ +section .data + before_format db "before %s", 13, 10, 0 + after_format db "after %s", 13, 10, 0 + mystring db "abcdefghij", 0 + +section .text + +extern printf +global main + +toupper: + push ebp + mov ebp, esp + + mov eax, dword [ebp+8] +check_one_byte: + mov bl, byte [eax] + test bl, bl + je out + sub bl, 0x20 + mov byte [eax], bl + inc eax + jmp check_one_byte + +out: + leave + ret + +main: + push ebp + mov ebp, esp + + push mystring + push before_format + call printf + add esp, 8 + + push mystring + call toupper + add esp, 4 + + push mystring + push after_format + call printf + add esp, 8 + + leave + ret diff --git a/chapters/stack/functions/drills/tasks/to_upper/support/Makefile b/chapters/stack/functions/drills/tasks/to_upper/support/Makefile new file mode 100644 index 00000000..5ea0d4dd --- /dev/null +++ b/chapters/stack/functions/drills/tasks/to_upper/support/Makefile @@ -0,0 +1,25 @@ +AS = nasm +CC = gcc +RM = rm + +SRCS := $(shell find . -name "*.asm") +OBJS := $(SRCS:.asm=.o) + +UTILSDIR := ../utils/ + +ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +CFLAGS ?= -Wall +LDFLAGS ?= -m32 -no-pie + +all: toupper + +toupper: toupper.o + $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +%.o: %.asm + $(AS) $(ASFLAGS) $< -o $@ + +.PHONY: clean + +clean: + $(RM) -r *.o toupper diff --git a/chapters/stack/functions/drills/tasks/to_upper/support/toupper.asm b/chapters/stack/functions/drills/tasks/to_upper/support/toupper.asm new file mode 100644 index 00000000..b3ad0ecf --- /dev/null +++ b/chapters/stack/functions/drills/tasks/to_upper/support/toupper.asm @@ -0,0 +1,39 @@ +section .data + before_format db "before %s", 13, 10, 0 + after_format db "after %s", 13, 10, 0 + mystring db "abcdefghij", 0 + +section .text + +extern printf +global main + +toupper: + push ebp + mov ebp, esp + + ; TODO + + leave + ret + +main: + push ebp + mov ebp, esp + + push mystring + push before_format + call printf + add esp, 8 + + push mystring + call toupper + add esp, 4 + + push mystring + push after_format + call printf + add esp, 8 + + leave + ret diff --git a/chapters/stack/functions/drills/tasks/to_upper/utils/printf32.asm b/chapters/stack/functions/drills/tasks/to_upper/utils/printf32.asm new file mode 100644 index 00000000..0617f3d8 --- /dev/null +++ b/chapters/stack/functions/drills/tasks/to_upper/utils/printf32.asm @@ -0,0 +1,24 @@ +; SPDX-License-Identifier: BSD-3-Clause + +;;; macro to use printf with 32bit parameters: +;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` +;;; escape \n and \x0 only work with backquotes +;;; - rest of parameters MUST be 32bit +;;; - gen purpose and flags are preserved +;;; - stack is cleaned +%macro PRINTF32 1-* + pushf + pushad + jmp %%endstr +%%str: db %1 +%%endstr: +%rep %0 - 1 +%rotate -1 + push dword %1 +%endrep + push %%str + call printf + add esp, 4*%0 + popad + popf +%endmacro diff --git a/chapters/stack/functions/guides/disassembling_c/README.md b/chapters/stack/functions/guides/disassembling_c/README.md new file mode 100644 index 00000000..7db19f2b --- /dev/null +++ b/chapters/stack/functions/guides/disassembling_c/README.md @@ -0,0 +1,136 @@ +# Disassembling a C program + +As mentioned, ultimately everything ends up in assembly language (to be 100% accurate, everything ends up as machine code, which has a fairly good correspondence with assembly code). Often, we find ourselves with access only to the object code of some programs and we want to inspect how it looks. + +To observe this, let's compile a C program to its object code and then disassemble it. We'll use the `test.c` program from the lab archive. + +> **NOTE:** To compile a C/C++ source file in the command-line, follow these steps: + +1. Open a terminal. (shortcut `Ctrl+Alt+T`) +2. Navigate to the directory containing your source code. +3. Use the command + +> ```Bash +> gcc -m32 -o +> ``` +> +where `` is the name of the source file (test.c) and `` is the name of the result executable. + +If you **only** want to compile (**without** linking it), use: + +> ```Bash +> gcc -m32 -c -o +> ``` +> +where `` is the name of the source file and `` is the name of the desired output object file. + +Since we want to transform `test.c` into an object file, we'll run: + +```Bash +gcc -m32 -c -o test.o test.c +``` + +After running the above command, we should see a file named `test.o`. + +Furthermore, we can use `gcc` to transform the `C` code in `Assembly` code: + +```Bash +gcc -m32 -masm=intel -S -o test.asm test.c +``` + +After running the above command we'll have a file called `test.asm`, which we can inspect using any text editor/reader, such as cat: + +```Bash +cat test.asm +``` + +In order to disassembly the code of an object file we'll use `objdump` as follows: + +```Bash +objdump -M intel -d +``` + +where `` is the path to the object file `test.o`. + +Afterwards, you'll see an output similar to the following: + +```Bash +> objdump -M intel -d test.o + +test.o: file format elf32-i386 + + +Disassembly of section .text: + +00000000 : + 0: 55 push ebp + 1: 89 e5 mov ebp,esp + 3: e8 fc ff ff ff call 4 + 8: 05 01 00 00 00 add eax,0x1 + d: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] + 10: 8b 10 mov edx,DWORD PTR [eax] + 12: 8b 45 0c mov eax,DWORD PTR [ebp+0xc] + 15: 01 c2 add edx,eax + 17: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] + 1a: 89 10 mov DWORD PTR [eax],edx + 1c: 90 nop + 1d: 5d pop ebp + 1e: c3 ret + +0000001f : + 1f: 55 push ebp + 20: 89 e5 mov ebp,esp + 22: 53 push ebx + 23: 83 ec 14 sub esp,0x14 + 26: e8 fc ff ff ff call 27 + 2b: 05 01 00 00 00 add eax,0x1 + 30: c7 45 f4 03 00 00 00 mov DWORD PTR [ebp-0xc],0x3 + 37: 83 ec 0c sub esp,0xc + 3a: 8d 90 00 00 00 00 lea edx,[eax+0x0] + 40: 52 push edx + 41: 89 c3 mov ebx,eax + 43: e8 fc ff ff ff call 44 + 48: 83 c4 10 add esp,0x10 + 4b: 83 ec 08 sub esp,0x8 + 4e: ff 75 f4 push DWORD PTR [ebp-0xc] + 51: 8d 45 08 lea eax,[ebp+0x8] + 54: 50 push eax + 55: e8 a6 ff ff ff call 0 + 5a: 83 c4 10 add esp,0x10 + 5d: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] + 60: 8b 5d fc mov ebx,DWORD PTR [ebp-0x4] + 63: c9 leave + 64: c3 ret + +00000065
: + 65: 8d 4c 24 04 lea ecx,[esp+0x4] + 69: 83 e4 f0 and esp,0xfffffff0 + 6c: ff 71 fc push DWORD PTR [ecx-0x4] + 6f: 55 push ebp + 70: 89 e5 mov ebp,esp + 72: 53 push ebx + 73: 51 push ecx + 74: e8 fc ff ff ff call 75 + 79: 81 c3 02 00 00 00 add ebx,0x2 + 7f: 83 ec 0c sub esp,0xc + 82: 6a 0f push 0xf + 84: e8 96 ff ff ff call 1f + 89: 83 c4 10 add esp,0x10 + 8c: 83 ec 08 sub esp,0x8 + 8f: 50 push eax + 90: 8d 83 0e 00 00 00 lea eax,[ebx+0xe] + 96: 50 push eax + 97: e8 fc ff ff ff call 98 + 9c: 83 c4 10 add esp,0x10 + 9f: b8 00 00 00 00 mov eax,0x0 + a4: 8d 65 f8 lea esp,[ebp-0x8] + a7: 59 pop ecx + a8: 5b pop ebx + a9: 5d pop ebp + aa: 8d 61 fc lea esp,[ecx-0x4] + ad: c3 ret +``` + +There are many other utilities that allow disassembly of object modules, most of them with a graphical interface and offering debugging support. `objdump` is a simple utility that can be quickly used from the command line. + +It's interesting to observe, both in the `test.asm` file and in its disassembly, the way a function call is made, which we'll discuss further. diff --git a/chapters/stack/functions/guides/disassembling_c/support/Makefile b/chapters/stack/functions/guides/disassembling_c/support/Makefile new file mode 100644 index 00000000..ecec6af7 --- /dev/null +++ b/chapters/stack/functions/guides/disassembling_c/support/Makefile @@ -0,0 +1,17 @@ +CC = gcc +RM = rm + +UTILSDIR := ..utils/ + +CFLAGS ?= -m32 -g -Wall -Wextra -Werror -fno-pic -masm=intel +LDFLAGS ?= -m32 -no-pie + +all: test + +test: test.c + $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +.PHONY: clean + +clean: + $(RM) test diff --git a/chapters/stack/functions/guides/disassembling_c/support/test.c b/chapters/stack/functions/guides/disassembling_c/support/test.c new file mode 100644 index 00000000..4fb88f2a --- /dev/null +++ b/chapters/stack/functions/guides/disassembling_c/support/test.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include + +static void second_func(int *p, int a) +{ + *p += a; +} + +static int first_func(int a) +{ + int b = 3; + + puts("Hello, World!"); + second_func(&a, b); + + return a; +} + +int main(void) +{ + printf("Value returned is: %d\n", first_func(15)); + return 0; +} diff --git a/chapters/stack/functions/guides/hello_world/README.md b/chapters/stack/functions/guides/hello_world/README.md new file mode 100644 index 00000000..c6ef5edd --- /dev/null +++ b/chapters/stack/functions/guides/hello_world/README.md @@ -0,0 +1,10 @@ +# Hello, world! + +Open the `hello_world.asm` file, assemble it, and run it. Notice the display of the message *Hello, world!* + +Note that: + +- The `hello_world.asm` program uses the `puts` function call (an external function of the current module) to perform the display. For this, it puts the argument on the stack and calls the function. +- The `msg` variable in the `hello-world.asm` program contains the byte `10`. This symbolizes the *line-feed* character (`\n`), used to add a new line on Linux. + +Ending with `\n` is generally useful for displaying strings. The `puts` function automatically adds a new line after the displayed string, but this must be explicitly added when using the `printf` function. diff --git a/chapters/stack/functions/guides/hello_world/support/Makefile b/chapters/stack/functions/guides/hello_world/support/Makefile new file mode 100644 index 00000000..d2027071 --- /dev/null +++ b/chapters/stack/functions/guides/hello_world/support/Makefile @@ -0,0 +1,25 @@ +AS = nasm +CC = gcc +RM = rm + +SRCS := $(shell find . -name "*.asm") +OBJS := $(SRCS:.asm=.o) + +UTILSDIR := ..utils/ + +ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)" +CFLAGS ?= -Wall +LDFLAGS ?= -m32 -no-pie + +all: hello-world + +hello-world: hello-world.o + $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +%.o: %.asm + $(AS) $(ASFLAGS) $< -o $@ + +.PHONY: clean + +clean: + $(RM) -r *.o hello-world diff --git a/chapters/stack/functions/guides/hello_world/support/hello_world.asm b/chapters/stack/functions/guides/hello_world/support/hello_world.asm new file mode 100644 index 00000000..d3c9f270 --- /dev/null +++ b/chapters/stack/functions/guides/hello_world/support/hello_world.asm @@ -0,0 +1,19 @@ +%include "../utils/printf32.asm" + +section .data + msg db 'Hello, world!', 0 + +section .text + +extern puts +global main +main: + push ebp ; since main is a function, it has to adhere to the same + mov ebp, esp ; convention + + push msg + call puts + add esp, 4 + + leave + ret diff --git a/chapters/stack/functions/guides/hello_world/utils/printf32.asm b/chapters/stack/functions/guides/hello_world/utils/printf32.asm new file mode 100644 index 00000000..61a930ed --- /dev/null +++ b/chapters/stack/functions/guides/hello_world/utils/printf32.asm @@ -0,0 +1,22 @@ +;;; macro to use printf with 32bit parameters: +;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0` +;;; escape \n and \x0 only work with backquotes +;;; - rest of parameters MUST be 32bit +;;; - gen purpose and flags are preserved +;;; - stack is cleaned +%macro PRINTF32 1-* + pushf + pushad + jmp %%endstr +%%str: db %1 +%%endstr: +%rep %0 - 1 +%rotate -1 + push dword %1 +%endrep + push %%str + call printf + add esp, 4*%0 + popad + popf +%endmacro diff --git a/chapters/stack/functions/media/stack.svg b/chapters/stack/functions/media/stack.svg new file mode 100644 index 00000000..0a4d6aaa --- /dev/null +++ b/chapters/stack/functions/media/stack.svg @@ -0,0 +1,4 @@ + + + +
Local variable 1
Local variable 2

argn-1

argn-2
...
arg1
Return Address
arg0
Caller
Caller Base Pointer
Local variable 3
Local variable 1
Local variable 2
Callee
EBP + 4
EBP
EBP - 4
ESP
Higher address (eg. 0xf8)
Lower address (eg. 0xa8)
Caller's Caller BP
Old EBP
\ No newline at end of file diff --git a/chapters/stack/functions/reading/README.md b/chapters/stack/functions/reading/README.md new file mode 100644 index 00000000..1bc42009 --- /dev/null +++ b/chapters/stack/functions/reading/README.md @@ -0,0 +1,172 @@ +# Functions + +In this lab, we'll discuss how function calls are performed. We'll see how we can use the `call` and `ret` instructions to make function calls and how we use the stack to pass function parameters. + +The lab follows a "learn by doing" format, with practical parts alternating between tutorial sections, which guide you step by step, and exercises to be solved. + +## Passing Parameters + +When it comes to calling a function with parameters, there are two major options for placing them: + +1. **Register Passing** - this method intuitively involves passing parameters through registers. + + **Advantages** + + - It is very easy to use when the number of parameters is small. + - It is very fast since parameters are immediately accessible from registers. + + **Disadvantages** + + - Because there is a limited number of registers, the number of parameters for a function becomes limited. + - It's very likely that some registers are used inside the called function, and it becomes necessary to temporarily save registers on the stack before the function call. Thus, the second advantage listed disappears because accessing the stack involves working with memory, meaning increased latency. + +2. **Stack Passing** - this method involves pushing all parameters onto the stack. + + **Advantages** + + - A large number of parameters can be passed. + + **Disadvantages** + + - It's slow because it involves memory access. + - More complicated in terms of parameter access. + +> **NOTE:** For **32-bit** architectures, the stack passing method is used, while for **64-bit** architectures, the register passing method is used. We will use the convention for 32-bit architecture. + +## Function Call + +When we call a function, the steps are as follows: + +- We put the arguments on the stack, pushing them in the reverse order in which they are sent as function arguments. +- We call `call`. +- We restore the stack at the end of the call. + +### Stack Operation + +As we know, stack operations fall into two types: + +- `push val` where the value `val` is placed on the stack +- `pop reg/mem` where what is on the top of the stack is placed into a register or memory area + +When we `push`, we say that the stack **grows** (elements are added). For reasons that will be better explained later, the stack pointer (indicated by the `esp` register in 32-bit mode) decreases in value when the stack grows (on `push`). However, this contradiction in naming comes from the fact that the stack is typically represented vertically, with smaller values at the top and larger values at the bottom. + +Similarly, when we `pop`, we say that the stack **shrinks** (elements are removed). Now the stack pointer (indicated by the `esp` register in 32-bit mode) increases in value. + +A summary of this is explained very well [here](https://en.wikibooks.org/wiki/X86_Disassembly/The_Stack). + +For example, if we have the function `foo` with the following signature (in C language): + +```C +int foo(int a, int b, int c); +``` + +The call to this function will look like this: + +```Assembly +mov ecx, [c] ; take the value of parameter c from a memory location +mov ebx, [b] +mov eax, [a] + +push ecx ; put parameters in reverse order, starting with c +push ebx ; then b +push eax ; then a +call foo ; call the function +add esp, 12 ; restore the stack +``` + +## Caller and Callee +When we call a function, we say that the calling function (the context that calls) is the **caller**, while the called function is the **callee**. In the previous paragraph, we discussed how things look at the caller level (how we build the stack). + +Now let's see what happens at the callee level. Until the `call` instruction, the stack contains the function's parameters. The `call` can be roughly equated to the following sequence: + +```Assembly +push eip +jmp function_name +``` + +That is, even the `call` uses the stack and saves the address of the next instruction, the one after the `call`, also known as the **return address**. This is necessary for the callee to know where to return to in the caller. + +In the callee, at its beginning (called preamble), the frame pointer is saved (in the i386 architecture, this is the `ebp` register), with the frame pointer then referring to the current function stack frame. This is crucial for accessing parameters and local variables via an offset from the frame pointer. + +Although not mandatory, saving the frame pointer helps in debugging and is used in most cases. For these reasons, any function call will generally have a preamble: + +```Assembly +push ebp +mov ebp, esp +``` + +These modifications take place in the callee. Therefore, it is the responsibility of the callee to restore the stack to its old value. Hence, it is customary to have an epilogue that restores the stack to its initial state; this epilogue is: + +```Assembly +leave +``` + +After this instruction, the stack is as it was at the beginning of the function (immediately after the call). + +To conclude the function, it is necessary for the execution to return and continue from the instruction following the `call` that started the function. This involves influencing the `eip` register and putting back the value that was saved on the stack initially by the `call` instruction. This is achieved using the instruction: + +```Assembly +ret +``` + +which is roughly equivalent to the instruction: + +```Assembly +pop eip +``` + +For example, the definition and body of the function foo, which calculates the sum of 3 numbers, would look like this: + +```Assembly + +foo: + push ebp + mov ebp, esp + + mov eax, [ebp + 8] + mov ebx, [ebp + 12] + mov ecx, [ebp + 16] + + add eax, ebx + add eax, ecx + + leave + ret +``` + +### Remarks: + +1. A function is defined by a label. + +2. After the function's preamble, the stack looks as follows: + + ![stack.svg](../media/stack.svg) + +3. Note that during the execution of the function, what does not change is the position of the frame pointer. This is the reason for its name: it points to the current function's frame. Therefore, it is common to access a function's parameters through the frame pointer. Assuming a 32-bit system and processor word-sized parameters (32 bits, 4 bytes), we will have: + + - the first argument is found at address `ebp+8` + - the second argument is found at address `ebp+12` + - the third argument is found at address `ebp+16` + - etc. + + This is why, to get the parameters of the foo function in the eax, ebx, ecx registers, we use the constructions: + +```Assembly + mov eax, dword [ebp+8] ; first argument in eax + mov ebx, dword [ebp+12] ; second argument in ebx + mov ecx, dword [ebp+16] ; third argument in ecx +``` + +4. The return value of a function is placed in registers (generally in eax). + - If the return value is **8 bits**, the function's result is placed in `al`. + - If the return value is **16 bits**, the function's result is placed in `ax`. + - If the return value is **32 bits**, the function's result is placed in `eax`. + - If the return value is **64 bits**, the result is placed in the `edx` and `eax` registers. The most significant 32 bits are placed in `edx`, and the rest in the `eax` register. + + *Additionally, in some cases, a memory address can be returned to the stack/heap (eg. malloc), or other memory areas, which refer to the desired object after the function call.* + +5. A function uses the same hardware registers; therefore, when exiting the function, the values of the registers are no longer the same. To avoid this situation, some/all registers can be saved on the stack. + + +> **NOTE:** Since assembly languages offer more opportunities, there is a need for calling conventions in x86. The difference between them may consist of the parameter order, how the parameters are passed to the function, which registers need to be preserved by the callee or whether the caller or callee handles stack preparation. More details can be found [here](https://en.wikipedia.org/wiki/X86_calling_conventions) or [here](https://levelup.gitconnected.com/x86-calling-conventions-a34812afe097) if Wikipedia is too mainstream for you. +> For us, the registers `eax`, `ecx`, `edx` are considered **clobbered** (or volatile), and the callee can do whatever it wants to them. On the other hand, the callee has to ensure that `ebx` exits the function with the same value it has entered with. diff --git a/config.yaml b/config.yaml index 863fbf94..76890373 100644 --- a/config.yaml +++ b/config.yaml @@ -233,6 +233,29 @@ docusaurus: - Find Substring/: find-substring/ - Mul Arrays/: mul-arrays/ - Courses/: courses/ + - Lab 9 - Functions: + - Functions: + path: chapters/stack/functions/ + extra: + - media/ + subsections: + - Reading/: reading/ + - Guides: + path: guides/ + subsections: + - Hello World/: hello_world/ + - Disassemblying a C program/: disassembling_c/ + - Drills: + path: drills/ + subsections: + - Tasks: + path: tasks/ + subsections: + - Printing a String/: string_print/ + - Printing the length of a String/: string_print_len/ + - Printing a String in Reverse/: print_rev_string/ + - To Upper/: to_upper/ + - Rot13/: rot13/ # static_assets: # - template-chapter-template-topic: /build/make_assets/chapters/template-chapter/template-topic/slides/_site config_meta: