Saturday, November 30, 2019

Tổng quan về Virtualization và Hypervisor

Virtualization là gì?

Virtualization, hay còn gọi là ảo hóa, là một công nghệ được thiết kế để tạo ra tầng trung gian giữa hệ thống phần cứng máy tính và phần mềm chạy trên nó. Ý tưởng của công nghệ ảo hóa máy chủ là từ một máy vật lý đơn lẻ có thể tạo thành nhiều máy ảo độc lập. Mỗi một máy ảo đều có một thiết lập nguồn hệ thống riêng rẽ, hệ điều hành riêng và các ứng dụng riêng. Ảo hóa có nguồn gốc từ việc phân chia ổ đĩa, chúng phân chia một máy chủ thực thành nhiều máy chủ logic. Một khi máy chủ thực được chia, mỗi máy chủ logic có thể chạy một hệ điều hành và các ứng dụng độc lập.
–> Tóm lại, ảo hóa là phương pháp để tạo ra phiên bản ảo hóa trên máy tính vật lý.

Tại sao nên sử dụng công nghệ ảo hóa?

Tiết kiệm chi phí và tối ưu hóa hạ tầng CNTT là điều mà các doanh nghiệp quan tâm, đặc biệt là các doanh nghiệp có nhiều chi nhánh trong cả nước hay trên toàn cầu. Ảo hóa giúp doanh nghiệp nâng cao năng lực bảo mật dữ liệu, tăng cường khả năng khôi phục hoạt động sau thảm họa, nâng cao tính linh hoạt và cắt giảm chi phí đầu tư cho CNTT như phải cập nhật liên tục các phần mềm, các tính năng mới… trên nhiều máy tính vật lý.

Virtual Machine là gì?

Virtual Machine hay còn gọi là máy ảo, là một môi trường hoạt động độc lập – phần mềm hoạt động cùng nhưng độc lập với hệ điều hành máy chủ.

Hypervisor/VMM là gì ?

Hypervisor hay còn gọi là phần mềm giám sát máy ảo: Là một chương trình phần mềm quản lý một hoặc nhiều máy ảo (VM). Nó được sử dụng để tạo, startup, dừng và reset lại các máy ảo. Các hypervisor cho phép mỗi VM hoặc “guest” truy cập vào lớp tài nguyên phần cứng vật lý bên dưới, chẳng hạn như CPU, RAM và lưu trữ. Nó cũng có thể giới hạn số lượng tài nguyên hệ thống mà mỗi máy ảo có thể sử dụng để đảm bảo cho nhiều máy ảo cùng sử dụng đồng thời trên một hệ thống.
–>Tóm lại, hypervisor là các phần mềm công nghệ để tạo máy ảo và giám sát, điều khiển máy ảo
Có 2 loại hypervisor là Native ( hay còn gọi là Bare metal ) và Host Based

Loại-1: Native

Một hypervisor ở dạng native (hay còn gọi “bare-metal”) chạy trực tiếp trên phần cứng. Nó nằm giữa phần cứng và một hoặc nhiều hệ điều hành khách (guest operating system). Nó được khởi động trước cả hệ điều hành và tương tác trực tiếp với kernel. Điều này mang lại hiệu suất cao nhất có thể vì không có hệ điều hành chính nào cạnh tranh tài nguyên máy tính với nó. Tuy nhiên, nó cũng đồng nghĩa với việc hệ thống chỉ có thể được sử dụng để chạy các máy ảo vì hypervisor luôn phải chạy ngầm bên dưới.
Các hypervisor dạng native này có thể kể đến như VMware ESXi, Microsoft Hyper-V và Apple Boot Camp.



Loại-2: Hosted

Một hypervisor dạng hosted được cài đặt trên một máy tính chủ (host computer), mà trong đó có một hệ điều hành đã được cài đặt. Nó chạy như một ứng dụng cũng như các phần mềm khác trên máy tính. Hầu hết các hypervisor dạng hosted có thể quản lý và chạy nhiều máy ảo cùng một lúc. Lợi thế của một hypervisor dạng hosted là nó có thể được bật lên hoặc thoát ra khi cần thiết, giải phóng tài nguyên cho máy chủ. Tuy nhiên, vì chạy bên trên một hệ điều hành, nó có thể đem lại hiệu suất tương tự như một hypervisor ở dạng native.
Ví dụ về các hypervisor dạng hosted bao gồm VMware Workstation, Oracle VirtualBox và Parallels Desktop for Mac.



Ring

Trong khoa học máy tính, Hierarchical Protection Domains (hay Protection Rings) là cơ chế nhằm bảo vệ dữ liệu và chức năng của một chương trình tránh khỏi nguy cơ lỗi hoặc bị truy cập trái phép bởi các chương trình khác.
Một Protection Ring là một mức độ (mode/level/layer) truy cập tài nguyên hệ thống. Số lượng Ring tùy thuộc vào kiến trúc CPU và hệ điều hành chạy trên kiến trúc đó có khả năng hỗ trợ bao nhiêu Ring.
Các Ring được sắp xếp có thứ bậc, từ mức có nhiều đặc quyền nhất (dành cho trusted-software, thường được đánh số 0) đến mức có ít đặc quyền nhất (dành cho untrusted-software, được đánh số cao nhất).
Dưới đây là hình minh họa các Ring trong kiến trúc CPU x86



Các chương trình hoạt động tại Ring 0 có đặc quyền cao nhất, có thể tương tác trực tiếp với phần cứng như CPU, Memory…
Để cho phép các ứng dụng nằm ở Ring có trọng số cao truy cập các tài nguyên được quản lý bởi các chương trình nằm ở Ring có trọng số thấp hơn, người ta xây dựng các cổng (gate) đặc biệt. Ví dụ như system call (lời gọi hàm hệ thống) giữa các Ring.
Việc quy định chặt chẽ chương trình nào nằm tại Ring nào cộng với việc xây dựng các cổng phù hợp giữa các Ring sẽ đảm bảo tính ổn định của hệ thống, đồng thời ngăn chặn các chương trình nằm trong Ring cao sử dụng trái phép (do vô tình hoặc cố ý) các tài nguyên dành cho các chương trình khác nằm tại Ring thấp hơn
Ví dụ, một spyware đang chạy với tư cách là ứng dụng cho người dùng thông thường (thuộc untrusted software) nằm tại Ring 3 có ý định bật webcam mà không được sự đồng ý của người dùng. Hành vi này sẽ bị hệ thống ngăn chặn vì muốn truy cập tới phần cứng là thiết bị webcam nó phải sử dụng một hàm trong phần mềm điều khiển thiết bị (device driver) của webcam (thuộc trusted software) nằm tại Ring 1.
Hầu hết các hệ điều hành chỉ sử dụng 2 Ring ngay cả khi phần cứng mà hệ điều hành chạy trên đó hỗ trợ nhiều hơn 2 Ring. Ví dụ, Windows chỉ sử dụng 2 mức là Ring 0 (tương ứng với Kernel Mode) và Ring 3 (tương ứng với User Mode).
–> Tóm lại, ring cách ly người dùng với hệ điều hành bằng các cấp đặc quyền.

Phân loại Virtualization

Trong ảo hóa, người ta có thể ảo hóa:
  • RAM virtualization
  • CPU virtualization
  • Network virtualization
  • Device I/O virtualization
Trong khuôn khổ bài viết, tôi sẽ chỉ tập trung đến CPU virtualization.

CPU virtualization

Có 4 loại CPU virtualization :
  • Full Virtualization
  • Paravirtualization
  • Container-based Virtualization
  • Hardware Assisted Virtualization
  • OS level Virtualization
  • Hybrid Virtualization: ( Hardware Virtualized with PV Drivers )
Trong khuôn khổ bài viết, tôi sẽ tập trung vào Full Virtualization và Paravirtualization

Full Virtualization




Trong giải pháp này, các non-virtualizable instruction từ guest OS được translate qua binary translation ở virtualization layer và cache lại kết quả dùng cho các lần sau. Còn user level application thì thực hiện direct execution xuyên qua virtualization layer. Bằng cách này, trở ngại các chỉ thị guest OS không hoạt động ở ring khác 0 bị vượt qua còn các user level application vẫn họat động ở native speed (tốc độ đáp ứng yêu cầu giống như khi không có ảo hóa). Guest OS hoàn toàn không nhận ra nó đang nằm trên một lớp ảo hóa vì các low-level request không có gì thay đổi. Do đó guestOS hoàn toàn không phải chỉnh sửa gì.



Hiểu dân dã:
Thằng Guest OS nó sẽ không bị sửa đổi hệ điều hành để tương thích với phần cứng, mà nó sẽ dịch nhị phân các yêu cầu, rồi đưa cho thằng VMM, xong thằng VMM làm trung gian đưa cho thằng Hardware xử lý.
Nhìn vào ring = 1 của nó, thì thằng Guest OS này chỉ chạy trên quyền user lever, chứ không chạy trên quyền privilege, nó không trực tiếp chạy trên thằng hardware. Nhưng vì code của OS không bị sửa đổi, nên thằng Guest OS nó không biết điều đó, và nó làm việc bình thường như trên máy thật vậy, nhưng thực chất nó đang làm việc với thằng VMM.



Paravirtualization




Trong paravirtualization, hypervisor sẽ cung cấp hypercall interface. Guest OS sẽ được chỉnh sửa kernel code để thay thế non-virtualizable instruction bằng các hypercall này. Do kernel code của guest OS phải chỉnh sửa nên giải pháp này không thể sử dụng được một số hệ điều hành mã nguồn đóng như windows. Thêm nữa, do guest OS sử dụng hypercall nên nó sẽ biết được nó đang nằm trên một virtualization layer.



Hiểu dân dã:
Thằng Guest OS bây giờ đã bị sửa đổi 1 tí, để có thể nằm ở ring o, Việt Nam gọi là nhập gia tùy tục. Thằng Guest OS nó hiểu vị trí của mình chỉ là thằng khách thôi, nhưng mà nó lại có thể nhìn trực tiếp tài nguyên của máy thật, quyền truy cập vào hardware vì nó nằm ở ring 0.
Nhưng đối với các App, nó vẫn thấy thằng Guest OS này không có gì thay đổi, vì App cần interface gì thì Guest OS nó vẫn cung cấp cho interface ý, vẫn là API ý.



Hardware Assisted Virtualization – Cập nhật thêm




Các giải pháp hỗ trợ ảo hóa của hardware vendor được công bố vào năm 2006 như VT-x của Intel hay AMD-v của AMD. Cả hai giải pháp này đều hướng đến việc xây dựng một CPU mode mới dành riêng cho virtualization layer gọi là root mode ( CPU mode -1). Bằng cách này, các OS request từ guest OS sẽ được tự động đi xuyên qua virtualization layer và cũng không cần kỹ thuật binary translation nữa do guest OS đã nằm ở ring 0. Trạng thái của guest OS sẽ được lưu trong Virtual machine control structure (VT-x) hoặc Virtual machine control block (AMD-v). Tuy rất hứa hẹn nhưng giải pháp này chưa tối ưu về code nên ứng dụng còn hạn chế. Hiện tại VMWare chỉ tận dụng hardware virtualization cho 64 bits guest OS.
Hiểu dân dã:
Đây chính là sự kết hợp của bố Full Virtualization và mẹ Paravirtualization, có tất cả ưu điểm của cả hai bố mẹ, vừa không bị sửa đổi OS, tương thích với phần cứng mà vẫn được chạy ở ring 0



Nguồn tham khảo :

Wednesday, October 23, 2019

Công nghệ ảo hóa KVM

Nhằm phục vụ cho các nhu cầu thao tác dữ liệu trên các tệp tin, ảnh, “tạo ổ đĩa ảo” (sau đó phát triển lên thành “tạo máy ảo” để chạy song song hoặc chạy thử các hệ điều hành) là sự lưạ chọn hàng đầu của người dùng.
Tất cả những “ổ đĩa ảo”, “máy ảo” đó được gọi là ảo hóa (Virtualization). Hiểu đơn giản là bạn tạo ra thêm một phiên bản phụ (phiên bản ảo) của một số phần mềm, linh kiện trong máy tính hay thậm chí là ảo hóa toàn bộ chiếc máy tính.

Công nghệ ảo hóa KVM là gì?

KVM (viết tắt của Kernel Virtualization Machine) là công nghệ ảo hóa mới cho phép ảo hóa thực sự trên nền tảng phần cứng. Nghĩa là OS (hệ điều hành) chính mô phỏng phần cứng cho các OS khác để chạy trên đó. Nó hoạt động tương tự như một người quản lý siêu việt chia sẻ công bằng các tài nguyên như disk (ổ đĩa), network IO và CPU.
Một số loại KVM được nhiều người sử dụng là CL1008M, CL1016M, CL1000M, CL5708M, CL5716M….
Ra đời với phiên bản đầu tiên vào năm 2007 bởi công ty Qumranet tại Isarel, KVM được tích hợp sẵn vào nhân của hệ điều hành Linux bắt đầu từ phiên bản 2.6.20.
Năm 2008, RedHat đã mua lại Qumranet và bắt đầu phát triển, phổ biến KVM Hypervisor.

Công nghệ ảo hóa KVM/VMware là công nghệ gì? 2

KVM hoạt động như thế nào?

KVM chuyển đổi một nhân Linux (Linux kernel) thành một bare metal hypervisor và thêm vào đó những đặc trưng riêng của các bộ xử lý Intel như Intel VT-X hay AMD như AMD-V.
Khi đã trở thành một hypervisor, KVM hoàn toàn có thể setup các máy ảo với các hệ điều hành khác nhau và không phụ thuộc vào hệ điều hành của máy chủ vật lý.
Trong kiến trúc của KVM, Virtual machine được thực hiện tương tự như là quy trình xử lý thông thường của Linux, được lập lịch hoạt động như các scheduler tiểu chuẩn của Linux.
Trên thực tế, mỗi CPU ảo hoạt động như một tiến trình xử lý của Linux. Do đó, KVM được quyền thừa hưởng những ưu điểm từ các tính năng của nhân Linux.
Công nghệ ảo hóa KVM/VMware là công nghệ gì? 3

Ưu điểm gì của KVM thuyết phục người dùng?

Linh hoạt: Tuy máy chủ gốc được cài đặt Linux, nhưng KVM hỗ trợ tạo máy chủ ảo có thể chạy cả Linux, Windows. Sử dụng kết hợp với QEMU, KVM có thể chạy Mac OS X. KVM cũng hỗ trợ cả x86 và x86-64 system.
Tính độc quyền cao: Cấu hình từng gói VPS KVM sẽ chỉ một người sở hữu và toàn quyền sử dụng tài nguyên đó (CPU, RAM, diskspace…) mà không hề bị chia sẻ hay ảnh hưởng bởi các VPS khác trên cùng hệ thống.
Độ bảo mật chắc chắn: Tích hợp các đặc điểm bảo mật của Linux như SELinux với các cơ chế bảo mật nhiều lớp, KVM bảo vệ các máy ảo tối đa và cách ly hoàn toàn.
Tiết kiệm chi phí, độ mở rộng cao: Được phát triển trên nền tảng mã nguồn mở hoàn toàn miễn phí, được hỗ trợ từ cộng đồng và từ nhà sản xuất thiết bị, KVM ngày càng lớn mạnh và trở thành một lựa chọn hàng đầu cho doanh nghiệp với chi phí thấp, hiệu quả sử dụng cao.

Hạn chế:

Yêu cầu cao về server/máy chủ: Là công nghệ ảo hóa hoàn toàn phần cứng, VPS KVM yêu cầu cấu hình server vật lý khá cao. Thậm chí yêu cầu phải sử dụng các server của các thương hiệu lớn như IBM, Dell thì mới đảm bảo hoạt động tốt được.

Friday, May 31, 2019

So sánh các ngôn ngữ trung gian IR!

Why did you choose VEX instead of another IR (such as LLVM, REIL, BAP, etc)?

We had two design goals in angr that influenced this choice:
  1. angr needed to be able to analyze binaries from multiple architectures. This mandated the use of an IR to preserve our sanity, and required the IR to support many architectures.
  2. We wanted to implement a binary analysis engine, not a binary lifter. Many projects start and end with the implementation of a lifter, which is a time consuming process. We needed to take something that existed and already supported the lifting of multiple architectures.
Searching around the internet, the major choices were:
  • LLVM is an obvious first candidate, but lifting binary code to LLVM cleanly is a pain. The two solutions are either lifting to LLVM through QEMU, which is hackish (and the only implementation of it seems very tightly integrated into S2E), or McSema, which only supported x86 at the time but has since gone through a rewrite and gotten support for x86-64 and aarch64.
  • TCG is QEMU's IR, but extracting it seems very daunting as well and documentation is very scarse.
  • REIL seems promising, but there is no standard reference implementation that supports all the architectures that we wanted. It seems like a nice academic work, but to use it, we would have to implement our own lifters, which we wanted to avoid.
  • BAP was another possibility. When we started work on angr, BAP only supported lifting x86 code, and up-do-date versions of BAP were only available to academic collaborators of the BAP authors. These were two deal-breakers. BAP has since become open, but it still only supports x86_64, x86, and ARM.
  • VEX was the only choice that offered an open library and support for many architectures. As a bonus, it is very well documented and designed specifically for program analysis, making it very easy to use in angr.
While angr uses VEX now, there's no fundamental reason that multiple IRs cannot be used. There are two parts of angr, outside of the angr.engines.vexpackage, that are VEX-specific:
  • the jump labels (i.e., the Ijk_Retfor returns, Ijk_Callfor calls, and so forth) are VEX enums.
  • VEX treats registers as a memory space, and so does angr. While we provide accesses to state.regs.raxand friends, on the backend, this does state.registers.load(8, 8), where the first 8is a VEX-defined offset for raxto the register file.
To support multiple IRs, we'll either want to abstract these things or translate their labels to VEX analogues.

Tìm hiểu file ELF

Cấu trúc file ELF
Chương trình thực thì trên hệ điều hành Linux nhúng chủ yếu là các file ELF (Executable and Linking Format). Mỗi file ELF gồm có phần header, các bảng chứa thông tin quản trị và các đoạn chưa dữ liệu và code của chương trình. 3 phần hcinhs của thông tin quản trị của file ELF là ELF header, section table và program table. ELF header trở đến section và program table, section table giữ thông tin về các section của file ELF trên đĩa và program table chứa thông tin về cách copy các section từ ổ đĩa vào bộ nhớ khi thực thi.
Đa số các file ELF chứa đầy đủ 3 phần như hình bên, song chỉ có phần header là bắt buộc. Trong file excutable ELF, trường program table là bắt buộc, trong các file linkable thì trường section table là bắt buộc






Saturday, May 25, 2019

Tìm hiểu pyvex

Đang ý tưởng tìm hiểu cách phát hiện mã độc đa nền tảng, tức học các mẫu mã độc trên x86 mà có thể phát hiện mã độc trên mips, arm...
Một trong các hương đó là chuyển về mã trung gian chung
Một trong các phương pháp tốt dựa trên Valgrind và angr là Vex Il trên cơ sở PyVex https://github.com/angr/pyvex

Để giải quyết vấn đề đa kiến trúc, ngôn ngữ trung gian là cần thiết. Những điều khác biệt ở các kiến trúc chip khác nhau là:
1. Register names: tên và số lượng các thanh ghi của các kiến trúc chip khác nhau là khác nhau rất nhiều, tất nhiên có những nhóm phổ biến như thanh ghi chung, thành ghi stack, thanh ghi cờ điều kiện...
2. Memory access: có nhiều chế độ truy cập khách nhau như big/little-endian.
3. Memory segmentation
4. Instruction side-effects: nhiều lệnh có ảnh hưởng ngầm đến các lệnh khác.

Để giải quyết bài toán khác biệt này, IR hướng tới giải pháp trừu tượng hoá và tường minh hoá các hành động và trạng thái để có thể giải quyết đa số các trường hợp.

Các thành phần cơ bản của Vex:
  • Expressions. IR Expressions represent a calculated or constant value. This includes memory loads, register reads, and results of arithmetic operations.
  • Operations. IR Operations describe a modification of IR Expressions. This includes integer arithmetic, floating-point arithmetic, bit operations, and so forth. An IR Operation applied to IR Expressions yields an IR Expression as a result.
  • Temporary variables. VEX uses temporary variables as internal registers: IR Expressions are stored in temporary variables between use. The content of a temporary variable can be retrieved using an IR Expression. These temporaries are numbered, starting at t0. These temporaries are strongly typed (i.e., "64-bit integer" or "32-bit float").
  • Statements. IR Statements model changes in the state of the target machine, such as the effect of memory stores and register writes. IR Statements use IR Expressions for values they may need. For example, a memory store IR Statement uses an IR Expression for the target address of the write, and another IR Expression for the content.
  • Blocks. An IR Block is a collection of IR Statements, representing an extended basic block (termed "IR Super Block" or "IRSB") in the target architecture. A block can have several exits. For conditional exits from the middle of a basic block, a special Exit IR Statement is used. An IR Expression is used to represent the target of the unconditional exit at the end of the block.
t0 = GET:I32(16)           1
t1 = 0x8:I32                  2
t3 = Sub32(t0,t1)          3
PUT(16) = t3                 4
PUT(68) = 0x59FC8:I32        5

Vex có mấy loại biểu thức phổ biến:
1. hằng số như lệnh 2, đặt tên là "con"
2. đọc lấy giá trị lưu trong thanh ghi như lệnh 1, đặt tên là "get"

3. đặt giá trị cho thanh ghi, đặt tên là "put"
4. đọc giá trị từ thanh ghi tạm, ví dụ RdTmp(t10), đặt tên là "RdT"
5. ghi giá trị vào thanh ghi tạm, ví dụ t2= 0x12, đặt tên là "WdT"
6. đọc giá trị từ bộ nhớ, đặt tên là "LDle"
7. ghi gía trị vào bộ nhớ, đặt tên "STle"
8. lệnh điều kiện if, đặt tên là ite
9. các toán tử như lệnh 3, đặt tên theo toán tử, 
đang tìm danh sách các toán tử mà vex hỗ trợ

Trong 1 câu lệnh của vex có thể có thể gồm nhiều biểu thức, toán tử và toán hang. Việc trích chọn đặc trưng n-gram sẽ chọn 1 câu là 1 biểu thức đặc trưng có thứ tự ưu tiên, như vậy có 1 số biểu thức sẽ không xuất hiêện là "gram", ví dụ như: con, vì hằng số bao giờ cũng sẽ gắn vào thanh ghi nào đó nên lấy "gram" là gán thanh ghi. 











Phát hiện bug trong phần mềm và tự động sửa!

Một chủ đề khá hay mà tôi mới được tiếp cận là làm sao tự động phát hiện lỗi và sửa lỗi phần mềm, chương trình tôi đang quan tâm là các file ELF trên Embedded Linux. Vì đang nghiên cứu IoT nên tôi chọn các giải pháp đa kiến trúc chip!
Trước tiên tôi gặp công cụ tự sửa lỗi trong firmware https://github.com/eschulte/netgear-repair

Công cụ thứ 2 giúp check phát hiện lỗi trong file ELF là https://github.com/fkie-cad/cwe_checker

Quá tìm tìm hiểu hướng này còn phát hiện ra công cụ rất hay https://fkie-cad.github.io/FACT_core/
Tôi sẽ tìm hiểu và giới thiệu các công cụ này trong bài này