嗨,我正在尝试将原始机器代码加载到内存中并在C程序中运行它,现在当程序执行时,它试图在内存上运行mprotect使其可执行时中断.我也不完全确定如果内存设置正确,它将执行.我目前在Ubuntu Linux x86上运行它(也许问题是Ubuntu的过度保护?)
我目前拥有以下内容:
#include <memory.h>
#include <sys/mman.h>
#include <stdio.h>
int main ( int argc, char **argv )
{
FILE *fp;
int sz = 0;
char *membuf;
int output = 0;
fp = fopen(argv[1],"rb");
if(fp == NULL)
{
printf("Failed to open file, aborting!\n");
exit(1);
}
fseek(fp, 0L, SEEK_END);
sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
membuf = (char *)malloc(sz*sizeof(char));
if(membuf == NULL)
{
printf("Failed to allocate memory, aborting!\n");
exit(1);
}
memset(membuf, 0x90, sz*sizeof(char));
if( mprotect(membuf, sz*sizeof(char), PROT_EXEC | PROT_READ | PROT_WRITE) …Run Code Online (Sandbox Code Playgroud) 我正在查看汇编中的不同指令,我对如何决定不同操作数和操作码的长度感到困惑.
这是你应该从经验中得知的东西,还是有办法找出哪个操作数/运算符组合占用了多少字节?
例如:
push %ebp ; takes up one byte
mov %esp, %ebp ; takes up two bytes
Run Code Online (Sandbox Code Playgroud)
所以问题是:
在看到给定的指令后,如何推断出其操作码需要多少字节?
我已经调试REP STOS DWORD PTR ES:[EDI]了一段时间了
从我的结论它总是使用
ECX作为反击.
EAX作为将被复制的值EDI然后附加ECX时间,因此在放入指向的转储后EDI
它似乎在EDI上覆盖指向的数据,看起来它总是只使用ECX作为计数器,同时将EDI改变4个字节.当计数器击中0时它停止工作
所以我提出了这种代码
while(regs.d.ecx != 0)
{
*(unsigned int *)(regs.d.edi) = regs.d.eax;
regs.d.edi += 4;
regs.d.ecx--;
}
Run Code Online (Sandbox Code Playgroud)
似乎工作..但我很担心,因为我只是运气和猜测工作.它结实吗?就像数据一样,它总是ECX作为计数器,EAX它总是复制4个字节永远不会少?
运行程序时,您可以传递参数,例如
$ myProgram par1 par2 par3
Run Code Online (Sandbox Code Playgroud)
在C中你可以通过查看来访问这些参数argv,
int main (int argc, char *argv[])
{
char* aParameter = argv[1]; // Not sure if this is 100% right but you get the idea...
}
Run Code Online (Sandbox Code Playgroud)
这将如何在assembly/x86机器代码中转换?你会如何访问给你的变量?系统如何为您提供这些变量?
我对组装非常新,它接缝只能访问寄存器和绝对地址.我很困惑你如何访问参数.系统是否会将参数预加载到特殊寄存器中?
考虑一下这个x64 NASM语法程序集:
inc qword [rax]
inc qword [rcx]
inc qword [rdx]
inc qword [rbx]
inc qword [rsp]
inc qword [rbp]
inc qword [rsi]
inc qword [rdi]
Run Code Online (Sandbox Code Playgroud)
与nasm组装(并与gnu ld链接)后,objdump -d报告以下内容:
4000b0: 48 ff 00 incq (%rax)
4000b3: 48 ff 01 incq (%rcx)
4000b6: 48 ff 02 incq (%rdx)
4000b9: 48 ff 03 incq (%rbx)
4000bc: 48 ff 04 24 incq (%rsp)
4000c0: 48 ff 45 00 incq 0x0(%rbp)
4000c4: 48 ff 06 incq (%rsi)
4000c7: 48 ff 07 incq …Run Code Online (Sandbox Code Playgroud) 我在C++中有以下代码:
#include <iostream>
int main(int argc, const char * argv[])
{
goto line2;
line1:
std::cout << "line 1";
goto line3;
line2:
std::cout << "line 2";
goto line1;
line3:
std::cout << "line 3";
goto line4;
line4:
std::cout << "Hello, World!\n";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果我做了一个更大的程序,让我说10,000行代码,我决定我永远不会使用我自己编写的函数,我只使用goto语句.我只使用全局变量.就最佳实践而言,我有点疯狂,但它的用途非常特殊.问题是,使用goto语句跳转是否有效?如果我有1000个转到标签怎么办?
goto语句是否直接转换为机器代码,它告诉计算机JUMP到另一个内存地址?与调用函数的成本相比,机器中的这种成本是否更低?
我想知道,因为我想编写一个非常有效的程序来进行一些计算,我需要非常高效,而不需要求助于汇编/机器代码.
无需告诉我这在维护,代码的可理解性,最佳实践方面都是一个坏主意,我非常清楚这一点,我只想回答这个问题.我不想在它是否善于使用函数调用或使用goto之间进行任何争论.
为了澄清这个问题,我担心在这种情况下只使用带有10,000行程序的gotos,它将如何与使用函数的传统程序进行比较.有两种方法可以比较和对比这两个程序,例如CPU缓存的执行方式.没有函数调用它会给出什么样的保存.如果没有调用堆栈,这将如何影响CPU缓存,因为CPU缓存通常会使堆栈保持关闭状态.是否会出现由于未正确使用缓存而导致性能损失的情况.与时间效率方面的跳跃相比,调用函数的实际成本是多少.在效率方面,有很多方法可以比较和对比两种编程风格.
我正在查看一些汇编代码和相应的内存转储,我无法理解正在发生的事情.我正在使用它作为x86操作码的参考,这是x86中寄存器的参考.我遇到了这些命令,我意识到我仍然错过了一大块难题.
8B 45 F8 - mov eax,[ebp-08]
8B 80 78040000 - mov eax,[eax+00000478]
8B 00 - mov eax,[eax]
Run Code Online (Sandbox Code Playgroud)
基本上我不明白操作码之后的两个字节是什么意思,我找不到任何能为命令提供逐位格式的地方(如果有人能指出一个我会非常感激).
CPU如何知道每个命令有多长?
根据我的参考,这个8B mov命令允许使用32b或16b寄存器,这意味着有16个可能的寄存器(AX,CX,DX,BX,SP,BP,SI,DI及其扩展等价物).这意味着您需要一个完整的字节来指定在每个操作数中使用哪个寄存器.
到目前为止仍然很好,操作码后面的两个字节可以指定使用哪些寄存器.然后我注意到这些命令在内存中逐字节堆叠,并且所有这三个命令使用不同的字节数来指定解除引用第二个操作数时要使用的偏移量.
我想你可以限制寄存器只能使用带有16b的16b和带32b的32b,但这只能释放一个位,不足以告诉CPU有多少字节的偏移量.
哪些值对应哪些寄存器?
困扰我的第二件事是,尽管我的引用明确地给寄存器编号,但是在这些命令中操作码之后没有看到与字节的任何相关性.即使是他们自己,这些命令似乎也不一致.第二个和第三个命令都是从eax到eax,但在第一个字节中间有一点不同.
根据我的参考,我假设0是EAX,1是ECX,2是EDX,依此类推.但是,这并不能让我深入了解如何在RAX,EAX,AX,AL和AH之间进行指定.一些命令似乎只接受8b寄存器,而其他命令接受16b或32b,而在x86_64上,一些命令似乎接受16b,32b或64b寄存器.所以你会做一些类似0-7的事情是R,8-15 E,16-23非延伸,24-31 H和L?即使它是这样的,似乎应该更容易找到手册或指定的东西.
我猜想Wasm二进制文件通常是JIT编译为本机代码,但是如果有Wasm源,是否有工具可以查看实际生成的x86-64机器代码?或以不同的方式询问,是否存在使用Wasm并输出本机代码的工具?
GAS对以下说明进行了以下编码:
push rbp # 0x55
push rbx # 0x53
push r12 # 0x41 0x54
push r13 # 0x41 0x55
Run Code Online (Sandbox Code Playgroud)
从AMD64规范(页313):
PUSH reg64 50 +rq将64位寄存器的上下文压入堆栈.
由于用于偏移rbp和rbx5个和3个,分别为第一两种编码有意义.但是我不明白最后两个编码是怎么回事.
据我所知,0x40-0x4f是一个REX前缀和0x41具有REX.B位集(其是一个扩展的MSB MODRM.rm或SIB.base,根据该外部参考).规范提到要访问所有16个GPR,你需要使用REX,但目前还不清楚截止点在哪里.
通过查阅MODRM和SIB的文档,我不认为使用了SIB,因为它的目的是使用base + offset寄存器进行索引(虽然说实话,我不能真正告诉你如何区分MODRM和SIB只是给出了编码).
所以,我怀疑这里使用的是MODRM.考虑到当前的push r12(0x41 0x54)(注意到r12有偏移12),我们有:
+----------------+--------------------+
| 0x41 | 0x54 |
+----------------+--------------------+
| REX | MODRM |
+--------+-------+-----+--------+-----+
| Prefix | WRXB | mod | reg …Run Code Online (Sandbox Code Playgroud) 我正在x86汇编(使用NASM)中进行一项练习,该练习的利基要求是将每条指令限制为最多3个字节。
我想调用标签,但是执行此操作的正常方法(如代码示例所示)总是导致指令大小为5个字节。我试图找出是否有一系列指令(每个指令不超过3个字节)可以完成此操作。
我试图将标签地址加载到一个寄存器中,然后调用该寄存器,但是似乎该地址随后被解释为绝对地址,而不是相对地址。
我环顾四周,看看是否有一种方法可以强制调用以将寄存器中的地址解释为相对地址,但找不到任何内容。我曾考虑过通过将返回地址推入堆栈并使用来模拟呼叫jmp rel8,但不确定如何获取要返回的位置的绝对地址。
这是做我想要的正常方法:
[BITS 32]
call func ; this results in a 5-byte call rel32 instruction
; series of instructions here that I would like to return to
func:
; some operations here
ret
Run Code Online (Sandbox Code Playgroud)
我已经尝试过像这样的事情:
[BITS 32]
mov eax, func ; 5-byte mov r32, imm32
call eax ; 2-byte call r32
; this fails, seems to interpret func's relative address as an absolute
... ; series of instructions here …Run Code Online (Sandbox Code Playgroud) machine-code ×10
assembly ×8
x86 ×5
c ×3
nasm ×2
x86-64 ×2
64-bit ×1
c++ ×1
code-size ×1
compilation ×1
executable ×1
goto ×1
intel ×1
memory ×1
mprotect ×1
webassembly ×1