blob: 9d450b1fab249c318edc0f0e8e2b0af75e5f7c8a [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_CHROME_CLEANER_PUP_DATA_PUP_DATA_H_
#define CHROME_CHROME_CLEANER_PUP_DATA_PUP_DATA_H_
#include <windows.h>
#include <stdint.h>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "base/files/file_path.h"
#include "chrome/chrome_cleaner/chrome_utils/force_installed_extension.h"
#include "chrome/chrome_cleaner/constants/uws_id.h"
#include "chrome/chrome_cleaner/logging/proto/shared_data.pb.h"
#include "chrome/chrome_cleaner/os/disk_util_types.h"
#include "chrome/chrome_cleaner/os/file_path_set.h"
#include "chrome/chrome_cleaner/os/registry.h"
#include "chrome/chrome_cleaner/proto/shared_pup_enums.pb.h"
#include "chrome/chrome_cleaner/settings/matching_options.h"
namespace chrome_cleaner {
class TestPUPData;
class SignatureMatcherAPI;
class UwSCatalog;
// This class exposes the data necessary to identify and remove Potentially
// Unwanted Programs (PUPs). We use a class instead of the null terminating
// C-Arrays directly so that we can filter based on UwSId's when only a subset
// of the PUPs are needed (e.g., the cleaner module is only interested in the
// PUPs that scanner has found).
class PUPData {
public:
// These flags are applied to whole PUPs.
typedef uint32_t Flags;
enum : Flags {
FLAGS_NONE = 0,
// Used when a PUP should be removed, otherwise it is only reported.
FLAGS_ACTION_REMOVE = 1 << 0,
// Used when a PUP should always be removed post-reboot, even when the
// Restart Manager doesn't think a reboot is needed.
FLAGS_REMOVAL_FORCE_REBOOT = 1 << 1,
// When the UwS is detected activate the detailed system report.
DEPRECATED_FLAGS_DETAILED_REPORT = 1 << 2,
// Used for PUPs that interfere with the prompt, so we don't want to report
// that a cleanup can be done when they are present. This only affects the
// reporter, not the removal tool.
DEPRECATED_FLAGS_INHIBIT_REPORTING = 1 << 3,
// Used for PUPs that interfere with the cleaner, so we don't want to offer
// a removal to the user when they are present. This affects both the
// reporter and the removal tool.
DEPRECATED_FLAGS_INHIBIT_REMOVAL = 1 << 4,
// Behaviour that breaks our UwS policies
// (https://www.google.com/about/unwanted-software-policy.html) has been
// observed and confirmed. PUP's without this flag are still being
// investigated and must not be removed.
FLAGS_STATE_CONFIRMED_UWS = 1 << 5,
// The UwS is considered a malware.
DEPRECATED_FLAGS_CATEGORY_MALWARE = 1 << 6,
// The UwS is hijacking the browser.
DEPRECATED_FLAGS_CATEGORY_BROWSER_HIJACKER = 1 << 7,
// The UwS is injecting an extension.
DEPRECATED_FLAGS_CATEGORY_EXTENSIONS_INJECTOR = 1 << 8,
// The UwS is injecting ads in webpages.
DEPRECATED_FLAGS_CATEGORY_ADS_INJECTOR = 1 << 9,
// The UwS is a scareware.
DEPRECATED_FLAGS_CATEGORY_SCAREWARE = 1 << 10,
// When adding new flags here, please update
// chrome_cleaner/proto/uws_spec_by_version.proto.
};
// A matching rule describes how the content of a disk footprint is matched.
enum DiskMatchRule {
// An invalid matching rule.
DISK_MATCH_INVALID = 0,
// Any file will match. So a folder is always marked as found, as long as it
// contains at least one file.
DISK_MATCH_ANY_FILE,
// Only binary files will match. So a folder is only marked as found if it
// contains at least one binary file (i.e., .exe, .dll, or .sys).
// Note, this can't be used as part of the scanning footprints for a removal
// signature (it can be used with a remove only footprint).
DISK_MATCH_BINARY_FILE,
// When the file path is found, clean containing folder X levels up. E.g.,
// if the path is C:\a\b\c\d.exe, then for DISK_MATCH_FILE_IN_FOLDER_DEPTH_1
// the whole folder c:\a\b\c is deleted, but for
// DISK_MATCH_FILE_IN_FOLDER_DEPTH_3 c:\a is deleted. Only paths to files
// are
// supported now, this won't match a folder within a folder.
DISK_MATCH_FILE_IN_FOLDER_DEPTH_1,
DISK_MATCH_FILE_IN_FOLDER_DEPTH_2,
DISK_MATCH_FILE_IN_FOLDER_DEPTH_3,
DISK_MATCH_FILE_IN_FOLDER_DEPTH_4,
// DISK_MATCH_FILE_IN_FOLDER_END should only be used to indicate the end
// value for file in folder depths.
DISK_MATCH_FILE_IN_FOLDER_END,
};
// This invalid UwSId is used to identify the end of a PUP array.
static const UwSId kInvalidUwSId = 0xFFFFFFFF;
// This id indicates when a footprint isn't associated with any flavour.
static const int kNoFlavour = -1;
// The default number of active files to be willing to remove for various
// UwS installation sizes.
static const size_t kMaxFilesToRemoveLargeUwS = 200;
static const size_t kMaxFilesToRemoveMediumUwS = 100;
static const size_t kMaxFilesToRemoveSmallUwS = 50;
// This id is used when a StaticDiskFootprint doesn't have a CSIDL.
static const int kInvalidCsidl = -1;
// The delimiters used to split registry values.
static const wchar_t kCommaDelimiter[];
static const size_t kCommaDelimiterLength;
static const wchar_t kCommonDelimiters[];
static const size_t kCommonDelimitersLength;
// The character used to escape wild-cards into registry key name patterns.
static const wchar_t kRegistryPatternEscapeCharacter;
// A disk footprint is just a path.
struct StaticDiskFootprint {
// A flavour is used to indicate what footprints should be grouped and
// removed together.
int flavour;
// A CSIDL value identifying the root of |path| if it is a relative path.
// |csidl| is simply ignored when |path| is an absolute path or is null.
int csidl;
// Is only null for the last entry identifying the end of the array.
// It can be either an absolute path, or a relative path, in which case,
// |csidl| is used to get the proper root.
const wchar_t* path;
// Describe the way |path| is used to match the disk footprint.
DiskMatchRule rule;
};
// Information stored in the registry. If |value_name| is empty, the whole
// key at |key_path| is the footprint. If |value_substring| is not empty,
// then |value_name| can not be empty, and only the substring in
// |value_substring| is scanned for, or removed. For example, we may want to
// only remove the PUP path from the AppInit_DLLs registry entry, and leave
// other paths in there. When |value_substring| is null but not |value_name|,
// then the whole value is to be removed.
struct StaticRegistryFootprint {
// A flavour is used to indicate what footprints should be grouped and
// removed together.
int flavour;
// The root to use for this registry footprint. Ignored if |key_path| is
// null.
RegistryRoot registry_root;
// Is only null for the last entry identifying the end of the array.
const wchar_t* key_path;
// Can be null if the whole key is to be removed. Can not be null when
// |value_substring| is not null.
const wchar_t* value_name;
// Can be null if the whole value is to be removed.
const wchar_t* value_substring;
// Describe the way |value_substring| is used to match the content of the
// registry value.
RegistryMatchRule rule;
};
// Represents a path to be matched on disk using regular expressions. There is
// a base path, which is either |csidl|/|path| if |path| is relative, or just
// |path| if it is absolute. Then, the |regex_components| will be used to
// match the remainder of the full path. |regex_components| can use
// backreferences into previous components, as they will be successively
// merged into the regex used to match the final path.
struct MatchPath {
// A CSIDL value identifying the root of |path| if it is a relative path.
// |csidl| is simply ignored when |path| is an absolute path.
int csidl;
// Is only null for the last entry identifying the end of the array.
// Can be either an absolute path, or a relative path, in which case,
// |csidl| is used to get the proper root. Regex matching will start at this
// path (but |path| itself is not a regex).
const wchar_t* path;
// The path components to be matched. A null entry indicates the end of the
// list. These components must not use path separators. As the filesystem
// gets enumerated, the components will be merged into one regex, which
// means that backreferences of groups matched in previous components will
// work.
const wchar_t** regex_components;
};
// This is the expanded version of |StaticRegistryFootprint|. |key_path| and
// |value_name| can no longer contain wildcards.
struct RegistryFootprint {
RegistryFootprint();
RegistryFootprint(const RegKeyPath& key_path,
const base::string16& value_name,
const base::string16& value_substring,
RegistryMatchRule rule);
RegistryFootprint(const RegistryFootprint& other);
~RegistryFootprint();
RegistryFootprint& operator=(const RegistryFootprint& other);
bool operator==(const RegistryFootprint& other) const;
// Must not be empty.
RegKeyPath key_path;
// Can be empty for default value or when value is unused.
base::string16 value_name;
// Can be empty when unused.
base::string16 value_substring;
// Describe the way |value_substring| is used to match the content of the
// registry value.
RegistryMatchRule rule;
};
// Forward declaration.
class PUP;
// A container for PUP data, allows fast access.
typedef std::unordered_map<UwSId, std::unique_ptr<PUP>> PUPDataMap;
// The type of the function to execute the custom matcher. The |pup| structure
// should be filled with the found footprint. The |active_footprint_found|
// must be set to true when non-leftover footprint are found.
// Return false on failure. On failure, the whole scan and remove process is
// interrupted for this PUP.
typedef bool (*CustomMatcher)(const MatchingOptions& options,
const SignatureMatcherAPI* signature_matcher,
PUP* pup,
bool* active_footprint_found);
// All the information and footprints of a single UwS.
//
// The fields of this structure should point to static memory so that they
// remain valid as UwSSignature objects are copied. This structure cannot
// have an explicit constructor because it is commonly aggregate-initialized.
struct UwSSignature {
UwSId id = 0;
Flags flags = 0;
// The name of the UwS. Can be null for anonymous UwS.
const char* name = nullptr;
// An upper limit on the number of files allowed to be deleted for the UwS
// to avoid damage caused by internal errors or malware interactions.
size_t max_files_to_remove = 0;
// The following arrays are terminated by an entry with a null [key_]path.
const StaticDiskFootprint* disk_footprints = nullptr;
const StaticRegistryFootprint* registry_footprints = nullptr;
// Custom matchers to scan UwS with dynamic footprints.
const CustomMatcher* custom_matchers = nullptr;
};
struct FileInfo {
FileInfo();
FileInfo(const FileInfo&);
explicit FileInfo(const std::set<UwS::TraceLocation>& found_in);
~FileInfo();
bool operator==(const FileInfo& other) const;
std::set<UwS::TraceLocation> found_in;
};
// The data that was matched for an UwS during scanning.
//
// PUP objects are created in the engine sandbox process as the engine finds
// UwS, and then copied to the broker process using the Mojo interface in
// interfaces/pup.mojom.
//
// For the legacy scanning engine PUP objects are created directly in the
// sandbox broker process.
class PUP {
public:
typedef FilePathMap<FileInfo> FileInfoMap;
// Default constructor required by Mojo. Initializes signature with a null
// pointer, since no information encoded there needs to be transmitted.
PUP();
explicit PUP(const UwSSignature* signature);
PUP(const PUP& other);
~PUP();
PUP& operator=(const PUP& other);
// Static data for an UwS. This will be empty in the sandbox target
// process. In the broker process the UwSSignature is looked up by UwSId
// and added to the PUP structure.
const UwSSignature& signature() const { return *signature_; }
// Add the given |file_path| to |expanded_disk_footprints|. Return false if
// |file_path| was already in the set.
bool AddDiskFootprint(const base::FilePath& file_path);
void ClearDiskFootprints();
// Mark disk footprint |file_path| as detected in |location|.
void AddDiskFootprintTraceLocation(const base::FilePath& file_path,
UwS::TraceLocation location);
// Add all matching data from |other| to the current pup.
void MergeFrom(const PUP& other);
// The set of expanded disk footprints generated by the scanner. Populated
// in the target process when the engine is sandboxed.
FilePathSet expanded_disk_footprints;
// The set of expanded registry footprints generated by the scanner. Only
// populated by the legacy unsandboxed engine.
std::vector<RegistryFootprint> expanded_registry_footprints;
// The list of expanded scheduled task names generated by the scanner.
// Only populated by the legacy unsandboxed engine.
std::vector<base::string16> expanded_scheduled_tasks;
// Mapping from detected files to where they were found. Populated in the
// target process when the engine is sandboxed.
FileInfoMap disk_footprints_info;
// List of UwE found by the scanner. Populated in the broker process after
// the PUPData is copied from the target process.
std::vector<ForceInstalledExtension> matched_extensions;
protected:
// Allow PUPData to update |signature_| when UpdateCachedUwSForTesting is
// called. The signature pointers can be invalidated when TestPUPData
// creates new test UwS, causing an existing vector of test UwS to be
// resized.
friend PUPData;
const UwSSignature* signature_;
};
PUPData();
~PUPData();
using UwSCatalogs = std::vector<const UwSCatalog*>;
// Loads cached information from the given |uws_catalogs|. Must be called in
// order to use static functions of PUPData. Calling it again will delete all
// cached PUP structures, which will lose their state (detected disk
// footprints, etc.)
static void InitializePUPData(const UwSCatalogs& uws_catalogs);
// Returns the |uws_catalogs| used in the last call to InitializePUPData.
static const UwSCatalogs& GetUwSCatalogs() {
DCHECK(last_uws_catalogs_);
return *last_uws_catalogs_;
}
// Returns true if |uws_id| corresponds to a cached pup.
static bool IsKnownPUP(UwSId uws_id);
// Retrieves information about a given PUP. The ID must be valid otherwise
// the function generates an error. The data returned is still owned by
// PUPData.
static PUP* GetPUP(UwSId uws_id);
// Returns a vector of UwSId's used to iterate over all pups.
static const std::vector<UwSId>* GetUwSIds();
// Return the name of |pup| if it has one, and "???" otherwise.
static const char* GetPUPName(const PUP* pup);
// Return whether |flags| identify a report only PUPs.
static bool HasReportOnlyFlag(Flags flags);
// Return whether |flags| identify PUPs to remove.
static bool HasRemovalFlag(Flags flags);
// Return whether |flags| identify PUPs that needs a reboot.
static bool HasRebootFlag(Flags flags);
// Return whether |flags| identify PUPs that has been confirmed malicious.
static bool HasConfirmedUwSFlag(Flags flags);
// Return whether |uws_id| corresponds to a report only UwS.
static bool IsReportOnlyUwS(UwSId uws_id);
// Return whether |uws_id| corresponds to a removable UwS.
static bool IsRemovable(UwSId uws_id);
// Return whether |uws_id| corresponds to a confirmed malicious UwS.
static bool IsConfirmedUwS(UwSId uws_id);
// Copy PUP ids to |output| when the predicate |chooser| returns true.
static void ChoosePUPs(const std::vector<UwSId>& input_pup_list,
bool (*chooser)(Flags),
std::vector<UwSId>* output);
// Return whether there is at least one PUP with flags matched by |chooser|.
static bool HasFlaggedPUP(const std::vector<UwSId>& input_pup_list,
bool (*chooser)(Flags));
// Convert a RegistryRoot to its corresponding HKEY. If
// |registry_root| is a group policy, and |policy_file| is not null, the path
// to the group policy file is set in |policy_file|.
// Return false on failure.
static bool GetRootKeyFromRegistryRoot(RegistryRoot registry_root,
HKEY* key,
base::FilePath* policy_file);
// Add a dynamic registry footprint to delete the registry key |key_path|.
static void DeleteRegistryKey(const RegKeyPath& key_path, PUP* pup);
// Add a dynamic registry footprint to delete the registry key |key_path|,
// only if the key exists.
static void DeleteRegistryKeyIfPresent(const RegKeyPath& key_path, PUP* pup);
// Add a dynamic registry footprint to delete the registry value
// |key_path|/|value_name|.
static void DeleteRegistryValue(const RegKeyPath& key_path,
const wchar_t* value_name,
PUP* pup);
// Add a dynamic registry footprint to delete the registry value
// |key_path|/|value_name| with |rule|.
static void UpdateRegistryValue(const RegKeyPath& key_path,
const wchar_t* value_name,
const wchar_t* value_substring,
RegistryMatchRule rule,
PUP* pup);
// Add a dynamic footprint to delete a scheduled task.
static void DeleteScheduledTask(const wchar_t* task_name, PUP* pup);
// Return the engine this UwSId belongs to.
static Engine::Name GetEngine(UwSId id);
private:
// So that tests can call UpdateCachedUwSForTesting to add new test UwS.
friend TestPUPData;
// Returns a cached map of PUPs from static arrays and caches it in a map.
// Exposed for testing.
static const PUPData::PUPDataMap* GetAllPUPs();
// Updates all cached UwSSignature objects to point to the current contents
// of the catalogs. Creates new PUP objects and adds them to the cache for
// any UwSSignature's that are not already in the cache. Does not delete any
// cached UwS; for that, call InitializePUPData again.
//
// This can be used when new test UwS is created to add that UwS to the cache
// without invalidating any existing PUP objects that may already contain
// expanded disk footprints.
static void UpdateCachedUwSForTesting();
static void AddPUPToMap(std::unique_ptr<PUPData::PUP> pup);
// Cached PUPData information.
static PUPDataMap* cached_pup_map_;
// Cached UwSId list.
static std::vector<UwSId>* cached_uws_ids_;
static UwSCatalogs* last_uws_catalogs_;
DISALLOW_COPY_AND_ASSIGN(PUPData);
};
// This macro makes it easier to create strings with the
// kRegistryPatternEscapeCharacter.
#define ESCAPE_REGISTRY_STR(str) L"\uFFFF" L##str
const PUPData::StaticDiskFootprint kNoDisk[] = {{}};
const PUPData::StaticRegistryFootprint kNoRegistry[] = {{}};
const PUPData::CustomMatcher kNoCustomMatcher[] = {{}};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_PUP_DATA_PUP_DATA_H_