# 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:

- Compile script chạy thử:

- Sau khi chạy thử thì thấy sever đã được kết nối đến và gửi một tin nhắn **hi**:

- Success!