Chào các bạn,
Tớ thấy đề tài này cũng vui nên tớ gửi qua đây để mọi người thảo luận thêm (bài gốc ở
https://hvaonline.forumvi.net/forum-f15/topic-t27.htm ).
python có cái module struct rất hữu dụng khi làm việc với mấy cái structure của C/C++. Nó có mấy cái struct như:
Code:
struct packet {
unsigned char ops;
unsigned short int id;
unsigned short int flags;
unsigned int assoc;
} __attribute__((__packed__));
typedef struct packet packet;
struct data_assoc {
unsigned short int len;
unsigned char buf[0];
} __attribute__((__packed__));
typedef struct data_assoc data_assoc;
drifter3 nó nhận một cái udp packet từ network vào, phần data của packet phải tuân thủ theo đúng hai cái structure này, thì nó mới chịu xử lý và trả về một udp packet. thường có 2 cách để nhập dữ liệu và xử lý kết quả cho mấy chương trình như thế này:
1. nhập và xử lý bằng tay. kiểu như chương trình yêu cầu một packet có ops = 1, id = 1, flags = 0, assoc = 0, thì mình phải nhập vào một chuỗi byte (little-edian) như \x01\x01\x00\x00\x00\x00\x00\x00\x00 rồi đợi chương trình trả kết quả về, và lại unpack kết quả bằng tay tiếp để xem mấy cái member của struct nó có kết quả là gì.
2. rõ ràng làm cách 1 rất chậm và dễ xảy ra lỗi. bất lợi lớn nhất của cách 1 là không thể làm tự động được. ngược lại cách thứ 2 mà tớ sắp nói rất tiện lợi, và hoàn toàn có thể tự động hóa được luôn.
ý tưởng chủ đạo (mà tớ chôm khi đọc source code của bọn nước ngoài :p) là sẽ biến mấy cái struct của C thành class của python, rồi dùng struct.unpack và struct.pack để nhập và xuất dữ liệu. chẳng hạn như đối với cái struct packet ở trên, tớ sẽ viết thành cái class Python như sau:
Code:
class Packet(object):
def __init__(self, ops=None, id=None, flags=None, assoc=None, bytes=None):
self.ops = ops
self.id = id
self.flags = flags
self.assoc = assoc
if bytes:
self.fromBytes(bytes)
def fromBytes(self, bytes):
# little-endian
(self.ops, self.id, self.flags, self.assoc) = struct.unpack("<BHHI", bytes)
def toBytes(self):
# little-endian
return struct.pack("<BHHI", self.ops, self.id, self.flags, self.assoc)
def __len__(self):
return struct.calcsize("<BHHI")
def __repr__(self):
return "ops %d, id %d, flags %d, assoc %d " % (self.ops, self.id, self.flags, self.assoc)
với cái class này, tớ có thể tạo và gửi một packet dễ dàng với đoạn mã sau:
Code:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
input = raw_input("ops, id, flags, assoc: ")
[ops, id, flags, assoc] = input.split(",")
header = Header(int(ops.strip()), int(id.strip()), int(flags.strip()), int(assoc.strip()))
p_out = Packet(header)
sock.sendto(p_out.toBytes(), ADDR) # hit breakpoint
data, addr = sock.recvfrom(PKTSIZE)
p_in = Packet(bytes=data)
print repr(p_in)
sock.close()
chẳng hạn như khi chạy, nó sẽ ra như thế này:
Code:
drifter3@games $ ./level3.py
out = ops, id, flags, assoc: 0,0,0,0
in = ops 0, id 0, flags 1, assoc 3696333875
rất rõ ràng và trong sáng phải không? điều tớ thích nhất ở phương pháp thứ 2 này là tớ có thể tự động hóa được hoàn toàn việc nhập liệu, debug và kiểm tra kết quả.
chẳng hạn như tớ có thể viết script, cài breakpoint trong chương trình, quăng dữ liệu vào và quan sát kết quả cũng như biết rõ những dòng lệnh nào được thực hiện ứng với loại dữ liệu đầu vào nào (nghe giống như fuzzing nhỉ? ). Cái này gọi là code coverage, có một tool khá hay làm việc này là Paimei, nhưng nó không chạy trên Linux :-p).
nói đi nói lại thì tớ rất thích cái module struct của python :-p (tớ thích nhất là em ctypes, tớ sẽ không thể sống nổi nếu thiếu em này lolz).
--m