Tower-9.0-342 MacOS分析

发布时间 2023-04-16 21:24:26作者: bodong

    这是一款MacOS下面的GIT客户端,这里是一些分析日记,若有侵权,请联系我,秒删。

    先安装,直接用Hopper Disassember调试,主要的模块是Tower/FNLicensing/FNFoundation。首先可以找到[GTProductConfig licenseCodeValidationPattern],这个函数校验了license格式,最终可以知道格式应该是这样:

TOWR(-[A-Z]{4}){4}
TOWR-ABCD-ABCD-ABCD-ABCD

    另外还有一系列的函数是关键点,可以从中获取各种重要信息:

模块:FNLicensing 
    FNMachineIdentifier::obfuscatedMachineUUID
    FNLicensingFileHelpers::licenseFileURLForLicenseType
    FNLicenseStoreManager::readProductLicense  read license file
    FNLicenseValidator::initWithProductConfig
    FNLicenseValidator::validateProductLicense
    FNProductLicense::licenseWithValuesFromDictionary

    FNLicenseHashGenerator::generateHashForDictionary
    FNLicenseHashGenerator::hashSourceForDictionary

    // see never is not expired...
    FNExpirationDateStringValueToExpirationDateTransformer::transformedValue
模块:Tower
    [GTProductConfig defaultConfig]
    [FNLicensingHelpers obfuscatedUserName]
    [FNLicensingHelpers obfuscatedMachineUUID]
    [FNLicensingHelpers machineName]
模块:FNFoundation 
    NSString::MD5String

     还有一些用于加料的数据:

macuuid salt : S842!kJoqsoS(2kS(l2)=1100JkloAs2
key salt : JuD324AiNyS89oTtS10sVyJoUaAgNv1q

    下面是一些关键字符串对应的名称,这些名称是dictionary的键,这些值的来源都可以通过前面的函数逆向分析得到。

// function name to string
_FNLicenseKeyKey            keys
_FNLicenseKeyIdentifier     uuid
_FNLicenseKeyMachineID      machine
_FNLicenseKeyUserID         user
_FNLicenseKeyEmail           email
_FNLicenseKeyLicenseName    name
_FNLicenseKeyCode                 code
_FNLicenseKeyMaskedLicenseCod     license_code
_FNLicenseKeyLicenseRevoked       revoked
_FNLicenseKeyExpirationDate         expiration_date
_FNLicenseKeyFeatures           plan_features
_FNLicenseKeyPlan               plan
_FNLicenseKeyPlanID             plan_uuid
_FNLicenseKeyProductName     product
_FNLicenseKeyLicenseType    type
_FNLicenseFeatureEnterpriseServices 

  如:

    self.productName = @"tower";
    self.productVersion = @"9.0";
    self.machineUUID = [FNProductConfig ObfuscateMachineUUID];
    self.userName = [FNProductConfig ObfuscateUserUUID];
    self.licensesDirectory = [[NSURL URLWithString:[NSString stringWithFormat:@"%@com.fournova.Tower3/%@", [FNProductConfig ApplicationSupportDirectory], self.machineUUID]] path];
    self.licensesFilePath = [NSString stringWithFormat:@"%@/license.plist", self.licensesDirectory];
    
    // format is regex pattern : TOWR(-[A-Z]{4}){4}
    self.licenseCode = @"TOWR-ABCD-ABCD-ABCD-ABCD";
    self.licensesUUID = [[NSUUID UUID] UUIDString];

     

+ (NSString*) ObfuscateMachineUUID
{
    NSMutableString* hardwareID = [NSMutableString stringWithString:[self HardwareUUID]];
    
    // add this salt
    [hardwareID appendString:@"S842!kJoqsoS(2kS(l2)=1100JkloAs2"];
   
    NSString* md5 = [self MD5String:hardwareID];
    
    return md5;
}

+ (NSString*) ObfuscateUserUUID
{
    NSString* username = NSUserName();
    
    return [FNProductConfig MD5String:username];
}

 

    在调试过程中,可以直接将这些数据调试输出出来:

// input license dictionary
po $rax
{
    code = code;
    email = email;
    "expiration_date" = never;
    "license_code" = "license_code";
    machine = machine;
    plan = plan;
    "plan_features" = "plan_features";
    "plan_uuid" = "plan_uuid";
    product = product;
    revoked = false;
    type = type;
    user = user;
    uuid = uuid;
}

// all keys
po $rax
<__NSArrayI_Transfer 0x600000223b40>(
uuid,
expiration_date,
user,
machine,
plan_uuid,
product,
type,
code,
revoked,
plan,
plan_features,
license_code,
email
)

// all used keys , remove "code"
po $rax
<__NSFrozenArrayM 0x600000c58330>(
uuid,
expiration_date,
user,
machine,
plan_uuid,
product,
type,
revoked,
plan,
plan_features,
license_code,
email
)

// sort ...
po $rax
<__NSArrayI_Transfer 0x600000249b00>(
email,
expiration_date,
license_code,
machine,
plan,
plan_features,
plan_uuid,
product,
revoked,
type,
user,
uuid
)

// get values...
po $rbx
<__NSArrayM 0x600000c46a30>(
email,
never,
license_code,
cfbb84998c388b71a5ed275ae3aedf26,
plan,
plan_features,
plan_uuid,
product,
false,
type,
f93d290deeda70e6270537a58d01d8a1,
uuid
)

    在计算最终的license字符串时,将这些值使用都好分割组合成一个字符串,再将这个字符串计算MD5,这个MD5值就是目标序列号:

// join by ","
po $rax
email,never,license_code,cfbb84998c388b71a5ed275ae3aedf26,plan,plan_features,plan_uuid,product,false,type,f93d290deeda70e6270537a58d01d8a1,uuid

// + salt , calc md5string
// this is the license key.
po $rbx
fabc35e08c01c552248f659a888464c8

如:

// FNLicensing FNLicenseHashGenerator::hashSourceForDictionary
+ (NSString*) GenerateHashSourceForDictionary:(NSDictionary *)dict
{
    // get all keys from dictionary
    NSArray* keys = [dict allKeys];
    NSMutableArray* validKeys = [NSMutableArray arrayWithArray:keys];
    
    // sort by alphabet
    // FNLicense sub_2518
    [validKeys sortUsingComparator:^(NSString* l, NSString* r)
     {
        return [l compare:r];
    }];
    
    NSMutableArray* values = [NSMutableArray new];
    
    for(int i=0; i<validKeys.count; ++i)
    {
        NSObject* value = [dict objectForKeyedSubscript:validKeys[i]];
        
        if(![value isKindOfClass:[NSArray class]])
        {
            [values addObject:value];
        }
    }
    
    NSString* result = [values componentsJoinedByString:@","];
    return result;
}

 

- (NSString*) genHashSourceString
{
    NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithDictionary:[self genDictionary]];
    
    NSString* baseHashSource = [FNProductConfig GenerateHashSourceForDictionary:dict];
    NSString* salt = @"JuD324AiNyS89oTtS10sVyJoUaAgNv1q";
    
    return [NSString stringWithFormat:@"%@%@", baseHashSource, salt];
}

- (NSString*) genHashString
{
    return [FNProductConfig MD5String:[self genHashSourceString]];
}

 

    所以只要你能把这些东西都搞清楚,就可以搞一个注册机出来了。