**Write-up for** [Assembly Crash Course](https://pwn.college/cse466-f2022/assembly-crash-course)
---
**Challenge 1: Set a register**
---
* Vì đây là level đầu nên yêu cầu đơn giản: gán giá trị cho trước vào thanh ghi được chỉ định
```text
In this level you will work with registers_use! Please set the following:
* rdi = 0x1337
```
* Đơn giản là dùng mov để gán 0x1337 cho $rdi
```nvim
_start:
mov rdi, 0x1337
```
**Challenge 2: Addition**
---
* Ở level này yêu cầu tăng thanh ghi chỉ định thêm 1 giá trị cho trước
```text
Do the following:
* add 0x331337 to rdi
We will now set the following in preparation for your code:
rdi = 0xc99
```
* Vì thanh ghi $rdi đã set sẵn = 0xc99, nên nếu dùng mov $rdi, 0x331337 như level trước sẽ chỉ gán $rdi = 0x331337 chứ không phải $rdi = $rdi + 0x331337.
* Có thể dùng add để tăng $rdi thêm 0x331337 hoặc dùng mov để gán $rdi = 0x331337+0xc99 = 331fd0
```nvim
_start:
add rdi, 0x331337
```
```nvim
_start:
mov rdi, 0x331fd0
```
**Challenge 3: Multiplication**
---
* Ở level trước đã làm quen với toán tử cộng, level này sẽ làm quen với toán tử nhân: imul
```text
Using your new knowledge, please compute the following:
f(x) = mx + b, where:
m = rdi
x = rsi
b = rdx
Place the value into rax given the above.
We will now set the following in preparation for your code:
rdi = 0x8ce
rsi = 0x1bd1
rdx = 0x1feb
```
* Đề yêu cầu ta tính phương trình ax + b với a là m được set tại $rdi, x được set tại $rsi và b được set tại $rdx
* Hướng giải quyết: dùng imul để nhân $rdi với $rsi, gán vào $rax rồi cộng $rax thêm $rdx
`imul $a, $b: gán $a = $a * $b`
```nvim
_start:
imul rdi, rsi
mov rax, rdi
add rax, rdx
```
**Challenge 4: Division**
---
* Có nhân thì sẽ có chia, level này sẽ giúp tiếp cận phép chia trong assembly
```text
Please compute the following:
speed = distance / time, where:
distance = rdi
time = rsi
Place the value of speed into rax given the above.
We will now set the following in preparation for your code:
rdi = 0x64f
rsi = 0x5f
```
* Như đề yêu cầu, nhiệm vụ của ta là gán $rax = $rdi / $rsi, tuy nhiên có chút khác biệt giữa phép chia và các phép toán trước đó
`idiv a: chỉ có 1 toán tử a, mặc định sẽ lấy $rax/a, trả phần nguyên về $rax, trả phần dư về $rdx. Yêu cầu $rdx = 0 để kết quả được chính xác`
* Tiếp cận: vì idiv chỉ lấy giá trị của $rax làm số bị chia, nên để thực hiện phép chia $rdx / $rsi, ta phải gán $rax = $rdx trước khi chia. Kết quả cũng tự động gán vào rax nên không cần thao tác thêm
```nvim
_start:
mov rax, rdx
mov rdx, 0
idiv rsi
```
**Challenge 5: Modulus**
---
* Level trước là chia lấy phần nguyên, level này sẽ tính toán chia lấy phần dư
```text
Please compute the following:
rdi % rsi
Place the value in rax.
We will now set the following in preparation for your code:
rdi = 0x39a66f69
rsi = 0x3f
```
* Như thông tin đã có ở level trước, phần dư của phép div sẽ lưu vào $rdx. Vậy nên sau khi thực hiện phép chia thì nhiệm vụ của ta chỉ còn là gán $rdx vào $rax
```nvim
_start:
mov rax, rdi
mov rdi, 0
idiv rsi
mov rax, rdi
```
**Challenge 6: Register sizes**
---
* Level này sẽ tìm hiểu về cách thanh ghi lưu trữ dữ liệu
```text
Using only the following instruction(s)
mov
Please compute the following:
rax = rdi modulo 256
rbx = rsi modulo 65536
We will now set the following in preparation for your code:
rdi = 0x2177
rsi = 0xdf5418a7
```
* Tuy yêu cầu chia lấy dư có thể thực hiện bằng idiv như level trước, chỉ có thể sử dụng mov ở level này
* 256 và 65536 lần lượt là 2^8^ và 2^16^ tương ứng với 100000000~2~(8 bit 0) và 10000000000000000~2~(16 bit 0)
=> Phần dư khi chia 256 và 65536 sẽ lần lượt là 8 và 16 bit cuối cùng của thanh ghi lưu số bị chia
* Các thanh ghi có thể truy cập những đoạn nhỏ hơn của chính nó bằng tên như sơ đồ dưới
```
+----------------------------------------+
| rax (64 bits) |
+--------------------+-------------------+
| eax (32 bits) |
+---------+---------+
| ax |
+----+----+
| ah | al |
+----+----+
Đối với 4 thanh ghi rax, rbx, rcx, rdx sẽ có dạng như sau:
r_x: 64bit
e_x: 32bit
_x: 16bit
_h, _l: 8bit
Các thanh ghi khác sẽ có tên gọi khác cho các thanh ghi con của nó
```
* Kích thước của thanh ghi ax là 16 bit, al, ah lần lượt là 8 bit. Trong đó ta cần những bit cuối cùng nên giữa al và ah ta sẽ lấy al
+ Đáp án cho phép mod $rdi với 256 sẽ là $dil
+ Đáp án cho phép mod $rsi với 65536 sẽ là $si
```nvim
_start:
mov rax, dil
mov rbx, si
```
**Challenge 7: Bitwise shift**
---
* Sau các phép toán bình thường sẽ là các thao tác bit
```text
Using only the following instructions:
mov, shr, shl
Please perform the following:
Set rax to the 5th least significant byte of rdi
i.e.
rdi = | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
Set rax to the value of B4
We will now set the following in preparation for your code:
rdi = 0x91affdcbc43713f0
```
* Ở level này sẽ dùng phép dịch bit để lấy giá trị tại byte được chỉ định
* Vì chỉ có thể sử dụng mov, shl, shr nên ta sẽ dịch sao cho B4 về cuối thanh ghi, sau đó lấy $dil gán vào $al, vậy là đã gán được byte số 4 vào $rax
```nvim
_start:
shr rdi, 32
mov al, dil
```
**Challenge 8: Bitwise and**
---
```text
Without using the following instructions:
mov, xchg
Please perform the following:
rax = rdi AND rsi
i.e. Set rax to the value of (rdi AND rsi)
We will now set the following in preparation for your code:
rdi = 0x72a2e58ce98b4813
rsi = 0x67bba0a6868b145c
```
* Ở bài này đã bị cấm lệnh mov, vậy nên không thể gán $rax như cách bình thường được
* Tuy nhiên ta có tính chất:
+ a xor a = 0
+ a xor b = b(a = 0)
=> Có thể dùng phép xor để gán $rax = rdi, sau đó and $rax với $rsi
```nvim
_start:
xor rax, rax
xor rax, rdi
and rax, rsi
```
**Challenge 9: Bitwise logic**
---
```text
Using only the following instructions:
and, or, xor
Implement the following logic:
if x is even then
y = 1
else
y = 0
where:
x = rdi
y = rax
We will now set the following in preparation for your code:
rdi = 0x3292dda5
```
* Như tên level, chúng ta phải hiểu được logic của bit và áp dụng.
* Trong đó, tính chẵn lẻ của 1 số sẽ được quyết định bởi bit 2^0^ của số. Nếu bit bật thì số là số lẻ, nếu không số là số chẵn
* Nếu $rdi chẵn thì $rax = 1, $rdi lẻ thì $rax = 0. Mà chỉ được dùng and, or, xor nên sẽ dùng and 1 để kiểm tra bit 2^0^ là 1 hay 0, sau đó xor với 1 để đảo bit 1 -> 0, 0 -> 1
```nvim
_start:
and rdi, 1
xor rax, rax
xor rax, rdi
xor rax, 1
```
**Challenge 10: Memory reads and writes**
---
```text
Please perform the following:
1. Place the value stored at 0x404000 into rax
2. Increment the value stored at the address 0x404000 by 0x1337
Make sure the value in rax is the original value stored at 0x404000 and make sure
that [0x404000] now has the incremented value.
We will now set the following in preparation for your code:
[0x404000] = 0x19be30
```
* Level này chỉ đơn giản là để ta làm quen thao tác với dữ liệu tại địa chỉ, trong đó, kiến thức về địa chỉ cũng đã được cung cấp trước khi đọc đề nên có thể dễ dàng cài đặt
```nvim
_start:
mov rax, [0x404000]
mov rbx, 0x1337
add [0x404000], rbx
```
**Challenge 11: Data sizes**
---
```text
Please perform the following:
1) Set rax to the byte at 0x404000
2) Set rbx to the word at 0x404000
3) Set rcx to the double word at 0x404000
4) Set rdx to the quad word at 0x404000
We will now set the following in preparation for your code:
[0x404000] = 0x1aea24
```
* Đề yêu cầu ta lấy các dữ liệu có kích thước khác nhau tại địa chỉ cho trước, lưu lần lượt vào các thanh ghi
* Có 1 điều cần lưu ý là khi mov dữ liệu từ địa chỉ vào thanh ghi, kích thước dữ liệu được lấy sẽ trùng với kích thước thanh ghi
`Quad word: thanh ghi 64bit`
`Double word: thanh ghi 32bit`
`Word: thanh ghi 16bit`
`Byte: thanh ghi 8bit`
=> Từ đó việc cài đặt đơn giản như sau
```nvim
_start:
mov al, [0x404000]
mov bx, [0x404000]
mov ecx, [0x404000]
mov rdx, [0x404000]
```
**Challenge 12: Dynamic address memory writes**
---
```text
For this challenge we will give you two addresses created dynamically each run. The first address
will be placed in rdi. The second will be placed in rsi.
Using the earlier mentioned info, perform the following:
1. set [rdi] = 0xdeadbeef00001337
2. set [rsi] = 0xc0ffee0000
Hint: it may require some tricks to assign a big constant to a dereferenced register. Try setting
a register to the constant then assigning that register to the derefed register.
We will now set the following in preparation for your code:
[0x404260] = 0xffffffffffffffff
[0x4049f8] = 0xffffffffffffffff
rdi = 0x404260
rsi = 0x4049f8
```
* Tuy đề có vẻ giống ở level trước, nhưng việc set dữ liệu tại các địa chỉ sẽ cần đến thanh ghi, vì vậy dựa vào kích thước dữ liệu gán vào địa chỉ, lựa chọn thanh ghi phù hợp để lưu dữ liệu rồi gán vào địa chỉ
`0xdeadbeef00001337 có 64bit ở dạng nhị phân -> thanh ghi 64bit`
`0xc0ffee0000 có 40bit ở dạng nhị phân -> thanh ghi 64bit`
```nvim
_start:
mov rax, qword 0xdeadbeef00001337
mov rbx, qword 0xc0ffee0000
mov [rdi], rax
mov [rsi], rbx
```
**Challenge 13: Consecutive memory reads**
---
```text
Perform the following:
1. load two consecutive quad words from the address stored in rdi
2. calculate the sum of the previous steps quad words.
3. store the sum at the address in rsi
We will now set the following in preparation for your code:
[0x4041e8] = 0x9bd96
[0x4041f0] = 0x20417
rdi = 0x4041e8
rsi = 0x404670
```
* Như đã được học ở những level trước, quadword sẽ lưu vào thanh ghi 64bit, do đó chỉ đơn giản là dùng 2 thanh ghi 64bit lấy dữ liệu tại đại chỉ chỉ định, sau đó gán vào địa chỉ lưu tại $rsi
```nvim
_start:
mov rax, [rdi]
add rax, [rdi + 8]
mov [rsi], rax
```
**Challenge 14: The stack**
---
```text
Replace the top value of the stack with (top value of the stack - rdi).
We will now set the following in preparation for your code:
rdi = 0x1765
(stack) [0x7fffff1ffff8] = 0x3259ce5
```
* Stack là kiểu dữ liệu first in last out có nhiều tài liệu trên internet để đọc hiểu
* Trong assembly thì stack cũng có cơ chế hoạt động tương tự. Ở level này ta cần lấy dữ liệu ở top của stack, sau đó đưa dữ liệu sau khi tính toán vào lại stack
`pop $a sẽ gán dữ liệu tại top của stack cho $a và xóa top khỏi stack`
`push $a sẽ đưa giá trị tại $a lên top của stack`
```nvim
_start:
pop rax
sub rax, rdi
push rax
```
**Challenge 15: Swap register values with the stack**
---
* Level này tiếp tục dùng để hiểu thêm về stack
```
Using only following instructions:
push, pop
Swap values in rdi and rsi.
i.e.
If to start rdi = 2 and rsi = 5
Then to end rdi = 5 and rsi = 2
We will now set the following in preparation for your code:
rdi = 0x3ab9d1ed
rsi = 0xad836d7
```
* Như đã nói, stack có cơ chế first in last out, hay là vào trước thì ra sau, vậy nên nếu đưa a vào trước b thì b sẽ ra trước a
* Vì chỉ có thể dùng push, pop tại level này nên ta có thể sử dụng cơ chế này của stack để đổi 2 giá trị của 2 thanh ghi được chỉ định
```nvim
_start:
push rdi
push rsi
pop rdi
pop rsi
```
**Challenge 16: Memory reads and writes with the stack**
---
```
Without using pop please calculate the average of 4 consecutive quad words stored on the stack.
Push the average on the stack. Hint:
RSP+0x?? Quad Word A
RSP+0x?? Quad Word B
RSP+0x?? Quad Word C
RSP Quad Word D
We will now set the following in preparation for your code:
(stack) [0x7fffff200000:0x7fffff1fffe0]
= ['0x1216cdb5', '0x5e5b770', '0x29e46e6b', '0x1ce25588'] (list of things)
```
* Để hiểu được lấy giá trị từ stack mà không push, pop thì ta cần phải hiểu $rsp, $rbp trước
`Để đơn giản thì $rsp là thanh ghi lưu địa chỉ của phần tử ở top trong stack, $rbp là thanh ghi lưu địa chỉ của phần tử ở bot trong stack`
* Thực ra ở level này chưa cần hiểu vì đề yêu cầu lấy dữ liệu từ các địa chỉ được chỉ định sẵn như ở level 11 sau đó tính toán
```nvim
_start:
mov rax, [rsp]
add rax, [rsp + 8]
add rax, [rsp + 16]
add rax, [rsp + 24]
mov rbx, 4
div rbx
push rax
```
**Challenge 17: Control flow**
---
```
Useful instructions for this level is:
jmp (reg1 | addr | offset) ; nop
Hint: for the relative jump, lookup how to use `labels` in x86.
Using the above knowledge, perform the following:
Create a two jump trampoline:
1. Make the first instruction in your code a jmp
2. Make that jmp a relative jump to 0x51 bytes from its current position
3. At 0x51 write the following code:
4. Place the top value on the stack into register rdi
5. jmp to the absolute address 0x403000
We will now set the following in preparation for your code:
- Loading your given code at: 0x4000ce
- (stack) [0x7fffff1ffff8] = 0xeb
```
* Để hiểu về jump thì ở level hiện tại, jump là để ta chuyển chương trình hiện tại đến địa chỉ được chọn và tiếp tục thực thi. Nôm na thì nó sẽ bỏ qua 1 đoạn code giữa địa chỉ hiện tại và địa chỉ đích và tiếp tục chương trình bắt đầu từ địa chỉ đích
`Mình stuck khá lâu ở bài này vì khi jump đến địa chỉ $+0x51 thì bị lỗi, các câu lệnh cần được viết đủ nhiều để jump có thể skip nhưng nop rồi jump vẫn bị lỗi. Chỉ có jump đến hàm được chỉ định, sau đó thay đổi dần dần số lần nop đến khi nó jump đến đúng địa chỉ được yêu cầu rồi mới thực hiện các lệnh bình thường được`
```nvim
_start:
jmp solve
times 0x51 nop
solve:
pop rdi
mov rax, 0x403000
jmp rax
```
**Challenge_18: Conditional branches**
---
```
Using the above knowledge, implement the following:
if [x] is 0x7f454c46:
y = [x+4] + [x+8] + [x+12]
else if [x] is 0x00005A4D:
y = [x+4] - [x+8] - [x+12]
else:
y = [x+4] * [x+8] * [x+12]
where:
x = rdi, y = rax. Assume each dereferenced value is a signed dword. This means the values can start as
a negative value at each memory position.
A valid solution will use the following at least once:
jmp (any variant), cmp
We will now run multiple tests on your code, here is an example run:
- (data) [0x404000] = {4 random dwords]}
- rdi = 0x404000
```
* Level này sé bắt đầu sử dụng đến việc jmp có điều kiện, bằng cmp hoặc test đã được giới thiệu đầy đủ trước khi vào đề, chỉ việc cài đặt điều kiện nữa thôi
`Mình không hiểu tại sao kết quả 32bit lưu trong thanh ghi 64bit bị lỗi vì 32bit sau của thanh ghi không = 0, đổi qua lưu bằng thanh ghi 32bit thì sai kết quả nên mình phải shl rax 32; shr rax, 32 để xóa 32bit sau của thanh ghi thì kết quả mới đúng`
```nvim
section .text
global _start
_start:
mov ebx, [rdi]
mov eax, [rdi + 4]
mov ecx, 0x7f454c46
mov edx, 0x00005A4D
cmp ebx, ecx
je Case_1
cmp ebx, edx
je Case_2
mov ebx, [rdi + 8]
imul rax, rbx
mov ebx, [rdi + 12]
imul rax, rbx
jmp END
Case_1:
mov ebx, [rdi + 8]
add rax, rbx
mov ebx, [rdi + 12]
add rax, rbx
jmp END
Case_2:
mov ebx, [rdi + 8]
sub rax, rbx
mov ebx, [rdi + 12]
sub rax, rbx
jmp END
END:
shl rax, 32
shr rax, 32
```
**Challenge 19: Jump tables**
---
```
Using the above knowledge, implement the following logic:
if rdi is 0:
jmp 0x403031
else if rdi is 1:
jmp 0x4030d2
else if rdi is 2:
jmp 0x40319c
else if rdi is 3:
jmp 0x40329f
else:
jmp 0x40334c
Please do the above with the following constraints:
- assume rdi will NOT be negative
- use no more than 1 cmp instruction
- use no more than 3 jumps (of any variant)
- we will provide you with the number to 'switch' on in rdi.
- we will provide you with a jump table base address in rsi.
Here is an example table:
[0x404344] = 0x403031 (addrs will change)
[0x40434c] = 0x4030d2
[0x404354] = 0x40319c
[0x40435c] = 0x40329f
[0x404364] = 0x40334c
```
* Lần này vẫn là cài đặt điều kiện nhưng khó hơn trong việc nhảy đến các địa chỉ được chỉ định lưu ở địa chỉ ban đầu tại $rsi
`Mình hoàn toàn bất lực vì jmp đến [rsi + rdi * 8] luôn bị lỗi read unmapped ;-;. Mình thử nhiều cách và rồi nop trước khi cmp và jmp là cách đưa mình đến cái flag mà không dính thêm cái lỗi ngáo ngơ nào`
```nvim
_start:
mov rcx, rdi
times 0x301 nop
mov rax, 4
cmp rcx, rax
jge Case_0
mov rbx, [rsi + rcx * 8]
jmp rbx
Case_0:
mov rbx, [rsi + 4 * 8]
jmp rbx
```
`P/S: Tham khảo writeup của các bạn khác thì các bạn lại có thể jmp và lấy flag mà không có 1 lỗi nào, ảo lòi 4 tiếng debug cái lỗi read unmapped`
**Challenge 20: Computing averages**
---
```
Please compute the average of n consecutive quad words, where:
rdi = memory address of the 1st quad word
rsi = n (amount to loop for)
rax = average computed
We will now set the following in preparation for your code:
- [0x404250:0x404570] = {n qwords]}
- rdi = 0x404250
- rsi = 100
```
* Dù đề chỉ đơn giản là yêu cầu tính trung bình cộng của n phần tử, có thể cài đặt bằng cách dùng điều kiện lặp bth, nhưng phải để ý đến chuyện tràn số.
* Nếu cộng liên tục n số 64bit thì sẽ bị tràn, vậy nên thay vì tính $\frac{a_{1} + a_{2} +... a_{n}}{n}$ để phần tổng ở trên bị tràn thì ta chuyển tách ra thành $\frac{a_{1}}{n}$ + $\frac{a_{2}}{n}$ + ... $\frac{a_{n}}{n}$ để chống tràn
`Vì các số đều nằm trong giới hạn 64bit nên kết quả cuối luôn không bị tràn khỏi 64bit`
`Tuy nhiên vì lúc đọc đề mình không rõ liệu chỉ n chỉ ở 100 số hay nhiều hơn và cũng chưa xét đến tính chất nêu trên nên cài đặt theo hướng khác khá phức tạp.`
* Code ở dưới đây là mình cài đặt theo hướng giới hạn số không vượt quá 2^63^ để cộng lại không bị tràn.
`Ratkhodam, nên tham khảo code từ nguồn wu khác`
```nvim
section .text
global _start
_start:
mov rcx, 0xffffffffffffffff/2
mov rbx, 0
mov r10, 0
mov rdx, 0
mov rax, 0
jmp Cin
Cin:
cmp rdx, rsi
je END
mov r10, [rdi]
add rdi, 0x8
jmp solve
Cal_r:
sub r10, rcx
inc rbx
jmp solve
Cal_a:
sub rax, rcx
inc rbx
jmp solve
solve:
cmp r10, rcx
jge Cal_r
cmp rax, rcx
jge Cal_a
add rax, r10
xor r10, r10
inc rdx
cmp rax, rcx
jge Cal_a
jmp Cin
END:
xor rdx, rdx
xor r8, r8
xor r9, r9
xor r11, r11
xor r12, r12
idiv rsi ; rax = a/n, rdx = remain
mov r8, rax
mov r9, rdx
xor rdx, rdx
mov rax, rcx
idiv rsi ; rax = c/n, rdx = remain
mov r11, rax
mov r12, rdx
xor rdx, rdx
xor rax, rax
mov r13, rbx
imul r13, r11
mov r14, rbx
imul r14, r12
add r8, r13
add r9, r14
mov rax, r9
idiv rsi
add r8, rax
mov rax, r8
```
**Challenge 21: Implementing strlen**
---
```text
Using the above knowledge, please perform the following:
Count the consecutive non-zero bytes in a contiguous region of memory, where:
rdi = memory address of the 1st byte
rax = number of consecutive non-zero bytes
Additionally, if rdi = 0, then set rax = 0 (we will check)!
An example test-case, let:
rdi = 0x1000
[0x1000] = 0x41
[0x1001] = 0x42
[0x1002] = 0x43
[0x1003] = 0x00
then: rax = 3 should be set
We will now run multiple tests on your code, here is an example run:
- (data) [0x404000] = {10 random bytes},
- rdi = 0x404000
```
* Mọi thứ theo đề là chúng ta sẽ đọc các byte liên tiếp từ byte được cho tại địa chỉ lưu trong $rdi, đến khi byte = 0 thì dừng và in ra số lượng byte đã đếm
* Kết hợp conditional jump và read address từ các level trước, level này hoàn toàn áp dụng việc cài đặt một cách đơn giản:
```nvim
_start:
xor rax, rax
xor rbx, rbx
cmp rdi, 0
je END
mov rbx, [rdi]
inc rdi
jmp solve
solve:
cmp rbx, 0
je END
inc rax
mov rbx, [rdi]
inc rdi
jmp solve
END:
```
**Challenge 22: Using libary functions**
---
```text
Please implement the following logic:
str_lower(src_addr):
i = 0
if src_addr != 0:
while [src_addr] != 0x00:
if [src_addr] <= 0x5a:
[src_addr] = foo([src_addr])
i += 1
src_addr += 1
return i
foo is provided at 0x403000. foo takes a single argument as a value and returns a value.
All functions (foo and str_lower) must follow the Linux amd64 calling convention (also known as System V AMD64 ABI):
https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI
Therefore, your function str_lower should look for src_addr in rdi and place the function return in rax.
An important note is that src_addr is an address in memory (where the string is located) and [src_addr] refers to the byte that exists at src_addr.
Therefore, the function foo accepts a byte as its first argument and returns a byte.
We will now run multiple tests on your code, here is an example run:
- (data) [0x404000] = {10 random bytes},
- rdi = 0x404000
```
* Bình thường ta sẽ viết 1 chương trình như ở hàm main, chương trình chính, tuy nhiên level này lại yêu cầu ta cài đặt 1 chương trình như 1 hàm được gọi theo quy tắc gọi hàm (calling convention). Trong đó tài liệu về calling convention cũng như stack, những kiến thức liên quan được cung cấp trong đề khá ít, đọc thêm từ [Assembly Calling Convention](https://cs61.seas.harvard.edu/site/2018/Asm2/) để hiểu thêm về cách truyền tham số, cơ chế hoạt động của stack khi gọi hàm
* Level này không level trước bao nhiêu, nhưng đặc biệt lưu ý ret ở cuối chương trình vì đây là 1 hàm, nếu không ret thì ctrinh sẽ không thể quay về lúc trước khi gọi hàm `(90% của 1 tuần mình debug bài này cũng là do không biết hàm phải có ret ở cuối)`
```nvim
_start:
mov rax, 0
mov r8, 0x403000
cmp rdi, 0
je last
call str_lower
str_lower:
jmp solve
solve:
mov rax, 0
mov al, byte [rdi]
cmp al, 0x0
je last
cmp al, 0x5a
jle con
inc rdi
jmp solve
con:
inc rbx
push rdi
mov rax, 0
mov al, byte [rdi]
mov rdi, 0
mov dil, al
call r8
pop rdi
mov byte [rdi], al
jmp solve
ret
last:
mov rax, rbx
ret
```
**Challenge 23: Compute the most common byte**
---
```text
Notice how rbp is always used to restore the stack to where it originally was. If we don't restore
the stack after use, we will eventually run out TM. In addition, notice how we subtracted from rsp
since the stack grows down. To make it have more space, we subtract the space we need. The ret
and call still works the same. It is assumed that you will never pass a stack address across functions,
since, as you can see from the above use, the stack can be overwritten by anyone at any time.
Once, again, please make function(s) that implements the following:
most_common_byte(src_addr, size):
i = 0
for i <= size-1:
curr_byte = [src_addr + i]
[stack_base - curr_byte] += 1
b = 0
max_freq = 0
max_freq_byte = 0
for b <= 0xff:
if [stack_base - b] > max_freq:
max_freq = [stack_base - b]
max_freq_byte = b
b += 1
return max_freq_byte
Assumptions:
- There will never be more than 0xffff of any byte
- The size will never be longer than 0xffff
- The list will have at least one element
Constraints:
- You must put the "counting list" on the stack
- You must restore the stack like in a normal function
- You cannot modify the data at src_addr
```
* Sau tất cả thì đây là level mà mình thấy chill nhất (vì khi đã học đủ từ 22 level trước thì level này không có gì quá khó khăn)
* Điều duy nhất cần quan tâm ở đây là khai báo bộ nhớ (khoảng byte trống cho hàm mình đang viết)
* Để ý thì for đầu tiên, địa chỉ tối đa ta truy cập sẽ là src_addrr + size, tuy nhiên đây là những byte lưu biến nhập vào hàm nên không cần khai báo
* Ở for thứ 2, stack base sẽ là stack_base - b, b <= 0xff => khai báo thêm > 0xff byte cho hàm để truy cập (Tuy nhiên khai báo thêm 0xff byte để chắc chắn không bị lỗi read_unmapped như nhiều lần thử ở trước)
```nvim
_start:
push rbp
mov rbp, rsp
sub rsp, 0x1fffe
mov rbx, 0
cmp rbx, rsi
jl build
build:
mov r10b, byte [rdi + rbx]
mov r11, rbp
sub r11, r10
mov r12, [r11]
inc r12
mov [r11], r12
inc rbx
cmp rbx, rsi
jle build
jmp solve
sub r11, r10
mov r12, [r11]
inc r12
mov [r11], r12
inc rbx
cmp rbx, rsi
jle build
jmp solve
solve:
mov rbx, 0
mov rcx, 0
mov rax, 0
jmp count
count:
cmp rbx, 0xff
jg end
mov r10, rbp
sub r10, rbx
mov r11b, byte [r10]
cmp r11, rcx
jle tmp
mov rcx, r11
mov rax, rbx
jmp tmp
tmp:
inc rbx
jmp count
end:
mov rsp, rbp
pop rbp
ret
```