obfuscated any constant encryption in compile time on any platform

Overview

oxorany

带有混淆的编译时任意常量加密

LICENSE

介绍

我们综合了开源项目ollvmxorstr一些实现思路,以及c++14标准中新加入的constexpr关键字和一些模板的知识,完成了编译时的任意常量的混淆(可选)和加密功能。

在C++14之前,我们如果要对程序中的常量进行保护,我们首先对常量进行加密操作,这里以字符串"some_data_or_string"逐字节-1为例,然后将加密后的数据"rnld^c`s`^nq^rsqhmf",写到代码里,同时进行逐字节+1解密。

代码如下

char encrypted[] = {"rnld^c`s`^nq^rsqhmf"};
char key = 0x1;
for (size_t i = 0; i < strlen(encrypted); i++) {
	encrypted[i] += key;
}
//output: some_data_or_string
printf("%s\n", encrypted);

上述的方法只能在需要被保护的数据的数量比较少时使用,当数据量增大,繁琐的加密过程所占用的时间也会水涨船高,而且使得代码的可读性、可维护性大大降低。而且不可能为每一个数据都单独设计一个解密算法和key,使得通用的解密工具更易于编写。

随着oxorany的出现,上述过程将被改变

🎨 特性

  • 支持任意平台(C++14),已在所有诸多编译器中进行了测试
  • 较高的可操作性,使用__asm _emit可进一步提高逆向难度
  • 所有的加密过程均在编译时完成
  • 所有的解密过程均在栈内完成,无法通过运行时dump获得解密后的数据,不同于 Armaririsflounder
  • 带有伪造控制流功能的解密算法
  • 通过编译优化为每一个加密算法生成唯一的控制流
  • 通过__COUNTER__ 宏为每一个加密算法生成唯一的key
  • 通过__TIME__宏动态产生key
  • 代码经过精心编写,足以破坏堆栈以对抗IDA F5
  • 基于堆栈变量的不透明谓词
  • 模糊数据长度
  • 由于解密算法的大部分代码不会被执行,所以对于效率的影响并不会特别大
  • 解密算法的复杂度仍有提升空间
  • 因为C++中常量的隐式转换特性,某些常量可能需要强制类型转换
  • 相当简单的使用方法

不能保证数据会被内联到代码段想要内联

支持的数据类型

  • 字符串(char* wchar_t*)
  • 枚举
  • 字符(char wchar_t)
  • 指针(NULL nullptr)
  • 整数(int8_t int16_t int32_t int64_t uint8_t uint16_t uint32_t uint64_t)
  • 浮点(float double)(会保留原数据)

支持的编译器

  • msvc
  • clang(llvm)(支持叠加ollvm)
  • gcc
  • android ndk(支持安卓)
  • leetcode gcc(支持类似的云编译器)
  • wdk(支持Windows驱动程序)
  • ...

🚀 使用

#include <iostream>
//#define OXORANY_DISABLE_OBFUSCATION
#include "oxorany.h"

enum class MyEnum : int {
    first = 1,
    second = 2,
};

#define NUM_1 1

int main() {
    // output:
    // 1 1 2 c w 00000000 00000000 12 1234 12345678 1234567887654321 1.000000 2.000000
    // string u8 string wstring raw string raw wstring abcd
    printf(oxorany("%d %d %d %c %C %p %p %hhx %hx %x %llx %f %lf\n%s %s %S %s %S %s\n")  //string
           , oxorany(NUM_1)                                                           //macro
           , oxorany(MyEnum::first), oxorany(MyEnum::second)                          //enum
           , oxorany('c')                                                             //char
           , oxorany(L'w')                                                            //wchar_t
           , oxorany(NULL), oxorany(nullptr)                                          //pointer
           , oxorany(0x12)                                                            //int8_t
           , oxorany(0x1234)                                                          //int16_t
           , oxorany(0x12345678)                                                      //int32_t
           , oxorany(0x1234567887654321)                                              //int64_t
           , oxorany(1.0f)                                                            //float
           , oxorany(2.0)                                                             //double

           , oxorany("string")                                                        //string
           , oxorany(u8"u8 string")                                                   //u8 string
           , oxorany(L"wstring")                                                      //wstring
           , oxorany(R"(raw string)")                                                 //raw string
           , oxorany(LR"(raw wstring)")                                               //raw wstring
           , oxorany("\x61\x62\x63\x64")                                              //binary data
    );
    return oxorany(0);
}

⚙️ 需要强制类型转换的示例

0 error 0 warning

MessageBoxA(0, 0, 0, 0);

错误(活动) E0167 "int" 类型的实参与 "HWND" 类型的形参不兼容

MessageBoxA(oxorany(0), 0, 0, 0);

出现上述问题的原因是因为C/C++0的特殊性,因为它可以隐式转换到任意类型的指针,也和NULL的定义有关

#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif

所以我们添加一个到HWND的强制类型转换就可以解决该问题

MessageBoxA(oxorany((HWND)0), 0, 0, 0);

⚙️ wdk中使用时需启用__TIME__

image


IDA中的控制流程图

image


编译优化测试

这里是测试编译优化对控制流程图的影响,期望的结果是每一次编译都拥有不同的控制流程图

#include "oxorany.h"
int main() {
	return oxorany(0);
}

使用msvc多次编译后IDA中的控制流程图

image


使用clang多次编译后IDA中的控制流程图

image


使用gcc多次编译后IDA中的控制流程图

image


使用android ndk编译后IDA中的控制流程图

image


使用leetcode gcc进行测试 (剑指 Offer 05. 替换空格)

S5(LFNXH~_KM6UH@L}U(CY6


使用wdk多次编译后IDA中的控制流程图

image


使用ollvm编译后IDA中的控制流程图

image


不透明谓词

不透明:opaque
来自拉丁语opacus,有阴影的,黑暗的,模糊的。

谓词:predicate
来自拉丁语praedicare,预测,断言,声称,来自prae,在前,早于,dicare,说,声称,词源同diction.并引申诸相关词义。

不透明谓词可以理解为“无法确定结果的判断”,词语本身并没有包含结果必为真或者必为假的含义,只是在这里使用了结果必为真的条件进行混淆。

代码中的rand() % 2 == 0实际上也是一个不透明谓词,因为我们无法确定它的结果,所以就无法确实程序是输出hello还是输出world

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
	srand((unsigned int)time(NULL));
	if (rand() % 2 == 0) {
		printf("hello\n");
	}
	else {
		printf("world\n");
	}
	return 0;
}

但是换一种情况,这里我们创建了一个全局变量zeor,并赋初值为0,不去修改zeor的值或者在保证谓词结果恒定的情况下进行合理的修改,那么谓词zeor < 1就是恒成立的,同时又由于全局变量的天然的不透明性,编译器不会进行优化,所以我们就增加一个伪造的控制流,无中生有。我们可以在不可达的基本块内加入任意代码,这里我们添加了一个典中典99乘法表作为示例,暗度陈仓

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int zeor = 0;
int main() {
	if (zeor < 1) {
		printf("hello\n");
	}
	else {
		//unreachable
		for (int i = 1; i <= 9; i++) {
			for (int j = 1; j <= 9; j++) {
				printf("%d*%d=%2d\t", i, j, i * j);
			}
		    	printf("\n");
		}
	}
	return 0;
}

这里copy一下ollvm中的代码,ASCII Picasso

// Before :
// 	         	     entry
//      		       |
//  	    	  	 ______v______
//   	    		|   Original  |
//   	    		|_____________|
//             		       |
// 		       	       v
//		             return
//
// After :
//           		     entry
//             		       |
//            		   ____v_____
//      		  |condition*| (false)
//           		  |__________|----+
//           		 (true)|          |
//             		       |          |
//           		 ______v______    |
// 	            +-->|   Original* |   |
// 	            |   |_____________| (true)
// 	            |   (false)|    !-----------> return
// 	            |    ______v______    |
// 	            |   |   Altered   |<--!
// 	            |   |_____________|
// 	            |__________|
//
//  * The results of these terminator's branch's conditions are always true, but these predicates are
//    opacificated. For this, we declare two global values: x and y, and replace the FCMP_TRUE
//    predicate with (y < 10 || x * (x + 1) % 2 == 0) (this could be improved, as the global
//    values give a hint on where are the opaque predicates)

ollvm中全局xy的定义

      GlobalVariable 	* x = new GlobalVariable(M, Type::getInt32Ty(M.getContext()), false,
          GlobalValue::CommonLinkage, (Constant * )x1,
          *varX);
      GlobalVariable 	* y = new GlobalVariable(M, Type::getInt32Ty(M.getContext()), false,
          GlobalValue::CommonLinkage, (Constant * )y1,
          *varY);

ollvm中不透明谓词y < 10 || x * (x + 1) % 2 == 0的实现,由Instruction::Sub可知,虽然注释是x + 1,但实际使用的确实x - 1问题不大,殊途同归

        //if y < 10 || x*(x+1) % 2 == 0
        opX = new LoadInst ((Value *)x, "", (*i));
        opY = new LoadInst ((Value *)y, "", (*i));

        op = BinaryOperator::Create(Instruction::Sub, (Value *)opX,
            ConstantInt::get(Type::getInt32Ty(M.getContext()), 1,
              false), "", (*i));
        op1 = BinaryOperator::Create(Instruction::Mul, (Value *)opX, op, "", (*i));
        op = BinaryOperator::Create(Instruction::URem, op1,
            ConstantInt::get(Type::getInt32Ty(M.getContext()), 2,
              false), "", (*i));
        condition = new ICmpInst((*i), ICmpInst::ICMP_EQ, op,
            ConstantInt::get(Type::getInt32Ty(M.getContext()), 0,
              false));
        condition2 = new ICmpInst((*i), ICmpInst::ICMP_SLT, opY,
            ConstantInt::get(Type::getInt32Ty(M.getContext()), 10,
              false));
        op1 = BinaryOperator::Create(Instruction::Or, (Value *)condition,
            (Value *)condition2, "", (*i));

将我们上面的代码稍作调整,以展示ollvm的实现,这里的x * (x + 1) % 2 == 0,以为xx + 1,必然是一个奇数一个偶数,根据奇偶性的运算法则可以得知x * (x + 1)的结果必然是偶数,所以% 2 == 0的判断将必然成立

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int x = 0;
int y = 0;
int main() {
	if (y < 10 || x * (x + 1) % 2 == 0) {
		printf("hello\n");
	}
	else {
		//unreachable
        	for (int i = 1; i <= 9; i++) {
			for (int j = 1; j <= 9; j++) {
                		printf("%d*%d=%2d\t", i, j, i * j);
			}
            		printf("\n");
        	}
	}
	return 0;
}

实现

受到ollvm伪造控制流功能的启发,我们创建了两个全局变量xy,并赋初值为0,作为实现不透明谓词的基础

image


由于栈环境的复杂性,我们将全局变量xy分别赋值给两个局部变量stack_xstack_y,以提高逆向的难度

image


我们在函数的很多位置创建了label,使用stack_xstack_y进行恒为真的判断进行混淆,在无法到达的基本快内添加goto label以尽可能得对基本块进行拆分。我们在多处对解密后的数据decrypted使用错误的key进行解密,使得真实的key在众多的错误的key中难以被识别,乱花渐欲迷人眼,浅草才能没马蹄

image


生成带有范围限制的随机数,因为这里可以出现相同的值,同时又因为编译优化的存在,重复的条件会被消除,这使得我们每一次的编译,都拥有不尽相同的控制流程图

image


我们在无法到达的基本快内加入非法的栈操作再加上代码中经过精心分配的控制流使得IDA的栈帧分析失败,以对抗F5

image


我们在将数据按16字节对齐并加上一定的随机值以模糊数据长度,这可能会浪费一点空间

image


我们在将xor替换为一种更加复杂的实现方式,以提高逆向的难度

image


使用__TIME__宏实现每一次编译都拥有唯一的key

image


带有范围限制的随机数产生器,使得不透明谓词相似于正常的谓词

image


综上所述,在oxorany的帮助下,软件的安全性将会得到进一步的提高

参考

Github

https://github.com/llxiaoyuan/oxorany

Issues
  • is there a const of doing this? like performance wise?

    is there a const of doing this? like performance wise?

    image

    in theory .. the right path is gonna be executed so no performance impact? right? and these random branches will not be executed at all.. and all of them are just to stop f5 from IDA?.

    this branches is for 1 int only . .that i print using printf ... does that mean.. if i have 10 ints.. i also have that kind of branches 10x?

    opened by gurachan 3
Owner
Chase
Don’t Forget To Be Awesome.
Chase
FractalCrypt - Free cryptoarchiver permitting any number of hidden volumes for deniable encryption

FractalCrypt - Free cryptoarchiver permitting any number of hidden volumes for deniable encryption

Ivan Serov 344 Jun 14, 2022
StrCrypt Compile-time string crypter library for C++

StrCrypt Compile-time string crypter library for C++ Having plain strings stored in the binary file or in memory can help reversering attempts to be m

null 58 Jun 26, 2022
Rubicon - a New Custom Encryption Algorithm/Tool

Rubicon - a New Custom Encryption Algorithm/Tool Disclaimer DO NOT use this project for purposes other than legitimate red teaming/pentesting jobs

null 25 Mar 12, 2022
Libraries and tools to perform fully homomorphic encryption operations on an encrypted data set.

Fully Homomorphic Encryption (FHE) This repository contains open-source libraries and tools to perform fully homomorphic encryption (FHE) operations o

Google 2.7k Jul 2, 2022
In cryptography, a cipher is an algorithm for performing encryption or decryption.

Cipher 1.0 In cryptography, a cipher is an algorithm for performing encryption or decryption. What can be done using this program? This program can us

null 1 Apr 21, 2022
Homomorphic Encryption PIR Postgres C/C++ Agregate Extension.

MuchPIR Demo Contact The MuchPIR Team: [email protected] What is PIR? Private Information Retrieval refers to the ability to query a database without disc

Escanor Liones 12 Jul 1, 2022
Oceantoo is an XOR/LFSR based encryption algorithm

Oceantoo Oceantoo is an XOR/LFSR based encryption algorithm What is an LFSR? A linear-feedback shift register (LFSR) is a method of manipulating a num

Gary Explains 6 Apr 10, 2022
a new encryption algorithm that uses increments instead of XOR

incrypt a new encryption algorithm that uses increments instead of XOR. how to compile for linux: gcc incrypt.c -o incrypt gcc dicrypt.c -o dicrypt ho

man_s_our 1 Nov 24, 2021
inetd-style TLS encryption wrapper

Description The tlswrapper is an TLS encryption wrapper between remote client and local program prog. Is executed from systemd.socket/inetd/tcpserver/

Jan Mojžíš 2 Feb 23, 2022
Transparent file encryption in git

git-crypt - transparent file encryption in git git-crypt enables transparent encryption and decryption of files in a git repository. Files which you c

Andrew Ayer 6.5k Jun 22, 2022
Intel Homomorphic Encryption Acceleration Library for FPGAs

main: development: Intel Homomorphic Encryption Acceleration Library for FPGAs (Intel HEXL for FPGA) Intel ®️ HEXL for FPGA is an open-source library

Intel Corporation 46 Jun 2, 2022
Collection of common encryption.

EasyCipher 概述 EasyCipher收集了几种常见的加密方法的C语言实现,并基于Android平台封装了jni接口。 提供的加密方法包括 AES加密核心部分,不涉及模式和padding,支持128bits和256bits AES/CBC/PKCS5Padding SHA256 HAMC-

Billy Wei 6 Jun 9, 2022
An implementation of Advanced Encryption Standard with calculative optimization

An implementation of Advanced Encryption Standard with calculative optimization. Complete round operations in a single function.

Echo Heath 1 Jan 24, 2022
A giant list of any useful signatures for developing CSGO cheats.

csgo-signatures A giant list of any useful signatures for developing CSGO cheats. If one of them doesn't work or is incorrect, please let me know and

KittenPopo 67 Mar 16, 2022
BlackDex is an Android unpack(dexdump) tool, it supports Android 5.0~12 and need not rely to any environment.

BlackDex is an Android unpack(dexdump) tool, it supports Android 5.0~12 and need not rely to any environment. BlackDex can run on any Android mobile phones or emulators, you can unpack APK File in several seconds.

null 3.7k Jun 22, 2022
CS 244B project to use a public blockchain as a two-phase commit coordinator to securely commit an atomic transaction across any two systems of a database.

blockchain-2pc CS 244B project to use a public blockchain as a two-phase commit coordinator to securely commit an atomic transaction across any two sy

null 4 Jun 13, 2022
Tink is a multi-language, cross-platform, open source library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse.

Tink A multi-language, cross-platform library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse. Ubuntu

Google 12.4k Jun 23, 2022
Jazzer is a coverage-guided, in-process fuzzer for the JVM platform developed by Code Intelligence.

Jazzer is a coverage-guided, in-process fuzzer for the JVM platform developed by Code Intelligence. It is based on libFuzzer and brings many of its instrumentation-powered mutation features to the JVM.

Code Intelligence 595 Jul 2, 2022
XMRig is a high performance, open source, cross platform RandomX, KawPow, CryptoNight and AstroBWT unified CPU/GPU miner

XMRig is a high performance, open source, cross platform RandomX, KawPow, CryptoNight and AstroBWT unified CPU/GPU miner and RandomX benchmark. Official binaries are available for Windows, Linux, macOS and FreeBSD.

null 7k Jun 24, 2022