【问题标题】:Shellcode in ARM crashes when called by function which is not 'main()'ARM中的Shellcode在被非'main()'函数调用时崩溃
【发布时间】:2023-04-03 02:09:01
【问题描述】:

当我在 main 函数中执行 shellcode 时,它​​工作正常。但是,当我通过 main 调用的其他函数执行它时,会导致分段错误。据我所知,函数调用应该影响堆栈,而shellcode应该在堆中。我的代码有问题吗?

shellcode由matesploit生成,我使用qemu-arm运行程序。

代码是:

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

// msfvenom -p linux/armle/exec CMD=/bin/pwd -f c
unsigned char buf[] = 
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0a\x30\x01\x90\x01"
"\xa9\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6e\x2f\x70\x77\x64";

void runShellCode(){
    unsigned char *pShellCode = (unsigned char *)calloc(1, 4096);
    memcpy(pShellCode, buf, sizeof(buf));
    (*(void(*)()) pShellCode)();
}

int main(int argc, char *argv[])
{
    // uncomment these lines it will work perfectly fine
    // unsigned char *pShellCode = (unsigned char *)calloc(1, 4096);
    // memcpy(pShellCode, buf, sizeof(buf));
    // (*(void(*)()) pShellCode)();

    runShellCode();
    return 0;
}

编译和运行的cmd:

arm-linux-gnueabi-gcc test.c -o test_arm -static
qemu-arm test_arm

shellcode的反汇编代码(不正确所以删除)


使用 mmap() 方式更新代码。但是,如果 main() 的参数是 void,它工作正常。虽然参数是int argc, char *argv[],但会导致SEGV。

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <sys/mman.h>

// msfvenom -p linux/armle/exec CMD=/bin/pwd -f c
unsigned char buf[] = 
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0a\x30\x01\x90\x01"
"\xa9\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6e\x2f\x70\x77\x64";

int main(int argc, char *argv[])
//int main(void)
{
    unsigned char *pShellCode = (unsigned char *)calloc(1, 4096);
    memcpy(pShellCode, buf, sizeof(buf));
    
    void (*sc) () = NULL;
    sc = mmap (0, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    memcpy (sc, pShellCode, 4096);
    __builtin___clear_cache (sc, sc + sizeof(sc));
    sc();

    return 0;
}

【问题讨论】:

  • 如果您能向我们提供您的 buf[] 数组的反汇编列表,并解释它试图做什么,将会很有帮助。
  • shellcode 正在尝试运行 cmd '/bin/pwd',正如评论所说,我更新了反汇编列表。
  • 那个反汇编列表在我看来像是胡言乱语。

标签: c arm shellcode


【解决方案1】:

发布的代码导致:

untitled1.c:14:7: warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]

Linux/Unix 特定:

You need a memory page with `write execute` permissions. 

See `mmap(2)` and `mprotect(2)`. 

Windows 特定:

`VirtualAlloc()` function to reserve memory 

`VirtualProtect()` function to mark as executable
applying, for instance, the `PAGE_EXECUTE_READ` flag.

you can cast the pointer to the allocated memory
to an appropriate function pointer type 
and just call the function. 

`VirtualFree()` function to clean up afterwards

关于您的编辑部分:

修改后的代码在通过编译器运行时会产生:

gcc -ggdb3 -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled1.c" -o "untitled1.o" (in directory: /home/richard/Documents/forum)
untitled1.c: In function ‘main’:
untitled1.c:18:8: warning: ISO C forbids assignment between function pointer and ‘void *’ [-Wpedantic]
     sc = mmap (0, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
        ^
untitled1.c:19:13: warning: ISO C forbids passing argument 1 of ‘memcpy’ between function pointer and ‘void *’ [-Wpedantic]
     memcpy (sc, pShellCode, 4096);
             ^~
In file included from /usr/include/memory.h:29:0,
                 from untitled1.c:3:
/usr/include/string.h:42:14: note: expected ‘void * restrict’ but argument is of type ‘void (*)()’
 extern void *memcpy (void *__restrict __dest, const void *__restrict __src,
              ^~~~~~
untitled1.c:20:37: warning: pointer to a function used in arithmetic [-Wpointer-arith]
     __builtin___clear_cache (sc, sc + sizeof(sc));
                                     ^
untitled1.c:20:30: warning: ISO C forbids passing argument 1 of ‘__builtin___clear_cache’ between function pointer and ‘void *’ [-Wpedantic]
     __builtin___clear_cache (sc, sc + sizeof(sc));
                              ^~
untitled1.c:20:30: note: expected ‘void *’ but argument is of type ‘void (*)()’
untitled1.c:20:34: warning: ISO C forbids passing argument 2 of ‘__builtin___clear_cache’ between function pointer and ‘void *’ [-Wpedantic]
     __builtin___clear_cache (sc, sc + sizeof(sc));
                                  ^~
untitled1.c:20:34: note: expected ‘void *’ but argument is of type ‘void (*)()’
untitled1.c:11:14: warning: unused parameter ‘argc’ [-Wunused-parameter]
 int main(int argc, char *argv[])
              ^~~~
untitled1.c:11:26: warning: unused parameter ‘argv’ [-Wunused-parameter]
 int main(int argc, char *argv[])
                          ^~~~
Compilation finished successfully.

即使编译器输出:编译成功这并不意味着生成的代码是正确的。这意味着编译器为每个“警告”替换了一些“解决方法”。这并不意味着结果就是你想要的。

请阅读executable data 了解如何执行数据。

【讨论】:

  • 你知道为什么从main() 调用它会起作用吗?我希望calloc 分配的内存权限是相同的,无论从哪里调用它。
  • 我怀疑“在 main() 中工作”只是一个巧合。我在回答中给出了正确的方法。
  • 是的,但是如果堆通常是不可执行的,它应该根本不可能工作,巧合与否。
  • 感谢您的回复!我用mmap()更新了代码,但还是不能执行。即使在main() 中,除非参数是void,否则它也不起作用。
  • 会不会是qemu的问题?请问这两个问题是否可以在您的环境中重现?谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-29
  • 2016-12-03
  • 2021-11-27
  • 1970-01-01
相关资源
最近更新 更多