![image](https://hackmd.io/_uploads/BJBJvdQZ0.png) > Date: XX/11/2023 Flow control Sure, here are some common registers in the x86 and x86-64 architectures and their typical usages: **EAX/RAX (Accumulator):** This register is often used in arithmetic and logic operations. It’s also used for I/O port access, and interrupt calls4. In the context of function calls, it’s used to store the return value4. **EBX/RBX (Base):** This register is often used as a base pointer for memory access1. **ECX/RCX (Counter):** This register is often used as a loop counter and in shift/rotate instructions3. **EDX/RDX (Data):** This register is used in arithmetic and I/O operations3. It’s also used with EAX/RAX for multiply and divide operations involving large values2. **ESI/RSI (Source Index) and EDI/RDI (Destination Index):** These registers are used for string and array operations1. In the context of function calls, they’re used to store the second and first parameters respectively1. **EBP/RBP (Base Pointer) and ESP/RSP (Stack Pointer):** These registers are used in stack operations2. EBP/RBP is often used as a base pointer for accessing local variables and function parameters on the stack2. ESP/RSP is used as a stack pointer, pointing to the top of the stack2. **EIP/RIP (Instruction Pointer):** This register points to the next instruction to be executed2. >Please note that the specific use of these registers can vary depending on the context and the specific assembly instructions being used. >Date:2X/11/2023 #### *Tra mạng về system call* In the context of *Linux system calls* on an x86 architecture, **mov eax, 4** is used to specify the write system call1. Here’s a breakdown of what each part does: **mov eax, 4:** This sets the value of the eax register to 4, which is the system call number for write. **mov ebx, 1:** This sets the ebx register to 1, which specifies that the output should go to stdout (the terminal). **mov ecx, variable:** This *sets* the ecx register to point to the data that you want to write. **mov edx, 1:** This *sets* the edx register to the length of the data that you want to write. **int 0x80:** This *triggers* the system call interrupt, which transfers control to the kernel1. Please note that this is specific to Linux on an **x86 architecture1**. Other operating systems or architectures may use different conventions1. #### Linux command let pc=4498 ![image](https://hackmd.io/_uploads/SJ35d_mWA.png) >23/11/2023 Viết writeup cho pwn.college ### Template python: ```python import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" int 3 """ )) print(process.readallS()) ``` > Linux command : **ipython** ### Level 2: Set multiple registers **Problem**: ![image](https://hackmd.io/_uploads/r1EGLsm-A.png) import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" mov rax, 0x1337 mov r12, 0xCAFED00D1337BEEF mov rsp, 0x31337 """)) print(process.readallS()) ### Level 3: Tìm hiểu về phép cộng - trừ - nhân ![image](https://hackmd.io/_uploads/BJ16udX-C.png) ### Level 4: Multipilcation ![image](https://hackmd.io/_uploads/Hk29tiXWC.png) ![image](https://hackmd.io/_uploads/SyHFFjQ-C.png) ```python import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" imul rdi,rsi add rdi,rdx mov rax, rdi """)) print(process.readallS()) ``` flag: pwn.college{Qi24oRY5i2oAk0HiL0m0N_hgfgL.0lN5EDL1MDOxQzW} ### Level 5: Tìm hiểu về phép chia Thông tin chung: ![image](https://hackmd.io/_uploads/HJ86udmWC.png) Từ đây ta biết: 1. Ban đầu số bị chia nằm ở lower 64 bits (hay **rax**).Kết quả trả về cũng chính là ở **rax** 2. Phần dư sẽ được lưu trong **rdx** >Chat GPT: **64-bit division DIV operand64** ; Divides RDX:RAX by operand64, result in RAX (quotient/ thương), remainder (số dư) in RDX *Note: You must be careful about what is in rdx and rax before you call div.* #### Problem: ![image](https://hackmd.io/_uploads/H1z_Fd7WC.png) #### Solution: Đề yêu cầu tính *speed = **dis** / time* thì ta đã biết **rdx** (upper 64-bits/remainder) và **rax**(lower 64-bits/quotient) lần lượt là phần dư và thương (hay dividend/số bị chia). Vì vậy rax = rdi(**dis**) còn rdx = 0 import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" mov rdx, 0 mov rax, rdi div rsi """ )) print(process.readallS()) ### Level 6: Modulus ![image](https://hackmd.io/_uploads/rkt6joXZ0.png) ```python import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" mov rax, rdi div rsi mov rax, rdx """)) print(process.readallS()) ``` Tương tự với chia thông thường vì nó sẽ rdx là remainder. > Note: rax là số bị chia/thương > flag: pwn.college{kpb00-Ll4mu2PXrKDZKMx-GE6f9.0FO5EDL1MDOxQzW} ### Level 7: #### Problem: ![image](https://hackmd.io/_uploads/BkF_VhmbA.png) ![image](https://hackmd.io/_uploads/HkjK4hmbA.png) #### Solution: import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" mov ah, 0x42 """)) print(process.readallS()) flag: pwn.college{EM1GJiQ8tAdcNc08CTJoAGSZELY.dFTM4MDL1MDOxQzW} ### Level 8: Tìm hiểu về mod 2^n và các sub register của rax, rdi, rsi #### Thông tin chung Hóa ra sử dụng div để tính *“%”* thì khá chậm. Khi tính x % y với y là ở dạng 2^n thì kết quả của phép tính sẽ là n bits thấp nhất của x. ![image](https://hackmd.io/_uploads/BkZBJYX-C.png) Tại sao lại như vậy? > **Chat GPT:** ![image](https://hackmd.io/_uploads/S1AhJKQbC.png) Ta biết một số y = 8 ở dạng 2^3 biểu diễn bits của nó sẽ là: 1000 thì trong trường hợp x = 23 nó sẽ được biểu diễn theo dạng x=2.2^3+7 thì khi ta lấy ra 2^3 từ 23 phần còn lại là phần nguyên 2 và phần dư 7. Biểu diễn trên bits thì đó chính là 111 trong 10111 của 23. **Problem**: ![image](https://hackmd.io/_uploads/Hk_Mtr7bR.png) ![image](https://hackmd.io/_uploads/HJhGFS7bR.png) #### Solution: import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" mov al, dil mov bx, si """)) print(process.readallS()) Quay lại với bài này, vì chỉ có thể dùng “mov” để lấy n bits sau một hồi search trên mạng về register rdi và rsi thì tôi nhận ra nó có một số điểm đáng chú ý: ![image](https://hackmd.io/_uploads/ryIeKBmbA.png) > Note: Tưởng tượng các register 64 bits khác cũng như thế này được lưu chồng lên nhau ![image](https://hackmd.io/_uploads/B1ASZY7Z0.png) > 256 và 65536 tương đương với 2^8 và 2^16 có nghĩa là cần lấy 8 bits và 16 bits cuối. Để lấy 8 bits hoặc 16 bits cuối theo đề bài ta chỉ cần quan tâm đến 2 register 8 bits và 16 bits *dil* và *si* của *rsi* và *rdi*. Thì lúc này ta đơn giản chỉ là chuyển 8 bits cuối(dil) của rdi sang 8 bit cuối của rax (al) và 16 bits cuối (sil) của rsi sang 8 bits cuối (dl) của rdx. ### Level 9: Bitwise shift Trong bài này ta nói về instructions shl (shift left) và shr(shift right) ![image](https://hackmd.io/_uploads/ByX1YrXZC.png) Khi ghi như shl/shr reg1, reg2 có nghĩa là dịch reg2 sang trái/phải đúng giá trị reg1 bits (lưu ý 1 byte = 8 bits) ![image](https://hackmd.io/_uploads/r1_ktSmZR.png) Trong đề yêu cầu ta lấy byte LSB (least significant byte) thứ 5 (nghĩa là từ phải sang trái đếm qua). Nhìn vào rdi ta thấy nó có 8 byte (1 byte biểu diễn 2 số) thì ta sẽ shift sang trái 4 byte để xóa 4 byte bên trái và shift sang trái 7 byte! Tại sao lại 7 byte nhỉ chẳng phải là chỉ có 3 byte thôi sao? ![image](https://hackmd.io/_uploads/ryLLOSQbR.png) Có lẽ là bởi vì bộ nhớ có 8 byte trống nên khi dịch qua phải 4 byte thì phần còn lại vẫn giữ nguyên là 7 byte + 1 byte đáp án. Giống như 1 cái ống nhựa chứa 8 quả bóng vậy, dù ta đã lấy ra bớt ra 4 quả ở “bên phải” (thì ta phải nghiêng để tất cả bóng đều chạy sang phải) thì kích thước nó vẫn không thay đổi, nên khi ta muốn lấy ra bớt tiếp 4 quả ở bên trái ta phải đi 1 quảng đường trong ổng tương đương 7 quả bóng. ### Level 10: Bitwise and #### Solution import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" and rdi, rsi xor rax, rax or rax, rdi """)) print(process.readallS()) Ý tưởng của tôi là sẽ lưu đáp án trong rdi = rdi and rsi sau đó xóa giá trị trong rax bằng xor (not or) rax với chính nó. Cuối cùng là lưu đáp án bằng or  ### Level 11: Bitwise logic ![image](https://hackmd.io/_uploads/rJr4drQ-A.png) #### Solution import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" xor rdi, 1 and rdi, 1 xor rax, rax or rax, rdi """)) print(process.readallS()) Bài này yêu cầu ta tìm chẳn lẻ thông qua duy nhất and, or, xor. Ở đây tôi nghĩ tới tính chất chẳn lẻ của số biểu diễn dưới dạng nhị phân vì 2^0=1 nên ta chỉ quan tâm bit đầu tiên này(nếu bit này tắt những trường hợp khác chắc chắn chẳn!). Bây giờ ta sẽ xor nó với 1 (00001). > Vì sao lại như vậy? Hãy tưởng tượng nếu bài toán là Chẳn thì y=0 Lẻ thì y=1 thì nó sẽ dễ hơn rất nhiều vì ta chỉ cần and nó với 000001 VD: Tôi có số 8 là 1000 thì tôi chỉ cần and nó với 0001 thì y sẽ bằng kết quả đó vì 8 chẳn nên y = 0 Mà giờ thì nó không như vậy, ta sẽ đảo các bit bằng xor sau đó làm như bình thường. ### Level 11: Memory reads and writes Bài này chắc cho ta học về addq [address], value import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" mov rax, [0x404000] addq [0x404000], 0x1337 """)) print(process.readallS()) ### Level 12: ![image](https://hackmd.io/_uploads/Syqgu27WR.png) ![image](https://hackmd.io/_uploads/B1u-_3X-C.png) mov rax, [0x404000] flag: pwn.college{8L2pSXIPKPHTT5tH9ij4xMDxHMd.dJTM4MDL1MDOxQzW} ### Level 16: Read multiple data sizes #### Problem: ![image](https://hackmd.io/_uploads/ryrAvSmZ0.png) #### Solution: Bài này đang nhắc ta về các phân bố bộ nhớ chồng chéo nhau của bộ nhớ register và cách chia bộ nhớ theo bộ 8 bits 16 bits, 32 bits, 64 bits. ![image](https://hackmd.io/_uploads/S1B3wSmbA.png) ### Level 17: Dynamic address memory writes #### Thông tin chung: ![image](https://hackmd.io/_uploads/rJ-LwB7ZR.png) ![image](https://hackmd.io/_uploads/r12BwBmbA.png) ![image](https://hackmd.io/_uploads/BklHPHXbR.png) Bài nhắc tới cách lưu dữ liệu trong x86, khi đó nó sẽ bị đảo ngược(nhưng từng bit ở trong byte thì không). Cái này được gọi là **“Little Endian”** Trong ví dụ khi lưu [0x1330] = 0x00000000deadc0de nó sẽ lần lượt bị đảo thành như hình trên. #### Problem: Bài này yêu cầu ta lưu Lưu [rdi] = 0xdeadbeef00001337 Lưu [rsi] = 0xc0ffee0000 ![image](https://hackmd.io/_uploads/SJmovr7W0.png) #### Solution: Ta nhận thấy ta phải giá trị big constant qua rax. Sau đó chuyển ngược lại qua [rdi]. >Big Constant: The constant in question is large, meaning it has a high numerical value or takes up a significant amount of memory. import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" mov rax, 0xdeadbeef00001337 mov [rdi], rax mov rax, 0xc0ffee0000 mov [rsi], rax """)) print(process.readallS()) >code ---------------- CODE ---------------- 0x400000: movabs rax, 0xdeadbeef00001337 0x40000a: mov qword ptr [rdi], rax 0x40000d: movabs rax, 0xc0ffee0000 0x400017: mov qword ptr [rsi], rax -------------------------------------- ### Level 18: Consecutive memory reads Dựa vào tên bài ta cũng phần nào đoán ra được chúng ta phải làm gì trong bài này. Tảng mạn: Khi lưu trữ dữ liệu ở một địa chỉ nào đó là chúng sẽ được lưu “trải” trên 1 vùng bộ nhớ chung nên xảy chuyện bị ghi đè là hoàn toàn có thể… #### Thông tin chung Nhắc lại một số kiến thức về little endian nó sẽ lưu ngược dữ liệu input (nhưng 2 bits ở trong thì không ngược) . Nó làm được gì cho chúng ta? ![image](https://hackmd.io/_uploads/ry6RISm-A.png) Bài đã cho thông tin khá đầy đủ, nó giúp ta truy cập những thứ kế bên nhau thông qua **offsets**. Ở đây ví dụ khi muốn truy cập byte thứ 5th của từ 1 địa chỉ chúng ta ghi **[address+4]** bởi vì nó đếm từ 0. Đơn giản nhỉ ;-; ![image](https://hackmd.io/_uploads/H137UrX-R.png) **Problem**: ![image](https://hackmd.io/_uploads/BkKH8H7bA.png) Rõ ràng là không như vậy rồi, bây giờ đề yêu cầu ta truy cập *2 quad words*(1 quad word là 8 bytes) liên tiếp trong rdi, rồi tính tổng của nó lưu trong rsi. Vấn đề là mới ban đầu đọc tôi không hiểu “Load” ở đây là đề muốn ta làm gì với 2 cái quad words đó chẳng phải đều cho sẳn rồi sao? Nhưng sau khi học kiến trúc máy tính thì tôi nhận ra đề đang yêu cầu chúng ta lưu dữ liệu từ 1 register sang memory. ---------------- CODE ---------------- 0x400000: mov rax, qword ptr [rdi] 0x400003: add rax, qword ptr [rdi +8] 0x400007: mov qword ptr [rsi], rax -------------------------------------- Rõ ràng là phần code trên workspace mà tôi đã dùng là chưa hoàn chỉnh, nó đã bị giản lược 1 số chỗ? Tôi nghĩ từ giờ nên để ý tới phần code chính hơn. ### Level 19: **Problem**: ![image](https://hackmd.io/_uploads/Hk3O4H7bA.png) Ở bài này ta nói về stack một khái niệm khá quen trong ngôn ngữ bậc cao. ![image](https://hackmd.io/_uploads/SkNYVB7bR.png) ![image](https://hackmd.io/_uploads/rJRK4HXW0.png) Mô tả về stack ![image](https://hackmd.io/_uploads/rJ7qESmZR.png) Thì nó giống như chồng đĩa vậy cái nào bỏ lên sau thì phải lấy nó ra trước. Liên quan tới stack ta còn có rsp (Stack Pointer Register) #### Solution pop reg1 -> là lấy giá trị top ra(xóa luôn) lưu trong reg1 push reg1 -> là đẩy giá trị reg1 vào ---------------- CODE ---------------- 0x400000: pop rax 0x400001: sub rax, rdi 0x400004: push rax -------------------------------------- flag: pwn.college{gFvtFBnzLX9e8nPvaX-ofON3M-H.01NwIDL1MDOxQzW} ### Level 20: #### Problem and Solution: ![image](https://hackmd.io/_uploads/Bka4VH7bA.png) ![image](https://hackmd.io/_uploads/SkHHVr7b0.png) ### Level 21: #### Thông tin chung & Problem: In the previous levels you used push and pop to store and load data from the stack. However you can also access the stack directly using the stack pointer. On x86, the stack pointer is stored in the special register, rsp. rsp always stores the memory address of the top of the stack, i.e. the memory address of the last value pushed. Similar to the memory levels, we can use [rsp] to access the value at the memory address in rsp. Chúng ta sẽ dùng [rsp+?] để truy cập vị trí trong stack pointer mà không dùng pop. Đếm trong stack thì mỗi value là dài 1 quad word = 8 byte #### Solution: ---------------- CODE ---------------- 0x400000: mov rax, qword ptr [rsp] 0x400004: add rax, qword ptr [rsp + 8] 0x400009: add rax, qword ptr [rsp + 0x10] 0x40000e: add rax, qword ptr [rsp + 0x18] 0x400013: shr rax, 2 0x400017: push rax -------------------------------------- flag: pwn.college{wdsAH9gG7GHrVNGzP8-f8Ru5HZQ.0VOwIDL1MDOxQzW} ---------------- CODE ---------------- 0x400000: mov rax, qword ptr [rsp] 0x400004: add rax, qword ptr [rsp + 8] 0x400009: add rax, qword ptr [rsp + 0x10] 0x40000e: add rax, qword ptr [rsp + 0x18] 0x400013: mov rdi, 4 0x40001a: div rdi 0x40001d: push rax -------------------------------------- Rõ ràng thì cách để lấy tổng 4 quad words là giống nhau, khi chia ta có 2 cách: 1. Đó là phép dịch bits 2. Hoặc chia bằng div import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" movq rax, [rsp] addq rax, [rsp+8] addq rax, [rsp+16] addq rax, [rsp+24] mov rdi, 4 div rdi push rax """)) print(process.readallS()) ### Level 22: #### Problem: ![image](https://hackmd.io/_uploads/rJJH_67ZA.png) ![image](https://hackmd.io/_uploads/H1TIOpXW0.png) ![image](https://hackmd.io/_uploads/Byc_dpmb0.png) #### Solution: push 0x403000 ret flag: pwn.college{kwRjrB7CxR4_maawHAbFgpnlPZ6.dVTM4MDL1MDOxQzW} ### Level 24: #### Thông tin chung: >Ta sẽ tìm hiểu về “rip” - the instruction pointer ![image](https://hackmd.io/_uploads/HJ8p52Q-A.png) #### Problem: 1. Đề yêu cầu ta tạo instruction đầu tiên là jmp tới 1 vị trí cách vị trí hiện tại 0x51 bytes. 2. Ở vị trí $+0x51 đó thì thực hiện đặt rdi bằng giá trị đầu của stack. 3. Sau đó thì jmp tới 0x403000, 1 địa chỉ cố định. ![image](https://hackmd.io/_uploads/B1HCq2m-0.png) #### Thông tin cần biết: 1. Absolute jump >In order to perform an absolute jump, we have to specify the address to jump to instead of a label. #### Code: ----------------- jmp 0x10 . . . 0x10 ----------------- Vấn đề của cách này là “endianness” nó sẽ lưu ngược các bits địa chỉ nhập vào. Có 2 cách xử lý. We can first copy the address in a register and then provide the register as the operand. (Chúng ta có thể copy địa chỉ trong 1 register rồi sử dụng register như một toán hạng) #### Code: ----------------- mov rax, 0x403000 jmp rax ----------------- For the second method we have to understand the how the ret instruction works in tandem with the instruction pointer. (Ở cách thứ 2 thì chúng ta phải hiểu được cách instruction ret hoặc động song song với instruction pointer (rip) ) 2. Instruction pointer: The instruction pointer is a register that holds the address of the instruction to be executed next, thus pointing to it. -------------------------------------------------------------- 0x00 Instruction 1 0x01 Instruction 2 Already executed 0x02 Instruction 3 rip ------> 0x03 Instruction 4 ## To be executed -------------------------------------------------------------- #### Ret Instruction: When we use the ret instruction, it pops the latest value on the stack into the instruction pointer rip. +------------------+ RSP+0x18 | 0x00 | <------ rbp +------------------+ RSP+0x10 | 0x01 | +------------------+ RSP+0x08 | 0x02 | +------------------+ RSP | 0x03 | <------ rsp +------------------+ after ret +------------------+ RSP+0x10 | 0x00 | <------ rbp +------------------+ RSP+0x08 | 0x01 | +------------------+ RSP | 0x02 | <------ rsp +------------------+ Repeat Loop: #### Lặp lại code nhiều lần theo ý muốn. -------------------------------------- .rept (number of times to be repeated) code .endr -------------------------------------- #### Solution & Code: First line, we will jump to the 0x51 position. +-----------+ jmp thejump +-----------+ And then we can define thejump bellow. +-----------+ thejump: pop rdi push 0x403000 ret +-----------+ However, remember to create the “filter” first by using repeat loop. +-----------+ .rept 0x51 nop .endr +-----------+ #### Code: import pwn pwn.context.update(arch="amd64") process = pwn.process("/challenge/run") process.write(pwn.asm(""" jmp thejump .rept 0x51 nop .endr thejump: pop rdi push 0x403000 ret """)) print(process.readallS())