Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fileextractor: linux edition #1788

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
fileextractor: changes for build with meson
  • Loading branch information
delvinru committed Apr 24, 2024
commit 91af04b6ef7b0bc4adbcbc424109f645cb440754
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ subdir('src/libdrakvuf')
subdir('src/libinjector')
subdir('src/libusermode')
subdir('src/libhook')
subdir('src/libfs')
subdir('src/librepl')
subdir('src/plugins')

Expand Down
8 changes: 0 additions & 8 deletions src/libdrakvuf/linux-offsets-map.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,6 @@ static const char* linux_offset_names[__LINUX_OFFSETS_MAX][2] =
[NSPROXY_UTS_NS] = {"nsproxy", "uts_ns"},
[UTS_NAMESPACE_NAME] = {"uts_namespace", "name"},
[NEW_UTSNAME_RELEASE] = {"new_utsname", "release"},
[EXTENT_STATUS_RB_NODE] = {"extent_status", "rb_node"},
[EXTENT_STATUS_ES_LBLK] = {"extent_status", "es_lblk"},
[EXTENT_STATUS_ES_LEN] = {"extent_status", "es_len"},
[EXTENT_STATUS_ES_PBLK] = {"extent_status", "es_pblk"},
[RB_NODE___RB_PARENT_COLOR] = {"rb_node", "__rb_parent_color"},
[RB_NODE_RB_RIGHT] = {"rb_node", "rb_right"},
[RB_NODE_RB_LEFT] = {"rb_node", "rb_left"},
[RB_ROOT_RB_NODE] = {"rb_root", "rb_node"},
};

#endif
8 changes: 0 additions & 8 deletions src/libdrakvuf/linux-offsets.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,6 @@ enum linux_offsets
NSPROXY_UTS_NS,
UTS_NAMESPACE_NAME,
NEW_UTSNAME_RELEASE,
EXTENT_STATUS_RB_NODE,
EXTENT_STATUS_ES_LBLK,
EXTENT_STATUS_ES_LEN,
EXTENT_STATUS_ES_PBLK,
RB_ROOT_RB_NODE,
RB_NODE_RB_LEFT,
RB_NODE_RB_RIGHT,
RB_NODE___RB_PARENT_COLOR,
__LINUX_OFFSETS_MAX
};

Expand Down
3 changes: 0 additions & 3 deletions src/libfs/base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,6 @@

#pragma once

#include <libdrakvuf/libdrakvuf.h>
#include <libdrakvuf/private.h>
#include <libdrakvuf/linux-offsets.h>
#include <plugins/helpers/vmi_lock_guard.h>

#include <cstring>
Expand Down
70 changes: 41 additions & 29 deletions src/libfs/ext4/ext4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,21 @@
{
ACCESS_CONTEXT(ctx,
.translate_mechanism = VMI_TM_PROCESS_DTB,
.dtb = drakvuf_->kpgd,
.addr = root + drakvuf_->offsets[RB_ROOT_RB_NODE]
.dtb = this->dtb_,
.addr = root + this->offsets[RB_ROOT_RB_NODE]
);

auto vmi = vmi_lock_guard(drakvuf_);
addr_t n;
if (VMI_FAILURE == vmi_read_addr(drakvuf_->vmi, &ctx, &n) || !n)
if (VMI_FAILURE == vmi_read_addr(vmi, &ctx, &n) || !n)
return 0;

ctx.addr = n + drakvuf_->offsets[RB_NODE_RB_LEFT];
ctx.addr = n + this->offsets[RB_NODE_RB_LEFT];
addr_t rb_left, tmp = 0;
while (VMI_SUCCESS == vmi_read_addr(drakvuf_->vmi, &ctx, &tmp) && tmp)
while (VMI_SUCCESS == vmi_read_addr(vmi, &ctx, &tmp) && tmp)
{
rb_left = tmp;
ctx.addr = rb_left + drakvuf_->offsets[RB_NODE_RB_LEFT];
ctx.addr = rb_left + this->offsets[RB_NODE_RB_LEFT];
}

return rb_left;
Expand All @@ -139,47 +140,48 @@
{
ACCESS_CONTEXT(ctx,
.translate_mechanism = VMI_TM_PROCESS_DTB,
.dtb = drakvuf_->kpgd,
.addr = node + drakvuf_->offsets[RB_NODE___RB_PARENT_COLOR]
.dtb = this->dtb_,
.addr = node + this->offsets[RB_NODE___RB_PARENT_COLOR]
);

auto vmi = vmi_lock_guard(drakvuf_);
addr_t empty_node = 0;
if (VMI_FAILURE == vmi_read_addr(drakvuf_->vmi, &ctx, &empty_node) || empty_node == node)
if (VMI_FAILURE == vmi_read_addr(vmi, &ctx, &empty_node) || empty_node == node)
return 0;

/* If we have a right-hand child, go down and then left as far as we can. */
addr_t rb_right = 0;
ctx.addr = node + drakvuf_->offsets[RB_NODE_RB_RIGHT];
if (VMI_SUCCESS == vmi_read_addr(drakvuf_->vmi, &ctx, &rb_right) && rb_right)
ctx.addr = node + this->offsets[RB_NODE_RB_RIGHT];
if (VMI_SUCCESS == vmi_read_addr(vmi, &ctx, &rb_right) && rb_right)
{
node = rb_right;

addr_t rb_left = 0;
ctx.addr = node + drakvuf_->offsets[RB_NODE_RB_LEFT];
while (VMI_SUCCESS == vmi_read_addr(drakvuf_->vmi, &ctx, &rb_left) && rb_left)
ctx.addr = node + this->offsets[RB_NODE_RB_LEFT];
while (VMI_SUCCESS == vmi_read_addr(vmi, &ctx, &rb_left) && rb_left)
{
// important: don't place assignment in loop it's break logic
node = rb_left;
ctx.addr = node + drakvuf_->offsets[RB_NODE_RB_LEFT];
ctx.addr = node + this->offsets[RB_NODE_RB_LEFT];
}

return node;
}

addr_t parent, tmp = 0;
ctx.addr = node + drakvuf_->offsets[RB_NODE___RB_PARENT_COLOR];
while (VMI_SUCCESS == vmi_read_addr(drakvuf_->vmi, &ctx, &tmp))
ctx.addr = node + this->offsets[RB_NODE___RB_PARENT_COLOR];
while (VMI_SUCCESS == vmi_read_addr(vmi, &ctx, &tmp))
{
parent = tmp & ~3;
if (!parent)
break;

ctx.addr = parent + drakvuf_->offsets[RB_NODE_RB_RIGHT];
if (VMI_FAILURE == vmi_read_addr(drakvuf_->vmi, &ctx, &rb_right) || node != rb_right)
ctx.addr = parent + this->offsets[RB_NODE_RB_RIGHT];
if (VMI_FAILURE == vmi_read_addr(vmi, &ctx, &rb_right) || node != rb_right)
break;

node = parent;
ctx.addr = node + drakvuf_->offsets[RB_NODE___RB_PARENT_COLOR];
ctx.addr = node + this->offsets[RB_NODE___RB_PARENT_COLOR];
}

return parent;
Expand All @@ -189,8 +191,9 @@
{
ACCESS_CONTEXT(ctx,
.translate_mechanism = VMI_TM_PROCESS_DTB,
.dtb = drakvuf_->kpgd
.dtb = this->dtb_,
);
auto vmi = vmi_lock_guard(drakvuf_);

addr_t node = rb_first(i_es_tree);

Expand All @@ -200,21 +203,21 @@

while (node)
{
addr_t es = node - drakvuf_->offsets[EXTENT_STATUS_RB_NODE];
addr_t es = node - this->offsets[EXTENT_STATUS_RB_NODE];

uint32_t es_lblk, es_len = 0;
uint64_t es_pblk = 0;

ctx.addr = es + drakvuf_->offsets[EXTENT_STATUS_ES_LBLK];
if (VMI_FAILURE == vmi_read_32(drakvuf_->vmi, &ctx, &es_lblk))
ctx.addr = es + this->offsets[EXTENT_STATUS_ES_LBLK];
if (VMI_FAILURE == vmi_read_32(vmi, &ctx, &es_lblk))
goto out;

ctx.addr = es + drakvuf_->offsets[EXTENT_STATUS_ES_LEN];
if (VMI_FAILURE == vmi_read_32(drakvuf_->vmi, &ctx, &es_len))
ctx.addr = es + this->offsets[EXTENT_STATUS_ES_LEN];
if (VMI_FAILURE == vmi_read_32(vmi, &ctx, &es_len))
goto out;

ctx.addr = es + drakvuf_->offsets[EXTENT_STATUS_ES_PBLK];
if (VMI_FAILURE == vmi_read_64(drakvuf_->vmi, &ctx, &es_pblk))
ctx.addr = es + this->offsets[EXTENT_STATUS_ES_PBLK];
if (VMI_FAILURE == vmi_read_64(vmi, &ctx, &es_pblk))
goto out;

ext4_extent extent =
Expand Down Expand Up @@ -244,7 +247,7 @@
return {};

uint64_t offset = extent.get_start() * super_block->get_block_size();
uint64_t length = extent.ee_len * super_block->get_block_size();
Fixed Show fixed Hide fixed

uint64_t rest = config->filesize - config->offset;
/* truncate last extent by actual filesize */
Expand Down Expand Up @@ -410,8 +413,10 @@
* @param inode_number inode in filesystem
* @return if the file is saved successfully, then true is returned, otherwise false
*/
bool Ext4Filesystem::save_file_by_inode(const std::string& output_file, uint64_t inode_number)
bool Ext4Filesystem::save_file_by_inode(const std::string& output_file, uint64_t inode_number, addr_t dtb)
{
this->dtb_ = dtb;

auto inode = get_inode(inode_number);
if (nullptr == inode)
return {};
Expand Down Expand Up @@ -439,8 +444,10 @@
* @param i_es_tree red black tree (https://elixir.bootlin.com/linux/v6.4/source/fs/ext4/ext4.h#L1040)
* @return if the file is saved successfully, then true is returned, otherwise false
*/
bool Ext4Filesystem::save_file_by_tree(const std::string& output_file, uint64_t i_size, addr_t i_es_tree)
bool Ext4Filesystem::save_file_by_tree(const std::string& output_file, uint64_t i_size, addr_t i_es_tree, addr_t dtb)
{
this->dtb_ = dtb;

/* prepare config for writing file */
auto config = std::make_unique<write_file_data>();

Expand Down Expand Up @@ -507,6 +514,11 @@
Ext4Filesystem::Ext4Filesystem(drakvuf_t drakvuf, uint64_t extract_size) : BaseFilesystem(drakvuf),
extract_size(extract_size)
{
if (!drakvuf_get_kernel_struct_members_array_rva(drakvuf, linux_offset_names, this->offsets.size(), this->offsets.data()))
{
PRINT_ERROR("[ext4] failed to get some offsets\n");
return;
}
init_super_block();
}

Expand Down
35 changes: 33 additions & 2 deletions src/libfs/ext4/ext4.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,40 @@
namespace libfs
{

enum
{
EXTENT_STATUS_RB_NODE,
EXTENT_STATUS_ES_LBLK,
EXTENT_STATUS_ES_LEN,
EXTENT_STATUS_ES_PBLK,
RB_ROOT_RB_NODE,
RB_NODE_RB_LEFT,
RB_NODE_RB_RIGHT,
RB_NODE___RB_PARENT_COLOR,
__LINUX_EXT4_OFFSET_MAX,
};

static const char* linux_offset_names[__LINUX_EXT4_OFFSET_MAX][2] =
{
[EXTENT_STATUS_RB_NODE] = {"extent_status", "rb_node"},
[EXTENT_STATUS_ES_LBLK] = {"extent_status", "es_lblk"},
[EXTENT_STATUS_ES_LEN] = {"extent_status", "es_len"},
[EXTENT_STATUS_ES_PBLK] = {"extent_status", "es_pblk"},
[RB_NODE___RB_PARENT_COLOR] = {"rb_node", "__rb_parent_color"},
[RB_NODE_RB_RIGHT] = {"rb_node", "rb_right"},
[RB_NODE_RB_LEFT] = {"rb_node", "rb_left"},
[RB_ROOT_RB_NODE] = {"rb_root", "rb_node"},
};

class Ext4Filesystem : public BaseFilesystem
{
private:
/* offsets for parsing kernel structures */
std::array<size_t, __LINUX_EXT4_OFFSET_MAX> offsets;

/* because we can't use directly drakvuf_->kpgd store in this variable */
addr_t dtb_;

/* config provided */
uint64_t extract_size;

Expand Down Expand Up @@ -159,8 +190,8 @@ class Ext4Filesystem : public BaseFilesystem
public:
Ext4Filesystem(drakvuf_t drakvuf, uint64_t extract_size);

bool save_file_by_inode(const std::string& output_file, uint64_t inode_number);
bool save_file_by_tree(const std::string& output_file, uint64_t i_size, addr_t i_es_tree);
bool save_file_by_inode(const std::string& output_file, uint64_t inode_number, addr_t dtb);
bool save_file_by_tree(const std::string& output_file, uint64_t i_size, addr_t i_es_tree, addr_t dtb);
};

}; // end namespace
Expand Down
11 changes: 11 additions & 0 deletions src/libfs/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
libfs_h = include_directories(
'..', '../..',
)
libfs = static_library('fs',
'base.cpp',
'ext4/ext4.cpp',

dependencies : deps,
include_directories : libfs_h,
link_with : libdrakvuf
)
2 changes: 1 addition & 1 deletion src/plugins/fileextractor/linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ std::unique_ptr<linux_fileextractor::extract_result> linux_fileextractor::extrac
umask(S_IWGRP|S_IWOTH);
try
{
if (!ext4->save_file_by_tree(output_filename, result->file_info->filesize, i_es_tree))
if (!ext4->save_file_by_tree(output_filename, result->file_info->filesize, i_es_tree, info->regs->cr3))
result->file_info = {};
}
catch (int)
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ endif

if get_option('plugin-fileextractor')
plugin_sources += 'fileextractor/fileextractor.cpp'
plugin_sources += 'fileextractor/linux.cpp'
plugin_sources += 'fileextractor/win.cpp'

config_h.set('ENABLE_PLUGIN_FILEEXTRACTOR', 1)
Expand Down Expand Up @@ -358,6 +359,6 @@ drakvuf_plugins = static_library('drakvuf_plugins',
sources : plugin_sources,
include_directories : incdir,
dependencies : deps,
link_with : [libdrakvuf, libhook, libusermode],
link_with : [libdrakvuf, libhook, libfs, libusermode],
link_args : hardened_link_args
)
Loading