Assembly 汇编中的跳转指令如何处理多个进程?
因此,我对跳转指令在操作系统中如何工作感到困惑。我认为跳转指令设置了处理器程序计数器中的值。但是程序可以在内存中的不同位置运行。我看到在x86中,有<代码> JMP EAX指令,但是我的C++代码似乎没有使用这个代码。我在VC++中编译了一些C++代码:Assembly 汇编中的跳转指令如何处理多个进程?,assembly,operating-system,Assembly,Operating System,因此,我对跳转指令在操作系统中如何工作感到困惑。我认为跳转指令设置了处理器程序计数器中的值。但是程序可以在内存中的不同位置运行。我看到在x86中,有 JMP EAX指令,但是我的C++代码似乎没有使用这个代码。我在VC++中编译了一些C++代码: int main() { int i = 0; while (i < 10) { ++i; if (i == 7) { i += 1;
int main()
{
int i = 0;
while (i < 10)
{
++i;
if (i == 7)
{
i += 1;
continue;
}
}
}
intmain()
{
int i=0;
而(i<10)
{
++一,;
如果(i==7)
{
i+=1;
继续;
}
}
}
这意味着:
int main()
{
00411370 push ebp
00411371 mov ebp,esp
00411373 sub esp,0CCh
00411379 push ebx
0041137A push esi
0041137B push edi
0041137C lea edi,[ebp-0CCh]
00411382 mov ecx,33h
00411387 mov eax,0CCCCCCCCh
0041138C rep stos dword ptr es:[edi]
int i = 0;
0041138E mov dword ptr [i],0
while (i < 10)
00411395 cmp dword ptr [i],0Ah
00411399 jge main+47h (4113B7h)
{
++i;
0041139B mov eax,dword ptr [i]
0041139E add eax,1
004113A1 mov dword ptr [i],eax
if (i == 7)
004113A4 cmp dword ptr [i],7
004113A8 jne main+45h (4113B5h)
{
i += 1;
004113AA mov eax,dword ptr [i]
004113AD add eax,1
004113B0 mov dword ptr [i],eax
continue;
004113B3 jmp main+25h (411395h)
}
}
004113B5 jmp main+25h (411395h)
}
004113B7 xor eax,eax
004113B9 pop edi
004113BA pop esi
004113BB pop ebx
004113BC mov esp,ebp
004113BE pop ebp
004113BF ret
intmain()
{
00411370推式ebp
00411371 mov ebp,esp
00411373子esp,0CCh
00411379推动ebx
0041137A推式esi
0041137B推式电子数据交换
0041137C lea电子数据交换[ebp-0CCh]
00411382 mov ecx,33小时
00411387 mov eax,0CCCH
0041138C代表stos dword ptr es:[edi]
int i=0;
0041138E mov dword ptr[i],0
而(i<10)
00411395 cmp dword ptr[i],0Ah
00411399 jge干管+47小时(4113B7小时)
{
++一,;
0041139B mov eax,dword ptr[i]
0041139E添加eax,1
004113A1 mov dword ptr[i],eax
如果(i==7)
004113A4 cmp dword ptr[i],7
004113A8 jne干管+45小时(4113B5h)
{
i+=1;
004113AA mov eax,dword ptr[i]
004113AD添加eax,1
004113B0 mov dword ptr[i],eax
继续;
004113B3 jmp干管+25小时(411395小时)
}
}
004113B5 jmp干管+25小时(411395小时)
}
004113B7异或eax,eax
004113B9 pop edi
004113BA pop esi
004113BB波普ebx
004113BC mov esp,ebp
004113BE pop ebp
004113BF ret
所以我很困惑,对于命令
jmp411395h
,这是否意味着程序总是加载在内存中的同一位置?因为这似乎不合逻辑。大多数芯片都有相对跳跃(相对于当前位置)和虚拟寻址。内存位置相对于进程main
始终位于内存中与程序开头相对的同一位置。否。在x86(以及其他体系结构)上,大多数跳转指令都是IP相对的:指令的二进制机器码表示与当前指令指针的偏移量。因此,无论代码加载到哪个虚拟地址,跳转指令都能正确运行。不,这里可能有两件事在起作用——您不指定操作系统,因此我将给出一个一般性的答案
首先,可执行文件很少采用最终格式。作为简化,编译将源文件转换为目标文件,链接将目标文件合并为可执行文件
但可执行文件必须加载到内存中,在那个阶段,可以进行更多的修改。这些修改之一可能是修复可执行文件中的内存引用,以指向已在不同位置加载的内存
这可以通过包含自身地址列表的可执行文件来实现,该列表需要在运行时修复
在许多现代操作系统中,虚拟内存和物理内存之间也存在着断开连接
当您的进程启动时,您将获得自己的(我相信是4G for Windows 32位)地址空间,您的进程将加载到其中。该地址空间中的地址与实际物理内存地址关系不大,两者之间的转换由内存管理单元(MMU)完成
事实上,当你的进程被调出或调进时,它可能会在物理地址空间中飞来飞去。但是,虚拟地址不会更改。相对跳转获取当前机器指令的地址(称为指令指针)并添加偏移量以计算要跳转到的地址 如果你看你的代码
004113B3 jmp main+25h (411395h)
004113B5 jmp main+25h (411395h)
004113B7 xor eax,eax
您将注意到jmp指令的长度为2字节(jmp为1字节,偏移量为1字节),并且不可能存储绝对的4字节地址
相对跳转是CPU的基本功能(据我所知是65xx、Z80、8086、68000),与虚拟内存、内存映射或地址空间随机化等高级功能无关。正如其他人所说,有相对跳转和相对调用指令,它们本质上为eip添加了一个固定值,因此不依赖于程序在内存中的位置;编译器喜欢尽可能地使用它们。您可以查看代码字节以查看编译器使用的确切指令。但是,我假设您询问的是跳转/呼叫绝对地址
int main()
{
00411370 push ebp
00411371 mov ebp,esp
00411373 sub esp,0CCh
00411379 push ebx
0041137A push esi
0041137B push edi
0041137C lea edi,[ebp-0CCh]
00411382 mov ecx,33h
00411387 mov eax,0CCCCCCCCh
0041138C rep stos dword ptr es:[edi]
int i = 0,int j=0;
0041138E mov dword ptr [i][j],0
while (i < 10)
00411395 cmp dword ptr [i][j[,0Bh
00411399 jge main+47h (4113B7h)
{
++i;
0041139B mov eax,dword ptr [i][j]
0041139E add eax,1
004113A1 mov dword ptr [i][j],eax '
if (i == 7)
004113A4 cmp dword ptr [i][j],7
004113A8 jne main+45h (4113B5h)
{
i += 1;
004113AA mov eax,ebx,dword ptr [i][j]
004113AD add eax,1
004113B0 mov dword ptr [i][j],ebx
continue;
004113B3 jmp main+25h (411395h)
}
}
004113B5 jmp main+25h (411395h)
}
004113B7 xor eax,ebx
004113B9 pop edi
004113BA pop esi
004113BB pop ecx
004113BC mov esp,ebp
004113BE pop ebp
004113BF ret
当链接器生成一个可执行文件时,它生成假定某个特定文件的绝对地址;Microsoft linker通常使用400000h
。当操作系统加载一个可执行文件或dll时,它通过添加实际加载可执行文件的地址与链接器基于可执行文件的地址之间的差异来“修复”所有绝对地址。除了.com
之外的所有可执行文件格式都指定某种修复表,该表列出了可执行文件中必须以这种方式修补的所有位置。因此,在操作系统将可执行文件加载到基址(例如,1500000h
)的内存中后,您的跳转看起来就像jmp 1511395h
。您可以通过使用调试器查看实际代码字节来检查这一点
较旧的Windows系统倾向于在链接器使用的基址加载可执行文件;这造成了安全风险,因为攻击者会提前知道内存中的内容。这就是为什么较新的系统使用基址随机化。intmain()
int main()
{
00411370 push ebp
00411371 mov ebp,esp
00411373 sub esp,0CCh
00411379 push ebx
0041137A push esi
0041137B push edi
0041137C lea edi,[ebp-0CCh]
00411382 mov ecx,33h
00411387 mov eax,0CCCCCCCCh
0041138C rep stos dword ptr es:[edi]
int i = 0,int j=0;
0041138E mov dword ptr [i][j],0
while (i < 10)
00411395 cmp dword ptr [i][j[,0Bh
00411399 jge main+47h (4113B7h)
{
++i;
0041139B mov eax,dword ptr [i][j]
0041139E add eax,1
004113A1 mov dword ptr [i][j],eax '
if (i == 7)
004113A4 cmp dword ptr [i][j],7
004113A8 jne main+45h (4113B5h)
{
i += 1;
004113AA mov eax,ebx,dword ptr [i][j]
004113AD add eax,1
004113B0 mov dword ptr [i][j],ebx
continue;
004113B3 jmp main+25h (411395h)
}
}
004113B5 jmp main+25h (411395h)
}
004113B7 xor eax,ebx
004113B9 pop edi
004113BA pop esi
004113BB pop ecx
004113BC mov esp,ebp
004113BE pop ebp
004113BF ret
{
00411370推式ebp
00411371 mov ebp,esp
00411373子esp,0CCh
004113