針對某移動辦公App的網絡安全分析

0 前言

逆向的目標之一是評定App安全等級,找到存在的安全隱患,并以有效手段進行反破解。之前實現任意位置打卡的幾種方法,都是修改實際的GPS位置信息,并沒有從網絡、代碼層面進行深入的分析。乘著假期,將網絡請求的流程梳理出來,看看能否從網絡層,獲取相關的請求、參數及加密方法。

1 網絡流程分析

Charels抓包,網絡請求走的都是HTTPS,沒有可參考的價值。

回歸代碼,結合頭文件,網絡請求用的AFNetworking。既然如此,直接Hook住AFHTTPSessionManager類中最常用的POST請求方法 - POST:parameters:progress:success:failure:,將url、參數打印出來:

//Hook的class
CHDeclareClass(AFHTTPSessionManager);

// - (id)POST:(id)arg1 parameters:(id)arg2 progress:(id)arg3 success:(id)arg4 failure:(id)arg5
CHMethod(5, void, AFHTTPSessionManager, POST, id, arg1, parameters, id, arg2, progress, id, arg3, success, id, arg4, failure, id, arg5)
{
    NSLog(@"%s::%@\n %@", __func__, arg1, arg2);
    return CHSuper(5, AFHTTPSessionManager, POST, arg1, parameters,arg2, progress, arg3, success, arg4, failure, arg5);
}

__attribute__((constructor)) static void entry()
{
    CHLoadLateClass(AFHTTPSessionManager);
    CHClassHook(5, AFHTTPSessionManager, POST, parameters, progress, success, failure);
}

1.1 登錄請求分析

接下來,打開App進行登錄,看看發送了哪些內容。打印信息如下:

https://app.xxxxtech.com/GetAndroidDataService.svc/LoginVerify
{
    jsonData = 0e10de0e07c7f127d43b326cb30ceac2566b2dfabd8facbd431e7e31708ffaf8bb310fd0abd77570e10fa3a61c36aac11261b59889c0aa606b64cbde9f43d6814503211a0e0a2a55552646c393e8e9fa72ab0703685cf19308f9a16522d58e405874fa9956a40a99313b01cdb6d3eab702019d3a2eedd819b0a2fab032a8fff52258133eb42b828a80fd4d15df199f4ab83cd4a2df67a5fac0906694ede697abde35c29587de9ca894036f785f0c2d94164a363cb6e92c4a0072d21e1f8b9b0843b1c2af7c90d3c89117318ef8e1c86fa32f99ec9182d57b5191a8e10ad0d80e6ee8de8cb0a3398be971e5e261d77b93858ed103b91af91570a832a95f42ac658737739cd60f46920d21b1154158a0ecae8f8cb7dbcb2ac6c3894e51a290c033715f11bb9e3ce18cb48429f4e5ba69be4e7d17861b355d96ce64d8ac472fafcf04ed891f29a98fa276086fc4d57d10ca;
}

得到請求url 【https://app.xxxxtech.com/GetAndroidDataService.svc/LoginVerify

參數是字典格式NSDictionary<NSString *, NSString *>的數據,key值為 jsonData,value值是一串很長的字符串,顯然是加密過的。單從value的字符串形式來看,是由十六進制格式的字符組成的。可能是某種加密方式與MD5的結合,但如果進行了MD5,服務端是無法解析數據的;App和服務采用對稱加密的方法最常用,AES/DES等,這里有可能先進行了對稱加密,再將每個字符進行轉換。如果能找到原始的請求參數、加密方法,整個網絡請求層應該都可以破解掉了。

1.2 請求回溯

要找到原始的參數,就需要找到哪個類調用了POST方法。然后往前一步一步回溯,將每個步驟串起來,就可以倒推網絡請求的流程。

借助Hopper,搜索字符串POST:parameters:progress:success:failure:,發現ServiceUtil中的幾個方法使用了:

queryService:
queryListService:
updateArrayService:
updateService:

POST.png

登錄一般只是檢驗密碼,hook住queryService,打印請求參數類型,確實有相應的輸出:

UserInfoModel

進一步分析,queryService并沒有被其他類直接調用,而是通過在RegisteredServicesMonitor函數內,注冊xxxx_MOBILE_ServiceQuery的通知方法被動調用的。

void -[ServiceUtil RegisteredServicesMonitor](void * self, void * _cmd) {
   ...
    r0 = [NSNotificationCenter defaultCenter];
    r0 = [r0 retain];
    stack[2032] = r0;
    _objc_msgSend(r0, *r0, self, @selector(queryService:), @"xxxx_MOBILE_ServiceQuery", 0x0);
    [stack[2032] release];
   ...
    return;
}

搜索通知xxxx_MOBILE_ServiceQuery,再根據[LoginViewController login]偽代碼,將整個代碼調用順序串起來,最終發現參數通過DESUtil類進行加密:

Login.png

2 加密分析

整個請求的流程清晰了,回過頭來看UserInfoModel是如何轉化為成十六進制字符串的。

queryService的參數是在ServiceUtil類的函數dictionaryFormQueryData被轉化了,具體過程分為兩步:

  • 模型轉字典

先用UserInfoModel對象的convertToUpdateDictionary 方法(其中敏感信息FItemNumber、FPassword作了隱藏處理),將對象轉化成字典結構

{
    FAppVersion = "v4.0.2.Basics (51)";
    FItemNumber = *****;
    FMobileType = IOS;
    FModuleId = 2990;
    FOSVersion = "10.3.2";
    FPassword = "*****";
    FVerSion = 51;
}
  • 字典轉查詢條件、并加密
    ObjectForDesAndReturnData函數內,將前面步驟的字典轉化成加密的字符串,并組成新的字典,作為請求的參數
{
    jsonData = 0e10de0e07c7f127d43b326cb30ceac25...
    }

2.1 DES加密

進入最終的加密函數 [DESUtil doCipher:key:context:],查看偽代碼:

void * +[DESUtil doCipher:key:context:](void * self, void * _cmd, void * arg2, void * arg3, unsigned int ret_addr) {
    r7 = (sp - 0x14) + 0xc;
    sp = sp - 0x1a4;
    var_20 = *___stack_chk_guard;
    objc_storeStrong(r7 - 0x2c, arg2);
    objc_storeStrong(r7 - 0x30, arg3);
    var_34 = ret_addr;
    var_3C = 0x0;
    if (var_34 == 0x1) {
            ...
    }
    else {
            r0 = [0x0 dataUsingEncoding:0x4, r0];
            r7 = r7;
            r0 = [r0 retain];
            var_C8 = r0;
            r1 = var_3C;
            var_3C = [r0 mutableCopy];
            [r1 release];
            [var_C8 release];
    }
    r0 = [0x0 dataUsingEncoding:0x4, r0];
    r7 = r7;
    r0 = [r0 retain];
    var_60 = [r0 mutableCopy];
    [r0 release];
    [var_60 setLength:0x8, r0];
    var_68 = [0x0 length] + 0x8 & 0xfffffff8;
    var_64 = malloc(var_68);
    __memset_chk();
    var_F0 = [objc_retainAutorelease(var_60) bytes];
    var_F8 = [var_60 length];
    *(r7 - 0x100) = [objc_retainAutorelease(var_60) bytes];
    objc_retainAutorelease(var_3C);
    *((r7 - 0x100) + 0xfffffffffffffffc) = _objc_msgSend;
    *((r7 - 0x100) + 0xfffffffffffffff8) = (*((r7 - 0x100) + 0xfffffffffffffffc))();
    *((r7 - 0x100) + 0xfffffffffffffff4) = _objc_msgSend;
    r2 = *((r7 - 0x100) + 0xfffffffffffffff4);
    *((r7 - 0x100) + 0xfffffffffffffff0) = (r2)(var_3C, @selector(length), r2, var_3C);
    *(var_34 + 0xffffffffffffffec) = 0x1;
    r12 = *(var_34 + 0xffffffffffffffec);
    *(var_34 + 0xffffffffffffffe8) = r7 - 0x6c;
    *(var_34 + 0xffffffffffffffe4) = var_64;
    var_70 = 0x0;
    *((r7 - 0x100) + 0xffffffffffffffe0) = CCCrypt(var_34, 0x1, r12, var_F0, var_F8, *(r7 - 0x100), *((r7 - 0x100) + 0xfffffffffffffff8), *((r7 - 0x100) + 0xfffffffffffffff0), *((r7 - 0x100) + 0xffffffffffffffe4), var_68, *((r7 - 0x100) + 0xffffffffffffffe8));
    if (var_34 == 0x1) {
           ...
    }
    else {
            *((r7 - 0x100) + 0xffffffffffffffc8) = _objc_msgSend;
            r0 = (*((r7 - 0x100) + 0xffffffffffffffc8))(@class(NSData), @selector(dataWithBytes:length:), var_64, 0x0);
            r7 = r7;
            var_74 = [r0 retain];
            free(var_64);
            *((r7 - 0x100) + 0xffffffffffffffc4) = _objc_msgSend;
            r2 = *((r7 - 0x100) + 0xffffffffffffffc4);
            (r2)(@class(NSMutableString), @selector(alloc), r2);
            *((r7 - 0x100) + 0xffffffffffffffc0) = @"";
            r3 = *((r7 - 0x100) + 0xffffffffffffffc0);
            *((r7 - 0x100) + 0xffffffffffffffbc) = _objc_msgSend;
            var_78 = (*((r7 - 0x100) + 0xffffffffffffffbc))();
            objc_retainAutorelease(var_74);
            *((r7 - 0x100) + 0xffffffffffffffb8) = _objc_msgSend;
            var_7C = (*((r7 - 0x100) + 0xffffffffffffffb8))();
            var_80 = 0x0;
            do {
                    *((r7 - 0x100) + 0xffffffffffffffb4) = _objc_msgSend;
                    r3 = *((r7 - 0x100) + 0xffffffffffffffb4);
                    *((r7 - 0x100) + 0xffffffffffffffb0) = var_80;
                    if (*((r7 - 0x100) + 0xffffffffffffffb0) >= (r3)(var_74, @selector(length), var_80, r3)) {
                        break;
                    }
                    s0 = *@"%x";
                    *((r7 - 0x100) + 0xffffffffffffffac) = @"%x";
                    *((r7 - 0x100) + 0xffffffffffffffa8) = _objc_msgSend;
                    r0 = (*((r7 - 0x100) + 0xffffffffffffffa8))(@class(NSString), @selector(stringWithFormat:), *((r7 - 0x100) + 0xffffffffffffffac), s0 & 0xff);
                    r7 = r7;
                    var_84 = [r0 retain];
                    *((r7 - 0x100) + 0xffffffffffffffa4) = _objc_msgSend;
                    r2 = *((r7 - 0x100) + 0xffffffffffffffa4);
                    if ((r2)(var_84, @selector(length), r2) == 0x1) {
                            r2 = *(("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics" | 0x2c0000) + 0x4bd9c);
                            *((r7 - 0x100) + 0xffffffffffffffa0) = @"0%@";
                            *((r7 - 0x100) + 0xffffffffffffff9c) = _objc_msgSend;
                            r1 = r2;
                            r2 = *((r7 - 0x100) + 0xffffffffffffffa0);
                            r12 = *((r7 - 0x100) + 0xffffffffffffff9c);
                            *((r7 - 0x100) + 0xffffffffffffff98) = var_78;
                            r0 = (r12)(@class(NSString), r1, r2, var_84);
                            r0 = [r0 retain];
                            r3 = *((r7 - 0x100) + 0xffffffffffffff98);
                            *((r7 - 0x100) + 0xffffffffffffff94) = r0;
                            r0 = r3;
                            *((r7 - 0x100) + 0xffffffffffffff90) = _objc_msgSend;
                            r2 = *((r7 - 0x100) + 0xffffffffffffff94);
                            r3 = *((r7 - 0x100) + 0xffffffffffffff90);
                            r0 = (r3)(r0, @selector(stringByAppendingString:), r2, r3);
                            r7 = r7;
                            r0 = [r0 retain];
                            *((r7 - 0x100) + 0xffffffffffffff8c) = r0;
                            *((r7 - 0x100) + 0xffffffffffffff88) = _objc_msgSend;
                            r2 = *((r7 - 0x100) + 0xffffffffffffff88);
                            r1 = var_78;
                            var_78 = (r2)(r0, @selector(mutableCopy), r2, r0);
                            [r1 release];
                            r0 = *((r7 - 0x100) + 0xffffffffffffff8c);
                            [r0 release];
                            r0 = *((r7 - 0x100) + 0xffffffffffffff94);
                            [r0 release];
                    }
                    else {
                            *((r7 - 0x100) + 0xffffffffffffff84) = @"%@";
                            *((r7 - 0x100) + 0xffffffffffffff80) = _objc_msgSend;
                            r2 = *((r7 - 0x100) + 0xffffffffffffff84);
                            r12 = *((r7 - 0x100) + 0xffffffffffffff80);
                            *((r7 - 0x100) + 0xffffffffffffff7c) = var_78;
                            r0 = (r12)(@class(NSString), @selector(stringWithFormat:), r2, var_84);
                            r0 = [r0 retain];
                            r3 = *((r7 - 0x100) + 0xffffffffffffff7c);
                            *((r7 - 0x100) + 0xffffffffffffff78) = r0;
                            r0 = r3;
                            *((r7 - 0x100) + 0xffffffffffffff74) = _objc_msgSend;
                            r2 = *((r7 - 0x100) + 0xffffffffffffff78);
                            r3 = *((r7 - 0x100) + 0xffffffffffffff74);
                            r0 = (r3)(r0, @selector(stringByAppendingString:), r2, r3);
                            r7 = r7;
                            r0 = [r0 retain];
                            *((r7 - 0x100) + 0xffffffffffffff70) = r0;
                            *((r7 - 0x100) + 0xffffffffffffff6c) = _objc_msgSend;
                            r2 = *((r7 - 0x100) + 0xffffffffffffff6c);
                            r1 = var_78;
                            var_78 = (r2)(r0, @selector(mutableCopy), r2, r0);
                            [r1 release];
                            r0 = *((r7 - 0x100) + 0xffffffffffffff70);
                            [r0 release];
                            r0 = *((r7 - 0x100) + 0xffffffffffffff78);
                            [r0 release];
                    }
                    objc_storeStrong(r7 - 0x84, 0x0);
                    var_80 = var_80 + 0x1;
            } while (true);
            objc_storeStrong(r7 - 0x70, var_78);
            objc_storeStrong(r7 - 0x78, 0x0);
            objc_storeStrong(r7 - 0x74, 0x0);
    }
    *((r7 - 0x100) + 0xffffffffffffff68) = [var_70 retain];
    objc_storeStrong(r7 - 0x70, 0x0);
    objc_storeStrong(r7 - 0x60, 0x0);
    objc_storeStrong(r7 - 0x3c, 0x0);
    objc_storeStrong(r7 - 0x30, 0x0);
    objc_storeStrong(r7 - 0x2c, 0x0);
    r0 = *((r7 - 0x100) + 0xffffffffffffff68);
    r0 = [r0 autorelease];
    r1 = *___stack_chk_guard;
    *((r7 - 0x100) + 0xffffffffffffff64) = r0;
    if (r1 == var_20) {
            r0 = *((r7 - 0x100) + 0xffffffffffffff64);
    }
    else {
            r0 = __stack_chk_fail();
    }
    return r0;
}

代碼很長,看起來很頭疼,其實也沒必要一行一行去看懂。利用CCCrypt的各個參數進行對比分析,大概的加密邏輯也是很容易推測出來的。

CCCryptorStatus CCCrypt(
    CCOperation op,         /* kCCEncrypt, etc. */
    CCAlgorithm alg,        /* kCCAlgorithmAES128, etc. */
    CCOptions options,      /* kCCOptionPKCS7Padding, etc. */
    const void *key,
    size_t keyLength,
    const void *iv,         /* optional initialization vector */
    const void *dataIn,     /* optional per op and alg */
    size_t dataInLength,
    void *dataOut,          /* data RETURNED here */
    size_t dataOutAvailable,
    size_t *dataOutMoved)
  • op,操作類型,由變量var_34控制,即doCipher:key:context最后一個參數,為0時,進行加密操作
    enum { kCCEncrypt = 0, kCCDecrypt, };

  • alg,加密算法,偽代碼為0x1,即kCCAlgorithmDES,為DES加密方式

enum {
    kCCAlgorithmAES128 = 0,
    kCCAlgorithmAES = 0,
    kCCAlgorithmDES,
    kCCAlgorithm3DES,       
    kCCAlgorithmCAST,       
    kCCAlgorithmRC4,
    kCCAlgorithmRC2,   
    kCCAlgorithmBlowfish    
};
typedef uint32_t CCAlgorithm;
  • options,代碼對應變量r12 = 0x1,對應枚舉為kCCOptionPKCS7Padding
*(var_34 + 0xffffffffffffffec) = 0x1;
r12 = *(var_34 + 0xffffffffffffffec)
  • key,密鑰,對應到doCipher:key:context的第2個參數,需要轉化成char *型,在[DESUtil encode]中,可以找到相應的密鑰02****5a(8位,屬于敏感信息,中間4位隱藏處理),偽代碼 對應 var_F0的值:
[[DESUtil doCipher:r0 key:@"02****5a" context:stack[2033], stack[2034], stack[2035]] retain]


var_F0 = [objc_retainAutorelease(var_60) bytes]
  • keyLength,密鑰長度,偽代碼var_F8 = [var_60 length]
  • iv,加密向量,代碼對應(r7 - 0x100)的值,與var_F0一致
*(r7 - 0x100) = [objc_retainAutorelease(var_60) bytes];
  • dataIn,需要加密的內容,即doCipher:key:context的1個參數
  • dataInLength,加密內容的長度
  • dataOut,加密后的內容
  • dataOutAvailable,加密后內容長度
var_68 = [0x0 length] + 0x8 & 0xfffffff8;
  • dataOutMoved,輸出值,不用關心

至此,明確了函數使用DES加密方式,并且加密向量與密鑰相同,繼續住下分析。

2.2 DES結果處理

  • 先將加密后的內容轉化成NSData值:
r0 = (*((r7 - 0x100) + 0xffffffffffffffc8))(@class(NSData), @selector(dataWithBytes:length:), var_64, 0x0)
  • 按字節讀取NSData值,并轉化成十六進制格式的字符串r2:
 r3 = *((r7 - 0x100) + 0xffffffffffffffb4);
 *((r7 - 0x100) + 0xffffffffffffffb0) = var_80;
 if (*((r7 - 0x100) + 0xffffffffffffffb0) >= (r3)(var_74, @selector(length), var_80, r3)) {
        break;
   }
  s0 = *@"%x";
  *((r7 - 0x100) + 0xffffffffffffffac) = @"%x";
  *((r7 - 0x100) + 0xffffffffffffffa8) = _objc_msgSend;
  r0 = (*((r7 - 0x100) + 0xffffffffffffffa8))(@class(NSString), @selector(stringWithFormat:), *((r7 - 0x100) + 0xffffffffffffffac), s0 & 0xff);
  r7 = r7;
  var_84 = [r0 retain];
  *((r7 - 0x100) + 0xffffffffffffffa4) = _objc_msgSend;
  r2 = *((r7 - 0x100) + 0xffffffffffffffa4);
  • 判斷r2長度,如果長度為1,則在前面補0;這也是為什么在解密代碼中,會有高16位、低16位判斷的原因
if ((r2)(var_84, @selector(length), r2) == 0x1) {
   r2 = *(("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics" | 0x2c0000) + 0x4bd9c);
   *((r7 - 0x100) + 0xffffffffffffffa0) = @"0%@";
   *((r7 - 0x100) + 0xffffffffffffff9c) = _objc_msgSend;
   r1 = r2;
   r2 = *((r7 - 0x100) + 0xffffffffffffffa0);
   r12 = *((r7 - 0x100) + 0xffffffffffffff9c);
  ...
   r0 = *((r7 - 0x100) + 0xffffffffffffff94);
  [r0 release];
}
  • 其實整個循環,就是將NSData的值按字節轉化成十六進制格式的字符串:
    比如字符串“a”,經過上述DES加密后,得到的NSData值為<ed531e0b 195ec5b7>,轉化成字符串后為 ed531e0b195ec5b7,即為最終加密的結果。而通常AES/DES加密后,會將結果直接轉換成Base64。

為了測試方便,在NSString增加了一個DES的類別,實現與偽代碼相似的功能,具體見附錄。

3 ServiceUtil

網絡請求部分都在ServiceUtil里面,設置AFNetWorking參數、HTTP請求頭等。

3.1 請求頭設置

看偽代碼,只是簡單設置了AcceptUser-agentAccept-LanguageContent-TypeContent-Length這幾個值,并沒有做過多的校驗,通過WEB模擬發送POST請求,應該也能通過。

void -[ServiceUtil setRequestHead:len:](void * self, void * _cmd, void * arg2, void * arg3) {
    objc_storeStrong((sp - 0x54) + 0x40, arg2);
    objc_storeStrong((sp - 0x54) + 0x3c, arg3);
    [0x0 addValue:@"application/json" forHTTPHeaderField:@"Accept", stack[2027], stack[2028], stack[2029]];
    [0x0 addValue:@"Mozilla/5.0" forHTTPHeaderField:@"User-agent", stack[2027], stack[2028], stack[2029]];
    [0x0 addValue:@"ZH-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4" forHTTPHeaderField:@"Accept-Language", stack[2027], stack[2028], stack[2029]];
    [0x0 addValue:@"application/json" forHTTPHeaderField:@"Content-Type", stack[2027], stack[2028], r2];
    [0x0 addValue:0x0 forHTTPHeaderField:@"Content-Length", r1, @"Content-Length"];
    objc_storeStrong((sp - 0x54) + 0x3c, 0x0);
    objc_storeStrong((sp - 0x54) + 0x40, 0x0);
    return;
}

3.2 HTTP端口

代碼內部有一個HTTP的端口:http://app.xxxxtech.com:8080,結合登錄url,可以推測,其他的請求都是用特定的功能字符串拼接起來的,用web模擬,發現8080端口也是有效的。
http://app.xxxxtech.com:8080/GetAndroidDataService.svc/xxxx
https://app.xxxxtech.com/GetAndroidDataService.svc/xxxx

void * -[ServiceUtil http](void * self, void * _cmd) {
    stack[2043] = r4;
    r7 = (sp - 0x14) + 0xc;
    *((sp - 0x14) + 0xfffffffffffffffc) = r8;
    sp = (sp - 0x14) + 0xfffffffffffffffc - 0x40;
    stack[2042] = self;
    if (stack[2042]->_http == 0x0) {
            r0 = (*@"%@%@%@")(@class(NSString), @selector(stringWithFormat:), @"%@%@%@", @"http://", @"app.xxxxtech.com:8080", @"/");
            r0 = [r0 retain];
            stack[2034] = r0;
            r0 = (*r0)(@class(NSMutableString), @selector(stringWithFormat:), @"%@%@%@", stack[2034], @"GetAndroidDataService.svc", @"/");

    ...
    }
    r0 = stack[2042]->_http;
    r0 = loc_239340(r0, *0x31aa04);
    return r0;
}

4 安全檢驗

登入App后,試了其他幾個請求,發現請求參數、請求頭并沒有登錄返回的FToken信息,難道登錄只是進入App的殼子,后續的請求根本不需要檢驗?找一個接口一試究竟。

查詢打卡時間的接口,參數只有工號是可變的:

https://app.xxxxtech.com/GetAndroidDataService.svc/GetCheckStatusData

{
  "FOSVersion" : "10.3.2",
  "FMobileType" : "IOS",
  "FAppVersion" : "v4.0.2.Basics (51)",
  "FModuleId" : "3093",
  "FItemNumber" : "*****"
}

FItemNumber修改為其他的5位數,并對參數加密,得到請求參數(敏感信息隱藏處理):

{"jsonData":"0e10de0e07c7f12758af1e31ffdea690c040bb8ecab59985f118ebfbb6d0500cafaa48ff3194a97be6eb6053ec6c6206db03e151be4b528d78db3becbcb1629fc29d0e049cff1a27e5584d684d8b78e7a925a47801cd1511d6a98c7b1c33debbe446eed7fe7674987e52e6b64bd3f73fb9ebfb7a986b5c16537..."}

使用web發送請求,可以得到正常返回:

    "FID": "",
    "IsSuccess": true,
    "Result": "{\"FStatus\":0,\"FAttendId\":0,\"FCheckInTime\":\"**:**\",\"FCheckOutTime\":\"22:04\"}",
    "ResultCode": 200
}

再試其他接口,也是可以直接通過的,可見除了登錄協議外,其他協議都沒有做安全性校驗。

5 總結

雖然App是內部用的,但有幾點還是可以再提高一下的:

增加請求頭、安全檢驗
增加DES加密的復雜度
關閉8080端口
關鍵幾處函數進行代碼混淆,反正不需要AppStore審核

附錄:加解密代碼

#import "NSString+DES.h"
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>

@implementation NSString (DES)

- (NSData *)jm_hexStringConvertToBytesData
{
    //異常字符串
    if (self.length % 2 != 0) {
        return nil;
    }
    
    Byte bytes[1024*3] = {0};
    int bytesIndex = 0;
    
    for(int i = 0; i < [self length]; i++)
    {
        int int_char;  /// 兩位16進制數轉化后的10進制數
        
        unichar hex_charUpper = [self characterAtIndex:i]; ///兩位16進制數中的第一位(高位*16)
        int int_charUpper;
        if(hex_charUpper >= '0' && hex_charUpper <='9') {
            int_charUpper = (hex_charUpper - 48 ) * 16;   // 0 的Ascll - 48
        } else if(hex_charUpper >= 'A' && hex_charUpper <= 'F') {
            int_charUpper = (hex_charUpper - 55 ) * 16; /// A 的Ascll - 65
        } else {
            int_charUpper = (hex_charUpper - 87 ) * 16; // a 的Ascll - 97
        }
        
        i++;
        
        unichar hex_charLower = [self characterAtIndex:i]; ///兩位16進制數中的第二位(低位)
        int int_charLower;
        if(hex_charLower >= '0' && hex_charLower <= '9') {
            int_charLower = (hex_charLower - 48); /// 0 的Ascll - 48
        } else if(hex_charUpper >= 'A' && hex_charUpper <='F') {
            int_charLower = (hex_charLower - 55); ///  A 的Ascll - 65
        } else {
            int_charLower = hex_charLower - 87; /// a 的Ascll - 97
        }
        
        int_char = int_charUpper + int_charLower;
        bytes[bytesIndex] = int_char;  ///將轉化后的數放入Byte數組里
        bytesIndex++;
    }
    
    NSUInteger dataLength = self.length / 2;
    NSData *data = [[NSData alloc] initWithBytes:bytes length:dataLength];
    return data;
}

- (NSString *)jm_urlDecode {
    NSString *decodedString = [self stringByRemovingPercentEncoding];
    return decodedString;
}

- (NSString *)jm_urlEncode {
    NSString *encodedString = [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!*'();:@&=+$,/?%#[]"]];
    return encodedString;
}

- (NSString *)jm_encryptUseDESByKey:(NSString *)key iv:(NSString *)iv
{
    NSString *ciphertext;
    NSString *encode = [self jm_urlEncode];
//    NSLog(@"%s encode::%@", __func__, encode);
    
    NSData *data = [encode dataUsingEncoding:NSUTF8StringEncoding];
    NSUInteger dataLength = data.length;
    NSUInteger bufferLength = 1024;
    unsigned char buffer[bufferLength];
    memset(buffer, 0, sizeof(char));
    
    size_t numBytesEncrypted = 0;

    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          kCCAlgorithmDES,
                                          kCCOptionPKCS7Padding,
                                          [key UTF8String],
                                          kCCKeySizeDES,
                                          [iv UTF8String] , //iv向量
                                          [data bytes],
                                          dataLength,
                                          buffer,
                                          bufferLength,
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
        //NSLog(@"%s buffer::%s", __func__, buffer);
        //NSLog(@"%s data::%@", __func__, data);
    
        ciphertext = @"";
        for (int index = 0; index < data.length; index++) {
            char byte;
            [data getBytes:&byte range:NSMakeRange(index, 1)];
            NSString *text = [NSString stringWithFormat:@"%x", byte&0xff];
            
            //不足兩位,前面補0
            if([text length] == 1) {
                text = [NSString stringWithFormat:@"0%@", text];
            }
            
            ciphertext = [ciphertext stringByAppendingString:text];
        }
    }
    
    NSLog(@"%s encryptText::%@", __func__, ciphertext);
    return ciphertext;
}

- (NSString *)jm_decryptUseDesByKey:(NSString *)key iv:(NSString *)iv
{
    NSString *decryptText;
    
    NSData *encryptData = [self jm_hexStringConvertToBytesData];
    const char *textBytes = [encryptData bytes];
    
    NSUInteger dataLength = encryptData.length;
    NSUInteger bufferLength = dataLength + 0x8 & 0xfffffff8;
    unsigned char buffer[bufferLength];
    memset(buffer, 0, sizeof(char));

    size_t numBytesEncrypted = 0;
    
    //將encryptText轉化為bytes
    CCCryptorStatus decryptStatus = CCCrypt(kCCDecrypt,
                                            kCCAlgorithmDES,
                                            kCCOptionPKCS7Padding,
                                            [key UTF8String],
                                            kCCKeySizeDES,
                                            [iv UTF8String] , //iv向量
                                            textBytes,
                                            dataLength,
                                            buffer,
                                            bufferLength,
                                            &numBytesEncrypted);
    if (decryptStatus == kCCSuccess ) {
        
        NSLog(@"%s buffer::%s", __func__, buffer);
        NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
        
        NSLog(@"%s data::%@", __func__, data);
        
        decryptText = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%s decryptText::%@", __func__, decryptText);
        
        decryptText = [decryptText jm_urlDecode];
        NSLog(@"%s decodeUrl::%@", __func__, decryptText);
        
    }
    
    return decryptText;
}
@end
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容

  • 1.ios高性能編程 (1).內層 最小的內層平均值和峰值(2).耗電量 高效的算法和數據結構(3).初始化時...
    歐辰_OSR閱讀 29,478評論 8 265
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,782評論 18 139
  • 夜太濃 它一定是硌疼了你匆匆趕路的高跟鞋 匆匆 它們如玫瑰一樣高貴的紅 是夜色中的王后 而陽光 是它們背棄的白裙 ...
    吾乃某山某某某閱讀 149評論 0 0
  • 單曲循環了三天的歌 今天終于去看了電影 有點浮夸 可是感情很細膩 很細微 很純真 很好看 還有 今天下了初...
    MaoMaoMei閱讀 314評論 5 0
  • 今年的生日恰逢周末,又趕上了麗水市圖書館的朗讀者之夜。吃過晚飯,我騎著小毛驢穿過半個城市去圖書館里參加此次活動。一...
    丁蜻閱讀 537評論 0 0