mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-03 21:46:32 +00:00
106 lines
6.5 KiB
C++
106 lines
6.5 KiB
C++
#include <cstring>
|
|
#include "object_api.h"
|
|
#include "providers.h"
|
|
|
|
#include <array>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
|
|
static void check(bool value,const char *message)
|
|
{
|
|
if (!value) throw std::runtime_error(message);
|
|
}
|
|
|
|
static size_t descriptors()
|
|
{
|
|
return size_t(std::distance(std::filesystem::directory_iterator("/proc/self/fd"),std::filesystem::directory_iterator{}));
|
|
}
|
|
|
|
int main(int argc,char **argv)
|
|
{
|
|
try {
|
|
check(argc==4,"bootstrap, SDK executable and output path required");
|
|
const auto fd=open(argv[3],O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC,0600);
|
|
check(fd>=0,"cannot open file contract output");
|
|
raven::NativeObjects objects;
|
|
const auto inherited=objects.inherit_file(fd);
|
|
check(!inherited.status && objects.file_type(inherited.value).value==1,"disk inheritance or type differs");
|
|
const uint8_t marker[]={1,0,2,3};
|
|
check(objects.write_file(inherited.value,marker).value==4 && lseek(fd,0,SEEK_CUR)==4,"inherited file offset or binary output differs");
|
|
check(objects.set_event(inherited.value,true).status==0xc0000024U && objects.query_event(inherited.value).status==0xc0000024U &&
|
|
objects.wait(inherited.value,0,std::chrono::steady_clock::now()).status==0xc0000024U,"file handle accepted as event");
|
|
check(!objects.close(inherited.value) && objects.file_type(inherited.value).status==0xc0000008U && fcntl(fd,F_GETFD)>=0,
|
|
"file close changed host descriptor or retained guest access");
|
|
raven::NativeObjects limited(0);
|
|
const auto before=descriptors();
|
|
for (size_t i=0;i<16;++i) check(limited.inherit_file(fd).status==0xc000009aU,"file handle budget ignored");
|
|
check(descriptors()==before && !limited.count() && limited.inherit_file(-1).status==0xc0000008U,"failed inheritance leaked descriptors");
|
|
int pipes[2];check(!pipe2(pipes,O_CLOEXEC),"cannot create pipe control");
|
|
const auto pipe=objects.inherit_file(pipes[1]);
|
|
// A pipe write reaches the other end byte for byte (the pipe is empty, so the write cannot block).
|
|
const auto piped=objects.write_file(pipe.value,marker);
|
|
uint8_t received[8]{};
|
|
check(!pipe.status && objects.file_type(pipe.value).value==3 && !piped.status && piped.value==sizeof marker &&
|
|
read(pipes[0],received,sizeof received)==ssize_t(sizeof marker) && !memcmp(received,marker,sizeof marker),
|
|
"pipe write did not deliver its bytes");
|
|
objects.close(pipe.value);close(pipes[0]);close(pipes[1]);
|
|
const auto readonly=open(argv[3],O_RDONLY | O_CLOEXEC);check(readonly>=0,"cannot open readonly control");
|
|
const auto read_handle=objects.inherit_file(readonly);close(readonly);
|
|
check(objects.write_file(read_handle.value,marker).status==0xc0000022U,"readonly file accepted output");
|
|
objects.close(read_handle.value);
|
|
raven::ModuleSet modules;
|
|
auto &proxy=modules.add("proxy.exe",raven::environment_provider("kernel32.dll",0x180000000ULL));
|
|
raven::LoadedImage *images[]{&proxy.loaded};
|
|
raven::NativeProcess process(images,argv[1],std::chrono::seconds(5));
|
|
raven::NativeEnvironment environment(process,proxy.loaded.base(),1,1);
|
|
const auto live=objects.inherit_file(fd);
|
|
environment.prepare_parameters({u"proxy.exe",u"proxy.exe"},{UINT64_MAX,live.value,UINT64_MAX});
|
|
raven::NativeObjectAPI api(process,environment,objects,std::chrono::steady_clock::now()+std::chrono::seconds(5));
|
|
const auto address=modules.resolve({"proxy.exe","GetLastError",0,false,0});process.add_api_trap(address);
|
|
const auto invoke=[&](std::string_view name,std::initializer_list<uint64_t> args) {
|
|
check(process.call(address,std::span(args.begin(),args.size()),std::chrono::seconds(1)).stop==raven::NativeStop::api,"file API proxy did not stop");
|
|
check(environment.dispatch(name) || api.dispatch(name),"file API did not dispatch");
|
|
const auto result=process.resume(std::chrono::seconds(1));
|
|
check(result.stop==raven::NativeStop::returned,"file API did not return");return result.value;
|
|
};
|
|
check(invoke("GetStdHandle",{uint32_t(-11)})==live.value,"standard handle is not the owned file");
|
|
const auto memory=process.map_memory(8192,PROT_READ | PROT_WRITE);
|
|
process.write_memory(memory,marker);process.protect_memory(memory+4096,4096,PROT_NONE);
|
|
const auto position=lseek(fd,0,SEEK_CUR);
|
|
for (const auto output : {memory+4096,memory+4094}) {
|
|
check(!invoke("WriteFile",{live.value,memory,4,output,0}) && environment.last_error()==998 && lseek(fd,0,SEEK_CUR)==position,
|
|
"bad byte-count output changed the host file");
|
|
}
|
|
process.protect_memory(memory,4096,PROT_READ);
|
|
check(!invoke("WriteFile",{live.value,memory,4,memory,0}) && environment.last_error()==998 && lseek(fd,0,SEEK_CUR)==position,
|
|
"readonly byte-count output changed the host file");
|
|
process.protect_memory(memory,4096,PROT_READ | PROT_WRITE);
|
|
check(!invoke("WriteFile",{live.value,memory+4096,4,memory+16,0}) && environment.last_error()==998 && lseek(fd,0,SEEK_CUR)==position,
|
|
"unreadable input changed the host file");
|
|
check(!invoke("WriteFile",{live.value,memory,1048577,memory+16,0}) && environment.last_error()==120 &&
|
|
!invoke("WriteFile",{live.value,memory,4,memory+16,memory}) && environment.last_error()==120 && lseek(fd,0,SEEK_CUR)==position,
|
|
"unsupported file mode changed the host file");
|
|
process.unmap_memory(memory);objects.close(live.value);
|
|
check(!ftruncate(fd,0) && lseek(fd,0,SEEK_SET)==0,"cannot reset SDK output");
|
|
std::ifstream input(argv[2],std::ios::binary | std::ios::ate);
|
|
check(bool(input) && input.tellg()>0 && input.tellg()<1024*1024,"invalid file SDK executable");
|
|
std::vector<uint8_t> bytes(size_t(input.tellg()));input.seekg(0);
|
|
check(bool(input.read(reinterpret_cast<char *>(bytes.data()),std::streamsize(bytes.size()))),"short file SDK input");
|
|
raven::ModuleSet sdk;auto &entry=sdk.add("client.exe",std::move(bytes));
|
|
raven::NativeLaunch launch{u"client.exe",u"client.exe"};launch.standard_fds={-1,fd,fd};launch.startup.flags=256;
|
|
const auto result=raven::run_native_process(sdk,entry,argv[1],std::chrono::seconds(5),{},{},{},launch);
|
|
check(result.stop==raven::NativeStop::returned && !result.value,"compiled standard-file SDK failed");
|
|
std::array<uint8_t,8> actual{};
|
|
check(lseek(fd,0,SEEK_SET)==0 && read(fd,actual.data(),actual.size())==4 &&
|
|
std::equal(actual.begin(),actual.begin()+4,std::array<uint8_t,4>{'A',0,'B','\n'}.begin()),"SDK file bytes or length differ");
|
|
close(fd);
|
|
std::cout<<"Native owned standard files, binary writes, shared offset, lifetime and memory boundaries passed\n";
|
|
} catch (const std::exception &error) { std::cerr<<error.what()<<'\n';return 1; }
|
|
return 0;
|
|
}
|