Non-invasive private class members extraction
Are private
class members really private? It turns out to be not quite so. There exists a mechanism that allows us to access any private member data or function, with the only requirement of knowing its type.
There is an impressive article on this topic, but I’ve found it to be a bit dense and hard to read, so I decided to add some clarifications.
Idea
The key ingredient is [temp.explicit]/12
, Working Draft from 2017-03-21:
The usual access checking rules do not apply to names used to specify explicit instantiations. [Note: In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects which would normally not be accessible and the template may be a member template or member function which would not normally be accessible. — end note]
So we explicitly instantiate a struct template<class Ptr, Ptr TargetPointer, size_t InstantiationId> struct steal
which contains a static field essence
whose constructor performs an assignment of the target pointer-to-member TargetPointer
to the global variable template<class Ptr, size_t InstantiationId> Ptr result
.
The role of the InstantiationId
param is to distinguish between different values of the template variable result
in case of instantiating it with the same class Ptr
parameter.
Since the object essence
has static storage duration ([basic.stc.static]/4), it gets initialized at the program initiation ([basic.start.static]/1); therefore, result<Ptr, InstantiationId>
gets assigned at the program initiation, before the execution of main
begins.
The actual access to the target member is then done via usual pointer-to-member dereferencing.
Code
Here is the idea described above, implemented in code.
namespace essence {
template<class Ptr, size_t Id>
Ptr result;
template<class Ptr, Ptr TargetPointer, size_t InstantiationId>
struct steal {
struct exec_on_instantiation {
exec_on_instantiation() {
result<Ptr, InstantiationId> = TargetPointer;
}
};
static exec_on_instantiation essence;
};
template <typename Ptr, Ptr TargetPointer, size_t InstantiationId>
typename steal<
Ptr,
TargetPointer,
InstantiationId
>::exec_on_instantiation steal<
Ptr,
TargetPointer,
InstantiationId
>::essence;
} // namespace essence
Demo
Here is a demo. See it in action here: https://ideone.com/Gnjvn2
#include <iostream>
using std::string;
using std::cout;
using std::endl;
class Victim {
void f() { cout << "PoC - member function f" << endl; }
void h() { cout << "PoC - member function h" << endl; }
string str = "PoC - member data";
static void g() { cout << "PoC - static member function" << endl; }
static string static_str;
};
string Victim::static_str = "PoC - static member data";
template struct essence::steal<decltype(&Victim::f), &Victim::f, 1>;
template struct essence::steal<decltype(&Victim::h), &Victim::h, 2>;
template struct essence::steal<decltype(&Victim::str), &Victim::str, 3>;
template struct essence::steal<decltype(&Victim::g), &Victim::g, 4>;
template struct essence::steal<
decltype(&Victim::static_str),
&Victim::static_str,
5
>;
int main() {
auto ptr1 = essence::result<void (Victim::*)(), 1>;
auto ptr2 = essence::result<void (Victim::*)(), 2>;
auto ptr3 = essence::result<string (Victim::*), 3>;
auto ptr4 = essence::result<void(*)(), 4>;
auto ptr5 = essence::result<string*, 5>;
Victim a;
(a.*ptr1)();
(a.*ptr2)();
a.*ptr3 += " (modified)", cout << a.*ptr3 << endl;
ptr4();
*ptr5 += " (modified)", cout << *ptr5 << endl;
}