Wednesday, August 31, 2005

Bản đồ Internet

Không ai trên thế giới có thể biết chính xác rằng có bao nhiêu máy nối mạng Internet (đếm địa chỉ IPs chẳng hạn), hay tổng số người dùng Internet. Hai lý do chính: (1) Internet về bản chất có tính phân bố cực cao, không một người hay tổ chức nào quản lý toàn bộ Internet; (2) Các con số trên thay đổi hàng phút hàng giây. Thậm chí, tổng số các routers (con số "tĩnh" hơn các số trên) cũng không ai biết được.

Vài nơi có ước lượng các số thống kê kiểu này, bao gồm World Fact Book của CIA, hay Computer Industry Almaniac (cũng viết tắt là CIA!). Theo thống kê của ISC thì đến tháng 7 năm 2005, Internet có khoảng hơn 350 triệu hosts. Tổng số người dùng thì đã hơn một tỉ.

Có khá nhiều dự án nhằm vẽ bản đồ Internet, bao gồm:

Tô màu bản đồ Internet cho các hình rất đẹp. Ví dụ, bản đồ dưới đây là của Opte Project:

http://www.opte.org/maps/static/1105841711.LGL.2D.400x400.png



[ Hình trên có kích thước 400 x 400; các độ phân giải khác: 1024x1024, 4000x4000 ]

MCI (hay MCI Worldcom/UUNET) hiện là ISP lớn nhất thế giới; mạng xương sống (backbone network) của họ đóng vai trò thiết yếu nối Internet toàn cầu. Xem bản đồ mạng xương sống của MCI ở đây.

Các hình ảnh mạng xương sống của các ISP lớn khác có thể xem ở CAIDA. Họ cũng có cả đồ thị nối kết của các Autonomous Systems cho IPv4 và cho IPv6.

Bạn có biết bản đồ các ISP của Việt Nam thì lấy ở đâu không? Hiện nay có bao nhiêu ISP, kích thước ra sao, ai làm chủ, ...?

Tuesday, August 30, 2005

Bài giảng nhân giải Turing của Cerf và Kahn

Theo truyền thống, sau khi nhận giải Turing thì (những) người đoạt giải sẽ có một bài giảng ở một hội nghị đầu ngành, gọi là Turing Award Lecture. Thứ hai vừa qua (22 tháng 8), các tiến sĩ Vinton Cerf và Robert Kahn có bài giảng ở hội nghị SIGCOMM 2005 tại Philadelphia.

Các bạn có thể nghe bài giảng ở đây (cần Real Player). Bài giảng mang tên: " đánh giá Internet: các bài học, chiến lược tiến triển, và các khả năng trong tương lai".

Saturday, August 27, 2005

Các bài báo kinh điển của KHMT (6)


Bài báo hôm nay là của các tiến sĩ Vinton CerfRobert Kahn, hai người cùng lãnh giải Turing năm 2004 (công bố tháng 2 năm 2005):

V. Cerf and R. Kahn, A Protocol for Packet Network Interconnection. IEEE Transactions on Communications COM-22, pp. 637-641 , 1974.

Đầu những năm 1960, các ý tưởng khởi điểm về mạng chuyển gói (packet switched networks) dẫn đến mạng ARPANET do DARPA tài trợ. Các nodes trên ARPANET chạy NCP (Network Control Protocol), bắt đầu năm 1970. Cùng năm này packet radio network đầu tiên - ALOHA Net -
bắt đầu làm việc. Cùng với sự ra đời của ứng dụng phổ biến nhất trên Internet - email - cuối 71, đầu 72, và phác thảo ý tưởng về Ethernet, Robert Kahn nhận rõ sự cần thiết của việc nối các mạng khác nhau với nhau để chia sẻ tài nguyên và thông tin. Kahn phác thảo ý tưởng về một cấu hình mạng mở (open networking architecture) trong đó các mạng với kiến trúc, cấu hình máy, hệ điều hành ... khác nhau có thể nối kết với nhau. Cái mạng của các mạng này được gọi là Internet.

NCP không đủ mạnh để làm việc này. Kahn phát triển giao thức TCP (Transmission Control Protocol), với các thành phần chính như sau:

  • Các mạng con, sau khi nối kết, không phải thay đổi gì (protocol, hệ điều hành, ...)
  • Truyền thông sẽ theo kiểu cố hết sức (best effort), nghĩa là bên gửi và bên nhận chịu trách nhiệm truyền lại nếu dữ liệu bị mất hoặc hư hỏng, Internet không chịu trách nhiệm làm việc này.
  • Các gateways sẽ liên kết các mạng con lại với nhau, chịu trách nhiệm chuyển dạng tín hiệu, dạng dữ liệu từ mạng này sang mạng khác.
  • Mạng Internet mang tính phân bố (distributed) cực cao.

(Chú ý rằng ngày nay cấu trúc của Internet y nguyên như thế.)

Còn khá nhiều vấn đề kỹ thuật cụ thể phải giải quyết: xử lý mất packets, packets đến sai thứ tự, cung cấp dịch vụ đáng tin cậy, cách đánh địa chỉ các máy và các tiến trình trong các máy, flow control, giao tiếp với các hệ điều hành khác nhau, vân vân và vân vân.

Đầu năm 1973, Kahn mời thêm Cerf, người rất quen thuộc và góp phần thiết kế và phát triển NCP, để cùng phát triển TCP. Đến tháng 9 năm 1973, Cerf và Kahn báo cáo về công trình của họ ở một hội nghị tại đại học Sussex, Anh quốc. Bài báo trên chứa nội dung chi tiết của các ý tưởng của họ.

Thật ra TCP đầu tiên là viết tắt của Transmission Control Program. Họ muốn nói đến một chương trình sẽ làm công việc truyền dữ liệu giữa các tiến trình của một máy này và các tiến trình của một máy khác trên Internet. Lúc đầu thì IP (Internet Protocol) là một phần của TCP, nhưng đến năm 1978 thì tách ra thành hai protocols riêng biệt ở hai mức khác nhau của thiết kế. Từ đó trở đi, họ protocols này được gọi là họ TCP/IP. Đa phần các máy tính ta đang dùng đều chạy TCP/IP để giao tiếp trên mạng.

Dù có phát triển và thích nghi, các ý tưởng căn bản của TCP/IP đứng vững trước bao nhiêu kỹ thuật mạng mới, bao nhiêu trăm triệu máy mới nối mạng, nghìn vạn các ứng dụng mới của Internet mà dù Cerf và Kahn có mơ cũng không thể thấy hết. Đây là bằng chứng hùng hồn cho một ý tưởng vĩ đại.

Tuesday, August 23, 2005

Tin tặc, hackers, và hồ sơ biệt ngữ

Luôn dịch hacker thành tin tặc hoặc các từ mang nghĩa xấu khác là sai nghiêm trọng.

Chữ hack trong tiếng Anh có nhiều nghĩa. Dò tự điển Webster thì các nghĩa phổ thông "to manage successfully", hoặc "to clear or make by or as if by cutting away vegetation" là gần nghĩa kỹ thuật nhất. Hai nghĩa gần đúng và thông dụng là "to write computer program for enjoyment" và "to gain access to computers illegally". Hack có thể được dịch là "chọc ngoáy", "mày mò". Khi ngồi nghịch mã nguồn các device drivers của Linux, ta có thể nói: "I am hacking around with Linux drivers".

Thế dân kỹ thuật nói gì về hack? Hack/hacker là các biệt ngữ của dân máy tính. Muốn hiểu thật kỹ thì ta phải đọc hồ sơ biệt ngữ hay còn gọi là The Jargon File. Dân máy tính và ... hackers nói riêng phải biết các biệt ngữ này.

Giáo sư Raphael Finkel (lúc đó là sinh viên ở Stanford) viết phiên bản đầu tiên của hồ sơ biệt ngữ năm 1975. Đây là bộ sưu tập các biệt ngữ của văn hóa kỹ thuật ở các trường đại học, phòng nghiên cứu nối với nhau ở thời kỳ sơ khai của Internet (mạng ARPANET).

Ý nghĩa chính của hack là "áp dụng sự khéo léo một cách thích đáng". Kết quả có thể là một đoạn mã chương trình thông minh, hay cả một hệ điều hành. Một nghĩa phụ của hack là trò đùa thực tế (practical joke). Bạn tham khảo
phần bàn về định nghĩa hack của hồ sơ biệt ngữ để biết thêm các ví dụ về hack.

Một trong những hacker nổi tiếng nhất thế giới là tiến sĩ
Richard M. Stallman , người khởi xướng GNU. Ông viết về ý nghĩa của hacking , rất đáng đọc.

Một trong những ví dụ tếu về hack mà tôi thích là vụ hiện thực hóa RFC 1149. Các
RFC (request for comments) là các tài liệu đề cử các chuẩn mới cho Internet. Ví dụ: RFC 793 cho TCP, RFC 791 cho IP. TCP và IP là các protocols thông dụng nhất của Internet.

Ngày cá tháng tư nămg 1990, RFC 1149 đề nghị chuyển dữ liệu qua ... lưng chim. Tựa đề của RFC này là "một chuẩn cho việc truyền các gói IP qua vận tải không gian". Dĩ nhiên đây là một trò đùa cá tháng tư. Thế nhưng đến 28 tháng 4 năm 2001, nhóm người dùng Linux Bergen đã thực hiện thành công vụ chuyển dữ liệu qua lưng chim này. Họ in dữ liệu của các gói IP theo dạng Hex ra giấy, bắt bồ câu kẹp giấy bay qua một hòn núi nhỏ ở Bergen, Na Uy, sau đó dùng một phần mềm nhận dạng chữ in (ORC) để quét dữ liệu từ giấy vào máy bên kia, thiết lập lại gói IP.

Monday, August 22, 2005

Phần mềm lậu: Việt Nam đứng đầu

Một press release gần đây của Business Software Alliance báo cáo rằng Việt Nam đứng đầu danh sách các nước dùng phần mềm lậu: 92% các phần mềm được dùng là phần mềm lậu. Vài nước kế tiếp trong danh sách là Ukraine (91%), Trung Quốc (90%), và Indonesia (87%). Mỹ có tỉ lệ dùng phần mềm lậu thấp nhất, ở mức 21%.

Các con số này và cái tên Việt Nam lại vừa được nêu trong số mới nhất của tờ Communications of the ACM. Con số này cũng là một vấn đề quan trọng được thảo luận giữa thủ tướng Phan Văn Khải và Bill Gates trong chuyến thăm Mỹ vừa qua của thủ tướng.


Dân học/làm nghề máy tính mà dùng phần mềm lậu thì chẳng khác gì tự bắn vào chân mình. Ngược lại, có những thứ liên quan thiết yếu đến nghề nghiệp như Visual Studio, sinh viên Việt Nam chẳng đủ tiền mua, thì phải có cách nào đó để nhảy vào cái vòng lặp tai quái này. Không dùng phần mềm lậu thì khó mà học được.

Vấn đề tổng quát hơn: sở hữu trí tuệ - cũng ở tình trạng tương tự.
Hơn 90%
sách tiếng nước ngoài được in hoặc dịch lậu. Các bản photocopies tràn lan. Đây là báo cáo đặc biệt của International Intellectual Property Alliance. Phim ảnh và nhạc thì không cần phải nói.

Dùng nhiều phần mềm, sách, phim ảnh, nhạc lậu rất dễ biến thành thói quen xấu. Đến khi có đủ khả năng mua ta vẫn không mua. Tự điển Lạc Việt là một ví dụ. Tôi thấy nhiều bạn dùng các sách photo tiếng Việt. Sách tiếng Việt và phần mềm Việt không phải là quá tầm tay của nhiều người. Đây còn là vấn đề tâm lý thú vị: nếu ta chôm vài trăm USD của người khác thì chắc chắn không làm, nhưng ra tiệm chép Windows XP dùng thì chẳng thấy tội lỗi gì.

Vấn đề phức tạp này cần một giải pháp vĩ mô hiệu quả.

Saturday, August 20, 2005

Mười bài học ước gì tôi đã được dạy

Các bài này tôi xin phép sử dụng lại từ GS. Ngo Quang Hung CSE of Buffalo.

Cố giáo sư Gian-Carlo Rota là người đã mang enumerative combinatorics vào Toán học chính thống. Một trong những học trò của ông, giáo sư Richard Stanley hiện là một trong những cây đại thụ của ngành Combinatorics thế giới.

Năm 1996, Rota có một bài nói chuyện nhan đề
mười bài học ước gì tôi đã được dạy. Một trong những bài học mà tôi hay kể cho sinh viên nghe, và bản thân tôi đã ứng dụng thành là cái mẹo Feynman.

Nhà vật lý lừng danh
Richard Feynman rất thích nói về cái mẹo sau đây: muốn trở thành thiên tài, bạn hãy giữ trong đầu khoảng một tá các vấn đề chưa ai giải được. Mỗi khi bạn đọc một bài báo mới, học một kỹ thuật mới, bạn thử áp dụng nó vào một tá bài toán mở đó. Đến lúc nào đó sẽ có một kỹ thuật dùng được cho một trong 12 bài toán, và người ta sẽ thốt lên ngạc nhiên: "tay này quả là thiên tài!"

Trong bài báo năm 1999, một buổi tối tôi đang ngồi thử tìm cách xây dựng một cấu trúc
thử nhóm chịu được lỗi, thì nảy ra ý tưởng dùng một khái niệm mới học được từ cấu hình tổ hợp của các đa thức vuông góc. Thế là mày mò ra được một cách xây dựng mới. Chẳng thành được thiên tài như Feynman đã dạy, nhưng có một bài báo mới cũng tốt chán :-). Sau đó vài tác giả khác đã phát triển thêm ý tưởng này.

Hiển nhiên đây là lối nói có phần ẩn dụ. Áp dụng bừa bãi thì chỉ có mất thời gian. Trên thực tế ta phải hiểu rất rõ các bài toán mở đó và kỹ thuật mới học được, sau đó để trực quan hướng đến kết luận là kỹ thuật này dùng được cho một trong những bài đó. Ngược lại, việc áp dụng thành công một kỹ thuật nằm ngoài mong đợi vào một bài toán cổ điển thường cho các kết quả rất tốt.

Các bài học khác của Rota cũng rất thú vị. Các bạn xem thử. Tôi cũng thích quyển
indiscrete thoughts của ông.

Thursday, August 18, 2005

Lỗi tràn bộ đệm (4)

4. Buffer overflow trong cấu hình SPARC 32 bit

Để biết chi tiết hơn về cấu hình SPARC của hãng Sun Microsystem, bạn có thể tham khảo phiên bản 9 của SPARC Architecture Manual.

Thường thì trong cấu hình SPARC mỗi CPU có từ 40 đến khoảng 512 thanh ghi. Trong đó chỉ có 32 thanh ghi hiện hành dành cho một chương trình đang chạy ở bất kỳ thời điểm nào. Mỗi thanh ghi chứa 32 bits. Có 4 nhóm thanh ghi, mỗi nhóm chứa 8 thanh:

  1. Nhóm toàn cục (global): %g0 đến %g7
  2. Nhóm cục bộ (local): %l0 đến %l7
  3. Nhóm nhập (in): %i0 đến %i7

%i6 chứa FP giống như %ebp trong cấu hình Intel IA-32. Biến %fp cũng trỏ đến %i6 cho tiện.

%i7 chứa return address của hàm hiện hành. Cấu hình SPARC không PUSH return address vào stack như cấu hình Intel. Tuy vậy, ta vẫn có thể dùng buffer overflow để thay đổi return address với cách làm hơi khác trường hợp của Intel một chút.

  1. Nhóm xuất (out): %o0 đến %o7

%o6 chứa SP chỉ đến đỉnh của stack frame hiện hành.

%o7 được dùng để lưu return address cho hàm được gọi (callee) biết trả điều khiển về đâu.


Trong cấu hình SPARC, các tham số hàm, return address, ... được truyền cho hàm được gọi dùng các thanh ghi này, thay vì được PUSH vào stack như trong cấu hình Intel.

Khi hàm foo() gọi hàm bar(), ngay trước khi bar() hoạt động thì nội dung các thanh ghi từ %i0 đến %i7, rồi %l0 đến %l7 của foo() được bỏ vào stack trước (tổng cộng 64 bytes), sau đó nội dung của tất cả các thanh ghi %o* được chuyển vào %i*. Như vậy, các thanh ghi xuất của hàm foo() "biến thành" các thanh ghi nhập của hàm bar(). Hàm bar() sau đó sẽ dùng nội dung của %i6 và %i7 để thiết lập lại trạng thái chương trình như cũ khi bar() kết thúc. Các thanh ghi %i0 đến %i5 được dùng để truyền tham số cho bar(). Nếu có nhiều tham số hơn nữa thì phải dùng stack.

Đến đây, ta đã có 64 bytes trên stack frame mới cho bar(). Kế tiếp có 4 bytes trỏ đến một struct nếu bar() trả về một struct. Kế đến có 24 bytes dành cho tham số (trong trường hợp %i0 đến %i5 không đủ dùng). Cuối cùng trên stack là các biến cục bộ của bar().

Để thay đổi dòng hoạt động của chương trình, ta có thể overflow lên nội dung thanh ghi %i7 được lưu trong stack của hàm foo(). Nội dung này là return address sau khi foo() chạy xong. Sau đây là một ví dụ trong cấu hình SPARC:

                                   - oOo -

/* ---------------------------------------------------------------------
* Vi' du. 3:
* ---------------------------------------------------------------------
*/
void bar() {
int buffer[24]; int i;
(*(buffer+239)) += 8;
}

void bar8() { bar(); }
void bar7() { bar8(); }
void bar6() { bar7(); }
void bar4() { bar6(); }
void bar3() { bar4(); }
void bar2() { bar3(); }
void bar1() { bar2(); }
void foo() { bar1(); }

int main() {
int x=0; foo(); x=10;
printf("x = %d\n", x);
return 0;
}

- oOo -

Lý do mà ta dùng nhiều hàm bar() là để buộc hệ điều hành phải dùng giá trị thanh ghi lưu trong stack thay vì dùng giá trị có sẵn trong thanh ghi. Trong các chương trình thực tế, hệ điều hành sẽ thường phải dùng các thanh ghi làm nhiều việc tính toán khác; và vì thế ta không nhất thiết phải có độ gọi hàm sâu như vậy để hiện thực hóa lỗi tràn bộ đệm.

Wednesday, August 17, 2005

Lỗi tràn bộ đệm (3)

3. Buffer overflow trong cấu hình Intel 32-bit

Bạn có thể tham khảo thêm về quá trình gọi hàm và tổ chức stack trong các manuals của Intel cho cấu hình IA-32, đặc biệt là chương 6 của Volume 1: Basic Architecture. Ở đây tôi chỉ tóm tắt các chi tiết chính
.

Trong miền bộ nhớ của một process, đáy stack bắt đầu từ một vị trí nhất định trong bộ nhớ, đỉnh stack thay đổi theo thời gian và được trỏ tới bởi stack pointer ( SP). Giá trị của biến SP nằm trong một thanh ghi (ESP) để truy cập nhanh. Stack chứa vài stack frames. Khi một hàm được gọi, stack frame tương ứng sẽ được PUSH(ed) vào stack. Stack frame của hàm được gọi chứa các tham số, biến cục bộ, và dữ liệu dùng để quay về hàm gọi.

Vì nhiều lý do, các bộ vi xử lý của Intel (IA-32, IA-34), Motorola, SPARC, và MIPS lưu giữ thêm một biến nữa gọi là frame pointer (FP, còn gọi là frame base pointer) trỏ về đáy của frame hiện tại trong stack. Trong cấu hình Intel, thanh ghi chứa base pointer là EBP. Thông thường thì hàm được gọi sẽ chép nội dung của ESP vào EBP trước khi PUSH các biến cục bộ lên stack. Các biến cục bộ, tham số, ... thường được truy cập theo địa chỉ tương đối từ FP.

Trong cấu hình i686 như tôi (và đa số các bạn xài PC) đang dùng thì stack phình xuống dưới vùng địa chỉ thấp, heap phình lên trên địa chỉ cao.

Để nhìn rõ hơn các khái niệm này, ta dịch ví dụ 1 ra Linux assembly dùng
gcc . Trình dịch gcc dùng ngữ pháp AT&T cho assembly file.

 hqn@hanoi (~/BO/Examples) % gcc -S -o e1.s e1.c


Xem file "e1.c", vài dòng đầu tiên của hàm foo là:

foo:
pushl %ebp
movl %esp, %ebp
subl $56, %esp

Biến trong thanh ghi (register) %ebp chính là FP cũ (trước khi gọi hàm foo), thanh ghi %esp chứa SP. Khi gọi một hàm mới, ta

  1. Ghi lại %ebp cũ bằng cách PUSH nó vào stack:
         pushl   %ebp

  1. Chép %esp vào %ebp để có FP mới (cho hàm sắp gọi):
         movl    %esp, %ebp

  1. Rồi chuyển SP "lên" đỉnh stack


(Lưu ý: thông thường thì là thế, nhưng các trình dịch không nhất thiết phải đi theo các bước này, nhất là khi ta chọn cho trình dịch tốt ưu hóa chương trình.)

Như vậy là cái stack frame mới cho foo có kích thước 56 bytes. Tại sao 56 bytes trong khi ta chỉ cần 20 bytes cho biến buffer, 8 bytes cho các biến "i" và "c", và 8 bytes nữa cho FP cũ và return address (tổng cộng 36 bytes)?

Để hiểu rõ hơn ta phải tham khảo các tài liệu về gcc và các yêu cầu về memory alignment của họ i686. Trình dịch gcc của GNU có một thuật toán allocate memory riêng cho từng cấu hình. Chi tiết này không quan trọng trong thảo luận của chúng ta. (Trong cấu hình i386 và i686, bạn có thể dùng chọn lựa

-mpreferred-stack-boundary

của gcc để ép trình dịch align memory theo số bytes nhất định. Các bộ vi xử lý khác cũng có chọn lựa tương tự.)

Điều ta biết (sau khi thí nghiệm vài lần) là trong stack của hàm foo, địa chỉ của buffer sẽ ở 44 bytes thấp hơn địa chỉ trả về của hàm foo (40 bytes từ buffer đến FP cũ, 4 bytes cho FP cũ). Ta có thể tận dụng kiến thức này để viết một đoạn chương trình lạ lùng như sau:

                                   - oOo -

/* ---------------------------------------------------------------------
* Vi' du. 2:
* ---------------------------------------------------------------------
*/
#include <stdio.h>

void foo() {
unsigned char buffer[20]; int i=0; int c;

/* ... ddoa.n ma~ gi` o+? dda^y cu~ng ddu+o+.c */

(*((int *) (buffer+44))) += 5;
}

int main() {
int x=0; foo(); x=1;
printf("x = %d\n", x);
return 0;
}
- oOo -

Dịch và chạy chương trình cho kết quả:

hqn@hanoi (~/BO/Examples) % e2
x = 0

Hừm ..., x=0 chứ không phải 1 ? Cái dòng lệnh

  (*((int *) (buffer+44))) += 5; 

đã làm gì nhỉ? Số là ta đã truy cập đến địa chỉ trả về của hàm foo và tăng nó lên 5, bỏ qua dòng gán x=1. Tại sao ta biết nhảy lên 5 bytes? Hãy thử disassemble chương trình e2 bằng gdb:

                                   - oOo -

hqn@hanoi (~/BO/Examples) % gdb e2
GNU gdb 5.2.1-2mdk (Mandrake Linux)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i586-mandrake-linux-gnu"...
(gdb) disas main
Dump of assembler code for function main:
0x8048352 <main>: push %ebp
0x8048353 <main+1>: mov %esp,%ebp
0x8048355 <main+3>: sub $0x8,%esp
0x8048358 <main+6>: and $0xfffffff0,%esp
0x804835b <main+9>: mov $0x0,%eax
0x8048360 <main+14>: sub %eax,%esp
0x8048362 <main+16>: movl $0x0,0xfffffffc(%ebp)
0x8048369 <main+23>: call 0x804833c <foo>
0x804836e <main+28>: movl $0x1,0xfffffffc(%ebp)
0x8048375 <main+35>: sub $0x8,%esp
0x8048378 <main+38>: pushl 0xfffffffc(%ebp)
0x804837b <main+41>: push $0x80483d8
0x8048380 <main+46>: call 0x8048268 <printf>
0x8048385 <main+51>: add $0x10,%esp
0x8048388 <main+54>: mov $0x0,%eax
0x804838d <main+59>: leave
0x804838e <main+60>: ret
0x804838f <main+61>: nop
End of assembler dump.

- oOo -

Ta thấy sau khi gọi foo ở dòng

 0x8048369 <main+23>:    call    0x804833c <foo>

thì dòng kế tiếp gán 1 vào x. Lệnh này chiếm 5 bytes (từ 0x804836e đến 0x8048375). Dòng lệnh ngộ nghĩnh của chúng ta đã tăng địa chỉ trả về lên 5 bytes! Ta sẽ thử tìm cách dùng ý tưởng này để chạy một shell sau.

Friday, August 12, 2005

Lại một con worm nữa

Lại thêm một con sâu nữa đang lây lan các máy chạy Windows 2000 trên toàn nước Mỹ và vòng quanh thế giới (Đức, và Châu Á). Các chuyên gia tin rằng con worm này tận dụng một lỗ hổng trong dịch vụ plug-and-play của Win2K. Tuần trước Microsoft có một patch lấp lỗ này, nhưng nhiều người chưa kịp lấp.

Kể ra người ta kiên nhẫn với các lỗi phần mềm máy tính một cách khủng khiếp. Ta chẳng bao giờ mua một cái TV mà cứ dăm bữa nửa tháng lại phải cập nhật gì đó, nếu không nó trây ra không chạy, hoặc hở ra là tắt xuống bật lên (re-boot như Win2K bị worm).


WormBlog
có thông tin mới nhất về các loại worm. Infosecdaily thu thập thông tin bảo mật. Security focus vừa phỏng vấn Jose Nazario, tác giả của hai websites này nhân vụ Win2K worm. Jose có viết một bài về tương lai của Internet worm năm 2001 .

Vài bài báo gần đây về worms và worm simulators:

Gần đây tôi rất quan tâm đến vấn đề mô hình hóa worm propagation trong mạng. Khác với các bài báo trên, con worm mà tôi quan tâm sẽ càng lúc càng mạnh hơn khi lây được vào nhiều nốt mới. [Mạnh = có nhiều thông tin hơn để lây vào các hệ thống khó lây.] Đây là một đề tài nghiên cứu thú vị.

Tuesday, August 09, 2005

Lỗi tràn bộ đệm (2)

Trong loạt bài này, tôi sẽ viết thêm (và học thêm) về các chi tiết kỹ thuật của sự tràn bộ đệm và các cách phòng chống. Một phần của loạt bài này tôi viết đã lâu, nhưng chưa bao giờ tìm ra thời gian viết tiếp. Nay nhân vụ Cisco chọi Michael Lynn, khi lỗi trong IOS của Cisco là lỗi tràn bộ đệm kinh điển, tôi nghĩ vẫn có nhu cầu cần phổ cập hóa kiến thức về cái bug này. (Xem thêm bài trình bày của Michael Lynn do một bạn đọc gửi.)

2. Căn bản về buffer overflow với ngôn ngữ C

Lỗi buffer overflow (hay overrun) khá phổ biến trong các chương trình viết bằng C, vì C không cung cấp cấu trúc dữ liệu mảng (array) kèm kích thước và sự kiểm tra kích thước mảng trước khi dùng. Đây là một trong những cái giá phải trả cho sự hiệu quả (efficiency) của C, ngôn ngữ thông dụng nhất cho lập trình hệ thống.

Với mục tiêu minh họa, C là ngôn ngữ lý tưởng cho các thao tác liên quan đến bộ nhớ, stack, heap, ...

Hãy xét một chương trình C nho nhỏ và "vô tội" sau:

                                 - oOo -

/* ---------------------------------------------------------------------
* Vi' du. 1:
* ---------------------------------------------------------------------
*/
#include <stdin.h>

void foo() {
unsigned char buffer[20]; int i=0; int c;
while ((c = getc(stdin)) != EOF) {
buffer[i++] = (unsigned char) c;
}
buffer[i] = '\0';
printf("You entered %s\n", buffer);
}

int main() {
foo();
return 0;
}

- oOo -

Ta dĩ nhiên có thể dùng stdin làm input cho chương trình. Tuy vậy, để làm ví dụ ta tạo một file dữ liệu làm input cho chương trình này. Tạo một file tên là data với nội dung chỉ có một dòng:

01234567890123456789012345678901234567 

Sau khi dịch, bạn thử chạy chương trình:

hqn@hanoi  (~/BO/Examples) % e1 < data
You entered 01234567890123456789012345678901234567

Không có vấn đề gì hết. Chương trình làm cái nó được thiết kết để làm. Thử cái khác nhé. Làm cho data dài hơn một byte:

hqn@hanoi  (~/BO/Examples) % more data
01234567890123456789012345678901234567*
hqn@hanoi (~/BO/Examples) % e1 < data
You entered 01234567890123456789012345678901234567*
Segmentation fault
hqn@hanoi (~/BO/Examples) %

Tôi chỉ thêm vào data một byte (dấu *) và chương trình đã bị lỗi phân đoạn (segmentation fault). Dễ thấy rằng segmentation fault là do ta đọc vào nhiều bytes hơn là ta đã dành cho buffer (20 bytes). Đây chính là nội dung cơ bản của sự tràn bộ đệm.

Segmentation fault thường xuất hiện khi ta truy cập phần bộ nhớ không thuộc về process hiện hành. Thế nhưng tại sao data file đầu tiên có đến 38 bytes thì vẫn chưa bị tràn mà 39 bytes thì lại bị, trong khi ta chỉ có 20 bytes cho buffer? Ta sẽ trả lời câu hỏi này sau.

Cũng cần chú ý rằng tôi đang dùng gcc phiên bản 3.2. Với gcc phiên bản khác và cấu hình máy khác thì con số 38, 39 có thể khác do stack alignment khác nhau (xem thêm về
data alignment trên MSDN). Tuy vậy nguyên tắc overflow này không đổi. Nếu 38 không phải là con số của bạn thì bạn có thể thí nghiệm tăng từ từ kể tử 21 lên đến con số cần thiết cho overflow.

Có thể thấy từ một ví dụ đơn giản này rằng một chương trình nào đó của hệ thống (httpd chẳng hạn), nếu lập trình cẩu thả là đã có thể bị phá hoại dễ dàng. Trong trường hợp này, ít nhất ta có thể làm nó ngưng chạy bằng cách cung cấp nhiều dữ liệu hơn dự tính.


Bạn có thể nghĩ rằng đọc từng byte từng byte một có vẻ không thực tế lắm. Không hẳn thế! Nhiều network protocols dùng các ký tự đặc biệt như '\n' hay '\r' để đánh dấu kết thúc một field nào đó trong packet header. Các TCP sockets cũng chỉ là file descriptors, và ta thường phải kiểm tra từng byte cho đến khi thấy ký tự giới hạn.

Sunday, August 07, 2005

CRA khởi tạo một blog mới

Blog này của CRA sẽ có nhiều thông tin bổ ích liên quan đến khoa học tính toán (computing science) như: tình trạng việc làm, lương bổng, các nhánh nghiên cứu, ... của KHMT và các ngành có liên quan.

Một thống kê cho biết tiền tài trợ nghiên cứu KHMT cho các trường đại học ở Mỹ được khoảng 1.3 tỉ USD trong năm 2003, trong đó đa phần vẫn là tài trợ của liên bang (tiền nhà nước). Được nhiều tiền tài trợ nhất vẫn là các ngành y/sinh học.

Tôi thắc mắc con số tương ứng cho Việt Nam là bao nhiêu, so tương đối với tỉ lệ ngân sách quốc gia thì VN hay Mỹ dành cho các đại học nhiều tiền nghiên cứu hơn?

Theo một số liệu báo cáo mới đây, tôi được biết tiền dành cho nghiên cứu khoa học của Việt Nam chiếm 0,86% GDP. Như vậy là rất ít. Cũng theo các nhà chức trách, nhà nước sẽ phấn đấu dành tiền cho nghiên cứu khoa học chiếm 1,5% GDP, tức là tương đương với 10.000 tỉ VND.

Một nước có phát triển hay không thì đòi hỏi nghiên cứu khoa học sáng tạo phải phát triển. Và muốn như thế thì phải đầu tư cho các nhà khoa học và cả cho chương trình giáo giục nữa.

Hy vọng là trong tương lai gần, chúng ta sẽ dễ dàng xin được nguồn vốn tài trợ của nhà nước nói chung và trong trường, viện nghiên cứu nói riêng để phục vụ cho các công trình nghiên cứu.