Chào mừng đến với Diễn đàn lập trình - Cộng đồng lập trình.
Kết quả 1 đến 7 của 7
  1. #1
    Ngày tham gia
    Sep 2015
    Bài viết
    0

    Các kĩ thuật ẩn lời gọi hàm API trên Win32 [Tài liệu tổng hợp]

    0. Lời nói đầu

    Bài viết được đăng tại congdongcviet.com, nếu bạn copy, vui lòng ghi rõ nguồn

    Đây là bài viết mình dịch và tổng hợp dựa trên bài viết A Museum of API Obfuscation on Win32 của tác giả Masaki Suenaga, thành viên của Symantec Security Response, xuất bản vào November 2009.

    Bài viết nguyên bản bằng tiếng Anh nhưng mình sẽ dịch lại và post dần theo dựa trên kinh nghiệm nghiên cứu và tìm hiểu thực tế của mình khi lập trình trên nền hệ điều hành Microsoft Windows 32bit.

    Theo quan điểm của mình, đây là một bài viết khá đầy đủ và chi tiết, cũng như đủ sâu về các vấn đề liên quan tới mã hóa lời gọi hàm API (API Obfuscation). Qua bài viết này, mình có thêm nhiều ý tưởng mới, và qua đó biết được thêm nhiều kĩ thuật mới, cũng như tối ưu các kĩ thuật đã có.

    Các kĩ thuật này đã có từ lâu, một số được sử dụng từ các virus trên DOS. Mặc dù các kĩ thuật này đã cũ nhưng ý tưởng của nó thì không lỗi thời. Nếu hiểu được các kĩ thuật này, việc phát triển chúng hoàn toàn có thể.

    Yêu cầu ( dành cho các bạn mới, nếu bạn quan tâm đến vấn đề này mà chưa biết bắt đầu từ đâu thì có thể theo thứ tự sau đây ):
    40px

    1.Giới thiệu

    Các công ty antivirus cố gắng để nhận diện các mối nguy hại bằng cách giải mã (unpack) các mẫu khả nghi, vì thế đó là đích ngắm tới của các unpacker. Khi các mẫu được giái mã thành công, mẫu được nhận diện. Cách làm này là phương pháp chung để nhận diện các mẫu malware cùng loại, cùng một họ và không cần phân tích những thao tác tới windows API của mẫu đó. Ngược lại, khi phân tích sâu những malware, cần phải có hiểu biết về cơ chế API thực hiện thế nào trong suốt quá trình thực thi của nó.

    Khi một mẫu không thể unpack, memory dump được sử dụng để phân tích những hành vi bên trong malware. Dữ liệu này đủ chi tiết để có thể phân tích chính xác liệu một mẫu có là mã độc hay không. mặc dù chúng có thể không phù hợp khi dùng các phần mềm disassembler, header của file thực thi cần được điều chỉnh một cách hợp lý để có thể có được các lời gọi API hợp lệ cũng như tránh các vấn đề với file alignment

    Các lời gọi làm API có thể bị che, mã hóa theo rất nhiều cách khác nhau.
    40px
    Người phân tích có thể rất khó khăn khi khi các phương pháp này được hết hợp với nhau hoặc chỉ trong một phạm vi nhất định. Bài viết này sẽ đề cập các phương pháp trên một cách chi tiết và có thể sử dụng nó để mã hóa, che giấu các lời gọi hàm API, các công cụ cần thiết cũng như công nghệ được sử dụng để phát hiện chúng.


    2 . File Image vs Memory Image

    Portable Executable (PE) format is là định dạng file thực thi trên windows 32-bit và 64-bit (Windows 95, 98, Me, NT 4.0, 2000, Server 2003, XP, Vista, windows 7). File PE không được load lên bộ nhớ giống như nội dung của nó trên đĩa, thay vào đó, chỉ có phần header được nạp, phần này chứa thông tin về file thực thi. Không giống như MS-DOS, các thanh ghi đoạn( segment registers) không được sử dụng để tái định vị lại dữ liệu và mã lệnh, bằng việc sử dụng các ‘sections’ chứa mã lệnh, biến , hằng số resource. Phần Header còn chứa thông tin về trạnh thái của file, ví dụ như chứa thông tin về kích thước bộ nhớ cần thiết. Không giống MS-DOS, Windows gán 2GB không gian địa chỉ cho mỗi chương trình mới được nạp.


    2.1. Vai trò của Loader

    Khi file PE được thực thi, file handle được chuyển cho hàm CreateProcess() và các DLL được import tĩnh sẽ được nạp cùng với file (EXE và DLL đều là các file PE, phân biệt bằng một cờ nằm trong header). Các thành phần thuộc hệ điều hành chịu trách nhiệm khởi tạo gọi là loader.

    Loader đọc phần header và copy nó vào vùng địa chỉ tính từ ImageBase, thông tin về ImageBase cũng lưu trong header. Nếu vùng địa chỉ này đã được nạp bởi một file PE khác, loader sẽ tự động chọn một không gian địa chỉ mới để nạp vào. Vì không phải lúc nào file cũng được load lên đúng địa chỉ ghi trong ImageBase của header, nên tất cả các địa chỉ và dữ liệu đều cần tính tương đối với địa chỉ ImageBase do loader cung cấp. Ngoài ra, cơ chế Address space layout randomization( ASLR) thay đổi địa chỉ ImageBase mỗi lần file PE được load lên nên khi lập trình, không được phép fix cứng giá trị này trong code.

    Sau khi loader đọc xong header, nó tiếp tục đọc thông tin về các section và copy dữ liệu tới mỗi vùng nhớ. Các vùng nhớ này được loader cấp phát dựa trên thông tin lưu trong header, vì thế, chỉ cần 1 sơ xuất nhỏ trong header có thể khiến file không thực thi được. Có 2 giá trị cần quan tâm là file alignment và section alignment. Nếu chúng khác nhau, loader sec cấp phát bộ nhớ và copy dữ liệu trong từng section dựa trên section alignment

    Nếu loader sử dụng giá trị ImageBase khác với giá trị lưu trong header( thường xảy ra với DLL), việc tái định vị địa chỉ (address relocation) cần được thực hiện. Các lệnh nhảy tương đối (relative jumps, Relative calls) thì không cần thay đổi, nhưng với các địa chỉ tuyệt đối, giá trị này cần được điều chỉnh. Đó là hiệu của giá trị ImageBase lưu trong header (preferred ImageBase) và ImageBase được sử dụng. tất cả các địa chỉ khác cũng cần thay đổi theo.

    Gần như tất cả các chương trình đều import DLL. Theo quy tắc, các file thực thi trên windows không thể gọi các lời gọi ngắt(int) như các chương trình trên dos mà phải thông quá API. Loader nạp các DLL được import tĩnh trên file PE, giải mã các API và ghi địa chỉ của chúng vào bộ nhớ.

    Khi các quá trình trên hoàn tất, chương trình được chạy từ EntryPoint. Một số tài liệu nói rằng EntryPoint bắt buộc phải nằm trong code, không thể nằm trong vùng dữ liệu, tuy nhiên, điều này có thể vượt qua bằng một số cách, mình sẽ mô tả sau.

    Bài viết được đăng tại congdongcviet.com, nếu bạn copy, vui lòng ghi rõ nguồn
    2.2. Định vị địa chỉ của hàm API trong Loader

    Các lời gọi hàm API bên trong chương trình được dịch bởi Microsoft Visual Studio thường là
    Mã:
    call [offset32]
    hoặc
    Mã:
    mov reg32, [offset32]
    call reg32
    Ví dụ:

    Mã:
    TranslateMessage(&msg);
    sẽ được dịch là

    Mã:
    lea eax, [ebp-20h]push eaxcall [01001270h]hoặcmov edi, [01001270h]lea eax, [EBP-20h]push eaxcall edi
    Địa chỉ của TranslateMessage() trong ví dụ trên được lưu tại ô nhớ có địa chỉ 0x1001270. Khi chương trình bắt đầu chạy, địa chỉ đúng của TranslateMessage() được ghi vào 4bytes tại 0x1001270; PE file lưu lại giá trị này.

    Giải mã API(API resolution) thực hiện trước khi chương trình hoạt động. Để làm điều này, loader nạp bảng IAT (import address table) ( vị trí bảng này được lưu trong header). IAT cho biết DLL nào cần được nạp, và địa chỉ của các hàm API sẽ được lưu tại đâu.


    2.3. IDA

    Interactive Disassembler (IDA) là phần mềm được rất nhiều người phân tích virus sử dụng ( giá của nó còn đắt gấp mấy lần windows đấy). IDA có thể đưa ra tên của các hàm API khi nó được gọi bên trong chương trình đựa vào Import Address Table (IAT). IDA không thể giải mã API mà không tìm thấy bảng IAT. Do đó, việc dump bộ nhớ xuống đĩa không phù hợp khi dùng với IDA ( tất nhiên vẫn có cách để vượt qua, mình sẽ trình bày sau)


    2.4. Môi trường phát triển và cách gọi các hàm API


    Sự khác nhau của các PE files được dịch bởi nhiều trình dịch khác nhau có thể hỗ trợ các nhà phần tích virus.

    PE file được dịch bởi Microsoft Visual C thường dùng các lời gọi API dạng call dword ptr [IAT entry].
    Các chương trình C++ đều giống các chương trình C khi gọi API nhưng một số được mã hóa khi gọi các thư viện. Ví dụ, toán tử new thường được dịch là call dword ptr [??2@YAPAXI@Z],
    trong đó ??2@YAPAXI@Z được ánh xạ tới void * __cdecl operator new (unsigned int). Tên của hàm rất khó đoán, nhưng IDA có thể giải mã chính xác đó là toán tử new

    Các phương thức khi viết các chương trình MFC, ví dụ như void CWnd::~CWnd() (có thể sẽ được dịch là ??1CWnd@@UAE@XZ) thường không được import theo tên. thay vào đó, ordinal number được sử dụng, ví dụ như 818 trong thư viện mfc42.dll. Do đó, chương trình sẽ gọi hàm số thứ tự là 818 của thư viện mfc42.dll. Vì thế, IDA tìm thư viện mfc42.dll, tìm hàm đó và hiển thị tên của nó; tuy nhiên, nếu đó là một memory dump, IDA sẽ không thể đưa ra thông tin của hàm.

    Các chương trình C được địch bằng Delphi hoặc Borland compilers lại có một cách khác để gọi hàm. Ví dụ, để gọi hàm GetSystemMetrics(), trình biên dịch sẽ dịch là

    Mã:
    call near ptr j_GetSystemMetrics;; something....;;......... j_GetSystemMetrics : jmp dword ptr [GetSystemMetrics] ;;.........
    địa chỉ ô nhớ chứa địa chỉ hàm GetSystemMetrics có thể tìm thấy trong IAT và được giải mã bởi loader. IDA đánh dấu địa chỉ này bằng nhãn __imp_GetSystemMetrics tại nơi gọi hàm GetSystemMetrics().

    Chương trình viết bằng Visual Basic import VB các hàm thông qua IAT, nhưng Windows APIs được import theo một cách hoàn toàn khác. Cách thường dùng là:

    Mã:
    00408094: db 'urlmon',0004080A0: db 'URLDownloadToFileA',0 004080B4: dd 408094     ; offset of 'urlmon'004080B8: dd 4080A0     ; offset of 'URLDownloadToFileA'004080BC: dd 040000004080C0: dd 4092D8     ; offset of a structure. . . 004080CC: URLDownloadToFileA proc nearmov eax, dword_4092E0 ; initially zeroor eax, eaxjz short 4080D7       ; If not yet resolved, call VB Runtime library.jmp eax004080D7: push 4080B4   mov eax, offset DllFunctionCall ; jmp [__imp_DllFunctionCall]jmp eax
    Khi URLDownloadToFileA() được gọi lần đầu, chương trình sẽ kiểm tra xem địa chỉ của URLDownloadToFileA đã có chưa. Nếu chưa, chương trình sẽ gọi tới hàm DllFunctionCall() bên trong thư viện VB Runtime và địa chỉ hàm này sẽ được giải mã, lưu lại, rồi chuyển cho chương trình.
    Một số API call không thể xác định được cho đến khi chương trình thực thi, việc dump bộ nhớ gần như không thể thực hiện dễ dàng.

    Bài viết được đăng tại congdongcviet.com, nếu bạn copy, vui lòng ghi rõ nguồn
    3. Phân tích API
    Phân tích lời gọi hàm API có thể không cần thiết khi phân tích các biến thể của cùng một họ mã độc, các mẫu có các chức năng chính gần như giống nhau, chỉ khác nhau ở một số đặc điểm như địa chỉ truy cập, URL, tên file và những xâu khác bên trong nó.

    Việc này chỉ cần thiết khi phân tích các mẫu lạ, và cần phải quyết định xem nó có phải malware hay không. Ví dụ, khi gặp một mẫu lạ, mà phân tích kĩ càng lời gọi API rất khó, có thể dựa vào các thông tin chính như địa chỉ email, URL, các xâu liên quan, kết quả ghi log bàn phím để quan sát và đoán nhận liệu đó có phải là một Trojan horse nhưng nếu không có ghi log thì rất có thể đó chỉ là chương trình game online. Khi đó cần phân tích kĩ.


    3.1. Phân tích và theo dõi lời gọi API

    Một vài phần mềm security sẽ theo dõi các lời gọi hàm API, bằng cách đánh giá liên tục, phần mềm có thể xác định tiến trình đó có ích hay có hại. Các phần mềm như vậy thường hook ở các hàm API, và một vài chỗ khác với mục đích tránh bị phát hiện.

    Ngược lại, người phân tích virus thường chỉ có một bản memory dump để bắt đầu và cần phải biết các hàm API được gọi thế nào.

    3.2 Tránh việc đoán hàm
    Trong khi phân loại các mẫu là mã độc hoặc chương trình bình thường, công việc của các nhà phân tích là hiểu rõ hành vi của từng mẫu. Nếu một hàm API được gọi mà không thể biết chắc đó là hàm nào, người phân tích có thể đoán tên hàm dựa trên các tham số truyền vào cho hàm. Một vài API như RegOpenKey() sử dụng các tham số rất dễ đoán nhận như HKEY_CURRENT_USER (giá trị là 0x80000001) trong khi một vài API khác thì không thể đoán dễ dàng như vậy.Tham số là các xâu luôn là gợi ý quan trọng để đoán đó là hàm API nào, ví dụ như nếu một hàm chứa tham số đầu vào là ‘Software\Microsoft\Windows\CurrentVersion’, thì khả năng cao hàm đó liên quan tới các thao tác đọc và ghi vào registry. Tuy nhiên, để tránh sự nhập nhằng, dễ nhầm lẫn, nếu có thể phân tích kĩ thì tránh đoán càng nhiều càng tốt, Công việc này cần phải có 1 quá trình kinh nghiệm làm việc lâu năm thì mới có thể đoán chính xác

    Trong quá trình khởi tạo của loader, bảng IAT chứa giá trị địa chỉ của tất cả các hàm được import, ví dụ như giá trị 0x77D16017 là địa chỉ của hàm GetSystemMetrics() trên Windows XP. Mã hóa API ẩn đi các thông tin này nên việc tìm ra hàm nào được gọi rất khó.

    3.3 Lý do cần thiết để mã hóa API

    Nếu các hàm API bị mã hóa, quá trình trên sẽ làm những nhà phân tích mẫu khó khăn hơn và tốn thời gian cũng như trí tuệ để phân tích hơn so với mẫu không được mã hóa. Thậm chí, nếu không biết về cách thức giải mã, việc dump bộ nhớ là cần thiết trong quá trình phân tích.Người viết mã độc cố gắng thực hiện mã hóa xâu, che dấu API với mục đích chính là tránh bị phát hiên và qua đó chương trình của mình được chấp nhận bởi các antivirus. Tuy nhiên, kĩ thuật này cũng thường xuyên được sử dụng trong các chương trình bình thường.

    Các chương trình game online là một ví dụ dễ thấy nhất. Người chơi thường cố gắng phân tích và ghi vào bộ nhớ của chương trình những giá trị hợp lệ với mục đích ăn gian. Mã hóa API sẽ giảm thiểu những điều không mong muốn này.

    Các chương trình có thu phí cũng sử dụng kĩ thuật này để tránh bị bẻ khóa quá dễ dàng. Với các chương trình quan trọng, việc mã hóa để tránh các bị đánh cắp các thuật toán giá trị.
    Mặc dù được sử dụng một cách hợp pháp, nhưng đa phần các kĩ thuật này dễ khiến cho chương trình của bạn bị nhận nhầm là các chương trình mã độc.

    3.4 Lý do cần thiết để vượt qua các kĩ thuật mã hóa API

    Việc phân tích kĩ các chương trình mã độc trước hết sẽ giúp cho khách hàng của các công ty AV tránh các mối nguy hại tiềm tàng, giảm thiếu việc tái lây nhiễm như đóng các cổng TCP/IP không cần thiết, thiết lập các luật mới cho tường lửa hoặc cấp các địa chỉ IP, cũng như khôi phục lại các giá trị bị thay đổi về trạng thái ban đầu, khôi phục lại file như khi chưa lây nhiễm.

    Việc phân tích kĩ giúp cho nhà phân tích có thể tạo ra các chương trình phòng chống cũng như gỡ bỏ mã độc một cách hiệu quả. Khi đã có được phần mềm gỡ bỏ hiệu quả, khách hàng sẽ không cần thiết phải cài đặt lại hệ thống, tránh được những sự cố không mong muốn sau này, tiến kiệm thời gian cũng như giảm thiểu tổn thất do mã độc gây ra.

    3.5 Công cụ để thực hiện giải mã

    Như đã đề cập trên đây, IDA là công cụ để disassembler được sử dụng rất nhiều và phổ biến, hỗ trợ ngôn ngữ script với cú pháp giống C : IDC script. IDC script cho phép tự động thực hiện các công việc lặp đi lặp lại, tìm kiếm, thay tên cũng như nhiều công việc tốn thời gian khác.

    Tuy nhiên, theo quan điểm của dịch giả, người phân tích cũng không nên quá dựa vào IDA và coi nó như một công cụ thần thánh, bạ đâu cũng dùng. Việc phân tích cần được kết hợp nhuần nhuyễn giữa các công cụ với nhau, thậm chí cả MS word để ghi báo cáo hay Xmind để mô hình hóa toàn bộ hoạt động của mã độc. Một chương trình nên hình nó từ tổng thể tới chi tiết, từ cấu trúc, thông tin mà PEiD đưa ra cho đến từng dòng trong mã lệnh. Nếu cần thiết, nên kết hợp cùng OllyDbg, WinDbg, HexWorkShop, các tool của sysinternals.....

    4. Tạo memory dump

    4.1 Phân giải tên hàm API mà không cần IAT
    Việc ánh xạ từ địa chỉ hàm API được sử dụng sang tên của hàm là một cách dễ dàng để tìm kiếm và sau đó thì không cần phải sử dụng bảng import nữa. Ví dụ, nếu một lệnh call [01001480h] xuất hiện trong memory dump, tool có thể đọc giá trị 77D16017h từ ô nhớ kia và sau đó tìm trong bộ nhớ để biết địa chỉ này nằm trong module nào, trong trường hợp này là user32.dll. Sau đó, dựa vào bàng export của user32.dll, tool sẽ đưa ra địa chỉ trên ứng với GetSystemMetrics().

    4.2 Xác nhận bảng IAT

    Bảng IAT trong quá trình dump memory xuống đĩa có thể không đáng tin, người viết mã độc có thể xáo bảng này khỏi bộ nhớ hoặc cố tình xây dựng một bảng IAT giả để gây khó khăn cho người phân tích. Vì vậy, tốt nhất không nên dùng bảng IAT này.

    4.3 Chỉnh lại ImageBase và section table

    IDA được thiết kế để dịch ngược các chương trình định dạng PE, vì thế nó sẽ coi như thông tin trong PE header là chính xác. Nếu ImageBase trong header là 1000000h, IDA sẽ hiển thị mã assembler với địa chỉ này mà không hề quan tâm tới việc tái định vị lại trong quá trình nạp, đặc biệt là với các DLL. Nếu một dll được định vị tại 12000000h thay vì 1000000h, tất cả các toán tử và cấu trúc dữ liệu sử dụng địa chỉ tuyệt đối sẽ được định vị lại. Định vị lại các giá trị này do loader thực hiện nhưng nó sẽ không cập nhật lại vào header. Đó là nguyên nhân tại sao IDA không thể hiển thị chính xác các xâu. Tất cả các phần mềm phân giải địa chỉ cần thực hiện điều này.
    Phân biệt hai giá trị alignment: Ví dụ, một file có giá trị file alignment là 200h và memory alignment là 1000h thì các section của file đó sẽ bắt đầu tại các offset 200h (hoặc 400h, 600h etc.), nhưng khi đã load lên bộ nhớ, địa chỉ bắt đầu của các section này là 1000h (or 2000h, 3000h, etc.). Khi IDA đọc một file thực thi, các giá trị đầu vào đươc tính toán, địa chỉ này được tính toán dựa trên file alignment.
    IDA cũng xem xét giá trị raw data offset trong mỗi section khác nhau. Ví dụ, nếu địa chỉ này là 1200h và virtual address là 2000h, IDA sẽ . For example, if the raw data offset of a section is 1200h and the virtual address of the section is 2000h, IDA tinh chỉnh tất cả địa chỉ trong mỗi section .

    4.4 Tái tạo header của file PE

    Một khi file đã chạy, header không còn cần thiết nữa, và vì thế, chương trình có thể ghi đè hoặc xóa nó. Vì thế khi có thể không load được memory dump vào IDA.
    Thậm chí,nếu không có header, có thể tạo một header mới chứa toàn bộ dữ liệu. Điều đó có nghĩa là, khi phân tích thì section cũng không cần thiết.
    4.5 Tìm kiếm các module ẩn
    Trong quá trình phân tích module chínhs có thể liệt kê các module bằng cách dùng EnumProcessModules() và GetModuleInformation() nhưng chỉ các module được quản lý bởi OS mới có thể liệt kê theo cách này.
    Trừ các hệ thống hỗ trợ NX (No eXecute) bit, Windows không yêu cầu các khối chứa mã thực thi phải đăng kí cờ ‘executable’ với hệ điều hành. Điều đó có nghĩa là mã chương trình nằm ở bất kể vị trí nào cũng thực thi được. Hệ thống được thiết kế để tương thích một cách uyển chuyển nhưng điều này cũng khiến cho các lỗi được khai thác dễ dàng như lỗi tràn bộ đệm.
    Các chương trình packer cũng thực hiện ghi đè chương trình bằng code sau khi giải mã, thông qua việc cấp phát vùng nhớ mới VirtualAlloc() or GlobalAlloc(), ghi mã chương trình và thực thi code từ đây.
    Do đó cần phải tìm toàn bộ bộ nhớ để tìm các module ẩn
    4.6 Tìm kiếm tên của API bên trong các thread bị Inject
    Exe và dll đều là các module chương trình, exe được tải lên trước, sau đó là các dll được liên kết cùng. Một khi các DLL này yêu cầu các dll khác, các DLL kia cũng được tải lên. Các module được tải lên bộ nhớ và được đặt trong các khối bộ nhớ, mỗi khối chứa header và các section. Không gian bộ nhớ stack được đi kèm với từng khối trên. Khi hàm cấp phát bộ nhớ được gọi, một khối mới được để ra cho tiến trình.
    Một chươn trình không thể truy cập không gian bộ nhớ của một tiến trình khác. Trên DOS, điều này hoàn toàn có thể.
    Hàm VirtualAllocEx() cho phép cấp phát một khối bộ nhớ nằm trong một tiến trình khác và code có thể được copy vào vùng nhớ này và thực thi thông qua hàm CreateRemoteThread(). Khi code đã được inject vào một tiến trình, nó sẽ được coi như một phần của tiến trình đó.
    Các thread được inject sẽ không mang theo bảng import mà nó sẽ sử dụng địa chỉ có sẵn của tiến trình bị inject.
    4.7 Các khối bộ nhớ khác
    Như đã đề cập, mã chương trình có thể đật trong các khối bộ nhớ được cấp phát và trên stack. Một khối bộ nhớ vừa chứa dữ liệu vừa chứa chương trình là cách che giấu hữu hiệu nhất.

  2. #2
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Giữ chỗ để post tiếp.

    Giữ chỗ để post tiếp.

  3. #3
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Phần thảo luận của các bạn mình sẽ cập nhật lên đây để tiện theo dõi

  4. #4
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Demo các kiểu (nếu có ). tạm thời giữ chỗ đã

  5. #5
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Bài viết hay... có tài liệu thì share luôn đi [IMG]images/smilies/2.gif[/IMG]

  6. #6
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Viết tiếp đi anh ơi. Đang hay mà.

  7. #7
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Bài viết hay... có tài liệu thì share luôn [IMG]images/smilies/applause.gif[/IMG]

    lịch bóng đá euro 2016
    web ca cuoc uy tin
    cách chơi cá độ bóng đá tài xỉu
    kieu nha cai

 

 

Quyền viết bài

  • Bạn Không thể gửi Chủ đề mới
  • Bạn Không thể Gửi trả lời
  • Bạn Không thể Gửi file đính kèm
  • Bạn Không thể Sửa bài viết của mình
  •