HyperDbg/hyperdbg/libhyperdbg/code/debugger/misc/assembler.cpp
2024-07-28 18:10:54 +09:00

264 lines
6.8 KiB
C++

/**
* @file assembler.cpp
* @author Abbas Masoumi Gorji (AbbasMG@hyperdbg.org)
* @brief Turns assembly codes into bytes
* @details
* @version 0.10
* @date 2024-07-16
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
/**
* @brief tries to solve the symbol issue with Keystone, which apparently originates from LLVM-MC.
*
* @return VOID
*/
VOID
AssembleData::ParseAssemblyData()
{
std::string RawAsm = AsmRaw;
//
// remove all "\n" instances
//
RawAsm.erase(std::remove(RawAsm.begin(), RawAsm.end(), '\n'), RawAsm.end());
//
// remove multiple spaces
//
std::regex MultipleSpaces(" +");
RawAsm = std::regex_replace(RawAsm, MultipleSpaces, " ");
//
// split assembly line by ';'
//
std::vector<std::string> AssemblyInstructions;
size_t Pos = 0;
std::string Delimiter = ";";
while ((Pos = RawAsm.find(Delimiter)) != std::string::npos)
{
std::string Token = RawAsm.substr(0, Pos);
if (!Token.empty())
{
AssemblyInstructions.push_back(Token);
}
RawAsm.erase(0, Pos + Delimiter.length());
}
if (!RawAsm.empty())
{
AssemblyInstructions.push_back(RawAsm);
}
//
// process each assembly instruction
//
for (auto & InstructionLine : AssemblyInstructions)
{
std::string Expr {};
UINT64 ExprAddr {};
size_t Start {};
while ((Start = InstructionLine.find('<', Start)) != std::string::npos)
{
size_t End = InstructionLine.find('>', Start);
if (End != std::string::npos)
{
std::string Expr = InstructionLine.substr(Start + 1, End - Start - 1);
if (!SymbolConvertNameOrExprToAddress(Expr, &ExprAddr) && ExprAddr == 0)
{
ShowMessages("err, failed to resolve the symbol [ %s ].\n", Expr.c_str());
Start += Expr.size() + 2;
continue;
}
std::ostringstream Oss;
Oss << std::hex << std::showbase << ExprAddr;
InstructionLine.replace(Start, End - Start + 1, Oss.str());
}
else
{
//
// No matching '>' found, break the loop
//
break;
}
Start += Expr.size() + 2;
}
}
//
// Append ";" between two std::strings
//
auto ApndSemCln = [](std::string a, std::string b) {
return std::move(a) + ';' + std::move(b);
};
//
// Concatenate each assembly line
//
AsmFixed = std::accumulate(std::next(AssemblyInstructions.begin()), AssemblyInstructions.end(), AssemblyInstructions.at(0), ApndSemCln);
if (!AsmFixed.empty() && AsmFixed.back() == ';')
{
//
// remove the last ";" for it will be counted as a statement by Keystone and a wrong number would be printed
//
AsmFixed.pop_back();
}
while (!AsmFixed.empty() && AsmFixed.back() == ';')
{
AsmFixed.pop_back();
}
}
INT
AssembleData::Assemble(UINT64 StartAddr, ks_arch Arch, INT Mode, INT Syntax)
{
ks_engine * Ks;
KsErr = ks_open(Arch, Mode, &Ks);
if (KsErr != KS_ERR_OK)
{
ShowMessages("err, failed on ks_open()");
return -1;
}
if (Syntax)
{
KsErr = ks_option(Ks, KS_OPT_SYNTAX, Syntax);
if (KsErr != KS_ERR_OK)
{
ShowMessages("err, failed on ks_option() with error code = %u\n", KsErr);
}
}
//
// as long as symbols are parsed before passing asm code, SymResolver is not needed, for now.
//
KsErr = ks_option(Ks, KS_OPT_SYM_RESOLVER, 0); // null SymResolver
if (KsErr != KS_ERR_OK)
{
ShowMessages("err, failed on ks_option() with error code = %u\n", KsErr);
}
if (ks_asm(Ks, AsmFixed.c_str(), StartAddr, &EncodedBytes, &BytesCount, &StatementCount))
{
KsErr = ks_errno(Ks);
ShowMessages("err, failed on ks_asm() with count = %lu, error code = %u\n", (int)StatementCount, KsErr);
}
else
{
if (BytesCount == 0)
{
ShowMessages("err, the assemble operation returned no bytes, most likely due to incorrect formatting of assembly snippet\n");
}
else
{
ShowMessages("generated assembly: %lu (decimal) bytes, %lu (decimal) statements ==>> ", (int)BytesCount, (int)StatementCount);
size_t i;
ShowMessages("%s = ", AsmFixed.c_str());
for (i = 0; i < BytesCount; i++)
{
ShowMessages("%02x ", EncodedBytes[i]);
EncBytesIntVec.push_back(static_cast<UINT64>(EncodedBytes[i]));
}
ShowMessages("\n");
ks_close(Ks);
return 0;
}
}
ks_close(Ks);
return -1;
}
AssembleData *
create_AssembleData()
{
return new AssembleData;
}
/**
* @brief Assembler function
*
* @param AssemblyCode The assembly code
* @param StartAddress The start address of the assembly code
* @param Length The length of the assembly code in bytes (to be returned)
*
* @return BOOLEAN Returns true if it was successful
*/
BOOLEAN
HyperDbgAssembleGetLength(const CHAR * AssemblyCode, UINT64 StartAddress, UINT32 * Length)
{
AssembleData AssembleData;
//
// Convert assembly code to string
//
std::string AsmCode = AssemblyCode;
AssembleData.AsmRaw = AsmCode;
AssembleData.ParseAssemblyData();
if (AssembleData.Assemble(StartAddress))
{
return FALSE;
}
else
{
//
// Get the BytesCount
//
*Length = (UINT32)AssembleData.BytesCount;
return TRUE;
}
}
/**
* @brief Assembler function
*
* @param AssemblyCode The assembly code
* @param StartAddress The start address of the assembly code
* @param BufferToStoreAssembledData The buffer to store the assembled data
* @param BufferSize The size of the buffer
*
* @return BOOLEAN Returns true if it was successful
*/
BOOLEAN
HyperDbgAssemble(const CHAR * AssemblyCode, UINT64 StartAddress, PVOID BufferToStoreAssembledData, UINT32 BufferSize)
{
AssembleData AssembleData;
//
// Convert assembly code to string
//
std::string AsmCode = AssemblyCode;
AssembleData.AsmRaw = AsmCode;
AssembleData.ParseAssemblyData();
if (AssembleData.Assemble(StartAddress))
{
return FALSE;
}
else
{
if (AssembleData.BytesCount > BufferSize)
{
return FALSE;
}
//
// Copy the assembled data to the buffer
//
memcpy(BufferToStoreAssembledData, AssembleData.EncodedBytes, AssembleData.BytesCount);
return TRUE;
}
}