Tag: Tutorial

Series Reversing/Cracking .NET cơ bản bằng tiếng Việt

Xin chào,

Tôi đã trở lại sau một thời gian vắng bóng. Chỉ là tôi ít viết blog hơn. Ít viết blog thôi nhé, chứ còn tôi vẫn viết, nhưng tôi viết và up ở nơi khác. Tôi viết cũng khá nhiều thể loại, từ văn học kích động quần chúng, công kích cá nhân, đả phá, xuyên tạc, bôi nhọ danh dự cũng như quyền lợi của các ông lớn (trong ngành phần mềm), nhiều khi tôi còn cổ súy một cách nồng nhiệt cho việc ngồi xổm lên luật pháp (vi phạm bản quyền), lúc hứng chí thì lại quay ra viết các thể loại ngôn tình sướt mướt, cũng ít nhiều lấy đi nước mắt người đọc và làm xao xuyến trái tím non nớt của nhiều bạn trẻ mới chập chững vào đời. Ôi chao…

Chính vì một trong các nguyên nhân đó mà đợt trước tôi có bị WordPress gọi lên cảnh cáo và yêu cầu rút kinh nghiệm sâu sắc, vì có những hành vi sai trái, không hợp chuẩn mực ứng xử của blogger trong thời gian vừa qua, nếu còn tái phạm chắc bị đình chỉ công tác và thuyên chuyển qua Blogspot, thế nên tôi sợ lắm, không dám manh động nữa, để giữ chắc cái ghế tôi đang ngồi, hoặc âm thầm tự diễn biến, tự chuyển hóa sang một dạng khác, lén lút hơn, tinh vi hơn và dĩ nhiên, có tổ chức hơn.

Nói nhiều như vậy nhưng nó chỉ xoay quanh một chủ thể chính: Những bài viết của tôi. Bởi vì tự nhiên hôm nay tôi giật mình ngồi nhìn lại, thấy trong những năm tháng qua, tôi viết nhiều papers, làm nhiều tuts, cố gắng giữ gìn rồi cũng rơi rụng bớt phần nào, là do mọi thứ thay đổi quá nhanh.

Có những cái host ngày xưa tôi đinh ninh rằng nó trường tồn lắm, như megaupload, rapidshare,…. Nhớ lại nhiều năm về trước, tôi cặm cụi làm rồi up, làm rồi up không ngừng nghỉ, hòng lưu giữ lại những thứ tôi coi là giá trị, những bài viết, những video tôi tự quay, những clip được dàn dựng cắt ghép công phu (tất nhiên là không che),…. Rồi một ngày đẹp trời, đám service provider bông nhiên lăn đùng ngã ngửa ra. Ngoảnh mặt lại, đã thấy mình trắng tay…

Giờ đây, đó đã là “vài năm về trước”, khi tôi còn hoang dại và thơ ngây, những bài viết non nớt. Ấy thế mà đó lại là một phần trong chuyến phiêu du của tôi, ghi lại những dấu ấn nhất định, và tôi rất tôn trọng chúng, chỉ tiếc rằng tôi không giữ nổi. Ngày đó nghèo, cái usb còn không có, chứ không đã lưu lại offline, để sau này truyền lại thành bài học cho con cháu, theo kiểu “Đấy, mày thấy chưa, thằng bố mày ngày xưa trẩu thế này này, đừng dại mà làm theo con nhé”.

Rồi tôi nghe phong phanh thấy thiên hạ đồn rằng up lên Github không sợ bị remove, nên tôi cũng thử một lần cho biết, bởi vì trước đây hình như có thằng cha nào up cả cái bộ IDA (leaked) lên, rồi bị report, Github mới biết và xóa đi. Cho nên chắc cũng ổn.

Xin giới thiệu với các bạn loạt tut về Reversing/Cracking .NET cơ bản dành cho người mới bắt đầu. Loạt tut này trước đây tôi làm rồi bán để lấy tiền mua cây đờn đặng tính học gảy để đờn cho người con gái nọ (đọc xong bài này bạn cuộn chuột lên dầu trang và liếc nhẹ sang bên phải). Thế rồi giờ em đã có người yêu, biết gảy đàn và hát hay, còn tôi thì phiêu bạt chân chời góc bể, đờn vẫn mang theo, những chẳng bao giờ gảy… Đó là cuộc đời…

Xin thứ lỗi, tôi không có ý định viết ngôn tình đâu, nhưng cứ động đến là cảm xúc nó lại cứ phọt ra như vậy đấy. Xin mô tả sơ qua về loạt tut này. Tut này là tut cũ, và tôi có share cách đây một thời gian trong một số channel mật. Thực ra nó không có gì đặc biệt để phải giấu diếm lâu như thế, chỉ bởi vì tôi tôn trọng những người hảo tâm đã mua đám tut này của tôi, nên tôi giữ kín trong một thời gian dài để đảm bảo quyền lợi cho họ. Còn bây giờ thì chắc là khấu hao nó cũng hết, nên tôi mới public.

Loạt tut này thuộc vào dạng “Hết sức cơ bản”, dành cho cả những người chưa biết gì. Trong mỗi tut tôi có cố gắng giải thích cặn kẽ hết sức có thể trong khả năng của tôi. Mỗi tut bao gồm:
– Video hướng dẫn
– Target được sử dụng trong mỗi video (crackme, apps,…)
– Các công cụ, tài liệu có liên quan đến nội dung video
– Bài tập: Các target khác tương tự như target trong video để người đọc sau khi xem xong có thể thực hành lại.

Mục lục:

  1. Gió
  2. Khởi đầu – Reflector và Serial Fishing
  3. Patching (Phiên bản văn minh với Reflexil)
  4. Patching (Phiên bản cồng kềnh với Hex Editor)
  5. Đọc code và Keygen (phần 1)
  6. Đọc code và Keygen (phần 2)
  7. Unpack .NET đơn giản
  8. Unpack .NET cùng Beloved Olly
  9. Tập cách kéo thả cùng de4dot
  10. Tạo keygen ứng dụng thương mại
  11. Vạn kiếm quy tông – Thử lửa với hần mềm đăng kí sim rác

Link download:

https://github.com/levisre/dotnettuts

Hi vọng nó sẽ có ích cho các bạn, ở một khía cạnh nào đó 🙂

Enjoy and best regards,

Levis

P/S: Về loạt bài Flare-on, tôi đình công vì phía FireEyes làm ăn cồng kềnh, gần nửa năm rồi mà không thấy giải thưởng gửi về. Bao giờ tôi nhận được giải thì biết đâu, bằng một biện pháp tâm linh nào đó, tôi lại có hứng để viết tiếp thì sao?

Xin đừng hỏi tôi như thế nữa!

“Anh ơi, cái này nó như thế này, giờ em phải làm gì tiếp?”
“Anh ơi, như thế này là sao vậy ạ?”
“Anh ơi, em muốn…, vậy em cần làm gì?”
“Anh ơi….”

Trời ạ, đừng hỏi tôi những câu như vậy nữa, có được không?

Mỗi ngày tôi nhận được 1 lượng không phải ít, cũng không phải nhiều những câu hỏi như thế này. Và tôi nói thật là, chúng khiến tôi phải ngán ngẩm, thực sự tôi rất nản. Các bạn trẻ, các bạn lặp đi lặp lại nhữngcâu hỏi kiểu như thế, các bạn không thấy chán à?

Các bạn nghĩ hỏi nhiều là chứng tỏ sự ham học hỏi à? Đối với những câu hỏi như trên, tôi chẳng thấy gì gọi là tinh thần học hỏi cả, chỉ thấy một sự thụ động và lười biếng, ngại khó ngại khổ mà thôi.

Tại sao?

Đọc bài viết này trước: http://www.hvaonline.net/hvaonline/posts/list/10525.hva . Khi nào bạn đọc xong, thì quay lại đọc tiếp bài viết này.

Bạn đọc xong rồi thì bạn đã rút ra điều gì chưa? Mấu chốt vấn đề nó nằm ở đấy đấy.

Bài viết mà tôi dẫn link phía trên, là bài viết rất kinh điển, và tôi nghĩ rằng tất cả mọi người cần phải đọc nó. Vậy bạn đọc rồi, bạn đã biết mình đang gặp ván đề gì chưa, và tại sao tôi phát khùng lên vì những câu hỏi đó chưa?

Thứ nhất, đặt những câu hỏi kiểu như thế chỉ khiến khả năng tư duy của mình bị kém đi. Hỏi những câu như thế, rồi được một người nào đó trợ giúp, trả lời hộ. Thế thì đáp án là đáp án của người ta, bạn không có gì ngoài cái kết quả đạt được trước mắt, mà bản thân không có một cái gì gọi là “tích lũy trải nghiệm”. Đúng, cái sự học muôn đời phải có chính là sự trải nghiệm, chỉ có chăm chăm tìm kiếm kết quả mà không tự mình trải nghiệm, tự mình tìm tòi, tự mình tích lũy, thì cuối cùng cũng chỉ có được mớ kiến thức của người ta thảy lại tận mồm, không phải của mình. Việc nghiên cứu giống như là một cuộc phiêu lưu vậy, thế nên, đừng quan trọng đích đến, hãy chú ý vào việc tìm kiếm cho mình những trải nghiệm trong suốt chuyến đi, điều đó mới thực sự là giá trị. Chứ nếu như bây giờ, bạn hỏi, tôi đưa cho bạn kết quả, và bạn sẽ chỉ biết là trong trường hợp đó thì phải làm như thế, và kết quả nó ra như vậy. Nếu như gặp phải trường hợp khác thì sao? Tôi biết rồi, bạn sẽ lại hỏi tôi, hoặc người nào khác. Cứ như thế mãi, trong tay bạn có được cái gì? Chẳng có gì đâu, thật sự đấy.

Thứ hai, đặt những câu hỏi kiểu như thế chỉ chứng tỏ rằng bạn không biết khả năng của bạn đang ở mức nào, dẫn đến việc không đánh giá đúng sự việc. Tại sao? Bạn nghĩ mình còn quá “gà”, quá thiếu kinh nghiệm, điều đó đúng, nhưng không thể dựa vào đó để bao biện cho việc lười tìm hiểu được. Và những người đưa cho bạn đáp án, bạn sẽ nghĩ người ta giỏi hơn bạn, và bạn sẽ có đôi lần mơ ước mình được trở nên giỏi như họ, phải vậy không? Thật lòng, tôi khuyên bạn đừng nghĩ theo phương diện đó, có ước mơ là tốt, có ngưỡng mộ là tốt, nhưng ở trong trường hợp này nó không phù hợp. Với cá nhân tôi, trước đây và bây giờ đây tôi luôn luôn tự học, tự nghiên cứu, khi nhìn thấy một người giải quyết được vấn đề nào đó mà tôi không làm được, tôi nghĩ rằng những vấn đề đó thực ra rất đỗi bình thường, họ giải quyết được, tôi không giải quyết được, đơn giản không phải họ giỏi hơn tôi, mà là tôi ngu hơn họ. Tôi biết mình ngu, nên càng phải phấn đấu hơn nữa để trở thành những người “bình thường” như họ. Và từ đó tôi có động lực và mong muốn được tự giải quyết vấn đề.

Tôi luôn tự hỏi mình với mỗi vấn đề tôi gặp phải, và không biết hướng giải quyết “Nếu những người “bình thường” kia là mình, thì họ sẽ giải quyết vấn đề này thế nào? Tại sao họ lại làm như vậy?”. Tin tôi đi, bạn hãy thử phương pháp này trong 1 thời gian, bạn sẽ biết tôi nói đúng hay sai. Hãy tự tìm ra hướng đi của mình, dù đúng hay sai, thì đó là hướng đi của bạn tự tìm ra, tự trải nghiệm, nó thực sự tốt hơn là đồ ăn sẵn. Đến một ngày nào đó, bạn sẽ trở thành 1 người “bình thường” như bao người khác.

Về kinh nghiệm của bản thân tôi, tôi chỉ thực sự tự học, trong khoảng hơn 3 năm trở lại đây. Trong suốt ba năm dấy, tôi chưa bao giờ đặt câu hỏi cho người khác, trên tất cả các forum hay các group mà tôi tham gia. Tôi chỉ hỏi chính bản thân tôi, và Google. Cái cách tôi học hỏi từ những người “bình thường” khác, là tôi tham gia cùng họ để thảo luận về các vấn đề, tôi đưa ra ý tưởng của tôi, và họ nếu thấy tôi sai, hoặc thiếu sót, họ sẽ chỉnh sửa và bổ sung cho tôi ngay lập tức. Có nghĩa là, tôi hoàn toàn không hỏi họ, không nhờ vả họ giúp tôi, tôi thảo luận với họ 1 cách bình đẳng, và từ đó tôi rút ra được những kinh nghiệm cho riêng mình, tự bổ sung những gì tôi còn thiếu và sửa chữa những gì tôi sai sót. Những người “bình thường”, họ rất chịu khó chia sẻ và thảo luận với bạn, nếu như bạn cho họ thấy bạn có quan điểm riêng, có sự tìm tòi nghiên cứu của riêng bạn. Đừng sợ mất thời gian, mà hãy sợ rằng mình không đủ kiên nhẫn để mà tìm hiểu, thế thôi.

Chút cảm nghĩ trong mấy ngày cuối tuần, và hi vọng rằng nó giúp ích cho ai đó…

[Video] Manual Unpacking Confuser 1.9

Hello mates,

This is a short video i made for a (not so) long time before, but not published (yet). And now i decided to public it for people who want to learn about unpacking Confuser 1.9. I’m not the author of this method, just follow an article made by 0xd4d, so all credits go to him. So feel free to watch and enjoy this video.

A short description about the method used:

– Load file using WinDbg

– Determine which .NET version which is used by target, load the correct SoS extension along side with CLR/MSCORWKS.

– Set breakpoint and run until target file is unpacked/decrypted, and all methods are restored (anti-tamper protection) in memory.

– Dump the memory region

– Use de4dot to clean file

– Patch 3 method (anti-tamper, anti debug, anti-dump) to prevent crash, use CFF + SAE.

– Done

 

Enjoy and best regards,

Levis

Reverse Engineering .NET – Advanced Patching, Playing with IL

Xin chào các bạn,

Đây là bài viết mới nhất của tôi về Reverse Engineering .NET. Trong bài viết này tôi sẽ nói về IL Code/IL bytecode và cách để patch IL code 1 cách “Advance” thay vì patch true,false thông thường (Cr@cking-related). Hi vọng các bạn sẽ hiểu thêm 1 chút về IL code, và cách mà 1 .NET file lưu trữ IL bytecode và các method như thế nào.

Các bạn có thể đọc bản online tại đây:

Link tải về (cho lưu trữ hoặc đọc offline):

DOWNLOAD LINK GOOGLE DRIVE

Best Regards,

Levis

[Write-up] WhiteHat Grand Prix RE300 – x86 crackme

[Write-up] WhiteHat Grand Prix RE300 – x86 crackme

Công cụ sử dụng : OllyDbg 2,Protection_ID(hoặc exeinfoPE)

Link download re300: https://dl.dropboxusercontent.com/u/97334260/re300.7z

Được 1 file re300.exe, dùng một số công cụ để scan, detect thì thấy rằng file không bị pack, được viết bằng MSVC++  và còn nguyên cả debug symbol trong đó.


-=[ ProtectionID v0.6.5.5 OCTOBER]=-

(c) 2003-2013 CDKiLLER & TippeX

Build 31/10/13-21:09:09

Ready...

Scanning -> C:\Users\Levis\Desktop\re300.exe

File Type : 32-Bit Exe (Subsystem : Win CUI / 3), Size : 15872 (03E00h) Byte(s)

[File Heuristics] -> Flag : 00000100000001001101000000000000 (0x0404D000)

[Entrypoint Section Entropy] : 6.31

[Debug Info]

Characteristics : 0x0 | TimeDateStamp : 0x540EBCF2 | MajorVer : 0 / MinorVer : 0 -> (0.0)

Type : 2 -> CodeView | Size : 0x55 (85)

AddressOfRawData : 0x3428 | PointerToRawData : 0x2228

CvSig : 0x53445352 | SigGuid EE046B68-C49F-4C26-8AD6C890ABE27C1E

Age : 0x1 | Pdb : C:\Users\c4sp3_000\Desktop\WHGP\Source\RE-2\Release\RE-2.pdb

[CompilerDetect] -> Visual C/C++

[!] File appears to have no protection or is using an unknown protection

- Scan Took : 0.172 Second(s) [0000000ACh tick(s)] [533 scan(s) done]

 

Khi chạy thử chương trình sẽ yêu cầu nhập username và password. Username đã được cho sẵn: WhiteHat_Grand_Prix_2014. password đánh bừa vào: 123456789, một thông báo hiện ra: “Ooop!!!”. Việc cần làm là tìm ra password cho username đó, và password đó cũng là flag.

Tiến hành load file vào Olly, bấm chuột phải chọn Search for -> All Referenced Strings. Chúng ta thấy có một số string đáng chú ý: “Enter your username:”,”Enter your password:”, “Goodjob!”,”Ooop!!!”. Bấm vào bất kì string nào trong các string đó, chúng ta sẽ trở lại cửa sổ CPU trong olly, và các phần code ở đó chính là nằm trong hàm main() của chương trình. Chuyển lên code phía trên đầu của hàm:


00F712B0   $  55            PUSH EBP

00F712B1   .  8BEC          MOV EBP,ESP

00F712B3   .  83E4 F8       AND ESP,FFFFFFF8                              QWORD (8-byte) stack alignment

00F712B6   .  81EC E8000000 SUB ESP,0E8

00F712BC   .  A1 1850F700   MOV EAX,DWORD PTR DS:[re300.0F75018]

00F712C1   .  33C4          XOR EAX,ESP

00F712C3   .  898424 E40000 MOV DWORD PTR SS:[ESP+0E4],EAX

//---------Lấy username nhập vào---------------------------------------------

00F712CA   .  8B0D 6430F700 MOV ECX,DWORD PTR DS:[<&MSVCP110.?cout@std@@3 ASCII "X!m]"

00F712D0   .  56            PUSH ESI

00F712D1   .  57            PUSH EDI

00F712D2   .  BA EC31F700   MOV EDX,OFFSET re300.00F731EC                 ASCII "Enter your username: 

"

00F712D7   .  E8 24070000   CALL re300.00F71A00

00F712DC   .  8B0D 6030F700 MOV ECX,DWORD PTR DS:[<&MSVCP110.?cin@std@@3V

00F712E2   .  8D9424 800000 LEA EDX,[ESP+80]     <-- Địa chỉ lưu username

00F712E9   .  E8 42090000   CALL re300.00F71C30

///-------------------lấy password nhập vào------------------------------

00F712EE   .  8B0D 6430F700 MOV ECX,DWORD PTR DS:[<&MSVCP110.?cout@std@@3 ASCII "X!m]"

00F712F4   .  BA 0432F700   MOV EDX,OFFSET re300.00F73204                 ASCII "Enter your password: 

"

00F712F9   .  E8 02070000   CALL re300.00F71A00

00F712FE   .  8B0D 6030F700 MOV ECX,DWORD PTR DS:[<&MSVCP110.?cin@std@@3V

00F71304   .  8D5424 18     LEA EDX,[ESP+18] <-- Địa chỉ lưu password

00F71308   .  E8 23090000   CALL re300.00F71C30

///--------------Kiểm tra password nhập vào------------------------------

00F7130D   .  8D4C24 18     LEA ECX,[ESP+18]

00F71311   .  8D51 01       LEA EDX,[ECX+1]

00F71314   >  8A01          MOV AL,BYTE PTR DS:[ECX]

00F71316   .  41            INC ECX

00F71317   .  84C0          TEST AL,AL

00F71319   .^ 75 F9         JNE SHORT re300.00F71314

00F7131B   .  2BCA          SUB ECX,EDX

00F7131D   .  83F9 10       CMP ECX,10                        <--- Độ dài pass = 0x10?

00F71320   .  0F85 53010000 JNE re300.00F71479 <-- Nhảy nếu độ dài pass != 0x10

Đoạn code tiếp theo không quan trọng, chỉ là tìm và chuyển tất cả các kí tự chữ in hoa có trong username về kí tự viết thường:


00F71326   .  33C9          XOR ECX,ECX

00F71328   >  8A440C 18     MOV AL,BYTE PTR SS:[ECX+ESP+18]

00F7132C   .  3C 5B         CMP AL,5B                                     Switch (cases 41..5A, 2 exits)

00F7132E   .  7D 0A         JGE SHORT re300.00F7133A

00F71330   .  3C 40         CMP AL,40

00F71332   .  7E 06         JLE SHORT re300.00F7133A

00F71334   .  04 20         ADD AL,20                                     Cases 41 ('A'), 42 ('B'), 43 ('C'), 44 ('D'), 4...

00F71336   .  88440C 18     MOV BYTE PTR SS:[ECX+ESP+18],AL

00F7133A   >  8A440C 19     MOV AL,BYTE PTR SS:[ECX+ESP+19]               Default case of switch re300.0F7132C

00F7133E   .  3C 5B         CMP AL,5B                                     Switch (cases 41..5A, 2 exits)

00F71340   .  7D 0A         JGE SHORT re300.00F7134C

00F71342   .  3C 40         CMP AL,40

00F71344   .  7E 06         JLE SHORT re300.00F7134C

00F71346   .  04 20         ADD AL,20                                     Cases 41 ('A'), 42 ('B'), 43 ('C'), 44 ('D'), 4...

00F71348   .  88440C 19     MOV BYTE PTR SS:[ECX+ESP+19],AL

00F7134C   >  8A440C 1A     MOV AL,BYTE PTR SS:[ECX+ESP+1A]               Default case of switch re300.0F7133E

00F71350   .  3C 5B         CMP AL,5B                                     Switch (cases 41..5A, 2 exits)

00F71352   .  7D 0A         JGE SHORT re300.00F7135E

00F71354   .  3C 40         CMP AL,40

00F71356   .  7E 06         JLE SHORT re300.00F7135E

00F71358   .  04 20         ADD AL,20                                     Cases 41 ('A'), 42 ('B'), 43 ('C'), 44 ('D'), 4...

00F7135A   .  88440C 1A     MOV BYTE PTR SS:[ECX+ESP+1A],AL

00F7135E   >  8A440C 1B     MOV AL,BYTE PTR SS:[ECX+ESP+1B]               Default case of switch re300.0F71350

00F71362   .  3C 5B         CMP AL,5B                                     Switch (cases 41..5A, 2 exits)

00F71364   .  7D 0A         JGE SHORT re300.00F71370

00F71366   .  3C 40         CMP AL,40

00F71368   .  7E 06         JLE SHORT re300.00F71370

00F7136A   .  04 20         ADD AL,20                                     Cases 41 ('A'), 42 ('B'), 43 ('C'), 44 ('D'), 4...

00F7136C   .  88440C 1B     MOV BYTE PTR SS:[ECX+ESP+1B],AL

00F71370   >  83C1 04       ADD ECX,4                                     Default case of switch re300.0F71362

00F71373   .  83F9 10       CMP ECX,10

00F71376   .^ 7C B0         JL SHORT re300.00F71328

Đoạn code tiếp theo, để kiểm tra xem các kí tự trong username có phải là các kí tự biểu diễn hexadecimal hay không (“0″….”9″,”a”…”f”):


00F71378   .  33C9          XOR ECX,ECX

00F7137A   .  8D9B 00000000 LEA EBX,[EBX]

00F71380   >  8A440C 18     MOV AL,BYTE PTR SS:[ECX+ESP+18]

00F71384   .  3C 30         CMP AL,30

00F71386   .  0F8C ED000000 JL re300.00F71479

00F7138C   .  3C 66         CMP AL,66

00F7138E   .  0F8F E5000000 JG re300.00F71479

00F71394   .  3C 39         CMP AL,39

00F71396   .  7E 08         JLE SHORT re300.00F713A0

00F71398   .  3C 61         CMP AL,61

00F7139A   .  0F8C D9000000 JL re300.00F71479

00F713A0   >  41            INC ECX

00F713A1   .  83F9 10       CMP ECX,10

00F713A4   .^ 7C DA         JL SHORT re300.00F71380

Như vậy đến đây ta biết được, password là 1 chuỗi gồm 16 kí tự (0x10), và các kí tự đều nằm trong nhóm các kí tự biểu diễn hexadecimal (“0123456789abcdef”).

Đoạn code tiếp theo thực hiện convert password (dạng hexadecimal) thành 1 byte array. Với password là 16 kí tự hexadecimal ta có 1 byte array 8 phần tử:


00F713C4   .  8A4454 18     MOV AL,BYTE PTR SS:[EDX*2+ESP+18] <-- địa chỉ lưu password

00F713C8   .  C64414 10 00  MOV BYTE PTR SS:[EDX+ESP+10],0      <-- Địa chỉ lưu byte array

00F713CD   .  3C 60         CMP AL,60

00F713CF   .  7E 04         JLE SHORT re300.00F713D5

00F713D1   .  2C 57         SUB AL,57

00F713D3   .  EB 02         JMP SHORT re300.00F713D7

00F713D5   >  2C 30         SUB AL,30

00F713D7   >  8A4C54 19     MOV CL,BYTE PTR SS:[EDX*2+ESP+19]

00F713DB   .  C0E0 04       SHL AL,4

00F713DE   .  884414 10     MOV BYTE PTR SS:[EDX+ESP+10],AL

00F713E2   .  80F9 60       CMP CL,60

00F713E5   .  7E 05         JLE SHORT re300.00F713EC

00F713E7   .  80E9 57       SUB CL,57

00F713EA   .  EB 03         JMP SHORT re300.00F713EF

00F713EC   >  80E9 30       SUB CL,30

00F713EF   >  02C1          ADD AL,CL

00F713F1   .  884414 10     MOV BYTE PTR SS:[EDX+ESP+10],AL 

00F713F5   .  42            INC EDX

00F713F6   .  83FA 08       CMP EDX,8 <-- Kiểm tra độ dài

00F713F9   .^ 7C C3         JL SHORT re300.00F713BE

VD như nhập password là “0123456789ABCDEF” ta sẽ có 1 byte array (0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF) lưu tại [esp+10]

Đoạn code tiếp theo thực hiện 1 thuật toán cipher đơn giản:


00F713FB   .  33FF          XOR EDI,EDI

00F713FD   .  8D49 00       LEA ECX,[ECX]

00F71400   >  8A4C3C 10     MOV CL,BYTE PTR SS:[EDI+ESP+10] <-- pass byte array

00F71404   .  33C0          XOR EAX,EAX

00F71406   .  85F6          TEST ESI,ESI            <-- Độ dài của username

00F71408   .  7E 1E         JLE SHORT re300.00F71428

00F7140A   .  8D9B 00000000 LEA EBX,[EBX]

00F71410   >  0FBE9404 8000 MOVSX EDX,BYTE PTR SS:[EAX+ESP+80] <-- lấy từng char trong username

00F71418   .  0FB6C9        MOVZX ECX,CL  

00F7141B   .  33D1          XOR EDX,ECX <-- Thực hiện xor

00F7141D   .  40            INC EAX <-- Chuyển đến char tiếp theo trong username

00F7141E   .  8A8A 7032F700 MOV CL,BYTE PTR DS:[EDX+re300.0F73270] <-- Lấy giá trị trong 1 table với index là kết quả vừa xor được

00F71424   .  3BC6          CMP EAX,ESI <-- Kiểm tra xem đã hết kí tự trong username chưa?

00F71426   .^ 7C E8         JL SHORT re300.00F71410

00F71428   >  884C3C 10     MOV BYTE PTR SS:[EDI+ESP+10],CL <-- Lưu lại vào pass byte array

00F7142C   .  47            INC EDI       <-- Chuyển đến phần tử tiếp theo trong pass byte array

00F7142D   .  83FF 08       CMP EDI,8 <-- Kiểm tra xem đã hết phần tử trong pass byte array chưa?

00F71430   .^ 7C CE         JL SHORT re300.00F71400 <-- Nếu chưa hết, thực hiện lại vòng lặp

Quan trọng là ở code này:


00F7141E   .  8A8A 7032F700 MOV CL,BYTE PTR DS:[EDX+re300.0F73270] <-- Lấy giá trị trong 1 table với index là kết quả vừa xor được

chúng ta có 1 table với khoảng 256 phần tử. Đưa chuột vào lệnh này và bấm chuột phải chọn Follow In Dump -> Address Contstant. Phần tử của table này bắt đầu từ 70 3B 68 F3 4D DB… và kết thúc tại …73 24 E9 FE 11 A7 36 C6. Hãy copy lại toàn bộ các giá trị của table này.

Chúng ta có thể mô phỏng lại code như sau(với username là WhiteHat_Grand_Prix_2014, password 0123456789ABCDEF):


table = [0x70, 0x3B, 0x68, 0xF3, 0x4D, 0xDB, 0xA4, 0xB7, 0x46, 0xBE, 0x2B, 0x38, 0xE1, 0xFA, 0x6B, 0x50, 0xFC, 0xE5, 0xF7, 0x62, 0xB0, 0x77, 0x5A, 0x5C, 0xD0, 0x8C, 0xD5, 0x1A, 0x87, 0xDC, 0x12, 0x3D, 0xCD, 0x3A, 0x9B, 0x7B, 0x4A, 0xEC, 0x4B, 0x1E, 0x63, 0x1D, 0x60, 0xC2, 0x78, 0xAD, 0xF6, 0x94, 0x23, 0xBC, 0x97, 0x2D, 0x8D, 0xE3, 0x8E, 0x69, 0x88, 0x66, 0x2C, 0x98, 0x9D, 0xCB, 0x1B, 0xFB, 0x20, 0xAA, 0x5D, 0xB1, 0x05, 0x61, 0x52, 0xF9, 0x1F, 0xBB, 0x04, 0xFF, 0x31, 0x10, 0x89, 0x55, 0xF1, 0x82, 0x7A, 0x45, 0x25, 0x49, 0x6F, 0x64, 0xED, 0x18, 0x9E, 0x1C, 0xD6, 0xD3, 0x9A, 0xF4, 0xC9, 0xC0, 0x0F, 0x0A, 0xE2, 0x28, 0x7E, 0x33, 0xFD, 0x34, 0xA0, 0x2F, 0x91, 0x57, 0xDD, 0x03, 0x27, 0xB6, 0x6D, 0xCE, 0xBF, 0x01, 0x16, 0x43, 0xA3, 0x59, 0xEF, 0x4C, 0xDF, 0xD1, 0x71, 0x15, 0xE0, 0x7F, 0x47, 0x85, 0x48, 0xC4, 0xDE, 0x56, 0x76, 0x4F, 0x53, 0x75, 0x5B, 0xB9, 0x95, 0x2A, 0x09, 0x5F, 0x92, 0x32, 0xD2, 0x6C, 0x08, 0x26, 0xA5, 0x8A, 0x58, 0x07, 0xF5, 0x51, 0xE8, 0x9F, 0xAB, 0xD8, 0xC3, 0xB2, 0xEE, 0xC7, 0x81, 0x44, 0x17, 0x80, 0x0D, 0xD7, 0x29, 0xE4, 0xA9, 0x83, 0xC1, 0x99, 0xE6, 0xF0, 0x0E, 0x6A, 0xD4, 0xA1, 0x74, 0x0B, 0xEB, 0x3F, 0xAE, 0x0C, 0xA6, 0x41, 0xB4, 0x93, 0xCA, 0x30, 0x35, 0xAF, 0x79, 0x72, 0xAC, 0xA8, 0x7C, 0xBD, 0x84, 0x14, 0xB5, 0xB3, 0xCC, 0xCF, 0x9C, 0x13, 0xB8, 0xC5, 0x40, 0x39, 0xE7, 0x8B, 0x22, 0x02, 0x65, 0xDA, 0x96, 0xF2, 0x90, 0x54, 0x06, 0xEA, 0x2E, 0x21, 0x42, 0xF8, 0xC8, 0x3E, 0x3C, 0x00, 0x5E, 0x19, 0xD9, 0x67, 0x86, 0x8F, 0xA2, 0x7D, 0x4E, 0x6E, 0x37, 0xBA, 0x73, 0x24, 0xE9, 0xFE, 0x11, 0xA7, 0x36, 0xC6]

pass_bytes_array=[0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF]

username="WhiteHat_Grand_Prix_2014"

for i in range(0,len(pass_bytes_array)):

                xorval=pass_bytes_array[i]^ord(username[0])

                for j in range(1,len(username)):

                                xorval=table[xorval]^ord(username[j])

                pass_bytes_array[i]=table[xorval]

print(pass_bytes_array)

Code tiếp theo thực hiện đưa 2 constant vào memory và so sánh từng byte của các constant này với từng byte của byte pass array, nếu giống nhau thì đưa ra thông báo đúng, nếu khác nhau thì đưa ra thông báo sai:


00F71432   .  C74424 08 2FE MOV DWORD PTR SS:[ESP+8],BAA5E82F <-- Constant 1

00F7143A   .  C74424 0C A4A MOV DWORD PTR SS:[ESP+0C],BD0FA4A4 <-- Constant 2

00F71442   .  33C9          XOR ECX,ECX

00F71444   >  8A440C 08     MOV AL,BYTE PTR SS:[ECX+ESP+8]       <-- Chuyển từng byte vào al

00F71448   .  3A440C 10     CMP AL,BYTE PTR SS:[ECX+ESP+10] <-- So sánh với byte pass array

00F7144C   .  75 0D         JNE SHORT re300.00F7145B <-- Nếu sai thì đưa ra thông báo "Ooop!!!"

00F7144E   .  41            INC ECX                      <-- Chuyển đến phần tử tiếp theo

00F7144F   .  83F9 08       CMP ECX,8           <-- Kiểm tra xem đã hết phần tử hay chưa

00F71452   .^ 7C F0         JL SHORT re300.00F71444               <-- Tiếp tục so sánh

00F71454   .  BA 1C32F700   MOV EDX,OFFSET re300.00F7321C                 ASCII "Goodjob!!

"

00F71459   .  EB 05         JMP SHORT re300.00F71460

00F7145B   >  BA 2832F700   MOV EDX,OFFSET re300.00F73228                 ASCII "Ooop!!!

"

00F71460   >  8B0D 6430F700 MOV ECX,DWORD PTR DS:[<&MSVCP110.?cout@std@@3

00F71466   .  E8 95050000   CALL re300.00F71A00                 <-- Đưa ra màn hình

2 Constant là 0xBAA5E82F và 0xBD0FA4A4. Khi đưa vào memory ta sẽ có 1 byte array mới:

(0x2F,0xE8,0xA5,0xBA,0xA4,0xA4,0x0F,0xBD), tạm gọi là constant byte array.

Như vậy thuật toán để tìm ra được pass tương ứng với username đã cho (cũng chính là flag), thì ta phải thực hiện bruteforce từng giá trị trong password (8 giá trị tương đương với 16 kí tự hexadecimal). Có nghĩa là bruteforce trong khoảng 0x00 -> 0xFF, thực hiện thuật toán cipher phía trên và so sánh kết quả với constant byte array.


#declare table

table = [0x70, 0x3B, 0x68, 0xF3, 0x4D, 0xDB, 0xA4, 0xB7, 0x46, 0xBE, 0x2B, 0x38, 0xE1, 0xFA, 0x6B, 0x50, 0xFC, 0xE5, 0xF7, 0x62, 0xB0, 0x77, 0x5A, 0x5C, 0xD0, 0x8C, 0xD5, 0x1A, 0x87, 0xDC, 0x12, 0x3D, 0xCD, 0x3A, 0x9B, 0x7B, 0x4A, 0xEC, 0x4B, 0x1E, 0x63, 0x1D, 0x60, 0xC2, 0x78, 0xAD, 0xF6, 0x94, 0x23, 0xBC, 0x97, 0x2D, 0x8D, 0xE3, 0x8E, 0x69, 0x88, 0x66, 0x2C, 0x98, 0x9D, 0xCB, 0x1B, 0xFB, 0x20, 0xAA, 0x5D, 0xB1, 0x05, 0x61, 0x52, 0xF9, 0x1F, 0xBB, 0x04, 0xFF, 0x31, 0x10, 0x89, 0x55, 0xF1, 0x82, 0x7A, 0x45, 0x25, 0x49, 0x6F, 0x64, 0xED, 0x18, 0x9E, 0x1C, 0xD6, 0xD3, 0x9A, 0xF4, 0xC9, 0xC0, 0x0F, 0x0A, 0xE2, 0x28, 0x7E, 0x33, 0xFD, 0x34, 0xA0, 0x2F, 0x91, 0x57, 0xDD, 0x03, 0x27, 0xB6, 0x6D, 0xCE, 0xBF, 0x01, 0x16, 0x43, 0xA3, 0x59, 0xEF, 0x4C, 0xDF, 0xD1, 0x71, 0x15, 0xE0, 0x7F, 0x47, 0x85, 0x48, 0xC4, 0xDE, 0x56, 0x76, 0x4F, 0x53, 0x75, 0x5B, 0xB9, 0x95, 0x2A, 0x09, 0x5F, 0x92, 0x32, 0xD2, 0x6C, 0x08, 0x26, 0xA5, 0x8A, 0x58, 0x07, 0xF5, 0x51, 0xE8, 0x9F, 0xAB, 0xD8, 0xC3, 0xB2, 0xEE, 0xC7, 0x81, 0x44, 0x17, 0x80, 0x0D, 0xD7, 0x29, 0xE4, 0xA9, 0x83, 0xC1, 0x99, 0xE6, 0xF0, 0x0E, 0x6A, 0xD4, 0xA1, 0x74, 0x0B, 0xEB, 0x3F, 0xAE, 0x0C, 0xA6, 0x41, 0xB4, 0x93, 0xCA, 0x30, 0x35, 0xAF, 0x79, 0x72, 0xAC, 0xA8, 0x7C, 0xBD, 0x84, 0x14, 0xB5, 0xB3, 0xCC, 0xCF, 0x9C, 0x13, 0xB8, 0xC5, 0x40, 0x39, 0xE7, 0x8B, 0x22, 0x02, 0x65, 0xDA, 0x96, 0xF2, 0x90, 0x54, 0x06, 0xEA, 0x2E, 0x21, 0x42, 0xF8, 0xC8, 0x3E, 0x3C, 0x00, 0x5E, 0x19, 0xD9, 0x67, 0x86, 0x8F, 0xA2, 0x7D, 0x4E, 0x6E, 0x37, 0xBA, 0x73, 0x24, 0xE9, 0xFE, 0x11, 0xA7, 0x36, 0xC6]

#declare result value to compare

resultval = [0x2f,0xE8,0xA5,0xBA,0xA4,0xA4,0x0F,0xBD]

#declare username

username = 'WhiteHat_Grand_Prix_2014'

global hx              #hx is variable which keeps the xor'ed value

flag =''   #initalize variable to store flag

for i in range(0,len(resultval)): #for each value in resultval

 for j in range(0,255):                         #initalize value to bruteforce. Value in range 0x00 -> 0xFF

    hx = j^ord(username[0])                              #Set value to hx at the first time

    for n in range(1,len(username)): #manipulate each character in username

        hx=table[hx]^ord(username[n])#Xor'em

        if(table[hx]==resultval[i]):#if value in table equal to value in resultval

             flag+=hex(j)[2:]                                #then add the value of j to flag in hexadecimal string

print(flag)                            #print out the flag

chạy code và có kết quả: e76f63389797f52f -> flag: Flag{e76f63389797f52f}

Bổ sung:

Code của anh Bolzano, không cần bruteforce (đây có thể xem là code tốt nhất để solve bài này):


list_values = [0x70, 0x3B, 0x68, 0xF3, 0x4D, 0xDB, 0xA4, 0xB7, 0x46, 0xBE, 0x2B, 0x38, 0xE1, 0xFA, 0x6B, 0x50, 0xFC, 0xE5, 0xF7, 0x62, 0xB0, 0x77, 0x5A, 0x5C, 0xD0, 0x8C, 0xD5, 0x1A, 0x87, 0xDC, 0x12, 0x3D, 0xCD, 0x3A, 0x9B, 0x7B, 0x4A, 0xEC, 0x4B, 0x1E, 0x63, 0x1D, 0x60, 0xC2, 0x78, 0xAD, 0xF6, 0x94, 0x23, 0xBC, 0x97, 0x2D, 0x8D, 0xE3, 0x8E, 0x69, 0x88, 0x66, 0x2C, 0x98, 0x9D, 0xCB, 0x1B, 0xFB, 0x20, 0xAA, 0x5D, 0xB1, 0x05, 0x61, 0x52, 0xF9, 0x1F, 0xBB, 0x04, 0xFF, 0x31, 0x10, 0x89, 0x55, 0xF1, 0x82, 0x7A, 0x45, 0x25, 0x49, 0x6F, 0x64, 0xED, 0x18, 0x9E, 0x1C, 0xD6, 0xD3, 0x9A, 0xF4, 0xC9, 0xC0, 0x0F, 0x0A, 0xE2, 0x28, 0x7E, 0x33, 0xFD, 0x34, 0xA0, 0x2F, 0x91, 0x57, 0xDD, 0x03, 0x27, 0xB6, 0x6D, 0xCE, 0xBF, 0x01, 0x16, 0x43, 0xA3, 0x59, 0xEF, 0x4C, 0xDF, 0xD1, 0x71, 0x15, 0xE0, 0x7F, 0x47, 0x85, 0x48, 0xC4, 0xDE, 0x56, 0x76, 0x4F, 0x53, 0x75, 0x5B, 0xB9, 0x95, 0x2A, 0x09, 0x5F, 0x92, 0x32, 0xD2, 0x6C, 0x08, 0x26, 0xA5, 0x8A, 0x58, 0x07, 0xF5, 0x51, 0xE8, 0x9F, 0xAB, 0xD8, 0xC3, 0xB2, 0xEE, 0xC7, 0x81, 0x44, 0x17, 0x80, 0x0D, 0xD7, 0x29, 0xE4, 0xA9, 0x83, 0xC1, 0x99, 0xE6, 0xF0, 0x0E, 0x6A, 0xD4, 0xA1, 0x74, 0x0B, 0xEB, 0x3F, 0xAE, 0x0C, 0xA6, 0x41, 0xB4, 0x93, 0xCA, 0x30, 0x35, 0xAF, 0x79, 0x72, 0xAC, 0xA8, 0x7C, 0xBD, 0x84, 0x14, 0xB5, 0xB3, 0xCC, 0xCF, 0x9C, 0x13, 0xB8, 0xC5, 0x40, 0x39, 0xE7, 0x8B, 0x22, 0x02, 0x65, 0xDA, 0x96, 0xF2, 0x90, 0x54, 0x06, 0xEA, 0x2E, 0x21, 0x42, 0xF8, 0xC8, 0x3E, 0x3C, 0x00, 0x5E, 0x19, 0xD9, 0x67, 0x86, 0x8F, 0xA2, 0x7D, 0x4E, 0x6E, 0x37, 0xBA, 0x73, 0x24, 0xE9, 0xFE, 0x11, 0xA7, 0x36, 0xC6]

dict_values = {}

for i in xrange(0,len(list_values)):

    dict_values[i] = list_values[i]

inverted_dict = dict([[v,k] for k,v in dict_values.items()])

szUserName = 'WhiteHat_Grand_Prix_2014'

def trace_char(char, szUserName):

    lenUserName = len(szUserName)

    for i in xrange(0,lenUserName):

        char = inverted_dict[char] ^ ord(szUserName[lenUserName-i-1])

    return str(hex(char))[2:]

print trace_char(0x2f, szUserName) + trace_char(0xe8, szUserName) + trace_char(0xa5, szUserName) + trace_char(0xba, szUserName) + trace_char(0xa4, szUserName) + trace_char(0xa4, szUserName) + trace_char(0x0f, szUserName) + trace_char(0xbd, szUserName)

 

Enjoy and best regards

Levis