From: A-Larsen Date: Thu, 15 Oct 2026 22:36:53 +0000 (-0400) Subject: first commit X-Git-Url: https://nyquistdev.net/git/?a=commitdiff_plain;h=18210534b850e0b7995efa7c766c3cdc59851977;p=beginning-low-level-learning.git first commit --- 18210534b850e0b7995efa7c766c3cdc59851977 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..571ee79 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +ifeq ($(PROG),function4) + +function4: function4.o circle.o rect.o + gcc -g -o function4 function4.o circle.o rect.o -no-pie + +function4.o: function4.nasm + nasm -f elf64 -g -F dwarf function4.nasm -l function4.lst + +circle.o: circle.nasm + nasm -f elf64 -g -F dwarf circle.nasm -l circle.lst + +rect.o: rect.nasm + nasm -f elf64 -g -F dwarf rect.nasm -l rect.lst + +else ifeq ($(PROG),inline_assembly) + +inline_assembly: inline_assembly.c + gcc -o inline_assembly inline_assembly.c + +else + +executatble: binary + gcc -o $(PROG) $(PROG).o -no-pie + +binary: $(PROG).nasm + nasm -f elf64 -g -F dwarf $(PROG).nasm + +endif + +clean: + rm -rf *.o *.lst + rm -rf $(shell find . -maxdepth 1 -type f -executable) + +.PHONY: clean + diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..a9266ca --- /dev/null +++ b/README.adoc @@ -0,0 +1,12 @@ +== Beginning Low Level Programming + +https://html-preview.github.io/?url=https://github.com/A-Larsen/beginning_low_level_learning/blob/main/docs/index.html[Notes] + +== building a binary + +---- +make PROG= +---- + + + diff --git a/asm_next.gdb b/asm_next.gdb new file mode 100644 index 0000000..4f7ae77 --- /dev/null +++ b/asm_next.gdb @@ -0,0 +1,6 @@ +set language asm +set disassembly-flavor intel +define asm_next +nexti +disassemble +end diff --git a/circle.nasm b/circle.nasm new file mode 100644 index 0000000..afd8e5f --- /dev/null +++ b/circle.nasm @@ -0,0 +1,34 @@ +extern pi + +global c_area +global c_circum + +section .data +section .bss +section .text + +c_area: + section .text + push rbp + mov rbp, rsp + + movsd xmm1, qword [pi] + mulsd xmm0, xmm0 ; radius in xmm0 + mulsd xmm0, xmm1 + + mov rsp, rbp + pop rbp + ret + +c_circum: + section .text + push rbp + mov rbp, rsp + + movsd xmm1, qword [pi] + addsd xmm0, xmm0 ; radius in xmm0 + mulsd xmm0, xmm1 + + mov rsp, rbp + pop rbp + ret diff --git a/circle_other.nasm b/circle_other.nasm new file mode 100644 index 0000000..0a05cfa --- /dev/null +++ b/circle_other.nasm @@ -0,0 +1,58 @@ +global main +extern printf + +section .data + radius dq 10.0 + +section .bss + +section .text + +area: +section .data + .pi dq 3.141592654 ; local to area +section .text + push rbp + mov rbp, rsp + movsd xmm0, [radius] + mulsd xmm0, [radius] + mulsd xmm0, [.pi] + leave + ret + +circum: +section .data + .pi dq 3.14 ; local to circum +section .text + push rbp + mov rbp, rsp + movsd xmm0, [radius] + addsd xmm0, [radius] + mulsd xmm0, [.pi] + leave + ret + +circle: +section .data + .fmt_area db "The area is %f",10, 0 + .fmt_circum db "The circumference if %f",10, 0 +section .text + push rbp + mov rbp, rsp + call area + mov rdi, .fmt_area + mov rax, 1 ; area in xmm0 + call printf + call circum + mov rdi, .fmt_circum + mov rax, 1 ; circumference in xmm0 + call printf + leave + ret + +main: + push rbp + mov rbp, rsp + call circle + leave + ret diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..9924424 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,5 @@ +FILENAME=*.adoc + +build: $(FILENAME) + rm -rf diag*.png + asciidoctor -r asciidoctor-diagram -b html5 $(FILENAME) diff --git a/docs/Q-and-A.adoc b/docs/Q-and-A.adoc new file mode 100644 index 0000000..f13cf15 --- /dev/null +++ b/docs/Q-and-A.adoc @@ -0,0 +1,101 @@ += Q and A +:docinfo: shared +:source-highlighter: pygments +:pygments-style: monokai +:icons: font +:stem: +:toc: left + +== Working through Question 21 +to find the number in each decimal place I could use the formula + +---- + floor(mod(num / 10 ^place); 10) +---- + +but I also need to find the numbers width +for that I could use the formula + +---- + round(log(10; num) + 1) +---- + +or + +---- + count the number of times I can divide by 10 before reaching zero +---- + +make sure to use unsigned math to floor output maybe? +might try this without tail call optimization first + +in this case I think I need to perform my own modulo +dang forgot an easy way to get remander from a division problem + +math for creating modulo + +---- + a / b = c + round(c) * b = d + a - d = answer +---- + +I can look through C's https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoa[itoa] source code and try something similar. + +[source, c] +---- + void itoa(int n, char s[]) + { + int i, sign; + + if ((sign = n) < 0) /* record sign */ + n = -n; /* make n positive */ + i = 0; + do { /* generate digits in reverse order */ + s[i++] = n % 10 + '0'; /* get next digit */ + } while ((n /= 10) > 0); /* delete it */ + if (sign < 0) + s[i++] = '-'; + s[i] = '\0'; + reverse(s); + } +---- + +== Q and A +A19:: + r13 is not cleared before it is used and could return junk values + +Q21:: + Try to rewrite `print_int` without calling `printf_uint` or copying its + code: Hint read about tail call optimization + +A21:: + look at question_print_int.nasm + +Q33:: + What is a label and does it have a size? + +A33:: + labels are address. The size is 8 bytes because pointers + +Q45:: + Decipher this selector: 0x08 + +A45:: +- https://en.wikipedia.org/wiki/X86_memory_segmentation#Protected_mode +- https://en.wikipedia.org/wiki/Segment_descriptor + +this is the A flag, and it determines if the memory was accessed + +== Other + From + https://en.wikipedia.org/wiki/X86_memory_segmentation[X86_memory_segmentation]: + + +"...The actual location of the beginning of a segment in the linear address + space can be calculated with segment×16..." +-- X86_memory_segmentation, wikipedia + +"Each segment begins at a multiple of 16 bytes, called a paragraph, from the +beginning of the linear (flat) address space..." +-- X86_memory_segmentation, wikipedia diff --git a/docs/Q-and-A.html b/docs/Q-and-A.html new file mode 100644 index 0000000..2920403 --- /dev/null +++ b/docs/Q-and-A.html @@ -0,0 +1,1028 @@ + + + + + + + +Q and A + + + + + + + + + + + + + + + + + + +
+
+

Working through Question 21

+
+
+

to find the number in each decimal place I could use the formula

+
+
+
+
 floor(mod(num / 10 ^place); 10)
+
+
+
+

but I also need to find the numbers width +for that I could use the formula

+
+
+
+
 round(log(10; num) + 1)
+
+
+
+

or

+
+
+
+
 count the number of times I can divide by 10 before reaching zero
+
+
+
+

make sure to use unsigned math to floor output maybe? +might try this without tail call optimization first

+
+
+

in this case I think I need to perform my own modulo +dang forgot an easy way to get remander from a division problem

+
+
+

math for creating modulo

+
+
+
+
 a / b = c
+ round(c) * b = d
+ a - d = answer
+
+
+
+

I can look through C’s itoa source code and try something similar.

+
+
+
+
 void itoa(int n, char s[])
+ {
+     int i, sign;
+
+     if ((sign = n) < 0)  /* record sign */
+         n = -n;          /* make n positive */
+     i = 0;
+     do {       /* generate digits in reverse order */
+         s[i++] = n % 10 + '0';   /* get next digit */
+     } while ((n /= 10) > 0);     /* delete it */
+     if (sign < 0)
+         s[i++] = '-';
+     s[i] = '\0';
+     reverse(s);
+ }
+
+
+
+
+
+

Q and A

+
+
+
+
A19
+
+

r13 is not cleared before it is used and could return junk values

+
+
Q21
+
+

Try to rewrite print_int without calling printf_uint or copying its +code: Hint read about tail call optimization

+
+
A21
+
+

look at question_print_int.nasm

+
+
Q33
+
+

What is a label and does it have a size?

+
+
A33
+
+

labels are address. The size is 8 bytes because pointers

+
+
Q45
+
+

Decipher this selector: 0x08

+
+
A45
+
+ +
+
+
+
+

this is the A flag, and it determines if the memory was accessed

+
+
+
+
+

Other

+
+
+
+
From
+https://en.wikipedia.org/wiki/X86_memory_segmentation[X86_memory_segmentation]:
+
+
+
+
+…​The actual location of the beginning of a segment in the linear address + space can be calculated with segment×16…​ +
+
+— X86_memory_segmentation
+wikipedia +
+
+
+
+Each segment begins at a multiple of 16 bytes, called a paragraph, from the +beginning of the linear (flat) address space…​ +
+
+— X86_memory_segmentation
+wikipedia +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/docs/c-disassembling.adoc b/docs/c-disassembling.adoc new file mode 100644 index 0000000..7a5303c --- /dev/null +++ b/docs/c-disassembling.adoc @@ -0,0 +1,73 @@ += C Disassembling +:docinfo: shared +:source-highlighter: pygments +:pygments-style: monokai +:icons: font +:stem: +:toc: left + +== Floating Points + +.assign_float.c +[source, c] +---- +#include +#include + +int +main(void){ + float a = 10.0f; + return 1; +} +---- + +.gdb disassemble +[source, gdb] +---- +(gdb) disass +---- + +.output +[%collapsible] +==== + 0x0000555555555129 <+0>: endbr64 + 0x000055555555512d <+4>: push rbp + 0x000055555555512e <+5>: mov rbp,rsp + 0x0000555555555131 <+8>: movss xmm0,DWORD PTR [rip+0xecb] # 0x5555555560 + 04 + 0x0000555555555139 <+16>: movss DWORD PTR [rbp-0x4],xmm0 + => 0x000055555555513e <+21>: mov eax,0x1 + 0x0000555555555143 <+26>: pop rbp + 0x0000555555555144 <+27>: ret +==== + +`push rbp`,`mov rbp, rsp` is the function prologue for the stack frame. +`pop rbp` is the function epilogue for the stack frame. In `mov eax, 0x1` eax is +the return value for the function which we are setting to one. `xmm0` is the +floating point register that has the value of `a` in single percision mode. + +.gdb print xmm0 register +[source, gdb] +---- +(gdb) p $xmm0 +---- + +.output +[%collapsible] +==== + $2 = { + v8_bfloat16 = {0, 10, 0, 0, 0, 0, 0, 0}, + v8_half = {0, 2.5625, 0, 0, 0, 0, 0, 0}, + v4_float = {10, 0, 0, 0}, + v2_double = {5.3982412455708344e-315, 0}, + v16_int8 = {0, 0, 32, 65, 0 }, + v8_int16 = {0, 16672, 0, 0, 0, 0, 0, 0}, + v4_int32 = {1092616192, 0, 0, 0}, + v2_int64 = {1092616192, 0}, + uint128 = 1092616192 + } +==== + +We can see `uint128` is 1092616192, which is the how the binary actually looks +in single point percision. We can also see that `v4_float` stores the value of +the number as we understand it. diff --git a/docs/c-disassembling.html b/docs/c-disassembling.html new file mode 100644 index 0000000..3feef72 --- /dev/null +++ b/docs/c-disassembling.html @@ -0,0 +1,954 @@ + + + + + + + +C Disassembling + + + + + + + + + + + + + + + + + + +
+
+

Floating Points

+
+
+
assign_float.c
+
+
#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(void){
+    float a = 10.0f;
+    return 1;
+}
+
+
+
+
gdb disassemble
+
+
(gdb) disass
+
+
+
+output +
+
+
+
   0x0000555555555129 <+0>:     endbr64
+   0x000055555555512d <+4>:     push   rbp
+   0x000055555555512e <+5>:     mov    rbp,rsp
+   0x0000555555555131 <+8>:     movss  xmm0,DWORD PTR [rip+0xecb]        # 0x5555555560
+04
+   0x0000555555555139 <+16>:    movss  DWORD PTR [rbp-0x4],xmm0
+=> 0x000055555555513e <+21>:    mov    eax,0x1
+   0x0000555555555143 <+26>:    pop    rbp
+   0x0000555555555144 <+27>:    ret
+
+
+
+
+
+

push rbp,mov rbp, rsp is the function prologue for the stack frame. +pop rbp is the function epilogue for the stack frame. In mov eax, 0x1 eax is +the return value for the function which we are setting to one. xmm0 is the +floating point register that has the value of a in single percision mode.

+
+
+
gdb print xmm0 register
+
+
(gdb) p $xmm0
+
+
+
+output +
+
+
+
$2 = {
+  v8_bfloat16 = {0, 10, 0, 0, 0, 0, 0, 0},
+  v8_half = {0, 2.5625, 0, 0, 0, 0, 0, 0},
+  v4_float = {10, 0, 0, 0},
+  v2_double = {5.3982412455708344e-315, 0},
+  v16_int8 = {0, 0, 32, 65, 0 <repeats 12 times>},
+  v8_int16 = {0, 16672, 0, 0, 0, 0, 0, 0},
+  v4_int32 = {1092616192, 0, 0, 0},
+  v2_int64 = {1092616192, 0},
+  uint128 = 1092616192
+}
+
+
+
+
+
+

We can see uint128 is 1092616192, which is the how the binary actually looks +in single point percision. We can also see that v4_float stores the value of +the number as we understand it.

+
+
+
+
+ + + + + \ No newline at end of file diff --git a/docs/diag-ditaa-md5-748c8d76a4b154138a91a12caf43c78a.png b/docs/diag-ditaa-md5-748c8d76a4b154138a91a12caf43c78a.png new file mode 100644 index 0000000..c289e70 Binary files /dev/null and b/docs/diag-ditaa-md5-748c8d76a4b154138a91a12caf43c78a.png differ diff --git a/docs/docinfo.html b/docs/docinfo.html new file mode 100644 index 0000000..217edb0 --- /dev/null +++ b/docs/docinfo.html @@ -0,0 +1,313 @@ + + + + + + + + + + + diff --git a/docs/docinfo.js b/docs/docinfo.js new file mode 100644 index 0000000..eb3839e --- /dev/null +++ b/docs/docinfo.js @@ -0,0 +1,97 @@ +const link_variable = "https://www.lua.org/manual/5.3/manual.html#2.1"; +const link_table = "https://www.lua.org/manual/5.3/manual.html#3.4.9"; + +function setTypeHtml(type, link) { + type.innerHTML = `<${type.outerText}>` +} + +function zebraChecklist(element) { + let f = 0; + for (let i = 0; i < element.length; ++i) { + children = element[i].children; + if (children) { + for (let i = 0; i < children.length; ++i) { + if (f % 2 == 0) { + children[i].querySelector("p").style.backgroundColor = "black"; + } + let innerChild = children[i].querySelector("ul.checklist"); + if (innerChild) { + zebraChecklist(innerChild); + } + f++; + } + } + f++; + } +} + +function hideTocChildren() { + const tocList = document.querySelector(".sectlevel1"); + + for (let i =0; i < tocList.children.length; ++i) { + let child = tocList.children[i]; + let lis = child.querySelectorAll("li"); + for (let j = 0; j < lis.length; ++j) { + lis[j].style.display = "none"; + } + } +} + +window.onload = function() { + const class_api_type = document.querySelectorAll(".api-type"); + const class_checklist = document.querySelectorAll("ul.checklist"); + const class_download = document.querySelectorAll(".download"); + const id_footer = document.querySelector("#footer"); + const tocList = document.querySelector(".sectlevel1"); + // document.querySelector(".sectlevel1").children[1].querySelectorAll("li")[0] + + class_api_type.forEach(type => { + switch(type.outerText) { + case "number" : { + setTypeHtml(type, link_variable); + break; + } + case "string" : { + setTypeHtml(type, link_variable); + break; + } + case "table" : { + setTypeHtml(type, link_table); + break; + } + } + }); + + class_download.forEach(span => { + let atag = span.querySelector("a"); + let href = atag.getAttribute("href"); + let regex_FileAtEndOfPath = /\/[\w-_]*.\w*$/; + let fileName = href.match(regex_FileAtEndOfPath)[0].slice(1); + atag.setAttribute("download", fileName); + atag.innerHTML = fileName; + }) + + zebraChecklist(class_checklist); + let footer = document.createElement("footer"); + footer.style.color = "black"; + footer.style.textAlign = "center"; + footer.style.backgroundColor = "white"; + footer.style.opacity = ".5" + footer.innerHTML = "

powered by asciidoctor

"; + document.body.appendChild(footer); + + for (let i =0; i < tocList.children.length; ++i) { + let child = tocList.children[i]; + let lis = child.querySelectorAll("li"); + for (let j = 0; j < lis.length; ++j) { + lis[j].style.display = "none"; + } + + child.addEventListener("click", event => { + hideTocChildren(); + for (let j = 0; j < lis.length; ++j) { + lis[j].style.display = "block"; + } + }) + } +} diff --git a/docs/index.adoc b/docs/index.adoc new file mode 100644 index 0000000..281d451 --- /dev/null +++ b/docs/index.adoc @@ -0,0 +1,1348 @@ += Low Level Learning +:docinfo: shared +:source-highlighter: pygments +:pygments-style: monokai +:icons: font +:stem: +:toc: left + +== Related Links +- https://html-preview.github.io/?url=https://github.com/A-Larsen/beginning_low_level_learning/blob/main/docs/c-disassembling.html[c-disassemebling] +- https://html-preview.github.io/?url=https://github.com/A-Larsen/beginning_low_level_learning/blob/main/docs/Q-and-A.html[Q and A] + +== Looking at the OpCode tables + +Ex. + + +.SAL/SHL +[%header cols="1,1,1,1,1,4"] +|=== +| Opcode | Instruction | Op/En | 64-bit Mode | Compat/Leg Mode | Description + +| D0/4 | SAL r/m8, 1 | M1 | Valid | valid | Multiply r/m8 by 2, once + +| C1 /4 ib | SHL r/m16,imm8 | M1 | Valid | valid | Multiply r/m32 by 2, once +|=== + + +This is an example of an instruction table that you might see, In this case I +took this from the +https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html[Intel 64 and IA-32 Architectures software Developer's Manual]. + +=== Instructions +r:: + "r" stands for a general purpose register, "r/m8" mean a 8 bit register, + "r/m16" means a 16 bit register. + +m:: + A 16-, 32- or 64-bit operand in memory + +imm:: + stands for immediate value (e.g., integer constant like 42 or 1335). + +rel8:: + A relative address in the range from 128 bytes before the end of the + instruction to 127 bytes after the end of the instruction. + +rel16, rel32:: + A relative address within the same code segment as the instruction + assembled. The rel16 symbol applies to instructions with an operand-size + attribute of 16 bits; the rel32 symbol applies to instructions with an + operand-size attribute of 32 bits + +=== Opcodes +REX.W:: + Indicates the use of a REX prefix that affects operand size or instruction + semantics. The ordering of the REX prefix and other optional/mandatory + instruction perfixes are discussed in Chapter 2. Note that REX prefixes + promote legacy instructions to 64-bit behavor are not listed explicitly in + the opcode column + +/Digit:: + A digit between 0 and 7 indicates that the ModR/M byte of the instruction + uses only the r/m (register or memory) operand. The reg field contains the + digit that provides an extension to the instruction's opcode. + +/r:: + Indicates that the ModR/M byte of the instruction contains a register + operand and an r/m operand. + +cb, cw, cd, cp, co, ct:: + A 1-byte (cb), 2-byte (cw), 4-byte (cd), 6-byte (cp), 8-byte (co) or + 10-byte (ct) value following the opcode. This value is used to specify a + code offset and possibly a new value for the code segment register. + +ib, iw, id, io:: + A 1-byte (ib), 2-byte (iw), 4-byte (id) or 8-byte (io) immediate operand to + the instruction that follows the opcode, ModR/M bytes or scale-indexing + bytes. The opcode determines if the operand is a signed value. All words, + doublewords and quadwords are given with the low-order byte first. + ++rb, +rw, +rd, +ro:: + Indicates the lower 3 bits of the opcode byte is used to encode the register + operand without a modR/M byte. The instruction lists the corresponding + hexadecimal value of the opcode byte with low 3 bits as 000b. In non-64-bit + mode, a register code, from 0 through 7, is added to the hexadecimal value + of the opcode byte. In 64-bit mode, indicates the four bit field of REX.b + and opcode[2:0] field encodes the register operand of the instruction. “+ro” + is applicable only in 64-bit mode. See Table 3-1 for the codes. + ++i:: + A number used in floating-point instructions when one of the operands is + ST(i) from the FPU register stack. The number i (which can range from 0 to + 7) is added to the hexadecimal byte given at the left of the plus sign to + form a single opcode byte. + + +NOTE: Look at Intel 64 and IA-32 Architectures Software Developer's Manual page + 2839 + + +== Data types +.datatypes +[%header] +|=== +| Type | Length | Name +| db | 8 bits | Byte +| dw | 16 bits | Word +| dd | 32 bits | Double Word +| dq | 64 bits | Quadword +|=== + +== Registers + +=== R_X vs E_X vs _X vs _H vs _L +R_X:: all 64 bits +E_X:: lower 32 bits +_X:: lower 16 bits +_H:: top 4 bits of last byte +_L:: bottom 4 bits of last byte + +=== General Purpose Registers + +[%header] +|=== +| 64-bit | 32-bit | 16-bit | low 8-bit | high 8-bit | comment +| rax | eax | ax | al | ah | +| rbx | ebx | bx | bl | bh | +| rcx | ecx | cx | cl | ch | +| rdx | edx | dx | dl | dh | +| rsi | esi | si | sil | | +| rdi | edi | di | dil | | +| rbp | ebp | bp | bpl | | Base Pointer +| rsp | esp | sp | spl | | Stack Pointer +| r8 | r8d | r8w | r8b | | +| r9 | r9d | r9w | r9b | | +| r10 | r10d | r10w | r10b | | +| r11 | r11d | r11w | r11b | | +| r12 | r12d | r12w | r12b | | +| r13 | r13d | r13w | r13b | | +| r14 | r14d | r14w | r14b | | +| r15 | r15d | r15w | r15b | | +|=== + +=== Flag Registers +|=== +| Name | Symbol | Bit | Content + +| Carry | CF | 0 | Previous instruction had a carry + +| parity | PF | 2 | Last byte has even number of 1s + +| Adjust | AF | 4 | BCD operations + +| Zero | ZF | 6 | Pervious instruction resulted in zero + +| Sign | SF | 8 | Previous instruction resulted in most significant + bit equal to 1 + +| Overflow | OF | 11 | Previous instruction resulted in overflow +|=== + +=== Instruction Pointer Register (rip) +The processor keeps trakc of the next instruction to be executed by storing the +address of the next instruction in rip. + +=== xmm and ymm Registers +These registers are used for floating-point calculations and SIMD. + +== Main +One file in your assembly program needs "main" defined. This seems to be defined +in the `.text` section. + +[source, nasm] +---- +section .text +global main +main: +---- + +== Labels +- Labels look like this `:` +- Local lablels Look like this `.:` + +If there is a local label within a global label then the full label name +could loop like this `main.loop`. Like in this example: + +[source, nasm] +---- +section .text +global main +main: + .loop + ; do somethings + jnz .loop + +---- + +== Jumping / looping + +`cmp` sets the approriate flags. `jl` jumps if rax < 42. `jmp` jumps straight to +the `ex` label. + +[source, nasm] +---- + cmp rax, 42 + jl yes + mov rbx, 0 + jmp ex +yes: + mov rbx, 1 +ex: +---- + +`ja`:: (jump if above)/ `jb` (jump if below) for a jump after a comparison + of _unsigned numbers with cmp_. + +`jg`:: (Jump if greater) / `jl` (jump if less) for _signed_. + +`jae`:: (jump if above or equal), jle (jump if less or equal) and similar + +https://www.felixcloutier.com/x86/loop:loopcc[loop]:: + checks RCX/ECX/CX register as a counter. Each time the LOOP instruction is + executed, the count register is decremented, the check for 0. If the count + is 0, the loop is terminated. If the count is not zero, a near jump is + performed. + +=== https://www.felixcloutier.com/x86/jmp[Different Types of Jumps] + +Near Jump:: + A jump to an instruction within the current code segment (the segment + currently pointed to by the CS register), sometimes referred to as an + intrasegment jump + +Short Jump:: + A near jump where the jump range is limited to –128 to +127 from the current + EIP value + +Far Jump:: + A jump to an instruction located in a different segment than the current + code segment but at the same privilege level, sometimes referred to as an + intersegment jump + +Task Switch:: + A jump to an instruction located in a different task + +== Function Calls +=== Requirements + +Must start wih a function prologue and end with a function epilogue followed by +a `pop rip`. + +.function prologue +[source, nasm] +---- +push rbp +mov rbp,rsp +---- + +.function epilogue +[source, nasm] +---- +mov rsp, rbp +pop rbp +---- + +Must move to the the function label with a jmp, like so: + +[source, nasm] +---- +push rip +jmp
+---- + +The address now stored in the stack (for rip contents) is called *return* +*address*. + +The first six arguments must be rdi, rsi, rdx, rcx, r8, and r9, respectively. + +One should not invoke `ret` (`pop rip`) unless the stack is in exactly the same +state as when the function started. + +Must restore all other register that where not arguments. + +Callee-saved registers:: + must be restored by the procedure being called. + rbx, rbp, rsp, r12-r15 + +Caller-saved registers:: + Should be saved before invoking a function and restored after. All other + registers are caller-saved. + +=== Pattern of Calling a Function +- Save all caller-saved registers you want to survive function call (you can use + push for that) + +- Store arguments in the relevent register (rdi, rsi, etc). + +- If you return a value from a function, you use xmm0 for floating point values + and use rax for other values, such as integers or addresses. + +- Restore caller-saved registers stored before the function call. + +NOTE: some system calls also return values - be careful to read the docs! + +=== Function Related Instructions +`ret`:: + Denotes the function end. same as `pop rip`. + +`leave`:: + Is the the same as `mov rsp, rbp` and `pop rbp` (the epilogue). + +`call
`:: + Is the same as `push rip` and `jmp
` + +`enter 0, 0`:: + Is the same as `push rbp` and `mov rbp, rsp` (the prologue). It + is said to have poor performance. + +=== Locality +Functions can have their own sections, such as `.data`, `.bss`, and `.text`. A +period before a variable means that variable is local to that function. + +=== Function types +Branch Functions:: + Contain calls to other functions + +Leaf Functions:: + Execute some commands and then return to the parent function. + +=== Alignment +The stack has to have 16-byte alignment when you call a function. Inside a leaf +function, you can in general ignore stack from and stack alignment; it is not +necessary as long as you don't mess with the stack. Similarly, if your function +does not use SIMD instructions, you do not need to care about alignment. +Compilers have optimizing functionality, and sometimes when you look at code +generated by compilers, you will find that there was no stack from used. That +happens when the compilers noticed during optimization that a stack form is no +needed. It **IS** a good habit to always include a stack from and check the +stack alignment; it can save you a lot of trouble later. A good reaason to +include a stack frome is the fact that GDB and GDB-based debugges (such as DDD +and SASM) expect to find a stack frame. If there is no stacfk form in your code, +the debugger will behave unpredictably, such as ignoring breakpoints or jumping +over instructions. + +== Syscalls +syscall instructions implicitly uses rcx. System calls cannot accept more than +six arguments. + +All the syscalls are listed in _/usr/include/nasm/unistd.h_ (or something +similar), together with their numbers (the value to put in EAX before you call +int 80h). In ubuntu I found the file in +_/usr/include/x86_64-linux-gnu/asm/unistd_64.h_. + +You can checkout https://www.man7.org/linux/man-pages/man2/syscalls.2.html[man syscalls] on links to lookup a syscall. + +checkout this https://hackeradam.com/x86-64-linux-syscalls/[syscall table] for +linux + +== Different Addressing types + +=== Immediate +use the specified value + +ex. + +[source, nasm] +---- +mov rax, 10 +---- + +=== Register +Use the value in teh specified register + +[source, nasm] +---- +mov rax, rbx +---- + +[source, nasm] +---- +mov rax, [10]; transfers 8 bytes starting at the tenth address into rax +---- + +[source, nasm] +---- +mov r9, 10 +mov rax, [r9] ; take the address from the register +---- + +=== Direct Memory +Use the value at the specified memory address + +[source, nasm] +---- +mov rdi, 0x172 ; not 100% sure about this +---- + +=== Base-indexed with scale and discplacement +Most addressing modes are generalized by this mode. The address here is +calculated based on the following components: + +---- +Address = base + index * scale + displacement +---- + +- Base is either immediate or a register +- Scale can only be immediate equal to 1, 2, 4, or 8 +- Index is immediate or a register +- Displacement is always immediate + +[source, nasm] +---- +mov rax, [rbx + 4 * rcx + 9] +mov rax, [4 * r9] +mov rdx, [rax + rbx] +lea rax, [rbx + rbx * 4] ; rax = rbx * 5 +add r8, [9 + rbx * 8 + 7] +---- + + +== Endianness +=== Least significant vs Most significant +This is dependent on the endianness of the system. Most systems are little +endian meaning that the less significant bits are further the the right. More +signifacant bits are further to the right. + +=== Text +This instruction + +[source, gdb] +---- +mov rax, [text1] +---- + +stores `text1` into rax in little endian mode + +.gdb rax register +[source, gdb] +---- +(gdb) i r +rax 0x5452455000636261 6075995057182433889 +---- + +0x61 is a, 0x62 is b, and 0x63 is c. + +=== Change a number from little endian to big endian in C +[source, c] +---- +#include +#include +#include + +int +main(void){ + unsigned int num = 0x12345678; + printf("%x\n", htonl(num)); +} +---- + +== Pointers and Differnt Addressing Types +Pointers are 8 bytes. + +you need to specify operation size, when trying to write shomewhere a value +whose size is not specified. + +[source, nasm] +---- +section .data + test: dq -1 +section .bss +section .text + global main +main: + mov byte[test], 1 + ;mov [test], 1 ; gives an error + mov word[test], 1 + mov dword[test], 1 + mov qword[test], 1 + + mov rax, 60 + mov rdi, 0 + syscall +---- + +== Instructions +=== https://www.felixcloutier.com/x86/neg[NEG] +The https://www.felixcloutier.com/x86/neg[NEG] instruction applys two complement. It sets the CF falgs to zero if the +source operand is 0; otherwise it is set to 1. + +=== https://www.felixcloutier.com/x86/imul[IMUL] +signed multiplication + +One-operand form:: + secound operand is in AL, AX, EAX, or RAX, depending on the + size + +Two-operand form:: + With this form the destination operand (the first operand) is multiplied by + the source operand + +Three-operand form:: + with `mul a, b, c`, the multiplication of `b` and `c` is + stored in `a` + +=== https://www.felixcloutier.com/x86/mul[MUL] +unsigned multiply + +the source operand is located in either AL, AX, or EAX, dending on the size of +the operand. + +The result is stored in RDX:RAX meaning that the lower 64 bits are stored in RAX +and the upper 64 bits are stored in RDX. This is because the result of two +numbers can be larger bit size they originaly had. + +=== https://www.felixcloutier.com/x86/sub[SUB] +subtract source operand from the destination operand and stores the result in +the destination operand. + +- integer subraction; signed and unsigned + +- sets OF flags to indicate an overflow in the signed or unsigned result, + respectively. + +- The SF flag indicates the sign of the signed result + +In 64-bit mode the instruction's default operation size is 32 bits + +=== https://www.felixcloutier.com/x86/idiv[IDIV] +signed divide. Divides the value in AX, DX:AX, or EDX:EAX (dividend) by the +source operand (divisor) and stores the result in AX (AH:AL), DX:AX, or EDX:EAX +registers. + +- Overflow is indicated withthe #DE (divide error) exception rather than with + the CF flag + +The **modulo** can be found in rdx. + +WARNING: Remember to set rdx to zero every time before you use idiv or the + resulting quotient may be wrong. + + +=== https://www.felixcloutier.com/x86/lea[LEA] +[source, nasm] +---- +lea rsi, [variable] +---- + +square brackets represent *indirect addressing*. + +=== https://www.felixcloutier.com/x86/div[DIV] +Divids unsigned the value in the AX, DX:AX, EDX:EAX, or RDX:RAX registers +(dividend) by the source operand (divisor) and stores the result in AX (AH:AL), +DX:AX, EDX:EAX, or RDX:RAX register + +=== https://www.felixcloutier.com/x86/test[TEST] +Computes the bit-wise logical AND of first operand (source 1 operand) and the +second operand (source 2 operand) and sets the SF, ZF, and PF status flags +according to the result. The result is then discarded. + +If the result of AND is zero then the ZF flag is set. + +=== https://www.felixcloutier.com/x86/nop[NOP] +The x86-64 ISA includes several no-op (no operation) instructions, including +`nop`, `nop A,` (no-op with an argument), and `data16`. No-ops do pretty much +nothing, but do set the rflags. The main reason for no-ops is to optimize +instruction memory (e.g., code size, alignment). + +=== MOVSD +move a double precision floating point value + +=== MOVSS +move a single precision floating point value + +=== What is the differnce between SAR and SHR? +SAR sets or clears the most significant bit depending on the sign, While SHR +just clears the most significant bit. + + + +=== What's the difference between the CF flag and OF flag. +Carry Flag:: + bits that are carried over in *UNSIGNED* integer arithmetic + +Overflow Flag:: + bits that are carried over in *SIGNED* integer arithmetic + +== NASM and GDB +=== Get Memory Allocated On The Heap + +List memory regions mapped by the specified process +- https://www.baeldung.com/linux/find-heap-memory-running-process[find-heap-memory-running-process] + +[source, gdb] +---- +(gdb) info proc mappings +---- + +.output +[%collapsible] +==== + (gdb) info proc mappings + process 56173 + Mapped address spaces: + Start Addr End Addr Size Offset Perms objfile + 0x555555554000 0x555555555000 0x1000 0x0 r--p /usr/local/share/tst/c/tst + 0x555555555000 0x555555556000 0x1000 0x1000 r-xp /usr/local/share/tst/c/tst + 0x555555556000 0x555555557000 0x1000 0x2000 r--p /usr/local/share/tst/c/tst + 0x555555557000 0x555555558000 0x1000 0x2000 r--p /usr/local/share/tst/c/tst + 0x555555558000 0x555555559000 0x1000 0x3000 rw-p /usr/local/share/tst/c/tst + 0x555555559000 0x55555557a000 0x21000 0x0 rw-p [heap] + 0x7fffb7d74000 0x7ffff7d78000 0x40004000 0x0 rw-p + 0x7ffff7d78000 0x7ffff7da0000 0x28000 0x0 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 + 0x7ffff7da0000 0x7ffff7f35000 0x195000 0x28000 r-xp /usr/lib/x86_64-linux-gnu/libc.so.6 + 0x7ffff7f35000 0x7ffff7f8d000 0x58000 0x1bd000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 + 0x7ffff7f8d000 0x7ffff7f8e000 0x1000 0x215000 ---p /usr/lib/x86_64-linux-gnu/libc.so.6 + 0x7ffff7f8e000 0x7ffff7f92000 0x4000 0x215000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 + 0x7ffff7f92000 0x7ffff7f94000 0x2000 0x219000 rw-p /usr/lib/x86_64-linux-gnu/libc.so.6 + 0x7ffff7f94000 0x7ffff7fa1000 0xd000 0x0 rw-p + 0x7ffff7fbb000 0x7ffff7fbd000 0x2000 0x0 rw-p + 0x7ffff7fbd000 0x7ffff7fc1000 0x4000 0x0 r--p [vvar] + 0x7ffff7fc1000 0x7ffff7fc3000 0x2000 0x0 r-xp [vdso] + 0x7ffff7fc3000 0x7ffff7fc5000 0x2000 0x0 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.s + o.2 + 0x7ffff7fc5000 0x7ffff7fef000 0x2a000 0x2000 r-xp /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.s + --Type for more, q to quit, c to continue without paging-- + o.2 + 0x7ffff7fef000 0x7ffff7ffa000 0xb000 0x2c000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.s + o.2 + 0x7ffff7ffb000 0x7ffff7ffd000 0x2000 0x37000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.s + o.2 + 0x7ffff7ffd000 0x7ffff7fff000 0x2000 0x39000 rw-p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.s + o.2 + 0x7ffffffdd000 0x7ffffffff000 0x22000 0x0 rw-p [stack] + 0xffffffffff600000 0xffffffffff601000 0x1000 0x0 --xp [vsyscall] +==== + + +From the heap memory addresses we can use `find` to find a string within the heap + +[source, gdb] +---- +find 0x555555559000, 0x55555557a000, 'h', 'e', 'l', 'l', 'o', '1' +---- + +=== Get type of +[source, gdb] +---- +(gdb) ptype 0x404034 +type = int +---- + +=== Breaking at local labels +[source, gdb] +---- +b main.string_size +---- + +=== Looking at flags +To get flag values: +[source, gdb] +---- +info registers eflags +---- + +An output like this: + +[source, gdb] +---- +eflags 0x41 [ CF ZF ] +---- + +means that the carry and zero flag are set + +=== Next instruction +assembly: +[source, gdb] +---- +(gdb) ni +---- + +C: +[source, gdb] +---- +(gdb) n +---- + +=== Steping into a function +To step into an assembly function +[source, gdb] +---- +(gdb) stepi +---- + +To step into an C function +[source, gdb] +---- +(gdb) step +---- + +=== Make Printing Look Nice +[source, gdb] +---- +(gdb) set print pretty +---- + +=== Get current line +[source, gdb] +---- +(gdb) frame +---- + +=== Break at specific instruction number +[source, gdb] +---- + (gdb) b * 0x0000000000401110 +---- + +=== Flavor +to set dissasembly flavor to intel instead of AT&T: + +[source, gdb] +---- +(gdb) set disassembly-flavor intel +---- + +=== Dissassembly a function + +[source, gdb] +---- +disassemble +---- + +* **function name** - can be the name of any function (i.e main) + + +=== Finding Immediate / registers / label values +If you have + +[source, nasm] +---- +msg db "hello, world", 10, 0 +---- + +In the `.data` section and use it later with + +[source, nasm] +---- +mov rsi, msg +---- + +The output of `(gdb) dissassembly ` might look something like +this + +[source, nasm] +---- +0x000000000040111a <+10>: movabs rsi,0x404028 +---- + +You can see what is inside the memory address `0x404028` with + +[source, gdb] +---- +(gdb) x/s 0x404028 +0x404028 : "hello, world\n" +---- + +As you can see that is the address is where `msg` is located + +* **get register value** -> `(gdb) x/s $rax` +* **get register value** -> `(gdb) p $rax` +* **get label value** -> `(gdb) x/s &codes` +* **get label value** -> `(gdb) p &codes` +* **x/s** -> output string +* **x/c** -> output character +* **x/13c** -> output 13 characters +* **x/13d** -> output 13 characters in decimal representation + +NOTE: checkout `(gdb) help x` + +=== TUI +[source, gdb] +---- +(gdb) tui +---- + +=== Registers +[source, gdb] +---- +(gdb) info registers +---- + +=== Source code +[source, gdb] +---- +(gdb) list +---- + +=== Steping through Instructions +.next_asm.gdb +[source, gdb] +---- +set language asm +set disassembly-flavor intel +define asm_next +nexti +disassemble +end +---- + +[source, sh] +---- +$ gdb -x next_asm.gdb +---- + +=== Step out of function +[source, gdb] +---- +(gdb) finish +---- + + +== Segment Registers + +Logical addresses can be explicitly specified in *x86 assembly language + +all segments have a base of zero + +In protected mode, code may always modify all segment registers except CS (the +code segment selector). This is because the current privilege level (CPL) of the +processor is stored in the lower 2 bits of the CS register + +The only ways to raise the processor privilege level (and reload CS) are through +the lcall (far call) and int (interrupt) instructions. Similarly, the only ways +to lower the privilege level (and reload CS) are through lret (far return) and +iret (interrupt return) instructions. In real mode, code may also modify the CS +register by making a far jump (or using an undocumented POP CS instruction on +the 8086 or 8088).[4] Of course, in real mode, there are no privilege levels; +all programs have absolute unchecked access to all of memory and all CPU +instructions + +[source, asm] +---- +movl $42, %fs:(%eax) ; Equivalent to M[fs:eax]<-42) in RTL +---- + +or in intel syntax + +[source, nasm] +---- +mov dword [fs:eax], 42 +---- + +* What data is in each segment + - All CPU instructions are implicitly fetched from the code segment + specified by the segment selector held in the CS register. + + - Most memory references come from the data segment specified by the segment + selector held in the DS register. These may also come from the extra + segment specified by the segment selector held in the ES register, if a + segment-override prefix precedes the instruction that makes the memory + reference. Most, but not all, instructions that use DS by default will + accept an ES override prefix. + + - Processor stack references, either implicitly (e.g. push and pop + instructions) or explicitly (memory accesses using the (E)SP or (E)BP + registers) use the stack segment specified by the segment selector held in + the SS register. + + - String instructions (e.g. stos, movs), along with data segment, also use + the extra segment specified by the segment selector held in the ES + register. + + - mov is used to manipulate data, so the address is relative to the data + segment + +=== SHDR +From https://linux.die.net/man/5/elf[man elf] we can see that the `Elf32_Shdr` or `Elf32_Shdr` structs, can +contain info about `.bss`, `.data`, etc. + +=== Data segment + +==== Example 1 + +[source, nasm] +---- +global main +section .data + bNum db 123 +section .text +main: +lea rax, [bNum] +---- + +.gdb disassembly +[source, gdb] +---- + lea rax,ds:0x404028 +---- + +ds:: data segment +0x404028:: address of bNum + +== Compiling and Linking +=== Makefile +The `-f` is follow by the output format, in our case elf64, which means +Executable and Linkable Format for 64-bit. The `-g` mains that we include debug +information in a debug format specified adter the `-F` option. We use dwarf +debug format. The `-l` tells NASM to generate a _.lst_ file. Nasm will create an +object file with a _.o_ extension. That object file is used by the linker. + +DWARF:: + **D**ebug With **A**rbitrary **R**ecord **F**ormat + +STABS:: + Debug with **S**ymbol **Tab**le **S**trings. + +elf64:: + **E**xecutable and **l**inkable **F**ormat for **64**-bit + +PIE:: + **P**osition-**I**ndependant **E**xecutables + +== Modes +Real mode:: +* the most ancient, 16-bit +* Computer is booted into this mode +* Can access pretty much any memory location +* No virtual memory + +Protected:: +* commonly referred as 32-bit one +* Virtual memory +* Protection rings +* Improved segmentation + +Virtual:: +* emulate real mode inside protected + +System management mode:: +* for sleep mode, power management, etc + +Long Mode:: +* Virtual memory + +"A 386 CPU can be put back into real mode by clearing a bit in the CR0 control +register, however this is a privileged operation in order to enforce security +and robustness" +-- X86_memory_segmentation, Wikipedia + + +== Memory +The memory size has a theoretical limit of 2^64 address. + +If you look at this _.lst_ file you can see the each memory address (second +column) has 8 hexadecimal digits meaning that there are 64 bits per instruction. +This is because we are using 64 bit assembler. + +.\.lst file +[code, lst] +---- + 11 00000000 B801000000 mov rax, 1 + 12 00000005 BF01000000 mov rdi, 1 +---- + + +=== Layout + +The stack in high memory can grow; it grows in the downward directection toward +`section .bss`. The available free memory between the stack and the other +sections is called the heap. + +The memory in `section .bss` is asigned at runtime. + +[ditaa] +---- ++----------------------------+ high address +| environment variables | +| command line arguments | ++----------------------------+ +| stack | +| | +| | +| | +| | +| | +| | +| heap | ++----------------------------+ +| | +| .bss | ++----------------------------+ +| | +| .data | ++----------------------------+ +| | +| .text | ++----------------------------+ low address (executable code) +---- + +[source, gdb] +---- +readelf --symbols ./memory | tail +10 | sort -k 2 -r +---- + +.output +[%collapsible] +==== + 38: 0000000000404090 0 NOTYPE GLOBAL DEFAULT 23 _end + 23: 0000000000404071 8 OBJECT LOCAL DEFAULT 23 qvar + 22: 000000000040405d 2 OBJECT LOCAL DEFAULT 23 wvar + 21: 0000000000404059 4 OBJECT LOCAL DEFAULT 23 dvar + 20: 0000000000404058 1 OBJECT LOCAL DEFAULT 23 bvar + 43: 0000000000404058 0 OBJECT GLOBAL HIDDEN 22 __TMC_END__ + 7: 0000000000404054 1 OBJECT LOCAL DEFAULT 23 completed.0 + 41: 0000000000404051 0 NOTYPE GLOBAL DEFAULT 23 __bss_start + 32: 0000000000404051 0 NOTYPE GLOBAL DEFAULT 22 _edata + 19: 000000000040404d 1 OBJECT LOCAL DEFAULT 22 text2 + 18: 0000000000404045 8 OBJECT LOCAL DEFAULT 22 qNum2 + 17: 0000000000404041 1 OBJECT LOCAL DEFAULT 22 text1 + 16: 0000000000404039 8 OBJECT LOCAL DEFAULT 22 qNum1 + 15: 0000000000404035 4 OBJECT LOCAL DEFAULT 22 dNum + 14: 000000000040402b 2 OBJECT LOCAL DEFAULT 22 warray + 13: 0000000000404029 2 OBJECT LOCAL DEFAULT 22 wNum + 12: 0000000000404028 1 OBJECT LOCAL DEFAULT 22 bNum + 36: 0000000000404020 0 OBJECT GLOBAL HIDDEN 22 __dso_handle + 31: 0000000000404018 0 NOTYPE WEAK DEFAULT 22 data_start + 34: 0000000000404018 0 NOTYPE GLOBAL DEFAULT 22 __data_start + 29: 0000000000404000 0 OBJECT LOCAL DEFAULT 21 _GLOBAL_OFFSET_TABLE_ + 27: 0000000000403e60 0 OBJECT LOCAL DEFAULT 19 _DYNAMIC + 8: 0000000000403e58 0 OBJECT LOCAL DEFAULT 18 __do_global_dtor[...] + 10: 0000000000403e50 0 OBJECT LOCAL DEFAULT 17 __frame_dummy_in[...] + 25: 0000000000402060 0 OBJECT LOCAL DEFAULT 16 __FRAME_END__ + 28: 0000000000402004 0 NOTYPE LOCAL DEFAULT 15 __GNU_EH_FRAME_HDR + 37: 0000000000402000 4 OBJECT GLOBAL DEFAULT 14 _IO_stdin_used + 33: 0000000000401190 0 FUNC GLOBAL HIDDEN 13 _fini + 42: 0000000000401110 0 NOTYPE GLOBAL DEFAULT 12 main + 9: 0000000000401100 0 FUNC LOCAL DEFAULT 12 frame_dummy + 6: 00000000004010d0 0 FUNC LOCAL DEFAULT 12 __do_global_dtors_aux + 5: 0000000000401090 0 FUNC LOCAL DEFAULT 12 register_tm_clones + 4: 0000000000401060 0 FUNC LOCAL DEFAULT 12 deregister_tm_clones + 39: 0000000000401050 5 FUNC GLOBAL HIDDEN 12 _dl_relocate_sta[...] + 40: 0000000000401020 38 FUNC GLOBAL DEFAULT 12 _start + 44: 0000000000401000 0 FUNC GLOBAL HIDDEN 11 _init + 2: 000000000040037c 32 OBJECT LOCAL DEFAULT 4 __abi_tag + 35: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ + 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND + 30: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_mai[...] + 11: 0000000000000000 0 FILE LOCAL DEFAULT ABS memory.nasm + 24: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c + 3: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c + 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS crt1.o + 26: 0000000000000000 0 FILE LOCAL DEFAULT ABS +==== + +- **main** -> 0x0000000000401110. +- **.bss** -> 0x0000000000404051 +- **.data** -> 0x0000000000404018 + +=== Stack +The stack segment starts in high memory, and when it grows, it grows in the +downward direction. Items are placed on the stack with the +https://www.felixcloutier.com/x86/push[push] instruction and removed from the +stack with the https://www.felixcloutier.com/x86/pop[pop] instruction. Every +time you push, the stack grows, every time you pop, the stack shrinks. + +The stack can be used as temporary storage to save values in registers and call +them back later or, more importantly, to transfer values to functions. + +The stack has to have a 16-byte alignment when you call a function. The reason +for this is that there are SIMD instructions that perform parallel operations on +larger blocks of data, and these SIMD instructions may require that these data +are located in memory on addresses that are multiples of 16 bytes. + +[source, nasm] +---- +section .data + string db "ABCDE", 0 + stringLen equ $ - string - 1 + +main: + xor rax, rax + mov rbx, string + mov rcx, stringLen + mov r12, 0 + pushLoop: + mov al, byte [rbx + r12] + push rax + inc r12 + loop pushLoop +---- + +[source, gdb] +---- +(gdb) i s +---- + +.output +[%collapsible] +==== + #0 0x000000000040116a in pushLoop () + #1 0x0000000000000044 in ?? () + #2 0x0000000000000043 in ?? () + #3 0x0000000000000042 in ?? () + #4 0x0000000000000041 in ?? () + #5 0x0000000000000001 in ?? () + #6 0x00007ffff7da1d90 in __libc_start_call_main (main=main@entry=0x401130
, + argc=1, argc@entry=-10544, argv=0x7fffffffd7e8, argv@entry=0x4) + at ../sysdeps/nptl/libc_start_call_main.h:58 + #7 0x00007ffff7da1e40 in __libc_start_main_impl (main=0x401130
, argc=-10544, + argv=0x4, init=, fini=, rtld_fini=, + stack_end=0x7fffffffd7d8) at ../csu/libc-start.c:392 + #8 0x0000000000401065 in _start () +==== + +Here you can see the stack, and can see 0x0000000000000041 (A), +0x0000000000000042 (B), 0x0000000000000042 \(C), 0x0000000000000043 (D) +0x0000000000000044 (E), being pushed onto the stack. + +=== Virtual Memory +From https://linux.die.net/man/5/elf[man elf] or `info elf` we can see that +`e_entry` from the `ElfN_Ehdr` struct gives the virtual address to which the +system first transfers control. + +=== What is that D Flag +The "D" flag determines the size of each element in the stack. It can be +changed, but changing it can result in an unaligned stack if you are not +careful. + + +== Strings and Symbol Tables +From https://linux.die.net/man/5/elf[man elf] we see that `Elf32_Sym` or `Elf64_Sym` can hold these tables. + +== Elf File Header + +[source, sh] +---- +$ readelf --file-header ./memory +---- + +.output +[%collapsible] +==== + Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 + Class: ELF64 + Data: 2's complement, little endian + Version: 1 (current) + OS/ABI: UNIX - System V + ABI Version: 0 + Type: EXEC (Executable file) + Machine: Advanced Micro Devices X86-64 + Version: 0x1 + Entry point address: 0x401020 + Start of program headers: 64 (bytes into file) + Start of section headers: 14520 (bytes into file) + Flags: 0x0 + Size of this header: 64 (bytes) + Size of program headers: 56 (bytes) + Number of program headers: 13 +==== + +[source, gdb] +---- +(gdb) disass +---- + +.output +[%collapsible] +==== + Dump of assembler code for function main: + 0x0000000000401110 <+0>: push %rbp + 0x0000000000401111 <+1>: mov %rsp,%rbp + => 0x0000000000401114 <+4>: lea 0x404028,%rax + 0x000000000040111c <+12>: movabs $0x404028,%rax + 0x0000000000401126 <+22>: mov 0x404028,%rax + 0x000000000040112e <+30>: mov %rax,0x404058 + 0x0000000000401136 <+38>: lea 0x404058,%rax + 0x000000000040113e <+46>: lea 0x404029,%rax + 0x0000000000401146 <+54>: mov 0x404029,%rax + 0x000000000040114e <+62>: lea 0x404041,%rax + 0x0000000000401156 <+70>: movabs $0x404041,%rax + 0x0000000000401160 <+80>: movabs $0x404042,%rax + 0x000000000040116a <+90>: lea 0x404042,%rax + 0x0000000000401172 <+98>: mov 0x404041,%rax + 0x000000000040117a <+106>: mov 0x404042,%rax + 0x0000000000401182 <+114>: mov %rbp,%rsp + 0x0000000000401185 <+117>: pop %rbp + 0x0000000000401186 <+118>: mov $0x3c,%eax + 0x000000000040118b <+123>: xor %rdi,%rdi + 0x000000000040118e <+126>: syscall +==== + +The entry point from `readelf` is smaller then the entry point from `disass`. +This is because their is a header for elf executables. + + +== Numerical +=== What does the most significant bit mean in numbers? +It can either be a part of the bit of the number or thing it is trying to +represent if it is a unsigned number. Or it can represent whether a number is +"signed" or "unsigned" if it is a signed number. + +=== Single vs. Double Precision +I decimal number has three parts: + +SIGN:: +Specifies a negitive or positive number, 0 being positive and 1 being +negative. + +EXPONENT:: +Takes the non-decimal version of the number and counts the first bit to the +right of most signicant on bit all the way to the least significant bit and +then adds 127 if it is single percision or 1023 if it is double percision. + +FRACTION:: +Takes the non-decimal version number and stores the first bit to the right +of most signicant on bit all the way to the least significant bit. + +If the exponent part of a single percision number is above 127 it is a positive +exponent, otherwise it is a negative exponent. + +If the exponent part of a double percision number is above 1023 it is a positive +exponent, otherwise it is a negative exponent. + +A single-precision number is stored in 32 bits: 1 sign bit, 8 exponent bits, +and 23 fraction bits + +---- +S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF +0 1 8 9 31 +---- + +A double-percision number is stored in 64-bits: 1 sign bit, 11 exponent bits, +and 52 fractin bits. + +---- +S EEEEEEEEEEE FFFFFFFFFFFFFF...FFFFFFFFFFFFFFF +0 1 11 12 63 +---- +=== Two's complement +Two's complement is used to represent negative numbers.This is the steps to get +the negative version of a number. + +1. Do a logical not to all the bits +2. add 1 + +Binary 4294967296 is zero, anything above this number is positive, anything +below this is negative. + +For example, I can convert the number 500 into the negative version of the +number in c. + +[source, c] +---- +#include + +#define NUM 500 + +int +main(void){ +unsigned int a = (~NUM) + 1; +printf("%d\n", a); +} +---- + +=== Checking if Signed Number is negative +==== binary +check if the most significant bit is 1 + +==== hexidecimal +check if lefmost byte is 8, 9, A, B, C D, E, or F. Break number into multiple +bytes or this will not work. For example 0xd12 would appear to be negative if +you didn't break it up into byte chunks, doing so you get: + +---- +0x0d, 0x12 +---- + +or + +---- +0x0d << 8 | 0x12 +---- + +Notice how the leftmost byte actually starts with a zero. Also notice how each +byte is two digits long. + +you can or bytes together to get the original answer by doing + +---- +byte4 << 24 | byte3 << 16 | byte2 << 8 | byte1 +---- + +In this case I or'ed together 4 bytes to get a 24 bit number. bitshift right 8, +16, 24 are offsets; shifting by one byte, two bytes, and then three. + +== Syntax +=== ATT vs. Intel Syntax +gdb uses ATT syntax by default, but you can change it to intel (nasm's syntax) +with the command `set disassembly-flavor intel` + +=== Strings +To explicitly state the length of a string: + +[source, nasm] +---- +db 27, 'Selling England by the Pound' +---- + +=== Constant Precomputation +The math in the code belows is calculated at compile time and turned into a +constant + +[source, nasm] +---- +lab: db 0 + +mov rax, lab + 1 + 2 *3 +---- + +== Books +- "Learn to Program with Assembly by Jonathan Bartlett" + * learning AT&T syntax with GNU assembler + +- "Low-Level Programming by Igor Zhirkov" + * intel syntax + +- "Linkers and Loaders, John R. Levine, 1999, The Morgan Kaufmann Series in + Sofware Engineering and Programming" + +- "Assembly Language for x86 Processors 6th Edition by Kip R. Irvine" + +- "Beginning x64 Assembly Programming from Novice to AVX Professional by Jo Van + Hoey" + +== Links +- https://www.felixcloutier.com/x86/[x86 and amd64 instruction reference] +- https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html[intel 64 IA-32 architectures Software Developer's Manual] +- https://redirect.cs.umbc.edu/portal/help/nasm/nasm.shtml[nasm and gdb] +- https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.99.pdf[System V Application Binary Interface AMD64] +- https://electronicsreference.com/assembly-language/linux_syscalls/[linux syscalls] +- https://blog.packagecloud.io/the-definitive-guide-to-linux-system-calls/[more on linux syscalls] +- https://github.com/torvalds/linux/blob/v3.13/arch/x86/syscalls/syscall_32.tbl[linux source code syscall table] +- https://stackoverflow.com/questions/15017659/how-to-read-the-intel-opcode-notation[op code notation] +- https://stackoverflow.com/questions/10684468/missing-debugging-information-with-gdb-and-nasm[debugging asm_next script nasm and gdb] + +== Asciidoctor +- https://docs.asciidoctor.org/diagram-extension/latest/diagram_types/bytefield/[bytefield extension] diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..166faf7 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,3241 @@ + + + + + + + +Low Level Learning + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+
+
+
+

Looking at the OpCode tables

+
+
+

Ex.

+
+ + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 1. SAL/SHL
OpcodeInstructionOp/En64-bit ModeCompat/Leg ModeDescription

D0/4

SAL r/m8, 1

M1

Valid

valid

Multiply r/m8 by 2, once

C1 /4 ib

SHL r/m16,imm8

M1

Valid

valid

Multiply r/m32 by 2, once

+
+

This is an example of an instruction table that you might see, In this case I +took this from the +Intel 64 and IA-32 Architectures software Developer’s Manual.

+
+
+

Instructions

+
+
+
r
+
+

"r" stands for a general purpose register, "r/m8" mean a 8 bit register, +"r/m16" means a 16 bit register.

+
+
m
+
+

A 16-, 32- or 64-bit operand in memory

+
+
imm
+
+

stands for immediate value (e.g., integer constant like 42 or 1335).

+
+
rel8
+
+

A relative address in the range from 128 bytes before the end of the +instruction to 127 bytes after the end of the instruction.

+
+
rel16, rel32
+
+

A relative address within the same code segment as the instruction +assembled. The rel16 symbol applies to instructions with an operand-size +attribute of 16 bits; the rel32 symbol applies to instructions with an +operand-size attribute of 32 bits

+
+
+
+
+
+

Opcodes

+
+
+
REX.W
+
+

Indicates the use of a REX prefix that affects operand size or instruction +semantics. The ordering of the REX prefix and other optional/mandatory +instruction perfixes are discussed in Chapter 2. Note that REX prefixes +promote legacy instructions to 64-bit behavor are not listed explicitly in +the opcode column

+
+
/Digit
+
+

A digit between 0 and 7 indicates that the ModR/M byte of the instruction +uses only the r/m (register or memory) operand. The reg field contains the +digit that provides an extension to the instruction’s opcode.

+
+
/r
+
+

Indicates that the ModR/M byte of the instruction contains a register +operand and an r/m operand.

+
+
cb, cw, cd, cp, co, ct
+
+

A 1-byte (cb), 2-byte (cw), 4-byte (cd), 6-byte (cp), 8-byte (co) or +10-byte (ct) value following the opcode. This value is used to specify a +code offset and possibly a new value for the code segment register.

+
+
ib, iw, id, io
+
+

A 1-byte (ib), 2-byte (iw), 4-byte (id) or 8-byte (io) immediate operand to +the instruction that follows the opcode, ModR/M bytes or scale-indexing +bytes. The opcode determines if the operand is a signed value. All words, +doublewords and quadwords are given with the low-order byte first.

+
+
+rb, +rw, +rd, +ro
+
+

Indicates the lower 3 bits of the opcode byte is used to encode the register +operand without a modR/M byte. The instruction lists the corresponding +hexadecimal value of the opcode byte with low 3 bits as 000b. In non-64-bit +mode, a register code, from 0 through 7, is added to the hexadecimal value +of the opcode byte. In 64-bit mode, indicates the four bit field of REX.b +and opcode[2:0] field encodes the register operand of the instruction. “+ro” +is applicable only in 64-bit mode. See Table 3-1 for the codes.

+
+
+i
+
+

A number used in floating-point instructions when one of the operands is +ST(i) from the FPU register stack. The number i (which can range from 0 to +7) is added to the hexadecimal byte given at the left of the plus sign to +form a single opcode byte.

+
+
+
+
+ + + + + +
+ + +Look at Intel 64 and IA-32 Architectures Software Developer’s Manual page + 2839 +
+
+
+
+
+
+

Data types

+
+ + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 2. datatypes
TypeLengthName

db

8 bits

Byte

dw

16 bits

Word

dd

32 bits

Double Word

dq

64 bits

Quadword

+
+
+
+

Registers

+
+
+

R_X vs E_X vs _X vs _H vs _L

+
+
+
R_X
+
+

all 64 bits

+
+
E_X
+
+

lower 32 bits

+
+
_X
+
+

lower 16 bits

+
+
_H
+
+

top 4 bits of last byte

+
+
_L
+
+

bottom 4 bits of last byte

+
+
+
+
+
+

General Purpose Registers

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
64-bit32-bit16-bitlow 8-bithigh 8-bitcomment

rax

eax

ax

al

ah

rbx

ebx

bx

bl

bh

rcx

ecx

cx

cl

ch

rdx

edx

dx

dl

dh

rsi

esi

si

sil

rdi

edi

di

dil

rbp

ebp

bp

bpl

Base Pointer

rsp

esp

sp

spl

Stack Pointer

r8

r8d

r8w

r8b

r9

r9d

r9w

r9b

r10

r10d

r10w

r10b

r11

r11d

r11w

r11b

r12

r12d

r12w

r12b

r13

r13d

r13w

r13b

r14

r14d

r14w

r14b

r15

r15d

r15w

r15b

+
+
+

Flag Registers

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSymbolBitContent

Carry

CF

0

Previous instruction had a carry

parity

PF

2

Last byte has even number of 1s

Adjust

AF

4

BCD operations

Zero

ZF

6

Pervious instruction resulted in zero

Sign

SF

8

Previous instruction resulted in most significant + bit equal to 1

Overflow

OF

11

Previous instruction resulted in overflow

+
+
+

Instruction Pointer Register (rip)

+
+

The processor keeps trakc of the next instruction to be executed by storing the +address of the next instruction in rip.

+
+
+
+

xmm and ymm Registers

+
+

These registers are used for floating-point calculations and SIMD.

+
+
+
+
+
+

Main

+
+
+

One file in your assembly program needs "main" defined. This seems to be defined +in the .text section.

+
+
+
+
section .text
+global main
+main:
+
+
+
+
+
+

Labels

+
+
+
    +
  • +

    Labels look like this <description>:

    +
  • +
  • +

    Local lablels Look like this .<description>:

    +
  • +
+
+
+

If there is a local label within a global label then the full label name +could loop like this main.loop. Like in this example:

+
+
+
+
section .text
+global main
+main:
+    .loop
+    ; do somethings
+    jnz .loop
+
+
+
+
+
+

Jumping / looping

+
+
+

cmp sets the approriate flags. jl jumps if rax < 42. jmp jumps straight to +the ex label.

+
+
+
+
    cmp rax, 42
+    jl yes
+    mov rbx, 0
+    jmp ex
+yes:
+    mov rbx, 1
+ex:
+
+
+
+
+
ja
+
+

(jump if above)/ jb (jump if below) for a jump after a comparison +of unsigned numbers with cmp.

+
+
jg
+
+

(Jump if greater) / jl (jump if less) for signed.

+
+
jae
+
+

(jump if above or equal), jle (jump if less or equal) and similar

+
+
loop
+
+

checks RCX/ECX/CX register as a counter. Each time the LOOP instruction is +executed, the count register is decremented, the check for 0. If the count +is 0, the loop is terminated. If the count is not zero, a near jump is +performed.

+
+
+
+
+

Different Types of Jumps

+
+
+
Near Jump
+
+

A jump to an instruction within the current code segment (the segment +currently pointed to by the CS register), sometimes referred to as an +intrasegment jump

+
+
Short Jump
+
+

A near jump where the jump range is limited to –128 to +127 from the current +EIP value

+
+
Far Jump
+
+

A jump to an instruction located in a different segment than the current +code segment but at the same privilege level, sometimes referred to as an +intersegment jump

+
+
Task Switch
+
+

A jump to an instruction located in a different task

+
+
+
+
+
+
+
+

Function Calls

+
+
+

Requirements

+
+

Must start wih a function prologue and end with a function epilogue followed by +a pop rip.

+
+
+
function prologue
+
+
push rbp
+mov rbp,rsp
+
+
+
+
function epilogue
+
+
mov rsp, rbp
+pop rbp
+
+
+
+

Must move to the the function label with a jmp, like so:

+
+
+
+
push rip
+jmp <address>
+
+
+
+

The address now stored in the stack (for rip contents) is called return +address.

+
+
+

The first six arguments must be rdi, rsi, rdx, rcx, r8, and r9, respectively.

+
+
+

One should not invoke ret (pop rip) unless the stack is in exactly the same +state as when the function started.

+
+
+

Must restore all other register that where not arguments.

+
+
+
+
Callee-saved registers
+
+

must be restored by the procedure being called. +rbx, rbp, rsp, r12-r15

+
+
Caller-saved registers
+
+

Should be saved before invoking a function and restored after. All other +registers are caller-saved.

+
+
+
+
+
+

Pattern of Calling a Function

+
+
    +
  • +

    Save all caller-saved registers you want to survive function call (you can use +push for that)

    +
  • +
  • +

    Store arguments in the relevent register (rdi, rsi, etc).

    +
  • +
  • +

    If you return a value from a function, you use xmm0 for floating point values +and use rax for other values, such as integers or addresses.

    +
  • +
  • +

    Restore caller-saved registers stored before the function call.

    +
  • +
+
+
+ + + + + +
+ + +some system calls also return values - be careful to read the docs! +
+
+
+
+ +
+
+
ret
+
+

Denotes the function end. same as pop rip.

+
+
leave
+
+

Is the the same as mov rsp, rbp and pop rbp (the epilogue).

+
+
call <address>
+
+

Is the same as push rip and jmp <address>

+
+
enter 0, 0
+
+

Is the same as push rbp and mov rbp, rsp (the prologue). It +is said to have poor performance.

+
+
+
+
+
+

Locality

+
+

Functions can have their own sections, such as .data, .bss, and .text. A +period before a variable means that variable is local to that function.

+
+
+
+

Function types

+
+
+
Branch Functions
+
+

Contain calls to other functions

+
+
Leaf Functions
+
+

Execute some commands and then return to the parent function.

+
+
+
+
+
+

Alignment

+
+

The stack has to have 16-byte alignment when you call a function. Inside a leaf +function, you can in general ignore stack from and stack alignment; it is not +necessary as long as you don’t mess with the stack. Similarly, if your function +does not use SIMD instructions, you do not need to care about alignment. +Compilers have optimizing functionality, and sometimes when you look at code +generated by compilers, you will find that there was no stack from used. That +happens when the compilers noticed during optimization that a stack form is no +needed. It IS a good habit to always include a stack from and check the +stack alignment; it can save you a lot of trouble later. A good reaason to +include a stack frome is the fact that GDB and GDB-based debugges (such as DDD +and SASM) expect to find a stack frame. If there is no stacfk form in your code, +the debugger will behave unpredictably, such as ignoring breakpoints or jumping +over instructions.

+
+
+
+
+
+

Syscalls

+
+
+

syscall instructions implicitly uses rcx. System calls cannot accept more than +six arguments.

+
+
+

All the syscalls are listed in /usr/include/nasm/unistd.h (or something +similar), together with their numbers (the value to put in EAX before you call +int 80h). In ubuntu I found the file in +/usr/include/x86_64-linux-gnu/asm/unistd_64.h.

+
+
+

You can checkout man syscalls on links to lookup a syscall.

+
+
+

checkout this syscall table for +linux

+
+
+
+
+

Different Addressing types

+
+
+

Immediate

+
+

use the specified value

+
+
+

ex.

+
+
+
+
mov rax, 10
+
+
+
+
+

Register

+
+

Use the value in teh specified register

+
+
+
+
mov rax, rbx
+
+
+
+
+
mov rax, [10]; transfers 8 bytes starting at the tenth address into rax
+
+
+
+
+
mov r9, 10
+mov rax, [r9] ; take the address from the register
+
+
+
+
+

Direct Memory

+
+

Use the value at the specified memory address

+
+
+
+
mov rdi, 0x172 ; not 100% sure about this
+
+
+
+
+

Base-indexed with scale and discplacement

+
+

Most addressing modes are generalized by this mode. The address here is +calculated based on the following components:

+
+
+
+
Address = base + index * scale + displacement
+
+
+
+
    +
  • +

    Base is either immediate or a register

    +
  • +
  • +

    Scale can only be immediate equal to 1, 2, 4, or 8

    +
  • +
  • +

    Index is immediate or a register

    +
  • +
  • +

    Displacement is always immediate

    +
  • +
+
+
+
+
mov rax, [rbx + 4 * rcx + 9]
+mov rax, [4 * r9]
+mov rdx, [rax + rbx]
+lea rax, [rbx + rbx * 4] ; rax = rbx * 5
+add r8, [9 + rbx * 8 + 7]
+
+
+
+
+
+
+

Endianness

+
+
+

Least significant vs Most significant

+
+

This is dependent on the endianness of the system. Most systems are little +endian meaning that the less significant bits are further the the right. More +signifacant bits are further to the right.

+
+
+
+

Text

+
+

This instruction

+
+
+
+
mov rax, [text1]
+
+
+
+

stores text1 into rax in little endian mode

+
+
+
gdb rax register
+
+
(gdb) i r
+rax            0x5452455000636261  6075995057182433889
+
+
+
+

0x61 is a, 0x62 is b, and 0x63 is c.

+
+
+
+

Change a number from little endian to big endian in C

+
+
+
#include <stdio.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+
+int
+main(void){
+    unsigned int num = 0x12345678;
+    printf("%x\n", htonl(num));
+}
+
+
+
+
+
+
+

Pointers and Differnt Addressing Types

+
+
+

Pointers are 8 bytes.

+
+
+

you need to specify operation size, when trying to write shomewhere a value +whose size is not specified.

+
+
+
+
section .data
+    test: dq -1
+section .bss
+section .text
+	global main
+main:
+   mov byte[test], 1
+    ;mov [test], 1 ; gives an error
+   mov word[test], 1
+   mov dword[test], 1
+   mov qword[test], 1
+
+	mov  rax, 60
+	mov rdi, 0
+	syscall
+
+
+
+
+
+

Instructions

+
+
+

NEG

+
+

The NEG instruction applys two complement. It sets the CF falgs to zero if the +source operand is 0; otherwise it is set to 1.

+
+
+
+

IMUL

+
+

signed multiplication

+
+
+
+
One-operand form
+
+

secound operand is in AL, AX, EAX, or RAX, depending on the +size

+
+
Two-operand form
+
+

With this form the destination operand (the first operand) is multiplied by +the source operand

+
+
Three-operand form
+
+

with mul a, b, c, the multiplication of b and c is +stored in a

+
+
+
+
+
+

MUL

+
+

unsigned multiply

+
+
+

the source operand is located in either AL, AX, or EAX, dending on the size of +the operand.

+
+
+

The result is stored in RDX:RAX meaning that the lower 64 bits are stored in RAX +and the upper 64 bits are stored in RDX. This is because the result of two +numbers can be larger bit size they originaly had.

+
+
+
+

SUB

+
+

subtract source operand from the destination operand and stores the result in +the destination operand.

+
+
+
    +
  • +

    integer subraction; signed and unsigned

    +
  • +
  • +

    sets OF flags to indicate an overflow in the signed or unsigned result, +respectively.

    +
  • +
  • +

    The SF flag indicates the sign of the signed result

    +
  • +
+
+
+

In 64-bit mode the instruction’s default operation size is 32 bits

+
+
+
+

IDIV

+
+

signed divide. Divides the value in AX, DX:AX, or EDX:EAX (dividend) by the +source operand (divisor) and stores the result in AX (AH:AL), DX:AX, or EDX:EAX +registers.

+
+
+
    +
  • +

    Overflow is indicated withthe #DE (divide error) exception rather than with +the CF flag

    +
  • +
+
+
+

The modulo can be found in rdx.

+
+
+ + + + + +
+ + +Remember to set rdx to zero every time before you use idiv or the + resulting quotient may be wrong. +
+
+
+
+

LEA

+
+
+
lea rsi, [variable]
+
+
+
+

square brackets represent indirect addressing.

+
+
+
+

DIV

+
+

Divids unsigned the value in the AX, DX:AX, EDX:EAX, or RDX:RAX registers +(dividend) by the source operand (divisor) and stores the result in AX (AH:AL), +DX:AX, EDX:EAX, or RDX:RAX register

+
+
+
+

TEST

+
+

Computes the bit-wise logical AND of first operand (source 1 operand) and the +second operand (source 2 operand) and sets the SF, ZF, and PF status flags +according to the result. The result is then discarded.

+
+
+

If the result of AND is zero then the ZF flag is set.

+
+
+
+

NOP

+
+

The x86-64 ISA includes several no-op (no operation) instructions, including +nop, nop A, (no-op with an argument), and data16. No-ops do pretty much +nothing, but do set the rflags. The main reason for no-ops is to optimize +instruction memory (e.g., code size, alignment).

+
+
+
+

MOVSD

+
+

move a double precision floating point value

+
+
+
+

MOVSS

+
+

move a single precision floating point value

+
+
+
+

What is the differnce between SAR and SHR?

+
+

SAR sets or clears the most significant bit depending on the sign, While SHR +just clears the most significant bit.

+
+
+
+

What’s the difference between the CF flag and OF flag.

+
+
+
Carry Flag
+
+

bits that are carried over in UNSIGNED integer arithmetic

+
+
Overflow Flag
+
+

bits that are carried over in SIGNED integer arithmetic

+
+
+
+
+
+
+
+

NASM and GDB

+
+
+

Get Memory Allocated On The Heap

+
+

List memory regions mapped by the specified process +- find-heap-memory-running-process

+
+
+
+
(gdb) info proc mappings
+
+
+
+output +
+
+
+
(gdb) info proc mappings
+process 56173
+Mapped address spaces:
+          Start Addr           End Addr       Size     Offset  Perms  objfile
+      0x555555554000     0x555555555000     0x1000        0x0  r--p   /usr/local/share/tst/c/tst
+      0x555555555000     0x555555556000     0x1000     0x1000  r-xp   /usr/local/share/tst/c/tst
+      0x555555556000     0x555555557000     0x1000     0x2000  r--p   /usr/local/share/tst/c/tst
+      0x555555557000     0x555555558000     0x1000     0x2000  r--p   /usr/local/share/tst/c/tst
+      0x555555558000     0x555555559000     0x1000     0x3000  rw-p   /usr/local/share/tst/c/tst
+      0x555555559000     0x55555557a000    0x21000        0x0  rw-p   [heap]
+      0x7fffb7d74000     0x7ffff7d78000 0x40004000        0x0  rw-p
+      0x7ffff7d78000     0x7ffff7da0000    0x28000        0x0  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
+      0x7ffff7da0000     0x7ffff7f35000   0x195000    0x28000  r-xp   /usr/lib/x86_64-linux-gnu/libc.so.6
+      0x7ffff7f35000     0x7ffff7f8d000    0x58000   0x1bd000  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
+      0x7ffff7f8d000     0x7ffff7f8e000     0x1000   0x215000  ---p   /usr/lib/x86_64-linux-gnu/libc.so.6
+      0x7ffff7f8e000     0x7ffff7f92000     0x4000   0x215000  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
+      0x7ffff7f92000     0x7ffff7f94000     0x2000   0x219000  rw-p   /usr/lib/x86_64-linux-gnu/libc.so.6
+      0x7ffff7f94000     0x7ffff7fa1000     0xd000        0x0  rw-p
+      0x7ffff7fbb000     0x7ffff7fbd000     0x2000        0x0  rw-p
+      0x7ffff7fbd000     0x7ffff7fc1000     0x4000        0x0  r--p   [vvar]
+      0x7ffff7fc1000     0x7ffff7fc3000     0x2000        0x0  r-xp   [vdso]
+      0x7ffff7fc3000     0x7ffff7fc5000     0x2000        0x0  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.s
+o.2
+      0x7ffff7fc5000     0x7ffff7fef000    0x2a000     0x2000  r-xp   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.s
+--Type <RET> for more, q to quit, c to continue without paging--
+o.2
+      0x7ffff7fef000     0x7ffff7ffa000     0xb000    0x2c000  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.s
+o.2
+      0x7ffff7ffb000     0x7ffff7ffd000     0x2000    0x37000  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.s
+o.2
+      0x7ffff7ffd000     0x7ffff7fff000     0x2000    0x39000  rw-p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.s
+o.2
+      0x7ffffffdd000     0x7ffffffff000    0x22000        0x0  rw-p   [stack]
+  0xffffffffff600000 0xffffffffff601000     0x1000        0x0  --xp   [vsyscall]
+
+
+
+
+
+

From the heap memory addresses we can use find to find a string within the heap

+
+
+
+
find 0x555555559000, 0x55555557a000, 'h', 'e', 'l', 'l', 'o', '1'
+
+
+
+
+

Get type of

+
+
+
(gdb) ptype  0x404034
+type = int
+
+
+
+
+

Breaking at local labels

+
+
+
b main.string_size
+
+
+
+
+

Looking at flags

+
+

To get flag values:

+
+
+
+
info registers eflags
+
+
+
+

An output like this:

+
+
+
+
eflags  0x41  [ CF ZF ]
+
+
+
+

means that the carry and zero flag are set

+
+
+
+

Next instruction

+
+

assembly:

+
+
+
+
(gdb) ni
+
+
+
+

C:

+
+
+
+
(gdb) n
+
+
+
+
+

Steping into a function

+
+

To step into an assembly function

+
+
+
+
(gdb) stepi
+
+
+
+

To step into an C function

+
+
+
+
(gdb) step
+
+
+
+
+

Make Printing Look Nice

+
+
+
(gdb) set print pretty
+
+
+
+
+

Get current line

+
+
+
(gdb) frame
+
+
+
+
+

Break at specific instruction number

+
+
+
 (gdb) b * 0x0000000000401110
+
+
+
+
+

Flavor

+
+

to set dissasembly flavor to intel instead of AT&T:

+
+
+
+
(gdb) set disassembly-flavor intel
+
+
+
+
+

Dissassembly a function

+
+
+
disassemble <function name>
+
+
+
+
    +
  • +

    function name - can be the name of any function (i.e main)

    +
  • +
+
+
+
+

Finding Immediate / registers / label values

+
+

If you have

+
+
+
+
msg db "hello, world", 10, 0
+
+
+
+

In the .data section and use it later with

+
+
+
+
mov rsi, msg
+
+
+
+

The output of (gdb) dissassembly <function name> might look something like +this

+
+
+
+
0x000000000040111a <+10>:    movabs rsi,0x404028
+
+
+
+

You can see what is inside the memory address 0x404028 with

+
+
+
+
(gdb) x/s 0x404028
+0x404028 <msg>: "hello, world\n"
+
+
+
+

As you can see that is the address is where msg is located

+
+
+
    +
  • +

    get register value → (gdb) x/s $rax

    +
  • +
  • +

    get register value → (gdb) p $rax

    +
  • +
  • +

    get label value → (gdb) x/s &codes

    +
  • +
  • +

    get label value → (gdb) p &codes

    +
  • +
  • +

    x/s → output string

    +
  • +
  • +

    x/c → output character

    +
  • +
  • +

    x/13c → output 13 characters

    +
  • +
  • +

    x/13d → output 13 characters in decimal representation

    +
  • +
+
+
+ + + + + +
+ + +checkout (gdb) help x +
+
+
+
+

TUI

+
+
+
(gdb) tui <enable/disable>
+
+
+
+
+

Registers

+
+
+
(gdb) info registers
+
+
+
+
+

Source code

+
+
+
(gdb) list <nothing or line number>
+
+
+
+
+

Steping through Instructions

+
+
next_asm.gdb
+
+
set language asm
+set disassembly-flavor intel
+define asm_next
+nexti
+disassemble
+end
+
+
+
+
+
$ gdb -x next_asm.gdb <executable>
+
+
+
+
+

Step out of function

+
+
+
(gdb) finish
+
+
+
+
+
+
+

Segment Registers

+
+
+

Logical addresses can be explicitly specified in *x86 assembly language

+
+
+

all segments have a base of zero

+
+
+

In protected mode, code may always modify all segment registers except CS (the +code segment selector). This is because the current privilege level (CPL) of the +processor is stored in the lower 2 bits of the CS register

+
+
+

The only ways to raise the processor privilege level (and reload CS) are through +the lcall (far call) and int (interrupt) instructions. Similarly, the only ways +to lower the privilege level (and reload CS) are through lret (far return) and +iret (interrupt return) instructions. In real mode, code may also modify the CS +register by making a far jump (or using an undocumented POP CS instruction on +the 8086 or 8088).[4] Of course, in real mode, there are no privilege levels; +all programs have absolute unchecked access to all of memory and all CPU +instructions

+
+
+
+
movl $42, %fs:(%eax)  ; Equivalent to M[fs:eax]<-42) in RTL
+
+
+
+

or in intel syntax

+
+
+
+
mov dword [fs:eax], 42
+
+
+
+
    +
  • +

    What data is in each segment

    +
    +
      +
    • +

      All CPU instructions are implicitly fetched from the code segment +specified by the segment selector held in the CS register.

      +
    • +
    • +

      Most memory references come from the data segment specified by the segment +selector held in the DS register. These may also come from the extra +segment specified by the segment selector held in the ES register, if a +segment-override prefix precedes the instruction that makes the memory +reference. Most, but not all, instructions that use DS by default will +accept an ES override prefix.

      +
    • +
    • +

      Processor stack references, either implicitly (e.g. push and pop +instructions) or explicitly (memory accesses using the (E)SP or (E)BP +registers) use the stack segment specified by the segment selector held in +the SS register.

      +
    • +
    • +

      String instructions (e.g. stos, movs), along with data segment, also use +the extra segment specified by the segment selector held in the ES +register.

      +
    • +
    • +

      mov is used to manipulate data, so the address is relative to the data +segment

      +
    • +
    +
    +
  • +
+
+
+

SHDR

+
+

From man elf we can see that the Elf32_Shdr or Elf32_Shdr structs, can +contain info about .bss, .data, etc.

+
+
+
+

Data segment

+
+

Example 1

+
+
+
global main
+section .data
+    bNum db 123
+section .text
+main:
+lea rax, [bNum]
+
+
+
+
gdb disassembly
+
+
 lea    rax,ds:0x404028
+
+
+
+
+
ds
+
+

data segment

+
+
0x404028
+
+

address of bNum

+
+
+
+
+
+
+
+
+

Compiling and Linking

+
+
+

Makefile

+
+

The -f is follow by the output format, in our case elf64, which means +Executable and Linkable Format for 64-bit. The -g mains that we include debug +information in a debug format specified adter the -F option. We use dwarf +debug format. The -l tells NASM to generate a .lst file. Nasm will create an +object file with a .o extension. That object file is used by the linker.

+
+
+
+
DWARF
+
+

Debug With Arbitrary Record Format

+
+
STABS
+
+

Debug with Symbol Table Strings.

+
+
elf64
+
+

Executable and linkable Format for 64-bit

+
+
PIE
+
+

Position-Independant Executables

+
+
+
+
+
+
+
+

Modes

+
+
+
+
Real mode
+
+
+
    +
  • +

    the most ancient, 16-bit

    +
  • +
  • +

    Computer is booted into this mode

    +
  • +
  • +

    Can access pretty much any memory location

    +
  • +
  • +

    No virtual memory

    +
  • +
+
+
+
Protected
+
+
+
    +
  • +

    commonly referred as 32-bit one

    +
  • +
  • +

    Virtual memory

    +
  • +
  • +

    Protection rings

    +
  • +
  • +

    Improved segmentation

    +
  • +
+
+
+
Virtual
+
+
+
    +
  • +

    emulate real mode inside protected

    +
  • +
+
+
+
System management mode
+
+
+
    +
  • +

    for sleep mode, power management, etc

    +
  • +
+
+
+
Long Mode
+
+
+
    +
  • +

    Virtual memory

    +
  • +
+
+
+
+
+
+
+A 386 CPU can be put back into real mode by clearing a bit in the CR0 control +register, however this is a privileged operation in order to enforce security +and robustness +
+
+— X86_memory_segmentation
+Wikipedia +
+
+
+
+
+

Memory

+
+
+

The memory size has a theoretical limit of 2^64 address.

+
+
+

If you look at this .lst file you can see the each memory address (second +column) has 8 hexadecimal digits meaning that there are 64 bits per instruction. +This is because we are using 64 bit assembler.

+
+
+
\.lst file
+
+
    11 00000000 B801000000                  mov rax, 1
+    12 00000005 BF01000000                  mov rdi, 1
+
+
+
+

Layout

+
+

The stack in high memory can grow; it grows in the downward directection toward +section .bss. The available free memory between the stack and the other +sections is called the heap.

+
+
+

The memory in section .bss is asigned at runtime.

+
+
+
+Diagram +
+
+
+
+
readelf --symbols ./memory | tail +10 | sort -k 2 -r
+
+
+
+output +
+
+
+
38: 0000000000404090     0 NOTYPE  GLOBAL DEFAULT   23 _end
+23: 0000000000404071     8 OBJECT  LOCAL  DEFAULT   23 qvar
+22: 000000000040405d     2 OBJECT  LOCAL  DEFAULT   23 wvar
+21: 0000000000404059     4 OBJECT  LOCAL  DEFAULT   23 dvar
+20: 0000000000404058     1 OBJECT  LOCAL  DEFAULT   23 bvar
+43: 0000000000404058     0 OBJECT  GLOBAL HIDDEN    22 __TMC_END__
+ 7: 0000000000404054     1 OBJECT  LOCAL  DEFAULT   23 completed.0
+41: 0000000000404051     0 NOTYPE  GLOBAL DEFAULT   23 __bss_start
+32: 0000000000404051     0 NOTYPE  GLOBAL DEFAULT   22 _edata
+19: 000000000040404d     1 OBJECT  LOCAL  DEFAULT   22 text2
+18: 0000000000404045     8 OBJECT  LOCAL  DEFAULT   22 qNum2
+17: 0000000000404041     1 OBJECT  LOCAL  DEFAULT   22 text1
+16: 0000000000404039     8 OBJECT  LOCAL  DEFAULT   22 qNum1
+15: 0000000000404035     4 OBJECT  LOCAL  DEFAULT   22 dNum
+14: 000000000040402b     2 OBJECT  LOCAL  DEFAULT   22 warray
+13: 0000000000404029     2 OBJECT  LOCAL  DEFAULT   22 wNum
+12: 0000000000404028     1 OBJECT  LOCAL  DEFAULT   22 bNum
+36: 0000000000404020     0 OBJECT  GLOBAL HIDDEN    22 __dso_handle
+31: 0000000000404018     0 NOTYPE  WEAK   DEFAULT   22 data_start
+34: 0000000000404018     0 NOTYPE  GLOBAL DEFAULT   22 __data_start
+29: 0000000000404000     0 OBJECT  LOCAL  DEFAULT   21 _GLOBAL_OFFSET_TABLE_
+27: 0000000000403e60     0 OBJECT  LOCAL  DEFAULT   19 _DYNAMIC
+ 8: 0000000000403e58     0 OBJECT  LOCAL  DEFAULT   18 __do_global_dtor[...]
+10: 0000000000403e50     0 OBJECT  LOCAL  DEFAULT   17 __frame_dummy_in[...]
+25: 0000000000402060     0 OBJECT  LOCAL  DEFAULT   16 __FRAME_END__
+28: 0000000000402004     0 NOTYPE  LOCAL  DEFAULT   15 __GNU_EH_FRAME_HDR
+37: 0000000000402000     4 OBJECT  GLOBAL DEFAULT   14 _IO_stdin_used
+33: 0000000000401190     0 FUNC    GLOBAL HIDDEN    13 _fini
+42: 0000000000401110     0 NOTYPE  GLOBAL DEFAULT   12 main
+ 9: 0000000000401100     0 FUNC    LOCAL  DEFAULT   12 frame_dummy
+ 6: 00000000004010d0     0 FUNC    LOCAL  DEFAULT   12 __do_global_dtors_aux
+ 5: 0000000000401090     0 FUNC    LOCAL  DEFAULT   12 register_tm_clones
+ 4: 0000000000401060     0 FUNC    LOCAL  DEFAULT   12 deregister_tm_clones
+39: 0000000000401050     5 FUNC    GLOBAL HIDDEN    12 _dl_relocate_sta[...]
+40: 0000000000401020    38 FUNC    GLOBAL DEFAULT   12 _start
+44: 0000000000401000     0 FUNC    GLOBAL HIDDEN    11 _init
+ 2: 000000000040037c    32 OBJECT  LOCAL  DEFAULT    4 __abi_tag
+35: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
+ 0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
+30: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_mai[...]
+11: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS memory.nasm
+24: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
+ 3: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
+ 1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crt1.o
+26: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS
+
+
+
+
+
+
    +
  • +

    main → 0x0000000000401110.

    +
  • +
  • +

    .bss → 0x0000000000404051

    +
  • +
  • +

    .data → 0x0000000000404018

    +
  • +
+
+
+
+

Stack

+
+

The stack segment starts in high memory, and when it grows, it grows in the +downward direction. Items are placed on the stack with the +push instruction and removed from the +stack with the pop instruction. Every +time you push, the stack grows, every time you pop, the stack shrinks.

+
+
+

The stack can be used as temporary storage to save values in registers and call +them back later or, more importantly, to transfer values to functions.

+
+
+

The stack has to have a 16-byte alignment when you call a function. The reason +for this is that there are SIMD instructions that perform parallel operations on +larger blocks of data, and these SIMD instructions may require that these data +are located in memory on addresses that are multiples of 16 bytes.

+
+
+
+
section .data
+    string db "ABCDE", 0
+    stringLen equ $ - string - 1
+
+main:
+    xor rax, rax
+    mov rbx, string
+    mov rcx, stringLen
+    mov r12, 0
+    pushLoop:
+        mov al, byte [rbx + r12]
+        push rax
+        inc r12
+        loop pushLoop
+
+
+
+
+
(gdb) i s
+
+
+
+output +
+
+
+
#0  0x000000000040116a in pushLoop ()
+#1  0x0000000000000044 in ?? ()
+#2  0x0000000000000043 in ?? ()
+#3  0x0000000000000042 in ?? ()
+#4  0x0000000000000041 in ?? ()
+#5  0x0000000000000001 in ?? ()
+#6  0x00007ffff7da1d90 in __libc_start_call_main (main=main@entry=0x401130 <main>,
+    argc=1, argc@entry=-10544, argv=0x7fffffffd7e8, argv@entry=0x4)
+    at ../sysdeps/nptl/libc_start_call_main.h:58
+#7  0x00007ffff7da1e40 in __libc_start_main_impl (main=0x401130 <main>, argc=-10544,
+    argv=0x4, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>,
+    stack_end=0x7fffffffd7d8) at ../csu/libc-start.c:392
+#8  0x0000000000401065 in _start ()
+
+
+
+
+
+

Here you can see the stack, and can see 0x0000000000000041 (A), +0x0000000000000042 (B), 0x0000000000000042 (C), 0x0000000000000043 (D) +0x0000000000000044 (E), being pushed onto the stack.

+
+
+
+

Virtual Memory

+
+

From man elf or info elf we can see that +e_entry from the ElfN_Ehdr struct gives the virtual address to which the +system first transfers control.

+
+
+
+

What is that D Flag

+
+

The "D" flag determines the size of each element in the stack. It can be +changed, but changing it can result in an unaligned stack if you are not +careful.

+
+
+
+
+
+

Strings and Symbol Tables

+
+
+

From man elf we see that Elf32_Sym or Elf64_Sym can hold these tables.

+
+
+
+
+

Elf File Header

+
+
+
+
$ readelf --file-header ./memory
+
+
+
+output +
+
+
+
Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
+Class:                             ELF64
+Data:                              2's complement, little endian
+Version:                           1 (current)
+OS/ABI:                            UNIX - System V
+ABI Version:                       0
+Type:                              EXEC (Executable file)
+Machine:                           Advanced Micro Devices X86-64
+Version:                           0x1
+Entry point address:               0x401020
+Start of program headers:          64 (bytes into file)
+Start of section headers:          14520 (bytes into file)
+Flags:                             0x0
+Size of this header:               64 (bytes)
+Size of program headers:           56 (bytes)
+Number of program headers:         13
+
+
+
+
+
+
+
(gdb) disass
+
+
+
+output +
+
+
+
Dump of assembler code for function main:
+   0x0000000000401110 <+0>:     push   %rbp
+   0x0000000000401111 <+1>:     mov    %rsp,%rbp
+=> 0x0000000000401114 <+4>:     lea    0x404028,%rax
+   0x000000000040111c <+12>:    movabs $0x404028,%rax
+   0x0000000000401126 <+22>:    mov    0x404028,%rax
+   0x000000000040112e <+30>:    mov    %rax,0x404058
+   0x0000000000401136 <+38>:    lea    0x404058,%rax
+   0x000000000040113e <+46>:    lea    0x404029,%rax
+   0x0000000000401146 <+54>:    mov    0x404029,%rax
+   0x000000000040114e <+62>:    lea    0x404041,%rax
+   0x0000000000401156 <+70>:    movabs $0x404041,%rax
+   0x0000000000401160 <+80>:    movabs $0x404042,%rax
+   0x000000000040116a <+90>:    lea    0x404042,%rax
+   0x0000000000401172 <+98>:    mov    0x404041,%rax
+   0x000000000040117a <+106>:   mov    0x404042,%rax
+   0x0000000000401182 <+114>:   mov    %rbp,%rsp
+   0x0000000000401185 <+117>:   pop    %rbp
+   0x0000000000401186 <+118>:   mov    $0x3c,%eax
+   0x000000000040118b <+123>:   xor    %rdi,%rdi
+   0x000000000040118e <+126>:   syscall
+
+
+
+
+
+

The entry point from readelf is smaller then the entry point from disass. +This is because their is a header for elf executables.

+
+
+
+
+

Numerical

+
+
+

What does the most significant bit mean in numbers?

+
+

It can either be a part of the bit of the number or thing it is trying to +represent if it is a unsigned number. Or it can represent whether a number is +"signed" or "unsigned" if it is a signed number.

+
+
+
+

Single vs. Double Precision

+
+

I decimal number has three parts:

+
+
+
+
SIGN
+
+

Specifies a negitive or positive number, 0 being positive and 1 being +negative.

+
+
EXPONENT
+
+

Takes the non-decimal version of the number and counts the first bit to the +right of most signicant on bit all the way to the least significant bit and +then adds 127 if it is single percision or 1023 if it is double percision.

+
+
FRACTION
+
+

Takes the non-decimal version number and stores the first bit to the right +of most signicant on bit all the way to the least significant bit.

+
+
+
+
+

If the exponent part of a single percision number is above 127 it is a positive +exponent, otherwise it is a negative exponent.

+
+
+

If the exponent part of a double percision number is above 1023 it is a positive +exponent, otherwise it is a negative exponent.

+
+
+

A single-precision number is stored in 32 bits: 1 sign bit, 8 exponent bits, +and 23 fraction bits

+
+
+
+
S   EEEEEEEE   FFFFFFFFFFFFFFFFFFFFFFF
+0   1      8   9                     31
+
+
+
+

A double-percision number is stored in 64-bits: 1 sign bit, 11 exponent bits, +and 52 fractin bits.

+
+
+
+
S  EEEEEEEEEEE   FFFFFFFFFFFFFF...FFFFFFFFFFFFFFF
+0  1         11  12                             63
+
+
+
+
+

Two’s complement

+
+

Two’s complement is used to represent negative numbers.This is the steps to get +the negative version of a number.

+
+
+
    +
  1. +

    Do a logical not to all the bits

    +
  2. +
  3. +

    add 1

    +
  4. +
+
+
+

Binary 4294967296 is zero, anything above this number is positive, anything +below this is negative.

+
+
+

For example, I can convert the number 500 into the negative version of the +number in c.

+
+
+
+
#include <stdio.h>
+
+#define NUM 500
+
+int
+main(void){
+unsigned int a = (~NUM) + 1;
+printf("%d\n", a);
+}
+
+
+
+
+

Checking if Signed Number is negative

+
+

binary

+
+

check if the most significant bit is 1

+
+
+
+

hexidecimal

+
+

check if lefmost byte is 8, 9, A, B, C D, E, or F. Break number into multiple +bytes or this will not work. For example 0xd12 would appear to be negative if +you didn’t break it up into byte chunks, doing so you get:

+
+
+
+
0x0d, 0x12
+
+
+
+

or

+
+
+
+
0x0d << 8 | 0x12
+
+
+
+

Notice how the leftmost byte actually starts with a zero. Also notice how each +byte is two digits long.

+
+
+

you can or bytes together to get the original answer by doing

+
+
+
+
byte4 << 24 | byte3 << 16 | byte2 << 8 | byte1
+
+
+
+

In this case I or’ed together 4 bytes to get a 24 bit number. bitshift right 8, +16, 24 are offsets; shifting by one byte, two bytes, and then three.

+
+
+
+
+
+
+

Syntax

+
+
+

ATT vs. Intel Syntax

+
+

gdb uses ATT syntax by default, but you can change it to intel (nasm’s syntax) +with the command set disassembly-flavor intel

+
+
+
+

Strings

+
+

To explicitly state the length of a string:

+
+
+
+
db 27, 'Selling England by the Pound'
+
+
+
+
+

Constant Precomputation

+
+

The math in the code belows is calculated at compile time and turned into a +constant

+
+
+
+
lab: db 0
+
+mov rax, lab + 1 + 2 *3
+
+
+
+
+
+
+

Books

+
+
+
    +
  • +

    "Learn to Program with Assembly by Jonathan Bartlett"

    +
    +
      +
    • +

      learning AT&T syntax with GNU assembler

      +
    • +
    +
    +
  • +
  • +

    "Low-Level Programming by Igor Zhirkov"

    +
    +
      +
    • +

      intel syntax

      +
    • +
    +
    +
  • +
  • +

    "Linkers and Loaders, John R. Levine, 1999, The Morgan Kaufmann Series in +Sofware Engineering and Programming"

    +
  • +
  • +

    "Assembly Language for x86 Processors 6th Edition by Kip R. Irvine"

    +
  • +
  • +

    "Beginning x64 Assembly Programming from Novice to AVX Professional by Jo Van +Hoey"

    +
  • +
+
+
+
+ +
+

Asciidoctor

+
+ +
+
+
+ + + + + \ No newline at end of file diff --git a/endianess.nasm b/endianess.nasm new file mode 100644 index 0000000..a7c7b91 --- /dev/null +++ b/endianess.nasm @@ -0,0 +1,59 @@ +section .data + newline_char: db 10 + codes: db '012345678abcdef' + demo1: dq 0x1122334455667788 + demo2: db 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 + +section .text + +global main + +print_newline: + mov rax, 1 ; 'write' syscall identifier + mov rdi, 1 ; 'stdout' file descriptor + mov rsi, newline_char ; where do we take data from + mov rdx, 1 ; the amount of bytes to write + syscall + ret + +print_hex: ; "function" + ; there are no Calle-saved registers in this function + mov rax, rdi + mov rdi, 1 + mov rdx, 1 + mov rcx, 64 ; how far are we shifting rax? + +iterate: ; this is still part of print_hex function + push rax + sub rcx, 4 + sar rax, cl + + and rax, 0xf + lea rsi, [codes + rax] + + mov rax, 1 + + push rcx + syscall + + pop rcx + pop rax + test rcx, rcx + jnz iterate + + ret + +main: + ; rdi is our one argument + + mov rdi, [demo1] + call print_hex + call print_newline + + mov rdi, [demo2] + call print_hex + call print_newline + + mov rax, 60 + xor rdi, rdi + syscall diff --git a/example2-3.nasm b/example2-3.nasm new file mode 100644 index 0000000..631adb2 --- /dev/null +++ b/example2-3.nasm @@ -0,0 +1,37 @@ +section .data + codes: db '012345678ABCDEF' ; +7 to go to next byte + +section .text +global main +main: + mov rax, 0x1122334455667788 + + mov rdi, 1 ; stdout for print instruction + mov rdx, 1 ; length of one byte + mov rcx, 64 + +.loop: + push rax ; decrements stack and places rax on top of stack + ; 4 bits can fit 16 values for our hex number + sub rcx, 4 + sar rax, cl ; cl = 60, 56, 52, 48, 44, etc + ;get four most significant bits, shift four, reapeat + + and rax, 0xf ; performs and places result in rax + ; rax = 7, 7, 8 + + lea rsi, [codes + rax] ; source for stdout + mov rax, 1 + + push rcx + syscall + pop rcx ; puts value at top of stack in rcx and increments stack + ; puts the old value of rcx before syscall back into rcx + + pop rax ; puts the old value of rax when initialized back into rax + test rcx, rcx ;; set flags from anding these + jnz .loop + + mov rax, 60 + xor rdi, rdi + syscall diff --git a/false.nasm b/false.nasm new file mode 100644 index 0000000..b94f61c --- /dev/null +++ b/false.nasm @@ -0,0 +1,30 @@ +global main +section .data + +test_string: db "abcdef", 0 + +section .text + +strlen: + xor rax, rax + +.loop: + cmp byte [rdi + rax], 0 + + je .end + + inc rax + + jmp .loop + +.end: + ret + +main: + mov rdi, test_string ; first arguemnt for strlen function + call strlen ; the "return" value for strlen in in rax + mov rdi, rax + + mov rax, 60 + syscall + diff --git a/fcalc.nasm b/fcalc.nasm new file mode 100644 index 0000000..1cc1e9e --- /dev/null +++ b/fcalc.nasm @@ -0,0 +1,74 @@ +global main +extern printf + +section .data + number1 dq 9.0 + number2 dq 73.0 + fmt db "The numbers are %f and %f", 10, 0 + + fmtfloat db "%s %f", 10, 0 + f_sum db "The float sum of %f and %f", 10, 0 + f_dif db "The float difference of %f and %f is %f", 10, 0 + f_mul db "The float profuct of %f and %f is %f", 10, 0 + f_div db "The float division of %f by %f is %f", 10, 0 + f_sqrt db "The float squareroot of %f is %f", 10, 0 + +section .text + +main: + push rbp + mov rbp, rsp + + ; print the numbers + + movsd xmm0, [number1] + movsd xmm1, [number2] + mov rdi, fmt + mov rax, 2 ; two floats + call printf + + ; sum + movsd xmm2, [number1] ; double percision float into xmm + addsd xmm2, [number2] ; add double percision to xmm + + ; print the result + movsd xmm0, [number1] + movsd xmm1, [number2] + mov rdi, f_sum + mov rax, 3 ;three floats + call printf + + ; difference + movsd xmm2, [number1] ; double percision float into xmm + subsd xmm2, [number2] ; subtract from xmm + ; print the result + movsd xmm0, [number1] + movsd xmm1, [number2] + mov rdi, f_dif + mov rax, 3 + call printf + + ;division + movsd xmm2, [number1] ; double precision float into xmm + divsd xmm2, [number2] ; divide xmm0 + ; print the result + mov rdi, f_div + movsd xmm0, [number1] + movsd xmm1, [number2] + mov rax, 1 ; one float + call printf + + ; squareroot + sqrtsd xmm1, [number1] + ; print the result + mov rdi, f_sqrt + movsd xmm0, [number1] + mov rax, 2 ; two floats + call printf + + mov rsp, rbp + pop rbp + + mov rax, 60 + xor rdi, rdi + syscall diff --git a/function4.nasm b/function4.nasm new file mode 100644 index 0000000..e8be249 --- /dev/null +++ b/function4.nasm @@ -0,0 +1,76 @@ +extern printf +extern c_area +extern c_circum +extern r_area +extern r_circum + +global pi +global main + +section .data + pi dq 3.141592654 + radius dq 10.0 + side1 dq 4 + side2 dq 5 + fmtf db "%s %f", 10, 0 + fmti db "%s %d", 10, 0 + ca db "The circle area is ", 0 + cc db "The circle circumference is ", 0 + ra db "The rectangle area is ", 0 + rc db "The rectangle circumference is ", 0 + +section .bss + +section .text +main: + push rbp + mov rbp, rsp + ; circle area + movsd xmm0, qword [radius] ; radius xmm0 argument + call c_area ; area returned in xmm0 + ; print the circle area + mov rdi, fmtf + mov rsi, ca + mov rax, 1 + call printf + + ;circle circumference + movsd xmm0, qword [radius] ; radius xmm0 argument + call c_circum ; circumference in xmm0 + ; print the circle circumference + mov rdi, fmtf + mov rsi, cc + mov rax, 1 + call printf + + ; rectangle area + mov rdi, [side1] + mov rsi, [side2] + call r_area + + ; print the rectangle area + mov rdi, fmti + mov rsi, ra + mov rdx, rax + mov rax, 0 + call printf + + ; rectangle circumference + mov rdi, [side1] + mov rsi, [side2] + call r_circum + + ; print the rectangle circumference + + mov rdi, fmti + mov rsi, rc + mov rdx, rax + mov rax, 0 + call printf + + mov rsp, rbp + pop rbp + + mov rax, 60 + xor rdi, rdi + syscall diff --git a/function_test1.nasm b/function_test1.nasm new file mode 100644 index 0000000..0a05cfa --- /dev/null +++ b/function_test1.nasm @@ -0,0 +1,58 @@ +global main +extern printf + +section .data + radius dq 10.0 + +section .bss + +section .text + +area: +section .data + .pi dq 3.141592654 ; local to area +section .text + push rbp + mov rbp, rsp + movsd xmm0, [radius] + mulsd xmm0, [radius] + mulsd xmm0, [.pi] + leave + ret + +circum: +section .data + .pi dq 3.14 ; local to circum +section .text + push rbp + mov rbp, rsp + movsd xmm0, [radius] + addsd xmm0, [radius] + mulsd xmm0, [.pi] + leave + ret + +circle: +section .data + .fmt_area db "The area is %f",10, 0 + .fmt_circum db "The circumference if %f",10, 0 +section .text + push rbp + mov rbp, rsp + call area + mov rdi, .fmt_area + mov rax, 1 ; area in xmm0 + call printf + call circum + mov rdi, .fmt_circum + mov rax, 1 ; circumference in xmm0 + call printf + leave + ret + +main: + push rbp + mov rbp, rsp + call circle + leave + ret diff --git a/hello_world.nasm b/hello_world.nasm new file mode 100644 index 0000000..11d7048 --- /dev/null +++ b/hello_world.nasm @@ -0,0 +1,14 @@ +section .data + msg db "hello, world", 10, 0 +section .bss +section .text + global main +main: + mov rax, 1 + mov rdi, 1 + mov rsi, msg + mov rdx, 13 + syscall + mov rax, 60 + mov rdi, 0 + syscall diff --git a/icalc.nasm b/icalc.nasm new file mode 100644 index 0000000..4b55dc0 --- /dev/null +++ b/icalc.nasm @@ -0,0 +1,56 @@ +global main +extern printf + +section .data + + number1 dq 128 + number2 dq 19 + neg_num dq -12 + fmt db "The numbers are %ld and %ld", 10, 0 + fmtint db "%s %ld", 10, 0 + sumi db "The sum is", 0 + difi db "The difference is", 0 + inci db "Number 1 Incremented:", 0 + deci db "Number 1 Decremented:", 0 + sali db "Number 1 Shift left 2 (x4):", 0 + sari db "Number 1 Shift right 2 (/4):", 0 + sariex db "Number 1 Shift right 2 (/4) with" + db "sign extension:", 0 + multi db "The product is", 0 + divi db "The integer quotient is", 0 + remi db "The modulo is", 0 + +section .bss + resulti resq 1 + modulo resq 2 + +section .text + +main: + push rbp ; function prologe + mov rbp, rsp ; function prologe + +; displaying the numbers + mov rdi, fmt + mov rsi, [number1] + mov rdx, [number2] + mov rax, 0 + call printf + +; adding ------------------------------- + + mov rax, [number1] + add rax, [number2] + + mov rdi, fmtint + mov rsi, sumi + mov rdx, [resulti] + mov rax, 0 + call printf + + mov rsp, rbp ; function epiloge + pop rbp ; function epiloge + + mov rax, 60 + xor rdi, rdi + syscall diff --git a/inline_assembly.c b/inline_assembly.c new file mode 100644 index 0000000..e6fccfe --- /dev/null +++ b/inline_assembly.c @@ -0,0 +1,21 @@ +#include +#include + +int +main(void){ + unsigned int syscall_nr = 1; + int exit_status = 42; + + asm ( + "movl %0, %%eax\n" + "movl %1, %%ebx\n" + "int $0x80" + : /* output parameters, we aren't outputting anything, no none */ + /* (none) */ + : /* input parameters mapped to %0 and %1, repsectively */ + "m" (syscall_nr), "m" (exit_status) + : /* registers that we are "clobbering", unneeded since we are calling exit */ + "eax", "ebx" + ); + +} diff --git a/loop.nasm b/loop.nasm new file mode 100644 index 0000000..22c5f83 --- /dev/null +++ b/loop.nasm @@ -0,0 +1,36 @@ +extern printf + +section .data + fmt: db "number: %d", 10, 0 +section .bss + var resq 1 + +section .text + global main +main: + push rbp ; push function prologe + mov rbp, rsp ; push function prologe + + xor rcx, rcx + mov [var], rcx + +loop: + mov rdi, fmt + mov rsi, [var] + mov rax, 0 + call printf + + mov rcx, [var] + inc rcx + mov [var], rcx + + cmp rcx, 12 + + jl loop + + mov rsp, rbp ; pop function prologe + pop rbp ; pop function prologe + + mov rax, 60 + mov rdi, 0 + syscall diff --git a/memory.nasm b/memory.nasm new file mode 100644 index 0000000..2f98317 --- /dev/null +++ b/memory.nasm @@ -0,0 +1,45 @@ +global main + +section .data + bNum db 123 + wNum dw 12345 + warray times 5 dw 0 + + dNum dd 12345 + qNum1 dq 12345 + text1 db "abc", 0 + qNum2 dq 3.141592654 + text2 db "cde", 0 + +section .bss + bvar resb 1 + dvar resd 1 + wvar resw 10 + qvar resq 3 + +section .text + +main: + push rbp + mov rbp, rsp + + lea rax, [bNum] + mov rax, bNum ;; effectively the same as above + mov rax, [bNum] + mov [bvar], rax ; because rax is bigger than bvar, this can cause issues + lea rax, [bvar] + lea rax, [wNum] + mov rax, [wNum] + lea rax, [text1] + mov rax, text1 + mov rax, text1 + 1 + lea rax, [text1 + 1] + mov rax, [text1] + mov rax, [text1 + 1] + + mov rsp, rbp + pop rbp + + mov rax, 60 + xor rdi, rdi + syscall diff --git a/print_call.nasm b/print_call.nasm new file mode 100644 index 0000000..01ef23b --- /dev/null +++ b/print_call.nasm @@ -0,0 +1,51 @@ +section .data + newline_char: db 10 + codes: db '012345678abcdef' ; what 9? + +section .text + +global main + +print_newline: + mov rax, 1 ; 'write' syscall identifier + mov rdi, 1 ; 'stdout' file descriptor + mov rsi, newline_char ; where do we take data from + mov rdx, 1 ; the amount of bytes to write + syscall + ret + +print_hex: ; "function" + ; there are no Calle-saved registers in this function + mov rax, rdi + mov rdi, 1 + mov rdx, 1 + mov rcx, 64 ; how far are we shifting rax? + +iterate: ; this is still part of print_hex function + push rax + sub rcx, 4 + sar rax, cl + + and rax, 0xf + lea rsi, [codes + rax] + + mov rax, 1 + + push rcx + syscall + + pop rcx + pop rax + test rcx, rcx + jnz iterate + + ret + +main: + mov rdi, 0x1122334455667788 + call print_hex + call print_newline + + mov rax, 60 + xor rdi, rdi + syscall diff --git a/printf.nasm b/printf.nasm new file mode 100644 index 0000000..1a187d7 --- /dev/null +++ b/printf.nasm @@ -0,0 +1,23 @@ +extern printf + +section .data + fmt: db "number: %d", 10, 0 + +section .text + global main + +main: + push rbp ; push function prologe + mov rbp,rsp ; push function prologe + + mov rdi, fmt + mov rsi, 12 + mov rax, 0 + call printf + + mov rsp, rbp ; pop function prologe + pop rbp ; pop function prologe + + mov rax, 60 ; 60 = exit + mov rdi, 0 ; 0 = success exit code + syscall ; quit diff --git a/question_print_int.nasm b/question_print_int.nasm new file mode 100644 index 0000000..8f8d91b --- /dev/null +++ b/question_print_int.nasm @@ -0,0 +1,74 @@ +; QUESTIONS: +; - what is EDX:EAX ? I though the colon just represented offset +; - why does syscall change rcx ? + +section .data + codes: db '0123456789' + temp: times 10 db 0 + +section .text + +global main + +; one argument of rdi +print_int: ; function + push rbx + xor rcx, rcx +.loop: + ; modulo + xor rdx, rdx + mov rax, rdi ; dividend (top) + mov rbx, 10 ; divisor (bottom) + div rbx + mul rbx ; rax * rbx, result i n edx:rax + mov rbx, rdi + sub rbx, rax ; answer in rbx + + ; store value 1 btyte of `codes` somewhere in temp + mov rax, [codes + rbx] + mov qword [temp + rcx], rax + inc rcx + + ; keep dividing by 10 + xor rdx, rdx + mov rax, rdi ; dividend (top) + mov rdi, 10 ; divisor (bottom) + div rdi + mov rdi, rax ; result in rdi + + test rdi, rdi + jnz .loop + +.reverse: + dec rcx + + push rcx + lea rax, [temp + rcx] + mov rsi, rax + + mov rax, 1 ; 'write' syscall + mov rdi, 1 ; stdout file description + + mov rdx, 1 + syscall ; syscall must change rcx in some way + + pop rcx + test rcx, rcx + jnz .reverse + + mov rax, 1 ; 'write' syscall + mov rdi, 1 ; stdout file description + mov rsi, 10 + mov rdx, 1 + syscall + + pop rbx + ret + +main: + mov rdi, 5495 + call print_int + + mov rax, 60 + xor rdi, rdi + syscall diff --git a/question_print_int_new.nasm b/question_print_int_new.nasm new file mode 100644 index 0000000..436da7b --- /dev/null +++ b/question_print_int_new.nasm @@ -0,0 +1,76 @@ +; QUESTIONS: +; - why does syscall change rcx ? + +section .data + codes: db '0123456789' + temp: times 10 db 0 + +section .text + +global main + +; one argument of rdi +print_int: ; function + push rbx + xor rcx, rcx +.loop: + ; modulo + xor rdx, rdx ; clear rdx to clear junk data + mov rax, rdi ; dividend (top) + mov rbx, 10 ; divisor (bottom) + div rbx ; result in RDX:RAX, modulo of division in rdx + + ; store value 1 btyte of `codes` somewhere in temp + mov rax, [codes + rdx] + mov qword [temp + rcx], rax + inc rcx + + ; keep dividing by 10 + xor rdx, rdx + mov rax, rdi ; dividend (top) + mov rdi, 10 ; divisor (bottom) + div rdi + mov rdi, rax ; result in rdi + + test rdi, rdi + jnz .loop + + +.reverse: + lea rax, [temp + rcx - 1] + mov rsi, rax + + mov rax, 1 ; 'write' syscall + mov rdi, 1 ; stdout file description + mov rdx, 1 ; 1 byte long + + push rcx + syscall ; syscall must change rcx in some way + pop rcx + + loop .reverse + + xor rcx, rcx + + mov rax, 1 ; 'write' syscall + mov rdi, 1 ; stdout file description + mov rsi, 10 + mov rdx, 1 + syscall + + pop rbx + ret + +main: + push rbp + mov rbp, rsp + + mov rdi, 5495 + call print_int + + mov rsp, rbp + pop rbp + + mov rax, 60 + xor rdi, rdi + syscall diff --git a/rect.nasm b/rect.nasm new file mode 100644 index 0000000..c53b998 --- /dev/null +++ b/rect.nasm @@ -0,0 +1,31 @@ +global r_circum +global r_area + +section .data +section .bss +section .text + +r_area: + section .text + push rbp + mov rbp, rsp + + mov rax, rsi + imul rax, rdi + mov rsp, rbp + + pop rbp + ret + +r_circum: + section .text + push rbp + mov rbp, rsp + + mov rax, rsi + add rax, rdi + add rax, rax + + mov rsp, rbp + pop rbp + ret diff --git a/stack.nasm b/stack.nasm new file mode 100644 index 0000000..9f9888f --- /dev/null +++ b/stack.nasm @@ -0,0 +1,61 @@ +global main +extern printf + +section .data + string db "ABCDE", 0 + stringLen equ $ - string - 1 + fmt1 db "The original string: %s", 10, 0 + fmt2 db "The reversed string: %s", 10, 0 + +section .bss + +section .text +main: + push rbp + mov rbp, rsp + + ; print the original string + mov rdi, fmt1 + mov rsi, string + mov rax, 0 + call printf + + ; push the stirng char per char on the stack + xor rax, rax + mov rbx, string + mov rcx, stringLen + mov r12, 0 + + pushLoop: + mov al, byte [rbx + r12] + push rax + inc r12 + loop pushLoop + + ; pop the string char per char from the stack + ; this will reverse the original string + + mov rbx, string + mov rcx, stringLen + mov r12, 0 + + popLoop: + pop rax + mov byte [rbx + r12], al + inc r12 + loop popLoop + + mov byte [rbx + r12], 0 ; terminate string with 0 + + ; Print the reversed string + mov rdi, fmt2 + mov rsi, string + mov rax, 0 + call printf + + mov rsp, rbp + pop rbp + + mov rax, 60 + xor rdi, rdi + syscall diff --git a/strlen.nasm b/strlen.nasm new file mode 100644 index 0000000..5e0e922 --- /dev/null +++ b/strlen.nasm @@ -0,0 +1,29 @@ +global main + +section .data +test_string: db "abcdef", 0 + +section .text + +strlen: + xor rax, rax +.loop: + cmp byte [rdi + rax], 0 + + je .end + + inc rax + + jmp .loop + +.end: + ret + +main: + mov rdi, test_string ; first arguemnt for strlen function + call strlen ; the "return" value for strlen in in rax + mov rdi, rax + + mov rax, 60 + syscall + diff --git a/tst.nasm b/tst.nasm new file mode 100644 index 0000000..e992177 --- /dev/null +++ b/tst.nasm @@ -0,0 +1,32 @@ +section .data + codes: db '012345678ABCDEF' + +section .text +global main +main: + mov rax, 0x01122334455667788 + + mov rdi, 1 + mov rdx, 1 + mov rcx, 64 + +.loop: + push rax + sub rcx, 4 + sar rax, cl + and rax, 0xf + + lea rsi, [codes + rax] + mov rax, 1 + + push rcx + syscall + pop rcx + + pop rax + test rcx, rcx + jnz .loop + + mov rax, 60 + xor rdi, rdi + syscall diff --git a/write_number.nasm b/write_number.nasm new file mode 100644 index 0000000..7378867 --- /dev/null +++ b/write_number.nasm @@ -0,0 +1,23 @@ +extern printf + +section .data + fmt: db "number %d", 10, 0 + thing: dq 88 +section .bss +section .text + global main +main: + push rbp ; push function prologe + mov rbp,rsp ; push function prologe + + mov rdi, fmt + mov rsi, [thing] + mov rax, 0 + call printf + + mov rsp, rbp ; pop function prologe + pop rbp ; pop function prologe + + mov rax, 60 + mov rdi, 0 + syscall