以汇编代码将全局数据转储到磁盘

逆向工程 部件 二元分析 linux C 内存转储
2021-07-10 13:09:40

实验在 Linux 上进行,x86 32-bit.

所以假设在我的汇编程序中,我需要定期(例如每次执行 100000 个基本块后)将一个数组.bss分段从内存转储到磁盘。数组的起始地址和大小是固定的。数组记录了执行的基本块的地址,大小就是16M现在。

我尝试编写一些本机代码,memcpy.bss部分到stack,然后将其写回磁盘。但在我看来,它很乏味,我担心性能和内存消耗,比如说,每次在堆栈上分配一个非常大的内存......

所以这是我的问题,如何以有效的方式从全局数据部分转储内存?我够清楚了吗?

3个回答

如果 IdaPro 可用 - 它也适用于 Linux - 您可以考虑使用 Ida 脚本,如下所示:

    static main()
    {
        DumpMem();
    }


    static DumpMem()
    {
        auto h;
        auto ea;
        auto eaStart = <here your start address>;
        Message("dumping...");
        h = fopen("dumped.bin", "wb");
        for (ea=eaStart; ea<eaStart+0x100000; ea=ea+1)
        {
            fputc(Byte(ea), h);
        }
        fclose(h);
        Message("done!\n");
    }

用法如下:

  1. 将上述代码复制粘贴到扩展名为 .idc 的文件中,将内存地址写入其中。

  2. 在需要转储的地方放置一个断点

  3. 当 BPt 触发时,在 Ida 中,从菜单文件、脚本文件...选择您的 idc 文件。

我不明白为什么您不能直接从堆栈将块转储到磁盘。

从...开始

#include <fcntl.h>

char *block;

int main(void) {
    int fd=open("/tmp/myfile", O_WRONLY|O_APPEND|O_CREAT, 0666);
    write(fd, block, 0x400000);
    close(fd);
}

并继续

gcc -m32 -S b.c

你到达这个 bs 文件

.LC0:
    .string "/tmp/myfile"
    .text
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    $438, 8(%esp)
    movl    $1089, 4(%esp)
    movl    $.LC0, (%esp)           <--- make sure this is the filename string
    call    open
    movl    %eax, 28(%esp)
    movl    block, %eax             <--- and this is the address of your buffer
    movl    $4194304, 8(%esp)
    movl    %eax, 4(%esp)
    movl    28(%esp), %eax
    movl    %eax, (%esp)
    call    write
    movl    28(%esp), %eax
    movl    %eax, (%esp)
    call    close
    leave
    ret

您只需稍作修改即可将其复制到您的仪器中。(您可能希望保存一些寄存器并在退出时恢复它们)。

或者,使用 gcc -static -m32 bc 和 objdump -d 编译程序生成的可执行文件以找出系统调用是如何实现的,然后用直接系统调用替换库调用。这还有一个额外的好处,即如果您的检测修改了一个完整的可执行文件,您就不必弄乱导入列表。

<push registers you want to save>
mov  $438, %edx
mov  $1089, %ecx
mov  filename, %ebx
mov  $0x5, %eax      ;5 is system call # for open
call *0x80d66c4      ;this is the system call address pulled from objdump
mov  %eax, %esi      ;save fd
mov  $0x400000, %edx
mov  block, %ecx     ;your buffer address
mov  %eax, %ebx
mov  $0x4, %eax      ;4 is system call # for write
call *0x80d66c4
mov  %esi, %ebx
mov  $0x6, eax       ;6 is system call # for close
call *0x80d66c4
<pop registers>

这不会进行任何错误检查,但是如果您将文件放在有足够空间的本地硬盘上,您应该没问题。此外,它每次都会附加到文件而不是创建它,因此您必须rm在每次运行之前添加文件中。

使用 gdb,您可以定义一个固定序列并执行它,
一个示例固定序列可以是

stepi {count} 追加内存 {filepath} {start} {end}

通过 gdb 在 cygwin gdb 上运行的示例

$ gdb gdb
GNU gdb (GDB) Cygwin 7.9.1-1

(gdb) define dumpy
Type commands for definition of "dumpy".
End with a line saying just "end".
>si 10000
>append value ../../cygdrive/c/tmp/dumpy.txt $pc
>append memory ../../cygdrive/c/tmp/dumpy.txt $pc $pc+4
>end
(gdb) break *0x401000
Breakpoint 1 at 0x401000
(gdb) r
Starting program: /usr/bin/gdb
[New Thread 3300.0xe20]

Breakpoint 1, 0x00401000 in ?? ()
(gdb) dumpy
[New Thread 3300.0xbc0]
0x610bf987 in auto_protect_for(void*) () from /usr/bin/cygwin1.dll

(gdb) x/x $pc
0x610bf987 <_ZL16auto_protect_forPv+135>:       0x05f6e572      

(gdb) dumpy
0x610f007b in sys_cp_wcstombs(int (*)(_reent*, char*, wchar_t, char const*, _mbstate_t*), char const*, char*, unsigned int, wchar_t const*, unsigned int) ()
   from /usr/bin/cygwin1.dll


(gdb) dumpy
0x610f0028 in sys_cp_wcstombs(int (*)(_reent*, char*, wchar_t, char const*, _mbstate_t*), char const*, char*, unsigned int, wchar_t const*, unsigned int) ()
   from /usr/bin/cygwin1.dll
(gdb)

xxd 不转换字节序,所以我编写了一个 powershell 脚本来转储 dumpy.txt

$file = gc -encoding byte $args[0]
$j=0
while($j-lt $file.length) {
$i=$j;$a="";($i+3)..($i+0)|%{$a+=("{0:x2}" -f $file[$_])};$a+=" ";
$i=$i+4;($i+3)..($i+0)|%{$a+=("{0:x2}" -f $file[$_])};
$a
$j+=8
}

将 dumpy.txt 转储为地址,内容与上面的 powershell 脚本配对,您可以看到它与 gdb 输出匹配

PS C:\tmp> powxxd.ps1 .\dumpy.txt
610bf987 05f6e572
610f007b f6851f74
610f0028 2c24448b