65.9K
CodeProject 正在变化。 阅读更多。
Home

显示最近的浏览历史记录

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2024年9月29日

CPOL

3分钟阅读

viewsIcon

9646

downloadIcon

260

本文解释了浏览历史记录的存储位置和方式,以及如何获取它。

Github 仓库:https://github.com/securedglobe/browsinghistory

引言

在尝试获取浏览历史记录时,有几个问题

- 它存储在哪里?

- 它更新的频率是多少?

- 它以什么格式存储?

- 访问该信息的最佳方法是什么?

Microsoft Edge

Microsoft Edge 将其历史记录存储在 "<用户配置文件>\AppData\Local\Microsoft\Edge\User Data\Default\History" 中。 它使用名为 urls 的表来存储每次访问,但不包括 InPrivate 模式

从编程的角度来看,如果您找到了用户的配置文件路径并将其存储在 *userProfilePath* 中,则数据库路径将是

userProfilePath + "\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\History"

Chrome

Google Chrome 将其历史记录存储在 "<用户配置文件>\AppData\Local\Google\Chrome\User Data\Default\History" 中。 它使用名为 urls 的表来存储每次访问,但不包括 InPrivate 模式

从编程的角度来看,如果您找到了用户的配置文件路径并将其存储在 *userProfilePath* 中,则数据库路径将是

userProfilePath + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\History"

构建块

为了本文的目的,我使用 Visual Studio 2022 创建了一个控制台应用程序。

数据库

由于浏览历史记录存储在 sqlite3 数据库中,因此我们需要包含 sqlite3 库或源代码文件。

为了能够读取浏览历史记录数据库,我们需要能够处理 sqlite3 数据库。 您可以在 这里 找到关于 sqlite3 的更多信息。 然后我们需要将 sqlite3.csqlite3.h 添加到项目中。

用户配置文件文件夹

所有位置都相对于 c:\users 文件夹中当前 用户配置文件路径。 我们首先需要找到该路径。 这是一个执行此操作的函数

// Get the current user's profile path (e.g., C:\Users\<username>\)
std::wstring GetUserProfilePath()
{
    WCHAR path[MAX_PATH];
    if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path)))
    {
        return std::wstring(path);
    }
    return L"";
}

使用被锁定的数据库

由于数据库可能被锁定,并且如果 EdgeChrome 正在运行,它们将被锁定,因此我们首先需要将它们复制到另一个位置并从那里访问它们。

// Function to copy the locked database to a temporary file for querying
bool CopyDatabaseToTemp(const std::wstring& dbPath, std::wstring& tempDbPath)
{
    wchar_t tempPath[MAX_PATH];
    if (GetTempPathW(MAX_PATH, tempPath) == 0)
    {
        return false;
    }

    wchar_t tempFileName[MAX_PATH];
    if (GetTempFileNameW(tempPath, L"dbcopy", 0, tempFileName) == 0)
    {
        return false;
    }

    tempDbPath = std::wstring(tempFileName);

    try
    {
        std::filesystem::copy_file(dbPath, tempDbPath, std::filesystem::copy_options::overwrite_existing);
        return true;
    }
    catch (const std::filesystem::filesystem_error& e)
    {
        wprintf(L"Failed to copy database: %s\n", ConvertUtf8ToWide(e.what()).c_str());
        return false;
    }
}

处理 Unix 日期和时间

有几种存储日期和时间的方法。

WebKit Epoch

WebKit 时间戳从 1601 年 1 月 1 日(UTC)开始。

Unix Epoch

Unix 时间戳从 1970 年 1 月 1 日(UTC)开始。

在 WebKit Epoch 和 Unix Epoch 之间转换

我们使用以下函数进行转换。ConvertWebKitToUnixTime()

这是它的工作原理

参数

webkitTime:这是一个 int64_t 值,表示自 WebKit epoch(1601 年 1 月 1 日)以来的微秒级时间戳。

返回类型

该函数返回一个 time_t 值,该值表示自 Unix epoch(1970 年 1 月 1 日)以来的秒级 Unix 时间戳。

逻辑

WebKit 时间戳以微秒为单位,而 Unix 时间戳以秒为单位。 通过将 webkitTime 除以 1,000,000,我们将微秒转换为秒。

调整 Epoch 差异
两个 epoch(1601 年 1 月 1 日和 1970 年 1 月 1 日)之间的差异为 369 年。
这种差异转换为 11644473600 秒。
计算中的 - 11644473600LL 将时间戳从 WebKit epoch 调整为 Unix epoch。

最终计算

static_cast<time_t>(webkitTime / 1000000 - 11644473600LL);

它采用以微秒为单位的 WebKit 时间戳,将其转换为秒,然后减去两个 epoch 之间以秒为单位的差异,从而产生正确的 Unix 时间戳。
 

这是函数

// Convert WebKit timestamp (microseconds) to Unix timestamp (seconds)
time_t ConvertWebKitToUnixTime(int64_t webkitTime)
{
    return static_cast<time_t>(webkitTime / 1000000 - 11644473600LL); // Adjusting for WebKit epoch
}

还有一些代码用于以人类可读的格式打印结果,在我的例子中,我以 UTC 格式显示它们。

// Convert time_t to human-readable UTC time string
std::wstring FormatUnixTimeToUTC(time_t unixTime)
{
    struct tm timeInfo;
    if (gmtime_s(&timeInfo, &unixTime) != 0) // Safe version of gmtime
    {
        return L"Invalid time";
    }

    wchar_t buffer[80];
    wcsftime(buffer, sizeof(buffer), L"%Y-%m-%d %H:%M:%S", &timeInfo); // Format time
    return std::wstring(buffer);
}

打印最近的浏览历史记录

在下面的源代码中,我们打印了 Edge 和 Chrome 最近的浏览历史记录。

// Function to read browsing history from a given database path
void PrintUrlsFromDatabase(const std::wstring& dbPath, const time_t currentTime, const time_t timeRangeInSeconds)
{
    std::wstring tempDbPath;
    if (!CopyDatabaseToTemp(dbPath, tempDbPath))
    {
        wprintf(L"Failed to copy database to temporary file: %s\n", dbPath.c_str());
        return;
    }

    sqlite3* db;
    if (sqlite3_open16(tempDbPath.c_str(), &db) != SQLITE_OK)
    {
        wprintf(L"Failed to open database: %s\n", tempDbPath.c_str());
        return;
    }

    // Query to get URLs and visit times
    const char* query = "SELECT u.url, v.visit_time FROM urls u JOIN visits v ON u.id = v.url ORDER BY v.visit_time DESC;";

    sqlite3_stmt* stmt;
    if (sqlite3_prepare_v2(db, query, -1, &stmt, nullptr) != SQLITE_OK)
    {
        wprintf(L"Failed to prepare statement: %S\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return;
    }

    // Execute the query and process the results
    while (sqlite3_step(stmt) == SQLITE_ROW)
    {
        const char* foundUrlUtf8 = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
        int64_t visitTimeWebKit = sqlite3_column_int64(stmt, 1);
        time_t visitTimeUnix = ConvertWebKitToUnixTime(visitTimeWebKit);

        // Check if the URL was visited within the last 10 minutes
        if (difftime(currentTime, visitTimeUnix) <= timeRangeInSeconds)
        {
            // Convert the URL from UTF-8 to wide string using the Windows API function
            std::wstring foundUrl = ConvertUtf8ToWide(foundUrlUtf8);

            // Format the visit time to a human-readable UTC string
            std::wstring visitTimeStr = FormatUnixTimeToUTC(visitTimeUnix);

            wprintf(L"URL: %s, Visit Time (UTC): %s\n", foundUrl.c_str(), visitTimeStr.c_str());
        }
    }

    sqlite3_finalize(stmt);
    sqlite3_close(db);

    // Remove the temporary file after use
    std::filesystem::remove(tempDbPath);
}

 

© . All rights reserved.