# Training KCSC 2024 ## Task1: Tìm hiểu về assembly - Nhiệm vụ của task 1 là `Tìm hiểu về assembly và cách dùng syscall. Viết 1 chương trình assembly x64 kết nối tới localhost port 12345 và gửi nội dung`. - Assembly là một ngôn ngữ lập trình bậc thấp. Assembly có 3 phần cơ bản là **data**, **bss** và **text**. +**Data** là phần chứa các biến ta khởi tạo lúc chạy chương trình. Nó không thay đổi trong khi chạy. +**Bss** là nơi sử dụng để khai báo các biến. +**Text** là nơi để chương trình nhận biết nơi bắt đầu. Phần này phải bắt đầu bằng `global _start` - Các câu lệnh trong Assembly đều ở trên 1 dòng và có cú pháp: `[label] instruction [operands] [;comment]` +`instruction` là câu lệnh được thực thi ví dụ như `mov`, `add`,... +`operands` là tham số của các câu lệnh. Nó có thể là địa chỉ, biến, độ dài,... - Nhìn chung, mỗi khi ta làm một hành động nào đó, ví dụ như in ra màn hình chẳng hạn, cấu trúc chung của các hành động đó là: ```python= mov rax, 1 ; gọi lệnh syscall để write mov rdi, 1 ; file descriptor (1 là stdout) mov rsi, hello ; địa chỉ của chuỗi "Hello World!" mov rdx, 13 ; chiều dài của chuỗi syscall ; gọi hệ thống - Quay trở lại với nhiệm vụ chính, để kết nối đến một server và gửi một nội dung, đại khái ta phải thực hiện công việc sau đây: tạo 1 socket, đưa socket vao `rdi`, kết nối đến sever, gửi tin nhắn, đóng socket và thoát chương trình. - Đầu tiên ta phải **khai báo dữ liệu**: ```python= section .data ip db '127.0.0.1' ; Địa chỉ IP của server port dw 12347 ; Port của server msg db 'hi', 0 ; Dữ liệu gửi đến server section .bss addr resb 16 ; Dùng để lưu địa chỉ sockaddr_in ``` - Tiếp đến là **tạo socket**: ```python= ; Tạo socket mov rax, 41 ;gọi lệnh syscall để tạo socket mov rdi, 2 ;dùng AF_INET (IPv4) mov rsi, 1 ;dùng TCP mov rdx, 0 ;đặt protocol=0 syscall ;gọi syscall ``` - Để kết nối đến một server, ta cần cung cấp địa chỉ IP và port mà server đang lắng nghe. Cấu trúc `sockaddr_in` là một dạng đặc biệt chứa thông tin về địa chỉ mạng mà socket sẽ kết nối. > Trong phần thiết lập `port`, ta sẽ phải chuyển từ giá trị **little-endian (byte thấp lưu trước)** thành **big-endian (byte cao lưu trước)**. Bởi vì trong kiến trúc `x86_64`, máy tính thường sẽ lưu trữ theo kiểu **little-endian** còn trong mạng thì sẽ là **big-endian**. ```python= ; Đưa socket vào rdi mov rdi, rax ; Thiết lập sockaddr_in mov word [addr], 2 ; Chuyển port thành big-endian mov ax, [port] ; Đọc cổng vào thanh ghi ax (16 bit) xchg al, ah ; Đảo byte thấp và byte cao cho nhau mov [addr+2], ax ; Lưu giá trị đã chuyển đổi vào sin_port ; Thiết lập địa chỉ ip mov dword [addr+4], 0x0100007F; sin_addr = 127.0.0.1 (dạng nhị phân) xor rax, rax ; mov qword [addr+8], rax ; ``` - Sau khi đã thiết lập xong `sockaddr_in`, ta chỉ cần **kết nối đến server và gửi nội dung**: ```python= ; Kết nối tới server mov rax, 42 ;lệnh syscall connect mov rsi, addr ;Địa chỉ của sockaddr_in mov rdx, 16 ;Kích thước sockaddr_in syscall ;Gọi syscall ; Gửi tin nhắn mov rax, 44 ;syscall send mov rsi, msg ;Địa chỉ của tin nhắn mov rdx, 2 ;Số byte cần gửi syscall ;Gọi syscall ``` - Cuối cùng, để hoàn thiện code, ta phải **đóng socket và kết thúc chương trình**: ```python= ; Đóng socket mov rax, 3 ;syscall close syscall ;Gọi syscall để đóng socket ; Thoát chương trình mov rax, 60 ;syscall exit xor rdi, rdi ;Exit code 0 syscall ;Gọi syscall để thoát ``` - **Final script**: ```pytho= section .data ip db '127.0.0.1' ;Địa chỉ IP của server port dw 12347 ;Sử dụng 'dw' vì cổng là 16-bit msg db 'hi', 0 ;Tin nhắn gửi đến server section .bss addr resb 16 ;Dùng để lưu địa chỉ sockaddr_in (16 byte) section .text global _start _start: ; Tạo socket mov rax, 41 ;syscall socket mov rdi, 2 ;AF_INET (IPv4) mov rsi, 1 ;SOCK_STREAM (TCP) mov rdx, 0 ;Protocol 0 syscall ;Gọi syscall, kết quả trả về trong rax ; Đưa socket vào rdi mov rdi, rax ; Thiết lập sockaddr_in mov word [addr], 2 ;sin_family = AF_INET (2) ; Chuyển port thành big-endian mov ax, [port] xchg al, ah mov [addr+2], ax ;sin_port = htons(12347) ; Thiết lập địa chỉ ip mov dword [addr+4], 0x0100007F; sin_addr = 127.0.0.1 (dạng nhị phân) xor rax, rax ;sin_zero = 0 mov qword [addr+8], rax ;Đặt phần sin_zero thành 0 ; Kết nối tới server mov rax, 42 ;syscall connect mov rsi, addr ;Địa chỉ của sockaddr_in mov rdx, 16 ;Kích thước sockaddr_in syscall ;Gọi syscall ; Gửi tin nhắn mov rax, 44 ;syscall send mov rsi, msg ;Địa chỉ của tin nhắn mov rdx, 2 ;Số byte cần gửi (2 byte cho "hi") syscall ;Gọi syscall để gửi tin nhắn ; Đóng socket mov rax, 3 ;syscall close syscall ;Gọi syscall để đóng socket ; Thoát chương trình mov rax, 60 ;syscall exit xor rdi, rdi ;Exit code 0 syscall ;Gọi syscall ``` - Tạo một server để chạy thử xem script có hoạt động không: ![image](https://hackmd.io/_uploads/rkfX9NWJkx.png) - Compile script chạy thử: ![image](https://hackmd.io/_uploads/H1gB54Wykl.png) - Sau khi chạy thử thì thấy sever đã được kết nối đến và gửi một tin nhắn **hi**: ![image](https://hackmd.io/_uploads/Hyow5NW1Jl.png) - Success!