Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ Checks: >
-readability-braces-around-statements,
-google-readability-braces-around-statements,
-misc-include-cleaner,
-cppcoreguidelines-non-private-member-variables-in-classes
-cppcoreguidelines-non-private-member-variables-in-classes,
-bugprone-bitwise-pointer-cast,
-performance-no-int-to-ptr

WarningsAsErrors: ''

Expand Down
1 change: 1 addition & 0 deletions src/cmd/environment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class Environment {
this->define("breakpoint", std::make_shared<BreakpointFn>(BreakpointFn()));
this->define("run", std::make_shared<RunFn>(RunFn()));
this->define("resume", std::make_shared<ContinueFn>(ContinueFn()));
this->define("target", std::make_shared<TargetFn>(TargetFn()));
}

public:
Expand Down
78 changes: 51 additions & 27 deletions src/cmd/stdlib.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@

#include "callable.hpp"
#include "cmd/object.hpp"
#include "cmd/util.hpp"
#include "core/target.hpp"
#include "error.hpp"
#include "expected.hpp"
#include "subcommand.hpp"
#include "token_type.hpp"
#include "typedefs.hpp"

class LenFn : public Callable {
public:
Expand Down Expand Up @@ -48,8 +50,6 @@ class PrintFn : public Callable {

class BreakpointFn : public Callable {
private:
using sv = std::string_view;

static Object rmOrToggleBreakpoint(const std::vector<std::string>& args,
bool toggle = false) {
Expected<u64, std::string> addrRes = strToAddr(args[0]);
Expand All @@ -62,13 +62,13 @@ class BreakpointFn : public Callable {
if (!bps.contains(addr)) return std::format("No breakpoint at {}", addr);

if (toggle) {
i32 res = target->disableBreakpoint(addr, true);
const i32 res = target->disableBreakpoint(addr, true);
if (res != 0) return "Error toggling breakpoint";
return std::format("Toggled breakpoint at {}, now {}", addr,
bps[addr].enabled);
}

i32 res = target->disableBreakpoint(addr, false);
const i32 res = target->disableBreakpoint(addr, false);
if (res != 0) return "Error disabling breakpoint";
return std::format("Removed breakpoint at {}", addr);
}
Expand All @@ -92,9 +92,9 @@ class BreakpointFn : public Callable {
FnPtr set = [](const std::vector<std::string>& args) -> Object {
Expected<u64, std::string> addrRes = strToAddr(args[0]);
if (!addrRes) return addrRes.error();
u64 addr = *addrRes;
const u64 addr = *addrRes;

i32 bpRes = Context::getTarget()->setBreakpoint(addr);
const i32 bpRes = Context::getTarget()->setBreakpoint(addr);
if (bpRes != 0)
return "Error setting breakpoint!";
else
Expand All @@ -115,24 +115,6 @@ class BreakpointFn : public Callable {
{sv("toggle"), toggle}},
"breakpoint"};

static std::vector<std::string> convertToStr(const std::vector<Object>& vec) {
std::vector<std::string> res{};
res.reserve(vec.size());

for (const auto& x : vec) {
const auto* const str = std::get_if<std::string>(&x);
if (str == nullptr) {
CmdError::error(TokenType::IDENTIFIER,
"Please provide all arguments as strings",
CmdErrorType::RUNTIME_ERROR);
return {}; // Return empty on error, not partial
}
res.emplace_back(*str);
}

return res;
}

public:
[[nodiscard]] int arity() const override { return 1; }
[[nodiscard]] std::string str() const override {
Expand All @@ -144,8 +126,8 @@ class BreakpointFn : public Callable {
CoreError::error("Target is not running!");
return std::monostate{};
}
std::vector<std::string> convertedArgs = BreakpointFn::convertToStr(args);
if (convertedArgs.size() < 1) return std::monostate{};
std::vector<std::string> convertedArgs = detail::convertToStr(args);
if (convertedArgs.empty()) return std::monostate{};
const std::string subcmd = convertedArgs.front();
convertedArgs.erase(convertedArgs.begin());
return m_subcmds.exec(subcmd, convertedArgs);
Expand Down Expand Up @@ -209,7 +191,7 @@ class RunFn : public Callable {
else
return "Unable to start target (are you running with sudo?)\n";

i32 res = m_target->attach();
const i32 res = m_target->attach();
if (res != 0) return "Could not attach to target!\n";
m_target->setTargetState(TargetState::RUNNING);
// TODO: Reset this when target exits
Expand Down Expand Up @@ -238,4 +220,46 @@ class ContinueFn : public Callable {
}
};

class TargetFn : public Callable {
private:
FnPtr info = [](const std::vector<std::string>& args) -> Object {
#pragma unused(args)
auto& target = Context::getTarget();
if (!target) return "Target not set!";
return target->getInfo();
};

FnPtr set = [](const std::vector<std::string>& args) -> Object {
auto& target = Context::getTarget();
if (target) {
if (target->getTargetState() == TargetState::RUNNING ||
target->getTargetState() == TargetState::STOPPED)
return "Cannot set new target whilst one is running!";
}

if (Target::isFileValid(args[0])) {
Context::setTarget(Target::create(args[0]));
return std::format("Target: {}", args[0]);
}

return std::format("{} is not a valid target path!", args[0]);
};

SubcommandHandler m_subcmds{{{sv("info"), info}, {sv("set"), set}}, "target"};

public:
[[nodiscard]] int arity() const override { return 1; }
[[nodiscard]] std::string str() const override {
return "<native fn: target>";
}

Object call(std::vector<Object> args) override {
std::vector<std::string> convertedArgs = detail::convertToStr(args);
if (convertedArgs.empty()) return std::monostate{};
const std::string subcmd = convertedArgs.front();
convertedArgs.erase(convertedArgs.begin());
return m_subcmds.exec(subcmd, convertedArgs);
}
};

#endif
18 changes: 18 additions & 0 deletions src/cmd/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,21 @@ double detail::parseNumber(const std::string& str) {
std::format("Cannot parse {} as number\n", str));
}
}

std::vector<std::string> detail::convertToStr(const std::vector<Object>& vec) {
std::vector<std::string> res{};
res.reserve(vec.size());

for (const auto& x : vec) {
const auto* const str = std::get_if<std::string>(&x);
if (str == nullptr) {
CmdError::error(TokenType::IDENTIFIER,
"Please provide all arguments as strings",
CmdErrorType::RUNTIME_ERROR);
return {}; // Return empty on error, not partial
}
res.emplace_back(*str);
}

return res;
}
3 changes: 3 additions & 0 deletions src/cmd/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
#define UTIL_H

#include <string>
#include <vector>

#include "cmd/object.hpp"
#include "token_type.hpp"

namespace detail {
bool isop(char c);
TokenType ctoop(char c);
double parseNumber(const std::string& str);
std::vector<std::string> convertToStr(const std::vector<Object>& vec);
} // namespace detail

#endif
43 changes: 23 additions & 20 deletions src/core/macho/macho.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ extern "C" {
// Adapted from https://lowlevelbits.org/parsing-mach-o-files/

void Macho::readMagic() {
u32 magic = 0;
const u32 magic = 0;
m_file.seekg(0, std::ios::beg);
m_file.read(std::bit_cast<char*>(&magic), sizeof(uint32_t));
m_file.read(std::bit_cast<char*>(&magic), sizeof(u32));
m_magic = magic;
}

Expand All @@ -82,16 +82,16 @@ void Macho::dumpHeader(int offset) {
loadCmdsOffset += headerSize;
std::cout << Macho::cpuTypeName(header.cputype) << '\n';
} else {
int headerSize = sizeof(struct mach_header);
const int headerSize = sizeof(struct mach_header);
auto header = loadBytesAndMaybeSwap<mach_header>(offset);
}

dumpSegmentCommands(loadCmdsOffset, ncmds);
}

void Macho::dumpSegmentCommands(int offset, uint32_t ncmds) {
void Macho::dumpSegmentCommands(int offset, u32 ncmds) {
u32 actualOffset = offset;
for (int i = 0; i < ncmds; i++) {
for (u32 i = 0; i < ncmds; i++) {
auto cmd = loadBytesAndMaybeSwap<load_command>(actualOffset);
if (cmd.cmd == LC_SEGMENT_64) {
auto segment = loadBytesAndMaybeSwap<segment_command_64>(actualOffset);
Expand All @@ -115,7 +115,7 @@ std::string Macho::cpuTypeName(cpu_type_t cpuType) {
return "unknown";
}

void Macho::dumpSections(uint32_t offset, uint32_t end) {
void Macho::dumpSections(u32 offset, u32 end) {
u32 actualOffset = offset;
while (actualOffset != end) {
auto section = loadBytesAndMaybeSwap<section_64>(actualOffset);
Expand Down Expand Up @@ -154,7 +154,7 @@ i32 Macho::launch(CStringArray& argList) {
m_pid = pid;

// TODO: This only works with sudo even when codesigned, why?
kern_return_t kr = task_for_pid(mach_task_self(), pid, &this->m_task);
const kern_return_t kr = task_for_pid(mach_task_self(), pid, &this->m_task);
if (kr != KERN_SUCCESS) {
CoreError::error(mach_error_string(kr));
}
Expand All @@ -163,15 +163,15 @@ i32 Macho::launch(CStringArray& argList) {

i32 Macho::attach() {
// http://uninformed.org/index.cgi?v=4&a=3&p=14
i32 res = this->setupExceptionPorts();
const i32 res = this->setupExceptionPorts();
ptrace(PT_ATTACHEXC, m_pid, nullptr, 0);
this->readAslrSlide();
return res;
};

// TODO: Make an overload with u32 for 32bit systems
i32 Macho::setBreakpoint(u64 addr) {
u64 actual = addr + m_aslr_slide;
const u64 actual = addr + m_aslr_slide;
auto sz = static_cast<mach_msg_type_number_t>(sizeof(u32));
vm_offset_t origBuf = 0;
kern_return_t kr = mach_vm_read(m_task, actual, sizeof(u32), &origBuf, &sz);
Expand All @@ -181,7 +181,7 @@ i32 Macho::setBreakpoint(u64 addr) {
return -1;
}

u32 origIns = *reinterpret_cast<u32*>(origBuf);
const u32 origIns = *reinterpret_cast<u32*>(origBuf);
mach_vm_deallocate(mach_task_self(), origBuf, sizeof(u32));
m_breakpoints[addr] = {.orig_ins = origIns, .enabled = true};

Expand Down Expand Up @@ -321,6 +321,8 @@ kern_return_t catch_mach_exception_raise_state(
const thread_state_t oldState, // NOLINT(misc-misplaced-const)
mach_msg_type_number_t oldStateCnt, thread_state_t newState,
mach_msg_type_number_t* newStateCnt) {
#pragma unused(flavour)
#pragma unused(oldState)
#pragma unused(excPort)
#pragma unused(exc)
#pragma unused(code)
Expand All @@ -335,9 +337,10 @@ kern_return_t catch_mach_exception_raise_state(
kern_return_t catch_mach_exception_raise_state_identity(
mach_port_t excPort, mach_port_t thread, mach_port_t task,
exception_type_t exc, mach_exception_data_t code,
mach_msg_type_number_t codeCnt, int* flavour, thread_state_t oldState,
mach_msg_type_number_t oldStateCnt, thread_state_t newState,
mach_msg_type_number_t* newStateCnt) {
mach_msg_type_number_t codeCnt,
int* flavour, // NOLINT(readability-non-const-parameter)
thread_state_t oldState, mach_msg_type_number_t oldStateCnt,
thread_state_t newState, mach_msg_type_number_t* newStateCnt) {
#pragma unused(excPort)
#pragma unused(task)
#pragma unused(code)
Expand Down Expand Up @@ -376,19 +379,19 @@ kern_return_t catch_mach_exception_raise_state_identity(

} // extern "C"

mach_port_t Macho::threadSelect() {
mach_port_t Macho::threadSelect() const {
std::cout << "TASK: " << m_task << '\n';
thread_act_array_t acts{};
mach_msg_type_number_t numThreads = 0;

kern_return_t kr = task_threads(m_task, &acts, &numThreads);
const kern_return_t kr = task_threads(m_task, &acts, &numThreads);

if (kr != KERN_SUCCESS) {
std::cout << "task_threads fail!\n";
CoreError::error(mach_error_string(kr));
}

thread_act_t mainThread = acts[0]; // Cleanup
const thread_act_t mainThread = acts[0]; // Cleanup
vm_deallocate(mach_task_self(), reinterpret_cast<vm_address_t>(acts),
numThreads * sizeof(thread_act_t));

Expand Down Expand Up @@ -546,12 +549,12 @@ void Macho::readAslrSlideFromRegions() {
if (kr != KERN_SUCCESS) break;

// Look for executable regions that could contain the main binary
if (info.protection & VM_PROT_EXECUTE) {
if ((info.protection & VM_PROT_EXECUTE) != 0) {
vm_offset_t headerBuf{};
auto sz = static_cast<mach_msg_type_number_t>(sizeof(u32));
kr = mach_vm_read(m_task, addr, sizeof(u32), &headerBuf, &sz);
if (kr == KERN_SUCCESS) {
u32 magic = *reinterpret_cast<u32*>(headerBuf);
const u32 magic = *reinterpret_cast<u32*>(headerBuf);
mach_vm_deallocate(mach_task_self(), headerBuf, sizeof(u32));
if (magic == MH_MAGIC_64) {
m_aslr_slide = addr - 0x100000000;
Expand All @@ -568,7 +571,7 @@ void Macho::readAslrSlideFromRegions() {
u64& Macho::getAslrSlide() { return m_aslr_slide; }

i32 Macho::restorePrevIns(u64 k) {
u64 addr = k + m_aslr_slide;
const u64 addr = k + m_aslr_slide;

kern_return_t kr =
mach_vm_protect(m_task, addr, sizeof(u32), FALSE,
Expand Down Expand Up @@ -605,7 +608,7 @@ i32 Macho::disableBreakpoint(u64 addr, bool remove) {

if (!bp.enabled) return 0;

i32 res = this->restorePrevIns(addr);
const i32 res = this->restorePrevIns(addr);
if (res != 0) return 1;

if (remove)
Expand Down
11 changes: 5 additions & 6 deletions src/core/macho/macho.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct CpuTypeNames {
const char* cpu_name;
};

class Macho : public Target {
class Macho final : public Target {
public:
explicit Macho(std::ifstream f, std::string filePath);

Expand Down Expand Up @@ -49,7 +49,6 @@ class Macho : public Target {
u32 m_magic = 0;
mach_port_t m_exc_port = 0;
mach_port_t m_thread_port = 0;
bool m_is_64 = false;
bool m_is_swap = false;
static constexpr std::array<CpuTypeNames, 4> CPU_TYPE_NAMES = {
{{.cpu_type = CPU_TYPE_I386, .cpu_name = "i386"},
Expand All @@ -63,20 +62,20 @@ class Macho : public Target {
void maybeSwapBytes();

template <typename T>
T loadBytesAndMaybeSwap(uint32_t offset) {
T loadBytesAndMaybeSwap(u32 offset) {
T buf;
m_file.seekg(offset, std::ios::beg);
m_file.read(std::bit_cast<char*>(&buf), sizeof(T));
if (m_is_swap) SwapDescriptor<T>::swap(&buf);
return buf;
}

void dumpSegmentCommands(int offset, uint32_t ncmds);
void dumpSegmentCommands(int offset, u32 ncmds);
static std::string cpuTypeName(cpu_type_t cpuType);
void dumpSections(uint32_t offset, uint32_t end);
void dumpSections(u32 offset, u32 end);
i32 setupExceptionPorts();
void readAslrSlideFromRegions();
mach_port_t threadSelect(); // TODO: doesn't work (yet)
mach_port_t threadSelect() const; // TODO: doesn't work (yet)
};

#endif // CAESAR_MACHO_HPP
Loading
Loading