Hi. Figured I would throw this together quickly; thought it was neat.
I, of course, am a programmer, and I deal with obfuscation on both the good team and the bad team. Obfuscation is important because:
It makes it hard to see what values are held in certain places at certain times
It makes it almost impossible to decrypt the values either statically or in a memory dump
It’s usually pretty simple to implement and oftentimes doesn’t reduce program performance
You may or may not have read RTMA Part 12, which was a deep dive into a self-defending program, which was pretty cool.
It deployed obfuscated values, but I didn’t really discuss them beyond the obfuscated strings (spoiler alert, I used Ay-Obfuscate for those). The obfuscated variables were only used for a few things, but I wrote a pretty cool class that I figured I would shape up slightly and share for anyone to use (Tor site is sadly now down forever and ever).
It’s simply a wrapper over a type that implicitly handles uses a simple XOR cipher to encrypt/decrypt values on access. I used a lot of metaprogramming to make it decently overkill for just about any use case which I definitely have not tested thoroughly enough.
A fun thing it can do is that you can use it with function pointers and obfuscated-ly call them implicitly. Very cool!
Go ahead and give it a whirl if you’re feeling especially obfuscatey; it even works in kernel-mode! I think.
Only works in C++20 and beyond.
#include <inttypes.h>
using key_t = uint64_t;
template <typename T>
static constexpr T cipher(const T &val, const key_t key)
{
T ret = val;
for (size_t i = 0; i < sizeof(T); i++)
{
// Split the value into 8 bit segments and then xor each
((uint8_t *)&ret)[i] ^= uint8_t(key >> ((i % 8) * 8));
}
return ret;
}
template <typename T, key_t KEY = 0xBADF000DCAFEBEEFull>
class ObfuscatedValue
{
public:
using type = T;
constexpr ObfuscatedValue() = default;
template <typename U, typename std::enable_if<std::is_convertible<U, T>::value, int>::type = 0>
constexpr ObfuscatedValue(const U &val)
: m_value{obfsc(static_cast<T>(val))}
{
}
template <typename U, key_t K>
ObfuscatedValue(const ObfuscatedValue<U, K> &other)
: m_value{obfsc(other.get())}
{
}
template <typename U, key_t K>
ObfuscatedValue(ObfuscatedValue<U, K> &&other)
: m_value{obfsc(other.get())}
{
}
constexpr ~ObfuscatedValue() = default;
private:
static T obfsc(const T &val)
{
return cipher(val, KEY);
}
static T obfsc(const T &val, key_t key)
{
return cipher(val, key);
}
public:
T get() const
{
// Ok this is a bad hack for default-constructed ObfuscatedValues to return 0
// You have a 2^64 chance of getting a false positive here (obfsc returns 0)
// Has to be this way because I need default constructors so unused lib funcs get optimized out
// Or, I could add a bool member to the class, but then that would waste 7 bytes :scream:
if (m_value == (T)0)
return (T)0;
return cipher(m_value, KEY);
}
T get(const key_t key) const
{
if (m_value == (T)0)
return (T)0;
return cipher(m_value, key);
}
#ifdef CONCAT_3
#undef CONCAT_3
#endif
#define CONCAT_3(one, two, three) one##two##three
#define OPERATOR_COMPOUND(op) \
template <typename U> \
ObfuscatedValue<T, KEY> &CONCAT_3(operator, op, =)(const U &val) \
{ \
m_value = obfsc(get() op val); \
return *this; \
}
#define OPERATOR_COMPARE(op) \
template <typename U> \
std::strong_ordering operator##op(const U &val) const \
{ \
return get() op val; \
}
#define OPERATOR_BITWISE(op) \
template <typename U, key_t K> \
friend ObfuscatedValue<T, KEY> operator##op(const ObfuscatedValue<T, KEY> &a, const ObfuscatedValue<U, K> &b) \
{ \
return ObfuscatedValue<T, KEY>(a.get() op b.get()); \
} \
template <typename U> \
friend ObfuscatedValue<T, KEY> operator##op(const ObfuscatedValue<T, KEY> &a, const U & b) \
{ \
return ObfuscatedValue<T, KEY>(a.get() op b); \
} \
template <typename U> \
friend ObfuscatedValue<T, KEY> operator##op(const U & a, const ObfuscatedValue<T, KEY> &b) \
{ \
return ObfuscatedValue<T, KEY>(a op b.get()); \
}
operator T() const
{
return get();
}
template <typename U>
typename std::enable_if<std::is_convertible<U, T>::value, ObfuscatedValue<T, KEY> &>::type
operator=(const U &value)
{
m_value = obfsc(static_cast<T>(value));
return *this;
}
// Omit '=' as macro does it for us
OPERATOR_COMPOUND(+)
OPERATOR_COMPOUND(-)
OPERATOR_COMPOUND(*)
OPERATOR_COMPOUND(/)
OPERATOR_COMPOUND(%)
OPERATOR_COMPOUND(&)
OPERATOR_COMPOUND(|)
OPERATOR_COMPOUND(^)
OPERATOR_COMPOUND(>>)
OPERATOR_COMPOUND(<<)
OPERATOR_COMPARE(<=>)
OPERATOR_COMPARE(&&)
OPERATOR_COMPARE(||)
OPERATOR_BITWISE(&)
OPERATOR_BITWISE(|)
OPERATOR_BITWISE(^)
OPERATOR_BITWISE(>>)
OPERATOR_BITWISE(<<)
ObfuscatedValue<T, KEY> &operator++()
{
m_value = obfsc(get() + 1);
return *this;
}
ObfuscatedValue<T, KEY> operator++(int)
{
ObfuscatedValue<T, KEY> tmp(*this);
m_value = obfsc(get() + 1);
return tmp;
}
ObfuscatedValue<T, KEY> &operator--()
{
m_value = obfsc(get() - 1);
return *this;
}
ObfuscatedValue<T, KEY> operator--(int)
{
ObfuscatedValue<T, KEY> tmp(*this);
m_value = obfsc(get() - 1);
return tmp;
}
T operator~() const { return ~get(); }
bool operator!() const { return !get(); }
// Yea I asked ChatGPT for these lololol
private:
template <typename U>
struct has_brackets
{
template <typename V>
static std::true_type test(decltype(std::declval<V>()[0]) *);
template <typename V>
static std::false_type test(...);
static constexpr bool value = decltype(test<U>(nullptr))::value;
};
template <typename U>
struct has_arrow
{
template <typename V>
static std::true_type test(decltype(std::declval<V>().operator->()) *);
template <typename V>
static std::false_type test(...);
static constexpr bool value = decltype(test<U>(nullptr))::value;
};
template <typename U>
struct has_star
{
template <typename V>
static auto test(const V *) -> decltype(std::declval<const V>().operator*(), std::true_type());
template <typename V>
static std::false_type test(...);
static constexpr bool value = decltype(test<U>(nullptr))::value;
};
public:
template <typename U = T>
typename std::enable_if<std::is_pointer<U>::value, typename std::remove_pointer<U>::type &>::type
operator[](std::ptrdiff_t index) const
{
return get()[index];
}
template <typename U = T>
typename std::enable_if<!std::is_pointer<U>::value && has_brackets<U>::value,
decltype(std::declval<U>()[0]) &>::type
operator[](std::ptrdiff_t index) const
{
return get()[index];
}
template <typename U = T>
typename std::enable_if<std::is_pointer<U>::value, U>::type
operator->() const
{
return get();
}
template <typename U = T>
typename std::enable_if<!std::is_pointer<U>::value && has_arrow<U>::value,
decltype(std::declval<U>().operator->())>::type
operator->() const
{
return get().operator->();
}
template <typename U = T>
typename std::enable_if<std::is_pointer<U>::value, typename std::remove_pointer<U>::type &>::type
operator*() const
{
return *get();
}
template <typename U = T>
typename std::enable_if<!std::is_pointer<U>::value && has_star<U>::value,
decltype(std::declval<U>().operator*())>::type
operator*() const
{
return get().operator*();
}
protected:
T m_value;
};
// Fucking operators
#define OPERATOR_MATH(op) \
template <typename T, key_t KEY> \
ObfuscatedValue<T, KEY> operator##op(const ObfuscatedValue<T, KEY> &lhs, const ObfuscatedValue<T, KEY> &rhs) \
{ \
return ObfuscatedValue<T, KEY>(lhs.get() op rhs.get()); \
} \
template <typename T, key_t KEY> \
ObfuscatedValue<T, KEY> operator##op(const ObfuscatedValue<T, KEY> &lhs, const T & rhs) \
{ \
return ObfuscatedValue<T, KEY>(lhs.get() op rhs); \
} \
template <typename T, key_t KEY> \
ObfuscatedValue<T, KEY> operator##op(const T & lhs, const ObfuscatedValue<T, KEY> &rhs) \
{ \
return ObfuscatedValue<T, KEY>(lhs op rhs.get()); \
}
OPERATOR_MATH(+)
OPERATOR_MATH(-)
OPERATOR_MATH(*)
OPERATOR_MATH(/)
OPERATOR_MATH(%)