mirror of
https://github.com/lifting-bits/sleigh
synced 2026-06-21 13:56:12 +00:00
example: Create sleigh-lift example program (#9)
* cmake: Specify headers for the sla and decomp libraries * example: Create `sleigh-lift` program * Move `sleigh-lift` under tools and make it part of the same CMake project * Turn on more stringent warnings * Consolidate on types * Only apply stringent compiler warnings for debug builds * Add missing cassert include * Rename `ParseBytes` => `ParseHexBytes` * Fix types * Add license header to `sleigh-lift` * Add section to README introducing `sleigh-lift` * Reword README * Add `libsleigh.hh` which includes all SLEIGH headers and applies appropriate pragmas to silence warnings * Change `find_package` test to use the `libsleigh.hh` header * Fix `libsleigh` header to check install directory * Older version of Clang don't have `-Wdeprecated-copy` as a separate warning to `-Wdeprecated`
This commit is contained in:
@@ -0,0 +1 @@
|
||||
BasedOnStyle: LLVM
|
||||
+4
-1
@@ -696,6 +696,7 @@ if(SLEIGH_ENABLE_INSTALL)
|
||||
"${library_root}/translate.hh"
|
||||
"${library_root}/types.h"
|
||||
"${library_root}/xml.hh"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/libsleigh.hh"
|
||||
)
|
||||
|
||||
install(
|
||||
@@ -757,7 +758,7 @@ if(SLEIGH_ENABLE_INSTALL)
|
||||
|
||||
DESTINATION
|
||||
"${CMAKE_INSTALL_BINDIR}"
|
||||
|
||||
|
||||
PERMISSIONS
|
||||
OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
||||
GROUP_READ GROUP_EXECUTE
|
||||
@@ -773,3 +774,5 @@ if(SLEIGH_ENABLE_INSTALL)
|
||||
FILE "${PROJECT_NAME}Config.cmake"
|
||||
)
|
||||
endif()
|
||||
|
||||
add_subdirectory(tools)
|
||||
|
||||
@@ -70,6 +70,38 @@ cmake --build .
|
||||
cmake --build . --target package
|
||||
```
|
||||
|
||||
## API Usage
|
||||
|
||||
An example program called `sleigh-lift` has been included to demonstrate how to use the SLEIGH API. It takes a hexadecimal string of bytes and either disassembles it or lifts it to p-code. The program can be invoked like so, where the `action` argument must be either `disassemble` or `pcode`:
|
||||
|
||||
```sh
|
||||
sleigh-lift [action] [sla_file] [bytes] [address:OPTIONAL]
|
||||
```
|
||||
|
||||
For example, to disassemble the following byte string:
|
||||
|
||||
```sh
|
||||
$ sleigh-lift disassemble <path where SLEIGH is installed>/share/sleigh/Processors/x86/data/languages/x86-64.sla 4881ecc00f0000
|
||||
0x00000000: SUB RSP,0xfc0
|
||||
```
|
||||
|
||||
And to lift it to p-code:
|
||||
|
||||
```sh
|
||||
$ sleigh-lift pcode <path where SLEIGH is installed>/share/sleigh/Processors/x86/data/languages/x86-64.sla 4881ecc00f0000
|
||||
(register,0x200,1) = INT_LESS (register,0x20,8) (const,0xfc0,8)
|
||||
(register,0x20b,1) = INT_SBORROW (register,0x20,8) (const,0xfc0,8)
|
||||
(register,0x20,8) = INT_SUB (register,0x20,8) (const,0xfc0,8)
|
||||
(register,0x207,1) = INT_SLESS (register,0x20,8) (const,0x0,8)
|
||||
(register,0x206,1) = INT_EQUAL (register,0x20,8) (const,0x0,8)
|
||||
(unique,0x12c00,8) = INT_AND (register,0x20,8) (const,0xff,8)
|
||||
(unique,0x12c80,1) = POPCOUNT (unique,0x12c00,8)
|
||||
(unique,0x12d00,1) = INT_AND (unique,0x12c80,1) (const,0x1,1)
|
||||
(register,0x202,1) = INT_EQUAL (unique,0x12d00,1) (const,0x0,1)
|
||||
```
|
||||
|
||||
The `SLEIGH_ENABLE_EXAMPLES` option must be set during the configuration step in order to build `sleigh-lift`.
|
||||
|
||||
## License
|
||||
|
||||
See the LICENSE file in the top directory of this repo.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (c) 2021-present, Trail of Bits, Inc.
|
||||
All rights reserved.
|
||||
|
||||
This source code is licensed in accordance with the terms specified in
|
||||
the LICENSE file found in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#include "address.hh"
|
||||
#include "context.hh"
|
||||
#include "emulate.hh"
|
||||
#include "error.hh"
|
||||
#include "float.hh"
|
||||
#include "globalcontext.hh"
|
||||
#include "loadimage.hh"
|
||||
#include "memstate.hh"
|
||||
#include "opbehavior.hh"
|
||||
#include "opcodes.hh"
|
||||
#include "partmap.hh"
|
||||
#include "pcoderaw.hh"
|
||||
#include "semantics.hh"
|
||||
#include "sleigh.hh"
|
||||
#include "sleighbase.hh"
|
||||
#include "slghpatexpress.hh"
|
||||
#include "slghpattern.hh"
|
||||
#include "slghsymbol.hh"
|
||||
#include "space.hh"
|
||||
#include "translate.hh"
|
||||
#include "types.h"
|
||||
#include "xml.hh"
|
||||
#pragma GCC diagnostic pop
|
||||
@@ -6,10 +6,6 @@
|
||||
the LICENSE file found in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <sleigh/loadimage.hh>
|
||||
#include <sleigh/sleigh.hh>
|
||||
#include <sleigh/emulate.hh>
|
||||
#include <sleigh/libsleigh.hh>
|
||||
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
int main() { return 0; }
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# Copyright (c) 2021-present, Trail of Bits, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed in accordance with the terms specified in
|
||||
# the LICENSE file found in the root directory of this source tree.
|
||||
#
|
||||
|
||||
add_subdirectory(sleigh-lift)
|
||||
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# Copyright (c) 2021-present, Trail of Bits, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed in accordance with the terms specified in
|
||||
# the LICENSE file found in the root directory of this source tree.
|
||||
#
|
||||
|
||||
add_executable(sleigh-lift
|
||||
src/main.cpp
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
if(MSVC)
|
||||
target_compile_options(sleigh-lift PRIVATE
|
||||
/W4
|
||||
/WX
|
||||
)
|
||||
else()
|
||||
target_compile_options(sleigh-lift PRIVATE
|
||||
-Wall
|
||||
-Werror
|
||||
-Wextra
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(sleigh-lift PRIVATE
|
||||
"${library_root}"
|
||||
"${PROJECT_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
target_link_libraries(sleigh-lift PRIVATE
|
||||
sla
|
||||
decomp
|
||||
)
|
||||
|
||||
if(SLEIGH_ENABLE_INSTALL)
|
||||
install(
|
||||
TARGETS
|
||||
sleigh-lift
|
||||
|
||||
EXPORT
|
||||
"${PROJECT_NAME}"
|
||||
|
||||
DESTINATION
|
||||
"${CMAKE_INSTALL_BINDIR}"
|
||||
|
||||
PERMISSIONS
|
||||
OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
||||
GROUP_READ GROUP_EXECUTE
|
||||
WORLD_READ WORLD_EXECUTE
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
Copyright (c) 2021-present, Trail of Bits, Inc.
|
||||
All rights reserved.
|
||||
|
||||
This source code is licensed in accordance with the terms specified in
|
||||
the LICENSE file found in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <libsleigh.hh>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
static void PrintUsage(void) {
|
||||
std::cerr
|
||||
<< "Usage: sleigh-lift [action] [sla_file] [bytes] [address:OPTIONAL]"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
class InMemoryLoadImage : public LoadImage {
|
||||
public:
|
||||
explicit InMemoryLoadImage(uint64_t base_addr)
|
||||
: LoadImage("nofile"), base_addr(base_addr) {}
|
||||
|
||||
void SetImageBuffer(std::string &&buf) {
|
||||
assert(image_buffer.empty());
|
||||
image_buffer = std::move(buf);
|
||||
}
|
||||
|
||||
void loadFill(unsigned char *ptr, int size, const Address &addr) override {
|
||||
uint8_t start = addr.getOffset();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
uint64_t offset = start + i;
|
||||
if (offset >= base_addr) {
|
||||
offset -= base_addr;
|
||||
ptr[i] = offset < image_buffer.size() ? image_buffer[i] : 0;
|
||||
} else {
|
||||
ptr[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string getArchType(void) const override { return "memory"; }
|
||||
void adjustVma(long) override {}
|
||||
|
||||
private:
|
||||
const uint64_t base_addr;
|
||||
std::string image_buffer;
|
||||
};
|
||||
|
||||
static std::string ParseHexBytes(std::string_view bytes, uint64_t addr,
|
||||
uint64_t addr_size) {
|
||||
std::string buffer;
|
||||
for (size_t i = 0; i < bytes.size(); i += 2) {
|
||||
const char nibbles[] = {bytes[i], bytes[i + 1], '\0'};
|
||||
char *parsed_to = nullptr;
|
||||
auto byte_val = strtol(nibbles, &parsed_to, 16);
|
||||
if (parsed_to != &(nibbles[2])) {
|
||||
std::cerr << "Invalid hex byte value '" << nibbles
|
||||
<< "' specified in bytes arg." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
const uint64_t addr_mask = ~0ULL >> (64UL - addr_size);
|
||||
auto byte_addr = addr + (i / 2);
|
||||
auto masked_addr = byte_addr & addr_mask;
|
||||
// Make sure that if a really big number is specified for `address`,
|
||||
// that we don't accidentally wrap around and start filling out low
|
||||
// byte addresses.
|
||||
if (masked_addr < byte_addr) {
|
||||
std::cerr << "Too many bytes specified to bytes arg, would result "
|
||||
<< "in a 32-bit overflow.";
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (masked_addr < addr) {
|
||||
std::cerr << "Too many bytes specified to bytes arg, would result "
|
||||
<< "in a 64-bit overflow.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
buffer.push_back(static_cast<char>(byte_val));
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
class AssemblyPrinter : public AssemblyEmit {
|
||||
public:
|
||||
void dump(const Address &addr, const std::string &mnemonic,
|
||||
const std::string &body) override {
|
||||
addr.printRaw(std::cout);
|
||||
std::cout << ": " << mnemonic << ' ' << body << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
static void PrintAssembly(Sleigh &engine, uint64_t addr, size_t len) {
|
||||
AssemblyPrinter asm_emit;
|
||||
Address cur_addr(engine.getDefaultCodeSpace(), addr),
|
||||
last_addr(engine.getDefaultCodeSpace(), addr + len);
|
||||
while (cur_addr < last_addr) {
|
||||
int32_t instr_len = engine.printAssembly(asm_emit, cur_addr);
|
||||
cur_addr = cur_addr + instr_len;
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintVarData(std::ostream &s, VarnodeData &data) {
|
||||
s << '(' << data.space->getName() << ',';
|
||||
data.space->printOffset(s, data.offset);
|
||||
s << ',' << std::dec << data.size << ')';
|
||||
}
|
||||
|
||||
class PcodePrinter : public PcodeEmit {
|
||||
public:
|
||||
void dump(const Address &, OpCode op, VarnodeData *outvar, VarnodeData *vars,
|
||||
int32_t isize) override {
|
||||
if (outvar) {
|
||||
PrintVarData(std::cout, *outvar);
|
||||
std::cout << " = ";
|
||||
}
|
||||
std::cout << get_opname(op);
|
||||
for (int32_t i = 0; i < isize; ++i) {
|
||||
std::cout << ' ';
|
||||
PrintVarData(std::cout, vars[i]);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
static void PrintPcode(Sleigh &engine, uint64_t addr, size_t len) {
|
||||
PcodePrinter pcode_emit;
|
||||
Address cur_addr(engine.getDefaultCodeSpace(), addr),
|
||||
last_addr(engine.getDefaultCodeSpace(), addr + len);
|
||||
while (cur_addr < last_addr) {
|
||||
int32_t instr_len = engine.oneInstruction(pcode_emit, cur_addr);
|
||||
cur_addr = cur_addr + instr_len;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 4 || argc > 5) {
|
||||
PrintUsage();
|
||||
return -1;
|
||||
}
|
||||
// Parse arguments
|
||||
const std::string action = argv[1];
|
||||
const char *sla_file_path = argv[2];
|
||||
const std::string bytes = argv[3];
|
||||
const char *addr_str = argc == 5 ? argv[4] : nullptr;
|
||||
if (bytes.size() % 2 != 0) {
|
||||
std::cerr << "Must provide an even number of bytes: " << bytes << std::endl;
|
||||
return -1;
|
||||
}
|
||||
// Get the address as an integer.
|
||||
uint64_t addr = 0;
|
||||
if (addr_str) {
|
||||
try {
|
||||
addr = std::stoul(addr_str);
|
||||
} catch (std::invalid_argument &ia) {
|
||||
std::cerr << "Invalid address argument: " << addr_str << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
} catch (std::out_of_range &oor) {
|
||||
std::cerr << "Address argument out of range: " << addr_str << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
// Put together SLEIGH components
|
||||
InMemoryLoadImage load_image(addr);
|
||||
ContextInternal ctx;
|
||||
Sleigh engine(&load_image, &ctx);
|
||||
DocumentStorage storage;
|
||||
Element *root = storage.openDocument(sla_file_path)->getRoot();
|
||||
storage.registerTag(root);
|
||||
engine.initialize(storage);
|
||||
// In order to parse and validate the byte string properly, we need to get the
|
||||
// address size from SLEIGH. Therefore this needs to happen after
|
||||
// initialization.
|
||||
//
|
||||
// Ensure that we don't start disassembling until we've set the image buffer.
|
||||
std::string image_buffer =
|
||||
ParseHexBytes(bytes, addr, engine.getDefaultSize());
|
||||
const size_t len = image_buffer.size();
|
||||
load_image.SetImageBuffer(std::move(image_buffer));
|
||||
if (action == "disassemble") {
|
||||
PrintAssembly(engine, addr, len);
|
||||
} else if (action == "pcode") {
|
||||
PrintPcode(engine, addr, len);
|
||||
} else {
|
||||
std::cerr << "Invalid action: " << action << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user