None
This commit is contained in:
964
core/Core.hpp
Normal file
964
core/Core.hpp
Normal file
@@ -0,0 +1,964 @@
|
||||
//
|
||||
// Created by acite on 5/1/24.
|
||||
//
|
||||
|
||||
#ifndef _H_CORE
|
||||
#define _H_CORE 1
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include "MemoryMap.hpp"
|
||||
#include <capstone/capstone.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <libelfin/elf/elf++.hh>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <capstone/capstone.h>
|
||||
|
||||
#define WORD_SIZE 8
|
||||
|
||||
enum Msg{
|
||||
MSG_PAUSE = 0,
|
||||
MSG_STOP = 1,
|
||||
MSG_CONTINUE = 2,
|
||||
MSG_STARTUP = 3,
|
||||
MSG_REG = 4,
|
||||
MSG_QUIT = 5,
|
||||
MSG_LOG = 6,
|
||||
MSG_ASM = 7,
|
||||
MSG_TITLE = 8,
|
||||
MSG_STEPINTO = 9,
|
||||
MSG_MEMORY = 10,
|
||||
MSG_SELECT = 11,
|
||||
MSG_CURRENTSYMBOL = 12,
|
||||
MSG_RESTART = 13,
|
||||
MSG_BREAK = 14,
|
||||
MSG_STEPOVER = 15,
|
||||
MSG_SYMBOLS = 16,
|
||||
MSG_SWITCHSYMBOL= 17,
|
||||
MSG_NONSYM = 18,
|
||||
MSG_PARSE = 19,
|
||||
MSG_INSYM = 20,
|
||||
MSG_DELBREAK = 21,
|
||||
MSG_SWITCHMODULE= 22,
|
||||
MSG_GETBREAKS = 23,
|
||||
MSG_CALLSTACK = 24,
|
||||
MSG_BREAKONCE = 25
|
||||
};
|
||||
|
||||
struct DebugCommand
|
||||
{
|
||||
char id;
|
||||
void* param;
|
||||
};
|
||||
|
||||
struct BreakPoint
|
||||
{
|
||||
uint64_t addr;
|
||||
bool once;
|
||||
uint64_t origin;
|
||||
uint16_t size;
|
||||
|
||||
bool enable;
|
||||
};
|
||||
|
||||
struct Symbol{
|
||||
char name[255];
|
||||
uint64_t address;
|
||||
uint64_t size;
|
||||
uint64_t base;
|
||||
};
|
||||
|
||||
struct CallstackStruct
|
||||
{
|
||||
uint64_t callPoint;
|
||||
uint64_t stackBase;
|
||||
Symbol symbol;
|
||||
char module[255];
|
||||
int depth;
|
||||
};
|
||||
|
||||
class Core{
|
||||
private:
|
||||
std::map<uint64_t, BreakPoint> _breakPoints;
|
||||
int _ep = -1;
|
||||
uint64_t _lastRip = 0;
|
||||
int _counter = 0;
|
||||
uint64_t _needToRestore = 0;
|
||||
|
||||
public:
|
||||
const uint64_t int3 = 0xCCCCCCCCCCCCCCCC;
|
||||
|
||||
int pipe = -1;
|
||||
int remotePipe = -1;
|
||||
|
||||
user_regs_struct regs;
|
||||
std::vector<Symbol> symbols;
|
||||
std::vector<Symbol> staticSymbols;
|
||||
std::unordered_map<std::string, std::vector<Symbol>> symbolMap;
|
||||
|
||||
Symbol currentSymbol{};
|
||||
|
||||
void PeekTexts(uint64_t* data, uint64_t words) const
|
||||
{
|
||||
auto addr = _lastRip;
|
||||
|
||||
for(uint64_t i=0;i<words;i++)
|
||||
{
|
||||
data[i] = ptrace(PTRACE_PEEKTEXT, _process, addr + i * 8, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void PeekTextsFrom(uint64_t* data, uint64_t words, uint64_t address) const
|
||||
{
|
||||
for(uint64_t i=0;i<words;i++)
|
||||
{
|
||||
data[i] = ptrace(PTRACE_PEEKTEXT, _process, address + i * 8, NULL);
|
||||
}
|
||||
}
|
||||
/*
|
||||
void PeekTextsFrom(uint64_t address, uint64_t size)
|
||||
{
|
||||
delete[] codeData;
|
||||
|
||||
codeSize = size;
|
||||
codeData = new uint8_t[codeSize + 16];
|
||||
codeAddr = address;
|
||||
for(uint64_t p = codeAddr, offset = 0; p < codeAddr + codeSize; p += WORD_SIZE, offset += WORD_SIZE)
|
||||
{
|
||||
*(uint64_t*)(codeData + offset) = ptrace(PTRACE_PEEKDATA, _process, p, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void RestoreCode()
|
||||
{
|
||||
for(auto k: _breakPoints) // Repair Breakpoint code
|
||||
{
|
||||
if(k.second.enable && k.second.addr >= codeAddr && k.second.addr < codeAddr + codeSize)
|
||||
{
|
||||
uint64_t localOffset = k.second.addr - codeAddr;
|
||||
auto px = reinterpret_cast<uint64_t*>(codeData + localOffset);
|
||||
uint64_t mask = 0xFFFFFFFFFFFFFFFF << k.second.size * 8;
|
||||
|
||||
*px = (k.second.origin & (~mask)) | ((*px) & mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
uint64_t GetBaseAddress()
|
||||
{
|
||||
if(_map == nullptr) return 0;
|
||||
|
||||
for(int i=0;i<_map->Size();i++)
|
||||
{
|
||||
auto j = _map->GetItem(i);
|
||||
if(_path == j.path)
|
||||
{
|
||||
return j.startAddress;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t GetBaseAddress(const std::string& module)
|
||||
{
|
||||
if(_map == nullptr) return 0;
|
||||
|
||||
for(int i=0;i<_map->Size();i++)
|
||||
{
|
||||
auto j = _map->GetItem(i);
|
||||
if(module == j.path)
|
||||
{
|
||||
return j.startAddress;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t GetEndAddress(const std::string& module)
|
||||
{
|
||||
if(_map == nullptr) return 0;
|
||||
uint64_t endAddr = 0;
|
||||
|
||||
for(int i=0;i<_map->Size();i++)
|
||||
{
|
||||
auto j = _map->GetItem(i);
|
||||
if(module == j.path)
|
||||
{
|
||||
endAddr = j.endAddress;
|
||||
}
|
||||
}
|
||||
return endAddr;
|
||||
}
|
||||
|
||||
uint64_t GetEntryPoint()
|
||||
{
|
||||
int fd = open(_path.c_str(), O_RDONLY);
|
||||
if(fd < 0) return 0;
|
||||
|
||||
elf::elf ef(elf::create_mmap_loader(fd));
|
||||
return ef.get_hdr().entry;
|
||||
}
|
||||
|
||||
std::vector<Symbol> GetSymbols(const std::string& module)
|
||||
{
|
||||
std::vector<Symbol> tr;
|
||||
std::map<std::string, Symbol> r;
|
||||
|
||||
uint64_t lbase = GetBaseAddress(module);
|
||||
uint64_t ebase = GetEndAddress(module);
|
||||
|
||||
if(symbolMap.contains(module))
|
||||
{
|
||||
tr = symbolMap[module];
|
||||
}
|
||||
else
|
||||
{
|
||||
int fd = open(module.c_str(), O_RDONLY);
|
||||
if(fd < 0) return {};
|
||||
|
||||
elf::elf ef(elf::create_mmap_loader(fd));
|
||||
for (const auto& section : ef.sections()) {
|
||||
if (section.get_hdr().type == elf::sht::symtab) {
|
||||
|
||||
const auto& lsymbols = section.as_symtab();
|
||||
for (const auto& symbol : lsymbols) {
|
||||
if (symbol.get_data().type() == elf::stt::func && symbol.get_data().value != 0) {
|
||||
// A symbol with zero address maybe an extern symbol in (.so)
|
||||
// We will not place it in local module symbol list
|
||||
// Because we fill find its body in the symbol table of another modules
|
||||
Symbol h{};
|
||||
strcpy(h.name, symbol.get_name().c_str());
|
||||
h.address = symbol.get_data().value;
|
||||
h.size = symbol.get_data().size;
|
||||
h.base = lbase;
|
||||
r.emplace(symbol.get_name(), h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& section : ef.sections()) {
|
||||
if (section.get_hdr().type == elf::sht::dynsym) {
|
||||
const auto& lsymbols = section.as_symtab();
|
||||
for (const auto& symbol : lsymbols) {
|
||||
if (symbol.get_data().type() == elf::stt::func && symbol.get_data().value != 0 && (!r.contains(symbol.get_name()))) {
|
||||
Symbol h{};
|
||||
strcpy(h.name, symbol.get_name().c_str());
|
||||
h.address = symbol.get_data().value;
|
||||
h.size = symbol.get_data().size;
|
||||
h.base = lbase;
|
||||
r.emplace(symbol.get_name(), h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(const auto& j : r)
|
||||
{
|
||||
tr.emplace_back(j.second);
|
||||
}
|
||||
|
||||
for(auto &k : tr)
|
||||
{
|
||||
if(k.size == 0) // Unmarked Symbol
|
||||
{
|
||||
// Find a symbol following behind it
|
||||
uint64_t addr = 0xffffffffffffffff;
|
||||
for(auto &j : tr)
|
||||
{
|
||||
if(j.address > k.address && j.address < addr) addr = j.address;
|
||||
}
|
||||
|
||||
if(addr != 0xffffffffffffffff){ k.size = addr - k.address; continue; }
|
||||
|
||||
// Found no Symbol
|
||||
// Set its size to the end of its section
|
||||
for (const auto& section : ef.sections())
|
||||
{
|
||||
if(k.address > section.get_hdr().addr && k.address < section.get_hdr().addr + section.size())
|
||||
{
|
||||
k.size = section.get_hdr().addr + section.size() - k.address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
symbolMap.emplace(module, tr);
|
||||
}
|
||||
|
||||
for(const auto &k : staticSymbols)
|
||||
{
|
||||
if(k.address >= lbase && k.address < ebase)
|
||||
{
|
||||
bool within = false;
|
||||
for(const auto &j : tr)
|
||||
{
|
||||
if(k.address >= j.address + j.base && k.address < j.address + j.size + j.base)
|
||||
{
|
||||
within = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!within) tr.emplace_back(k);
|
||||
}
|
||||
}
|
||||
|
||||
return tr;
|
||||
}
|
||||
|
||||
void InsertBreakPoint(uint64_t addr, bool once = true)
|
||||
{
|
||||
if(_breakPoints.contains(addr)) return;
|
||||
|
||||
auto ins = disassemblyFrom(addr, 1, 0);
|
||||
|
||||
BreakPoint bp{};
|
||||
bp.once = once;
|
||||
bp.addr = addr;
|
||||
bp.origin = ptrace(PTRACE_PEEKTEXT, _process, addr, NULL);
|
||||
bp.size = ins[0].size;
|
||||
if(bp.size > 8) bp.size = 8;
|
||||
bp.enable = true;
|
||||
|
||||
uint64_t mask = 0xFFFFFFFFFFFFFFFF << bp.size * 8;
|
||||
|
||||
uint64_t int3_data = (bp.origin & mask) | (int3 & (~mask));
|
||||
ptrace(PTRACE_POKETEXT, _process, addr, int3_data);
|
||||
|
||||
_breakPoints.emplace(bp.addr, bp);
|
||||
}
|
||||
|
||||
void DisableBreakPoint(uint64_t addr)
|
||||
{
|
||||
if(!_breakPoints.contains(addr)) return;
|
||||
|
||||
_breakPoints[addr].enable = false;
|
||||
|
||||
uint64_t origin = ptrace(PTRACE_PEEKTEXT, _process, addr, NULL);
|
||||
uint64_t mask = 0xFFFFFFFFFFFFFFFF << _breakPoints[addr].size * 8;
|
||||
|
||||
ptrace(PTRACE_POKETEXT, _process, addr,
|
||||
(_breakPoints[addr].origin & (~mask)) | (origin & mask));
|
||||
}
|
||||
|
||||
void RestoreBreakPoint(uint64_t addr)
|
||||
{
|
||||
if(!_breakPoints.contains(addr)) return;
|
||||
|
||||
_breakPoints[addr].enable = true;
|
||||
uint64_t origin = ptrace(PTRACE_PEEKTEXT, _process, addr, NULL);
|
||||
|
||||
uint64_t mask = 0xFFFFFFFFFFFFFFFF << _breakPoints[addr].size * 8;
|
||||
uint64_t int3_data = (origin & mask) | (int3 & (~mask));
|
||||
|
||||
ptrace(PTRACE_POKETEXT, _process, addr, int3_data);
|
||||
}
|
||||
|
||||
void DeleteBreakPoint(uint64_t addr)
|
||||
{
|
||||
if(!_breakPoints.contains(addr)) return;
|
||||
|
||||
DisableBreakPoint(addr);
|
||||
_breakPoints.erase(addr);
|
||||
}
|
||||
|
||||
bool ContainsBreakPoint(uint64_t addr)
|
||||
{
|
||||
return _breakPoints.contains(addr);
|
||||
}
|
||||
|
||||
std::vector<BreakPoint> GetBreakpoints()
|
||||
{
|
||||
std::vector<BreakPoint> r;
|
||||
for(const auto &k : _breakPoints)
|
||||
{
|
||||
r.emplace_back(k.second);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void TryContinue()
|
||||
{
|
||||
if(_needToRestore == 0)
|
||||
ptrace(PTRACE_CONT, _process, NULL, NULL);
|
||||
else
|
||||
{
|
||||
ptrace(PTRACE_SINGLESTEP, _process, NULL, NULL);
|
||||
_temporaryStop = true;
|
||||
}
|
||||
}
|
||||
|
||||
void queueMessage(char id, void* param) const
|
||||
{
|
||||
DebugCommand msg{id, param};
|
||||
if(pipe != -1)
|
||||
{
|
||||
write(remotePipe, &msg, sizeof(DebugCommand));
|
||||
}
|
||||
}
|
||||
|
||||
static bool ContainerContains(const std::vector<std::string>& c, const std::string& e)
|
||||
{
|
||||
for(const auto &k : c)
|
||||
{
|
||||
if(k == e) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// disassembly from an address
|
||||
/// \param address
|
||||
/// \param cc
|
||||
/// \param sz cc and sz cannot be both 0 at the same time
|
||||
/// \return
|
||||
[[nodiscard]] std::vector<cs_insn> disassemblyFrom(uint64_t address, int cc, uint32_t sz, const std::vector<std::string>& end = {}, bool make = false)
|
||||
{
|
||||
if(cc == 0 && sz == 0) return {};
|
||||
uint64_t originAddress = address;
|
||||
|
||||
csh handle;
|
||||
cs_insn *insn;
|
||||
size_t count;
|
||||
std::vector<cs_insn> r;
|
||||
uint64_t data[4];
|
||||
|
||||
if(!cc) cc = INT_MAX;
|
||||
if(!sz) sz = 0xFFFFFFFF;
|
||||
|
||||
if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK)
|
||||
return {};
|
||||
cs_option(handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
|
||||
|
||||
for(int i=0, p=0;i<cc && p < sz;i++)
|
||||
{
|
||||
PeekTextsFrom(data, 4, address);
|
||||
for(auto k: _breakPoints) // Repair Breakpoint code
|
||||
{
|
||||
if(k.second.enable && k.second.addr == address)
|
||||
{
|
||||
uint64_t mask = 0xFFFFFFFFFFFFFFFF << k.second.size * 8;
|
||||
data[0] = (k.second.origin & (~mask)) | ((data[0]) & mask);
|
||||
}
|
||||
}
|
||||
|
||||
count = cs_disasm(handle, (uint8_t*)data, 32, address, 1, &insn);
|
||||
if(count == 1)
|
||||
{
|
||||
if((!r.empty()) && ContainerContains(end, insn[0].mnemonic)) {
|
||||
cs_free(insn, count);
|
||||
break;
|
||||
}
|
||||
cs_insn k{};
|
||||
memcpy(&k, &insn[0], sizeof(cs_insn));
|
||||
r.emplace_back(k);
|
||||
address += k.size;
|
||||
p += k.size;
|
||||
cs_free(insn, count);
|
||||
}else break;
|
||||
}
|
||||
cs_close(&handle);
|
||||
|
||||
if(make) // Save to static symbols
|
||||
{
|
||||
for(int i=0;i<staticSymbols.size();i++)
|
||||
{
|
||||
if(originAddress >= staticSymbols[i].address && originAddress < staticSymbols[i].address + staticSymbols[i].size)
|
||||
{
|
||||
staticSymbols.erase(staticSymbols.begin() + i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
Symbol sym{};
|
||||
sym.address = originAddress;
|
||||
sym.base = 0;
|
||||
sprintf(sym.name, "call_%lx", sym.address);
|
||||
for(const auto &k : r)
|
||||
{
|
||||
sym.size += k.size;
|
||||
}
|
||||
staticSymbols.emplace_back(sym);
|
||||
}
|
||||
|
||||
return std::move(r);
|
||||
}
|
||||
|
||||
bool GetMapEntire(uint64_t addr, MapEntire& ent)
|
||||
{
|
||||
for(int i=0;i<_map->Size();i++) // Read Text Data
|
||||
{
|
||||
auto item = _map->GetItem(i);
|
||||
if(item.startAddress == addr) {
|
||||
ent = item;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
bool entryBreak = true;
|
||||
|
||||
int _process = -1;
|
||||
pthread_t _th = 0;
|
||||
std::string _path;
|
||||
std::string _argv;
|
||||
std::string _module;
|
||||
std::shared_ptr<MemoryMap> _map = nullptr;
|
||||
bool _temporaryStop = false;
|
||||
|
||||
bool _trapped = false;
|
||||
|
||||
void sendMessage(char id, void* param) const {
|
||||
DebugCommand msg{id, param};
|
||||
if (pipe != -1) {
|
||||
write(pipe, &msg, sizeof(DebugCommand));
|
||||
}
|
||||
}
|
||||
|
||||
void debugHandler()
|
||||
{
|
||||
char str_buffer[128];
|
||||
std::vector<MapEntire> me;
|
||||
|
||||
int fk = fork();
|
||||
if(fk == 0)
|
||||
{
|
||||
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
|
||||
perror("ptrace");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char env[255];
|
||||
strcpy(env, _path.c_str());
|
||||
env[_path.rfind('/') + 1] = 0;
|
||||
|
||||
chdir(env);
|
||||
execl(_path.c_str(), _argv.c_str(), NULL);
|
||||
perror("execve");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
_process = fk;
|
||||
|
||||
sprintf(str_buffer, "%s - [/proc/%d/]", _argv.c_str(), _process);
|
||||
sendMessage(MSG_TITLE, str_buffer);
|
||||
|
||||
int status = 0;
|
||||
long err;
|
||||
char buffer[32];
|
||||
|
||||
_ep = epoll_create1(0);
|
||||
auto ev1 = epoll_event{.events = EPOLLIN};
|
||||
ev1.data.fd = pipe;
|
||||
epoll_ctl(_ep, EPOLL_CTL_ADD, pipe, &ev1);
|
||||
sendMessage(MSG_STARTUP, nullptr); // Notify Main Thread
|
||||
|
||||
while(true)
|
||||
{
|
||||
// Handle User IO Message
|
||||
int n = epoll_wait(_ep, &ev1, 1, 1);
|
||||
if(n > 0)
|
||||
{
|
||||
auto sz = read(pipe, buffer, sizeof(DebugCommand));
|
||||
if(sz != sizeof(DebugCommand)) continue;
|
||||
|
||||
auto msg = (DebugCommand*)buffer;
|
||||
switch (msg->id) {
|
||||
case MSG_PAUSE:
|
||||
kill(_process, SIGTRAP);
|
||||
break;
|
||||
case MSG_STOP:
|
||||
kill(_process, SIGKILL);
|
||||
sendMessage(MSG_STOP, nullptr);
|
||||
return;
|
||||
case MSG_RESTART:
|
||||
kill(_process, SIGKILL);
|
||||
sendMessage(MSG_RESTART, nullptr);
|
||||
return;
|
||||
case MSG_CONTINUE:
|
||||
if(_trapped) {
|
||||
TryContinue();
|
||||
if(err == -1) perror("ptrace");
|
||||
sendMessage(MSG_CONTINUE, nullptr);
|
||||
_trapped = false;
|
||||
}
|
||||
break;
|
||||
case MSG_STEPINTO:
|
||||
if(_trapped) {
|
||||
err = ptrace(PTRACE_SINGLESTEP, _process, NULL, NULL);
|
||||
sendMessage(MSG_CONTINUE, nullptr);
|
||||
_trapped = false;
|
||||
break;
|
||||
}
|
||||
case MSG_STEPOVER:
|
||||
{
|
||||
auto ins = disassemblyFrom(_lastRip, 2, 0);
|
||||
|
||||
if(ins.size() < 2 || !std::string(ins[0].mnemonic).contains("call")){
|
||||
err = ptrace(PTRACE_SINGLESTEP, _process, NULL, NULL);
|
||||
sendMessage(MSG_CONTINUE, nullptr);
|
||||
_trapped = false;
|
||||
break;
|
||||
}
|
||||
if(!_breakPoints.contains(ins[1].address))
|
||||
InsertBreakPoint(ins[1].address, true);
|
||||
|
||||
TryContinue();
|
||||
sendMessage(MSG_CONTINUE, nullptr);
|
||||
_trapped = false;
|
||||
break;
|
||||
}
|
||||
case MSG_SWITCHSYMBOL:
|
||||
{
|
||||
auto b = reinterpret_cast<Symbol*>(msg->param);
|
||||
auto ci = disassemblyFrom(b->base + b->address, 0, b->size);
|
||||
auto ch = new std::vector<cs_insn>;
|
||||
*ch = ci;
|
||||
|
||||
strcpy(currentSymbol.name, b->name);
|
||||
currentSymbol.address = b->address;
|
||||
currentSymbol.size = b->size;
|
||||
currentSymbol.base = b->base;
|
||||
// currentSymbol
|
||||
sendMessage(MSG_ASM, ch);
|
||||
sendMessage(MSG_CURRENTSYMBOL, nullptr);
|
||||
sendMessage(MSG_SELECT, nullptr);
|
||||
break;
|
||||
}
|
||||
case MSG_PARSE:
|
||||
{
|
||||
for(int i=0;i<_map->Size();i++) // Read Text Data
|
||||
{
|
||||
auto item = _map->GetItem(i);
|
||||
me.emplace_back(item);
|
||||
if(_lastRip >= item.startAddress && _lastRip < item.endAddress)
|
||||
{
|
||||
auto moduleBase = GetBaseAddress(_module);
|
||||
|
||||
auto ci = disassemblyFrom(_lastRip, 10000, item.endAddress - _lastRip, {"int3", "endbr64"}, true);
|
||||
if(ci.empty()) break;
|
||||
|
||||
auto ch = new std::vector<cs_insn>;
|
||||
*ch = ci;
|
||||
|
||||
sendMessage(MSG_ASM, ch);
|
||||
|
||||
sprintf(str_buffer, "%s - [/proc/%d/] - [%s] at %lx",
|
||||
_argv.c_str(), _process, _module.c_str(), moduleBase);
|
||||
|
||||
sprintf(currentSymbol.name, "call_%lx", _lastRip);
|
||||
currentSymbol.address = _lastRip;
|
||||
currentSymbol.size = item.endAddress - _lastRip;
|
||||
currentSymbol.base = moduleBase;
|
||||
|
||||
symbols = GetSymbols(_module);
|
||||
sendMessage(MSG_INSYM, nullptr);
|
||||
sendMessage(MSG_TITLE, str_buffer);
|
||||
sendMessage(MSG_SELECT, nullptr);
|
||||
sendMessage(MSG_CURRENTSYMBOL, nullptr);
|
||||
sendMessage(MSG_SYMBOLS, nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSG_BREAK:
|
||||
{
|
||||
auto addr = (uint64_t)msg->param;
|
||||
InsertBreakPoint(addr, false);
|
||||
break;
|
||||
}
|
||||
case MSG_BREAKONCE:
|
||||
{
|
||||
auto addr = (uint64_t)msg->param;
|
||||
InsertBreakPoint(addr, true);
|
||||
break;
|
||||
}
|
||||
case MSG_DELBREAK:
|
||||
{
|
||||
auto addr = (uint64_t)msg->param;
|
||||
DeleteBreakPoint(addr);
|
||||
break;
|
||||
}
|
||||
case MSG_SWITCHMODULE:
|
||||
{
|
||||
_module = (char*)msg->param;
|
||||
auto moduleBase = GetBaseAddress(_module);
|
||||
symbols = GetSymbols(_module);
|
||||
if(symbols.empty()) break;
|
||||
currentSymbol = symbols[0];
|
||||
|
||||
sprintf(str_buffer, "%s - [/proc/%d/] - [%s] at %lx", _argv.c_str(), _process, _module.c_str(), moduleBase);
|
||||
|
||||
auto ci = disassemblyFrom(moduleBase + currentSymbol.address, 0, currentSymbol.size);
|
||||
auto ch = new std::vector<cs_insn>;
|
||||
*ch = ci;
|
||||
|
||||
sendMessage(MSG_ASM, ch);
|
||||
sendMessage(MSG_TITLE, str_buffer);
|
||||
sendMessage(MSG_SYMBOLS, nullptr);
|
||||
sendMessage(MSG_CURRENTSYMBOL, nullptr);
|
||||
sendMessage(MSG_SELECT, nullptr);
|
||||
|
||||
delete[](char*)msg->param;
|
||||
break;
|
||||
}
|
||||
case MSG_GETBREAKS:
|
||||
{
|
||||
auto dst = new cs_insn[_breakPoints.size()];
|
||||
int p = 0;
|
||||
|
||||
for(const auto &k : GetBreakpoints())
|
||||
{
|
||||
auto ins = disassemblyFrom(k.addr, 1, 0);
|
||||
dst[p++] = ins[0];
|
||||
}
|
||||
sendMessage(MSG_GETBREAKS, dst);
|
||||
break;
|
||||
}
|
||||
case MSG_CALLSTACK:
|
||||
{
|
||||
GetCallStack();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Debug Process Signal
|
||||
auto r = waitpid(_process, &status, WNOHANG);
|
||||
if(r == 0) continue;
|
||||
|
||||
if(WIFEXITED(status) || WIFSIGNALED(status)) break;
|
||||
int signal_number = WSTOPSIG(status);
|
||||
|
||||
switch (signal_number) {
|
||||
case SIGTRAP: {
|
||||
_trapped = true;
|
||||
_map = std::make_shared<MemoryMap>(_process);
|
||||
regs = {};
|
||||
|
||||
me.clear();
|
||||
ptrace(PTRACE_GETREGS, _process, NULL, ®s);
|
||||
|
||||
|
||||
if(entryBreak) // If this is the first time enter trap, insert int3 break point to Entry Point
|
||||
{
|
||||
entryBreak = false;
|
||||
_trapped = false;
|
||||
auto base = GetBaseAddress();
|
||||
uint64_t entryPoint = base + GetEntryPoint();
|
||||
InsertBreakPoint(entryPoint);
|
||||
|
||||
ptrace(PTRACE_CONT, _process, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
bool couldBeBp = true;
|
||||
if(_needToRestore != 0)
|
||||
{
|
||||
RestoreBreakPoint(_needToRestore);
|
||||
_needToRestore = 0;
|
||||
couldBeBp = false;
|
||||
}
|
||||
|
||||
if(_temporaryStop){
|
||||
_trapped = false;
|
||||
_temporaryStop = false;
|
||||
ptrace(PTRACE_CONT, _process, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
if(_breakPoints.contains(regs.rip - 1) && couldBeBp)
|
||||
{
|
||||
// Reduce RIP by 1
|
||||
regs.rip -= 1;
|
||||
ptrace(PTRACE_SETREGS, _process, NULL, ®s);
|
||||
// Restore origin data
|
||||
if(_breakPoints[regs.rip].once)
|
||||
DeleteBreakPoint(regs.rip);
|
||||
else
|
||||
{
|
||||
DisableBreakPoint(regs.rip);
|
||||
_needToRestore = regs.rip;
|
||||
}
|
||||
}
|
||||
|
||||
_lastRip = regs.rip;
|
||||
bool updateModule = false;
|
||||
bool ridSymbols = true;
|
||||
|
||||
for(int i=0;i<_map->Size();i++) // Read Text Data
|
||||
{
|
||||
auto item = _map->GetItem(i);
|
||||
me.emplace_back(item);
|
||||
if(regs.rip >= item.startAddress && regs.rip < item.endAddress)
|
||||
{
|
||||
if(_module != item.path) updateModule = true;
|
||||
|
||||
_module = item.path;
|
||||
auto moduleBase = GetBaseAddress(_module);
|
||||
symbols = GetSymbols(_module);
|
||||
|
||||
for(auto b : symbols)
|
||||
{
|
||||
// If Rip is between the start and the end of a module
|
||||
if(regs.rip >= b.base + b.address && regs.rip < b.base + b.address + b.size)
|
||||
{
|
||||
ridSymbols = false;
|
||||
if (currentSymbol.address == b.address) break;
|
||||
currentSymbol = b;
|
||||
|
||||
auto ci = disassemblyFrom(moduleBase + b.address, 0, b.size);
|
||||
auto ch = new std::vector<cs_insn>;
|
||||
*ch = ci;
|
||||
|
||||
sendMessage(MSG_ASM, ch);
|
||||
sendMessage(MSG_CURRENTSYMBOL, nullptr);
|
||||
}
|
||||
}
|
||||
if(ridSymbols) { // ridSymbols keeps true means RIP isn't in any Symbol
|
||||
sprintf(str_buffer, "%s - [/proc/%d/] - [%s] at %lx (!Symbol table Escaping!)",
|
||||
_argv.c_str(), _process, _module.c_str(), moduleBase);
|
||||
sendMessage(MSG_NONSYM, nullptr);
|
||||
|
||||
strcpy(currentSymbol.name, "[Out of Symbol Table]");
|
||||
currentSymbol.address = _lastRip;
|
||||
currentSymbol.size = item.endAddress - _lastRip;
|
||||
currentSymbol.base = moduleBase;
|
||||
sendMessage(MSG_CURRENTSYMBOL, nullptr);
|
||||
}
|
||||
else{
|
||||
sprintf(str_buffer, "%s - [/proc/%d/] - [%s] at %lx", _argv.c_str(), _process, _module.c_str(), moduleBase);
|
||||
sendMessage(MSG_INSYM, nullptr);
|
||||
}
|
||||
sendMessage(MSG_TITLE, str_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage(MSG_PAUSE, nullptr);
|
||||
sendMessage(MSG_REG, ®s);
|
||||
sendMessage(MSG_MEMORY, &me);
|
||||
if(updateModule) sendMessage(MSG_SYMBOLS, nullptr);
|
||||
sendMessage(MSG_SELECT, nullptr);
|
||||
break;
|
||||
}
|
||||
case SIGTERM:
|
||||
kill(_process, SIGKILL);
|
||||
waitpid(_process, &status, 0);
|
||||
_process = -1;
|
||||
sendMessage(MSG_STOP, nullptr);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage(MSG_STOP, nullptr);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit Core(const std::string& f, const std::string& a)
|
||||
{
|
||||
_path = f;
|
||||
_argv = a;
|
||||
|
||||
auto lambda = [](void *arg) {
|
||||
Core* instance = static_cast<Core*>(arg);
|
||||
instance->debugHandler();
|
||||
return (void*)nullptr;
|
||||
};
|
||||
|
||||
pthread_create(&_th, nullptr, lambda, this);
|
||||
}
|
||||
|
||||
~Core()
|
||||
{
|
||||
if(_process > 0)
|
||||
{
|
||||
pthread_join(_th, nullptr);
|
||||
// delete[] codeData;
|
||||
close(_ep);
|
||||
}
|
||||
}
|
||||
|
||||
bool Status() const
|
||||
{
|
||||
return _trapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::optional<Symbol> GetSymbolAt(uint64_t address, std::string &mod)
|
||||
{
|
||||
auto _m = _map->GetModules();
|
||||
for(const auto &k : _m)
|
||||
{
|
||||
if(address >= k.startAddress && address < k.endAddress)
|
||||
{
|
||||
mod = k.path;
|
||||
for(const auto &l : GetSymbols(k.path))
|
||||
{
|
||||
if(address >= l.address + l.base && address < l.address + l.size + l.base)
|
||||
{
|
||||
return l;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void GetCallStack()
|
||||
{
|
||||
std::vector<std::pair<uint64_t, uint64_t>> callPoints;
|
||||
auto r = new std::stack<CallstackStruct>;
|
||||
uint64_t cbp = regs.rbp;
|
||||
|
||||
callPoints.emplace_back(regs.rip, cbp);
|
||||
|
||||
while(cbp)
|
||||
{
|
||||
callPoints.emplace_back(ptrace(PTRACE_PEEKDATA, _process, cbp + 8, NULL), ptrace(PTRACE_PEEKDATA, _process, cbp, NULL));
|
||||
cbp = ptrace(PTRACE_PEEKDATA, _process, cbp, NULL);
|
||||
}
|
||||
int cc = 0;
|
||||
for(const auto &k : callPoints)
|
||||
{
|
||||
std::string mod;
|
||||
auto s = GetSymbolAt(k.first, mod);
|
||||
|
||||
Symbol s2{};
|
||||
strcpy(s2.name, "<???>");
|
||||
if(s.has_value()) s2 = s.value();
|
||||
|
||||
CallstackStruct e{};
|
||||
e.callPoint = k.first;
|
||||
sprintf(e.module, "<%s>", mod.empty() ? "???" : mod.c_str());
|
||||
e.symbol = s2;
|
||||
e.stackBase = k.second;
|
||||
e.depth = cc++;
|
||||
|
||||
r->emplace(e);
|
||||
}
|
||||
CallstackStruct e{};
|
||||
e.depth = _process;
|
||||
r->emplace(e);
|
||||
sendMessage(MSG_CALLSTACK, r);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
133
core/MemoryMap.hpp
Normal file
133
core/MemoryMap.hpp
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifndef _H_MEMORYMAP
|
||||
#define _H_MEMORYMAP 1
|
||||
|
||||
static std::vector<std::string> split(const std::string& str, char delimiter) {
|
||||
std::vector<std::string> tokens;
|
||||
std::string token;
|
||||
std::stringstream ss(str);
|
||||
|
||||
while (std::getline(ss, token, delimiter)) {
|
||||
tokens.push_back(token);
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
enum Access{
|
||||
READ = 0x01,
|
||||
WRITE = 0x02,
|
||||
EXECUTE = 0x04,
|
||||
PRIVATE = 0x08,
|
||||
SHARED = 0x10
|
||||
};
|
||||
|
||||
struct MapEntire
|
||||
{
|
||||
uint64_t startAddress;
|
||||
uint64_t endAddress;
|
||||
uint8_t access = 0;
|
||||
uint32_t offset;
|
||||
char path[512] = {0};
|
||||
|
||||
MapEntire()
|
||||
{
|
||||
startAddress = 0;
|
||||
endAddress = 0;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
explicit MapEntire(const std::string& src)
|
||||
{
|
||||
auto e = split(src, ' ');
|
||||
for(int i=0;i<e.size();i++)
|
||||
if(e[i].empty())
|
||||
{
|
||||
e.erase(e.begin() + i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
startAddress = std::stoull(split(e[0], '-')[0], nullptr, 16);
|
||||
endAddress = std::stoull(split(e[0], '-')[1], nullptr, 16);
|
||||
|
||||
if(e[1].contains("r")) access |= READ;
|
||||
if(e[1].contains("w")) access |= WRITE;
|
||||
if(e[1].contains("x")) access |= EXECUTE;
|
||||
if(e[1].contains("p")) access |= PRIVATE;
|
||||
if(e[1].contains("s")) access |= SHARED;
|
||||
|
||||
offset = std::stoull(e[2], nullptr, 16);
|
||||
|
||||
if(e.size() < 6) return;
|
||||
e[5].erase(e[5].end() - 1);
|
||||
strcpy(path, e[5].c_str());
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryMap
|
||||
{
|
||||
protected:
|
||||
static bool readAll(std::vector<std::string>& dst, const std::string& path)
|
||||
{
|
||||
auto f = fopen(path.c_str(), "r");
|
||||
if(!f){ perror("fopen()"); return false; }
|
||||
|
||||
dst.clear();
|
||||
char line[1024];
|
||||
while(fgets(line, 1024, f))
|
||||
{
|
||||
dst.emplace_back(line);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
char _path[255] = {0};
|
||||
std::vector<std::string> _content;
|
||||
|
||||
public:
|
||||
explicit MemoryMap(int pid)
|
||||
{
|
||||
sprintf(_path, "/proc/%d/maps", pid);
|
||||
readAll(_content, _path);
|
||||
}
|
||||
|
||||
MapEntire GetItem(int index)
|
||||
{
|
||||
auto u = MapEntire(_content[index]);
|
||||
return u;
|
||||
}
|
||||
|
||||
size_t Size()
|
||||
{
|
||||
return _content.size();
|
||||
}
|
||||
|
||||
std::vector<MapEntire> GetModules()
|
||||
{
|
||||
std::vector<MapEntire> r;
|
||||
for(int i=0;i<Size();i++)
|
||||
{
|
||||
auto k = GetItem(i);
|
||||
if(!std::ranges::any_of(r, [&](const MapEntire& i){
|
||||
return strcmp(k.path, i.path) == 0;
|
||||
}) && k.path[0] && k.path[0] != '[' )
|
||||
{
|
||||
r.emplace_back(k);
|
||||
}
|
||||
if(strcmp(k.path, (*(r.end() - 1)).path) == 0)
|
||||
{
|
||||
(*(r.end() - 1)).endAddress = k.endAddress;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,26 +0,0 @@
|
||||
//
|
||||
// Created by acite on 5/1/24.
|
||||
//
|
||||
|
||||
#include <gtkmm.h>
|
||||
|
||||
using namespace Gtk;
|
||||
|
||||
extern Button* bAttach;
|
||||
extern Dialog* dialogAttach;
|
||||
extern Window* MainWindow;
|
||||
|
||||
void button_attach()
|
||||
{
|
||||
dialogAttach->show();
|
||||
}
|
||||
|
||||
void button_exec()
|
||||
{
|
||||
int fk = fork();
|
||||
if(fk == 0)
|
||||
{
|
||||
execl("/usr/bin/netstat", "netstat" ,"-tupln", NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
//
|
||||
// Created by acite on 5/1/24.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user