火狐浏览器收藏夹的hash校验分析



     背景:随着firefox的不断更新,不知不觉间已经更新到了56.0.2版本。之前收藏夹一直都是好好的,但是突然某一天发现收藏夹设置不上了。于是进行了深入分析,于是就写下了这个文章,也算是冒泡下表示下存在。

    我们先来看存在的问题是什么。直接上图。
    
火狐浏览器收藏夹的hash校验分析1

火狐浏览器收藏夹的hash校验分析2

通过图我们可以发现:favicons.sqlite数据库的表moz_pages_w_icons的page_url_hash字段对page_url进行了hash校验
同样的moz_icons表中的fixed_icon_url_hash也对icon_url进行了hash校验。
首先我们通过尝试乱写一个hash值会怎么样?运气好的话,可能不会产生什么影响。但是天公不做美,胡乱改了hash校验值之后
收藏夹的图标竟然就不正常了。这个蛋疼,看来非得研究下到底是如何hash的。
首先:不得不提的是,如果能猜出来的话,坚决就不要去浪费时间分析。好的。
我们一看就首先会想到crc32,可惜的是,结果根本对不上,那么肯定就不是crc32了,于是继续进行猜解

这里需要尝试的工具和算法分别是如图:
火狐浏览器收藏夹的hash校验分析3

通过这个密码学工具没发现能有匹配上的hash值(其实这个工具猜也不行,因为这个不主要是计算hash值的)

于是又专门对进行hash计算的进行了匹配如下图: 
#算法结果结果(大写)长度备注
1md57f4c79817fabaeaa0e909754cfe655e77F4C79817FABAEAA0E909754CFE655E732前16位:
小写:7f4c79817fabaeaa
大写:7F4C79817FABAEAA
2sha1bae3c9c6409a80602f33799b18b49b9a2cdcc2a2BAE3C9C6409A80602F33799B18B49B9A2CDCC2A240
3sha2568dd0d3350e771893b901733c799eb4e0d6d5ec6393a8f4e1f29f353497f736178DD0D3350E771893B901733C799EB4E0D6D5EC6393A8F4E1F29F353497F7361764
4sha512da685f291d0bb664ed16a6c052d2379911b0145ed762cda135da4228c3b7334914a3a34767f6475d6e8d79fbd49b2fb704cfaeb97c65ea5ab8b388bf5c04fe1dDA685F291D0BB664ED16A6C052D2379911B0145ED762CDA135DA4228C3B7334914A3A34767F6475D6E8D79FBD49B2FB704CFAEB97C65EA5AB8B388BF5C04FE1D128
5adler323e90066a3E90066A8
6crc327f1df95c7F1DF95C8
7crc32b54c44ecd54C44ECD8
8fnv13272081c9072081C908
9fnv164e7837b6f0aa25bd0E7837B6F0AA25BD016
10fnv1a327ef421527EF421528
11fnv1a6426fde1d5080a72b226FDE1D5080A72B216
12gost8937e7d5f96b0a10a75101d38326291eafe129d0296d9ba3b46ae8bd03954a9f8937E7D5F96B0A10A75101D38326291EAFE129D0296D9BA3B46AE8BD03954A9F64
13gost-cryptof1ae905f3dc3e00f48f6341a2f6ee17a911fc2a44487dba5b3d52191caf917c0F1AE905F3DC3E00F48F6341A2F6EE17A911FC2A44487DBA5B3D52191CAF917C064
14haval128,3da6b7922c8f2c2a0a4527c0e1c2c3527DA6B7922C8F2C2A0A4527C0E1C2C352732
15haval128,49a2f738dba21363844a53e93fa7336349A2F738DBA21363844A53E93FA73363432
16haval128,542d50658a8797e15debbb8b9f049a0da42D50658A8797E15DEBBB8B9F049A0DA32
17haval160,33aaa82730af3184bb9d3cdef8930045f052aa7713AAA82730AF3184BB9D3CDEF8930045F052AA77140
18haval160,4e1470f77599d96989273ea110b0eac473726faa7E1470F77599D96989273EA110B0EAC473726FAA740
19haval160,592e5bf63f1eb6e0729789837a40a99baf9669eff92E5BF63F1EB6E0729789837A40A99BAF9669EFF40
20haval192,3cecf625bca8e5c9d42029a39e1a9e7c0999bec518e87d7ecCECF625BCA8E5C9D42029A39E1A9E7C0999BEC518E87D7EC48
21haval192,458ba63fd0969d98d4e0f338c717436934b24cc59cdd0a62858BA63FD0969D98D4E0F338C717436934B24CC59CDD0A62848
22haval192,53f4562d504b7e7eb55c0c885f7bda04fbdff7fb76c1d11fb3F4562D504B7E7EB55C0C885F7BDA04FBDFF7FB76C1D11FB48
23haval224,32b983078b1bf7a1837a201b6e70b71922152fafebe7d25ced14cb7ae2B983078B1BF7A1837A201B6E70B71922152FAFEBE7D25CED14CB7AE56
24haval224,47b8c0730275c2d320806fc86823aecbae93bbcab3109abceab9f05337B8C0730275C2D320806FC86823AECBAE93BBCAB3109ABCEAB9F053356
25haval224,5d17c556996ed4593390acd7c2ca5b39b44a1186fb575ccfab1cb2131D17C556996ED4593390ACD7C2CA5B39B44A1186FB575CCFAB1CB213156
26haval256,3594a3f8339f591af11297a943141adea90fc3336fae44ccf0e90b0a4273c4126594A3F8339F591AF11297A943141ADEA90FC3336FAE44CCF0E90B0A4273C412664
27haval256,49cae8d5aeaed19b7ef3bb0540b81193ef39daea4b631050b6c05e392c4cd4a139CAE8D5AEAED19B7EF3BB0540B81193EF39DAEA4B631050B6C05E392C4CD4A1364
28haval256,5ff7f42518fc67d3d4a40f8f76696a6d963f1ff55c79962dfefaca13cadd82d4dFF7F42518FC67D3D4A40F8F76696A6D963F1FF55C79962DFEFACA13CADD82D4D64
29joaat843bd690843BD6908
30md2ad63c4e08e817c3f8da0aff661a6277cAD63C4E08E817C3F8DA0AFF661A6277C32
31md494ee16a9bd52b09a2cb3d04b703acf4894EE16A9BD52B09A2CB3D04B703ACF4832
32ripemd128d29ca082da0837424468ef35b2ab9831D29CA082DA0837424468EF35B2AB983132
33ripemd16006376f0b4541dd12212a08ac5f3348b315c9ef3206376F0B4541DD12212A08AC5F3348B315C9EF3240
34ripemd256242964e7c3f39b56229b4f5efcb38f00ea1c0205a865abec70348f96d1e79ab6242964E7C3F39B56229B4F5EFCB38F00EA1C0205A865ABEC70348F96D1E79AB664
35ripemd320f89b2dcb11b2bc99ff60dfdc8eb0cf50d169cbe7f68b945dcc6b3a70ada96d531777c07e9313a1c9F89B2DCB11B2BC99FF60DFDC8EB0CF50D169CBE7F68B945DCC6B3A70ADA96D531777C07E9313A1C980
36sha22460794256cb6fab46c4b7867c3548db250cd4ebde4d1beeada20053bb60794256CB6FAB46C4B7867C3548DB250CD4EBDE4D1BEEADA20053BB56
37sha3840babbc39788eb326edc3d8f7bbd37288bd699dd696fcf07ba1358311fd5fad35137e2b79e391dcb01622e7bdf1827e380BABBC39788EB326EDC3D8F7BBD37288BD699DD696FCF07BA1358311FD5FAD35137E2B79E391DCB01622E7BDF1827E3896
38snefruac7bb447a099b2ed17730779fe072f3749d71bb2dd45963de5bd864879d54ffdAC7BB447A099B2ED17730779FE072F3749D71BB2DD45963DE5BD864879D54FFD64
39snefru256ac7bb447a099b2ed17730779fe072f3749d71bb2dd45963de5bd864879d54ffdAC7BB447A099B2ED17730779FE072F3749D71BB2DD45963DE5BD864879D54FFD64
40tiger128,3aa235c032b41d4bc7f40b3ba3811a4e3AA235C032B41D4BC7F40B3BA3811A4E332
41tiger128,45e0ab0723d5fe5c55c20acd5b2c2936f5E0AB0723D5FE5C55C20ACD5B2C2936F32
42tiger160,3aa235c032b41d4bc7f40b3ba3811a4e350922fe8AA235C032B41D4BC7F40B3BA3811A4E350922FE840
43tiger160,45e0ab0723d5fe5c55c20acd5b2c2936ff141f1a55E0AB0723D5FE5C55C20ACD5B2C2936FF141F1A540
44tiger192,3aa235c032b41d4bc7f40b3ba3811a4e350922fe80703f9eeAA235C032B41D4BC7F40B3BA3811A4E350922FE80703F9EE48
45tiger192,45e0ab0723d5fe5c55c20acd5b2c2936ff141f1a5787862935E0AB0723D5FE5C55C20ACD5B2C2936FF141F1A57878629348
46whirlpool11b50098eecbd14d84ae2f9c7c3834e269e4300845796839338a36d722615c8271a38c3586fbf61f5d82544b5782567733918f9c81491bba9bd1fd6c3a1b52bb11B50098EECBD14D84AE2F9C7C3834E269E4300845796839338A36D722615C8271A38C3586FBF61F5D82544B5782567733918F9C81491BBA9BD1FD6C3A1B52BB128

结果可想而知,一个匹配的都没有。到这里说实话当时的想法想骂娘,还好我们都有讲素质的人,骂也没用。
于是深深的抽了一只烟,下定决心要搞它,看看到底是啥玩意的hash计算。在搞之前说个题外话,我觉的真的有必要
把目前所有的国际流行经典的加密解密等全部集合成一个工具。以后猜都好猜,不过这只能留给有心人了,估计不是asmer还真搞不来这事。

    下来我们就要开搞了,开始进入加速模式了。
    首先依照国际惯例,我们必须要找到firefox浏览器的安装目录下,到底是哪个dll或者哪个exe,或者说到底是哪个pe在负责hash计算。
    当然还是ProcessMonitor这个niubility的工具了,进程名写firefox.exe 操作写WriteFile。(至于这个工具不做解释反正很是强大都是系统的一些注册表回调了 sfilter miniport等核心操作)。

    然后打开firefox浏览器,增加个收藏夹,最后我们通过栈属性中调用的关系,最后定位到了xul.dll这个文件。
    然而可恨的是什么呢?上图就知道了
    火狐浏览器收藏夹的hash校验分析4

可恨的是这个dll的体积竟然高达55.6MB,在茫茫的代码海洋中寻找到hash计算的函数断点,绝对不是一件轻松的事情。
不过还是得找。于是花了一两个小时的时间把xul.dll载入到ida中(时间太久了蛋疼)
 
通过ida的findcrypt2识别算法插件发现如下图:

火狐浏览器收藏夹的hash校验分析5

总共有8个已知的加密算法,这些算法前面都验证过,都不是。只能真正的找hash函数并打断点了,各种投机取巧的策略都失败了。

于是开始搜索各种可能的字符串作为标记,最后在hash这个字符串上通过各种交叉引用找到了如下片段:

int __stdcall sub_1072C31E(int a1, int a2, _DWORD *a3)
{
  int result; // eax@1
  int v4; // ecx@1
  signed int v5; // ebx@3
  int v6; // ecx@3
  int v7; // eax@5
  char *v8; // edi@9
  int v9; // ecx@9
  unsigned int k; // edx@9
  int v11; // ecx@10
  unsigned int v12; // esi@11
  int v13; // edx@11
  unsigned int v14; // ecx@11
  int v15; // edx@12
  int v16; // ST08_4@14
  void *v17; // esi@14
  int v18; // ecx@19
  unsigned int l; // edx@19
  int v20; // ecx@20
  int v21; // ecx@23
  unsigned int i; // edx@23
  int v23; // ecx@24
  int v24; // ecx@27
  unsigned int j; // edx@27
  int v26; // ecx@28
  unsigned __int64 v27; // [sp-10h] [bp-BCh]@21
  unsigned int v28; // [sp+Ch] [bp-A0h]@8
  char *v29; // [sp+10h] [bp-9Ch]@8
  char *v30; // [sp+14h] [bp-98h]@8
  char *v31; // [sp+18h] [bp-94h]@8
  int (__stdcall **v32)(int, int, int, int); // [sp+20h] [bp-8Ch]@8
  _DWORD *v33; // [sp+24h] [bp-88h]@1
  unsigned int v34; // [sp+28h] [bp-84h]@1
  char *v35; // [sp+2Ch] [bp-80h]@8
  char *v36; // [sp+30h] [bp-7Ch]@8
  char *v37; // [sp+34h] [bp-78h]@8
  const char *v38; // [sp+38h] [bp-74h]@8
  int v39; // [sp+3Ch] [bp-70h]@8
  __int16 v40; // [sp+40h] [bp-6Ch]@8
  __int16 v41; // [sp+42h] [bp-6Ah]@8
  char *v42; // [sp+44h] [bp-68h]@8
  unsigned int v43; // [sp+48h] [bp-64h]@8
  int v44; // [sp+4Ch] [bp-60h]@16
  void *v45; // [sp+50h] [bp-5Ch]@7
  char *v46; // [sp+54h] [bp-58h]@3
  int v47; // [sp+58h] [bp-54h]@7
  int v48; // [sp+5Ch] [bp-50h]@16

  v33 = a3;
  result = (*(int (__stdcall **)(int, int *))(*(_DWORD *)a2 + 12))(a2, (int *)&v34);
  if ( result >= 0 )
  {
    if ( v34 - 1 <= 1 )
    {
      sub_1054CB92(v4);
      v5 = 0;
      (*(void (__stdcall **)(int, _DWORD, int))(*(_DWORD *)a2 + 36))(a2, 0, v6);
      sub_1044E031((int)&v46);
      if ( v34 > 1 )
        (*(void (__stdcall **)(int, signed int, int *))(*(_DWORD *)a2 + 32))(a2, 1, (int *)&v46);
      v7 = moz_xmalloc(64);
      if ( v7 )
      {
        *(_WORD *)(v7 + 40) = 255;
        *(_BYTE *)(v7 + 48) = 1;
        *(_DWORD *)v7 = &off_124114B4;
        *(_DWORD *)(v7 + 56) = 0;
      }
      else
      {
        v7 = 0;
      }
      sub_1083531E(&v45, v7);
      if ( v47 )
      {
        v38 = "prefix_lo";
        v39 = 9;
        v40 = 33;
        v41 = 2;
        if ( (unsigned __int8)sub_1057CEA8((int)&v46, (int)&v38) )
        {
          v21 = 0;
          for ( i = 0; i < v43; ++i )
          {
            v23 = __ROL4__(v21, 5);
            v21 = -1640531527 * (*(_WORD *)&v42[2 * i] ^ v23);
          }
          HIDWORD(v27) = (unsigned __int16)v21;
          LODWORD(v27) = 0;
        }
        else
        {
          v38 = "prefix_hi";
          v39 = 9;
          v40 = 33;
          v41 = 2;
          if ( !(unsigned __int8)sub_1057CEA8((int)&v46, (int)&v38) )
          {
            v5 = -2147467259;
            if ( v45 )
              sub_10939928(v45);
            goto LABEL_16;
          }
          v24 = 0;
          for ( j = 0; j < v43; ++j )
          {
            v26 = __ROL4__(v24, 5);
            v24 = -1640531527 * (*(_WORD *)&v42[2 * j] ^ v26);
          }
          v27 = __PAIR__((unsigned __int16)v24, 0) + 0xFFFFFFFF;
        }
      }
      else
      {
        v35 = v42;
        v37 = v42;
        v29 = v42;
        v36 = &v42[2 * v43];
        v30 = &v42[2 * v43];
        v31 = &v42[2 * v43];
        v40 = 33;
        v41 = 2;
        v32 = &off_123DC3FC;
        v38 = ":";
        v28 = (unsigned int)v42;
        v39 = 1;
        if ( sub_1061E6B1(
               (int)&v35,
               (int)&v38,
               (int)&v29,
               (int (__thiscall ***)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))&v32) )
        {
          sub_105343D7((int)&v35, v28, (unsigned int)v37);
          v8 = v35;
          v9 = 0;
          for ( k = 0; k < (unsigned int)v36; ++k )
          {
            v11 = __ROL4__(v9, 5);
            v9 = -1640531527 * (*(_WORD *)&v35[2 * k] ^ v11);
          }
          v12 = (unsigned __int16)v9;
          v13 = 0;
          v14 = 0;
          if ( v43 > 0 )
          {
            do
            {
              v15 = __ROL4__(v13, 5);
              v13 = -1640531527 * (*(_WORD *)&v42[2 * v14++] ^ v15);
            }
            while ( v14 < v43 );
            v8 = v35;
          }
          v16 = ((unsigned int)v13 + __PAIR__(v12, 0)) >> 32;
          v17 = v45;
          sub_1072C4E6((int)v45, v13, v16);
          sub_105606C0(v8, (unsigned __int8)v37);
          goto LABEL_15;
        }
        v18 = 0;
        for ( l = 0; l < v43; ++l )
        {
          v20 = __ROL4__(v18, 5);
          v18 = -1640531527 * (*(_WORD *)&v42[2 * l] ^ v20);
        }
        v27 = (unsigned int)v18;
      }
      v17 = v45;
      sub_1072C4E6((int)v45, v27, SHIDWORD(v27));
LABEL_15:
      *v33 = v17;
LABEL_16:
      sub_105606C0(v46, v48);
      sub_105606C0(v42, v44);
      return v5;
    }
    result = -2147467259;
  }
  return result;
}

在这个上面打上断点,然后打开firefox.exe后挂载上,然后设置一个收藏夹,在经历接近半个小时的等待后发现的确是能断下来,但是跟了好几次(不小心就跑飞了),别小看这好几次,好几次X0.5小时 = 好几个小时。思想接近崩溃了。

在基本无奈之时,忽然想起来firefox不是开源的吗。哎呀我去。直接是浪费了时间。于是各种搜索

找到了firefox 56.0.2的下载版本。地址如下
http://releases.mozilla.org/pub/firefox/releases/56.0.2/source/firefox-56.0.2.source.tar.xz ;
下载解压如下图:

火狐浏览器收藏夹的hash校验分析6

压缩文件1.5G解压后 n个G的大小,在这么多文件中找到hash也不是容易的事情。
于是下载了
xsearch.exe (实际上EveryThing更好,但是木有下载)

 看来上面的
int __stdcall sub_1072C31E(int a1, int a2, _DWORD *a3)函数还是有用的
起码里面有 prefix_hi这个字符串,我们就按照这个来查询,如下图:
 
火狐浏览器收藏夹的hash校验分析7

经过对查询到的每个文件进行快速感觉,最后发现了
toolkit\components\places\SQLFunctions.cpp 文件,找到如下片段代码

////////////////////////////////////////////////////////////////////////////////
//// Hash Function

  /* static */
  nsresult
  HashFunction::create(mozIStorageConnection *aDBConn)
  {
    RefPtr<HashFunction> function = new HashFunction();
    return aDBConn->CreateFunction(
      NS_LITERAL_CSTRING("hash"), -1, function
    );
  }

  NS_IMPL_ISUPPORTS(
    HashFunction,
    mozIStorageFunction
  )

  NS_IMETHODIMP
  HashFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
                               nsIVariant **_result)
  {
    // Must have non-null function arguments.
    MOZ_ASSERT(aArguments);

    // Fetch arguments.  Use default values if they were omitted.
    uint32_t numEntries;
    nsresult rv = aArguments->GetNumEntries(&numEntries);
    NS_ENSURE_SUCCESS(rv, rv);
    NS_ENSURE_TRUE(numEntries >= 1  && numEntries <= 2, NS_ERROR_FAILURE);

    nsString str;
    aArguments->GetString(0, str);
    nsAutoCString mode;
    if (numEntries > 1) {
      aArguments->GetUTF8String(1, mode);
    }

    RefPtr<nsVariant> result = new nsVariant();
    if (mode.IsEmpty()) {
      // URI-like strings (having a prefix before a colon), are handled specially,
      // as a 48 bit hash, where first 16 bits are the prefix hash, while the
      // other 32 are the string hash.
      // The 16 bits have been decided based on the fact hashing all of the IANA
      // known schemes, plus "places", does not generate collisions.
      nsAString::const_iterator start, tip, end;
      str.BeginReading(tip);
      start = tip;
      str.EndReading(end);
      if (FindInReadable(NS_LITERAL_STRING(":"), tip, end)) {
        const nsDependentSubstring& prefix = Substring(start, tip);
        uint64_t prefixHash = static_cast<uint64_t>(HashString(prefix) & 0x0000FFFF);
        // The second half of the url is more likely to be unique, so we add it.
        uint32_t srcHash = HashString(str);
        uint64_t hash = (prefixHash << 32) + srcHash;
        result->SetAsInt64(hash);
      } else {
        uint32_t hash = HashString(str);
        result->SetAsInt64(hash);
      }
    } else if (mode.Equals(NS_LITERAL_CSTRING("prefix_lo"))) {
      // Keep only 16 bits.
      uint64_t hash = static_cast<uint64_t>(HashString(str) & 0x0000FFFF) << 32;
      result->SetAsInt64(hash);
    } else if (mode.Equals(NS_LITERAL_CSTRING("prefix_hi"))) {
      // Keep only 16 bits.
      uint64_t hash = static_cast<uint64_t>(HashString(str) & 0x0000FFFF) << 32;
      // Make this a prefix upper bound by filling the lowest 32 bits.
      hash +=  0xFFFFFFFF;
      result->SetAsInt64(hash);
    } else {
      return NS_ERROR_FAILURE;
    }

    result.forget(_result);
    return NS_OK;
  }

} // namespace places
} // namespace mozilla 

从中我们发现出了算法的整个结构 以及核心的
HashString的算法,我们再对HashString进行搜索发现了mfbt\HashFunctions.h
代码如下:

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Utilities for hashing. */

/*
 * This file exports functions for hashing data down to a 32-bit value,
 * including:
 *
 *  - HashString    Hash a char* or char16_t/wchar_t* of known or unknown
 *                  length.
 *
 *  - HashBytes     Hash a byte array of known length.
 *
 *  - HashGeneric   Hash one or more values.  Currently, we support uint32_t,
 *                  types which can be implicitly cast to uint32_t, data
 *                  pointers, and function pointers.
 *
 *  - AddToHash     Add one or more values to the given hash.  This supports the
 *                  same list of types as HashGeneric.
 *
 *
 * You can chain these functions together to hash complex objects.  For example:
 *
 *  class ComplexObject
 *  {
 *    char* mStr;
 *    uint32_t mUint1, mUint2;
 *    void (*mCallbackFn)();
 *
 *  public:
 *    uint32_t hash()
 *    {
 *      uint32_t hash = HashString(mStr);
 *      hash = AddToHash(hash, mUint1, mUint2);
 *      return AddToHash(hash, mCallbackFn);
 *    }
 *  };
 *
 * If you want to hash an nsAString or nsACString, use the HashString functions
 * in nsHashKeys.h.
 */

#ifndef mozilla_HashFunctions_h
#define mozilla_HashFunctions_h

#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/Char16.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Types.h"

#include <stdint.h>

#ifdef __cplusplus
namespace mozilla {

/**
 * The golden ratio as a 32-bit fixed-point value.
 */
static const uint32_t kGoldenRatioU32 = 0x9E3779B9U;

inline uint32_t
RotateBitsLeft32(uint32_t aValue, uint8_t aBits)
{
  MOZ_ASSERT(aBits < 32);
  return (aValue << aBits) | (aValue >> (32 - aBits));
}

namespace detail {

inline uint32_t
AddU32ToHash(uint32_t aHash, uint32_t aValue)
{
  /*
   * This is the meat of all our hash routines.  This hash function is not
   * particularly sophisticated, but it seems to work well for our mostly
   * plain-text inputs.  Implementation notes follow.
   *
   * Our use of the golden ratio here is arbitrary; we could pick almost any
   * number which:
   *
   *  * is odd (because otherwise, all our hash values will be even)
   *
   *  * has a reasonably-even mix of 1's and 0's (consider the extreme case
   *    where we multiply by 0x3 or 0xeffffff -- this will not produce good
   *    mixing across all bits of the hash).
   *
   * The rotation length of 5 is also arbitrary, although an odd number is again
   * preferable so our hash explores the whole universe of possible rotations.
   *
   * Finally, we multiply by the golden ratio *after* xor'ing, not before.
   * Otherwise, if |aHash| is 0 (as it often is for the beginning of a
   * message), the expression
   *
   *   (kGoldenRatioU32 * RotateBitsLeft(aHash, 5)) |xor| aValue
   *
   * evaluates to |aValue|.
   *
   * (Number-theoretic aside: Because any odd number |m| is relatively prime to
   * our modulus (2^32), the list
   *
   *    [x * m (mod 2^32) for 0 <= x < 2^32]
   *
   * has no duplicate elements.  This means that multiplying by |m| does not
   * cause us to skip any possible hash values.
   *
   * It's also nice if |m| has large-ish order mod 2^32 -- that is, if the
   * smallest k such that m^k == 1 (mod 2^32) is large -- so we can safely
   * multiply our hash value by |m| a few times without negating the
   * multiplicative effect.  Our golden ratio constant has order 2^29, which is
   * more than enough for our purposes.)
   */
  return kGoldenRatioU32 * (RotateBitsLeft32(aHash, 5) ^ aValue);
}

/**
 * AddUintptrToHash takes sizeof(uintptr_t) as a template parameter.
 */
template<size_t PtrSize>
inline uint32_t
AddUintptrToHash(uint32_t aHash, uintptr_t aValue)
{
  return AddU32ToHash(aHash, static_cast<uint32_t>(aValue));
}

template<>
inline uint32_t
AddUintptrToHash<8>(uint32_t aHash, uintptr_t aValue)
{
  uint32_t v1 = static_cast<uint32_t>(aValue);
  uint32_t v2 = static_cast<uint32_t>(static_cast<uint64_t>(aValue) >> 32);
  return AddU32ToHash(AddU32ToHash(aHash, v1), v2);
}

} /* namespace detail */

/**
 * AddToHash takes a hash and some values and returns a new hash based on the
 * inputs.
 *
 * Currently, we support hashing uint32_t's, values which we can implicitly
 * convert to uint32_t, data pointers, and function pointers.
 */
template<typename T,
         bool TypeIsNotIntegral = !mozilla::IsIntegral<T>::value,
         typename U = typename mozilla::EnableIf<TypeIsNotIntegral>::Type>
MOZ_MUST_USE inline uint32_t
AddToHash(uint32_t aHash, T aA)
{
  /*
   * Try to convert |A| to uint32_t implicitly.  If this works, great.  If not,
   * we'll error out.
   */
  return detail::AddU32ToHash(aHash, aA);
}

template<typename A>
MOZ_MUST_USE inline uint32_t
AddToHash(uint32_t aHash, A* aA)
{
  /*
   * You might think this function should just take a void*.  But then we'd only
   * catch data pointers and couldn't handle function pointers.
   */

  static_assert(sizeof(aA) == sizeof(uintptr_t), "Strange pointer!");

  return detail::AddUintptrToHash<sizeof(uintptr_t)>(aHash, uintptr_t(aA));
}

// We use AddUintptrToHash() for hashing all integral types.  8-byte integral types
// are treated the same as 64-bit pointers, and smaller integral types are first
// implicitly converted to 32 bits and then passed to AddUintptrToHash() to be hashed.
template<typename T,
         typename U = typename mozilla::EnableIf<mozilla::IsIntegral<T>::value>::Type>
MOZ_MUST_USE inline uint32_t
AddToHash(uint32_t aHash, T aA)
{
  return detail::AddUintptrToHash<sizeof(T)>(aHash, aA);
}

template<typename A, typename... Args>
MOZ_MUST_USE uint32_t
AddToHash(uint32_t aHash, A aArg, Args... aArgs)
{
  return AddToHash(AddToHash(aHash, aArg), aArgs...);
}

/**
 * The HashGeneric class of functions let you hash one or more values.
 *
 * If you want to hash together two values x and y, calling HashGeneric(x, y) is
 * much better than calling AddToHash(x, y), because AddToHash(x, y) assumes
 * that x has already been hashed.
 */
template<typename... Args>
MOZ_MUST_USE inline uint32_t
HashGeneric(Args... aArgs)
{
  return AddToHash(0, aArgs...);
}

namespace detail {

template<typename T>
uint32_t
HashUntilZero(const T* aStr)
{
  uint32_t hash = 0;
  for (T c; (c = *aStr); aStr++) {
    hash = AddToHash(hash, c);
  }
  return hash;
}

template<typename T>
uint32_t
HashKnownLength(const T* aStr, size_t aLength)
{
  uint32_t hash = 0;
  for (size_t i = 0; i < aLength; i++) {
    hash = AddToHash(hash, aStr[i]);
  }
  return hash;
}

} /* namespace detail */

/**
 * The HashString overloads below do just what you'd expect.
 *
 * If you have the string's length, you might as well call the overload which
 * includes the length.  It may be marginally faster.
 */
MOZ_MUST_USE inline uint32_t
HashString(const char* aStr)
{
  return detail::HashUntilZero(reinterpret_cast<const unsigned char*>(aStr));
}

MOZ_MUST_USE inline uint32_t
HashString(const char* aStr, size_t aLength)
{
  return detail::HashKnownLength(reinterpret_cast<const unsigned char*>(aStr), aLength);
}

MOZ_MUST_USE
inline uint32_t
HashString(const unsigned char* aStr, size_t aLength)
{
  return detail::HashKnownLength(aStr, aLength);
}

MOZ_MUST_USE inline uint32_t
HashString(const char16_t* aStr)
{
  return detail::HashUntilZero(aStr);
}

MOZ_MUST_USE inline uint32_t
HashString(const char16_t* aStr, size_t aLength)
{
  return detail::HashKnownLength(aStr, aLength);
}

/*
 * On Windows, wchar_t is not the same as char16_t, even though it's
 * the same width!
 */
#ifdef WIN32
MOZ_MUST_USE inline uint32_t
HashString(const wchar_t* aStr)
{
  return detail::HashUntilZero(aStr);
}

MOZ_MUST_USE inline uint32_t
HashString(const wchar_t* aStr, size_t aLength)
{
  return detail::HashKnownLength(aStr, aLength);
}
#endif

/**
 * Hash some number of bytes.
 *
 * This hash walks word-by-word, rather than byte-by-byte, so you won't get the
 * same result out of HashBytes as you would out of HashString.
 */
MOZ_MUST_USE extern MFBT_API uint32_t
HashBytes(const void* bytes, size_t aLength);

/**
 * A pseudorandom function mapping 32-bit integers to 32-bit integers.
 *
 * This is for when you're feeding private data (like pointer values or credit
 * card numbers) to a non-crypto hash function (like HashBytes) and then using
 * the hash code for something that untrusted parties could observe (like a JS
 * Map). Plug in a HashCodeScrambler before that last step to avoid leaking the
 * private data.
 *
 * By itself, this does not prevent hash-flooding DoS attacks, because an
 * attacker can still generate many values with exactly equal hash codes by
 * attacking the non-crypto hash function alone. Equal hash codes will, of
 * course, still be equal however much you scramble them.
 *
 * The algorithm is SipHash-1-3. See <https://131002.net/siphash/>;.
 */
class HashCodeScrambler
{
  struct SipHasher;

  uint64_t mK0, mK1;

public:
  /** Creates a new scrambler with the given 128-bit key. */
  constexpr HashCodeScrambler(uint64_t aK0, uint64_t aK1) : mK0(aK0), mK1(aK1) {}

  /**
   * Scramble a hash code. Always produces the same result for the same
   * combination of key and hash code.
   */
  uint32_t scramble(uint32_t aHashCode) const
  {
    SipHasher hasher(mK0, mK1);
    return uint32_t(hasher.sipHash(aHashCode));
  }

private:
  struct SipHasher
  {
    SipHasher(uint64_t aK0, uint64_t aK1)
    {
      // 1. Initialization.
      mV0 = aK0 ^ UINT64_C(0x736f6d6570736575);
      mV1 = aK1 ^ UINT64_C(0x646f72616e646f6d);
      mV2 = aK0 ^ UINT64_C(0x6c7967656e657261);
      mV3 = aK1 ^ UINT64_C(0x7465646279746573);
    }

    uint64_t sipHash(uint64_t aM)
    {
      // 2. Compression.
      mV3 ^= aM;
      sipRound();
      mV0 ^= aM;

      // 3. Finalization.
      mV2 ^= 0xff;
      for (int i = 0; i < 3; i++)
        sipRound();
      return mV0 ^ mV1 ^ mV2 ^ mV3;
    }

    void sipRound()
    {
      mV0 += mV1;
      mV1 = RotateLeft(mV1, 13);
      mV1 ^= mV0;
      mV0 = RotateLeft(mV0, 32);
      mV2 += mV3;
      mV3 = RotateLeft(mV3, 16);
      mV3 ^= mV2;
      mV0 += mV3;
      mV3 = RotateLeft(mV3, 21);
      mV3 ^= mV0;
      mV2 += mV1;
      mV1 = RotateLeft(mV1, 17);
      mV1 ^= mV2;
      mV2 = RotateLeft(mV2, 32);
    }

    uint64_t mV0, mV1, mV2, mV3;
  };
};

} /* namespace mozilla */
#endif /* __cplusplus */

#endif /* mozilla_HashFunctions_h */
 
好了,这下根据

firefox-56.0.2\toolkit\components\places\SQLFunctions.cpp
firefox-56.0.2\mfbt\HashFunctions.h
和IDA的逆向结果。最后写出大概如下代码(只写片段)

static const uint32_t kGoldenRatioU32 = 0x9E3779B9U;

uint32_t
RotateBitsLeft32(uint32_t aValue, uint8_t aBits)
{
  。。。
}

uint32_t
AddU32ToHash(uint32_t aHash, uint32_t aValue)
{
。。。
}

uint32_t
HashUntilZero(const char* aStr)
{
  。。。
}

uint32_t
HashKnownLength(const char* aStr, size_t aLength)
{
  。。。
}

uint64_t ff_hash(char *s)
{
。。。
}

char *ff_fixup(char *s)
{
    。。。
}

uint64_t page_url_hash(char *url)
{
    return ff_hash(url);
}

uint64_t icon_url_hash(char *url)
{
    return ff_hash(ff_fixup(url));
}

int main()
{
    char url[1024];
    printf("page url:\n");
    gets(url);
    printf("hash: %I64d\n", page_url_hash(url));
    printf("icon url:\n");
    gets(url);
    printf("hash: %I64d\n", icon_url_hash(url));
    return 0;
}

测试hash如下:
 
火狐浏览器收藏夹的hash校验分析8

把这个结果和问题最开始一图的hash比较,那绝对的一模一样的。好了到这里核心问题算是解决了

出个结果图如下:
 火狐浏览器收藏夹的hash校验分析9

ok了冒泡完毕!还是那句话,这年头什么东西都越来越难了,不过只要静下心来,问题都可以去解决。

浏览器的各种加密来的更猛烈些吧。哥不怕你~

CopyRight @2018, Www.LiuLanQiCode.Com, Inc.All Rights Reserved. QQ:8560851 转载请标明来源,否则木有小jj

鲁ICP备18034788号-1

鲁公网安备 37078302000299号