在 Linux C 程序中写入自己的可执行文件,错误“文本文件忙”

Posted

技术标签:

【中文标题】在 Linux C 程序中写入自己的可执行文件,错误“文本文件忙”【英文标题】:Write to own executable in Linux C program, error "Text file busy" 【发布时间】:2013-10-22 15:28:22 【问题描述】:

对于一个安全类,我应该为一个程序编写自修改代码,该程序在磁盘上找到自己的可执行文件,读取二进制数据,并在将其写回磁盘之前加密其中的一部分。这应该就像一个多态病毒,它会改变自己以欺骗检测已知签名的防病毒扫描程序。

我已经准备好了所有的东西:

我正在使用/proc/self/exe 查找可执行文件。 我正在使用一个简单的 AES 实现来加密可执行文件中一些虚拟代码中的 16 字节字符串。 我能够读取二进制数据并找到我需要加密的部分。

我的问题是我能够打开可执行文件的唯一方法是只读模式"rb"。如果我尝试以"wb""r+b" 模式打开文件进行写入,则会返回错误"Text file busy"。无论如何,我是否可以在 C 中写入进程自己的可执行文件?我可以通过某种方式更改权限来做到这一点吗?

编辑:我想要完成的是有一个可执行文件,它会在每次运行时对其自身的一部分进行加密,以便每次运行后都会有一个新的校验和。 p>

从可执行二进制文件中读取数据后,如何将其写回或删除并替换为具有相同文件名的新文件?

【问题讨论】:

尝试复制自己(在只读模式下),执行复制,然后从副本中删除原件。 【参考方案1】:

您无法写入当前映射为可执行文件的文件。但是,您可以写入与当前可执行文件具有相同路径 的文件,只要它实际上不是同一个文件 - 尝试取消您正在执行的文件的链接并创建一个例如,新文件。

【讨论】:

你能详细说明一下吗? same path 是指在与当前可执行文件相同的目录中创建的新文件,但文件名不同? unlink 是指使用这个unlink 函数吗? 是的,您可以unlink() 或移动(link() 然后unlink())可执行文件。 谢谢,我可以使用unlink 函数完成我需要的工作。我首先以"rb"模式打开可执行二进制文件,读入数据,关闭文件,取消链接文件,以"wb"模式在同一路径打开一个要写入的新文件,写入修改后的二进制数据,关闭文件,然后我使用chmod 将新文件标记为可执行文件。【参考方案2】:

为了进行自我修改,我在 nasm 中编写了一个小代码(可以用作存根),它会自行打开,并且在代码中间(就在 mmap 之后),我们有一个指针指向我们可以修改的可执行文件的字节。

代码如下:

BITS 64

section .text
    global _start

_start:
    call _main__

    mov rax, 60
    mov rdi, 0x0
    syscall ; exit(0);

_main__:
    push rbp
    mov rbp, rsp
    sub rsp, 144 ; stat_file
    mov rdi, [rbp+0x18]
    lea rsi, [rsp]
    call _open_self ; open self
    push r12 ; len file
    push rax ; addr
    mov r14, rsi

    

    mov rdi, [rbp+0x18] ; pathname
    pop rsi ; addr
    pop rdx ; len
    push rdx
    push rsi
    call __create
    mov r13, rax ; second fd

    mov rdi, r14 ; fd
    pop rsi ; addr -> mmap
    pop rdx ; len_file
    call __close_unmap

    mov rax, 87
    mov rdi, [rbp+0x18]
    syscall

    mov rax, 0x3 ; close(scnd_fd);
    mov rdi, r13
    syscall

    mov rax, 86
    push 'nasm'
    lea rdi, [rsp]
    mov rsi, [rbp+0x18]
    syscall ; link tmp name to original name

    mov rax, 87
    lea rdi, [rsp]
    syscall ; delete old tmp file

    leave
    ret


; ===============================

; Open himself
_open_self:
    push rbp
    mov rbp, rsp

    mov r15, rsi ; &stat_file
    mov r12, rdi ; *pathname

    mov rax, 0x2
    mov rsi, 0x0 ; 0_RD
    mov rdx, 509
    syscall

    push rax ; fd

    mov rdi, rax ; fd
    mov rsi, r15 ; struct stat
    mov rax, 5 ; fstat
    syscall

    xor rdi, rdi
    mov rsi, qword [r15+48]
    mov rdx, 0x4
    mov r10, 0x2
    pop r8
    push r8
    mov r9, 0x0
    mov rax, 9
    syscall ; mmap

    ; rax -> byte of the executable that we gonna dump

    mov r12, qword [r15+48]

    pop rsi ; fd
    leave
    ret

; ===============================

; int __create(const char *pathname, void *addr, ssize_t len_bytes_mapped);

__create:
    push rbp
    mov rbp, rsp
    push rsi ; addr
    push rcx ; len

    push 'nasm'
    lea rdi, [rsp]
    mov rax, 0x2
    mov rsi, 0x42 ; 0_CREAT | O_RDWR
    mov rdx, 509
    syscall ; sys_open

    add rsp, 0x8 ; 'nasm'
    mov r9, rax ; fd
    mov rdi, rax ; fd

    mov rax, 0x1
    pop rdx
    pop rsi
    syscall ; sys_write

    mov rax, r9 ; fd final

    leave
    ret

; int __close_unmap(int fd, unsigned lon addr, ssize_t len_file);

__close_unmap:
    push rbp
    mov rbp, rsp

    push rdi

    mov rdi, rsi
    mov rsi, rdx
    mov rax, 11
    syscall ; munmap(addr, len_file)

    pop rdi
    mov rax, 3
    syscall ; close(fd);

    leave
    ret

它有点长,但它只是:

-以读取模式自行打开 (O_RD == 0x0)

-做一个统计(*pathname, &buffer_struct_stat);

-然后是一个mmap(0, buffer_struct_stat.st_size, 0x4, MAP_PRIVATE, fd_read_only, 0);

-在这里,您可以通过编辑 mmap 返回的地址处的字节来编辑可执行文件

-创建一个名为“nasm”的 tmp 文件

-做一个写(fd_tmp, address_of_mmap, buffer_struct_stat.st_size)

-关闭两个文件描述符,然后对mmap进行munmap

-现在很酷:unlink(pathname) 和 link("nasm", "pathname")

【讨论】:

以上是关于在 Linux C 程序中写入自己的可执行文件,错误“文本文件忙”的主要内容,如果未能解决你的问题,请参考以下文章

如何在linux中运行c程序

Linux中,运行一个C语言程序如何运行

程序编译链接后形成的可执行文件是啥文件

如何将在windows下写的程序编译成linux下的可执行程序文件?

Linux学习(第一篇)

交叉编译-如何编译Android平台的可执行程序