mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-07 01:55:20 +00:00
Add quicklook_cache to Darwin (#2099)
This commit is contained in:
parent
77273f6500
commit
9c01d4a6e3
140
osquery/tables/system/darwin/quicklook_cache.cpp
Normal file
140
osquery/tables/system/darwin/quicklook_cache.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2014-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
#include <osquery/filesystem.h>
|
||||
#include <osquery/logger.h>
|
||||
#include <osquery/tables.h>
|
||||
|
||||
#include "osquery/core/conversions.h"
|
||||
#include "osquery/sql/sqlite_util.h"
|
||||
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
namespace osquery {
|
||||
namespace tables {
|
||||
|
||||
/**
|
||||
* @brief The Apple reference date offset.
|
||||
*
|
||||
* Some Apple epoch dates use 1/1/2001 UTC as the beginning of time.
|
||||
* Since *most* things in osquery are UNIX epoch, append the 1970's offset.
|
||||
*/
|
||||
const size_t kReferenceDateOffset = 978307200;
|
||||
|
||||
/// Basic GLOB pattern for discovering caches.
|
||||
const std::string kQuicklookPattern =
|
||||
"/private/var/folders/%/%/%/com.apple.QuickLook.thumbnailcache/"
|
||||
"index.sqlite";
|
||||
|
||||
void genQuicklookRow(sqlite3_stmt* stmt, Row& r) {
|
||||
for (int i = 0; i < sqlite3_column_count(stmt); i++) {
|
||||
auto column_name = std::string(sqlite3_column_name(stmt, i));
|
||||
auto column_type = sqlite3_column_type(stmt, i);
|
||||
if (column_type == SQLITE_TEXT) {
|
||||
auto value = sqlite3_column_text(stmt, i);
|
||||
if (value != nullptr) {
|
||||
r[column_name] = std::string((const char*)value);
|
||||
}
|
||||
} else if (column_type == SQLITE_INTEGER) {
|
||||
// Handle INTEGER columns explicitly to handle the date-value offset.
|
||||
auto value = sqlite3_column_int(stmt, i);
|
||||
if (column_name == "last_hit_date") {
|
||||
value += kReferenceDateOffset;
|
||||
}
|
||||
r[column_name] = INTEGER(value);
|
||||
} else if (column_type == SQLITE_BLOB) {
|
||||
// Handle BLOB values explicitly to avoid the default char* termination
|
||||
// for binary-plist data.
|
||||
auto getField = [](const pt::ptree& tree, const std::string& field) {
|
||||
if (field == "mtime" && tree.count(field) > 0) {
|
||||
// Apply a special case for embedded date-value fields.
|
||||
return INTEGER(tree.get<size_t>(field) + kReferenceDateOffset);
|
||||
}
|
||||
return (tree.count(field) > 0) ? tree.get<std::string>(field) : "";
|
||||
};
|
||||
|
||||
if (column_name == "version") {
|
||||
pt::ptree tree;
|
||||
auto version = std::string((const char*)sqlite3_column_blob(stmt, i),
|
||||
sqlite3_column_bytes(stmt, i));
|
||||
if (parsePlistContent(version, tree)) {
|
||||
r["mtime"] = getField(tree, "date");
|
||||
r["size"] = getField(tree, "size");
|
||||
r["label"] = getField(tree, "gen");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transform the folder/file_name into an aggregate path.
|
||||
r["path"] = std::move(r["folder"]) + "/" + std::move(r["file_name"]);
|
||||
r.erase("folder");
|
||||
r.erase("file_name");
|
||||
|
||||
// Transform the encoded fs_id.
|
||||
auto details = osquery::split(r["fs_id"], "=.");
|
||||
if (details.size() == 4) {
|
||||
r["volume_id"] = details[2];
|
||||
r["inode"] = details[3];
|
||||
}
|
||||
}
|
||||
|
||||
QueryData genQuicklookCache(QueryContext& context) {
|
||||
QueryData results;
|
||||
|
||||
// There may be several quick look caches.
|
||||
// Apply a GLOB search since the folder is randomized.
|
||||
std::vector<std::string> databases;
|
||||
if (!resolveFilePattern(kQuicklookPattern, databases)) {
|
||||
return results;
|
||||
}
|
||||
|
||||
for (const auto& index : databases) {
|
||||
sqlite3* db = nullptr;
|
||||
auto rc = sqlite3_open_v2(
|
||||
("file://" + index).c_str(), &db,
|
||||
(SQLITE_OPEN_READONLY | SQLITE_OPEN_PRIVATECACHE | SQLITE_OPEN_NOMUTEX),
|
||||
nullptr);
|
||||
if (rc != SQLITE_OK || db == nullptr) {
|
||||
VLOG(1) << "Cannot open " << index << " read only";
|
||||
if (db != nullptr) {
|
||||
free(db);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// QueryData file_results;
|
||||
std::string query =
|
||||
"SELECT f.*, last_hit_date, hit_count, icon_mode FROM (SELECT rowid, * "
|
||||
"FROM files) f, (SELECT *, max(last_hit_date) AS last_hit_date FROM "
|
||||
"thumbnails GROUP BY file_id) t WHERE t.file_id = rowid;";
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
rc = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr);
|
||||
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||
Row r;
|
||||
genQuicklookRow(stmt, r);
|
||||
|
||||
// For each row added to the results from this database, add the path to
|
||||
// the database, then move into the table's result set.
|
||||
r["cache_path"] = index;
|
||||
results.push_back(r);
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
sqlite3_finalize(stmt);
|
||||
free(db);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
19
specs/darwin/quicklock_cache.table
Normal file
19
specs/darwin/quicklock_cache.table
Normal file
@ -0,0 +1,19 @@
|
||||
table_name("quicklook_cache")
|
||||
description("Files and thumbnails within OS X's Quicklook Cache")
|
||||
schema([
|
||||
Column("path", TEXT, "Path of file"),
|
||||
Column("rowid", INTEGER, "Quicklook file rowid key"),
|
||||
Column("fs_id", TEXT, "Quicklook file fs_id key"),
|
||||
Column("volume_id", INTEGER, "Parsed volume ID from fs_id"),
|
||||
Column("inode", INTEGER, "Parsed file ID (inode) from fs_id"),
|
||||
Column("mtime", INTEGER, "Parsed version date field"),
|
||||
Column("size", INTEGER, "Parsed version size field"),
|
||||
Column("label", TEXT, "Parsed version 'gen' field"),
|
||||
Column("last_hit_date", INTEGER,
|
||||
"Apple date format for last thumbnail cache hit"),
|
||||
Column("hit_count", TEXT, "Number of cache hits on thumbnail"),
|
||||
Column("icon_mode", BIGINT, "Thumbnail icon mode"),
|
||||
Column("cache_path", TEXT, "Path to cache data"),
|
||||
])
|
||||
attributes(cachable=True)
|
||||
implementation("quicklook_cache@genQuicklookCache")
|
@ -1 +1 @@
|
||||
Subproject commit a7c9f15f4bece0ecd1e9d66c9c6983e1750524c9
|
||||
Subproject commit a5d9d9266031e1bbdced2ea268a26d1760e60127
|
Loading…
Reference in New Issue
Block a user