Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
binprotect — x64 PE bin2bin obfuscator which doesn't add a section to the binary | Kitploit
Tools/GitHubGitHub/noahware/binprotect
Static AnalysisDynamic Analysis (Sandboxing)Reverse EngineeringMalware AnalysisBinary Analysis
GitHubnoahware/binprotect

binprotect

x64 PE bin2bin obfuscator which doesn't add a section to the binary

View Repository
30436111 month agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Build instructions

See 4. Building for build instructions.

Contents

  1. Introduction
  2. Binary rewriter
    • 2.1. Relative address tracking
    • 2.2. Disassembly
      • 2.2.1. Basic block splitting
      • 2.2.2. Indirect control flow
        • 2.2.2.1. Jump tables
          • 2.2.2.1.1. Bounded jump tables
          • 2.2.2.1.2. Unbounded jump tables
          • 2.2.2.1.3. Different types of jump tables
        • 2.2.2.2. Edge cases
      • 2.2.3. Functions
      • 2.2.4. 'Noreturn' call handling
    • 2.3. Exceptions support
      • 2.3.1. Unwind support
        • 2.3.1.1. Implementation
      • 2.3.2. Exception info parsing
        • 2.3.2.1. SEH/C_SCOPE_TABLE
        • 2.3.2.2. FuncInfo3 and FuncInfo4
      • 2.3.3. RTTI and ThrowInfo parsing
        • 2.3.3.1. RTTI
        • 2.3.3.2. ThrowInfo
  3. Obfuscation
    • 3.1. Virtual machine
      • 3.1.1. Unwind support
    • 3.2. Opaque predicate blocks
    • 3.3. Control flow flattening
    • 3.4. Linear substitution
    • 3.5. Mixed boolean arithmetic
  4. Building
  5. Usage
  6. Acronyms
  7. Credits

1. Introduction

This obfuscator is bin2bin, which means it takes an already compiled executable binary and reproduces it with the obfuscation passes applied. This can be used to protect an application without having access to the original source code. Only x64 PE (portable executable) files are supported as of now, but there are plans to add support for other binary formats (e.g. ELF) in the future.

Currently, all of the well known bin2bin obfuscators insert a section to the end of the binary to place the obfuscated code or data inside. This is so that the original layout of the binary is still preserved by not having to change the contents of the pre-existing sections. This is much easier to manage as it keeps most of the RVAs (relative addresses) valid.

This project takes a unique approach to bin2bin, where any obfuscated code or data is inserted within the original sections of the binary. This requires tracking every single RVA in the application. The benefits of this approach are:

  • Less suspicious to malware analysis as there are no additional executable sections added to the binary. Note: this was tested purely for educational and research purposes.
  • Reduced size of the output executable binary. This is because the original unobfuscated code can be erased from the binary as the sections are able to be resized.
  • Harder for analysis to be done as a reverse engineer will not be able to separate obfuscated and unobfuscated code just by the sections they are in, each routine would have to be checked to see if it is obfuscated.

This document will describe both the rewriting of the executable binary as well as the obfuscation techniques implemented. The following obfuscation techniques have been implemented:

  • Virtual machine.
  • Opaque predicates.
  • Control flow flattening.
  • Linear substitution.
  • Mixed boolean arithmetic.

Furthermore, this project also has exceptions support (C++ exceptions and SEH) and is able to obfuscate functions that have exception handling.

To assist with disassembly and discovery of code in the binary, symbol files (both PDB and MAP) are accepted optionally. Providing symbol files is not required but assists with disassembly in complex binaries. Some features such as exceptions support and control flow flattening require a symbol file to be provided.

2. Binary rewriter

A binary rewriter takes an executable binary and changes the code or data inside it to produce an output binary with the changes applied.

2.1. Relative address tracking

As the obfuscated code is inserted directly into the original sections of the binary, the relative addresses in the program must be tracked so that all references to them can be adjusted. This is so that the references still point to the same location after the code and data has been inserted. Otherwise data or code would be accessed at the wrong location, thus changing the behaviour of the output binary as well as causing severe instability.

Whenever any reference to a relative address is found (e.g. instruction containing rip relative operands or PE data directories), it is added to a tracking list to be updated at the end of the rewriting. The RVA where the reference occurs is tracked (to know where to update the reference) as well as the RVA which is being referenced (to know what RVA to update the reference with).

Whenever the disassembler finds a rip relative instruction, it will add it to a list of references to be updated at the end of the obfuscation. This ensures that all of those instructions are still pointing to the location that they originally had. Other relative instruction(s) cases such as jump tables are also added as references to be updated.

All of the tracked RVAs need to be adjusted whenever any bytes are inserted or removed from the binary. For example, here is the byte insertion handler:

void binwrite::binary_t::insert(const rva_t rva, const std::span<const std::uint8_t> data, const bool inclusive)  
{  
	buffer_.insert_range(buffer_.begin() + rva.value(), data);

	update_rvas(rva, static_cast<rva_t::size_type>(data.size()), inclusive);  
}  

update_rvas is where every tracked RVA is updated to reflect the change that happened in the binary. Here is a diagram of this process:

Figure 1. Relative address tracking.

The data inserted (blue) shifts the current data (grey). The instruction's referenced RVA (orange) updates to point to the same memory, accounting for the inserted data (blue).

2.2. Disassembly

All of the potential code entries (exports, entry point, relocations pointing to code section, etc) are added to a disassembly queue. If a symbol file is present, all functions described by the symbol file are also added to the disassembly queue. Each entry in the queue is treated as an individual basic block.

A basic block is a group of instructions with no branches; this means it gets terminated at control flow instructions (e.g. jump, ret, int). Basic blocks do not terminate at calls as they are expected to return in most cases. Some functions do not return (e.g. _CxxThrowException) and will be referred to as 'noreturn' calls from now on.

When a basic block from the disassembly queue is being processed, each instruction is disassembled starting from the top until one of the following happens:

  • Another already analysed basic block is reached, causing an overlap. See “Basic block splitting”.
  • Terminating instruction is found (jump, return, int).
  • The instruction disassembly has failed.
  • Code padding has been found.

Below is a diagram of the disassembly and entry of the disassembly queue (code padding check omitted in diagram). This is repeated until the disassembly queue is empty.

Figure 2. Disassembly processing.

2.2.1 Basic block splitting

If two basic blocks overlap, then one of them must be split. This stops two blocks describing the same instructions. For example:

wcslen proc  
    or      rax, 0FFFFFFFFFFFFFFFFh  
loc_140001078:  
    inc     rax  
    cmp     word ptr [rcx+rax*2], 0  
    jnz     short loc_140001078  
    retn  
wcslen endp  

This is an implementation of wcslen, which gets the length of a wide string. When the first instruction 'or rax, FFFFFFFFFFFFFFFF' is disassembled as the start of a basic block, it will continue disassembling until the 'retn'.

Download Tool