飄云閣(PYG官方)

 找回密碼
 加入論壇

QQ登錄

只需一步,快速開始

掃一掃,訪問微社區

查看: 554|回復: 18
打印 上一主題 下一主題

[macOS] 手把手教你破解 MacOS 系列之 Bartender 3 - 操作實戰5步走

[復制鏈接]
  • TA的每日心情
    開心
    2019-3-17 22:44
  • 簽到天數: 132 天

    [LV.7]常住居民III

    跳轉到指定樓層
    樓主
    發表于 2019-9-19 02:33:54 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
    本帖最后由 tree_fly 于 2019-9-19 09:36 編輯

    手把手教你破解MacOS系列之Bartender 3
    逆向分析、敏捷劫持、追溯爆破、算法推算、公鑰替換、注冊機


    【破文作者】tree_fly/P.Y.G
    【作者郵箱】[email protected]
    【作者主頁】itreefly.com
    【破解平臺】MacOS
    【破解聲明】請勿商用;本文僅做研究所用。





    一、開始
      大家好,我是tree_fly。歡迎來到飄云閣。今天來分析一款macOS平臺的軟件(這是一款管理菜單欄圖標的軟件 Bartender 3 v3.0.64),嘗試從不同的角度慢慢切入,盡量闡述清晰,偏于操作實戰,易于上手,最重要的是一起享受沉浸逆向的愉快時光。


    Bartender 3 官網:www.macbartender.com/



    二、收集資料


    先簡單分析一些基礎的信息,比如注冊界面及功能限制的內容





    • 采用用戶名及注冊碼驗證模式
    • 沒有網絡驗證
    • 4周時間全功能試用
    • 試用期結束后功能失效,彈過期購買窗口




    三、逆向實戰

    收集完一些基本資料后,啟動神器[Hopper V4],載入`/Applications/Bartender 3.app/Contents/MacOS/Bartender 3`分析。根據一些關鍵詞如`license`,快速切入到注冊驗證相關的函數。
    Hopper V4是Hopper Disassembler V4的簡稱,官網:https://www.hopperapp.com/ ,v4.0.8是一個[特別的版本](https://chinapyg.com/thread-87967-1-1.html)




    依據經驗,`[Class* isLicensed]`這樣的Bool返回值函數是要重點關注的。

    **操作實戰 1**

    說做就做,先注入到程序,讓所有`[Class* isLicensed]`返回true,測試一下,看看好用不好用。

    這次我們換個花樣玩,不在Hopper內手動修改函數開頭的匯編代碼:

    [Asm] 純文本查看 復制代碼
    /* return true */
    mov rax, 0x1
    ret


    采用敏捷調試方案 **Frida**。

    正常運行Bartender 3,打開iTerm(或系統的終端),輸入注入代碼:

    [Bash shell] 純文本查看 復制代碼
    ~ frida-trace -m "-[*Bartender* *icense*]" "Bartender 3"


    操作無誤的情況下,終端會顯示如下信息:
    [Bash shell] 純文本查看 復制代碼
    Instrumenting functions...
    -[Bartender_3.AboutPreferencesController isLicensed]: Auto-generated handler at "/Users/lg/__handlers__/__Bartender_3.AboutPreferencesCo_07d0e87a.js"
    -[Bartender_3.LicensePreferencesController licenseBartenderWithSender:]: Auto-generated handler at "/Users/lg/__handlers__/__Bartender_3.LicensePreferences_b42e61ac.js"
    -[Bartender_3.LicensePreferencesController licenseSegmentClickedWithSender:]: Auto-generated handler at "/Users/lg/__handlers__/__Bartender_3.LicensePreferences_c4914f22.js"
    -[Bartender_3.LicensePreferencesController isLicensed]: Auto-generated handler at "/Users/lg/__handlers__/__Bartender_3.LicensePreferences_aa4237d6.js"
    -[Bartender_3.AppDelegate showPreferencesLicenseView:]: Auto-generated handler at "/Users/lg/__handlers__/__Bartender_3.AppDelegate_showPr_ad1fa37a.js"
    -[Bartender_3.AppDelegate isLicensed]: Auto-generated handler at "/Users/lg/__handlers__/__Bartender_3.AppDelegate_isLicensed_.js"
    Started tracing 6 functions. Press Ctrl+C to stop.


    最后一行提示共計有6個函數被hook,并且已經在當前工作目錄下創建了“__handlers __”文件夾,其中包含了對應的6個js文件。

    按下**Ctrl+C**中斷,仔細查看終端提示的信息,其中有3個`isLicensed`函數被擊中,分別是:

    [Objective-C] 純文本查看 復制代碼
    -[Bartender_3.AboutPreferencesController isLicensed]
    -[Bartender_3.LicensePreferencesController isLicensed]
    -[Bartender_3.AppDelegate isLicensed]


    接下來要做的是逐一修改對應的js文件,比如打開`-[Bartender_3.AppDelegate isLicensed]`對應的`Bartender_3.AppDelegate_isLicensed_.js`,其內容是:

    [JavaScript] 純文本查看 復制代碼
    {
      onEnter: function (log, args, state) {
        log('-[Bartender_3.AppDelegate isLicensed]');
      },
      onLeave: function (log, retval, state) {
      }
    }


    修改onLeave函數內容,僅僅加入一行代碼:`retval.replace(1);`。代碼意思就是在函數運行結束時修改返回值為1(true)。如下:

    [JavaScript] 純文本查看 復制代碼
    {
      onEnter: function (log, args, state) {
        log('-[Bartender_3.AppDelegate isLicensed]');
      },
      onLeave: function (log, retval, state) {
        retval.replace(1);  /* Just add one line code. */
      }
    }


    其他2個函數的hook代碼修改同上操作,完成了所有的js文件修改后,再次輸入注入代碼:

    [Bash shell] 純文本查看 復制代碼
    ~ frida-trace -m "-[*Bartender* *icense*]" "Bartender 3"


    此時!測試Bartender 3,發現沒有了試用提醒,沒有試用倒計時,也沒有注冊碼輸入框了,活脫脫的已激活狀態。恭喜你!你已經完成了操作實戰 1的內容。

    然而!不要高興的太早,面前的假象容易迷惑了雙眼。4周的試用時間還沒有到呢,怎么知道不會有暗樁呢~

    為了交點智商稅,手動修改下系統時間為4周后,果不其然,功能失效了。不要泄氣,至少目前知道了是該進一步分析`isLicensed`的偽代碼了。


    **操作實戰 2**

    有了操作實戰 1的經驗積累,接下來逐一分析`isLicensed`函數的細節。

    挑選其中一個查看一下偽代碼:

    [C] 純文本查看 復制代碼
    /* @class _TtC11Bartender_311AppDelegate */
    -(char)isLicensed {
        rax = sub_100068310();
        rax = ObjectiveC._convertBoolToObjCBool(rax & 0xff);
        rax = sign_extend_64(rax);
        return rax;
    }


    切換到Hopper的匯編代碼界面,雙擊并進入子函數`sub_100068310`, 右鍵點擊匯編代碼第一行,選擇`References to 0x100068310`查看交叉引用信息。



    細心的你是否發現除了之前打交道的3個`isLicensed`函數,還有3處也在調用注冊驗證函數`sub_10068310`。所以明白了單靠操作實戰 1為什么不會有好下場了吧。

    好了,繼續閱讀`sub_10068310`的偽代碼吧。*為了邏輯看上去更清晰,以下展示的代碼修剪了一些干擾。*

    [C] 純文本查看 復制代碼
    int sub_100068310() {
        r13 = Swift.String() -> __C.NSString(0x8000000000000000 | "license2HoldersName", 0x13);
        rbx = Swift.String() -> __C.NSString(0xe800000000000000, 0x3265736e6563696c);
        r13 = sub_100068b50(var_30, r14, r15, r12, 0xea00000000003272);
        rbx = 0x1;
        if ((r13 & 0x1) == 0x0) {
                rbx = sub_100068f30(var_30, r15, r12, r14);
        }
        rax = rbx;
        return rax;
    }


    `0x3265736e6563696c`轉為字符串是`2esnecil`,字節高低轉位就是`license2`。

    代碼在讀取配置文件,使用**AppDelete**快速查看配置文件的路徑。



    打開配置文件`com.surteesstudiOS.Bartender.plist`,這里記錄了很多信息,但是對分析沒有什么幫助,都不是關鍵點。



    關鍵的是代碼中的2個函數:`sub_100068b50` 和 `sub_100068f30`,從邏輯上看任一函數返回true都能完成驗證。

    簡單看了下兩個函數的偽代碼,都很長,很長,很長,真是眼花繚亂,有沒有什么分析技巧呢?額,反復調試、反復分析代碼就是了。

    靜態分析

    靜態分析,是相對于動態調試而言。對于新手,可以嘗試倒序閱讀偽代碼,更容易理清代碼的邏輯關系。比如`sub_10068b50`,僅有1個`return`,其結尾的代碼是:

    [C] 純文本查看 復制代碼
                }
        }
        else {
                rbx = 0x0;
        }
        rax = rbx;
        return rax;
    }


    這里的else提示false,繼續向上看關注`rbx`寄存器值:

    [C] 純文本查看 復制代碼
                   if (r13 != 0x0) {
                         rbx = sub_1000627c0(r12, r14, r15, var_50, r13);
                   }
                    else {
                         rbx = 0x0;
                   }
             }
        }
        else {
                rbx = 0x0;
        }
        rax = rbx;
        return rax;
    }


    所以`sub_1000627c0`的返回值很重要,假想返回`0x1`就好了。繼續向上,理清邏輯關系。*為了邏輯看上去更清晰,以下展示的代碼修剪了一些干擾。*

    [C] 純文本查看 復制代碼
    int sub_100068b50(int arg0, int arg1, int arg2, int arg3, int arg4) {
        if ((r14 != 0x0) && (rdx != 0x0)) {
                rbx = sub_100062dc0(r13, rbx, rdx, rcx, r8);
                if (rbx != 0x0) {
                    r13 = sub_1000627c0(var_38, var_40, var_58, var_30, rbx);
                    if ((r13 & 0x1) != 0x0) {
                            rbx = 0x1;
                    }
                }
                else {
                    if (r13 != 0x0) {
                        rbx = sub_1000627c0(r12, r14, r15, var_50, r13);
                    }
                    else {
                        rbx = 0x0;
                    }
                }
        }
        else {
                rbx = 0x0;
        }
        rax = rbx;
        return rax;
    }


    縱覽這段代碼,只要`sub_1000627c0`返回`0x1`,一切就都好了。如果進一步細看`sub_1000627c0`,其實已經無限接近注冊碼算法了,算法后面我們再分析,氣氛似乎看起來有些微妙了。

    deep layer  |      func call layer
    -------------------------
      4             |       注冊碼算法層
      3             |      sub_1000627c0
      2             |      sub_10068b50
      1             |        isLicensed

    經過操作實戰 2的分析,已經從`isLicesed`的第一層,逐步分析到了第三層了。恭喜你,少年!練成九陰真經指日可待!

    問題來了,怎樣讓`sub_1000627c0`返回`true`呢?



    **操作實戰 3**

    經過操作實戰 2的分析,接下來要注入函數`sub_1000627c0`并返回`true`。繼續采用敏捷調試方案 **Frida**。

    正常運行Bartender 3,打開iTerm(或系統的終端),輸入注入代碼:

    [Bash shell] 純文本查看 復制代碼
    ~ frida-trace -i "sub_1000627c0" "Bartender 3"


    提示注入失敗,看來參數`-i`對于無符號名的函數不起作用。

    再仔細閱讀一下注釋內容,好像也沒有其他的選項可供使用:

    [Bash shell] 純文本查看 復制代碼
    ~ frida-trace -h
    Usage: frida-trace [options] target
    
    Options:
      --version             show program's version number and exit
      -h, --help            show this help message and exit
      -I MODULE, --include-module=MODULE
                            include MODULE
      -X MODULE, --exclude-module=MODULE
                            exclude MODULE
      -i FUNCTION, --include=FUNCTION
                            include FUNCTION
      -x FUNCTION, --exclude=FUNCTION
                            exclude FUNCTION
      -a MODULE!OFFSET, --add=MODULE!OFFSET
                            add MODULE!OFFSET
      -T, --include-imports
                            include program's imports
      -t MODULE, --include-module-imports=MODULE
                            include MODULE imports
      -m OBJC_METHOD, --include-objc-method=OBJC_METHOD
                            include OBJC_METHOD
      -M OBJC_METHOD, --exclude-objc-method=OBJC_METHOD
                            exclude OBJC_METHOD
      -s DEBUG_SYMBOL, --include-debug-symbol=DEBUG_SYMBOL
                            include DEBUG_SYMBOL
      -q, --quiet           do not format output messages
      -o OUTPUT, --output=OUTPUT


    其實`sub_xxxxxxx`只是Hopper這樣的逆向分析軟件顯示的函數名,內存中就不存在這樣的函數符號名,僅提示函數的偏移地址,而且開啟`ASLR`情況下,每次加載的地址都是隨機地址。說這么多就是告訴你**這句代碼不起作用**。

    對于無符號的函數,怎樣用Frida來hook呢?

    應該要找到程序加載的基地址`module address`,根據地址偏移量`offset`,才能定位到內存中的真實地址`target address`。



    所以,需要一些編程,但不是那么復雜,一起來看一下。

    注入器的代碼 inject.py

    [JavaScript] 純文本查看 復制代碼
    import frida
    import sys
    import codecs
    
    def on_message(message, data):
        print("[{}] => {}".format(message, data))
    
    def main(target_process):
        session = frida.attach(target_process)
    
        with codecs.open(sys.argv[2], 'r', 'utf-8') as f:
            source = f.read()
    
        script = session.create_script(source)
        script.on("message", on_message)
        script.load()
        print("[!] Ctrl+D or Ctrl+Z to detach from instrumented program.\n\n")
        sys.stdin.read()
        session.detach()
    
    
    if __name__ == "__main__":
        main(sys.argv[1])
    



    需要注入的代碼 Bartender3.js

    [JavaScript] 純文本查看 復制代碼
    function get_rva(module, offset) {
        var base_addr = Module.findBaseAddress(module);
        if (base_addr == null)
            base_addr = enum_to_find_module(module);
        console.log(module + ' addr: ' + base_addr);
        var target_addr = base_addr.add(offset);
     
        return target_addr;
    }
     
    var target_addr = get_rva("Bartender 3", 0x627c0);
    console.log("sub_1000627c0 addr: " + target_addr);
    
    Interceptor.attach(target_addr, {
        onEnter: function(args) {
        },
        onLeave: function(retval) {
            console.log("sub_1000627c0 return:" + retval + " replaced: 0x1");
            retval.replace(0x1);
        },
    });


    接下來,驗證奇跡的時刻到了。

    正常運行Bartender 3,打開iTerm(或系統的終端),輸入以下命令:

    [Bash shell] 純文本查看 復制代碼
    ~ python3 inject.py "Bartender 3" Bartender3.js




    **Surprise!!!** 試用提示消失了,軟件為已激活狀態,再調整系統時間至4周后,軟件依然提示激活狀態,并且功能測試正常,這次不是偽破解,是完美爆破!

    所以經過以上Frida的調試,最佳的Patch的方案就是修改`sub_1000627c0`函數開頭的匯編代碼,多么熟悉的味道。

    [Asm] 純文本查看 復制代碼
    /* return true */
    mov rax, 0x1
    ret


    恭喜你,少年!你已完成操作實戰 3的所有項目!并且學會了Frida的其中兩種Hooking Functions方法。是時候進入下一層了。



    **操作實戰 4**

    還有什么比攻破軟件注冊碼驗證機制并且寫出注冊機更興奮的呢?來吧,少年,攻與防的比賽還沒有結束,未來屬于你的,繼續讀下去。

    是時候分析`sub_1000627c0`了。還是那么長、那么長、那么長的代碼,硬著頭皮上吧,負責任的告訴你很快一些特殊的字符串會出現在你的眼前。

    動態調試

    是時候好好展示你的動態調試技術了,提前關閉已經運行的Bartender 3,打開Hopper調試器,清除所有的斷點,點擊運行按鈕(第一個小圖標),稍等片刻。



    程序運行后,打開注冊碼輸入框,輸入用戶名:tree_fly,以及任意密碼:AAAABBBBCCCCDDDDEEEE。在點擊注冊按鈕之前,在`sub_100627c0`函數頭下個斷點,點擊注冊按鈕后程序斷在`sub_100627c0`。

    繼續按下F6,或者上圖的第5個小圖標,逐行Step Over運行匯編代碼。

    很快第一個特殊字符串`SecDecodeTransformCreate`出現了:

    SecDecodeTransformCreate(**_kSecBase32Encoding, &var_60);

    關于Security Transforms API可以參考官方文檔: [Security Transforms Programming Guide-Signing and Verifying](https://developer.apple.com/libr ... ansformsBasics.html)

    看上去是進行Base32解密,莫不會是解密注冊碼?快速向下定位到屬性配置API看看:`SecTransformSetAttribute`。

    在`0000000100062919 call imp___stubs__SecTransformSetAttribute`這一行下斷點,直接點擊運行按鈕,將RIP指向此行。
    [Asm] 純文本查看 復制代碼
    000000010006290b  lea  rcx, qword [rbp+var_60]     ; argument "error" for SecTransformSetAttribute
    000000010006290f  mov  rdi, qword [rbp+var_48]     ; argument "transformRef" for SecTransformSetAttribute
    0000000100062913  mov  rsi, r14                    ; argument "key" for SecTransformSetAttribute
    0000000100062916  mov  rdx, rbx                    ; argument "value" for SecTransformSetAttribute
    0000000100062919  call imp___stubs__SecTransformSetAttribute  ; SecTransformSetAttribute


    如果你不是很熟悉MacOS x64的寄存器傳值規則,細心的Hopper已經幫你注釋出來了。

    沒錯,\$rbx存儲著即將Base32解密的value,看下就知道,切換到Debugger Console標簽,輸入以下調試命令:

    [Asm] 純文本查看 復制代碼
    po $rdx
    <41414141 42424242 43434343 44444444 45454545>
    
    x $rdx
    0x1019a2010: 81 49 28 8e ff ff 1d 02 14 00 00 00 00 00 00 00  .I(.............
    0x1019a2020: 41 41 41 41 42 42 42 42 43 43 43 43 44 44 44 44  AAAABBBBCCCCDDDD
    
    x -c0x40 $rdx
    0x1019a2010: 81 49 28 8e ff ff 1d 02 14 00 00 00 00 00 00 00  .I(.............
    0x1019a2020: 41 41 41 41 42 42 42 42 43 43 43 43 44 44 44 44  AAAABBBBCCCCDDDD
    0x1019a2030: 45 45 45 45 00 00 00 00 00 00 00 00 00 00 00 00  EEEE............
    0x1019a2040: 31 00 00 00 00 00 00 00 1d 00 00 00 00 00 00 00  1...............

    在lldb中,x是 memorry read的縮寫,-c 是顯示字節長度,默認是0x20。

    以上讀取到的正是剛才輸入的注冊碼,看來**是對注冊碼進行Base32解密**。

    為了向正確的道路越走越近,偽造一串Base32加密字符串作為注冊碼:

    Base32EncString("hello, tree_fly") = NBSWY3DPFQQHI4TFMVPWM3DZ

    重新來過,這次注冊碼的輸入框內容輸入新的加密字符串,激活斷點,繼續向下。

    很快一段奇怪的代碼出現了,只見加解密執行API:`SecTransformExecute`,未見加解密屬性配置API:`SecTransformSetAttribute`,以及具體的加解密方案。

    [C] 純文本查看 復制代碼
        rax = sub_100063140(var_70, r14, rdx, var_50, r13, r15, rbx);
        if (0x0 == 0x0) goto loc_100062b41;
    
    loc_100062cc8:
        rax = rbx;
        return rax;
    
    loc_100062b41:
        rax = SecTransformExecute(rdi, &var_60);
        r15 = rax;
        if (r15 == 0x0) goto loc_100062cd9;


    仔細查看代碼,這個7個參數的子函數`sub_100063140`要進入看一看。

    進入`sub_100063140`后,很快看到了 `SecVerifyTransformCreate` 和 `_kSecDigestSHA1`,到這里一切都明朗了。

    *咳咳咳,敲黑板*,聰明的你,看到這些信息,腦海里是不是已經浮現了具體的加解密算法。

    沒錯,就是RSA的數字簽名算法(Sign&Verify)。

    既然考慮RSA算法,兩個問題思考下:

    待加密字符串是什么及采用哪種hash方式?

    公布的公鑰是什么?(為什么不提私鑰,想什么呢,少年。)

    **待加密的字符串及加密方式**

    快速定位到 `SecVerifyTransformCreate` 其后的第一個`SecTransformSetAttribute`,在`000000010006320c         call       imp___stubs__SecTransformSetAttribute`這一行下斷點,繼續運行程序,成功斷在此行。

    [Asm] 純文本查看 復制代碼
    00000001000631fc mov rbx, rax
    00000001000631ff lea rcx, qword [rbp+var_28]  ; argument "error" 
    0000000100063203 mov rdi, r14                 ; argument "transformRef" 
    0000000100063206 mov rsi, r13                 ; argument "key"
    0000000100063209 mov rdx, rbx                 ; argument "value"
    000000010006320c call imp___stubs__SecTransformSetAttribute       ; SecTransformSetAttribute


    輸入調試代碼:

    [Asm] 純文本查看 復制代碼
    po $rdx
    <42617274 656e6465 72322c74 7265655f 666c79>
    
    x -c0x40 $rdx
    0x108fc27c0: 81 49 28 8e ff ff 1d 02 13 00 00 00 00 00 00 00  .I(.............
    0x108fc27d0: 42 61 72 74 65 6e 64 65 72 32 2c 74 72 65 65 5f  Bartender2,tree_
    0x108fc27e0: 66 6c 79 00 00 00 00 00 00 00 00 00 00 00 00 00  fly.............
    0x108fc27f0: ff ff ff ff 00 00 01 00 00 00 00 00 00 00 00 00  ................


    所以待加密的字符串是:“Bartender2,tree_fly”,正是字符串“Bartender2,”與用戶名拼接。

    在反復的調試過程中,容易發現這個加密字符串還是有變化的。
    [Asm] 純文本查看 復制代碼
    x -c0x40 $rdx
    0x100ebef30: 81 49 28 8e ff ff 1d 02 1a 00 00 00 00 00 00 00  .I(.............
    0x100ebef40: 42 61 72 74 65 6e 64 65 72 32 55 70 67 72 61 64  Bartender2Upgrad
    0x100ebef50: 65 2c 74 72 65 65 5f 66 6c 79 00 00 00 00 00 00  e,tree_fly......
    0x100ebef60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................


    一個新的值出現:“Bartender2Upgrade,tree_fly”。

    這也解釋了操作實戰 2中的那句話。

    關鍵的2個函數:`sub_100068b50` 和 `sub_100068f30`,從邏輯上看任一函數返回true都能完成驗證。

    所以,從軟件作者設計注冊碼的角度看,其實分兩類,但是,這不重要了,任選其一即可。

    關于文本的hash方式,已經很明顯了,就是SHA1。

    **公布的公鑰**

    公鑰的藏身之處要多得去了,有的存儲為文件,有的以原文PEM字符串形式存儲在軟件中,有的還要進行解密才能獲取到真正的公鑰。

    搜關鍵詞**SecItemImport**,在下面這行下斷點:

    [Asm] 純文本查看 復制代碼
    ; ================ B E G I N N I N G   O F   P R O C E D U R E ================
    ; Variables:
    ;    outItems: void *, 16
    ;    importKeychain: int, 8
    
     imp___stubs__SecItemImport:        // SecItemImport
    00000001000a62e8  jmp  qword [_SecItemImport_ptr]


    斷下后,按照調用棧逆向尋找,定位到以下位置:

    [Asm] 純文本查看 復制代碼
    0000000100062f29  lea  rdx, qword [rbp+var_50]     ; argument "inputFormat" for SecItemImport
    0000000100062f2d  lea  rcx, qword [rbp+var_48]     ; argument "itemType" for SecItemImport
    0000000100062f31  lea  r9, qword [rbp+var_B8]      ; argument "keyParams" for SecItemImport
    0000000100062f38  mov  esi, 0x0                    ; argument "fileNameOrExtension" for SecItemImport
    0000000100062f3d  xor  r8d, r8d                    ; argument "flags" for SecItemImport
    0000000100062f40  mov  rdi, r13                    ; argument "importedData" for SecItemImport
    0000000100062f43  push rax                         ; argument "outItems" for SecItemImport
    0000000100062f44  push 0x0                         ; argument "importKeychain" for SecItemImport
    0000000100062f46  call imp___stubs__SecItemImport  ; SecItemImport


    參考Hopper的函數參數注釋,importedData對應的\$rdi存儲的是公鑰數據,打印一下:

    [Asm] 純文本查看 復制代碼
    po $r13
    <2d2d2d2d 2d424547 494e2050 55424c49 43204b45 592d2d2d 2d2d0a4d 4948774d 49476f42 67637168 6b6a4f4f 4151424d 49476341 6b45416b 61346f73 3865494d 59375469 6d58696e 51673136 5453754e 456c794d 70744c4e 6b6a680a 70474363 51763331 56446d73 36637630 52397248 6d2f4c69 4c624741 6c495146 36624c55 4f48706f 556a5333 56743947 6b514956 414d394a 7353556c 455a4461 0a415562 43467649 50706c52 3266314e 74416b41 5249652f 35415242 41583252 6676715a 52753465 37737556 65714f64 58614534 4e787064 512b2b48 57473246 790a3035 614d4e56 48367351 55755962 6133725a 78695472 3079716d 31565946 45354450 5a5a3849 36444130 4d41416b 42304b67 7245394a 6a47656c 34653566 58630a7a 6e6c7269 77314f38 48422b2b 316b476a 32793632 635a336b 76663669 76654132 6e5a786b 4e533956 4c443775 30393154 5a737464 4854456f 6b393577 4177430a 64674e7a 0a2d2d2d 2d2d454e 44205055 424c4943 204b4559 2d2d2d2d 2d0a>
    
    x -c0x200 $r13
    0x101998270: 31a3df8dffff1d008414000001000000  1...............
    0x101998280: 7e010000000000007e01000000000000  ~.......~.......
    0x101998290: 00000000000000000000000000000000  ................
    0x1019982a0: 2d2d2d2d2d424547494e205055424c49  -----BEGIN PUBLI
    0x1019982b0: 43204b45592d2d2d2d2d0a4d4948774d  C KEY-----.MIHwM
    0x1019982c0: 49476f42676371686b6a4f4f4151424d  IGoBgcqhkjOOAQBM
    0x1019982d0: 494763416b45416b61346f733865494d  IGcAkEAka4os8eIM
    0x1019982e0: 593754696d58696e516731365453754e  Y7TimXinQg16TSuN
    0x1019982f0: 456c794d70744c4e6b6a680a70474363  ElyMptLNkjh.pGCc
    0x101998300: 5176333156446d733663763052397248  Qv31VDms6cv0R9rH
    0x101998310: 6d2f4c694c6247416c49514636624c55  m/LiLbGAlIQF6bLU
    0x101998320: 4f48706f556a5333567439476b514956  OHpoUjS3Vt9GkQIV
    0x101998330: 414d394a7353556c455a44610a415562  AM9JsSUlEZDa.AUb
    0x101998340: 4346764950706c523266314e74416b41  CFvIPplR2f1NtAkA
    0x101998350: 5249652f35415242415832526676715a  RIe/5ARBAX2RfvqZ
    0x101998360: 527534653773755665714f6458614534  Ru4e7suVeqOdXaE4
    0x101998370: 4e787064512b2b4857473246790a3035  NxpdQ++HWG2Fy.05
    0x101998380: 614d4e5648367351557559626133725a  aMNVH6sQUuYba3rZ
    0x101998390: 786954723079716d3156594645354450  xiTr0yqm1VYFE5DP
    0x1019983a0: 5a5a3849364441304d41416b42304b67  ZZ8I6DA0MAAkB0Kg
    0x1019983b0: 7245394a6a47656c3465356658630a7a  rE9JjGel4e5fXc.z
    0x1019983c0: 6e6c726977314f3848422b2b316b476a  nlriw1O8HB++1kGj
    0x1019983d0: 32793632635a336b7666366976654132  2y62cZ3kvf6iveA2
    0x1019983e0: 6e5a786b4e5339564c44377530393154  nZxkNS9VLD7u091T
    0x1019983f0: 5a7374644854456f6b3935774177430a  ZstdHTEok95wAwC.
    0x101998400: 64674e7a0a2d2d2d2d2d454e44205055  dgNz.-----END PU
    0x101998410: 424c4943204b45592d2d2d2d2d0a0000  BLIC KEY-----...
    


    調試到這里,已經從內存中找到了軟件的公鑰,如何存儲的呢?使用字符串“BEGIN PUBLIC”搜索看看,是不是以原文字符串存儲在軟件中。



    果真如此,那么Patch公鑰不在話下了。

    通過操作實戰 4,我們分析出了RSA數字簽名算法,被加密的文本,加密文本的hash方式SHA1,及公鑰內容。有了這些信息,離注冊機還遠嗎?



    **實戰操作 5**

    來到這里的都是好漢了,少年,你很棒!

    分析公鑰長度

    從公布的PEM格式的公鑰,誰能看出來對應私鑰長度是多少位的請舉手。

    [Plain Text] 純文本查看 復制代碼
    -----BEGIN PUBLIC KEY-----
    MIHwMIGoBgcqhkjOOAQBMIGcAkEAka4os8eIMY7TimXinQg16TSuNElyMptLNkjh
    pGCcQv31VDms6cv0R9rHm/LiLbGAlIQF6bLUOHpoUjS3Vt9GkQIVAM9JsSUlEZDa
    AUbCFvIPplR2f1NtAkARIe/5ARBAX2RfvqZRu4e7suVeqOdXaE4NxpdQ++HWG2Fy
    05aMNVH6sQUuYba3rZxiTr0yqm1VYFE5DPZZ8I6DA0MAAkB0KgrE9JjGel4e5fXc
    znlriw1O8HB++1kGj2y62cZ3kvf6iveA2nZxkNS9VLD7u091TZstdHTEok95wAwC
    dgNz
    -----END PUBLIC KEY-----


    從經驗來看,推測其長度介于1024-2048之間。起初我測試了一下用1024位私鑰對應的公鑰Patch掉作者的公鑰,發現有長度檢測,注冊失敗。所以制造一個長度相同的公鑰迫在眉睫。

    等等,為什么要Patch公鑰呢?看張圖吧,看圖說話。



    繼續回到密鑰長度問題,首先我嘗試用openssl來分析,但是提示錯誤信息:
    [Bash shell] 純文本查看 復制代碼
    ~ openssl rsa -pubin -text -in public.pem
    4415460972:error:06FFF07F:digital envelope routines:CRYPTO_internal:expecting an rsa key:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.250.1/libressl-2.6/crypto/evp/p_lib.c:295:



    然后使用以下2個命令,指定密鑰長度位于 1024-2048之間,采用二分法反復驗證,以期公鑰的長度相同。

    [Bash shell] 純文本查看 復制代碼
    openssl genrsa -out pri.pem 1024
    openssl rsa -in pri.pem -pubout > pub1024.pem



    最終發現長度在1678時恰如其分(不是密鑰長度的唯一值)。少年,長度都幫你算好了,創建自己的密鑰吧。

    [Bash shell] 純文本查看 復制代碼
    openssl genrsa -out pri.1678.pem 1678
    openssl rsa -in pri.1678.pem -pubout > pub.1678.pem





    如何手動Patch公鑰

    有很多的字節編輯軟件,推薦010 Editor,選中字符串后原位貼入即可。16進制字節粘貼的方法選擇Edit->Paste From->Paste from Hex Text。注意仔細檢查替換的正確性。





    用Swift 寫注冊機

    論壇有Go寫的RSA算法注冊機,這里換Swift 5。
    [Bash shell] 純文本查看 復制代碼
    mkdir Bartender3KeyMaker
    cd Bartender3KeyMaker
    swift package init --type executable



    以上創建了Bartender3KeyMaker文件夾以及同名的Swift項目。

    打開Package.swift,添加2個dependencies:

    [Swift] 純文本查看 復制代碼
    // swift-tools-version:5.1
    // The swift-tools-version declares the minimum version of Swift required to build this package.
    
    import PackageDescription
    
    let package = Package(
        name: "Bartender3KeyMaker",
        dependencies: [
            .package(url: "https://github.com/IBM-Swift/BlueRSA", from: "1.0.0"),
            .package(url: "https://github.com/norio-nomura/Base32", from: "0.5.4"),
        ],
        targets: [
    
            .target(
                name: "Bartender3KeyMaker",
                dependencies: ["CryptorRSA", "Base32"]),
            .testTarget(
                name: "Bartender3KeyMakerTests",
                dependencies: ["Bartender3KeyMaker"]),
        ]
    )




    修改main.swift:

    [Swift] 純文本查看 復制代碼
    import CryptorRSA
    import Base32
    
    if #available(OSX 10.12, *) {
        
        print("*** Bartender 3 KeyMaker ***")
        
        let publicKeyPEM = """
    -----BEGIN PUBLIC KEY-----
    MIHwMA0GCSqGSIb3DQEBAQUAA4HeADCB2gKB0i4NHp5du5mNFuKif70Ra4Au7d3s
    5id3pgD5X7IO6oRtDvIqWYVFON2iY01T48hUxN8BCHpbt575PAhT0cV2mUeeElNW
    QxhhXo2VcP98wlbzvTM4+jnwytK7kqNQINjyuxJucm9/Ak7VuIrZpvAR72UHN2dz
    FGKEie4liTy4u7/rYAqlWTjp5GvPgkk9Fspdisjm8MSxpv8q+bO6cY3sUXfN8lHI
    t3HLOOuyEYnhBJ2429xrtveKEAogxagLexmucAyo3J7CYR/D6sPwjJF/SoHykwID
    AQAB
    -----END PUBLIC KEY-----
    """
        
        let privateKeyPEM = """
    -----BEGIN RSA PRIVATE KEY-----
    MIIDyQIBAAKB0i4NHp5du5mNFuKif70Ra4Au7d3s5id3pgD5X7IO6oRtDvIqWYVF
    ON2iY01T48hUxN8BCHpbt575PAhT0cV2mUeeElNWQxhhXo2VcP98wlbzvTM4+jnw
    ytK7kqNQINjyuxJucm9/Ak7VuIrZpvAR72UHN2dzFGKEie4liTy4u7/rYAqlWTjp
    5GvPgkk9Fspdisjm8MSxpv8q+bO6cY3sUXfN8lHIt3HLOOuyEYnhBJ2429xrtveK
    EAogxagLexmucAyo3J7CYR/D6sPwjJF/SoHykwIDAQABAoHSEeoqqiL+swpvB7V9
    ifi34ELhaD8bfekO3Dwm3SbuVpvyf4S4FJ9MMvRUOyXSbAGGINbPDIKXmTGOCBNL
    fMzZbkHxERhyu45NcTjcn5dSJu9lAAM/XMDutjIgJoYqcRtkaRQsUnGPXUnIztPZ
    S8mTiyUkOUwAyjHn5XwEam5mDj42gkrbIZk/S0THnQOdrbSZnnwcq8jmvS3g0xup
    ryjOhKwe2174khAP2bD1emEM5UyygJEcsJV91NTSlqKrPbVPre5DdibGX73aWOCw
    6GMv2l9RAml4eKeKZ9DIr98IJbXBIDjQaTM1HxT3ekAyWm+eiBo0qXsfvZCEOJkS
    KgTzkiQ1GSh9y/N9qmixgCLKiqqsli4vHnMgwsOMnO7Lf913ELFvA7/DgOpLTOTB
    lu3KLaXH+lnx6Obvsdk+6ysCaWHbutRcHcezwrQ6cO77r9IYp9xf8J6RRMqXwOGp
    Gn1Qy2i5pV5KYm9wL1Mmkxr+zymTeJ964EuItPl2a/NMx7ln0UQNBU7oGG5rK1Sc
    xUYWjWJGPyXA/eF80UFDryM/5nG+nA1Nb1vCOQJpFbYQ47Wv/+sKM+qv5d1Lv+ul
    qeYvHiavGSQJR7XZmzIMGX1NZTbaB1cBS3BEDDm7fWhbOoOSmKKyInR5K99o9V70
    eqv/GAFUW+JwZDvi7lHrpm0+TFHQTD9KHYy6et7YhOtnaz1PHLK/AmlDf1/6oh8Y
    Y/FkhvrmnEvFyqPd6X76oJCmfM3Z2N4gmd3zujlKNFx5KRQ7clv9Psx9jO6icgrL
    jtvlRb1n8AnC5Mz+90w2BPj1EI6uqgOYOG4E3xcnX1q+cW2Uaq8ezTCSPDs/Ia4x
    yGECaTg3UbgNnQGhmrwbrtqIt+/aVjiE7UMDCl+svCvaaHHZCY1J/aRqu6JAnURU
    I5qqQ46zTzJR+i9A9jryT1S11sIxuVr5hDmKC+2JA/9ogrwbYuHnCZrCx2P53Bhm
    kDd2Ypk3F7cQTVgQTA==
    -----END RSA PRIVATE KEY-----
    """
    
        let publicKey = try! CryptorRSA.createPublicKey(withPEM: publicKeyPEM)
        let privateKey = try! CryptorRSA.createPrivateKey(withPEM: privateKeyPEM)
    
        print("Please input your name: ", separator: "", terminator: "")
        let userName = readLine() ?? "tree_fly"
        
        let data = "Bartender2,\(userName)".data(using: String.Encoding.utf8)!
        let plainText = CryptorRSA.createPlaintext(with: data)
    
        let signature = try plainText.signed(with: privateKey, algorithm: .sha1)!
        let verification = try plainText.verify(with: publicKey, signature: signature, algorithm: .sha1)
        print("Key Verification: ", verification)
    
        let signBase32String = signature.data.base32EncodedString
        
        print("\nName:", userName)
        print("SN:", signBase32String)
        
    } else {
        print("ERROR(OSX NEED 10.12+)")
    }



    然后運行程序

    [Bash shell] 純文本查看 復制代碼
    swift run



    一切順利情況下,即可正確計算注冊碼:




    或者:

    [Plain Text] 純文本查看 復制代碼
    Name: [url=http://www.mjfyfb.icu]www.mjfyfb.icu[/url]
    SN: BR7K5KAGMS4PIM2KNJOVCLP4XYHWNL6VM7C6XF2DJYZFJPR7CBJ5YUCOQBMQHDFXN6EMI6YT4P4VZ4XL543G733FXYGNRDGKKHPPXKZG2LCZX433BY7BAAQNVJZ54GBNJS6DG5ASX5D3A6LQ24FBKX5NX2A4XOR2NLMY32FOL7PVDN6GZKAAUPBTX3Z65TYBEPMYW76T4XDTPUONUNLXAUNMTPVT37G73C5HNX2QUVC5XOQCUCTTJVXDINP4NYP73FAQTBGWDNZY6LREV6QUKIA3ZYLI26NXR5XLPA4VGBNYK7Z36SXPTUHGUHSRQTMAGGAKJRFX4UNKEMPV





    四、結束語

    本文詳細介紹了操作實戰5步走,少年,出來走一走

    時間倉促,紕漏難免,旨在拓寬思路,拋磚引玉,希望能給讀者帶來一些啟發和實戰操作的引導。


    感謝您的閱讀,歡迎留言。



    格式整齊、排版清秀的PDF在文末,獻上你的PYB吧 ^_^


    tree_fly/P.Y.G


    2019-09-18



    參考資料








    PDF文檔下載:


    本帖子中包含更多資源

    您需要 登錄 才可以下載或查看,沒有帳號?加入論壇

    x

    評分

    參與人數 7威望 +18 飄云幣 +18 收起 理由
    0xcb + 1 + 1 贊一個,這個帖子很給力!
    Dweling + 1 + 1 吃水不忘打井人,給個評分懂感恩!
    dryzh + 5 + 5 哇塞Frida高階
    Master.lu + 1 + 1 PYG有你更精彩!
    small-q + 8 + 8 贊一個,這個帖子很給力!
    分享之源 + 1 + 1 感謝分享!
    不破不立 + 1 + 1 PYG有你更精彩!

    查看全部評分

    分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友 微信微信
    收藏收藏7 轉播轉播 分享分享 分享淘帖 頂 踩 掃碼贊助 微信分享
  • TA的每日心情
    開心
    4 小時前
  • 簽到天數: 699 天

    [LV.9]以壇為家II

    沙發
    發表于 2019-9-19 06:53:55 | 只看該作者
    坐沙發前排學習
    回復 支持 反對

    使用道具 舉報

  • TA的每日心情
    開心
    4 小時前
  • 簽到天數: 957 天

    [LV.10]以壇為家III

    藤椅
    發表于 2019-9-19 07:23:28 | 只看該作者
    飛樹表哥666,回家跟著學習,mac環境還用不習慣.
    回復 支持 反對

    使用道具 舉報

  • TA的每日心情
    開心
    昨天 08:54
  • 簽到天數: 44 天

    [LV.5]常住居民I

    板凳
    發表于 2019-9-19 08:32:37 | 只看該作者
    搬個小板凳坐一會
    回復 支持 反對

    使用道具 舉報

  • TA的每日心情

    昨天 10:08
  • 簽到天數: 1105 天

    [LV.10]以壇為家III

    報紙
    發表于 2019-9-19 09:18:05 | 只看該作者
    得先安裝一個黑蘋果才能跟著操作
    回復 支持 反對

    使用道具 舉報

  • TA的每日心情
    奮斗
    2016-1-13 12:25
  • 簽到天數: 3 天

    [LV.2]偶爾看看I

    地板
    發表于 2019-9-19 09:42:29 | 只看該作者
    感謝吊大的飛樹,火速前來學習
    回復 支持 反對

    使用道具 舉報

  • TA的每日心情
    奮斗
    1 小時前
  • 簽到天數: 289 天

    [LV.8]以壇為家I

    7#
    發表于 2019-9-19 10:08:48 | 只看該作者
    厲害厲害,值得學習。
    回復 支持 反對

    使用道具 舉報

  • TA的每日心情
    開心
    2019-9-30 09:30
  • 簽到天數: 15 天

    [LV.4]偶爾看看III

    8#
    發表于 2019-9-19 12:59:09 | 只看該作者
    精品文章,由淺入深,贊。
    回復 支持 反對

    使用道具 舉報

  • TA的每日心情
    奮斗
    1 小時前
  • 簽到天數: 81 天

    [LV.6]常住居民II

    9#
    發表于 2019-9-19 13:31:44 | 只看該作者
    這個必須要收藏
    回復 支持 反對

    使用道具 舉報

  • TA的每日心情
    奮斗
    2015-10-29 08:08
  • 簽到天數: 3 天

    [LV.2]偶爾看看I

    10#
    發表于 2019-9-19 14:40:35 | 只看該作者
    6到∞大,感謝飛樹博士的圖文教程
    回復 支持 反對

    使用道具 舉報

    您需要登錄后才可以回帖 登錄 | 加入論壇

    本版積分規則

    關閉

    站長推薦上一條 /1 下一條

    快速回復 返回頂部 返回列表
    北京pk10遗漏统计