Buffer overflow kìa
Mình có 1 chương trình kiểm tra password:
và khi chạy:Code:#include <stdio.h> #include <string.h> #include <stdlib.h> int check_authentication(char *password){ int auth_flag = 0; char password_buffer[16]; strcpy(password_buffer, password); if(strcmp(password_buffer, "password1") == 0){ auth_flag = 1; } return auth_flag; } int main(int argc, char *argv[]){ if(check_authentication(argv[1])){ printf("Access granted!"); } else{ printf("Access denied!"); } }
The problem is not the problem. the problem is your attitude about the problem
Buffer overflow kìa
Đã được chỉnh sửa lần cuối bởi prog10 : 06-03-2015 lúc 11:24 PM.
do password_buffer[16] chỉ chứa được tối đa 15 chữ nên input nhiều hơn 15 chữ sẽ bị chép đè...
EDIT: "password1" ko nằm trên stack
password_buffer chiếm 16 bytes từ 0-15Code:password_buffer ??????????? auth_flag [0][1][2]...[14][15] [16][17]...[27] [28][29][30][31]
??????? chiếm 12 bytes từ 16-27
auth_flag chiếm 4 bytes từ 28-31
chuỗi có 29 ký tự sẽ chép đè lên cả vị trí của "password1" và auth_flag, làm auth_flag mang giá trị khác 0 (do có ký tự '9'). Còn chuỗi 28 ký tự tuy cũng có thể chép đè lên auth_flag, nhưng nó chép ký tự '\0' nên auth_flag vẫn mang giá trị 0.
vì sao "password1" 9 ký tự chiếm 12 bytes chứ ko phải 10 bytes thì là do data alignment. Data alignment nôm na là máy tính đọc thường đọc 4 bytes 1 lúc, ko đọc từng byte. Ở đây nếu "password1" chiếm 10 byte thì để máy tính đọc auth_flag, máy tính sẽ phải đọc 2 lần: lần 1 là 2 bytes cuối của "password1" và 2 bytes đầu của auth_flag. Lần 2 là 2 bytes cuối của auth_flag và 2 byte rác phía sau. Sau đó phải ghép 2 bytes đầu và 2 bytes cuối của auth_flag lại với nhau để được auth_flag. Vì đọc khó khăn vậy nên password1" chiếm luôn 12 bytes, việc đọc auth_flag sẽ dễ hơn, chỉ cần đọc 1 lần và chả phải ghép gì.
Đã được chỉnh sửa lần cuối bởi INTP : 07-03-2015 lúc 01:19 AM.
@thớt: vậy tại sao dữ liệu lại có thứ tự như vậy
ta cũng nghĩ là ko nằm ở đó được, nhưng chạy cái test trên thì thấy đúng 29 ký tự thì nó cho access granted, 28 ký tự thì denied, tức là auth_flag phải nằm ở đúng byte thứ 28-31. Như vậy khoảng cách giữa auth_flag và password_buffer phải là 12 bytes, vừa khớp cho chuỗi "password1" kia. Như vậy chắc là trình duyệt tạo thêm 1 biến tạm thời chứa giá trị "password1" nên nó bị đẩy lên stack luôn :-?
nếu ko có thì xâu 28 hay 17 ký tự là chép hư auth_flag rồi @_@
ở ví dụ trên nếu chuyển độ dài password_buffer nhỏ hơn ví dụ password_buffer[10] thì cái giải thích của INTP có vẻ không đúng thì phải?
The problem is not the problem. the problem is your attitude about the problem
Làm vài ví dụ thì thấy, nếu password_buffer nhỏ hơn 12 bytes thì ô nhớ kế dùng để lưu biến auth_flag
còn nếu password_buffer lớn hơn 12 bytes thì ô nhớ kế đó dùng để lưu "password1"
Không biết có phải không!
- - - Nội dung đã được cập nhật ngày 06-03-2015 lúc 11:55 PM - - -
mình nhập thôi mà...nhập thế cho dễ đếm thôi !!!
The problem is not the problem. the problem is your attitude about the problem
ta chạy thử thấy nó denied hết mà @_@ Hơn nữa in địa chỉ từng biến ra thì thấy auth_flag được để nằm trước password_buffer và "password1" thì nằm trong vùng .data chứ ko phải stack. Ko hiểu sao lần chạy kia 29 ký tự lại granted =)
compile có phải ở chế độ debug ko? có optimize gì ko?
ồ ta ko xài -O2 thì nó ra lỗi granted như trên =)
"password1" KHÔNG nằm trên stack. Cách giải thích của ta sai rồi. Nhưng sửa buffer lại thành [12] thì chuỗi 13 14 ký tự nó đều cho granted trong khi 16 lại ko cho =)
[12] thì buffer ở cách 0x23fe00 16 byte
23fe10
23fe1c
=> buffer cách auth_flag 12 ký tự, input >= 13 ký tự sẽ làm auth_flag mang giá trị khác 0
[16] thì buffer ở ngay 0x23fe00
23fe00
23fe1c
=> buffer cách auth_flag 28 ký tự, input >= 29 ký tự sẽ làm auth_flag mang giá trị khác 0
vậy là tùy thằng biên dịch lúc chưa optimize nó sắp xếp dữ liệu như thế nào.
nếu có flag -O2
[16] hay [12] thì đều ra
23fe00
23fdfc
tức là auth_flag nằm trước buffer 4 bytes => ko bị chép đè, bảo đảm ko bị lỗi granted.
(nhưng bao giờ cũng gặp lỗi buffer overflow)
Đã được chỉnh sửa lần cuối bởi INTP : 07-03-2015 lúc 01:23 AM.
"password1" là literal string mà, nằm ở .data chứ
Mà nếu vọc sao đó để sinh mã assembly sẽ dễ thấy hơn.