Hits: 374

WinRAR Keygen

1. What is WinRAR?

  • WinRAR is a trialware file archiver utility for Windows, developed by Eugene Roshal of win.rar GmbH.
  • It can create and view archives in RAR or ZIP file formats and unpack numerous archive file formats.
  • WinRAR is not a free software. If you want to use it, you should pay to RARLAB and then you will get a license file named "rarreg.key".
  • This repository will tell you how WinRAR license file "rarreg.key" is generated.

2. How is “rarreg.key” generated?

  • WinRAR uses a signature algorithm, which is a variant of Chinese SM2 digital signature algorithm, to process the user’s name and the license type he/she got. Save the result to “rarreg.key” and add some header info, then a license file is generated.
  • The following will talk about the detail of the signature algorithm that WinRAR uses and how WinRAR process the user’s name and his/her license type.

    2.1. Signature Algorithm

    • WinRAR uses ECC (Elliptic-curve Cryptography) to do signature. The elliptic curve it uses is over a composite finite field FiniteField where the primitive polynomial of base finite field is:

    • And the primitive polynomial of extend finite field is:

    • The elliptic curve equation is:

    • Let G be base point that will be used during signature. The exact value is:
      auto G = ecCurve.GetPoint({           // X, represents a polynomial 
        0x38CC, 0x052F, 0x2510, 0x45AA,   // over GF((2 ^ 15) ^ 17), the first term  
        0x1B89, 0x4468, 0x4882, 0x0D67,   // is in the front.
        0x4FEB, 0x55CE, 0x0025, 0x4CB7,
        0x0CC2, 0x59DC, 0x289E, 0x65E3,
        0x56FD
      }, {                                  // Y, represents a polynomial 
        0x31A7, 0x65F2, 0x18C4, 0x3412,   // over GF((2 ^ 15) ^ 17), the first term  
        0x7388, 0x54C1, 0x539B, 0x4A02,   // is in the front.
        0x4D07, 0x12D6, 0x7911, 0x3B5E,
        0x4F0E, 0x216F, 0x2BF2, 0x1974,
        0x20DA
      });
    • P is the order that will be used during signature. The exact value is:
      const uint64_t P[4] = {   // P = 0x1026dd85081b82314691ced9bbec30547840e4bf72d8b5e0d258442bbcd31
          0x5e0d258442bbcd31,
          0x0547840e4bf72d8b,
          0x2314691ced9bbec3,
          0x0001026dd85081b8
      };

      2.1.1 The Generation of PrivateKey PrivateKey, Random Number K and Hash e

      • To generate PrivateKey PrivateKey, random number K and hash e, we need two byte-arrays. We call them in_dataand in_private. Their length is in_data_length and in_private_length respectively.
      • PrivateKey is generated by in_private.
        e is generated by in_data.
        K is generated by both in_data and in_private.

For better understanding, here I give an case:

byte in_private[7] = { 'P', 'h', 'a', 'n', 't', 'o', 'm' }; 
byte in_data[9] = { 'L', 'a', 'b', 'y', 'r', 'i', 'n', 't', 'h' };

where in_private_length is 7 and in_data_length is 9.

2.1.1.1 The Generation of PrivateKey.
  • Calculate in_private‘s SHA-1 digest.In the case I give, it should be:
    byte in_private_sha1[] = {        //  standard sha-1 value is : {
        0xc0, 0x16, 0x3f, 0x02,       //      0x02, 0x3f, 0x16, 0xc0, 
        0x78, 0x4b, 0xac, 0x8c,       //      0x8c, 0xac, 0x4b, 0x78, 
        0xaf, 0x97, 0x0d, 0xe6,       //      0xe6, 0x0d, 0x97, 0xaf, 
        0xd9, 0x46, 0xa4, 0x91,       //      0x91, 0xa4, 0x46, 0xd9, 
        0xdc, 0xb3, 0xbf, 0x06        //      0x06, 0xbf, 0xb3, 0xdc
    };                                //  }

    NOTICE: Compared with standard SHA-1 calculation, WinRAR reversed each 32-bits block.

    If in_private is null, use default value:

    byte in_private_sha1[] = { 
        0x81, 0xb7, 0x3e, 0xeb, 
        0x29, 0x53, 0x26, 0x50, 
        0xa3, 0xf4, 0x5e, 0xdc, 
        0xd5, 0xb9, 0x47, 0x68, 
        0x4c, 0x3b, 0xe4, 0xcd 
    };

    It is probably the SHA-1 digest of some secret data used in RARLAB

  • To get each part of PrivateKey, do 15 rounds of SHA-1 calculation:1. Let i be uint32_t varing from 1 to 15.2. In each round, we calculate the SHA-1 digest of a 24-bytes-long byte-array which is the combination of i (little-endian) and src_data_sha1. Then take the first two bytes and append them at the end of PrivateKey.
  • After that, PrivateKey should be a 30-bytes-long and its value is:

    In the case I give, PrivateKey is:

    byte PrivateKey[30] = {       // PrivateKey = 0xb7e256217a67f14e3fb4246e889ea18b69b246616e04525e96d515831f2a  
        0x2a, 0x1f, 0x83, 0x15,   // which is a 240-bits-long integer.
        0xd5, 0x96, 0x5e, 0x52, 
        0x04, 0x6e, 0x61, 0x46, 
        0xb2, 0x69, 0x8b, 0xa1, 
        0x9e, 0x88, 0x6e, 0x24, 
        0xb4, 0x3f, 0x4e, 0xf1, 
        0x67, 0x7a, 0x21, 0x56, 
        0xe2, 0xb7 
    };

    In my code I use a uint64_t[4] array to store it.

2.1.1.2 The Generation of K and e.
  • During the generation of PrivateKey, there is a temporary 24-bytes-long byte-array. After all of the 15 rounds, the temporary byte-array should be the combination of uint32_t(15) (little endian) and src_data_sha1.In the case I give, it should be:
    byte in_private_sha1_temp[] = { 
        0x0f, 0x00, 0x00, 0x00, 
        0xc0, 0x16, 0x3f, 0x02,  
        0x78, 0x4b, 0xac, 0x8c,  
        0xaf, 0x97, 0x0d, 0xe6,  
        0xd9, 0x46, 0xa4, 0x91, 
        0xdc, 0xb3, 0xbf, 0x06 
    };
  • Calculate in_data‘s SHA-1 digest:In the case I give, it should be:
    byte in_data_sha1[] = { 
        0xfc, 0x0e, 0xcd, 0xba, 
        0x12, 0xa0, 0xc6, 0x2e, 
        0x96, 0xf6, 0xbe, 0xdb, 
        0x9f, 0x89, 0x72, 0x10, 
        0x05, 0x05, 0x71, 0xe0 
    };
  • Append
    byte empty_sha1[] = { 
        0x43, 0x8d, 0xfd, 0x0f, 
        0x7c, 0x3c, 0xe3, 0xb4, 
        0xd1, 0x1b, 0x46, 0x53, 
        0x46, 0xa5, 0x27, 0x0f, 
        0x0d, 0xd9, 0x50, 0x10
    };

    at the end of in_data_sha1 so we can get byte-array (in_data_sha1 + empty_sha1). The byte-array appended is the SHA-1 digest of null while 5 SHA-1 initial constants is set 0. Then take the first 30 bytes of (in_data_sha1 + empty_sha1) as e.

  • Append (in_data_sha1 + empty_sha1) at the end of in_private_sha1_temp.In the case I give, it should be:
    byte in_private_sha1_temp[] = { 
        0x0f, 0x00, 0x00, 0x00,   // original in_private_sha1_temp.
        0xc0, 0x16, 0x3f, 0x02,  
        0x78, 0x4b, 0xac, 0x8c,  
        0xaf, 0x97, 0x0d, 0xe6,  
        0xd9, 0x46, 0xa4, 0x91, 
        0xdc, 0xb3, 0xbf, 0x06, 
        0xfc, 0x0e, 0xcd, 0xba,   // in_data_sha1
        0x12, 0xa0, 0xc6, 0x2e, 
        0x96, 0xf6, 0xbe, 0xdb, 
        0x9f, 0x89, 0x72, 0x10, 
        0x05, 0x05, 0x71, 0xe0,
        0x43, 0x8d, 0xfd, 0x0f,   // empty_sha1
        0x7c, 0x3c, 0xe3, 0xb4, 
        0xd1, 0x1b, 0x46, 0x53, 
        0x46, 0xa5, 0x27, 0x0f, 
        0x0d, 0xd9, 0x50, 0x10
    };
  • To get each part of red_K, do 15 rounds of SHA-1 calculation.
    In each round, do
++*reinterpreter_cast<uint32_t*>(in_private_sha1_temp);

first, then calculate the SHA-1 digest of in_private_sha1_temp and append the first two bytes of the digest at the end of K.

In the case I give, it should be:

byte K[] = { 
    0xeb, 0xed, 0x4f, 0xba, 
    0x0b, 0x30, 0xe8, 0x26, 
    0xf4, 0xec, 0xe6, 0x92, 
    0x76, 0xcc, 0xe8, 0x0b, 
    0xa8, 0x9c, 0x6f, 0x3a, 
    0x41, 0x6d, 0x5c, 0xfe, 
    0x21, 0x42, 0x5a, 0x5a, 
    0x5d, 0xbe
};

In my code I use a uint64_t[4] array to store it.

2.1.2 The Generation of Singnature rs and PublicKey PublicKey

  • Now we have PrivateKey PrivateKey, random number K, hash e, order P and base point G.
  • equationNOTICE:1. The dot in equation refers to the elliptic curve point multiplication on equation over FiniteField.

    2. equation means takeing X-axis value of a point. This value is a polynomial over FiniteField.

    3. Function T converts a polynomial over FiniteField to a integer whose bit length would not larger than 15 * 17 = 255. The detail of function T will be talked about later.

    4. equation is integer addition.

  • equationNOTICE:1. equation is integer multiplication.
  • equationNOTICE:1. equation is division over FiniteField.

    2. equation is binary AND operation.

  • About function T:
    As I said before, it converts a polynomial over FiniteField to a integer. If I use a 17-elements-long list represent a polynomial over FiniteField, whose every element represents a polynomial over equation, function T can be defined as the following Python code:
def T(x : list):
    ret = 0
    for i in range(0, 17):
        ret += x[i] * 2 ** (15 * i)
    
    return ret

2.2. The Generation of “rarreg.key”

  • rarreg.key consists of a headeruser’s namelicense typeUIDregistration data and checksum.

    2.2.1 Header

    • It is just a text line:

      RAR registration data

    • Actually, when WinRAR verifies user’s license file, it does not care what content the header have.

    2.2.2 User’s name

    • It is also just a text line. Here I give a case:

      Phantom

    2.2.3 License type

    • Also just a text line. Actually it can be any text. Here I give a case:

      Single PC usage License

    2.2.5 UID

    • It is just a join of two parts of registration data. Here I give a case:

      UID=294d3fd81ae79b20c48c

    • Actually, when WinRAR verifies user’s license file, it does not case UID at all.

    2.2.5 Registration data

    • Registration data has four parts. We name them RegData0RegData1RegData2RegData3 respectively.
    • If ECC signature of license type is r and s (, which means in_data = license type, in_private = null) , convert their value to hex string str_Rstr_S. The max possible length of str_R and str_S is 60 because r and s are both 30 bytes long. So RegData1 is the format output of “60”str_S and str_R:
      _stprintf_s(RegData[1], TEXT("60%060s%060s"), str_S, str_R);

      In the case I give, it should be:

      char RegData[1] = "60cc0b2d34fdb287124c6ca6b4f3239d36aa3ef82a9aba3a22d45552465c93260c3f609e601c26f312c4f44e7f8773a98b078809297303f8cac4a5ff92";
    • Let str_Kpub be the hex string of PublicKey that is generated by user’s name (in_private = user’s name). RegData3is:
      _stprintf_s(RegData[3], TEXT("%zd%.48s"), strlen(str_Kpub) - 4, str_Kpub);

      In the case I give, it should be:

      char RegData[3] = "6067c38462f2ff189c4b04e3c0540548d096e1068d9235023c";
    • Let str_Kpub2 be the hex string of PublicKey that is generated by RegData3 (in_private = RegData3). So:
      _stprintf_s(RegData[0], TEXT("%s"), str_Kpub2);

      In the case I give, it should be:

      char RegData[0] = "c48c1c168f2ddf266ab3c33118678236ddeb1319b06790c929a038e487c79c60";
    • UID is:
      _stprintf_s(UID, TEXT("UID=%.16s%.4s"), str_Kpub + 48, str_Kpub2);

      In the case I give, it should be:

      char UID = "UID=294d3fd81ae79b20c48c";

      which is the same as what said before.

    • If ECC signature of str_UserName_RegData0 that is the join of user’s name and RegData0 is r and s (, which means in_data = user’s name + RegData0in_private = null) , convert their value to hex string str_R2str_S2. So RegData2 is the format output of “60”str_S2 and str_R2:
      _stprintf_s(RegData[2], TEXT("60%060s%060s"), str_S2, str_R2);

      In the case I give, it should be:

      char RegData[2] = "60625690ab228461b54ab9a4c0a4cc5f7fdf47aa192f839596b1628abf1bec0f405163f46ce6a778adc398cefae18dbb0e4f46291e4d61e573ae0eaeb9";

    2.2.5 Registration data

    • TODO

  • The output:
      _tprintf_s(TEXT("RAR registration data\n%s\n%s\n%s\n"), 
             UserName,
             LicenseType,
             UID);
      
      char temp[1024] = { };
      _stprintf_s(temp, TEXT("%zd%zd%zd%zd%s%s%s%s%lu"),
              strlen(RegisterData[0]),
              strlen(RegisterData[1]),
              strlen(RegisterData[2]),
              strlen(RegisterData[3]),
              RegisterData[0],
              RegisterData[1],
              RegisterData[2],
              RegisterData[3], 
              checksum);
    
      for (int i = 0; i < 8; ++i)
          _tprintf_s(TEXT("%.54s\n"), temp + i * 54);

    is rarreg.key.

Subscribe Me On Youtube

Subscribe Me Now

Loading...