====================
== Alert Overload ==
====================
Tales from a SOC analyst

AceLauncher

AceLauncher

AceLauncher is a Potentially Unwanted Program (PUP) similar to Wave Browser, OneStart, and OneLaunch. It’s a Chromium based browser that creates several tasks, AppData directories, and registry keys to maintain persistence on a device. While not overtly malicious, users will likely want to remove the browser as it does redirect and serve potentially malicious content. This includes functions that link to ManualsLib domains and references to Wave Browser and Recipe Lister. The browser also uses Yahoo’s Hosted Search platform to serve sponsored content driving revenue to the AceLauncher organization.

AceLauncher Removal Guide

This is a simple removal script for AceLauncher installations. While the browser does come with an uninstallation function that does remove the CurrentVersion\Run\ registry item, it does not appear to remove all artifacts.

This script enumerates all users on the device and removes any AceLauncher files in AppData/Local/*, Downloads, HKU:\*\Software\, and HKU\*\Software\Microsoft\Windows\CurrentVersion\run\.

Note: You can pull the History file for the AceLauncher browser for analysis at C:\Users\%USER%\AppData\Local\AceLauncher\User Data\Default\History. This will show all the URL visits and downloads the user made while using the browser. It may be prudent to ensure that all downloads and visited domains are legitimate or policy abiding.

AceLaunch sets itself as the default browser. You likely want to change this back to the primary browser. Removing the files without changing the default browser will cause issues with shell associations for html and other web files.


$FolderName="AceLauncher"

$users = (Get-Item C:\Users\*).Name

Write-Host "Stopping all AceLauncher processes"
Get-Process "*AceLauncher*" | Stop-Process -Force

foreach($user in $users){
    $path = "C:\Users\$user\Downloads"
    $appath = "C:\Users\$user\AppData\Local\"
    
    Write-Host "Removing all AceLauncher Folders in $appath"
    Remove-Item "$appath\$FolderName*" -Recurse -Force 

    Write-Host "Removing AceLauncher Tasks"
    unregister-scheduledtask -TaskName "AceLauncher*" -Confirm:$false

    Write-Host "Removing installers from Downloads"
    Remove-Item "$path\*$FolderName*" -Force

    Remove-ItemProperty -path "registry::hku\*\Software\Microsoft\Windows\CurrentVersion\run\" -name "com.acelauncher.dock" -Force 
    Remove-ItemProperty -path "registry::hku\*\Software\Microsoft\Windows\CurrentVersion\run\" -name "GoogleChromeAutoLaunch_*" -Force 
    Remove-Item -path "registry::hku\*\Software\AceLauncher" -Recurse -Force 
    Remove-Item -path "registry::hku\*\Software\AceLauncherUpdater" -Recurse -Force 
}

Manual Removal

AceLauncher creates directories at the following AppData/Local paths:

  • AceLauncher
  • AceLauncherAutoUpdate
  • AceLauncherDock

Additionally, AceLauncher creates the following scheduled tasks:

  • AceLauncher Autoupdate
  • AceLauncher Statistics
  • AceLauncher Wakeup

AceLauncher registry items:

  • CurrentVersion\run\ com.acelauncher.dock
  • CurrentVersion\run\ GoogleChromeAutoLaunch_
  • Software\AceLauncher
  • Software\AceLauncherUpdater

You may also find that numerous processes are running. All processes should include AceLauncher in the name. Proceseses must be stopped before removal begins.

AceLauncher Internals

This mostly focuses on the Dock application and the extensions. The main browser application and updater were not fully analyzed.

AceLauncher Dock

The AceLauncher browser functions like other common PUPs. It silently launches in the background, creating RunKeys to ensure that it launches on startup. This can be seen in the InstallHelper and OnInstall functions.

public static class InstallHelper
{
    private const int maxRetries = 5;

    private static TimeSpan poll_freq => AceBrowserHelper.poll_freq;

    public static async Task OnInstall()
    {
        if (!File.Exists(ConstValues.BrowserExeFullPath))
        {
            return;
        }
        Util.GenerateNativeManifest();
        LocalState.SetBackgroundMode(false);
        Process proc = new Process
        {
            // Start in bacckground
            StartInfo = new ProcessStartInfo(ConstValues.BrowserExeFullPath, "--from-installer --silent-launch")
            {
                WorkingDirectory = Path.GetDirectoryName(ConstValues.BrowserExeFullPath),
                CreateNoWindow = true,
                UseShellExecute = false,
                RedirectStandardInput = false,
                RedirectStandardOutput = false,
                RedirectStandardError = false,
                WindowStyle = ProcessWindowStyle.Normal
            }
        };
        try
        {
            proc.Start();
            if (!(await AceBrowserHelper.AreExtensionsUnpacked((TimeSpan?)TimeSpan.FromSeconds(7.0))))
            {
                BackgroundTaskBox.Run((Action)async delegate
                {
                    await OnInstall2(proc);
                });
                return;
            }
            proc.Kill();
        }
        catch
        {
        }
        await OnInstall2(proc);
    }

    private static async Task OnInstall2(Process first_proc)
    {
        await AceBrowserHelper.AreExtensionsUnpacked((TimeSpan?)null);
        if (!first_proc.HasExited)
        {
            first_proc.Kill();
        }
        await AceBrowserHelper.CloseAceBrowser();
        if (!AceBrowserHelper.IsAceBrowserRunning)
        {
            AceBrowserHelper.RepairPositioning();
        }
        int i;
        if (File.Exists(ConstValues.BrowserLockFile))
        {
            i = 12;
            while (i > 0 && File.Exists(ConstValues.BrowserLockFile))
            {
                await Task.Delay(poll_freq);
                int num = i - 1;
                i = num;
            }
        }
        bool flag = !string.IsNullOrWhiteSpace(BrowserUpdaterSettings.VerboseLoggingPath);
        string arguments = "--start-maximized" + (flag ? " --enable-logging=stderr --v=1" : string.Empty);
        Process process = new Process
        {
            StartInfo = new ProcessStartInfo(ConstValues.BrowserExeFullPath, arguments)
            {
                // Start with no window
                WorkingDirectory = Path.GetDirectoryName(ConstValues.BrowserExeFullPath),
                CreateNoWindow = true,
                UseShellExecute = false,
                RedirectStandardInput = false,
                RedirectStandardOutput = false,
                RedirectStandardError = flag,
                WindowStyle = ProcessWindowStyle.Maximized
            }
        };
        if (flag)
        {
            process.ErrorDataReceived += delegate(object s, DataReceivedEventArgs e)
            {
                File.AppendAllLines(BrowserUpdaterSettings.VerboseLoggingPath, new string[1] { e.Data });
            };
        }
        process.Start();
        if (flag)
        {
            process.BeginErrorReadLine();
        }
        await AceBrowserHelper.BringToFront(false);
        i = 5;
        while (i > 0)
        {
            if (await Util.CheckExtensionAvailable())
            {
                if (BrowserUpdaterSettings.RunInBackgroundEnabled)
                {
                    LocalState.SetBackgroundMode(true);
                }
                break;
            }
            await Task.Delay(TimeSpan.FromMilliseconds(200 << 5 - i));
            int num = i - 1;
            i = num;
        }
    }
}

...

    private static RegistryKey GetAutoRunRegistryKey()
    {
        // RunKey creation
        return Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", writable: true);
    }

    private static async Task OnInstall()
    {
        AceBrowserHelper.TryLaunchThankYouPage();
        DigestSender.BlockDigest();
        using (RegistryKey registryKey = GetAutoRunRegistryKey())
        {
            registryKey?.SetValue(AutoRunAppName, "\"" + Application.ExecutablePath + "\" " + ConstValues.WakeUpArg);
        }
        Util.AddUnlockHook();
        DigestSender.IncrementMetric($"{(object)(DigestMetrics)3}_{BrowserUpdaterSettings.NewTabStyle}");
        int num = ProfileCopiers.GetProfiles(LocalState.AceLauncherUserFolder).Count();
        DigestSender.IncrementMetric($"{(object)(DigestMetrics)4}_{num}");
        DigestSender.SendDigest((ToolbarNotifyType)1);
        await InstallHelper.OnInstall();
        DigestSender.ForceDigest();
        DigestSender.SendDailyDigest();
    }

The Search bar function of AceLauncher routes through the Yahoo Hosted Search (YHS) platform using the yhs-acelauncher identifier. The browser likely derives revenue by pushing sponsored results to the top of the Yahoo search results using the YHS platform.

public static string GetYahooSearchURL(string query)
    {
        return "https://search.yahoo.com/yhs/search?hspart=ssl&hsimp=yhs-acelauncher&type=" + SearchTypeTag + "&p=" + Uri.EscapeDataString(query);
    }

Bundeled within the browser is a search function that redirects user queries to well known ManualsLib domains. These domains are often infected with malicious advertisements and offer further PUPs for download.

 public ManualDialog()
    {
        BlockInitialFocus();
        Util.StylizeForm((ShapedForm)(object)this, InitializeComponent);
        ((Control)(object)this).BackColor = Color.FromArgb(241, 241, 241);
        ((ContainerControl)this).AutoScaleMode = AutoScaleMode.None;
        iconClose = IconButton.MakeSimpleButton(Icons.close, "Close");
        iconClose.AllowMouseover = false;
        iconClose.Location = phClose.Location;
        iconClose.Anchor = phClose.Anchor;
        ((Control)this).Controls.Remove(phClose);
        IconButton iconButton = iconClose;
        iconButton.Click = (EventHandler)Delegate.Combine(iconButton.Click, (EventHandler)delegate
        {
            ((Form)this).Close();
        });
        ((Control)this).Controls.Add(iconClose);
        siteTable.Controls.Add(MakeLogo(ManualLogo.manuals_lib, "ManualsLib", "https://www.manualslib.com/brand/"), 0, 0);
        siteTable.Controls.Add(MakeLogo(ManualLogo.hifi, "HiFi Engine", "https://www.hifiengine.com/manual-library.shtml"), 0, 1);
        siteTable.Controls.Add(MakeLogo(ManualLogo.free_service, "Free Service Manuals", "http://freeservicemanuals.info/en/"), 0, 2);
        siteTable.Controls.Add(MakeLogo(ManualLogo.audio, "Audioservice Manuals", "https://www.audioservicemanuals.com/"), 0, 3);
        siteTable.Controls.Add(MakeLogo(ManualLogo.manuallz, "Manualzz", "https://manualzz.com/"), 1, 0);
        siteTable.Controls.Add(MakeLogo(ManualLogo.manua_ls, "Manua.ls", "https://www.manua.ls/"), 1, 1);
        siteTable.Controls.Add(MakeLogo(ManualLogo.elektrotanya, "Elektrotanya", "https://elektrotanya.com/keres"), 1, 2);
        siteTable.Controls.Add(MakeLogo(ManualLogo.internet_archive, "Internet Archive", "https://archive.org/search?query=subject%3A%22Manuals%22"), 1, 3);
        brandTable.Controls.Add(MakeBrand(ManualLogo.logo_acer, "Acer", "https://www.manualslib.com/brand/acer/"), 0, 0);
        brandTable.Controls.Add(MakeBrand(ManualLogo.logo_bosch, "Bosch", "https://www.manualslib.com/brand/bosch/"), 1, 0);
        brandTable.Controls.Add(MakeBrand(ManualLogo.logo_cisco, "Cisco", "https://www.manualslib.com/brand/cisco/"), 2, 0);
        brandTable.Controls.Add(MakeBrand(ManualLogo.logo_epson, "Epson", "https://www.manualslib.com/brand/epson/"), 3, 0);
        brandTable.Controls.Add(MakeBrand(ManualLogo.logo_ge, "GE", "https://www.manualslib.com/brand/ge/"), 0, 1);
        brandTable.Controls.Add(MakeBrand(ManualLogo.logo_honeywell, "Honeywell", "https://www.manualslib.com/brand/honeywell/"), 1, 1);
        brandTable.Controls.Add(MakeBrand(ManualLogo.logo_kenmore, "Kenmore", "https://www.manualslib.com/brand/kenmore/"), 2, 1);
        brandTable.Controls.Add(MakeBrand(ManualLogo.logo_lg, "LG", "https://www.manualslib.com/brand/lg/"), 3, 1);
        brandTable.Controls.Add(MakeBrand(ManualLogo.logo_samsung, "Samsung", "https://www.manualslib.com/brand/samsung/"), 0, 2);
        brandTable.Controls.Add(MakeBrand(ManualLogo.logo_sony, "Sony", "https://www.manualslib.com/brand/sony/"), 1, 2);
        brandTable.Controls.Add(MakeBrand(ManualLogo.logo_whirlpool, "Whirlpool", "https://www.manualslib.com/brand/whirlpool/"), 2, 2);
        FontLoader.ProcessFonts(((Control)this).Controls, lblLinks, lblTitle, lblTitleBrands);
        brandTable.Controls.Add(MakeMore(), 3, 2);
        txtBrand.PlaceHolderText = "Type brand name";
        txtModel.PlaceHolderText = "Type model name";
        ((Control)this).MouseEnter += refreshHighlight;
        ((Control)this).MouseLeave += refreshHighlight;
        siteTable.MouseEnter += refreshHighlight;
        siteTable.MouseLeave += refreshHighlight;
        ((Control)this).Click += delegate(object sender, EventArgs args)
        {
            txtBrand.SearchBox_Leave(sender, args);
            txtModel.SearchBox_Leave(sender, args);
            FocusTrap.Focus();
        };
        Util.StylizeButton(btnSearch);
        txtBrand.KeyUp += txtSearch_KeyUp;
        txtModel.KeyUp += txtSearch_KeyUp;
        txtBrand.Text = BrandText;
        txtModel.Text = ModelText;
        ManualDialog_SizeChanged(null, null);
    }

The AceLauncher browser comes with a custom DLL named AceLauncherShared.dll. This DLL has a function named SendDigest that sends the following parameters to hxxps[://]analytics[.]acelauncher[.]com/partialdigest

Parameter Usage
Notify Type
User Key Identifier unique to installation
Toolbar Version The version of the AceLauncherDock
Default Search Provider The default search provider for the AceLauncher browser
Dock Hidden/Visible Status of the AceLauncherDock
Has Shift2/No Shift2 Queries SOFTWARE\\Clients\\StartMenuInternet for Shift Browser
Extension Status Status of the AceLauncher browser extension
Has Wave/No Wave Queries Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\WaveBrowser for Wave Browser
AceLaunch Browser Status Queries Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\AceLauncher and Software\\AceLauncherUpdater\\BrowserSettings for installation and wakeup status
public void SendDigest(ToolbarNotifyType notifyType)
    {
        try
        {
            string userKey = UserKey;
            DigestParameter param = new DigestParameter
            {
                NotifyType = notifyType,
                UserKey = userKey,
                ToolbarVersion = (Version ?? "")
            };
            if (notifyType == ToolbarNotifyType.DailyDigest && DigestVersion == DigestVersion.Dock)
            {
                Dictionary<string, int> dictionary = DockSettings.Metrics ?? new Dictionary<string, int>();
                string searchProvider = GetSearchProvider();
                if (!string.IsNullOrWhiteSpace(searchProvider))
                {
                    dictionary["defaultsearch_" + searchProvider] = 1;
                }
                else
                {
                    dictionary["defaultsearch_unknown"] = 1;
                }
                if (DockSettings.ClosedByUser)
                {
                    dictionary["dockhidden"] = 1;
                }
                else
                {
                    dictionary["dockvisible"] = 1;
                }
                dictionary[SharedUtil.HasShift() ? "hasshift2" : "noshift2"] = 1;
                if (!DockSettings.ExtensionConnected.HasValue)
                {
                    dictionary["ExtensionConnection_Unknown"] = 1;
                }
                else if (DockSettings.ExtensionConnected == true)
                {
                    dictionary["ExtensionConnection_Connected"] = 1;
                }
                else if (DockSettings.ExtensionConnected == false)
                {
                    dictionary["ExtensionConnection_DisConnected"] = 1;
                }
                using (RegistryKey registryKey = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\WaveBrowser"))
                {
                    dictionary[(registryKey != null) ? "haswave" : "nowave"] = 1;
                }
                bool flag;
                using (RegistryKey registryKey2 = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\AceLauncher"))
                {
                    flag = registryKey2 != null;
                }
                bool flag2;
                using (RegistryKey registryKey3 = Registry.CurrentUser.OpenSubKey("Software\\AceLauncherUpdater\\BrowserSettings"))
                {
                    flag2 = registryKey3 != null && bool.TrueString.Equals(registryKey3.GetValue("wakeup")?.ToString(), StringComparison.InvariantCultureIgnoreCase);
                }
                if (!flag)
                {
                    dictionary["browseruninstalled"] = 1;
                }
                else if (flag2)
                {
                    dictionary["browserwakeup"] = 1;
                }
                else
                {
                    dictionary["browsersleep"] = 1;
                }
                string metrics = ((dictionary == null || !dictionary.Any()) ? null : string.Join(",", dictionary.Select((KeyValuePair<string, int> x) => $"{x.Key}.{x.Value}")));
                param.Metrics = metrics;
                DockSettings.Metrics = new Dictionary<string, int>();
            }
            BackgroundTaskBox.Run(async delegate
            {
                string text = SendDigest(param);
                if (!string.IsNullOrWhiteSpace(text))
                {
                    UserKey = text;
                }
                await SendUserKey(UserKey);
            });
        }
        catch
        {
        }
    }

    private static string SendDigest(DigestParameter parameter)
    {
        using WebClient webClient = new WebClient();
        webClient.Headers[HttpRequestHeader.ContentType] = "application/json";
        try
        {
            string text = webClient.UploadString("https://" + AnalyticsDomain + "/partialdigest/", JsonConvert.SerializeObject((object)parameter));
            if (!string.IsNullOrWhiteSpace(text))
            {
                return JsonConvert.DeserializeObject<DigestResponse>(text)?.UserKey;
            }
            return null;
        }
        catch (WebException ex)
        {
            using StreamReader streamReader = new StreamReader(ex.Response.GetResponseStream());
            streamReader.ReadToEnd();
            return null;
        }
    }
}

Extensions

Ace Browser Shield Extension

hpoaphfloccfinlenemcmjbimnlagkgd manifest.json

{
  "action": {
    "default_icon": {
      "16": "img/16x16_adblocker_icon_blue.png",
      "32": "img/32x32_adblocker_icon_blue.png",
      "64": "img/64x64_adblocker_icon_blue.png"
    }
  },
  "background": {
    "service_worker": "/js/aceblock.js",
    "type": "module"
  },
  "description": "Browser Shield",
  "icons": {
    "16": "img/16x16_adblocker_icon_blue.png",
    "32": "img/32x32_adblocker_icon_blue.png",
    "64": "img/64x64_adblocker_icon_blue.png",
    "128": "img/128x128_adblocker_icon_blue.png"
  },
  "manifest_version": 3,
  "minimum_chrome_version": "119.0",
  "name": "Browser Shield",
  "permissions": [
    "declarativeNetRequest",
    "tabs",
    "webRequest",
    "scripting",
    "storage",
    "unlimitedStorage",
    "downloads"
  ],
  "host_permissions": [
    "*://*/*"
  ],
  "web_accessible_resources": [
    {
      "resources": [
        "img/*"
      ],
      "matches": [ "<all_urls>" ]
    }
  ],
  "declarative_net_request": {
    "rule_resources": [
      {
        "id": "ruleset",
        "enabled": true,
        "path": "ruleset.json"
      }
    ]
  },
  "short_name": "BrShld",
  "version": "2025.2.010.0"
}

The Browser Shield extension POSTs the UserKey value and other parameters to to the hxxps[://]analytics[.]acelauncher[.]com/browsershield endpoint.

            chrome.storage.local.get(["InstallDate", "RecentBlocks", "UserKey", "Type"]).then(async function (x) {
              if (x) {
                if (x.RecentBlocks && typeof x.RecentBlocks[f] < "u") {
                  l(x.RecentBlocks[f]);
                  return;
                } else if (E[f]) {
                  E[f].push(l);
                  return;
                } else E[f] = [l];
                var k = {ToolbarVersion: st, UserKey: "", installDate: "", hash: f, Type: ""};
                if (x.InstallDate) {
                  var A = new Date(x.InstallDate);
                  k.installDate = A.getMonth() + 1 + "/" + A.getDate() + "/" + A.getFullYear();
                }
                x.UserKey || await chrome.runtime.sendMessage(at, {action: "userkey"}).then(function (w) {
                  w && (k.UserKey = w, chrome.storage.local.set({UserKey: w}));
                }), x.Type || await chrome.runtime.sendMessage(at, {action: "getSearchTypeTag"}).then(function (w) {
                  w && (k.Type = w, chrome.storage.local.set({Type: w}));
                }), fetch("https://analytics.acelauncher.com/browsershield", {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(k)}).then(w => w.ok ? w.json() : null).then(w => {
                  x || (x = {}), x.RecentBlocks || (x.RecentBlocks = {}), x.RecentBlocks[f] = w, chrome.storage.local.set({RecentBlocks: x.RecentBlocks});
                  for (let T = 0; T < E[f].length; T++) E[f][T](w);
                  delete E[f];
                });
              }
            });

It also POSTs data to hxxps[://]tbwsjs[.]befrugal[.]com. This domain hosts the SOAP API used by the extension. They’ve got a nice API tester built in if you want to play around with the different endpoints. Most of them require a UserKey and Toolbar version to operate.

        l = "toolbarversion=&installDate=" + (c.getMonth() + 1) + "/" + c.getDate() + "/" + c.getFullYear(), fetch("https://tbwsjs.befrugal.com/wssimple.asmx/GetBrowserShieldSettings", {method: "POST", headers: {"Content-Type": "application/x-www-form-urlencoded"}, body: l}).then(m => m.ok ? m.json() : null).then(m => {

alt text

alt text

Performance Extension

oobincnjpkooeennnochfgblilnhldfg manifest.json

{
	//https://developer.chrome.com/docs/extensions/reference/manifest/chrome-settings-override
	"manifest_version": 3,
	"name": "Performance",
	"version": "2025.2.51",
	"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
	"author": "Sunstream Labs",
	"icons": {
		"16": "icon16.png",
		"32": "icon32.png",
		"48": "icon48.png",
		"128": "icon128.png"
	},
	"background": {
		"service_worker": "background.js"
	},
	"externally_connectable": {
		"ids": [ "fcadlabnobpfhnnlmmheomecljlcnodb", "hpoaphfloccfinlenemcmjbimnlagkgd" ],
		"matches": [
			"*://*.acelauncher.com/*",
			"*://*.onestartpage.com/*"
		]
	},
	"web_accessible_resources": [
		{
			"resources": [ "_favicon/*" ],
			"matches": [
				"*://*.acelauncher.com/*",
				"*://*.onestartpage.com/*"
			]
		}
	],
	"host_permissions": [
		"<all_urls>"
	],
	"permissions": [
		"background",
		"management",
		"topSites",
		"favicon",
		"tabs",
		"storage",
		"nativeMessaging",
		"webNavigation",
		"scripting",
		"alarms"
	]
}

This extension tracks and logs search engines used in the browser.

// storing metrics 
var m = async function (e) {
    await R(`BrowserSearch_${e}`);
  }, R = async function (e) {
    var t = await p.get() || {}, a = t[e] || 0;
    ++a, t[e] = a, await p.set(t);
  }, j = async function (e, t) {
    var a = await p.get() || {};
    a[e] = t, await p.set(a);
  }, O = async function () {
    var e = await p.get() || {};
    return Object.entries(e).map(t => `${t[0]}.${t[1]}`).join(",");
  }, W = async function () {
    await p.set({});
  };

//   Tracking search engine use
  chrome.webNavigation.onBeforeNavigate.addListener(function (e) {
    i && (i.postMessage({MessageType: 8, Message: "searchext"}), m("searchext"));
  }, {url: [{hostContains: "www.searchext.com"}]});
  chrome.webNavigation.onBeforeNavigate.addListener(function (e) {
    if (i) {
      var t = new URLSearchParams(URL.parse(e.url).searchParams);
      if (!parseInt(t.get("b")) && t.get("p")) {
        var a = (t.get("hsimp") || "").toLowerCase().endsWith("acelauncher") ? "yahoo-ace" : "yahoo-base";
        i.postMessage({MessageType: 8, Message: a}), m(a.replace("-", ""));
      }
    }
  }, {url: [{hostContains: "search.yahoo.com"}]});
  chrome.webNavigation.onBeforeNavigate.addListener(function (e) {
    if (i) {
      var t = new URLSearchParams(URL.parse(e.url).searchParams);
      !parseInt(t.get("start")) && t.get("q") && (i.postMessage({MessageType: 8, Message: "google"}), m("google"));
    }
  }, {url: [{hostContains: "www.google.com"}]});
  chrome.webNavigation.onBeforeNavigate.addListener(function (e) {
    if (i) {
      var t = new URLSearchParams(URL.parse(e.url).searchParams);
      !t.get("rdr") && !t.get("first") && t.get("q") && (i.postMessage({MessageType: 8, Message: "bing"}), m("bing"));
    }
  }, {url: [{hostContains: "www.bing.com"}]});
  chrome.webNavigation.onBeforeNavigate.addListener(function (e) {
    if (i) {
      var t = new URLSearchParams(URL.parse(e.url).searchParams);
      t.get("q") && (i.postMessage({MessageType: 8, Message: "duckduckgo"}), m("duckduckgo"));
    }
  }, {url: [{hostContains: "duckduckgo.com"}]});

It collects the search engine metrics and several other components and POSTs the data to digest endpoint hxxps[://]analytics[.]acelauncher[.]com/partialdigest/.

  var S = f("BrowserVersion"), U = f("DigestDate"), C = f("UserKey"), I = f("launchCount"), V = f("NewTabStyle"), x = f("SearchTypeTag"), p = f("ActivityMetrics");
  (async function () {
    var t = await S.get();
    if (!t) {
      var a = navigator.userAgent.match(/Chrom(?:e|ium)\/([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/);
      a && a.length == 2 && await S.set(a[1]);
    }
  }());
  var E = async function () {
    var e = await S.get();
    return `AceBrowserW-${e}`;
  }, D = "BF-Digest", K = async function () {
    var e = await C.get(), t = await E(), a = await U.get(), n = (new Date).toLocaleDateString();
    if (!(a && a === n)) {
      await U.set(n);
      var u = "hpoaphfloccfinlenemcmjbimnlagkgd", s = await new Promise(function (l) {
        chrome.runtime.sendMessage(u, {Request: "BlockedCount"}).then(function (h) {
          l(h || 0);
        }, function () {
          l(0);
        });
      });
      j("ShieldWarningCount", s);

    //   Sending NotifyType, UserKey, ToolbarVersion, and the Search Engine metrics to the partialdigest endpoint
      var c = {NotifyType: 5, UserKey: e, ToolbarVersion: t, Metrics: await O()};
      await W();
      var o = await fetch("https://analytics.acelauncher.com/partialdigest/", {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(c)}), w = await o.json();
      w && w.userKey && await C.set(w.userKey);
    }
  };

IOCs

Indicator Type Comments
updates[.]acelauncher[.]com Domain Updater Domain
updates-cdn[.]acelauncher[.]com Domain Updater Domain
analytics[.]acelauncher[.]com Domain POST/Collection Endpoint
tbwsjs[.]befrugal[.]com Domain SOAP API
hpoaphfloccfinlenemcmjbimnlagkgd Chrome Extension Ace Browser Shield Extension
oobincnjpkooeennnochfgblilnhldfg Chrome Extension Performance Extension
1C158184198B70CE18DB327F82109F0B4C827794FB9D973F158EB5EB077EF881 SHA256 Hash Ace Browser Shield Extension
0E787942719765C2711988E59F440678E8D728554168DA7E30BD5F8C9BCF8E5C SHA256 Hash Performance Extension
1606A97D45D3F20FAC0E5141C488621EF29E149707385E7104F3EA5749A78E0F SHA256 Hash AceLauncher Installer
EC0E19CC51CAB88A0A1217DB2AF674E503E5DB9F9208D580ABCF92D29F75072F SHA256 Hash AceLauncher.exe (Main)
0F69E90FF9267680DB9BEC8F12FEFC0359FC126C6BAE1741298F72FBA0FA968A SHA256 Hash chrome_proxy.exe
AA5ED7D711AADDCCB616F9C7A1A2E7F4383CE5103539683BE27D442E604AA55D SHA256 Hash AceLauncherAutoUpdate.exe
F9EE1BBB68BD268F20A90D696E027869DDE1CBDAC30C734CAC7FFF1FA41CA7D6 SHA256 Hash Update.exe (AutoUpdate)
7FAA96AB5EB320FE89E2D437FF5F5E1EA3C62BC89219D998900F8EE678675FEC SHA256 Hash AceLauncher.exe (Dock)
F9EE1BBB68BD268F20A90D696E027869DDE1CBDAC30C734CAC7FFF1FA41CA7D6 SHA256 Hash Update.exe (Dock)
44DBDCBDF3197559D81DEA5BD1011210456E3B7396E487C4EC66FE22753E78EC SHA256 Hash AceLauncherShared.dll