--- title: 'Assembly Writeup (pwn.college)' --- Visit Course: https://pwn.college/computing-101/ --- # Most common byte **Problem statement:** First of all, the concept of new register is introduced in this problem which is RBP (Stack Base Pointer). It is used to to tell us where the stack frame first initialized from which is used to construct list. ```assembly ; setup the base of the stack as the current top mov rbp, rsp ; move the stack 0x14 bytes (5 * 4) down ; acts as an allocation sub rsp, 0x14 ; assign list[2] = 1337 mov eax, 1337 mov [rbp-0xc], eax ; do more operations on the list ... ; restore the allocated space mov rsp, rbp ret ``` As you can see, the rbp save the current rsp address which is used as the highest address in the contiguous list. Then substract rsp by 0x14 in order to initialize the list size. Using displacement (offset) to interact with list element (modify it) >[!Warning] >We have to restore the stack to its base initialization or it will run out. As the aforemention, substract the exact willing list size. Now the real problem statement is to immplement this status satisify this ```c++ most_common_byte(src_addr, size): i = 0 while i <= size-1: curr_byte = [src_addr + i] [stack_base - curr_byte * 2] += 1 i += 1 b = 0 max_freq = 0 max_freq_byte = 0 while b <= 0xff: if [stack_base - b * 2] > max_freq: max_freq = [stack_base - b * 2] max_freq_byte = b b += 1 return max_freq_byte ``` For more information read the full problem in lieu of these summary lines. **Solution**: ```assembly .intel_syntax noprefix .global _start .most_common_byte: # rdi is scr_addr, rsi is size # init rcx as a counter xor rcx, rcx push rbp mov rbp, rsp sub rsp, 600 .while_restore: cmp rcx, 512 jnb .exit_while_restore mov rax, rbp sub rax, rcx sub rax, 8 mov WORD PTR [rax], 0 add rcx, 2 jmp .while_restore .exit_while_restore: # ------------------------------ xor rcx, rcx .while_loop_1: cmp rcx, rsi jae .exit_loop_1 movzx eax, BYTE PTR [rdi + rcx] mov edx, 2 mul edx mov rdx, rbp sub rdx, rax sub rdx, 8 inc WORD PTR [rdx] xor rdx, rdx inc rcx jmp .while_loop_1 .exit_loop_1: # ------------------------------ # ------------------------------ xor rcx, rcx # b xor r9, r9 # max_freq xor r10, r10 # max_freq_byte .while_loop_2: cmp rcx, 0xff ja .exit_loop_2 .if: mov eax, ecx mov edx, 2 mul edx mov rdx, rbp sub rdx, rax sub rdx, 8 cmp WORD PTR [rdx], r9w jna .exit_if movzx r9, WORD PTR [rdx] mov r10, rcx .exit_if: inc rcx jmp .while_loop_2 .exit_loop_2: # ------------------------------ mov rax, r10 mov rsp, rbp pop rbp ret _start: call .most_common_byte ``` Well, I first initialize the most_common_bytes function and call it. Inside the function, I divided into three part. The first is to allocate list memory on stack as well as assigning all value equal to zero for further calculation. In order not to against the Call Convention. We have to accept the registe RDI as the scr_addr and RSI as the size in the function parameters. During the whole assembling process. There is a few notes that you may need to notice. >[!Note] >* At first glance, everyone knew it a Call Convention >* Secondly, RBP is a callee-saved register. So you must rollback to its base value >* Furthermore, when dereference the register, you have to specify its type. For example, `BYTE PTR`, `WORD PTR`, `DWORD PTR`, `QWORD PTR` for CPU to understand it. Noticely, [register] need to judge as a bytes and each value stored in stack is considered as 2-byte data which is DWORD. >* Lastly, don't forget RAX is our last return value and the `ret` in the end. The final flag is: `pwn.college{4w3wgqB5EEkY_TgOeuJsNS4zGGl.dZTMywiN3UTNzEzW}` ``` ```