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:
Alex Cameron
2021-11-06 02:00:27 +11:00
committed by GitHub
parent b1b8d7c1a0
commit 66c4f8b915
8 changed files with 326 additions and 7 deletions
+1
View File
@@ -0,0 +1 @@
BasedOnStyle: LLVM
+4 -1
View File
@@ -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)
+32
View File
@@ -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.
+35
View File
@@ -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
+2 -6
View File
@@ -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; }
+9
View File
@@ -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)
+54
View File
@@ -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()
+189
View File
@@ -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;
}