Linux:通过 ptrace() 执行系统调用
嘿 :) 我目前正在为 x86/x64 Linux 开发一个内存黑客库。 我挣扎的重点是实现某种远程系统调用执行。
这是我的代码,当我尝试执行有效的系统调用时,它只会使其他进程崩溃。
(我在代码中使用的所有函数都是 ptrace 的包装器) 您可以在此处找到完整的代码:http://code.google.com/p/ ethonmem/source/browse/
long Debugger::executeSyscall(
unsigned long code, std::vector<unsigned long> const& args) const
{
// Backup registers.
Registers buRegs = getRegisters(buRegs);
FpuRegisters buFregs = getFpuRegisters(buFregs);
// Get register set to modify.
Registers regs = buRegs;
#if __WORDSIZE == 32
// EAX stores the syscall code.
regs.eax = code;
// If less than 7 args exist, they are stored in registers.
size_t argCount = args.size();
if(argCount < 7)
{
while(argCount)
{
switch(argCount)
{
case 1:
regs.ebx = args[0];
break;
case 2:
regs.ecx = args[1];
break;
case 3:
regs.edx = args[2];
break;
case 4:
regs.esi = args[3];
break;
case 5:
regs.edi = args[4];
break;
case 6:
regs.ebp = args[5];
break;
}
--argCount;
}
}
// Otherwise we have to use memory.
else
{
// Get stack space.
regs.esp -= argCount * sizeof(unsigned long);
// Write arguments to stack.
for(size_t i = 0; i < argCount; ++i)
writeWord(regs.esp + i * sizeof(unsigned long), args[i]);
// EBX stores the address.
regs.ebx = regs.esp;
}
// Write INT 0x80-instruction to current instruction pointer position.
unsigned long const oldInstruction = readWord(regs.eip);
uint8_t newInstruction[sizeof(long)] = { 0xCD, 0x80, 0xCC, 0xCC };
writeWord(regs.eip, *reinterpret_cast<unsigned long*>(&newInstruction[0]));
#elif __WORDSIZE == 64
// RAX stores the syscall code.
regs.rax = code;
// If less than 7 args exist, they are stored in registers.
size_t argCount = args.size();
if(argCount < 7)
{
while(argCount)
{
switch(argCount)
{
case 1:
regs.rdi = args[0];
break;
case 2:
regs.rsi = args[1];
break;
case 3:
regs.rdx = args[2];
break;
case 4:
regs.r10 = args[3]; // Or RCX ???
break;
case 5:
regs.r8 = args[4];
break;
case 6:
regs.r9 = args[5];
break;
}
--argCount;
}
}
// Otherwise this fails.
else
{
BOOST_THROW_EXCEPTION(EthonError() <<
ErrorString("More than 6 arguments passed to a 64bit syscall"));
}
// Write SYSCALL-instruction to current instruction pointer position.
unsigned long const oldInstruction = readWord(regs.rip);
uint8_t newInstruction[sizeof(long)] =
{ 0x0F, 0x05, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC };
writeWord(regs.rip, *reinterpret_cast<unsigned long*>(&newInstruction[0]));
#endif
// Apply new registers.
setRegisters(regs);
// Step to begin of syscall.
stepSyscall();
// Step to end of syscall.
stepSyscall();
// Fetch return value and restore patched word
getRegisters(regs);
long returnValue;
#if __WORDSIZE == 32
returnValue = regs.eax;
writeWord(regs.eip, oldInstruction);
#elif __WORDSIZE == 64
returnValue = regs.rax;
writeWord(regs.rip, oldInstruction);
#endif
// Restore registers.
setRegisters(buRegs);
setFpuRegisters(buFregs);
return returnValue;
}
有人能够检测到我的错误吗?谢谢 :) 问候, 弗洛里安
Hey :)
I am currently developing a memoryhacking-library for x86/x64 Linux.
The point I struggle is to implement some kind of remote syscall execution.
Here is my code which just crashes the other process when I attempt to execute a valid syscall.
(All of my functions used within the code are wrappers around ptrace)
You can find the complete code here: http://code.google.com/p/ethonmem/source/browse/
long Debugger::executeSyscall(
unsigned long code, std::vector<unsigned long> const& args) const
{
// Backup registers.
Registers buRegs = getRegisters(buRegs);
FpuRegisters buFregs = getFpuRegisters(buFregs);
// Get register set to modify.
Registers regs = buRegs;
#if __WORDSIZE == 32
// EAX stores the syscall code.
regs.eax = code;
// If less than 7 args exist, they are stored in registers.
size_t argCount = args.size();
if(argCount < 7)
{
while(argCount)
{
switch(argCount)
{
case 1:
regs.ebx = args[0];
break;
case 2:
regs.ecx = args[1];
break;
case 3:
regs.edx = args[2];
break;
case 4:
regs.esi = args[3];
break;
case 5:
regs.edi = args[4];
break;
case 6:
regs.ebp = args[5];
break;
}
--argCount;
}
}
// Otherwise we have to use memory.
else
{
// Get stack space.
regs.esp -= argCount * sizeof(unsigned long);
// Write arguments to stack.
for(size_t i = 0; i < argCount; ++i)
writeWord(regs.esp + i * sizeof(unsigned long), args[i]);
// EBX stores the address.
regs.ebx = regs.esp;
}
// Write INT 0x80-instruction to current instruction pointer position.
unsigned long const oldInstruction = readWord(regs.eip);
uint8_t newInstruction[sizeof(long)] = { 0xCD, 0x80, 0xCC, 0xCC };
writeWord(regs.eip, *reinterpret_cast<unsigned long*>(&newInstruction[0]));
#elif __WORDSIZE == 64
// RAX stores the syscall code.
regs.rax = code;
// If less than 7 args exist, they are stored in registers.
size_t argCount = args.size();
if(argCount < 7)
{
while(argCount)
{
switch(argCount)
{
case 1:
regs.rdi = args[0];
break;
case 2:
regs.rsi = args[1];
break;
case 3:
regs.rdx = args[2];
break;
case 4:
regs.r10 = args[3]; // Or RCX ???
break;
case 5:
regs.r8 = args[4];
break;
case 6:
regs.r9 = args[5];
break;
}
--argCount;
}
}
// Otherwise this fails.
else
{
BOOST_THROW_EXCEPTION(EthonError() <<
ErrorString("More than 6 arguments passed to a 64bit syscall"));
}
// Write SYSCALL-instruction to current instruction pointer position.
unsigned long const oldInstruction = readWord(regs.rip);
uint8_t newInstruction[sizeof(long)] =
{ 0x0F, 0x05, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC };
writeWord(regs.rip, *reinterpret_cast<unsigned long*>(&newInstruction[0]));
#endif
// Apply new registers.
setRegisters(regs);
// Step to begin of syscall.
stepSyscall();
// Step to end of syscall.
stepSyscall();
// Fetch return value and restore patched word
getRegisters(regs);
long returnValue;
#if __WORDSIZE == 32
returnValue = regs.eax;
writeWord(regs.eip, oldInstruction);
#elif __WORDSIZE == 64
returnValue = regs.rax;
writeWord(regs.rip, oldInstruction);
#endif
// Restore registers.
setRegisters(buRegs);
setFpuRegisters(buFregs);
return returnValue;
}
Is anyone able to detect my error? Thank you :)
Regards,
Florian
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您知道系统调用发生时 %rip 是否已递增到下一条指令吗?通常在 e8 或 e9(call/jmp)之后,并且可能在 0f05 系统调用之后,%rip 指向调用后的地址,而不是直接指向它。使用 %rip - 2 可能会修复它。
Do you know whether %rip has been incremented to the next instruction when the syscall happens? Normally after an e8 or e9 (call/jmp), and possibly after a 0f05 syscall, %rip points to the address after the call, instead of to it directly. Using %rip - 2 might fix it.
只是一个疯狂的猜测,您是否确保作为系统调用参数传递的任何内存地址都是有效的?
Just a wild guess, do you make sure any memory adresses passed as argument with syscall are valid?