עבור לתוכן

באג ב C++? גישה ושינוי כל שדה PRIVATE במחלקה?

Featured Replies

פורסם

רעיון נחמד שעלה לי לראש בזמן היותי במכללה היום:

לפני כחודש+ היינו צריכים לממש מבנה נתונים מחסנית על מבנה נתונים מערך דינאמי. ואז חשבתי הרי קיימת פונקציה getTop אשר מחזירה מצביע לאיבר שנמצא בראש המחסנית, והרי לכם גישה לגשת לכל האיברים במחסנית:


int *p = getTop(s);
p++; //item 2
p++; //item 3
etc

באג. לא ביג דיל, בכל מקרה לממש מסנית על מערך זה טיפשי. ואז חשבתי הרי כל המשתנים נשמרים ברצף בזכרון אז מה הבעיה לגשת לכל משתנה private/protected במחלקה לשנות אותם? וצדקתי, אין שום בעיה לעשות זאת. להלן הקוד אשר מאפשר לקבל גישה לכל משתנה סגור/מוגן וגם לשנות אותם, הקוד עושה בדיוק מה שכתוב ב printfים:


#ifndef HACKME_H_INCLUDED
#define HACKME_H_INCLUDED

class HackMe{
public:
HackMe(){
_int1 = 10;
_int2 = 20;
_int3 = 30;
}

const int &getConstInt1() const{
return _int1;
}

int &getInt1(){
return _int1;
}

void printAll(){
printf("\nIn Hack Me [HackMe::printAll()]\nint1 = %d int2 = %d int3 = %d\n\n", _int1, _int2, _int3);
}

private:
int _int1, _int2, _int3;
};

#endif // HACKME_H_INCLUDED



#include <iostream>
#include "HackMe.h"

int main(){
HackMe h;

h.printAll();

int *pi = &h.getInt1();
printf("\nValue of HackMe::_int1, we can access to it via HackMe::getInt1()\n");
printf("%x = %d\n", &(*pi), *pi);


pi += 1;
printf("\nValue of HackMe::_int2, we cant access to it via HackMe at all!\n");
printf("%x = %d\n", &(*pi), *pi);

pi += 1;
printf("\nValue of HackMe::_int3, we cant access to it via HackMe at all!\n");
printf("%x = %d\n", &(*pi), *pi);


printf("\n\nLets modify some things \n\n");
printf("HackMe::getConstInt1() return a const reference to HackMe::_int1, \
const means we can not modify it. Lets check if its true:\n");

const int *foo = &h.getConstInt1();
int *p = const_cast<int*>(foo);
*p = 3;
printf("We set HackMe::_int1 to 3, lets see if it worked\n");
h.printAll();


printf("We dont have access to HackMe::_int2 but we can access HackMe::_int1, lets modify int2 via HackMe::getConstInt1()\n");
const int *foo2 = &h.getConstInt1();
foo2 ++;
int *p2 = const_cast<int*>(foo2);
*p2 = 7;
printf("We set HackMe::_int2 to 7, lets see if it worked\n");
h.printAll();

printf("One more? Ok, lets modify HackMe::_int3 using HackMe::getInt1()\n");
int *abc = &h.getInt1();
abc += 2;
*abc = 18;
printf("We set HackMe::_int3 to 18, lets see if it worked\n");
h.printAll();

printf("Last one. Modify HackMe::_int1 wihtout additional variables (to show how cool I am in pointers )\n");
*(&h.getInt1()) = 90;
printf("We set HackMe::_int1 to 90, lets see if it worked\n");
h.printAll();

printf("Was fun to hack C++ class encapsulation, have a good day :)");

return 0;
}

ולהלן ה OUTPUT למי שלא רוצה לקמפל לבד:



In Hack Me [HackMe::printAll()]
int1 = 10 int2 = 20 int3 = 30


Value of HackMe::_int1, we can access to it via HackMe::getInt1()
bfcdd780 = 10

Value of HackMe::_int2, we cant access to it via HackMe at all!
bfcdd784 = 20

Value of HackMe::_int3, we cant access to it via HackMe at all!
bfcdd788 = 30


Lets modify some things

HackMe::getConstInt1() return a const reference to HackMe::_int1, const means we can not modify it. Lets check if its true:
We set HackMe::_int1 to 3, lets see if it worked

In Hack Me [HackMe::printAll()]
int1 = 3 int2 = 20 int3 = 30

We dont have access to HackMe::_int2 but we can access HackMe::_int1, lets modify int2 via HackMe::getConstInt1()
We set HackMe::_int2 to 7, lets see if it worked

In Hack Me [HackMe::printAll()]
int1 = 3 int2 = 7 int3 = 30

One more? Ok, lets modify HackMe::_int3 using HackMe::getInt1()
We set HackMe::_int3 to 18, lets see if it worked

In Hack Me [HackMe::printAll()]
int1 = 3 int2 = 7 int3 = 18

Last one. Modify HackMe::_int1 wihtout additional variables (to show how cool I am in pointers )
We set HackMe::_int1 to 90, lets see if it worked

In Hack Me [HackMe::printAll()]
int1 = 90 int2 = 7 int3 = 18

Was fun to hack C++ class encapsulation, have a good day

אומנם בהתחלה הייתה בעיה אם מגידירים שהפונקציה מחזירה רפרנס קבוע, לא היה ניתן לשנות אותו, ואז נזכרתי באופרטור הקסם const_cast, ומפה זה לא היה קשה.

כן נכון תגידו שידעתי את המבנה של המחלקה ובלה בלה בלה, זה עדיין אפשרות לגשת לזכרון של מחלקה...

איך ניתן לחסום את זה? והאם הבאג הזה ידוע למפתחי השפה?

פורסם

זה לא באג,

אם המתכנת פונה ישירות לזכרון וקורא\משנה שם ערכים המערכת מניחה שהוא יודע מה שהוא עושה ואין לה סיבה למנוע את זה גם אם הערכים האלה כביכול הם private. הגדרות של private (או protected) נועדו כדי לממש עקרונות שאמורים להקל על המתכנת, הם לא מתיימרים להיות פתרונות הרמטיים לאבטחת מידע.

פורסם

בדיוק. כל המנגנונים האלה לא נועדו לאבטח שום דבר, בדיוק כי למתכנת יש גישה מלאה לזכרון.

המטרה של מנגנונים כאלה היא למנוע באגים אפשריים.

שים לב שההצהרה const עובדת באותו אופן - אם יש לך משתנה שהוא const אז איבר להוריד ממנו את ה-const באמצעות const_cast (זה מובנה בשפה). האם זו חולשה? לא - אם מתכנת מצא סיבה מספיק טובה להשתמש ב-const_cast (והוא מודע להשלכות של זה), למה שאנחנו נמנע ממנו?

כמובן, זו הגישה של שפות כמו ++C. בשפות יותר חדשות, כמו Java או #C, הגישה הרבה יותר קפדנית - אתה לא יכול לגשת ישירות לזכרון, אתה לא יכול להמיר בין טיפוסים באופן גמיש (אתה אפילו לא יכול להתייחס לתו בתור מספר בלי להשתמש בפונקציה שממירה אותו). מצד אחד זה מאוד קפדני, ולכן מונע באגים. מצד שני, זה כן מכביד על התוכנית (צריך הרבה יותר המרות ובדיקות בזמן ריצה).

פורסם
  • מחבר

הממ יש בזה הגיון. תודה רבה על ההסברים :)

פורסם

גם ב C#, יש גישה ל PRIVATE של הכל אם אתה רוצה, בעזרת REFLECTIONS.

סתם מתוך סקרנות, למה GETTOP מחזירה מצביע לאיבר הראשון, ולא את האיבר הראשון?

במקרה הזה, הבעייה לא בשפה, אלה בהגדרה שלך של מבנה הנתונים.

פורסם
  • מחבר

לא יודע, לא למדתי C# לעומק כל כך.

הממ כי אם נניח אתה שומר מחסנית של רשומות שכל אחת מהן היא עץ בינארי, ויש לך דגש על זמן ריצה, למה להחזיר עותק של איבר (כן אני יודע זה לא רלוונטי לאיבר אחד ומבנה דיי פשוט, אבל תניח שאתה מוציא ומכניס אלפים או עשרות אלפים של מבנים מסובכים) אז יותר הגיוני להחזיר רפרנס, אך לא קיים כזה בשפת C (לפחות לא הצלחתי להשתמש בו) אז אני מחזיר מצביע. חוץ מזה זו הייתה הדרישה של עבדות ההגשה.

פורסם

ב-C באמת אין reference, אבל ב-++C יש.

ערך ההחזרה של getTop צריך להיות ∫ במקום *int, והיא צריכה להחזיר את המשתנה עצמו, במקום את המצביע אליו (כלומר אם כבר יש לך מצביע אז תעשה לו dereference בהחזרה).

ארכיון

דיון זה הועבר לארכיון ולא ניתן להוסיף בו תגובות חדשות.

דיונים חדשים