Սկիզբ » Ուսումնական նյութեր » Ծրագրավորում » Ծրագրավորման լեզուներ » C և C++ » C++ hack կամ private -ը այնքան էլ չի պաշտպանում ձեր տվյալները

C++ hack կամ private -ը այնքան էլ չի պաշտպանում ձեր տվյալները

| Նոյեմբեր 19, 2013 | Մեկնաբանված չէ |

Շատերի համար C++ լեզուն առաջին հերթին հետաքրքիր է նրանով, որ թույլ է տալիս անմիջականորեն գործ ունենալ հիշողության հետ ՝ օգտագործելով ցուցիչները (pointers)։ Բայց հիշողությանը ուղղակիորեն դիմելը նաև անկանխատեսելի հետևանքներ կարող է առաջացնել ;)։
Ցուցիչները առաջին հերթին հետաքրքիր են նրանով, որ թույլ են տալիս դիմել հիշողության որոշակի հասցեների և խմբագրել դրանք։ Եվ ահա այստեղ է, որ առաջանում են բազմաթիվ գլխացավանքներ, որոնք կարող են հանգեցնել անկանխատեսելի հետևանքների։ Այս հոդվածում կներկայացնենք մի իրավիճակ, երբ տվյալների պարզ տիպի ցուցիչի (օրինակ int*) միջոցով  կարելի է ստանալ և փոխել դասի private անդամ փոփոխականի արժեքը, այդպիսով խախտելով դասի ինկապսուլացումը !: Դա անելու համար հարկավոր է իմանալ, թե ինչպես է դասի օբյեկտը (նմուշը) պահվում հիշողության մեջ:

Դիցուք ունենք հետևյալ դասը․

class foo
{
int a;
char c;
void bar();
};

Ինչպես ցույց է տրված նկարում, դասի օբյեկտը հիշողության մեջ զբաղեցնում է անընդհատ տիրույթ և այդ տիրույթը զբաղեցնում են միայն դասի անդամ փոփոխականները, տվյալ դեպքում օբյեկտի չափը կլինի 5 բայթ ( int a 4 բայթ + char c 1 բայթ )՝ չհաշված հավասարեցումը ! : Եթե անգամ դասը չի պարունակում ոչ մի անդամ փոփոխական, դրա օբյեկտի չափը տարբեր է զրոյից, իսկ թե որքան է դրա չափը, կախված է կոնկրետ մեքենայից և կոմպիլյատորից։
Դասի անդամ ֆունկցիաները որևէ կերպ չեն անդրադառնում հիշողությամ մեջ օբյեկտի կառուցվածքի վրա։ Պարզ ասած, օբյեկտի զբաղեցրած չափը հիշողության մեջ կախված է միայն դրա անդամ փոփոխականների գումարային չափից, իսկ եթե դասը նաև պարունակում է վիրտուալ ֆունկցիաներ և/կամ գտնում է ժառանգման շղթայի մեջ այնպիսի դասերից, որոնք պարունակում են վիրտուալ ֆունկցիաներ, ապա  օբյեկտի չափի մեջ ներառվում է նաև վիրտուլ ֆունկցիաների աղյուսակի ցուցիչի (vptr) չափը (եթե դասը գտնվում է ժառանգման շղթայի մեջ այնպիսի դասերից, որոնք պարունակում են վիրտուալ ֆունկցիաներ, ապա տվյալ դասի օբյեկտը պարունակում է նաև բազային դասերի վիրտուալ ֆունկցիաների աղյուսակների ցուցիչները)։ Հիմա կասեք, իսկ ինչպե՞ս է այդ դեպքում կանչվում է bar()  ֆունկցիան, եթե այն չի մտնում օբյեկտի հիշողության մեջ։ Ամեն ինչ շատ պարզ է 🙂 ։ Իրականում low level տեսակետից դասի անդամ ֆունկցիան քիչ է տարբերվում գլոբալ ֆունկցիայից։ Տարբերությունը կայանում է նրանում, որ դասի անդամ ֆունկցիայի կանչի դեպքում կոմպիլյատորը գիտի այն օբյեկտի հասցեն, որի միջոցով կանչվում է տվյալ ֆունկցիան և որպես թաքնված պարամետր տվյալ անդամ ֆունկցիան կոմպիլյատորի կողմից ավտոմատ ստանում է this ցուցիչը (տվյալ օբյեկտի հասցեն)։ Այսինքն, եթե ունենք․

foo f;
f.bar();

պսևդոկոդով f.bar()-ը կարելի է գրել հետևյալ կերպ․

foo::bar(&f);

Լավ թողնենք ֆունկցիաների վերաբերյալ փոքրիկ լիրիկական զեղումը և անցնենք private -ին։ Ինչպես գիտենք, եթե դասի փոփոխականները հայտարարված են private, ապա դրանք հասանելի են միայն տվյալ դասի անդամ ֆունկցիաների և friend -ների համար։ Բայց, եթե կարողանանք ստանալ դասի օբյեկտի հասցեն և հաշվի առնելով այն, որ օբյեկտը հիշողության մեջ զբաղեցնում է հաջորդական անընդհատ տիրույթ, ապա կկարողանանք նաև դիմել դրա անդամներին !!! Ասվածը գրված է․

#include <iostream>

class foo
{
    private:
    int a;
    public:
    int get_a(){return a;}
};

int main()
{
   foo obj;                                                            // 1
   int* p = (int*)&obj;                                                // 2 
   *p = 27;                                                            // 3
   std::cout << "Data member value  " << obj.get_a() << std::endl;           // 4

   return 0;
}

Բերված կոդի 1 տողում սովորականի պես ստեղծում ենք foo դասի obj օբյեկտը, իսկ ամենահետաքրքիրը կատարվում է 2-րդ տողում․  2 – րդ տողում (int*)&obj գրառման միջոցով ստանում ենք obj օբյեկտի հասցեն, կամ որ նույնն է, օբյեկտի հասցեական շեղումը հիշողության մեջ։ Օբյեկտի հասցեն հիշողության մեջ ցույց է տալիս այն կետը, որտեղից սկսում է օբյեկտի պարունակութունը, հետևաբար այն միաժամանակ հանդիսանում է նաև օբյեկտի առաջին փոփոխականի հասցեն (մեր մոտ int a անդամ փոփոխականի), այսինքն դիցուք obj օբյեկտի հասցեն է 0x40000000, դա նշանակում է, որ a փոփոխականի հասցեն նույնպես կլինի 0x40000000 կամ որ նույնն է &obj, իսկ c փոփոխականի հասցեն կլինի 0x40000004 (char – տվյալների տիպը հիշողության մեջ զբաղեցնում է 1 բայթ)։ 2 -րդ տողում մենք int* տիպի p ցուցիչին վերագրում ենք մեր օբյեկտի հասցեն, դրանից հետո p ցուցիչը կհղվի մեր obj օբյեկտի շեղման վրա (հիշողության մեջ օբյեկտի սկզբի վրա), իսկ դա նշանակում է, որ 3-րդ տողում *p = 27 գրառումով մենք փոխում ենք p հասցեով 4 բայթ տարածք հիշողության մեջ, դե կռահեք խնդրեմ, թե դա որ փոփոխականի տարածքն է հիշողության մեջ ;): Եվ վերջապես 4-րդ տողում տպելով մեր a private անդամ փոփոխականի արժեքը իրոք համոզվում ենք որ այն հավասար է 27 -ի 😀 😉

C++ hack կամ private -ը այնքան էլ չի պաշտպանում ձեր տվյալները, 9.0 out of 10 based on 9 ratings

Նշագրեր:

Բաժին: C և C++, Ծրագրավորում

Կիսվել , տարածել , պահպանել

VN:F [1.9.20_1166]
Rating: 9.0/10 (9 votes cast)

Մեկնաբանեք

Կհաստատվեն միայն մեսրոպատառ հայերենով գրած մեկնաբանությունները

247