lập trình an toàn - secure programming
TRANSCRIPT
Lương Ánh Hoàng [email protected]
LẬP TRÌNH AN TOÀN Secure Programming
• Cung cấp các kiến thức, kỹ thuật cơ bản để xây dựng các ứng dụng an toàn.
Mục đích
2
• Yêu cầu về kiến thức: – An ninh mạng – Ngôn ngữ lập trình C/C++.
• Lên lớp đầy đủ
Yêu cầu
3
• Thời lượng: 45 tiết – Lý thuyết: 30 tiết – Bài tập:15 tiết
Thời lượng môn học
4
• Secure Program Cookbook for C and C++, Matt Messier, John Viega, O'Reilly 2003.
Tài liệu
5
• Chương 1. Kiểm tra đầu vào • Chương 2. Kiểm soát truy nhập • Chương 3. Kiểm soát xung đột • Chương 4. Mã hóa đối xứng • Chương 5. Hàm băm và xác thực thông điệp • Chương 6. Mã hóa công khai • Chương 7. Anti-‐Tampering • Chương 8. Các vấn đề khác
Nội dung
6
7
• Bài tập lớn: 70% • Quá trình: 30%
Đánh giá
8
Lương Ánh Hoàng [email protected]
Chương 1. Kiểm tra đầu vào Input Validation
1.1 Nguyên tắc kiểm tra. 1.2 Các hàm định dạng xâu (string formatting) . 1.3 Tràn bộ đệm. 1.4 Tràn số học. 1.5 Kiểm tra tên }ile và đường dẫn. 1.6 Giải mã URL 1.7 Cross-‐Site Scripting 1.8 SQL Injection
Nội dung
10
• Luôn luôn giả định dữ liệu đầu vào là không đáng tin cậy – Dữ liệu từ mạng trong mô hình client-‐server – Dữ liệu từ người dùng – Dữ liệu từ tệp tin – …
• Ưu tiên loại bỏ dữ liệu hơn là cố gắng sửa chữa dữ liệu. • Thực hiện kiểm tra đầu vào tại nhiều cấp, nhiều điểm
– Kiểm tra đầu vào ở các hàm – Kiểm tra đầu vào giữa các module. – …
• Không tiếp nhận lệnh trực tiếp từ người dùng nếu chưa qua kiểm tra. • Kiểm tra các ký tự đặc biệt, dấu nháy. • Tìm hiểu và sử dụng cơ chế trích dẫn (quoting mechanism) nếu cần. • Càng hiểu về dữ liệu bao nhiêu càng lọc được tốt bấy nhiêu.
1.1 Các nguyên tắc kiểm tra
11
• Họ các hàm printf() , syslog() cho phép định dạng dữ liệu rất mềm dẻo và mạnh mẽ tuy nhiên cũng cực kỳ nguy hiểm.
• Thận trọng khi sử dụng “%n” – Tham số %n cho phép ghi ra số lượng ký tự đã kết xuất được ra một địa chỉ bất kỳ chỉ ra
trong tham số tương ứng. Nếu không tồn tại tham số nào thì printf sẽ ghi đè lên một vùng nào đó thuộc stack của luồng đang thực thi.
– VD. int counter = 0; printf(“Hello%n”,&counter); // OK, counter = 5 printf(“Hello%n”); // Nguy hiểm !!!
• Không sử dụng trực tiếp xâu định dạng từ nguồn bên ngoài – Xâu định dạng có nguồn gốc từ ngoài chương trình có thể có một vài ký tự đặc biệt mà
chương trình chưa lường trước được, hoặc không có tham số thay thế tương ứng. – VD.
char str[1024]; gets(str); printf(“Xin chao:”); printf(str); // Nguy hiểm !!! printf(“%s”,str); // OK
1.2 Các hàm định dạng xâu
12
• Thận trọng khi sử dụng sprintf, vsprintf với “%s” – Các hàm trên đều giả định kích thước bộ đệm cho xâu đích là vô hạn. – Nên chỉ rõ số lượng ký tự tối đa sẽ sử dụng khi dùng với %s. – Nên sử dụng snprintf, vsnprintf nếu có thể. – VD
char str[1024]; char dst[32]; gets(str); sprintf(dst,”Xau vua nhap vao la %s”,str); // Nguy hiểm sprintf(dst,”Xau vua nhap vao la %.16s”,str); // OK snprintf(dst,32,”Xau vua nhap vao la %s”,str);// OK
1.2 Các hàm định dạng xâu
13
• Tràn bộ đệm (Buffer Over}low): copy dữ liệu vượt quá biên của một bộ đệm nào đó => đè lên vùng nhớ của biến (cấu trúc) khác.
• Phần lớn các hàm xử lý xâu trong C đều không thực hiện kiểm tra biên của bộ đệm: gets, strcpy, …
• VD1: Dữ liệu bị hỏng int x = 0; char buff[8]; strcpy(buff,”Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAA”); printf(“%d”,x);
• VD2: Stack bị hỏng char name[8]; gets(name); printf(name);
1.3 Tràn bộ đệm
14
• VD3: Không trở về được từ chương trình con void Hello() { char name[8]; printf(“What is your name ?”); gets(name); printf(“Hello %s !”, name); } void main() { Hello(); printf(“Bye”); }
1.3 Tràn bộ đệm
15
• VD4: Tấn công có chủ ý trên bộ đệm void Bye() { printf(“Bye”); } void Hello() { void (*p)() = Bye; char name[8]; printf(“What is your name ?”); gets(name); printf(“Hello %s !”, name); p(); } void main() { Hello(); }
1.3 Tràn bộ đệm
16
• Giải pháp: – Sử dụng các hàm strncpy, memcpy…và những hàm có kiểm soát kích thước bộ đệm một cách tường minh.
– Sử dụng Stack Guard trong các trình biên dịch hỗ trợ. – Sử dụng DEP (Data Execution Preventation) trên hệ điều hành hỗ trợ. – Sử dụng ASLR (Address Space Layout Randomization) trên trình biên dịch và hệ điều hành hỗ trợ.
1.3 Tràn bộ đệm
17
• Dữ liệu nhận về có thể có sai sót trong trường liên quan đến kích thước.
• Các thao tác liên quan đến số nguyên lớn có thể bị tràn, lẫn lộn giữa số nguyên không dấu và có dấu
• VD1: Tràn số unsigned int x = 0xFFFFFFFF; // MAX_INT if ( x+5 > 5 ) printf (“X > 0” )
else printf(“X < 0”); • VD2: Dùng sai kiểu có/không dấu if (x < MAX_SIZE) { // x, số byte cần cấp phát tùy theo giải thuật tính được
if (!(ptr = (unsigned char *)malloc(x))) abort( ); } else {
/* Handle the error condition ... */ }
1.4 Tràn số học
18
• Dữ liệu nhận về có thể là tên }ile, ứng dụng cần xác định đường dẫn tuyệt đối nếu cần thiết.
• Dùng hàm realpath() trên Unix/Linux và GetFullPathName trên Windows.
• Sử dụng realpath() § Nguyên mẫu:
char *realpath(const char *pathname, char resolved_path[MAXPATHLEN]); § Thận trọng: Có thể tràn resolved_path và không thread-‐safe. § Thư viện: stdlih.h § VD
char resolved[1024]; char * result = realpath("printf.c",resolved); printf("%s",result);
1.5 Kiểm tra tên qile và đường dẫn
19
• Sử dụng GetFullPathName() § Thư viện: windows.h § Nguyên mẫu:
DWORD GetFullPathName(LPCTSTR lpFileName, DWORD nBufferLength, LPTSTR lpBuffer, LPTSTR *lpFilePath);
§ VD: int nBufferLen = 0; LPTSTR lpBuffer;
nBufferLen = GetFullPathName(L"test.c",0,0,0); if (nBufferLen>0) { lpBuffer = new TCHAR[nBufferLen+1]; GetFullPathName(L"test.c",nBufferLen,lpBuffer,0); wprintf(L"%s",lpBuffer); }
1.5 Kiểm tra tên qile và đường dẫn
20
• RFC 1738 quy định cách mã hóa các ký tự không nhìn thấy được trong URL dưới dạng “%<Mã hexa>”.
• VD: http://m%61il.google.com http://m%25%36%31il.google.com
• Cách giải mã: duyệt từ đầu đến cuối , tìm các ký tự % và thay thế bằng mã ASCII tương ứng.
• Không sử dụng các hàm xử lý xâu chuẩn vì có thể có ký tự NULL trong URL.
1.6 Giải mã URL
21
• Cross-‐Site Scripting (XSS) là hình thức tấn công vào trình duyệt người dùng bắt nguồn từ việc kiểm tra lỏng lẻo từ server.
• Có thể dẫn đến thất thoát thông tin nhạy cảm: mật khẩu, session, cookie…
• Thực hiện bằng cách chèn mã HTML/JAVASCRIPT vào dữ liệu sẽ hiển thị ra trình duyệt => đoạn mã sẽ chạy trên trình duyệt của nạn nhân.
• VD. Một ứng dụng web có hai trang – Hello.php: Hiển thị form và nhận tên của người dùng. – Chao.php: hiển thị tên nhận được lại cho người dùng.
1.7 Cross-‐Site Scripting
22
• File Hello.php <HTML> Xin chào, vui lòng nhập tên bạn <FORM action="chao.php" method=“POST"> <input type="text" name="name"/><br/> <input type="submit" value="Submit"> </FORM> </HTML>
• File Chao.php <?PHP echo "Xin chao ".$_POST['name']; ?>
• Demo • Với tên là : Secure • Với tên là: Secure <script>alert('XSS was found !');</script> • Với tên là: Secure <s%63ript>alert(‘Hacked’);</s%63ript>
• Giải pháp: Lọc bỏ các thẻ HTML khỏi dữ liệu từ người dùng. Mỗi ngôn ngữ lập trình có một cách riêng.
1.7 Cross-‐Site Scripting
23
• SQL Injection: Tấn công vào CSDL thông qua dữ liệu nhập từ trình duyệt.
• Lợi dụng việc kiểm tra lỏng lẻo từ đầu vào, chèn mã lệnh SQL vào các truy vấn đến CSDL của ứng dụng web.
• Thường lợi dụng dấu nháy “ ‘ “ để kết thúc câu truy vấn SQL hoặc thêm các câu truy vấn khác.
• VD: Lệnh so sánh tên và mật khẩu trong SQL – select * from users where username = ‘$user’ and password = ‘$pass’ – Nếu $user hoặc $pass chứa dấu “’” thì SQL sẽ hiểu nhầm nội dung truy vấn…
• Các kỹ thuật khai thác: An ninh mạng • VD: Một ứng dụng web muốn kiểm tra tên và mật khẩu gồm hai trang
– ask.php: Hiện form đăng nhập và thu nhận tên, mật khẩu – login.php: Kết nối đến CSDL và kiểm tra
1.8 SQL Injection
24
• VD (tiếp – File ask.php
<HTML> Vui long nhap ten va mat khau <FORM action="login.php" method="GET"> Ten: <INPUT type="text" name="user"/><BR/> Mat khau:<INPUT type="text" name="pass"/><BR/> <INPUT type="submit" name="Submit"> </FORM>
1.8 SQL Injection
25
• VD (tiếp) – File login.php
<?PHP $db_server = "localhost"; $db_username= "root"; $db_password= "123456"; $db = "test"; $table = "users"; $conn = mysql_connect($server,$db_username,$db_password); if (!$conn) { echo "Khong ket noi dc den CSDL"; return; } $ret = mysql_select_db($db,$conn);
1.8 SQL Injection
26
if (!$ret) { echo "Khong ton tai CSLD tuong ung"; return; } $user = $_GET['name']; $pass = $_GET['pass']; $sql = "select * from $table where username='$user' and password='$pass'"; echo $sql; $ret =mysql_query($sql,$conn); if (mysql_num_rows($ret)>0) echo "Dang nhap thanh cong"; else echo "Sai ten hoac mat khau";
?>
1.8 SQL Injection
27
• Tấn công – username = a’ or ‘1’=‘1 – password = b’ or ‘1’=‘1 – …
• Phòng chống – Loại bỏ tất cả các dấu ‘ và các ký tự đặc biệt nếu cần. – Sử dụng escaped string
• Với php/mysql: mysql_real_escape_string, hoặc thêm ‘\’. • Với SQL server: thêm ký tự ‘ trước ký tự đặc biệt. • Với Oracle DB: thêm ký tự ‘\’ trước ký tự đặc biệt.
1.8 SQL Injection
28
Lương Ánh Hoàng [email protected]
Chương 2. Kiểm soát truy nhập Access Control
2.1 Cơ chế kiểm soát truy nhập trên Unix/Linux 2.2 Cơ chế kiểm soát truy nhập trên Windows 2.3 Hạ thấp quyền truy nhập của tiến trình 2.4 Xóa }ile an toàn 2.5 Hạn chế quyền truy nhập trên }ile 2.6 Khóa }ile 2.7 Tạo }ile tạm 2.8 Hạn chế truy nhập đến hệ thống }ile
Nội dung
30
• Trên Unix/Linux tất cả các tài nguyên đều được coi là }ile: tệp tin, ổ đĩa, bộ nhớ, thiết bị….
• Mỗi }ile kiểm soát bởi user id và group id. • Mỗi tiến trình có ba quyền: effective user id, real user id, saved user id.
Effective user id được sử dụng trong phần lớn các kiểm tra. • Mỗi tiến trình cũng thuộc về ba nhóm: effective group id, real group id,
saved group id. • Có ba loại quyền
• Đọc (read) • Ghi (write) • Thực thi (execute)
2.1 Cơ chế kiểm soát truy nhập trên Unix/Linux
31
• Mỗi }ile sẽ có ba nhóm quyền tương ứng với: user id, group id, và other.
-‐rwxr-‐xr-‐x 1 Luong Anh Hoang None 17964 Aug 28 23:45 test.exe • Khi tiến trình tạo một }ile hoặc tài nguyên, hệ thống sẽ gán user id và
group id cho }ile mới đó bằng effective user id và effective group id của tiến trình.
• Khi tiến trình truy nhập một }ile hoặc tài nguyên, hệ thống sẽ lần lượt so sánh user id, group id của tiến trình và }ile và chọn ra tập quyền tương ứng. Nếu không khớp thì lớp quyền thứ 3 sẽ được sử dụng.
2.1 Cơ chế kiểm soát truy nhập trên Unix/Linux
32
• Mỗi }ile cũng có thể có 3 bit đặc biệt • Sticky. Nếu bit này được thiết lập, người dùng sẽ không thể xóa hay đổi tên }ile của người khác nằm trong thưc mục mà người dùng quản lý. Mặc định là không được thiết lập.
• Setuid: Bit này liên quan đến quá trình tạo một tiến trình mới. Nếu bit này được thiết lập, tiến trình được tạo từ }ile này sẽ không kế thừa quyền từ tiến trình cha, mà sẽ có quyền từ user id của chính }ile đó.
• Setgid: • Đối với }ile thực thi, nếu bit này được thiết lập thì một tiến trình mới được tạo sẽ có quyền từ groupd id của }ile đó chứ không kế thừa từ tiến trình cha (tương tự Setuid).
• Đối với thưc mục, nếu bit này được thiết lập thì các }ile tạo trong thư mục này sẽ có groupd id của thư mục cha, chứ không kế thừa từ tiến trình tạo ra }ile đó.
2.1 Cơ chế kiểm soát truy nhập trên Unix/Linux
33
• Windows sử dụng ACL: Access Control List để phân quyền tài nguyên.
• Các tài nguyên của Windows: }ile, registry, mutex, event, IPC… được kiểm soát thông qua DACL và SACL.
• DACL là danh sách các ACE, mỗi ACE là một luật quy định một quyền hạn cụ thể.
• DACL rỗng tương đương với việc tất cả mọi người có toàn quyền truy nhập tới đối tượng.
• Mỗi ACE bao gồm 3 thông tin: • SID: Đại diện cho một user hay một group trong hệ thống • Quyền truy nhập • Giá trị boolean tương ứng với cho phép hay không cho phép.
2.2 Cơ chế kiểm soát truy nhập trên Windows
34
• Các quyền truy nhập
2.2 Cơ chế kiểm soát truy nhập trên Windows
35
TÊN Diễn giải DELETE The ability to delete the object
READ_CONTROL The ability to read the object's security descriptor, not including its SACL
SYNCHRONIZE The ability for a thread to wait for the object to be put into the signaled state; not all objects support this functionality
WRITE_DAC The ability to modify the object's DACL WRITE_OWNER The ability to set the object's owner
GENERIC_READ The ability to read from or query the object
GENERIC_WRITE The ability to write to or modify the object
GENERIC_EXECUTE The ability to execute the object (applies primarily to }iles)
GENERIC_ALL Full control
• Ví dụ ACE • DENY GENERIC_ALL Everyone: Cấm mọi quyền với group Everyone • ALLOW GENERIC_WRITE Marketing: Cho phép nhóm group Marketing được quyền ghi
• Mỗi đối tượng đều có một Owner, chính là người tạo ra đối tượng.
• Owner có toàn quyền với đối tượng bất kể trong DACL có cấm hay không.
• Owner có thể bị chiếm bởi user khác.
2.2 Cơ chế kiểm soát truy nhập trên Windows
36
• Nếu một tiến trình có đặc quyền cao, thực hiện các thao tác nguy hiểm => cần hạ thấp quyền trước khi thực hiện.
• Tiến trình có thể kiểm tra real user id, real group id bằng lệnh getuid (), getgid(). Đây là các đặc quyền kế thừa từ tiến trình cha.
• Tiến trình có thể kiểm tra effective user id và effective group id bằng lệnh geteuid() và getegid(). Đây thường là user id có đặc quyền cao hơn (do được khởi chạy từ super user, hoặc các bit setuid được bật).
• Tiến trình từ bỏ đặc quyền bằng việc thiết lập group mới chính là real user id qua lệnh • setgroups(): Thiết lập lại nhóm của tiến trình. • setegid(): Thiết lập lại effective group id của tiến trình. • seteuid(): Thiết lập lại effective user id của tiến trình.
2.3 Hạ thấp quyền truy nhập của tiến trình
37
• Thông thường, một }ile sau khi xóa sẽ được hệ điều hành đánh dấu là xóa, nội dung chưa hoàn toàn bị loại bỏ trên đĩa.
• Giải pháp • Ghi đè thông tin khác nhiều lần lên đĩa. • Ghi đè dữ liệu ngâu nhiên nhiều lần lên đĩa. • Ghi đè sử dụng mẫu định sẵn lên đĩa. • Sau mỗi chu kỳ ghi, sử dụng fsync để đồng bộ với đĩa, vô hiệu hóa cơ chế cache. Hoặc lệnh f}lush() nếu sử dụng thư viện C.
• Một vài mẫu được sử dụng • static unsigned char single_pats[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
• static unsigned char triple_pats[6][3] = { { 0x92, 0x49, 0x24 }, { 0x49, 0x24, 0x92 }, { 0x24, 0x92, 0x49 }, { 0x6d, 0xb6, 0xdb }, { 0xb6, 0xdb, 0x6d }, { 0xdb, 0x6d, 0xb6 } };
2.4 Xóa qile an toàn
38
• Unix/Linux sử dụng umask cho mỗi tiến trình để vô hiệu hóa một vài bit khi tiến trình tạo }ile.
• Hàm fopen, open luôn luôn tạo }ile với quyền 666. • Giả sử tiến trình muốn tạo }ile với quyền 666:
requested_permissions = 0666; actual_permissions = requested_permissions & ~umask( );
• Ứng dụng thay đổi umask bằng hàm umask() trước khi thực hiện lời gọi tạo }ile. #include <sys/types.h> #include <sys/stat.h> mode_t umask(mode_t mask);
2.5 Hạn chế quyền truy nhập trên qile
39
• Tiến trình muốn kiểm soát truy nhập trên một phần của }ile hay toàn bộ }ile để tránh xung đột khi có nhiều tiến trình cùng truy nhập trên }ile.
• Unix/Linux cung cấp cơ chế khóa mềm: Mọi tiến trình đều có quyền giành được khóa và thao tác trên }ile, tuy nhiên không phải tiến trình nào cũng tuân thủ theo khóa và có thể phá hỏng dữ liệu của tiến trình khác.
• Windows thực hiện vấn đề này tốt hơn bằng khóa cứng. Có hai loại khóa: • Shared Lock: Cho phép các tiến trình khác (kể cả tiến trình giành được khóa) đọc nhưng không được ghi vào một phần đã khóa của }ile.
• Exclusive Lock : Cấm tất cả các tiến trình khác không được đọc hay ghi vào phần đã khóa của }ile. Tiến trình giành được khóa có quyền đọc hoặc ghi vào }ile.
2.6 Khóa qile
40
• Các hàm khóa }ile trên Windows • LockFile, UnlockFile: Khóa và mở khóa đồng bộ, sẽ không trở về đến khi giành được khóa hoặc mở được khóa.
• LockFileEx, UnlockFileEx: Khóa và mở khóa đồng bộ hoặc bất đồng bộ. • Khóa }ile cũng có thể xác định lúc tạo lập/truy nhập }ile thông qua hàm
CreateFile. • Đoạn chương trình sau sẽ mở một }ile để đọc với chế độ Shared Lock.
char buff[1024]; DWORD bytesRead = 0; HANDLE fileHandle = NULL; fileHandle = CreateFile(L"C:\\SecureProgramming\\Test.txt", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
2.6 Khóa qile
41
• VD (tiếp) ReadFile(fileHandle,buff,128,&bytesRead,0); buff[bytesRead] = 0; printf("File content:%s\n",buff); LockFile(fileHandle,0,0,100,0); // Exclusive Lock printf("File is locked, press any key to unlock...\n"); getch(); UnlockFile(fileHandle,0,0,100,0); printf("File is unlocked\n"); getch(); CloseHandle(fileHandle);
2.6 Khóa qile
42
• Ứng dụng tạo }ile tạm để lưu trữ thông tin tạm thời của chương trình. • File tạm nên được tạo lập một cách an toàn, và xóa khi kết thúc chương
trình. • Trên unix/linux:
• Hàm mkstemp() có thể sử dụng để tạo }ile tạm với tên ngẫu nhiên. • Ứng dụng cần xóa }ile theo tên, ngay sau lời gọi mkstemp để đảm bảo không tiến
trình nào truy nhập được. • Sau khi tiến trình kết thúc một cách bình thường/không bình thường, }ile tạm sẽ
không thể truy nhập được nữa. • VD
char szPath[] = “fileXXXXXX"; int fd; fd = mkstemp(szPath); unlink(szPath); printf("Temperary file created, press any key to continue..."); write(fd,"Hello",5); close(fd);
2.7 Tạo qile tạm
43
• Trên Windows: • Không có hàm tương đương mkstemp() • GetTempFileName() sinh tên }ile ngẫu nhiên nhưng dễ đoán. • GetTempPath() lấy đường dẫn đến thư mục tạm của người dùng hiện tại. • Tạo }ile bằng hàm CreateFile với hai thuộc tính FILE_ATTRIBUTE_TEMPORARY và
FILE_FLAG_DELETE_ON_CLOSE • VD
HANDLE }ileHandle = NULL; }ileHandle = CreateFile(L"C:\\SecureProgramming\\Tmp.txt",
GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY| FILE_FLAG_DELETE_ON_CLOSE, 0);
2.7 Tạo qile tạm
44
• Trên Unix/Linux, ứng dụng có thể tự giới hạn phạm vi truy nhập hệ thống tệp tin của mình bằng lệnh chroot()
• Sau khi gọi chroot(): • Tiến trình không thể mở rộng phạm vi truy nhập bằng lệnh chroot lần nữa.. • Tiến trình chỉ có thể thu hẹp hơn nữa phạm vi truy nhập của mình. • Tiến trình phải chủ động gọi thêm chdir() để lệnh chroot có hiệu lực.
• VD: #include <unistd.h> chroot("/new/root/directory"); chdir("/");
2.8 Hạn chế truy nhập đến hệ thống qile
45
Lương Ánh Hoàng [email protected]
Chương 3. Kiểm soát xung đột Synchronization Technique
3.1 Khái niệm 3.2 Đoạn găng (Critical Section). 3.3 Đèn hiệu (Semaphore) 3.4 Mutex 3.5 Event 3.6 SRW Lock
Nội dung
47
§ Xung đột là vấn đề phát sinh khi nhiều luồng của chương trình chạy trên bộ vi xử lý đa nhân cùng truy nhập một tài nguyên hệ thống.
§ Ví dụ
3.1 Khái niệm
48
int x = 0; DWORD WINAPI Thread(LPVOID lpParam) {
for (int i=0;i<2000000;i++) x++; return 0;
} int _tmain(int argc, _TCHAR* argv[]) {
HANDLE hThread1 = CreateThread(NULL,NULL,Thread,NULL,NULL,NULL); HANDLE hThread2 = CreateThread(NULL,NULL,Thread,NULL,NULL,NULL); WaitForSingleObject(hThread1,INFINITE); WaitForSingleObject(hThread2,INFINITE); printf("X=%d",x); getch(); return 0;
}
§ Đoạn găng (Critical Section) là đoạn chương trình được bảo vệ bởi hệ điều hành sao cho tại mọi thời điểm chỉ có một luồng được phép thực thi.
§ Sử dụng § Khai báo: CRITICAL_SECTION cs § Khởi tạo: InitializeCriticalSection(&cs) § Sử dụng
EnterCriticalSection(&cs) // Bắt đầu đoạn găng … // Kết thúc đoạn găng LeaveCriticalSection(&cs) // DeleteCriticalSection(&cs)
§ Ví dụ § Đặc điểm: chỉ có tác dụng trong cùng một tiến trình
3.2 Đoạn găng
49
§ Đèn hiệu (Semaphore) dùng để giới hạn số lượng luồng tối đa được phép thực thi cùng một đoạn chương trình.
§ Sử dụng § Khai báo: HANDLE hSemaphore § Khởi tạo: hSemaphore = CreateSemaphore(NULL,5,5,NULL) § Sử dụng
WaitForSingleObject(hSemaphore, 0L) // … // ReleaseSemaphore(hSemaphore,1,NULL) // CloseHandle(hSemaphore)
§ Ví dụ § Đặc điểm
§ Dùng chung được giữa các tiến trình § Tốc độ chậm hơn CRITICAL_SECTION
3.3 Đèn hiệu
50
§ Mutex dùng để bảo vệ tài nguyên của chương trình, tại một thời điểm chỉ cho phép một luồng của một tiến trình truy nhập.
§ Sử dụng § Khai báo: HANDLE hMutex; § Khởi tạo: hMutex = CreateMutex(NULL, FALSE, NULL) § Sử dụng
WaitForSingleObject(hMutex, INFINITE) // … // ReleaseMutex(hMutex)
§ Ví dụ § Đặc điểm
§ Chậm hơn CRITICAL_SECTION § Có thể đồng bộ giữa các tiến trình
3.4 Mutex
51
§ Event dùng đồng bộ hoạt động của các luồng thông qua cơ chế báo hiệu.
§ Sử dụng § Khai báo: HANDLE hEvent; § Khởi tạo: hMutex = CreateEvent(NULL, TRUE, FALSE, “MyEvent”) § Sử dụng
WaitForSingleObject(hEvent, INFINITE) // … SetEvent(hEvent) // Báo hiệu các luồng khác ResetEvent(hEvent) // Chặn các luồng khác … // CloseHandle(hEvent)
§ Ví dụ
3.5 Event
52
§ SRW Lock (Slim Reader Writer Lock )dùng đồng bộ hoạt động của các luồng thông tương tự như đoạn găng
§ Sử dụng § Khai báo: SRWLOCK lock; § Khởi tạo: InitializeSRWLock(&lock); § Sử dụng
AcquireSRWLockShared(&lock) AcquireSRWLockExclusive(&lock) // … // ReleaseSRWLockShared(&lock) ReleaseSRWLockExclusive(&lock)
§ Ví dụ
3.5 SRW Lock
53
1. Viết chương trình chat Client và Server với kênh truyền đã mã hóa bằng thuật toán AES-‐256, sử dụng thư viện OpenSSL và CryptoAPI. Mật khẩu mã hóa là : 123456. Việc mã hóa và việc nhận dữ liệu được thực hiện đồng thời trên 2 luồng riêng biệt, sử dụng cơ chế đồng bộ CRITICAL_SECTION
2. Viết chương trình mã hóa và giải mã tệp tin theo thuật toán AES-‐256, với tên nhập từ bàn phím, mật khẩu mã hóa là :nopass. Sử dụng thư viện OpenSSL và CryptoAPI
Bài tập
54
Lương Ánh Hoàng [email protected]
Chương 4. Mã hóa đối xứng Symmetric Crytography
4.1 Biểu diễn khóa 4.2 Chuyển đổi chuỗi hexa và khóa nhị phân. 4.3 Mã hóa và giải mã Base64 4.4 Các phương pháp mã hóa đối xứng 4.5 Mã hóa đối xứng với OpenSSL 4.6 Mã hóa đối xứng với Microsoft Crypto API
Nội dung
56
• Khóa đối xứng: Một số rất lớn sử dụng để mã hóa và giải mã thông điệp.
• Biểu diễn khóa: • Phân tách thành các byte và lưu dưới dạng một mảng.
unsigned char key[KEYLEN_BYTES] • Biểu diễn dưới dạng số nguyên lớn nếu khóa có chiều dài 64-‐bit
long long key • Biểu diễn dưới dạng chuỗi chữ số hexa
char key[]=“AF12B5C7E0…” • Biểu diễn dưới dạng xâu ASCII (mật khẩu).
char key[]=“secret!!!” • Lưu ý về tính “endian” của máy thực hiện mã hóa.
4.1 Biểu diễn khóa
57
• Chuyển đổi khóa nhị phân sang dạng chuỗi chữ số hexa #define MAX_KEY_LEN 32 unsigned char key[MAX_KEY_LEN]; char result[MAX_KEY_LEN*2+1]; for (int i=0;i<MAX_KEY_LEN;i++) sprintf(result+i*2,"%2X",key[i]); printf("Key:%s",result);
4.2 Chuyển đổi chuỗi hexa và khóa nhị phân
58
• Chuyển đổi chuỗi hexa sang khóa nhị phân
char Hex2Dec(char c) { if (('a'<=c)&&(c<='z')) return c -‐ 'a'+10; if (('A'<=c)&&(c<='Z')) return c -‐ 'A'+10; if (('0'<=c)&&(c<='9')) return c -‐ '0'; return -‐1; } … #define MAX_KEY_LENGTH 32 char hexa[]="AF125C4D8E"; unsigned char key[MAX_KEY_LENGTH]; int keylen = strlen(hexa); char c1,c2; if ((keylen%2!=0)||(keylen/2 >
MAX_KEY_LENGTH)) printf("Invalid key length"); keylen = keylen/2; for (int i=0;i<keylen;i++) {
c1 = Hex2Dec(hexa[i*2]); c2 = Hex2Dec(hexa[i*2+1]); if ((c1==-‐1)||(c2==-‐1)) { printf("Invalid character !!!"); break; }; key[i] = (c1<<4)|c2;
};
4.2 Chuyển đổi chuỗi hexa và khóa nhị phân
59
• Mã hóa Base64 • Sử dụng 6-‐bit để mã hóa dữ liệu và biểu diễn dưới dạng các chữ cái ASCII. • Cứ 3 byte dữ liệu vào sẽ được biểu diễn thành 4 byte dữ liệu ra. • Các ký tự ra nằm trong khoảng:
• ‘A’ – ‘Z’ tương đương các giá trị của từ mã từ 0-‐25. • ‘a’ – ‘z’ tương đương các giá trị của từ mã từ 26-‐51. • ‘0’-‐ ‘9’ tương đương các giá trị từ mã từ 52-‐61. • ‘+’ , ‘-‐’ tương ứng với các giá trị mã 62,63.
• Nếu dữ liệu vào có kích thước không chia hết cho 3 sẽ thì được thêm vào bằng ký tự ‘=‘.
• VD Dữ liệu gốc: ‘A’ – 0100.0001 Dữ liệu mã hóa dạng Base64: 010000.010000.000000.000000 ~ QQ== Dữ liệu gốc: ‘AA’ – 0100.0001.0100.0001 Dữ liệu mã hóa dạng Base64: 010000.010100.000100.000000 ~ QUE= Dữ liệu gốc: ‘AAA’ – 0100.0001.0100.0001.0100.0001 Dữ liệu dạng mã hóa Base64: 010000.010100.000101.000001 ~ QUFB
4.3 Mã hóa và giải mã Base64
60
• Mã hóa Base64
4.3 Mã hóa và giải mã Base64
61
Value Char
Value Char
Value Char
Value Char
0 A 16 Q 32 g 48 w
1 B 17 R 33 h 49 x
2 C 18 S 34 i 50 y
3 D 19 T 35 j 51 z
4 E 20 U 36 k 52 0
5 F 21 V 37 l 53 1
6 G 22 W 38 m 54 2
7 H 23 X 39 n 55 3
8 I 24 Y 40 o 56 4
9 J 25 Z 41 p 57 5
10 K 26 a 42 q 58 6
11 L 27 b 43 r 59 7
12 M 28 c 44 s 60 8
13 N 29 d 45 t 61 9
14 O 30 e 46 u 62 +
15 P 31 f 47 v 63 /
• Đoạn chương trình mã hóa Base64: P4.5 – Secure C Programming Cookbook
• Đoạn chương trình giải mã Base64: P4.6 – Secure C Programming Cookbook
4.3 Mã hóa và giải mã Base64
62
• Mã hóa đối xứng: Sử dụng chung một khóa cho mã hóa và giải mã • Có hai loại: Mã khối và mã dòng • Có nhiều chế độ mã hóa: ECB, CBC, CFB, OFB, CTR, CWC… • Có nhiều giải thuật:
4.4 Các phương pháp mã hóa đối xứng
63
Cipher Key size Speed[4] Implementation Notes
AES 128 bits[5] 14.1 cpb in asm, 22.6 cpb in C
Brian Gladman's[6] The assembly version currently works only on Windows.
AES 128 bits 41.3 cpb OpenSSL
Triple DES 192 bits[7] 108.2 cpb OpenSSL
SNOW 2.0 128 or 256 bits 6.4 cpb Fast reference implementation[8]
This implementation is written in C.
RC4 Up to 256 bits (usually 128 bits)
10.7 cpb OpenSSL
Serpent 128, 192, or 256 bits 35.6 cpb Fast reference implementation
It gets a lot faster on 64-‐bit platforms and is at least as fast as AES in hardware.
Blowfish Up to 256 bits (usually 128 bits)
23.2 cpb OpenSSL
• Thư viện OpenSSL: Thư viện mã nguồn mở, mạnh mẽ và dễ sử dụng. • OpenSSL hỗ trợ:
• Nhiều thuật toán mã hóa: AES, DES , 3DES, Blow}ish, CAST, Idea, RC2, RC5. • Nhiều chế độ mã hóa: ECB, CBC, CFB, OFB, CTR… • Mã hóa dòng: RC4. • Các giải thuật băm: MD2, MD4, MD5,SHA-‐1,SHA-‐224,SHA-‐256… • MAC: HMAC. MDC2 • Các giải thuật mã hóa công khai: DH, DSA, RSA, ECC
• Sử dụng thư viện: • Trên Unix/Linux: Tải source về và biên dịch. Kết quả là }ile libcrypto.[so/a], libssl.[so/a] và các }ile .h để include vào chương trình.
• Trên Windows: Tải bản binary đã biên dịch sẵn: libeay32.dll, ssleay32.dll, tệp tiêu đề (.h) và tệp thư viện (.lib). Link http://www.ie7pro.com/openssl.html
4.5 Mã hóa đối xứng với OpenSSL
64
• Giao diện OpenSSL EVP • Là API mức cao của OpenSSL, cho phép truy nhập đến các thuật toán ở mức thấp một cách tập trung, dễ dàng.
• Tệp tiêu đề <openssl/evp.h>. • Tệp thư viện: libeay32.lib, ssleay32.lib
• Mã hóa AES với OpenSSL EVP. • Khởi tạo khóa, vector khởi tạo, salt với EVP_BytesToKey hoặc tự chọn một bộ Key, IV nào đó.
• Khởi tạo ngữ cảnh mã hóa với hàm EVP_EncryptInit_ex. • Khởi tạo ngữ cảnh giải mã với hàm EVP_DecryptInit_ex. • Mã hóa dữ liệu bằng việc liên tục gọi hàm EVP_EncryptUpdate, kết thúc quá trình mã hóa bằng hàm EVP_EncryptFinal_ex.
• Giải mã dữ liệu bằng việc liên tục gọi hàm EVP_DecryptUpdate, kết thúc quá trình giải mã bằng hàm EVP_DecryptFinal_ex.
4.5 Mã hóa đối xứng với OpenSSL
65
• VD • Sinh key và iv bằng hàm EVP_BytesToKey
char key[32]; char iv[32]; char * key_data = “nopass”; unsigned int salt[] = {12345, 54321}; EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, 6, 1, key, iv);
• Khởi tạo ngữ cảnh mã hóa với key và iv đã chọn EVP_CIPHER_CTX e_ctx; EVP_CIPHER_CTX_init(&e_ctx); EVP_EncryptInit_ex(&e_ctx, EVP_aes_256_cbc(),NULL, key, iv);
• Khởi tạo ngữ cảnh giải mã với key và iv đã chọn EVP_CIPHER_CTX d_ctx; EVP_CIPHER_CTX_init(&d_ctx); EVP_DecryptInit_ex(&d_ctx, EVP_aes_256_cbc(),NULL, key, iv);
4.5 Mã hóa đối xứng với OpenSSL
66
• VD (tiếp) • Mã hóa với ngữ cảnh đã được khởi tạo
char * plaintext=“Hello”; int len = strlen(plaintext); char ciphertext[1024]; int c_len = 0, f_len = 0; /* Gọi lại hàm này để cho phép OpenSSL sử dụng lại ngữ cảnh phiên mã hóa trước */ EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); … // Mỗi chu kỳ Update, c_len sẽ chứa số byte của xâu mã được EVP_EncryptUpdate(e, ciphertext, &c_len, plaintext, len); … // Cuối chu kỳ Update, f_len sẽ chưa số byte còn lại của xâu mã EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len); …
4.5 Mã hóa đối xứng với OpenSSL
67
• VD (tiếp) • Giải mã với ngữ cảnh đã được khởi tạo
char plaintext[1024]; int p_len = 0; /* Gọi lại hàm này để cho phép OpenSSL sử dụng lại ngữ cảnh phiên giãi mã hóa trước */ EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL); … // Giải mã với ciphertext và len được cung cấp trước EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len); … // Kết thúc quá trình giải mã, cập nhật dữ liệu còn lại vào plaintext. EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
4.5 Mã hóa đối xứng với OpenSSL
68
• Thư viện CryptoAPI • Cung cấp các hàm mật mã học cơ bản thông qua các Cryptographic Service Providers (CSP). • Microsoft Base Cryptographic Service Provider: RC2, RC4, DES • Microsoft Enhanced Cryptographic Service Provider: Triple-‐DES • Microsoft AES Cryptographic Service Provider: AES • …
• Cung cấp các hàm mã hóa và giải mã chứng thư số, và đồng thời bổ sung các hàm băm.
• Cung cấp các hàm quản lý và lưu trữ chứng thư số. • Các hàm mã thông điệp hóa mức cao (Simpli}ied Message Functions). • Các hàm mã hóa thông điệp mức thấp (Low-‐Level Message Functions).
4.6 Microsoft Crypto API
69
• Thư viện CryptoAPI
4.6 Microsoft Crypto API
70
• Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Tệp tiêu đề wincript.h • Thư viện Crypt32.lib • Trình tự sử dụng
4.6 Microsoft Crypto API
71
Khởi tạo Provider
Tạo khóa • Ngẫu nhiên • Từ mật khẩu • Từ bên ngoài
Đặt chế độ mã • CBC • ECB • …
Thiết lập vector khởi tạo
Thực hiện Mã hóa/Giải mã
• Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Khởi tạo ngữ cảnh Provider thông qua hàm CryptAcquireContext
BOOL WINAPI CryptAcquireContext(__out HCRYPTPROV* phProv, __in LPCTSTR pszContainer, __in LPCTSTR pszProvider, __in DWORD dwProvType, __in DWORD dwFlags ); VD: HCRYPTPROV hProvider; if (!CryptAcquireContext(&hProvider, 0, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) return 0;
4.6 Microsoft Crypto API
72
• Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Sử dụng Key thông qua một trong ba hàm. Kết quả trả về là đối tượng HCRYPTKEY • CryptGenKey( ): Sinh khóa ngẫu nhiên. • CryptDeriveKey( ): Sinh khóa từ mật khẩu. • CryptImportKey( ) : Sinh khóa từ một đối tượng trong bộ nhớ.
VD1. Sinh khóa ngẫu nhiên DWORD dwFlags; HCRYPTKEY hKey; DWORD dwSize = 256; dwFlags = ((dwSize << 16) & 0xFFFF0000) | CRYPT_EXPORTABLE; if (!CryptGenKey(hProvider, CALG_AES_256, dwFlags, &hKey)) return 0;
4.6 Microsoft Crypto API
73
• Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. VD2. Sinh khóa từ mật khẩu: Cần phải băm mật khẩu và truyền vào hàm CryptDeriveKey char * password = “nopass”; BOOL bResult; DWORD cbData; HCRYPTKEY hKey; // Lưu Key HCRYPTHASH hHash; // Lưu giá trị băm của mật khẩu if (!CryptCreateHash(hProvider, CALG_SHA1, 0, 0, &hHash)) // Khởi tạo hàm băm
return 0; cbData = lstrlen(password) * sizeof(TCHAR); if (!CryptHashData(hHash, (BYTE *)password, cbData, 0)) // Băm mật khẩu
{ CryptDestroyHash(hHash); return 0; }
// Tạo key từ giá trị băm của mật khẩu bResult = CryptDeriveKey(hProvider, CALG_AES_256, hHash, CRYPT_EXPORTABLE, &hKey); CryptDestroyHash(hHash);
4.6 Microsoft Crypto API
74
• Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Thiết lập chế độ mã hóa CBC với hàm CryptSetKeyParam
DWORD dwMode = CRYPT_MODE_CBC; CryptSetKeyParam(hKey, KP_MODE, (BYTE *)&dwMode, 0);
• Sinh ngẫu nhiên vector khởi tạo (IV) BOOL bResult; // Lưu kết quả BYTE *pbTemp; // Lưu vector khởi tạo DWORD dwBlockLen, dwDataLen; dwDataLen = sizeof(dwBlockLen); // Lấy kích thước block của thuật toán mã hóa if (!CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE *)&dwBlockLen, &dwDataLen, 0)) return 0; dwBlockLen /= 8; if (!(pbTemp = (BYTE *)LocalAlloc(LMEM_FIXED, dwBlockLen))) return FALSE; // Sinh ngẫu nhiên IV bResult = CryptGenRandom(hProvider, dwBlockLen, pbTemp); // Thiết lập IV bResult = CryptSetKeyParam(hKey, KP_IV, pbTemp, 0); LocalFree(pbTemp);
4.6 Microsoft Crypto API
75
• Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Mã hóa với CryptEncrypt
• Với các giải thuật mã hóa dòng thì kích thước dữ liệu ra = kích thước dữ liệu vào. • Với các giải thuật mã hóa khối thì kích thước dữ liệu ra <= kích thước dữ liệu vào
+ kích thước khối. • Hàm CryptEncrypt sẽ ghi đè dữ liệu mã hóa được vào bộ đệm chứa dữ liệu vào. • Đoạn chương trình thực hiện mã hóa chung cho cả hai loại.
4.6 Microsoft Crypto API
76
ALG_ID Algid; // Giải thuật mã char * pbData = "Hello CryptAPI"; // Xâu nguồn cần mã char * pbResult = 0; // Xâu kết quả DWORD dwDataLen = 0,dwBlockLen = 0;
cbData = strlen(pbData); // Chiều dài xâu nguồn dwDataLen = sizeof(ALG_ID); // Lấy thông tin về giải thuật mã hóa với key cho trước if (!CryptGetKeyParam(hKey, KP_ALGID, (BYTE *)&Algid, &dwDataLen, 0))
return 0;
• Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Mã hóa với CryptEncrypt
4.6 Microsoft Crypto API
77
if (GET_ALG_TYPE(Algid) != ALG_TYPE_STREAM) // Mã hóa khối { dwDataLen = sizeof(DWORD); ret = CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE*)&dwBlockLen,
&dwDataLen, 0); // Lấy kích thước block theo bit dwBlockLen = dwBlockLen/8; // Đổi kích thước block ra đơn vị byte // Cấp phát bộ nhớ để chứa kết quả pbResult = (char*)malloc(cbData+dwBlockLen); memcpy(pbResult,pbData,cbData); // Thực hiện mã hóa, kết quả là dwDataLen byte lưu trong pbResult dwDataLen = cbData; CryptEncrypt(hKey, 0, TRUE, 0, (BYTE*)pbResult, &dwDataLen, cbData+16)) ;
}
• Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Mã hóa với CryptEncrypt (tiếp)
4.6 Microsoft Crypto API
78
else // Mã hóa dòng {
// Cấp phát bộ nhớ lưu kết quả pbResult = (char*)malloc(cbData); // Bảo toàn dữ liệu nguồn memcpy(pbResult,pbData,cbData); // Thực hiện mã hóa CryptEncrypt(hKey,0,TRUE,0,pbResult,&dwDataLen,cbData);
}
• Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Giải mã với CryptDecrypt
• Kích thước dữ liệu đích <= kích thước dữ liệu nguồn • Thực hiện đơn giản hơn so với CryptEncrypt • Ví dụ
4.6 Microsoft Crypto API
79
char * pbData ; // Dữ liệu nguồn DWORD cbData; // Kích thước nguồn char * pbResult; // Dữ liệu đích DWORD dwDataLen; // Kích thước đích … // Cấp phát bộ nhớ và sao chép dữ liệu nguồn vào đích pbResult = (char*)malloc(cbData); memcpy(pbResult, pbData, cbData); dwDataLen = cbDataLen; // Giải mã, kết quả là dwDataLen byte lưu trong pbResult CryptDecrypt(hKey,0,TRUE,0,pbResult,&dwDataLen);
• Trao đổi khóa với OpenSSL • CryptoAPI không cho phép nhập và xuất khóa dạng thô như OpenSSL. • Để trao đổi khóa với thư viện khác, cần mã hóa khóa theo giải thuật AT_KEYEXCHANGE, và thực hiện nhập xuất dưới dạng cấu trúc BLOB.
• Hàm CryptImportKeyvà CryptExportKey dùng để thực hiện nhập xuất khóa.
• Xem thêm phần 5.26, 5.27 trong Secure Programming Cookbook.
4.6 Microsoft Crypto API
80
Lương Ánh Hoàng [email protected]
Chương 5. Hàm băm và xác thực thông điệp
Hashes and Message Authentication
5.1 Các loại hàm băm và MAC thông dụng 5.2 Băm với OpenSSL 5.3 Băm dữ liệu với CryptoAPI 5.4 Xác thực thông điệp với HMAC 5.5 Salt
Nội dung
82
• Hàm băm (hashes) – Nhận đầu vào là một xâu và đầu ra là một chuỗi bit có chiều dài xác định.
– Tỉ lệ đụng độ rất nhỏ. – Dùng để kiểm tra tính toàn vẹn của dữ liệu nhưng không đảm bảo tính xác thực của dữ liệu.
– Thường kết hợp với mô hình mã hóa công khai chứ không sử dụng một mình.
– Các giải thuật băm thông dụng: MD5, SHA1
5.1 Các hàm băm và MAC thông dụng
83
• Hàm băm (hashes)
5.1 Các hàm băm và MAC thông dụng
84
Algorithm Digest size Security conqidence Small message
speed (64 bytes), in cycles per byte[2]
Large message speed (8K), in cycles per byte
Uses block cipher
Davies-‐Meyer-‐AES-‐128
128 bits (same length as cipher block size)
Good 46.7 cpb 57.8 cpb Yes
MD2 128 bits Good to low 392 cpb 184 cpb No
MD4 128 bits Insecure 32 cpb 5.8 cpb No
MD5 128 bits Very low, may be insecure
40.9 cpb 7.7 cpb No
MDC-‐2-‐AES-‐128 256 bits Very high 93 cpb 116 cpb Yes
MDC-‐2-‐DES 128 bits Good 444 cpb 444 cpb Yes
RIPEMD-‐160 160 bits High 62.2 cpb 20.6 cpb No
SHA1 160 bits High 53 cpb 15.9 cpb No
SHA-‐256 256 bits Very high 119 cpb 116 cpb No
SHA-‐384 384 bits Very high 171 cpb 166 cpb No
SHA-‐512 512 bits Very high 171 cpb 166 cpb No
• Xác thực thông điệp (Message Authentication Code) – Nhận đầu vào là một xâu và một khóa bí mật, đầu ra là một mã có chiều dài xác định.
– Dùng để kiểm tra tính toàn vẹn và xác thực của dữ liệu. – Các giải thuật thông dụng: OMAC, CMAC, HMAC
5.1 Các hàm băm và MAC thông dụng
85
• Xác thực thông điệp (Message Authentication Code)
5.1 Các hàm băm và MAC thông dụng
86
MAC Built upon Small message speed (64 bytes)[4]
Large message speed (8K)
Appropriate for hardware
Patent restric-‐tions Parallel-‐izable
CMAC A universal hash and AES ~18 cpb ~18 cpb Yes No Yes
HMAC-‐SHA1 Message digest function 90 cpb 20 cpb Yes No No
MAC127 hash127 + AES ~6 cpb ~6 cpb Yes No Yes
OMAC1 AES 29.5 cpb 37 cpb Yes No No
OMAC2 AES 29.5 cpb 37 cpb Yes No No
PMAC-‐AES Block cipher 72 cpb 70 cpb Yes Yes Yes
RMAC Block cipher 89 cpb 80 cpb Yes No No
UMAC32 UHASH and AES 19 cpb cpb No No Yes
XMACC-‐SHA1 Any cipher or MD function 162 cpb 29 cpb Yes Yes Yes
• OpenSSL cung cấp hai loại giao diện với các hàm băm – Giao diện riêng rẽ với mỗi giải thuật băm cụ thể.
• Mỗi giải thuật băm có tệp tiêu đề riêng • Tên gọi các hàm là khác nhau cho các giải thuật băm.
– Giao diện chung EVP cho mọi loại hàm băm. • Tệp tiêu đề chung: <openssl/evp.h> • Trình tự sử dụng như nhau:
– Khởi tạo ngữ cảnh: EVP_DigestInit – Cập nhật dữ liệu băm: EVP_DigestUpdate – Lấy kết quả: EVP_DigestFinal.
5.2 Băm dữ liệu với OpenSSL
87
• VD: Băm với SHA1 5.2 Băm dữ liệu với OpenSSL
88
#include <openssl/sha.h> int i; SHA_CTX ctx; unsigned char result[SHA_DIGEST_LENGTH]; /* SHA1 has a 20-‐byte digest. */ unsigned char *s1 = (unsigned char*)"Testing"; unsigned char *s2 = (unsigned char*)"...1...2...3..."; SHA1_Init(&ctx); SHA1_Update(&ctx, s1, strlen((char*)s1)); SHA1_Update(&ctx, s2, strlen((char*)s2)); /* Yes, the context object is last. */ SHA1_Final(result, &ctx); printf("SHA1(\"%s%s\") = ", s1, s2); for (i = 0; i < SHA_DIGEST_LENGTH; i++) printf("%02x", result[i]); printf("\n");
• VD: Băm với giao diện EVP 5.2 Băm dữ liệu với OpenSSL
89
#include <openssl/evp.h> #include <stdio.h> #include <string.h> int i , ol; EVP_MD_CTX ctx; unsigned char *result; unsigned char *s1 = (unsigned char*)"Testing"; unsigned char *s2 = (unsigned char*)"...1...2...3..."; EVP_DigestInit(&ctx, EVP_sha1()); EVP_DigestUpdate(&ctx, s1, strlen((char*)s1)); EVP_DigestUpdate(&ctx, s2, strlen((char*)s2)); if (!(result = (unsigned char *)malloc(EVP_MD_CTX_block_size(&ctx))))abort(); EVP_DigestFinal(&ctx, result, &ol); printf("SHA1(\"%s%s\") = ", s1, s2); for (i = 0; i < ol; i++) printf("%02x", result[i]); printf("\n"); free(result);
• Trình tự băm với CryptoAPI – Tệp tiêu đề: Wincrypt.h – Khởi tạo ngữ cảnh Provider: CryptAcquireContext – Tạo đối tượng hash: CryptCreateHash – Băm liên tiếp với: CryptHashData – Lấy kết quả: CryptGetHashParam – Giải phóng đói tượng hash: CryptDestroyHash
5.3 Băm dữ liệu với CryptoAPI
90
• Ví dụ: Băm dữ liệu với thuật toán SHA-‐256
5.3 Băm dữ liệu với CryptoAPI
91
BYTE *pbData; DWORD cbData = sizeof(DWORD), cbHashSize, i; HCRYPTHASH hSHA256; HCRYPTPROV hProvider; unsigned char *s1 = (unsigned char*)"Testing"; unsigned char *s2 = (unsigned char*)"...1...2...3..."; // Khởi tạo ngữ cảnh Provider CryptAcquireContext(&hProvider, 0, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0); // Tạo đối tượng hàm băm CryptCreateHash(hProvider, CALG_SHA_256, 0, 0, &hSHA256); // Thực hiện băm CryptHashData(hSHA256, s1, strlen((char*)s1), 0); CryptHashData(hSHA256, s2, strlen((char*)s2), 0); // Thực hiện băm…
• Ví dụ: Băm dữ liệu với thuật toán SHA-‐256 (tiếp)
5.3 Băm dữ liệu với CryptoAPI
92
// Lấy kích thước dữ liệu băm được CryptGetHashParam(hSHA256, HP_HASHSIZE, (BYTE *)&cbHashSize, &cbData, 0); pbData = (BYTE *)LocalAlloc(LMEM_FIXED, cbHashSize); // Lấy dữ liệu băm được CryptGetHashParam(hSHA256, HP_HASHVAL, pbData, &cbHashSize, 0); // Giải phóng đối tượng băm và ngữ cảnh Provider CryptDestroyHash(hSHA256); CryptReleaseContext(hProvider, 0); printf("SHA256(\"%s%s\") = ", s1, s2); for (i = 0; i < cbHashSize; i++) printf("%02x", pbData[i]); printf("\n"); LocalFree(pbData);
• Với OpenSSL – Tệp tiêu đều <openssl/hmac.h> – Gọi hàm HMAC_Init để khởi tạo ngữ cảnh và key sẽ sử dụng – Liên tục gọi hàm HMAC_Update để cập nhật dữ liệu. – Gọi hàm HMAC_Final để kết thúc quá trình băm – Gọi hàm HMAC_cleanup để xóa key khỏi bộ nhớ. – Có thể gọi hàm All-‐in-‐one HMAC – Bên nhận kiểm tra lại bằng cách thực hiện băm với với cùng một key và giải thuật và so sánh kết quả
5.4 Xác thực thông điệp với HMAC
93
• Với OpenSSL
5.4 Xác thực thông điệp với HMAC
94
int i; HMAC_CTX ctx; unsigned int len; unsigned char out[20]; unsigned char * key = (unsigned char*)"secret"; int keylen = strlen((char*)key); // Khởi tạo HMAC với key là secret HMAC_Init(&ctx, key, keylen, EVP_sha1( )); // Thực hiện băm xâu "fr HMAC_Update(&ctx, (unsigned char*)"hash me pls", 11); // Lấy kết quả HMAC_Final(&ctx, out, &len); for (i = 0; i < len; i++) printf("%02x", out[i]); printf("\n");
• Với CryptAPI – Tạo đối tượng Hash với hàm CryptCreateHash, trong đó tham số hKey là một key đã được tạo trước.
– Thiết lập thông tin về giải thuật băm với hàm CryptSetHashParam. – Thực hiện băm với hàm CryptHashData – Lấy kích thước, nội của dữ liệu băm được với hàm CryptGetHashParam.
– Giải phóng đói tượng Hash và Key
5.4 Xác thực thông điệp với HMAC
95
• Với CryptAPI
5.4 Xác thực thông điệp với HMAC
96
BYTE out[20]; DWORD cbData = sizeof(out), i; HCRYPTKEY hKey; HMAC_INFO HMACInfo; HCRYPTHASH hHash; HCRYPTPROV hProvider; // Lấy ngữ cảnh provider CryptAcquireContext(&hProvider,0,MS_ENH_RSA_AES_PROV,PROV_RSA_AES,CRYPT_VERIFYCONTEXT); // Sinh key từ mật khẩu hKey = CreateKeyFromPassword(hProvider,"secret"); // Tạo đối tượng băm CryptCreateHash(hProvider, CALG_HMAC, hKey, 0, &hHash);
• Với CryptAPI
5.4 Xác thực thông điệp với HMAC
97
// Thiết lập giải thuật băm HMACInfo.HashAlgid = CALG_SHA1; HMACInfo.pbInnerString = HMACInfo.pbOuterString = 0; HMACInfo.cbInnerString = HMACInfo.cbOuterString = 0; CryptSetHashParam(hHash, HP_HMAC_INFO, (BYTE *)&HMACInfo, 0); // Thực hiện băm CryptHashData(hHash, (BYTE *)"Hash me plz", 11, 0); // Lấy kết quả CryptGetHashParam(hHash, HP_HASHVAL, out, &cbData, 0); for (i = 0; i < cbData; i++) printf("%02x", out[i]); printf("\n"); CryptDestroyHash(hHash); CryptDestroyKey(hKey); CryptReleaseContext(hProvider, 0);
• Salt – Chuỗi dữ liệu thêm vào để tăng không gian khóa và chống lại hình thức replay-‐attack.
– Hai bên có thể thỏa thuận chung một salt nào đó thay đổi theo thời gian.
– Salt thường được thêm vào đầu thông điệp gốc, sau đó thực hiện băm cả salt cả thông điệp.
5.5 Sử dụng Salt
98
1. Viết chương trình mã hóa và giải mã tệp tin bằng giải thuật AES-‐256 bit. Mật khẩu nhập từ bàn phím. Kiểm tra tính đúng đắn của kết quả bằng giải thuật SHA-‐256. Sử dụng thư viện OpenSSL. Khuôn dạng dữ liệu của tệp tin sau khi mã hóa có thể như sau:
<Kích thước><Nội dung tệp tin đã mã><Giá trị băm SHA-‐256> 2. Viết chương trình chat client-‐server đơn giản trong đó kênh truyền được mã hóa theo giải thuật AES-‐256. Key được sinh ra từ mật khẩu thỏa thuận trước và không truyền qua mạng, Vector khởi tạo là mã BCD được thiết lập từ ngày và giờ hiện tại của hệ thống (Hàm API GetSystemTime). Ví dụ: Nếu hiện tại là 07h ngày 10/10/2011 thì giá trị dưới dạng hexa của vector khởi tạo là
2011101007000….00
Bài tập
99
3. Viết chương trình băm nội dung một }ile bằng giải thuật HMAC-‐AES256, sử dụng thư viện OpenSSL. Mật khẩu để băm nhập từ bàn phím.Kết quả băm được lưu vào cuối }ile. 4. Viết chương trình kiểm tra tính toàn vẹn của một }ile bằng giải thuật HMAC-‐AES256. Mật khẩu để kiểm tra nhập từ bàn phím.
Bài tập
100
Lương Ánh Hoàng [email protected]
Chương 6. Mã hóa công khai Public Key Cryptography
6.1 Mã hóa với OpenSSL RSA 6.2 Chữ ký số 6.3 Biểu diễn khóa 6.4 Kết nối SSL 6.5 Hạ tầng khóa công khai
Nội dung
102
• Mã hóa bất đối xứng • Là các giải thuật sử dụng một cặp khóa cho việc mã hóa và giải mã • Dữ liệu được mã hóa bằng khóa công khai sẽ được giải mã bằng
khóa bí mật và ngược lại. • Các giải thuật thông dụng: RSA, DSA, Dif}ie-‐Hellman. • Không sử dụng trực tiếp để mã hóa dữ liệu vì tốc độ rất chậm. • Thường được sử dụng để
• Trao đổi khóa đối xứng trong phiên truyền mật • Chữ ký số • Xác nhận danh tính • …
6.1 Mã hóa với OpenSSL RSA
103
• OpenSSL RSA • Thường được sử dụng trao đổi khóa • Lưu trữ tất cả thông tin về một khóa dưới cấu trúc RSA. • Tệp tiêu đề rsa.h • Sinh cặp khóa đối xứng bằng hàm RSA *RSA_generate_key(int bits, // Kích thước khóa: 1024,2048…
unsigned long exp,// Số mũ: 3, 17, 65537 void (*cb)(int, int, void), // Callback void *cb_arg);
6.1 Mã hóa với OpenSSL RSA
104
• Mã hóa với khóa công khai • Sử dụng hàm RSA_public_encrypt:
int RSA_public_encrypt(int l, // Chiều dài dữ liệu unsigned char *pt, // Xâu/số cần mã unsigned char *ct, // Kết quả RSA *r, // Cấu trúc RSA int p); // Kiểu padding Kết quả trả về: chiều dài xâu mã được.
6.1 Mã hóa với OpenSSL RSA
105
• Giải mã với khóa bí mật • Sử dụng hàm RSA_private_decrypt:
int RSA_private_decrypt(int l, unsigned char *ct, unsigned char *pt, RSA *r, int p); Kết quả trả về: chiều dài xâu giải mã được
6.1 Mã hóa với OpenSSL RSA
106
• Bài tập – Viết chương trình chat console client-‐server sử dụng giải thuật RSA. Chỉ chia sẻ public key trên đường truyền.
6.1 Mã hóa với OpenSSL RSA
107
• Chữ ký số dữ liệu nhằm xác thực danh tính của người gửi, tương tự như HMAC nhưng sử dụng giải thuật RSA
• Quá trình ký số dữ liệu nhận đầu vào là giá trị băm của thông điệp, khóa bí mật của người gửi, đầu ra là giá trị hàm băm đã được mã hóa.
• Bên nhận thực hiện quá trình ngược lại: tính giá trị băm của thông điệp, giải mã giá trị băm đã mã hóa của bên gửi bằng khóa công khai và so sánh hai giá trị băm này.
• Hacker không thể giả mạo giá trị băm vì không có khóa bí mật của bên gửi.
6.2 Chữ ký số
108
• Sơ đồ ký
6.2 Chữ ký số
109
Dữ liệu
Giá trị băm (MD)
Hash
(SHA1)
Chữ ký
Khóa bí mật
Mã hóa
Dữ liệu + Chữ ký
• Thực hiện bằng OpenSSL RSA – Hàm RSA_sign là hàm mức thấp của OpenSSL thực hiện ký số dữ liệu
6.2 Chữ ký số
110
int RSA_sign(int md_type, // Loại Message Digest: SHA1, MD5… unsigned char *dgst, // Bản thân dữ liệu (Message Digest) unsigned int dlen, // Kích thước unsigned char *sig, // Chữ ký unsigned int *siglen, // Chiều dài chữ ký RSA *r); // Khóa bí mật
• Thực hiện bằng OpenSSL RSA – Hàm RSA_verify thực hiện công việc ngược lại: kiểm tra tính hợp lệ của chữ ký
• Thực hiện bằng OpenSSL DSA – Xem thêm trong sách (phần 7.15)
6.2 Chữ ký số
111
int RSA_verify(int md_type, //Loại message digest: md5,sha1,… unsigned char *dgst, // message digest unsigned int dlen, // kích thước message digest unsigned char *sig, // Chữ ký unsigned int siglen, // Chiều dài chữ ký RSA *r); // Khóa công khai
• Biểu diễn khóa và chứng thực – DER (Binary) – PEM (Plaintext)
• Biểu diễn DER – Chuẩn quốc tế thông dụng – Các hàm OpenSSL tương ứng: i2d và d2i (internal representation ó DER) – Ví dụ chuyển khóa công khai RSA sang lưu trữ dưới dạng DER
6.3 Biểu diễn khóa
112
unsigned char *DER_encode_RSA_public(RSA *rsa, int *len) {
unsigned char *buf, *next; *len = i2d_RSAPublicKey(rsa, 0); if (!(buf = next = (unsigned char *)malloc(*len))) return 0; i2d_RSAPublicKey(rsa, &next); /* If we use buf here, return buf; becomes wrong */ return buf;
}
• Biểu diễn DER – Ví dụ chuyển khóa từ dạng DER sang dạng khóa công khai RSA
6.3 Biểu diễn khóa công khai
113
RSA *DER_decode_RSA_public(unsigned char *buf, long len) {
return d2i_RSAPublicKey(0, &buf, len); }
• Biểu diễn PEM (Privacy Enhanced Mail) – Thực chất là biểu diễn DER dưới dạng Base64 và có thêm phần header, footer và có thể mã hóa
– Ví dụ -‐-‐-‐-‐-‐BEGIN RSA PRIVATE KEY-‐-‐-‐-‐-‐ Proc-‐Type: 4,ENCRYPTED DEK-‐Info: DES-‐EDE3-‐CBC,F2D4E6438DBD4EA8 LjKQ2r1Yt9foxbHdLKZeClqZuzN7PoEmy+b+dKq9qibaH4pRcwATuWt4/Jzl6y85 NHM6CM4bOV1MHkyD01tFsT4kJ0GwRPg4tKAiTNjE4Yrz9V3rESiQKridtXMOToEp Mj2nSvVKRSNEeG33GNIYUeMfSSc3oTmZVOlHNp9f8LEYWNmIjfzlHExvgJaPrixX QiPGJ6K05kV5FJWRPET9vI+kyouAm6DBcyAhmR80NYRvaBbXGM/MxBgQ7koFVaI5 zoJ/NBdEIMdHNUh0h11GQCXAQXOSL6Fx2hRdcicm6j1CPd3AFrTt9EATmd4Hj+D4 91jDYXElALfdSbiO0A9Mz6USUepTXwlfVV/cbBpLRz5Rqnyg2EwI2tZRU+E+Cusb /b6hcuWyzva895YMUCSyDaLgSsIqRWmXxQV1W2bAgRbs8jD8VF+G9w== -‐-‐-‐-‐-‐END RSA PRIVATE KEY-‐-‐-‐-‐-‐
6.3 Biểu diễn khóa công khai
114
• Biểu diễn PEM (Privacy Enhanced Mail) – Các hàm OpenSSL:
• Tệp tiêu đề <openssl/pem.h> • PEM_read_*** • PEM_write_***
– Ví dụ ghi ra bộ nhớ khóa bí mật RSA được mã hóa dưới dạng PEM – AES256-‐CBC
6.3 Biểu diễn khóa công khai
115
#include <openssl/pem.n> #include <openssl/bio.h> int password_cb(char *buf, int len, int rwqlag, void *cb_arg) {
strcpy(buf,"hello"); return strlen(buf);
} BIO * mem = BIO_new(BIO_s_mem()); BUF_MEM * bp; BIO_get_mem_ptr(mem,&bp); PEM_write_bio_RSAPrivateKey(mem,key,EVP_aes_256_cbc(),
0,0,password_cb,0);
• Secure Socket Layer (SSL) là giao thức ở tầng ứng dụng cung cấp dịch vụ kết nối an toàn giữa hai ứng dụng trên cơ sở hạ tầng khóa công khai.
• OpenSSL cung cấp SSL API để có thể viết ứng dụng SSL nhanh chóng. • http://www.ibm.com/developerworks/linux/library/l-‐openssl/
index.html#ibm-‐pcon • OpenSSL common commands – NSA Cyber Security
6.4 Kết nối SSL
116
• Sử dụng bên thứ ba để chứng thực danh tính các bên. • Chống được hình thức tấn công Man-‐In-‐The Middle • Một số lệnh thông dụng với OpenSSL
– http://security.ncsa.illinois.edu/research/grid-‐howtos/usefulopenssl.html
6.5 Hạ tầng khóa công khai
117
Lương Ánh Hoàng [email protected]
Chương 7. Anti-‐Tampering
7.1 Phát hiện thay đổi (Detecting modi}ication) 7.2 Che giấu mã (Code hiding) 7.3 Sử dụng con trỏ hàm (Function Pointer) 7.4 Giấu xâu (String hiding) 7.5 Phát hiện debugger (Anti-‐Debugger) 7.6 Self-‐modifying code 7.7 Giải pháp tổng thể
Nội dung
119
• Mục tiêu: Phát hiện chương trình đã bị crack chưa (Detecting modi}ication)
• Kỹ thuật: – Tính MD5, SHA1, hoặc HMAC mã lệnh của }ile thực thi. – Đánh dấu một đoạn trong chương trình sẽ dùng để lưu mã băm }ile thực thi và ghi
giá trị băm vào đó bằng một chương trình Hex edit khác. – Tại thời điểm runtime tính lại giá trị băm của }ile thực thi và so sánh với mã băm
trước đó. • Ví dụ:
7.1 Phát hiện thay đổi mã lệnh
120
// Khai báo xâu đánh dấu mã băm char * md5hash = "AAAAXXXXXXXXXXXXXXXX"; int _tmain(int argc, _TCHAR* argv[]) {
unsigned char hash[16];// Giá trị băm tính được unsigned char expectedhash[16];// Giá trị băm lưu trong }ile FILE * fp = fopen(argv[0],"rb");// Mở }ile để tính lại giá trị băm unsigned char * pFile ; fseek(fp,0,SEEK_END); int len = ftell(fp);
• Ví dụ:
7.1 Phát hiện thay đổi mã lệnh
121
pFile = (unsigned char*)malloc(len); fseek(fp,0,SEEK_SET); fread(pFile,1,len,fp); md5hash = 0; for (int i=0;i<len-‐20;i++) // Dò tìm giá trị băm trong }ile
if ((pFile[i]=='A')&&(pFile[i+1]=='A')&& (pFile[i+2]=='A')&&(pFile[i+3]=='A')) { memcpy(expectedhash,pFile+i+4,16); // Lưu ra mảng khác memset(pFile+i+4,0,16); // Xóa trắng giá trị này trong }ile }
MD5(pFile,len,hash); // Tính lại giá trị băm printf("File hash:"); print_md5(hash); printf("Expected hash:"); print_md5(expectedhash); if (memcmp(hash,expectedhash,16)) { printf("File veri}ication failed"); } getch(); return 0;
}
• Hạn chế – Dễ bị đánh bại nếu sử dụng hash vì cracker cũng có thể tính lại giá trị băm và sửa }ile
cho chính xác. – Nếu sử dụng HMAC thì phải lưu mật khẩu ở đâu đó
• Lưu trong }ile: Cũng sẽ bị cracker dò ra • Lưu trên internet: cần có kết nối internet và dễ dàng bị dò ra nếu dùng sniffer. • CURL
7.1 Phát hiện thay đổi mã lệnh
122
• Mục tiêu – Gây khó khăn cho quá trình dịch ngược và phân tích bằng các disassembler
(Obfuscating code). Windasm, OllyDbg, IDA – Che giấu các cấu trúc điều khiển quan trọng trong chương trình.
• Cấu trúc điều kiện • Cấu trúc lặp
• Kỹ thuật – Cần sự hỗ trợ của trình biên dịch – Thực hiện ở mức hợp ngữ – Sử dụng các điều kiện so sánh “bất thường”
• So sánh khác thay vì bằng • Vòng lặp giảm thay vì tăng. • Vòng lặp có chỉ số tăng khác 1. • …
7.2 Che giấu mã
123
• Mục tiêu – Gây khó khăn cho các công cụ disassembler trong việc phân tích lời gọi hàm
• Kỹ thuật – Không thực hiện lời gọi hàm trực tiếp trong chương trình mà sử dụng các con trỏ
hàm. – VD
7.3 Sử dụng con trỏ hàm
124
void my_func() { } typedef void (*FUNC)(); int _tmain(int argc, _TCHAR* argv[]) {
FUNC ptr; ptr = my_func; printf("ptr address:%p",ptr); ptr(); /* make the function call */ return 0;
}
• Mục tiêu – Gây khó khăn cho các công cụ disassembler trong việc phân tích các xâu nhạy cảm
trong chương trình.VD • Invalid cd-‐key • Registration successful • …
• Kỹ thuật – Mã hóa các xâu trong chương trình
• Base64 • RC4 • Giải thuật tự chọn • …
7.4 Giấu xâu
125
• Ví dụ: dịch mã các ký tự trong xâu đi 0x19
7.4 Giấu xâu
126
#include <stdio.h> #de}ine A(c) (c) -‐ 0x19 #de}ine UNHIDE_STR(str) do { char *p = str; while (*p) *p++ += 0x19; } while (0) #de}ine HIDE_STR(str) do { char *p = str; while (*p) *p++ -‐= 0x19; } while (0) int main(int argc, char *argv[ ]) { char str[ ] = { A('/'), A('e'), A('t'), A('c'), A('/'), A('p'), A('a'), A('s'), A('s'), A('w'), A('d'), 0 }; UNHIDE_STR(str); printf("%s\n", str); HIDE_STR(str); return 0; }
• Mục đích: Phát hiện sự tồn tại của debugger • Kỹ thuật: rất nhiều (google : anti-‐debugger)
– Kernel32!IsDebuggerPresent – PEB!IsDebugged – PEB!NtGlobalFlags – Self-‐debugging – ….
• Ví dụ
7.5 Phát hiện debugger
127
#include <windows.h> int _tmain(int argc, _TCHAR* argv[]) {
if(IsDebuggerPresent()) { printf("Program is being debugged"); } return 0;
}
• Mục đích: – Mã hóa lệnh quan trọng, chỉ giải mã khi cần thực hiện
• Kỹ thuật – Không có sự trợ giúp của trình biên dịch. – Gần như không thể thực hiện bằng C/C++ – Cần hiểu rất sâu về hệ điều hành + hợp ngữ – Googe: shellcode
• Ưu điểm – Không thể dissassembler nếu chưa giải mã. – Không dành cho amater cracker.
• Nhược điểm – Rất khó bảo trì – Vẫn có thể crack được nếu đặt đúng breakpoint tại điểm decrypt – Xung đột với DEP (Data Execution Preventation)
7.6 Self-‐modifying code
128
• Các kỹ thuật trên nếu kết hợp lại có thể có kết quả rất tốt. • Trên Windows:
– Sử dụng các chương trình all-‐in-‐one: “Packer” • ASPack • ASProtect • PECompact • PECrypt • Themida • …
– Code-‐sign ứng dụng bằng chữ ký số http://msdn.microsoft.com/en-‐us/library/ms537361%28v=vs.85%29.aspx
• Trên các hệ điều hành khác – Đang tiến dần đến xu hướng code-‐sign: VD iOS, MacOS…
7.7 Giải pháp tổng thể
129
• Các kỹ thuật trên nếu kết hợp lại có thể có kết quả rất tốt. • Trên Windows:
– Sử dụng các chương trình all-‐in-‐one: “Packer” • ASPack • ASProtect • PECompact • PECrypt • Themida • …
– Code-‐sign ứng dụng bằng chữ ký số http://msdn.microsoft.com/en-‐us/library/ms537361%28v=vs.85%29.aspx
• Trên các hệ điều hành khác – Đang tiến dần đến xu hướng code-‐sign: VD iOS, MacOS…
7.7 Giải pháp tổng thể
130
1. Tìm hiểu và minh hoạ các kỹ thuật SQL Injection hiện nay (đặc biệt là blind sql injection).(Tuần 14 -‐ Thuý) 2. Tìm hiểu và minh hoạ kỹ thuật tấn công tràn bộ đệm buffer over}low và khai thác qua shellcode. Có thể dùng Metasploit để thử nghiệm.(Tuần 15-‐ Xuan Hiep) 3. Tìm hiểu cơ chế leo thang đặc quyền trên một thiết bị chạy hệ điều hành Android (rooting, tìm cái dễ root nhất).(Tuần 15 – Phi Hiệp) 4. Sử dụng một trong các kỹ thuật đồng bộ để thực hiện thuật toán quicksort trên bộ vi xử lý đa nhân. Yêu cầu tốc độ sắp xếp phải đạt được tuyến tính với số nhân của bộ vi xử lý.(Tuần 12 – Ngọc Anh) 5. Sử dụng thuật toán HMAC để phát hiện thay đổi trong mã lệnh của chương trình. Với key và giá trị băm được tải về từ internet trong mỗi lần kiểm tra.(Tuần 12 -‐ Linh) 6. Sử dụng chữ ký số để phát hiện thay đổi trong mã lệnh của chương trình. Với public key được tải về từ internet trong mỗi lần kiểm tra.(Tuần 13 – Quynh, Thành) 7. Tìm hiểu các kỹ thuật phát hiện debugger (anti-‐debugger) và các kỹ thuật chống phát hiện debugger (anti-‐anti-‐debugger).(Tuần 13 – Hoa, Tùng) 8. Xây dựng hệ thống cấp phát chứng thực số dựa trên openssl với CA sinh sẵn Chỉ yêu cầu viết bằng php/html để nhận tham số của chứng thực qua form, sau đó sinh ngay cặp chứng thực/key cho client và biểu diễn dưới dạng PEM (.crt, .key) (Tuần 14 – Dat)
Các đề tài tìm hiểu
131