1. Tải bản cài đặt AutoIT mới nhất

    Chào Khách. Nếu bạn mới tham gia và chưa cài đặt AutoIT.
    Vui lòng vào topic trên để tải bản AutoIT mới nhất nhé
    Dismiss Notice
  2. Quy định và nội quy

    Chào Khách. Vui lòng đọc kỹ nội quy và quy định của diễn đàn
    Để tránh bị ban một cách đáng tiếc nhé!
    Dismiss Notice
  3. Hướng dẫn chèn mã AutoIT trong diễn đàn

    Chào Khách. Vui lòng xem qua bài viết này
    Để biết cách chèn mã AutoIT trong diễn đàn bạn nhé :)
    Dismiss Notice

Thuật toán Garena - Hướng dẫn cách Login và phương thức mã hoá mật khẩu của nó với _HttpRequest

Thảo luận trong 'Ý tưởng - Thuật toán' bắt đầu bởi Huân Hoàng, 12/6/17.

  1. Huân Hoàng

    Huân Hoàng Administrator Thành viên BQT Administrator Super Moderator
    • 93/113

    Tham gia ngày:
    29/9/15
    Bài viết:
    642
    Đã được thích:
    1,181
    - Thực ra có vài cách nhanh để lấy mật khẩu mã hoá là nhúng javascript (đọc bài bên dưới sẽ hiểu là những javascript gì) vào IE (dùng IE Object để làm) hoặc sử dụng hàm _JS_Execute trong UDF _HttpRequest (xem full code login Garena bằng _JS_Execute tại đây: http://teamcodedao.com/forum/index.php?/topic/11-login-garena-gon-nhe-bang-_httprequest-ver14/), nhưng cái mình hướng đến là thuật toán làm hoàn toàn bằng code AutoIt thông thường nên mình sẽ không đề cập đến vấn đề này.

    - Bài viết mang tính chất tìm hiểu, mở rộng khả năng tư duy code, giải quyết vấn đề, và mình chỉ nêu các bước làm, không đưa đầy đủ code vì một số lý do.

    - Đầu tiên ta theo dõi request đăng nhập Garena và thấy phần Login data có nội dung sau:
    Mã (Text):
    https://sso.garena.com/api/login?account=huanhoang&password=ecb732647df2347283478263845aa&redirect_uri=https://www.garena.vn/&format=json&id=1472983484321&app_id=10000
    Ta thấy giá trị của password id là không biết ở đâu ra, password nó khác cái mình điền nên chắc là nó bị mã hoá rồi. Thế là tìm trong Header, nhưng không có, rồi lấy source trang đăng nhập của Garena thì thấy cái source cũng chả có gì ngoài những dòng include thư viện javascript. Vậy ta có thể đoán nó giấu phương thức mã hoá password và cách lấy id ở trong 1 cái javascript nào đó:
    Đối với 1 người không biết gì về javascript như mình thì cứ việc mò thôi, nó mã hoá pass thì chắc code js sẽ có đoạn liên quan keyword password. Và thế là tìm một hồi thì thấy cái js sau: https://sso.garena.com/js/sso.js?v=0.77 là chứa những dữ kiện liên quan. (Mình sẽ gọi javascript là js thôi cho nhanh nhé).

    - Đầu tiên ta nói đến cái giá trị của id, các bạn cứ thấy 1 chuỗi số mà mở đầu là 15, độ dài chuỗi là 13 hoặc 10 thì có thể đoán ngay đầu tiên khả năng lớn thằng này là timestamp. Và đúng như dự đoán, khớp với 1 đoạn code trong sso.js:
    [​IMG]
    Chuỗi timestamp có độ dài là 13 nên nó sẽ tính đến mili-giây (còn nếu độ dài là 10 thì nó sẽ tính đến giây thôi), thì tìm trên mạng ta có được đoạn code lấy timestamp tính tới mili-giây sau:

    Mã (AutoIt):

    $id = _TimeStampUNIX_ms()

    Func _TimeStampUNIX_ms($iYear = @YEAR, $iMonth = @MON, $iDay = @MDAY, $iHour = @HOUR, $iMin = @MIN, $iSec = @SEC)
        Local $stSystemTime = DllStructCreate('ushort;ushort;ushort;ushort;ushort;ushort;ushort;ushort')
        DllCall('kernel32.dll', 'none', 'GetSystemTime', 'ptr', DllStructGetPtr($stSystemTime))
        $iMSec = StringFormat('%03d', DllStructGetData($stSystemTime, 8))
        Local $nYear = $iYear - ($iMonth < 3 ? 1 : 0)
        Return ((Int(Int($nYear / 100) / 4) - Int($nYear / 100) + $iDay + Int(365.25 * ($nYear + 4716)) + Int(30.6 * (($iMonth < 3 ? $iMonth + 12 : $iMonth) + 1)) - 2442110) * 86400 + ($iHour * 3600 + $iMin * 60 + $iSec)) * ($iMSec ? 1000 : 1) + $iMSec
    EndFunc
    - Tiếp tục là đến cái password. Lúc trước Garena sử dụng phương thức mã hoá RSA để mã hoá mật khẩu đăng nhập nhưng hiện nay nó đã đổi sang cách thức khác, không khó để tìm thấy trong thư viện sso.js cách mã hoá của nó:

    [​IMG]
    Giờ ta sẽ phân tích đoạn js trên:

    + Dòng 1: var password = ... tương ứng với cái pass ta điền vào, không có gì để bàn đến.​

    + Dòng 2: var passwordMd5 = CryptoJS.MD5(password). Mã hoá pass bằng MD5. AutoIt đã có sẵn hàm mã hoá MD5. #include thư viện <Crypt.au3> vào code để dùng hàm này.
    Mã (AutoIt):
        $passwordMD5 = _Crypt_HashData($password, $CALG_MD5)
    *Lưu ý là AutoIt sẽ trả về dạng ví dụ như: 0xABCDEF6132545234... nhưng js thì trả về abcdef6132545234... nên bạn cần Format lại giá trị $passwordMD5 nhé, mình sẽ dùng hàm sau để format:​
    Mã (AutoIt):
    Func FormatBinary($bData)
        Return StringLower(Hex($bData))
    EndFunc
    Vậy $passwordMD5 = FormatBinary($passwordMD5)

    + Dòng 3: var passwordKey = CryptoJS.SHA256(CryptoJS.SHA256(passwordMd5 + data.v1) + data.v2);
    Ăn thua nhau là ở dòng này đây: Data.v1 và Data.v2 là cái quái gì vậy ? Nó chính là giá trị trả về của request sau:​
    Mã (AutoIt):
    _HttpRequest(2, 'https://sso.garena.com/api/prelogin?account=' & $username & '&format=json&id=' & _TimeStampUNIX_ms() & '&app_id=10000', '', '', '', 'X-Requested-With: XMLHttpRequest')
    Đoạn url này liên quan đến cái hình thứ 1 mình đăng ở trên đấy, hoặc lúc bạn đọc Live HTTP Headers / F12 cũng sẽ thấy nó.

    Trường hợp dễ là vậy, trường hợp khó hơn là nếu thằng Garena nó chơi ác chèn Captcha vào thành ra thế này: - Vì khá dài nên bỏ trong Spoiler cho gọn bài viết
    Mã (AutoIt):
     _HttpRequest(2, 'https://sso.garena.com/api/prelogin?account=' & $username & '&format=json&id=' & _TimeStampUNIX_ms() & '&app_id=10000&captcha_key=' & $keyCaptcha & '&captcha=' & $reCaptcha, '', '', '', 'X-Requested-With: XMLHttpRequest')
    thì captcha_key captcha làm sao tìm ?
    Trước tiên ta phải tìm URL sinh ra cái Captcha đó, thấy trên LHH:
    https://captcha.garena.com/image?key=7126787124ecdb12dbccc
    => lại thêm 1 câu đánh đố nữa, cái giá trị của key ở đâu ra vậy :)) (lưu ý là key và captcha_key là cùng 1 giá trị nên tìm được thằng key là xong)

    Thôi thì lại dò thằng js thôi chứ ở chỗ khác chả có thằng nào liên quan cả, lại thấy:

    [​IMG]
    Là nó liên quan chứ còn gì nữa :)) Hàm uuid sẽ có cách thức hoạt động như sau:

    Replace toàn bộ x và y bằng giá trị trả về của function(c): Tưởng tượng nhé, ta phân chuỗi 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' thành 1 mảng, mỗi phần tử của mảng sẽ chứa 1 ký tự: [0] = 'x', [1] = 'x', [2] = 'x', .... , [12] = 'y', ..... (Bỏ qua các dấu - và 4 vì nó chỉ replace [xy])
    => c trong function(c) sẽ tương đương với các phần tử đó, tăng dần từ phần tử [0] đến phần tử cuối. Cách thức hoạt động của function(c) :
    r = Math.random()*16|0
    => Gán biến r có giá trị random từ 0 đến 15 (tương đương với số hex đó)
    var v = c == 'x' ? r : (r&0x3|0x8)
    => viết lại cho dễ hiểu này: var v = ((c == 'x') ? r : r & 0x3 | 0x8)
    => Gán biến v có giá trị là: khi c là 'x' thì v = r, khi c không phải là x (tức là c = 'y' đó) thì nó sẽ là BitOR(BitAND($r, 0x3), 0x8) với dấu & là toán tử AND, | là toán tử OR (trên Bit).
    return v.toString(16)
    => chuyển số sang dạng Hex​

    Xong xuôi hết rồi thì Replace dấu - thằng rỗng là được cái key.

    Vậy là ta viết được ngay sang AutoIt:
    Mã (AutoIt):

    $key = UUID()

    Func UUID()
            Local $sResult = '', $sString = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx', $aString = StringSplit($sString, '')
            For $i = 1 To $aString[0]
                $Random = Random(0, 15, 1)
                Switch $aString[$i]
                    Case 'x'
                        $sResult &= Hex($Random, 1)
                    Case 'y'
                        $sResult &= Hex(BitOR(BitAND($Random, 0x3), 0x8), 1)
                    Case 4 ; khi là 4 thì giữ nguyên
                        $sResult &= $aString[$i]
                    Case '-' ;không cần nối vào kết quả (tương đương việc replace '-' bằng rỗng)

                EndSwitch
            Next
            Return StringLower($sResult)
        EndFunc
    key rồi thì request lấy Captcha thôi:
    Mã (AutoIt):
     _HttpRequest(-2, 'https://captcha.garena.com/image?key=' & $keyCaptcha) ;<=lấy ảnh captcha ở dạng Binary
    Bạn tuỳ ý tạo GUI hay làm gì đó để hiển thị cái ảnh Captcha đó, dĩ nhiên từ ảnh Captcha thì suy ra được giá trị của captcha, vậy là xong phần captcha.

    OK, hoàn thành công đoạn khó để làm tiếp rồi.
    Giá trị trả về của request https://sso.garena.com/api/prelogin?account=.... là một chuỗi trả về có dạng như sau:
    Ta dùng StringRegexp để lấy v1 v2. Nó chính là 2 giá trị Data.v1 và Data.v2 cần tìm đó.​

    * Tiếp tục phần mã hoá:
    var passwordKey = CryptoJS.SHA256(CryptoJS.SHA256(passwordMd5 + data.v1) + data.v2) => mã hoá SHA256 cũng đã có sẵn trong thư viện Crypt.au3. Ta viết lại thành code AutoIt như sau: (Lưu ý dấu + giữa 2 chuỗi trong js nghĩa là nối 2 chuỗi lại với nhau.)​
    Mã (AutoIt):
        $passwordKey = FormatBinary(_Crypt_HashData(FormatBinary(_Crypt_HashData($passwordMD5 & $vData[0], $CALG_SHA256)) & $vData[1], $CALG_SHA256))
    *Trong đó $vData[0] và $vData[1] tương ứng với Data.v1 và Data.v2 mà ta đã tách ra được.
    *Lưu ý nếu chương trình báo lỗi không có $CALG_SHA256 thì tự khai báo $CALG_SHA256 = 0x0000800c nhé.
    Ta dùng hàm FormatBinary để bỏ 0x và chuyển sang chữ thường: $passwordKey = FormatBinary($passwordKey)

    + 2 dòng cuối:​
    • var encryptedPassword = CryptoJS.AES.encrypt(passwordMd5, passwordKey, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.NoPadding})
    => encrypt AES_256 mode ECB, không có Padding, muốn hiểu rõ về kiểu mã hoá này thì Google nhé​
    • encryptedPassword = CryptoJS.enc.Base64.parse(encryptedPassword.toString()).toString(CryptoJS.enc.Hex)
    => Vì CryptoJS.AES.encrypt trả về dữ liệu Base64 nên đoạn code này là để chuyển Base64 về lại Binary, đơn giản.​

    Vậy làm sao để encrypt AES_256 mode ECB, thư viện Crypt.au3 không có sẵn, thực ra là có AES nhưng ko liên quan (không lấy được đúng giá trị trả về). Mình có 2 phương án sau:​
    1. Request lấy kết quả encrypt từ dịch vụ online, cách này gọn, không cần thêm thư viện nào khác:
    Mã (AutoIt):

    $encryptedPassword = AES_ECB_Encrypt($passwordMD5, $passwordKey, 256)

    Func AES_ECB_Encrypt($sInputData, $sKey, $iBlockSize = 128)
        Local $aResult = StringRegExp(_HttpRequest(2, 'http://www.cryptogrium.com/crypto.php', 'optype=aes_ecb&operation=encrypt&blocksize=' & $iBlockSize & '&key=' & $sKey & '&input=' & $sInputData), '<response>([^<]+)</response>', 1)
        Return (@error ? SetError(1, 0, '') : $aResult[0])
    EndFunc
     
    2. Dùng UDF AES.au3, cái này không có sẵn, phải tải từ autoscript.com, Google là thấy:​
    Mã (AutoIt):
      #include "AES.au3"
    $key = _AesEncryptKey(Binary("0x" & $passwordKey))
    $encryptedPassword = FormatBinary(_AesEncryptECB($key, Binary("0x" & $passwordMD5)))
     
    Lưu ý là 2 cách trên trả thẳng Binary luôn nên không cần chuyển Base64 về Binary như cái javascript nên không cần quan tâm đến cái dòng js cuối (chuyển B64->Bin)

    Vậy là đã có trong tay $encryptedPassword và $id rồi. Hết chuyện =)) Request thôi :D

    [​IMG]

    - Bài hướng dẫn _HttpRequest: https://autoitvn.com/threads/update...http-vi-du-hinh-anh-cac-buoc-lam-chi-tiet.267


    Cảm ơn các thým đã đọc :))
     
    Học Việc, zCafex, quochoa and 9 others like this.
  2. tuoitre

    tuoitre Thành viên năng động
    • 28/34

    Tham gia ngày:
    15/9/15
    Bài viết:
    135
    Đã được thích:
    89
  3. DuyMinh

    DuyMinh Thành viên năng động
    • 28/34

    Tham gia ngày:
    14/3/17
    Bài viết:
    177
    Đã được thích:
    92
    Kinh thật, nếu là 1 người biết JS thì không nói gì? Nhưng thím hoàn toàn không biết JS. Quá bá đạo. :)) Nothing to say. :))
     
  4. Huân Hoàng

    Huân Hoàng Administrator Thành viên BQT Administrator Super Moderator
    • 93/113

    Tham gia ngày:
    29/9/15
    Bài viết:
    642
    Đã được thích:
    1,181
    zCafex, Vũ Nguyễn and DuyMinh like this.
  5. DuyMinh

    DuyMinh Thành viên năng động
    • 28/34

    Tham gia ngày:
    14/3/17
    Bài viết:
    177
    Đã được thích:
    92
    Ngon quá thím êi :))
    À thím, cho iêm hỏi ngu 1 về thuật toán với... Giờ cái tool leech text của iêm ví dụ nó support nhiều trang đi... giờ iêm muốn người ta nhập link vào, là iêm sẽ tự check xem trang đó iêm có hỗ trợ không, rồi mới bắt đầu get info của nó. Vậy thì iêm bây giờ phải tạo 1 mảng, chứa các trang iêm support vào, rồi sau đó check lần lượt từng cái phải không thiếm? Rồi giả sử check được, iêm có 1 vấn đề muốn hỏi tiếp... giả sử, trang truyencv.com và bachngocsach.com nó support định dạng là truyencv.com/abcxyz|bachngocsach.com/reader/abcxyz, giờ iêm làm sao để nó check được định dạng người ta nhập vào là đúng... Ý tưởng của iêm sẽ là, nếu trang đúng, thì luôn chứa thông tin đúng... như tên truyện, tác giả, v.v... nếu như iêm regexp không ra... => tụi nó nhập sai. :))
    Thím thông não cho iêm xem ý tưởng của iêm có đúng không ạ... Iêm cảm ơn thím trước. <3
     
  6. Huân Hoàng

    Huân Hoàng Administrator Thành viên BQT Administrator Super Moderator
    • 93/113

    Tham gia ngày:
    29/9/15
    Bài viết:
    642
    Đã được thích:
    1,181
    @DuyMinh đúng rồi thým, thým tổng hợp lại tất cả các dạng link đúng của mấy trang thým cần, rồi đi so sánh với link người dùng nhập, còn không thì request thử cái link người dùng nhập rồi check nội dung trả về khớp với đặc điểm nhận dạng gì đó của trang đó không :p
     
    DuyMinh thích bài này.
  7. duc

    duc Thành viên
    • 8/11

    Tham gia ngày:
    28/2/16
    Bài viết:
    38
    Đã được thích:
    23
    Full code e mới chép của anh Huân Hoàng lại hahahahahahahaha

    Nội dung bị ẩn:
    **Nội dung ẩn: Nội dung của khối ẩn này chỉ có thể được nhìn thấy bởi các thành viên của (các nhóm: nhóm Administrative, Moderating, Registered). **
     
    thien ho thích bài này.
  8. Tran Duy

    Tran Duy Thành viên mới
    • 3/6

    Tham gia ngày:
    26/4/16
    Bài viết:
    24
    Đã được thích:
    10
    Quá đỉnh bác ơi :autoit:
     
  9. Huân Hoàng

    Huân Hoàng Administrator Thành viên BQT Administrator Super Moderator
    • 93/113

    Tham gia ngày:
    29/9/15
    Bài viết:
    642
    Đã được thích:
    1,181
    Vỡi thým, làm việc nhanh vỡi =.=" :|
     
  10. duc

    duc Thành viên
    • 8/11

    Tham gia ngày:
    28/2/16
    Bài viết:
    38
    Đã được thích:
    23
    Hehe , em vừa lướt forum thấy vào là ngâm cứu ngay :D:D:D:D:autoit::autoit::autoit::autoit:
     
    Huân Hoàng thích bài này.
  11. Xuân Dũng 38

    Xuân Dũng 38 Thành viên hiểu biết
    • 83/90

    Tham gia ngày:
    9/9/15
    Bài viết:
    89
    Đã được thích:
    1,967
    Nơi ở:
    Hà Nội
    Max ghê , qua autoIt luôn :)
    Like HH
     
  12. Huy Bui

    Huy Bui Thành viên mới
    • 1/6

    Tham gia ngày:
    15/7/17
    Bài viết:
    4
    Đã được thích:
    0
    đoạn này có thể chuyển sang autoit hay làm cách nào không các bác
    Mã (Javascript):
    var public_key = "-----BEGIN PUBLIC KEY-----\
            MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDR7FHnzqB8syM62mAJAG7z6/ie\
            /Vz3eq0hEFHQCAd9xxQocrjDbulx1LNox5wTprvLibVRqDCMaPcXZMFRnerZC1YO\
            Ems2U3VwDMWi5s+B4qD+6jG1PB+NPzrlIt+asZtcDDkdmX1t5WgHMoubvV9tCOpH\
            YUBgF34S9lvbldXW4wIDAQAB\
            -----END PUBLIC KEY-----"
    ;
       
        function encryptPassword(password) {
            var encrypt = new JSEncrypt();
            encrypt.setPublicKey(public_key);
            return encrypt.encrypt(password);
        }
     
  13. DuyMinh

    DuyMinh Thành viên năng động
    • 28/34

    Tham gia ngày:
    14/3/17
    Bài viết:
    177
    Đã được thích:
    92
    Phải tìm được cái hàm JSEncryot nó làm gì mới chuyển được chứ?
     
    Huân Hoàng thích bài này.
  14. Huân Hoàng

    Huân Hoàng Administrator Thành viên BQT Administrator Super Moderator
    • 93/113

    Tham gia ngày:
    29/9/15
    Bài viết:
    642
    Đã được thích:
    1,181
    UDF _HttpRequest có sẵn hàm _JS_Execute để giải js đó, cơ mà đúng như thým Duy Minh nói, thým quăng cái đoạn js trên mà không đưa thư viện js Encrypt thì giải bằng răng ? =))
     
  15. huybuism

    huybuism Thành viên mới
    • 1/6

    Tham gia ngày:
    25/8/17
    Bài viết:
    1
    Đã được thích:
    0
  16. DuyMinh

    DuyMinh Thành viên năng động
    • 28/34

    Tham gia ngày:
    14/3/17
    Bài viết:
    177
    Đã được thích:
    92
    Cái JS này không có chứa class JSEncrypt(), chắc JS khác rồi.
     
  17. Đức Trần

    Đức Trần Thành viên mới
    • 3/6

    Tham gia ngày:
    12/8/17
    Bài viết:
    14
    Đã được thích:
    10
  18. DuyMinh

    DuyMinh Thành viên năng động
    • 28/34

    Tham gia ngày:
    14/3/17
    Bài viết:
    177
    Đã được thích:
    92
    Cái in đậm là khai báo class, func phiếc gì đó, mà trong cái JS ở trên nó không có cái func đó thì sao mà giải. @@
    function encryptPassword(password){
    var encrypt =new JSEncrypt();
    encrypt.setPublicKey(public_key);
    return encrypt.encrypt(password);
    }
     
  19. duc

    duc Thành viên
    • 8/11

    Tham gia ngày:
    28/2/16
    Bài viết:
    38
    Đã được thích:
    23
    ctrl + shift + j chạy thử xem encrypt dc k
     
  20. enjoyedtvn

    enjoyedtvn Thành viên mới
    • 3/6

    Tham gia ngày:
    17/7/16
    Bài viết:
    17
    Đã được thích:
    13
    Hay quá bác ạ, đúng thứ mình đang thắc mắc :D
     
    Huân Hoàng thích bài này.

Chia sẻ trang này

Đang tải...