- Putting a function in shared memory - 5 Updates
- Using floating-point math to do integral sqrt - 4 Updates
- Are bitfields useful? - 12 Updates
- Declaring template member of template class a friend - 2 Updates
Frederick Gotham <cauldwell.thomas@gmail.com>: Aug 01 04:23PM -0700 The following function: void cpystring(char *to, char const *from) { while ( *to++ = *from++ ); } gets compiled on a x86_64 Linux machine to: 00000000000005ca <_Z9cpystringPcPKc>: 5ca: 55 push %rbp 5cb: 48 89 e5 mov %rsp,%rbp 5ce: 48 89 7d f8 mov %rdi,-0x8(%rbp) 5d2: 48 89 75 f0 mov %rsi,-0x10(%rbp) 5d6: 48 8b 55 f0 mov -0x10(%rbp),%rdx 5da: 48 8d 42 01 lea 0x1(%rdx),%rax 5de: 48 89 45 f0 mov %rax,-0x10(%rbp) 5e2: 48 8b 45 f8 mov -0x8(%rbp),%rax 5e6: 48 8d 48 01 lea 0x1(%rax),%rcx 5ea: 48 89 4d f8 mov %rcx,-0x8(%rbp) 5ee: 0f b6 12 movzbl (%rdx),%edx 5f1: 88 10 mov %dl,(%rax) 5f3: 0f b6 00 movzbl (%rax),%eax 5f6: 84 c0 test %al,%al 5f8: 0f 95 c0 setne %al 5fb: 84 c0 test %al,%al 5fd: 74 02 je 601 <_Z9cpystringPcPKc+0x37> 5ff: eb d5 jmp 5d6 <_Z9cpystringPcPKc+0xc> 601: 90 nop 602: 5d pop %rbp 603: c3 retq And so here's the first program that allocates the interprocess memory and copies the machine code into it: char unsigned const g_cpystring_bytes[] = { 0x55, 0x48, 0x89, 0xE5, 0x48, 0x89, 0x7D, 0xF8, 0x48, 0x89, 0x75, 0xF0, 0x48, 0x8B, 0x55, 0xF0, 0x48, 0x8D, 0x42, 0x01, 0x48, 0x89, 0x45, 0xF0, 0x48, 0x8B, 0x45, 0xF8, 0x48, 0x8D, 0x48, 0x01, 0x48, 0x89, 0x4D, 0xF8, 0x0F, 0xB6, 0x12, 0x88, 0x10, 0x0F, 0xB6, 0x00, 0x84, 0xC0, 0x0F, 0x95, 0xC0, 0x84, 0xC0, 0x74, 0x02, 0xEB, 0xD5, 0x90, 0x5D, 0xC3 }; #include <cstring> // memcpy #include <boost/interprocess/shared_memory_object.hpp> // shared_memory_object #include <boost/interprocess/mapped_region.hpp> // mapped_region using namespace boost::interprocess; int main(void) { shared_memory_object shm_obj(create_only, //only create "gotham_cpystring", //name read_write ); //read-write mode shm_obj.truncate(sizeof g_cpystring_bytes); mapped_region region(shm_obj, read_write); std::memcpy(region.get_address(), g_cpystring_bytes, region.get_size()); } This first program compiles fine and seems to do its job properly, the shared memory object is created in "/dev/shm" and it's 58 bytes in size. And then next I have the second program that tries to call the function: #include <boost/interprocess/shared_memory_object.hpp> // shared_memory_object #include <boost/interprocess/mapped_region.hpp> // mapped_region using namespace boost::interprocess; char buf1[] = "cat", buf2[] = "dog"; //Not sure if I need to specify the calling convention (e.g. sysv_abi) void (*cpystring)(char *to, char const *from) = nullptr; int main(void) { //Open already created shared memory object. shared_memory_object shm_obj(open_only, "gotham_cpystring", read_write); //Map the whole shared memory in this process mapped_region region(shm_obj, read_write); cpystring = reinterpret_cast<decltype(cpystring)>(region.get_address()); cpystring(buf1, buf2); } This second program fails with a segfault. I thought the problem might be that the shared memory is in a page that isn't marked for execution, and so I set it as executable as follows: void Set_Writeability_Of_Memory(void *const p, bool const writeable) { uintptr_t const page_size = sysconf(_SC_PAGE_SIZE); union { void *p_start_of_page; uintptr_t i_start_of_page; }; p_start_of_page = p; i_start_of_page -= (i_start_of_page % page_size); mprotect(i_start_of_page, page_size, PROT_READ | (writeable ? PROT_WRITE : 0u)); } however this hasn't fixed it. I call the function "Set_Writeability_Of_Memory" from within my first program and also from within my second program, but the second program still segfaults. Anyone got any ideas? Here's what I'm getting from the GDB debugger: Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7ff7000 in ?? () (gdb) bt #0 0x00007ffff7ff7000 in ?? () #1 0x0000555555555366 in main () at drone.cpp:26 |
Frederick Gotham <cauldwell.thomas@gmail.com>: Aug 01 04:44PM -0700 On Sunday, August 2, 2020 at 12:23:39 AM UTC+1, Frederick Gotham wrote: > void Set_Writeability_Of_Memory(void *const p, bool const writeable) I meant to set the page as 'executable' -- not as 'writeable'. I have re-written my second program and everything is working now. Here's the second program: void Set_Executability_Of_Memory(void*, bool); #include <cstring> // memcpy #include <iostream> using std::cout; using std::endl; #include <boost/interprocess/shared_memory_object.hpp> // shared_memory_object #include <boost/interprocess/mapped_region.hpp> // mapped_region using namespace boost::interprocess; char buf1[] = "cat"; char buf2[] = "dog"; void (*cpystring)(char *to, char const *from) = nullptr; int main(void) { //Open already created shared memory object. shared_memory_object shm_obj(open_only, "gotham_cpystring", read_write); //Map the whole shared memory in this process mapped_region region(shm_obj, read_write); cout << "About to set memory page as executable" << endl; Set_Executability_Of_Memory(region.get_address(), true); cout << "About to call cpystring" << endl; cpystring = reinterpret_cast< decltype(cpystring) >( region.get_address() ); cpystring(buf1, buf2); cout << "buf1 : " << buf1 << endl; cout << "buf2 : " << buf2 << endl; } void Set_Executability_Of_Memory(void *const p, bool const executable) { uintptr_t const page_size = sysconf( _SC_PAGE_SIZE ); union { void *p_start_of_page; uintptr_t i_start_of_page; }; p_start_of_page = p; i_start_of_page -= (i_start_of_page % page_size); mprotect(p_start_of_page, page_size, PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0u)); } Of course it will be cleaner to put the call to "Set_Exec..." in the first program. It's late now but tomorrow I'll play around with calling other functions from inside "cpystring". When I have this working the way I want to, I'll be able to unload all libraries and just leave some phantom code in RAM for doing some really strange things (for example I can leave a stub function in memory when I unload a library). |
Manfred <noname@add.invalid>: Aug 02 04:29PM +0200 On 8/1/2020 10:57 PM, Frederick Gotham wrote: > (1) When building, at the linking stage just specify -l:libmonkey.so > (2) At runtime, call dlopen, LoadLibrary followed by dlsym, GetProcAddress > (3) Copy executable code into a shared memory object, and then just get other processes to map the shared memory and call the function What you are trying to do here is replace the functionality of the dynamic linker within your program. None of this is specified by C++ (or C) as a language (i.e. its standard), in fact it is about the platform ABI, out of the scope of the language. C++ (and C) do specify some linkage properties, but they are all high level requirements, nothing anywhere near the level required to execute some actual code. This is to say that you won't get answers to what you are trying to do from the language, as a start you need to study the ABI of your target platform. Second, you should consider the compiled version of your cpystring function as a flat bytearray that happens to contain executable code; you shouldn't rely on how your 4 lines of code get compiled by a C or C++ compiler - for example you can't even rely on the fact that the entry point of the function will be the start address of the compiled bytearray. You may get closer to what you want using assembly or even machine code (and if you use assembly you should inspect closely your assembler to verify what its output is). Third, later on you rely on a blind reinterpret_cast in the hope you'll get the right call. This is not enough, for example on Windows there are at least 3 different calling conventions used by the compiler within a single program for a single architecture. This is why even for conventional library calls their header files thoroughly decorate each function prototype to ensure that the correct ABI convention is enforced by the compiler. And then there's the whole chapter about memory protection. Back to your list above, (1) and (2) should work, if used properly. I would consider (3) unusable for any serious work - toying is always free of course. |
Paavo Helde <eesnimi@osa.pri.ee>: Aug 02 08:09PM +0300 02.08.2020 02:44 Frederick Gotham kirjutas: > Of course it will be cleaner to put the call to "Set_Exec..." in the first program. > It's late now but tomorrow I'll play around with calling other functions from inside "cpystring". > When I have this working the way I want to, I'll be able to unload all libraries and just leave some phantom code in RAM for doing some really strange things (for example I can leave a stub function in memory when I unload a library). Not quite sure about your motivations, so far it seems you have just duplicated a small part of dynamic loader functionality. Is this meant as a stress test for antivirus products, to see how capable they are in detecting suspicious activities? |
Frederick Gotham <cauldwell.thomas@gmail.com>: Aug 02 03:51PM -0700 I've re-written my first program like this: #include <cstring> // memcpy #include <iostream> using std::cout; using std::endl; #include <boost/interprocess/shared_memory_object.hpp> // shared_memory_object #include <boost/interprocess/mapped_region.hpp> // mapped_region void cpystring(char *to, char const *from) __attribute__ ((noinline)); using namespace boost::interprocess; int main(void) { shared_memory_object::remove("gotham_cpystring"); shared_memory_object shm_obj(create_only, //only create "gotham_cpystring", //name read_write); //read-write mode shm_obj.truncate(128u); // Averages about 17 - 58 bytes, so let's be safe with 128 bytes mapped_region region(shm_obj, read_write); std::memcpy(region.get_address(), (void const*)&cpystring, region.get_size()); } void cpystring(char *to, char const *from) { while ( *to++ = *from++ ); } And I've re-written my second program like this: #include <iostream> using std::cout; using std::endl; #include <boost/interprocess/shared_memory_object.hpp> // shared_memory_object #include <boost/interprocess/mapped_region.hpp> // mapped_region using namespace boost::interprocess; char buf1[] = "cat"; char buf2[] = "dog"; void (*cpystring)(char *to, char const *from) = nullptr; int main(void) { //Open already created shared memory object. shared_memory_object shm_obj(open_only, "gotham_cpystring", read_only); //Map the whole shared memory in this process mapped_region region(shm_obj, read_only); cout << "About to set memory page as executable" << endl; extern void Set_Executability_Of_Memory(void *const p, bool const executable); Set_Executability_Of_Memory(region.get_address(), true); cout << "About to call cpystring" << endl; cpystring = reinterpret_cast< decltype(cpystring) >( region.get_address() ); cpystring(buf1, buf2); cout << "buf1 : " << buf1 << endl; cout << "buf2 : " << buf2 << endl; } #ifdef BOOST_INTERPROCESS_WINDOWS extern "C" int VirtualProtect(uint64_t,uint64_t,uint32_t,uint32_t*); struct SYSTEM_INFO { char stuff[4]; uint32_t dwPageSize; char more_stuff[128]; }; extern "C" void GetSystemInfo(uint64_t);
Subscribe to:
Post Comments (Atom)
|
No comments:
Post a Comment