Added API scanner tool

New tool for issue #171 which links to libxenia to dump the API usage
from packaged content
This commit is contained in:
x1nixmzeng
2015-02-21 17:33:56 +00:00
parent 130c11a2ca
commit 25f78ed325
13 changed files with 433 additions and 40 deletions

View File

@@ -0,0 +1,24 @@
## api-scanner
api-scanner will dump out the API imports from a packaged 360 game
### Usage
Run from the command line
`api-scanner <package_path>`
or:
`api-scanner --target <package_path>`
Output is printed to the console window, so it self-managed.
The suggested usage is to append the output to a local file:
`api-scanner <package_path> >> title_log.txt`
### Issues
- Duplicate code for loading containers
- Several issues with gflags library - incorrectly prints usage from other files (due to linkage with libxenia)

View File

@@ -0,0 +1,189 @@
/**
******************************************************************************
* api-scanner - Scan for API imports from a packaged 360 game *
******************************************************************************
* Copyright 2015 x1nixmzeng. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "api_scanner_loader.h"
#include "xenia/kernel/xam_module.h"
#include "xenia/kernel/xboxkrnl_module.h"
namespace xe {
namespace tools {
void apiscanner_logger::operator()(const LogType type, const char* szMessage) {
switch (type) {
case LT_WARNING:
fprintf(stderr, "[W] %s\n", szMessage);
break;
case LT_ERROR:
fprintf(stderr, "[!] %s\n", szMessage);
break;
default:
break;
}
}
apiscanner_loader::apiscanner_loader()
: export_resolver(nullptr)
, memory_(nullptr)
{
export_resolver = std::make_unique<ExportResolver>();
kernel::XamModule::RegisterExportTable(export_resolver.get());
kernel::XboxkrnlModule::RegisterExportTable(export_resolver.get());
memory_ = std::make_unique<Memory>();
memory_->Initialize();
}
apiscanner_loader::~apiscanner_loader()
{
if (export_resolver != nullptr) {
export_resolver.reset();
}
if (memory_ != nullptr) {
memory_.reset();
}
}
bool apiscanner_loader::LoadTitleImports(const std::wstring& target) {
auto type(file_system.InferType(target));
int result = file_system.InitializeFromPath(type, target);
if (result) {
log(log.LT_ERROR, "Could not load target");
return false;
}
return ReadTarget();
}
bool apiscanner_loader::ReadTarget() {
// XXX Do a wildcard search for all xex files?
const char path[] = "game:\\default.xex";
kernel::XFile* file(nullptr);
bool read_result(false);
auto fs_entry = file_system.ResolvePath(path);
if (!fs_entry) {
log(log.LT_WARNING, "Could not resolve xex path");
return false;
}
// If the FS supports mapping, map the file in and load from that.
if (fs_entry->can_map()) {
auto mmap = fs_entry->CreateMemoryMapping(kernel::fs::Mode::READ, 0, 0);
if (!mmap) {
if (file) {
file->Release();
}
log(log.LT_WARNING, "Could not map filesystem");
return false;
}
title res;
read_result = ExtractImports(mmap->address(), mmap->length(), res);
if (read_result) {
loaded_titles.push_back(res);
}
}
else {
kernel::XFileInfo file_info;
if (fs_entry->QueryInfo(&file_info)) {
if (file) {
file->Release();
}
log(log.LT_WARNING, "Could not read xex attributes");
return false;
}
// Load into memory
std::vector<uint8_t> buffer(file_info.file_length);
// XXX No kernel state again
int result = file_system.Open(std::move(fs_entry), nullptr,
kernel::fs::Mode::READ, false, &file);
if (result) {
if (file) {
file->Release();
}
log(log.LT_WARNING, "Could not open xex file");
return false;
}
size_t bytes_read = 0;
result = file->Read(buffer.data(), buffer.size(), 0, &bytes_read);
if (result) {
if (file) {
file->Release();
}
log(log.LT_ERROR, "Could not read xex data");
return false;
}
title res;
read_result = ExtractImports(buffer.data(), bytes_read, res);
if (read_result) {
loaded_titles.push_back(res);
}
}
if (file) {
file->Release();
}
return read_result;
}
bool apiscanner_loader::ExtractImports(const void* addr, const size_t length,
title& info)
{
// Load the XEX into memory and decrypt.
xe_xex2_options_t xex_options = { 0 };
xe_xex2_ref xex_(xe_xex2_load(memory_.get(), addr, length, xex_options));
if (!xex_) {
log(log.LT_ERROR, "Failed to parse xex file");
return false;
}
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
info.title_id = header->execution_info.title_id;
// XXX Copy out library versions?
for (size_t n = 0; n < header->import_library_count; n++) {
const xe_xex2_import_library_t* library = &header->import_libraries[n];
xe_xex2_import_info_t* import_infos;
size_t import_info_count;
if (!xe_xex2_get_import_infos(xex_, library, &import_infos,
&import_info_count)) {
for (size_t m = 0; m < import_info_count; m++) {
const xe_xex2_import_info_t* import_info = &import_infos[m];
KernelExport* kernel_export = export_resolver->GetExportByOrdinal(
library->name, import_info->ordinal);
if ((kernel_export && kernel_export->type == KernelExport::Variable)
|| import_info->thunk_address) {
const std::string named(kernel_export ? kernel_export->name : "");
info.imports.push_back(named);
}
}
}
}
xe_xex2_dealloc(xex_);
std::sort(info.imports.begin(), info.imports.end());
return true;
}
} // tools
} // xe

View File

@@ -0,0 +1,69 @@
/**
******************************************************************************
* api-scanner - Scan for API imports from a packaged 360 game *
******************************************************************************
* Copyright 2015 x1nixmzeng. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "poly/main.h"
#include "poly/string.h"
#include "poly/math.h"
#include "xenia/kernel/fs/filesystem.h"
#include "xenia/memory.h"
#include "xenia/kernel/objects/xfile.h"
#include "xenia/export_resolver.h"
#include "xenia/kernel/util/xex2.h"
#include "xenia/xbox.h"
#include <vector>
namespace xe {
namespace tools {
class apiscanner_logger
{
public:
enum LogType {
LT_WARNING,
LT_ERROR
};
void operator()(const LogType type, const char* szMessage);
};
class apiscanner_loader
{
private:
kernel::fs::FileSystem file_system;
apiscanner_logger log;
std::unique_ptr<Memory> memory_;
std::unique_ptr<ExportResolver> export_resolver;
public:
apiscanner_loader();
~apiscanner_loader();
bool LoadTitleImports(const std::wstring& target);
struct title
{
uint32_t title_id;
std::vector<std::string> imports;
};
const std::vector<title>& GetAllTitles() const { return loaded_titles; }
private:
std::vector<title> loaded_titles;
bool ReadTarget();
bool ExtractImports(const void* addr, const size_t length, title& info);
};
} // tools
} // xe

View File

@@ -0,0 +1,47 @@
/**
******************************************************************************
* api-scanner - Scan for API imports from a packaged 360 game *
******************************************************************************
* Copyright 2015 x1nixmzeng. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <gflags/gflags.h>
#include "api_scanner_loader.h"
namespace xe {
namespace tools {
DEFINE_string(target, "", "List of file to extract imports from");
int api_scanner_main(std::vector<std::wstring>& args) {
// XXX we need gflags to split multiple flags into arrays for us
if (args.size() == 2 || !FLAGS_target.empty()) {
apiscanner_loader loader_;
std::wstring target(FLAGS_target.empty() ? args[1] :
poly::to_wstring(FLAGS_target));
std::wstring target_abs = poly::to_absolute_path(target);
// XXX For each target?
if (loader_.LoadTitleImports(target)) {
for (const auto title : loader_.GetAllTitles()) {
printf("%08x\n", title.title_id);
for (const auto import : title.imports) {
printf("\t%s\n", import.c_str());
}
}
}
}
return 0;
}
} // namespace tools
} // namespace xe
DEFINE_ENTRY_POINT(L"api-scanner", L"api-scanner --target=<target file>",
xe::tools::api_scanner_main);

View File

@@ -0,0 +1,8 @@
# Copyright 2015 x1nixmzeng. All Rights Reserved.
{
'sources': [
'api_scanner_loader.cc',
'api_scanner_loader.h',
'api_scanner_main.cc'
]
}