It is well known that class members in C++ can have three access specifiers: public
, protected
, and private
:
[ISO/IEC 14882:2014] A member of a class can be
- private: that is, its name can be used only by members and friends of the class in which it is declared.
- protected: that is, its name can be used only by members and friends of the class in which it is declared, by classes derived from that class, and by their friends (see 11.4).
- public: that is, its name can be used anywhere without access restriction.
From the standard’s intention, it aims to hide implementation details and underlying data of a class, which is encapsulation. However, we can also bypass access restrictions through some special means.
First, let’s correct a misconception: in C++, class access control restricts the access permissions of members, not their visibility.
[ISO/IEC 14882:2014] It should be noted that it is access to members and base classes that is controlled, not their visibility.
Any member of a class is visible to any code that can see its class implementation; the issue is whether it can be accessed. When you directly access a member’s name (identifier) inside a class, it will check whether you have permission to access that member. If you mark it as private
and access it from outside the class definition or a friend, it will prompt a compilation error. For a specific description of visibility and accessibility in access control, please refer to my other article: Visibility and Accessibility in Access Control Mechanisms.
Thus, we can access class members without triggering the access control mechanism by not using member names. One way to access class members without using class member names is through pointers to class members. For detailed content on pointers to class members in C++, please check my previous article: Pointers to Class Members Are Not Pointers.
From an invasive implementation perspective, we can actively provide methods to access class private
members through class member functions.
A common way is to return a reference to the private
data member or return a pointer to class members. I prefer returning a pointer to members since if a reference is returned, only a specific object’s members can be accessed later, whereas a function pointer can access members of any object of that class.
1 | class A{ |
Since what we get here is the address of the object (data member/function object), it can be accessed externally through an object of the class or a pointer to the class object, as accessing the pointer to class members is actually calculating the offset of that member through this
, so it won’t trigger any name lookup or accessibility checks:
1 | A example(456); |
As we can see, the private
data member of class A was modified externally, and its private
member function was also callable externally. This approach can be combined into STL’s operator library to perform certain operations on class objects in bulk.
However, this is based on the prerequisite that the class implementer actively provides access permissions.
Sometimes, for various reasons, we do not wish to manually modify class code (adding or removing class interfaces), but still have a need to access private
members. In this case, we can use the following two methods.
Assuming we have the following class:
1 | class A{ |
Because it has a template member function, we can break through the class access permissions through this template member function.
The method is to add a specialized version of this member function externally, because it is a specialization of the class member, and it also has permission to access all members inside the class:
1 | namespace { |
Here, when we use ZZZ
as a parameter to call A::func
member function, we can access the private member, thereby breaking the access restrictions of the C++ class. Whether directly manipulating private
inside or passing out member pointers (to incoming objects), the private members of class A can be accessed externally.
1 | A example(456); |
If a class has a friend template, the same behavior can also be achieved, and this method is fully compliant with the C++ standard.
Next, I’ll discuss a method to bypass access permissions dependent on compiler implementation:
Similarly using class A as the target for breaking through.
We can mimic a class with the same layout as the target, but change the access specifier of the corresponding private
member to public
or protected
, or other more relaxed access permissions (this actually depends on the implementation).
1 | // The only difference is that private_ is public in class B |
Then it can be used to manipulate:
1 | A example(456); |
The core idea here is reinterpret_cast<B&>(example)
, which forces the compiler to interpret an object of class A as an object of class B, then access the public member private_
in class B, but actually modifies class A’s private
member private_
. However, this also relies on the compiler’s implementation since we assume that the modified class B and class A have identical layouts, which is not guaranteed (this works in G++).
[TC++PL4th] A compiler may reorder sections of a class with separate access specifiers.
We can also place objects of class A
and class B
in a union
, storing in A’s way and reading in B’s way:
1 | union U{ |
Another trick to access a class’s private members is shown in the code below (the latest code will be placed on gist: hack-private-data-member.cpp):
1 |
|
The implementation of the above code utilizes templates to acquire pointers to member functions, resulting in the following expansion:
1 | class A{ |
The reason for using templates is to bypass access permission checks at compile time.
Since we need to acquire the address of A::func
, there would be access permission checks at runtime when calling &A::func
, which will not compile, thus necessitating the use of a template for early execution.
Hence, we define a new class where the type of the member we want to acquire is represented as a typedef
(MemType). This transforms the inaccessible member type A::func
into accessible member type A_func::MemType
, and importantly, it passes the template parameter (&A::func
) to it (T::MemType M
) to break through access control of the class.
Moreover, polymorphism is also supported:
1 | class A{ |
Conclusion: From the first method of bypassing class access control, one can see the influence of member function templates on class access control. It can be done by using member templates to bypass access control mechanisms. Importantly, this approach is portable and allows for code upgrades (as long as member names do not change). However, any attempts to circumvent member access restrictions are not recommended.
It is well known that in C++, class members can have three types of access control: public
/ protected
/ private
:
[ISO/IEC 14882:2014] A member of a class can be
- private: that is, its name can be used only by members and friends of the class in which it is declared.
- protected: that is, its name can be used only by members and friends of the class in which it is declared, by classes derived from that class, and by their friends (see 11.4).
- public: that is, its name can be used anywhere without access restriction.
From the perspective of the standard’s intent, it aims to hide the implementation details and underlying data of the class, which is encapsulation. However, we can also break through the access control restrictions through some special means.
First, let’s correct a misconception: the access control of class in C++ restricts the access permission of members, not their visibility.
[ISO/IEC 14882:2014] It should be noted that it is access to members and base classes that is controlled, not their visibility.
Any member in a class is visible to any code that can see the implementation of that class; the issue is whether it can be accessed. That is, when you directly access the name (identifier) of a member in a class, it will check whether you have the right to access that member. If you mark it as private
and attempt to access it from outside the class definition or outside a friend, a compilation error will be raised. For a detailed description of the visibility and accessibility of access control, please refer to my other article: Visibility and Accessibility of Access Control Mechanisms.
So we can bypass the access control mechanism by not using the member’s name. A way to access class members without using the class member names is—pointers to the class members. For specific content on pointers to class members in C++, you can refer to my previous article: Pointers to Class Members in C++ are Not Pointers.
From the perspective of intrusive implementation, we can actively provide access to class private
members through class member functions.
A common method is to return a reference to the private
data member or return pointers to class members. I prefer returning a pointer to the member, because if you return a reference, the accessible member later can only be that of a specific object, while a function pointer can access any member of that class object.
1 | class A{ |
Since what we obtain here is the object address (data member/function object), it can be accessed externally through a class object or a pointer to a class object. This is because the access to class member pointers is essentially calculated through this
to determine the offset of that member, thus not triggering any name lookup or accessibility checks:
1 | A example(456); |
As can be seen, the private
data member of class A has been modified externally, and we can also call its private
member function from outside. They can also be combined with the STL operator library to perform certain operations on a batch of class objects.
However, this is based on the premise of the class implementer proactively providing access rights.
There are also cases where, for some reasons, we may not want to manually modify the class code (add or delete class interfaces), yet still require access to private
members. In such cases, there are two approaches we can use.
Assuming we have the following class:
1 | class A{ |
Since it has a template member function, we can use this template member function to break through the class’s access permissions. The method is to add a specialization of this member function from outside because it is a specialization of the class member and it has access to all members of the class:
1 | namespace { |
Here, when we use ZZZ
as a parameter to call the A::func
member function, we can access the private
member, thus breaking the access limitations of the C++ class. We can either directly manipulate the private
member or pass out the member pointer (to the incoming object), and we can access class A’s private
member externally.
1 | A example(456); |
If a class has friend templates, the same behavior can also be achieved, and this method is fully compliant with the C++ standard.
Next, let’s discuss a method for breaking access permissions that depends on the compiler implementation:
Using the above class A as the target for the breakthrough.
We can create a class that mimics the same layout as the target class but changes the access specifiers for corresponding private
members to public
or protected
and other more lenient access privileges (this is actually implementation-dependent).
1 | // The only difference is that private_ is public in class B |
Then it can be used to manipulate:
1 | A example(456); |
The core of this is reinterpret_cast<B&>(example)
, which forces the compiler to interpret an object of class A as an object of class B, allowing access to the public member private_
in class B, but actually modifying the private
member private_
of class A.
However, this also relies on the compiler’s implementation because we assume that the layout of the modified class B and class A is exactly the same, which is not guaranteed (works in G++).
[TC++PL4th] A compiler may reorder sections of a class with separate access specifiers.
We can also place the objects of class A
and class B
in a union
, storing them as A’s type and reading them as B’s type:
1 | union U{ |
Another clever trick to access class private members is shown directly in the code below (the latest code will be placed on gist: hack-private-data-member.cpp):
1 |
|
The implementation above utilizes templates to obtain pointers to member functions, and it unfolds as follows:
1 | class A{ |
The reason for using templates is to bypass access permission checks at compile time. Since what we want to obtain is the address of A::func
, the access permission check for &A::func
occurs at runtime and cannot be compiled. Therefore, we need templates to perform early execution. Thus, we create a class definition, using a typedef
(MemType) for the member type we wish to acquire and turning the non-accessible member type A::func
into the accessible member type A_func::MemType
. The key is passing the template’s actual parameter (&A::func
) to it (T::MemType M
) to break through the access control of the class.
Moreover, polymorphism is also supported:
1 | class A{ |
Conclusion: We can see from the first method of breaking class access control that member function templates influence access control mechanisms, allowing access control to be bypassed through member templates. Most importantly, this approach is portable and can be code-upgradeable (as long as the member names do not change). However, any behavior that breaks member access restrictions is not recommended.