Skip to content

Commit

Permalink
Add locale spoofing #16 beta.8
Browse files Browse the repository at this point in the history
Spoof the Intl API, headers, and system locale values.
Added the following properties:
- locale:language
- locale:region
- locale:script
  • Loading branch information
daijro committed Sep 28, 2024
1 parent 809dc52 commit 5263cb6
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 24 deletions.
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ Camoufox will automatically add the following default fonts associated your spoo
**Notes**:

- **navigator.webdriver** is set to false at all times.
- `navigator.language` & `navigator.languages` will fall back to the `locale:language`/`locale:region` values if not set.
- When spoofing Chrome fingerprints, the following may leak:
- navigator.userAgentData missing.
- navigator.deviceMemory missing.
Expand Down Expand Up @@ -264,21 +265,26 @@ Camoufox can override the following network headers:

<details>
<summary>
Geolocation
Geolocation & Intl
</summary>

| Property | Status | Description |
| --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| geolocation:latitude || Latitude to use. Requires `geolocation:longitude` to be set as well. |
| geolocation:longitude || Longitude to use. Requires `geolocation:longitude` to be set as well. |
| geolocation:accuracy || Accuracy in meters. This will be calculated automatically using the decminal percision of `geolocation:latitude` & `geolocation:longitude` if not set. |
| timezone || Set a custom TZ timezone (e.g. "America/Chicago"). This will also change `Date()` to return the local time. |
| Property | Status | Description | Required Keys |
| --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- |
| geolocation:latitude || Latitude to use. | `geolocation:longitude` |
| geolocation:longitude || Longitude to use. | `geolocation:latitude` |
| geolocation:accuracy || Accuracy in meters. This will be calculated automatically using the decminal percision of `geolocation:latitude` & `geolocation:longitude` if not set. | |
| timezone || Set a custom TZ timezone (e.g. "America/Chicago"). This will also change `Date()` to return the local time. | |
| locale:language || Spoof the Intl API, headers, and system language (e.g. "en") | `locale:region` |
| locale:region || Spoof the Intl API, headers, and system region (e.g. "US"). | `locale:language` |
| locale:script || Set a custom script (e.g. "Latn"). Will be set automatically if not specified. | |
The **Required Keys** are keys that must also be set for the property to work.
**Notes:**
- Setting `geolocation:latitude` & `geolocation:longitude` will automatically accept Location permission prompts.
- Location permission prompts will be accepted automatically if `geolocation:latitude` and `geolocation:longitude` are set.
- `timezone` **must** be set to a valid TZ identifier. See [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for a list of valid timezones.
- To change your locale, set the `intl.accept_languages` preference.
- `locale:language` & `locale:region` **must** be set to valid locale values. See [here](https://simplelocalize.io/data/locales/) for a list of valid locale-region values.
</details>
Expand Down Expand Up @@ -406,7 +412,7 @@ Miscellaneous (battery status, etc)
- Support for spoofing both inner and outer window viewport sizes
- Network headers (Accept-Languages and User-Agent) are spoofed to match the navigator properties
- WebRTC IP spoofing at the protocol level
- Geolocation & timezone spoofing
- Geolocation, timezone, and locale spoofing
- etc.
#### Anti font fingerprinting
Expand Down
42 changes: 30 additions & 12 deletions patches/chromeutil.patch
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp
index 6833d2227f..d3d2ef088d 100644
index 6833d2227f..0b2ea99615 100644
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -5,6 +5,7 @@
Expand All @@ -10,13 +10,12 @@ index 6833d2227f..d3d2ef088d 100644

#include "JSOracleParent.h"
#include "js/CallAndConstruct.h" // JS::Call
@@ -2068,6 +2069,25 @@ bool ChromeUtils::IsDarkBackground(GlobalObject&, Element& aElement) {
@@ -2068,6 +2069,24 @@ bool ChromeUtils::IsDarkBackground(GlobalObject&, Element& aElement) {
return nsNativeTheme::IsDarkBackground(f);
}

+/* static */
+void ChromeUtils::CamouDebug(GlobalObject& aGlobal,
+ const nsAString& aVarName) {
+void ChromeUtils::CamouDebug(GlobalObject& aGlobal, const nsAString& aVarName) {
+ if (auto value = MaskConfig::GetBool("debug");
+ value.has_value() && !value.value()) {
+ return;
Expand All @@ -36,13 +35,13 @@ index 6833d2227f..d3d2ef088d 100644
double ChromeUtils::DateNow(GlobalObject&) { return JS_Now() / 1000.0; }

/* static */
@@ -2094,6 +2114,28 @@ void ChromeUtils::GetAllPossibleUtilityActorNames(GlobalObject& aGlobal,
@@ -2094,6 +2113,39 @@ void ChromeUtils::GetAllPossibleUtilityActorNames(GlobalObject& aGlobal,
}
}

+/* static */
+int32_t ChromeUtils::CamouGetInt(GlobalObject& aGlobal,
+ const nsAString& aVarName) {
+ const nsAString& aVarName) {
+ NS_ConvertUTF16toUTF8 utf8VarName(aVarName);
+ if (auto value = MaskConfig::GetInt32(utf8VarName.get())) {
+ return value.value();
Expand All @@ -52,21 +51,32 @@ index 6833d2227f..d3d2ef088d 100644
+
+/* static */
+double ChromeUtils::CamouGetDouble(GlobalObject& aGlobal,
+ const nsAString& aVarName,
+ double aDefaultValue) {
+ const nsAString& aVarName,
+ double aDefaultValue) {
+ NS_ConvertUTF16toUTF8 utf8VarName(aVarName);
+ if (auto value = MaskConfig::GetDouble(utf8VarName.get())) {
+ return value.value();
+ }
+ return aDefaultValue;
+}
+
+/* static */
+void ChromeUtils::CamouGetString(GlobalObject& aGlobal,
+ const nsAString& aVarName,
+ nsAString& aRetVal) {
+ NS_ConvertUTF16toUTF8 utf8VarName(aVarName);
+ if (auto value = MaskConfig::GetString(utf8VarName.get())) {
+ aRetVal.Assign(NS_ConvertUTF8toUTF16(value.value()));
+ } else {
+ aRetVal.Truncate();
+ }
+}
+
/* static */
bool ChromeUtils::ShouldResistFingerprinting(
GlobalObject& aGlobal, JSRFPTarget aTarget,
diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h
index 0150c59670..d297ab5788 100644
index 0150c59670..e3d203ed1a 100644
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -301,6 +301,10 @@ class ChromeUtils {
Expand All @@ -80,20 +90,23 @@ index 0150c59670..d297ab5788 100644
static double DateNow(GlobalObject&);

static void EnsureJSOracleStarted(GlobalObject&);
@@ -310,6 +314,11 @@ class ChromeUtils {
@@ -310,6 +314,14 @@ class ChromeUtils {
static void GetAllPossibleUtilityActorNames(GlobalObject& aGlobal,
nsTArray<nsCString>& aNames);

+ static int32_t CamouGetInt(GlobalObject& aGlobal, const nsAString& aVarName);
+
+ static double CamouGetDouble(GlobalObject& aGlobal, const nsAString& aVarName,
+ double aDefaultValue);
+
+ static void CamouGetString(GlobalObject& aGlobal, const nsAString& aVarName,
+ nsAString& aRetVal);
+
static bool ShouldResistFingerprinting(
GlobalObject& aGlobal, JSRFPTarget aTarget,
const Nullable<uint64_t>& aOverriddenFingerprintingSettings);
diff --git a/dom/chrome-webidl/ChromeUtils.webidl b/dom/chrome-webidl/ChromeUtils.webidl
index bf196f039d..994b5803df 100644
index bf196f039d..04e0cdcabd 100644
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -746,6 +746,13 @@ partial namespace ChromeUtils {
Expand All @@ -110,7 +123,7 @@ index bf196f039d..994b5803df 100644
/**
* Starts the JSOracle process for ORB JavaScript validation, if it hasn't started already.
*/
@@ -757,6 +764,16 @@ partial namespace ChromeUtils {
@@ -757,6 +764,21 @@ partial namespace ChromeUtils {
[ChromeOnly]
readonly attribute unsigned long aliveUtilityProcesses;

Expand All @@ -123,6 +136,11 @@ index bf196f039d..994b5803df 100644
+ * Get a double value from Camoufox MaskConfig.
+ */
+ double camouGetDouble(DOMString varName, double defaultValue);
+
+ /**
+ * Get a string value from Camoufox MaskConfig.
+ */
+ DOMString camouGetString(DOMString varName);
+
/**
* Get a list of all possible Utility process Actor Names ; mostly useful to
Expand Down
186 changes: 186 additions & 0 deletions patches/locale-spoofing.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
diff --git a/browser/base/content/browser-init.js b/browser/base/content/browser-init.js
index 16f4dd7d42..63c9c39741 100644
--- a/browser/base/content/browser-init.js
+++ b/browser/base/content/browser-init.js
@@ -109,6 +109,17 @@ var gBrowserInit = {
window.TabBarVisibility.update();
TabsInTitlebar.init();

+ let camouLocale;
+ let language = ChromeUtils.camouGetString("locale:language");
+ let region = ChromeUtils.camouGetString("locale:region");
+ if (language && region) {
+ camouLocale = language + "-" + region + ", " + language;
+ } else {
+ camouLocale = ChromeUtils.camouGetString("navigator.language");
+ }
+ if (camouLocale)
+ Services.prefs.setCharPref("intl.accept_languages", camouLocale);
+
new LightweightThemeConsumer(document);

if (
diff --git a/intl/components/src/Locale.cpp b/intl/components/src/Locale.cpp
index 9a043518cf..a8d3733212 100644
--- a/intl/components/src/Locale.cpp
+++ b/intl/components/src/Locale.cpp
@@ -24,11 +24,56 @@

#include "unicode/uloc.h"
#include "unicode/utypes.h"
+#include "MaskConfig.hpp"

namespace mozilla::intl {

using namespace intl::LanguageTagLimits;

+
+// Helper methods for header file
+
+const char* Locale::GetCamouLanguage() {
+ if (auto lang = MaskConfig::GetString("locale:language"))
+ return lang.value().c_str();
+ return nullptr;
+}
+
+const char* Locale::GetCamouRegion() {
+ if (auto region = MaskConfig::GetString("locale:region"))
+ return region.value().c_str();
+ return nullptr;
+}
+
+const char* Locale::GetCamouScript() {
+ if (auto script = MaskConfig::GetString("locale:script"))
+ return script.value().c_str();
+ return nullptr;
+}
+
+// Publicly exposed methods
+
+const char* Locale::GetCamouLocale() {
+ static std::string locale;
+
+ if (auto lang = MaskConfig::GetString("locale:language"))
+ if (auto region = MaskConfig::GetString("locale:region"))
+ locale = (lang.value() + "-" + region.value());
+ if (auto value = MaskConfig::GetString("navigator.language"))
+ locale = value.value();
+ if (locale.empty())
+ return nullptr;
+ return locale.c_str();
+}
+
+const char* Locale::GetDefaultLocale() {
+ // In Camoufox, GetDefaultLocale is defined outside the header
+ // to access MaskConfig.
+ if (auto value = GetCamouLocale())
+ return value;
+ return uloc_getDefault();
+}
+
template <typename CharT>
bool IsStructurallyValidLanguageTag(Span<const CharT> aLanguage) {
// unicode_language_subtag = alpha{2,3} | alpha{5,8};
diff --git a/intl/components/src/Locale.h b/intl/components/src/Locale.h
index 1f4e06f543..448486a479 100644
--- a/intl/components/src/Locale.h
+++ b/intl/components/src/Locale.h
@@ -190,8 +190,11 @@ using UniqueChars = UniquePtr<char[]>;
*/
class MOZ_STACK_CLASS Locale final {
LanguageSubtag mLanguage = {};
+ mutable LanguageSubtag mCamouLanguage = {};
ScriptSubtag mScript = {};
+ mutable ScriptSubtag mCamouScript = {};
RegionSubtag mRegion = {};
+ mutable RegionSubtag mCamouRegion = {};

using VariantsVector = Vector<UniqueChars, 2>;
using ExtensionsVector = Vector<UniqueChars, 2>;
@@ -314,9 +317,28 @@ class MOZ_STACK_CLASS Locale final {
}
};

- const LanguageSubtag& Language() const { return mLanguage; }
- const ScriptSubtag& Script() const { return mScript; }
- const RegionSubtag& Region() const { return mRegion; }
+ const LanguageSubtag& Language() const {
+ if (const char* lang = GetCamouLanguage()) {
+ mCamouLanguage.Set(mozilla::Span<const char>(lang, strlen(lang)));
+ return mCamouLanguage;
+ }
+ return mLanguage;
+ }
+ const ScriptSubtag& Script() const {
+ if (const char* script = GetCamouScript()) {
+ mCamouScript.Set(mozilla::Span<const char>(script, strlen(script)));
+ return mCamouScript;
+ }
+ return mScript;
+ }
+ const RegionSubtag& Region() const {
+ if (const char* region = GetCamouRegion()) {
+ mCamouRegion.Set(mozilla::Span<const char>(region, strlen(region)));
+ return mCamouRegion;
+ }
+ return mRegion;
+ }
+
auto Variants() const { return SubtagEnumeration(mVariants); }
auto Extensions() const { return SubtagEnumeration(mExtensions); }
Maybe<Span<const char>> PrivateUse() const {
@@ -483,7 +505,15 @@ class MOZ_STACK_CLASS Locale final {
*
* Also see <https://unicode-org.github.io/icu/userguide/locale>.
*/
- static const char* GetDefaultLocale() { return uloc_getDefault(); }
+ static const char* GetDefaultLocale();
+
+ static const char* GetCamouLocale();
+
+ static const char* GetCamouLanguage();
+
+ static const char* GetCamouRegion();
+
+ static const char* GetCamouScript();

/**
* Returns an iterator over all supported locales.
diff --git a/intl/locale/OSPreferences.cpp b/intl/locale/OSPreferences.cpp
index b87924b61a..2eaa960c04 100644
--- a/intl/locale/OSPreferences.cpp
+++ b/intl/locale/OSPreferences.cpp
@@ -406,6 +406,10 @@ bool OSPreferences::GetDateTimeConnectorPattern(const nsACString& aLocale,
*/
NS_IMETHODIMP
OSPreferences::GetSystemLocales(nsTArray<nsCString>& aRetVal) {
+ if (const char* camouLocale = mozilla::intl::Locale::GetCamouLocale()) {
+ aRetVal.AppendElement(camouLocale);
+ return NS_OK;
+ }
if (!mSystemLocales.IsEmpty()) {
aRetVal = mSystemLocales.Clone();
return NS_OK;
@@ -425,7 +429,10 @@ OSPreferences::GetSystemLocales(nsTArray<nsCString>& aRetVal) {

NS_IMETHODIMP
OSPreferences::GetSystemLocale(nsACString& aRetVal) {
- if (!mSystemLocales.IsEmpty()) {
+ if (const char* camouLocale = mozilla::intl::Locale::GetCamouLocale()) {
+ aRetVal = camouLocale;
+ return NS_OK;
+ } else if (!mSystemLocales.IsEmpty()) {
aRetVal = mSystemLocales[0];
} else {
AutoTArray<nsCString, 10> locales;
@@ -439,6 +446,10 @@ OSPreferences::GetSystemLocale(nsACString& aRetVal) {

NS_IMETHODIMP
OSPreferences::GetRegionalPrefsLocales(nsTArray<nsCString>& aRetVal) {
+ if (const char* camouLocale = mozilla::intl::Locale::GetCamouLocale()) {
+ aRetVal.AppendElement(camouLocale);
+ return NS_OK;
+ }
if (!mRegionalPrefsLocales.IsEmpty()) {
aRetVal = mRegionalPrefsLocales.Clone();
return NS_OK;
5 changes: 4 additions & 1 deletion settings/properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,8 @@
{ "property": "geolocation:latitude", "type": "double" },
{ "property": "geolocation:longitude", "type": "double" },
{ "property": "geolocation:accuracy", "type": "double" },
{ "property": "timezone", "type": "str" }
{ "property": "timezone", "type": "str" },
{ "property": "locale:language", "type": "str" },
{ "property": "locale:region", "type": "str" },
{ "property": "locale:script", "type": "str" }
]
2 changes: 1 addition & 1 deletion upstream.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version=130.0.1
release=beta.7
release=beta.8

0 comments on commit 5263cb6

Please sign in to comment.