هل يمكنني الوصول إلى أعضاء خاصين من خارج الفصل دون الاستعانة بالأصدقاء؟

StackOverflow https://stackoverflow.com/questions/424104

سؤال

تنصل

نعم، أدرك تمامًا أن ما أسأل عنه غبي تمامًا وأن أي شخص يرغب في تجربة شيء كهذا في كود الإنتاج يجب أن يتم طرده و/أو إطلاق النار عليه.أنا أتطلع بشكل رئيسي لمعرفة ما إذا كان يستطيع تتم.

الآن بعد أن أصبح هذا بعيدًا، هل هناك أي طريقة للوصول إلى أعضاء الفصل الخاص في C++ من خارج الفصل؟على سبيل المثال، هل هناك أي طريقة للقيام بذلك باستخدام إزاحات المؤشر؟

(نرحب بالتقنيات الساذجة وغير الجاهزة للإنتاج)

تحديث

كما هو مذكور في التعليقات، طرحت هذا السؤال لأنني أردت أن أكتب تدوينة حول التغليف الزائد (وكيف يؤثر ذلك على TDD).أردت أن أرى ما إذا كانت هناك طريقة لقول "استخدام المتغيرات الخاصة ليس وسيلة موثوقة بنسبة 100 ٪ لفرض التغليف ، حتى في C ++." في النهاية ، قررت التركيز أكثر على كيفية حل المشكلة بدلاً من سبب مشكلة ، لذلك لم أتعرض بعض الأشياء التي ظهرت هنا بشكل بارز كما خططت ، لكنني ما زلت أترك رابطًا.

على أية حال، إذا كان أي شخص مهتمًا بكيفية ظهور ذلك، فإليك ما يلي: أعداء التطوير الموجه بالاختبار الجزء الأول:التغليف (أقترح قراءتها قبل أن تقرر أنني مجنون).

هل كانت مفيدة؟

المحلول

إذا الطبقة تحتوي على أية وظائف عضو قالب يمكنك متخصصون أن دالة عضو لتناسب احتياجاتك. حتى لو لم المطور الأصلي لا يفكر في ذلك.

وsafe.h

class safe
{
    int money;

public:
    safe()
     : money(1000000)
    {
    }

    template <typename T>
    void backdoor()
    {
        // Do some stuff.
    }
};

وMAIN.CPP:

#include <safe.h>
#include <iostream>

class key;

template <>
void safe::backdoor<key>()
{
    // My specialization.
    money -= 100000;
    std::cout << money << "\n";
}

int main()
{
    safe s;
    s.backdoor<key>();
    s.backdoor<key>();
}

وإخراج:

900000
800000

نصائح أخرى

لقد أضفت الدخول إلى مدونتي (انظر أدناه) يوضح كيف يمكن القيام بذلك.فيما يلي مثال على كيفية استخدامه للفئة التالية

struct A {
private:
  int member;
};

ما عليك سوى الإعلان عن البنية الخاصة بها حيث تصفها وإنشاء مثيل لفئة التنفيذ المستخدمة للسرقة

// tag used to access A::member
struct A_member { 
  typedef int A::*type;
  friend type get(A_member);
};

template struct Rob<A_member, &A::member>;

int main() {
  A a;
  a.*get(A_member()) = 42; // write 42 to it
  std::cout << "proof: " << a.*get(A_member()) << std::endl;
}

ال Rob يتم تعريف قالب الفصل على هذا النحو، ويحتاج إلى تعريفه مرة واحدة فقط، بغض النظر عن عدد الأعضاء الخاصين الذين تخطط للوصول إليهم

template<typename Tag, typename Tag::type M>
struct Rob { 
  friend typename Tag::type get(Tag) {
    return M;
  }
};

ومع ذلك، لا يُظهر هذا أن قواعد الوصول الخاصة بـ c++ غير موثوقة.تم تصميم قواعد اللغة للحماية من الأخطاء العرضية - إذا حاولت سرقة بيانات كائن ما، أي اللغة من تصمبم لا يستغرق طرقا طويلة لمنعك.

وفيما يلي متستر وغير قانوني وتعتمد على المترجم، وقد لا تعمل بالاعتماد على مختلف تفاصيل التنفيذ.

#define private public
#define class struct

ولكن هذا هو الجواب على OP الخاص بك، والتي يمكنك دعوة صراحة وهو الأسلوب الذي، وأنا أقتبس، هو "غبي تماما وأن أي شخص يرغب في محاولة شيء من هذا القبيل في رمز الإنتاج يجب أن تطلق و / أو إطلاق النار عليهم ".


وأسلوب آخر هو الوصول إلى البيانات أعضاء خاصة، من خلال contructing مؤشرات باستخدام إزاحة الثابت تلوينها / تلوينها يدويا من بداية الكائن.

وهممم، لا أعرف إذا كان ذلك العمل، ولكن قد يكون يستحق المحاولة. إنشاء فئة أخرى مع نفس التخطيط ككائن مع أعضاء من القطاع الخاص ولكن مع تغير الخاص إلى العام. إنشاء متغير مؤشر إلى هذه الفئة. استخدام الجبس بسيط الإشارة إلى ذلك وجوه الخاص بك مع أعضاء الخاص ومحاولة استدعاء دالة خاصة.

وتوقع الشرر وربما حادث.)

class A 
{ 
   int a; 
}
class B
{
   public: 
   int b;
}

union 
{ 
    A a; 
    B b; 
};

وهذا ينبغي أن نفعل ذلك.

وETA: وسوف يعمل لهذا النوع من الطبقة تافهة، ولكن كشيء العام أنها لن تفعل ذلك.

<اقتباس فقرة>   

وTC ++ PL القسم C.8.3: "هناك فئة مع منشئ، المدمر، أو نسخ العملية لا يمكن أن يكون نوع لأحد أعضاء اتحاد ... لأن من شأنه أن المترجم لا يعرف أي عضو لتدمير"

وهكذا نحن غادر مع أفضل رهان الحاضر لإعلان class B لمطابقة تخطيط A والإختراق للنظر في حافظون فئة ل.

إذا كنت تستطيع الحصول على مؤشر إلى عضو من فئة يمكنك استخدام مؤشر بغض النظر عن ما هي المحددات وصول (حتى طرق).

class X;
typedef void (X::*METHOD)(int);

class X
{
    private:
       void test(int) {}
    public:
       METHOD getMethod() { return &X::test;}
};

int main()
{
     X      x;
     METHOD m = x.getMethod();

     X     y;
     (y.*m)(5);
}

وبالطبع بلدي الإختراق القليل المفضل هو القالب صديق الباب الخلفي.

class Z
{
    public:
        template<typename X>
        void backDoor(X const& p);
    private:
        int x;
        int y;
};

وعلى افتراض خالق مستتر فوق حددت لاستخدامات له العادية. ولكنك تريد الوصول إلى كائن والنظر في المتغيرات عضو الخاصة. حتى لو تم تجميع الطبقة المذكورة أعلاه إلى مكتبة ثابتة يمكنك إضافة الخاصة بك قالب التخصص الخاص لمستتر، وبالتالي الوصول إلى أعضاء.

namespace
{
    // Make this inside an anonymous namespace so
    // that it does not clash with any real types.
    class Y{};
}
// Now do a template specialization for the method.
template<>
void Z::backDoor<Y>(Y const& p)
{
     // I now have access to the private members of Z
}

int main()
{
    Z  z;   // Your object Z

    // Use the Y object to carry the payload into the method.
    z.backDoor(Y());
}

ومن الممكن بالتأكيد للوصول أعضاء خاص مع مؤشر تعويض في C ++. دعونا نفترض كان لي تعريف نوع التالية التي أردت الوصول إليها.

class Bar {
  SomeOtherType _m1;
  int _m2;
};

وعلى افتراض عدم وجود طرق الظاهري في بار، وهذه القضية السهلة هي _m1. يتم تخزين الأعضاء في C ++ كما إزاحة من موقع الذاكرة من الكائن. الكائن الأول هو عند الإزاحة 0، الكائن الثاني في إزاحة sizeof (العضو الأول)، الخ ...

وحتى هنا هو وسيلة للوصول إلى _m1.

SomeOtherType& GetM1(Bar* pBar) {
  return*(reinterpret_cast<SomeOtherType*>(pBar)); 
}

والآن هو _m2 قليلا أكثر صعوبة. نحن بحاجة لتحريك مؤشر sizeof الأصلي (SomeOtherType) بايت من الأصل. المدلى بها إلى شار هو التأكد من أن أنا تزايد في الإزاحة

int& GetM2(Bar* pBar) {
  char* p = reinterpret_cast<char*>(pBar);
  p += sizeof(SomeOtherType);
  return *(reinterpret_cast<int*>(p));
}

والسؤال بارد راجع للشغل ... وهنا قال لي قطعة:

using namespace std;

class Test
{

private:

  int accessInt;
  string accessString;

public:

  Test(int accessInt,string accessString)
  {
    Test::accessInt=accessInt;
    Test::accessString=accessString;
  }
};

int main(int argnum,char **args)
{
  int x;
  string xyz;
  Test obj(1,"Shit... This works!");

  x=((int *)(&obj))[0];
  xyz=((string *)(&obj))[1];

  cout<<x<<endl<<xyz<<endl;
  return 0;
}

وآمل أن يساعد هذا.

ويستند هذا الجواب على مفهوم المحدد الذي أبداه @ يوهانس في الجواب / بلوق ، لأن ذلك يبدو أن فقط "المشروعة" وسيلة. لقد تحول هذا الرمز سبيل المثال إلى أداة يدوية. انها متوافقة بسهولة مع C ++ 03 (من خلال تنفيذ std::remove_reference واستبدال nullptr).

مكتبة

#define CONCATE_(X, Y) X##Y
#define CONCATE(X, Y) CONCATE_(X, Y)

#define ALLOW_ACCESS(CLASS, TYPE, MEMBER) \
  template<typename Only, TYPE CLASS::*Member> \
  struct CONCATE(MEMBER, __LINE__) { friend TYPE (CLASS::*Access(Only*)) { return Member; } }; \
  template<typename> struct Only_##MEMBER; \
  template<> struct Only_##MEMBER<CLASS> { friend TYPE (CLASS::*Access(Only_##MEMBER<CLASS>*)); }; \
  template struct CONCATE(MEMBER, __LINE__)<Only_##MEMBER<CLASS>, &CLASS::MEMBER>

#define ACCESS(OBJECT, MEMBER) \  
(OBJECT).*Access((Only_##MEMBER<std::remove_reference<decltype(OBJECT)>::type>*)nullptr)

API

ALLOW_ACCESS(<class>, <type>, <member>);

الاستخدام

ACCESS(<object>, <member>) = <value>;   // 1
auto& ref = ACCESS(<object>, <member>); // 2

مثال

struct X {
  int get_member () const { return member; };
private:
  int member = 0;
};

ALLOW_ACCESS(X, int, member);

int main() {
  X x;
  ACCESS(x, member) = 42;
  std::cout << "proof: " << x.get_member() << std::endl;
}

إذا كنت تعرف كيف mangles مترجم ++ C بك أسماء، نعم.

ما لم يكن، على ما اعتقد، انها وظيفة افتراضية. ولكن بعد ذلك، إذا كنت تعرف كيف يبني مترجم C ++ الخاص بك VTABLE ...

وتحرير: النظر في ردود أخرى، وأنا أدرك أنني أخطأت في مسألة ويعتقد أنه كان على وشك ظائف الأعضاء، وليس بيانات الأعضاء. ومع ذلك، وهذه النقطة لا يزال قائما: إذا كنت تعرف كيفية المترجم الخاص بك يضع من البيانات، ثم يمكنك الوصول إلى تلك البيانات

وانها في الواقع من السهل جدا:

class jail {
    int inmate;
public:
    int& escape() { return inmate; }
};

وكبديل لالقالب طريقة مستتر يمكنك استخدام فئة القالب مستتر. الفرق هو أنك لا تحتاج إلى وضع هذه الفئة مستتر في المنطقة العامة للصفك ذاهبون لاختبار. يمكنني استخدام حقيقة أن العديد من المجمعين تسمح الطبقات المتداخلة للوصول إلى المناطق الخاصة لتضم الفئة (وهي ليست بالضبط 1998 معيار لكنها اعتبرت أن "الحق" السلوك). وبالطبع في C ++ 11 أصبح هذا السلوك القانوني.

وانظر هذا المثال:

#include <vector>
#include <cassert>
#include <iostream>
using std::cout;
using std::endl;


///////// SystemUnderTest.hpp
class SystemUnderTest
{
   //...put this 'Tested' declaration into private area of a class that you are going to test
   template<typename T> class Tested;
public:
   SystemUnderTest(int a): a_(a) {}
private:
   friend std::ostream& operator<<(std::ostream& os, const SystemUnderTest& sut)
   {
      return os << sut.a_;
   }
   int a_;
};

/////////TestFramework.hpp
class BaseTest
{
public:
   virtual void run() = 0;
   const char* name() const { return name_; }
protected:
   BaseTest(const char* name): name_(name) {}
   virtual ~BaseTest() {}
private:
   BaseTest(const BaseTest&);
   BaseTest& operator=(const BaseTest&);
   const char* name_;
};

class TestSuite
{
   typedef std::vector<BaseTest*> Tests;
   typedef Tests::iterator TIter;
public:
   static TestSuite& instance()
   {
      static TestSuite TestSuite;
      return TestSuite;
   }
   void run()
   {
      for(TIter iter = tests_.begin(); tests_.end() != iter; ++iter)
      {
         BaseTest* test = *iter;
         cout << "Run test: " << test->name() << endl;
         test->run();
      }
   }
   void addTest(BaseTest* test)
   {
      assert(test);
      cout << "Add test: " << test->name() << endl;
      tests_.push_back(test);
   }
private:
   std::vector<BaseTest*> tests_;
};

#define TEST_CASE(SYSTEM_UNDER_TEST, TEST_NAME) \
class TEST_NAME {}; \
template<> \
class SYSTEM_UNDER_TEST::Tested<TEST_NAME>: public BaseTest \
{ \
   Tested(): BaseTest(#SYSTEM_UNDER_TEST "::" #TEST_NAME) \
   { \
      TestSuite::instance().addTest(this); \
   } \
   void run(); \
   static Tested instance_; \
}; \
SYSTEM_UNDER_TEST::Tested<TEST_NAME> SYSTEM_UNDER_TEST::Tested<TEST_NAME>::instance_; \
void SYSTEM_UNDER_TEST::Tested<TEST_NAME>::run()


//...TestSuiteForSystemUnderTest.hpp
TEST_CASE(SystemUnderTest, AccessPrivateValueTest)
{
   SystemUnderTest sut(23);
   cout << "Changed private data member from " << sut << " to ";
   sut.a_ = 12;
   cout << sut << endl;
}

//...TestRunner.cpp
int main()
{
   TestSuite::instance().run();
}

بجانب #تعريف الخاص العام بامكانك ايضا #تعريف المحمية الخاصة ثم قم بتعريف بعض فئات foo على أنها سليل للفئة المطلوبة للوصول إلى أساليبها (المحمية الآن) عبر صب النوع.

ومجرد خلق الخاصة الدالة العضو صولك إلى توسيع الطبقة.

لجميع الناس الذين يقترحون "#تعريف الخاص العام":

هذا النوع من الشيء هو غير قانوني.يحظر المعيار تحديد/إلغاء تحديد وحدات الماكرو المكافئة معجميًا للكلمات الأساسية للغة المحجوزة.على الرغم من أن المترجم الخاص بك ربما لن يشتكي (لم أر بعد مترجمًا يفعل ذلك)، إلا أنه ليس شيئًا "جيدًا" للقيام به.

"استخدام المتغيرات الخاصة ليس طريقة موثوقة بنسبة 100% لفرض التغليف، حتى في لغة C++."حقًا؟يمكنك تفكيك المكتبة التي تحتاجها، والعثور على جميع الإزاحات المطلوبة واستخدامها.سيعطيك ذلك القدرة على تغيير أي عضو خاص تريده ...لكن!لا يمكنك الوصول إلى الأعضاء الخاصين دون بعض القرصنة القذرة.دعنا نقول أن الكتابة مقدار ثابت لن تجعل ثابتك ثابتا حقا ، لأنك تستطيع يلقي مقدار ثابت بعيدًا أو استخدم عنوانه فقط لإبطاله.إذا كنت تستخدم MSVC++ وقمت بتحديد "-merge:.rdata=.data" لرابط، فستعمل الخدعة دون أي أخطاء في الوصول إلى الذاكرة.يمكننا حتى أن نقول أن كتابة التطبيقات بلغة C++ ليست طريقة موثوقة لكتابة البرامج، لأن التعليمات البرمجية ذات المستوى المنخفض الناتجة قد يتم تصحيحها من مكان ما بالخارج أثناء تشغيل تطبيقك.إذن ما هي الطريقة الموثقة الموثوقة لفرض التغليف؟هل يمكننا إخفاء البيانات في مكان ما في ذاكرة الوصول العشوائي (RAM) ومنع أي شيء من الوصول إليها باستثناء الكود الخاص بنا؟الفكرة الوحيدة التي أملكها هي تشفير الأعضاء الخاصين وعمل نسخة احتياطية لهم، لأن شيئًا ما قد يفسد هؤلاء الأعضاء.آسف إذا كانت إجابتي وقحة جدًا، لم أقصد الإساءة إلى أي شخص، لكنني حقًا لا أعتقد أن هذا البيان حكيم.

ولأن لديك كائن من الدرجة المطلوبة وأنا على التخمين أن لديك إعلان الدرجة. الآن ما يمكنك القيام به هو الإعلان فئة أخرى مع نفس الأعضاء ولكن يبقى كل من هناك محددات وصول والجمهور.

وعلى سبيل المثال الدرجة السابقة هي:

class Iamcompprivate
{
private:
    Type1 privateelement1;
    Typ2 privateelement2;
    ...

public:
    somefunctions
}

ويمكنك تعريف فئة ك

class NowIampublic
{
**public:**
    Type1 privateelement1;
    Type2 privateelement2;
    ...

    somefunctions
};

والمؤشر الآن كل ما عليك القيام به هو يلقي من Iamcompprivate الصف إلى مؤشر من NowIampublic الطبقة واستخدامها كما يحلو U.

مثال:

NowIampublic * changetopublic(Iamcompprivate *A)
{
    NowIampublic * B = (NowIampublic *)A;
    return B;
}

وبواسطة الرجوع إلى * <م> هذه تمكين مستتر لجميع البيانات الخاصة ضمن كائن.

class DumbClass
{
private:
    int my_private_int;
public:
    DumbClass& backdoor()
    {
        return *this;
    }
}

والى حد بعيد في كثير من الأحيان توفر الفئة أساليب mutator إلى البيانات الخاصة (حاصل على واضعي).

وإذا لم فئة توفير جالبة أن بإرجاع مرجع CONST (ولكن لا اضع)، ثم يمكنك فقط const_cast قيمة الإرجاع للجالبة، واستخدام ذلك باعتبارها ذات قيمة ل:

class A {
  private:
    double _money;
  public:
    A(money) :
      _money(money)
    {}

    const double &getMoney() const
    {
      return _money;
    }
};

A a(1000.0);
const_cast<double &>(a.getMoney()) = 2000.0;

لقد استخدمت طريقة (وحلًا) مفيدًا آخر للوصول إلى عضو خاص/محمي في c++.
الشرط الوحيد هو أن تكون قادرًا على الوراثة من الفصل الذي تريد الوصول إليه.
ثم يذهب كل الفضل إلى reinterpret_cast<>().

المشكلة المحتملة هي أنها لن تعمل إذا قمت بإدراج وظيفة افتراضية، والتي ستقوم بتعديل الجدول الظاهري، وبالتالي حجم/محاذاة الكائن.

class QObject
{
    Q_OBJECT
    Q_DECLARE_PRIVATE(QObject)
    void dumpObjectInfo();
    void dumpObjectTree();
...
protected:
    QScopedPointer<QObjectData> d_ptr;
...
}

class QObjectWrapper : public QObject
{
public:
    void dumpObjectInfo2();
    void dumpObjectTree2();
};

ثم تحتاج فقط إلى استخدام الفصل على النحو التالي:

QObject* origin;
QObjectWrapper * testAccesor = reinterpret_cast<QObjectWrapper *>(origin);
testAccesor->dumpObjectInfo2();
testAccesor->dumpObjectTree2();

مشكلتي الأصلية كانت كالتالي:كنت بحاجة إلى حل لا يتضمن إعادة ترجمة مكتبات QT.
هناك طريقتان في QObject, dumpObjectInfo() و dumpObjectTree()، أن اعمل فقط إذا تم تجميع QT libs في وضع التصحيح ، ويحتاجون بالطبع إلى الوصول إلى d_ptr عضو محمي (من بين الهياكل الداخلية الأخرى).
ما فعلته هو استخدام الحل المقترح لإعادة تنفيذ (بالنسخ واللصق) تلك الأساليب dumpObjectInfo2() و dumpObjectTree2() في صفي الخاص (QObjectWrapper) إزالة حراس المعالج المسبق لتصحيح الأخطاء.

والتعليمة البرمجية التالية يصل ويعدل عضو خاص من فئة باستخدام مؤشر إلى تلك الفئة.

#include <iostream>
using namespace std;
class A
{
    int private_var;
    public:
    A(){private_var = 0;}//initialized to zero.
    void print(){cout<<private_var<<endl;}
};

int main()
{
    A ob;
    int *ptr = (int*)&ob; // the pointer to the class is typecast to a integer pointer.  
    (*ptr)++; //private variable now changed to 1.
    ob.print();
    return 0;
}
/*prints 1. subsequent members can also be accessed by incrementing the pointer (and
  type casting if necessary).*/

والغرض الدراسة فقط .... حاول هذا .... قد يكون مفيدا، واعتقد ..... هذا البرنامج يمكن الوصول إلى البيانات الخاصة فقط من خلال معرفة القيم ...

//GEEK MODE....;)
#include<iostream.h>
#include<conio.h>

    class A
    {
    private :int iData,x;
    public: void get()             //enter the values
        {cout<<"Enter iData : ";
            cin>>iData;cout<<"Enter x : ";cin>>x;}

        void put()                               //displaying values
    {cout<<endl<<"sum = "<<iData+x;}
};

void hack();        //hacking function

void main()
{A obj;clrscr();
obj.get();obj.put();hack();obj.put();getch();
}

void hack()         //hack begins
{int hck,*ptr=&hck;
cout<<endl<<"Enter value of private data (iData or x) : ";
cin>>hck;     //enter the value assigned for iData or x
for(int i=0;i<5;i++)
{ptr++;
if(*ptr==hck)
{cout<<"Private data hacked...!!!\nChange the value : ";
cin>>*ptr;cout<<hck<<" Is chaged to : "<<*ptr;
return;}
}cout<<"Sorry value not found.....";
}

ومستوحاة منJohannes شواب - litb، قد تكون التعليمة البرمجية التالية أسهل قليلا لهضم

    struct A {
    A(): member(10){}
    private:
    int get_member() { return member;}
    int member;
   };

   typedef int (A::*A_fm_ptr)();
   A_fm_ptr  get_fm();

  template<   A_fm_ptr p> 
  struct Rob{ 
     friend A_fm_ptr  get_fm() {
   return p;
  }
};

 template struct Rob<  &A::get_member>;

 int main() {
   A a;
  A_fm_ptr p = get_fm();

    std::cout << (a.*p)() << std::endl;

  }
class Test{
    int a;
    alignas(16) int b;
    int c;
};

Test t;

الطريقة أ :مزاج تدخلي. نظرا لأنه يمكننا الوصول إلى شفرة المصدر وإعادة الامتثال لها ، يمكننا استخدامها العديد من الطرق الأخرى مثل فئة الأصدقاء للوصول إلى عضو خاص ، كلها باب خلفي قانوني.

الطريقة ب :مزاج وحشي.

int* ptr_of_member_c = reinterpret_cast<int*>(reinterpret_cast<char*>(&t) + 20);

نستخدم الرقم السحري (20)، وهو ليس صحيحًا دائمًا.عندما يتغير تخطيط الفصل الدراسي، يكون الرقم السحري مصدرًا للأخطاء الكبيرة.

الطريقة ج :مزاج الهاكر الفائق. هل هناك أي مزاج غير تدخلي وغير غاشم؟نظرا لأن معلومات تخطيط اختبار الفصل يتم إخفاؤها بواسطة المتوافق ، لا يمكننا الحصول على معلومات تعويض من فم الامتثال.السابق.

offsetof(Test,c); //complie error. they said can not access private member.

لا يمكننا أيضًا الحصول على مؤشر العضو من اختبار الفصل.السابق.

&Test::c ;  //complie error. they said can not access private member.

@ يوهانس شواب - لدى litb مدونة، وقد وجد طريقة لسرقة مؤشر الأعضاء الخاصين.لكنني اعتقدت أن هذا يجب أن يكون خطأ الملتزم أو مأزق اللغة.يمكنني الالتزام به على gcc4.8، لكن ليس على vc8.

لذلك قد يكون الاستنتاج: المالك يبني كل الباب الخلفي.لدى اللص دائمًا طريقة وحشية وسيئة لاقتحامها.يتمتع المتسلل العرضي بطريقة أنيقة وآلية لاقتحامه.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top