[英]How does the jump instruction in assembly work with multiple processes?
所以,我對跳轉指令在操作系統中的工作原理感到困惑。 我認為跳轉指令設置處理器程序計數器中的值。 但程序可以在內存中的不同位置運行。 我在x86中看到,有JMP EAX
指令,但我的C ++代碼似乎沒有使用它。 我在VC ++中編譯了一些C ++代碼:
int main()
{
int i = 0;
while (i < 10)
{
++i;
if (i == 7)
{
i += 1;
continue;
}
}
}
這意味着:
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
所以我很困惑,對於命令jmp 411395h
,這是否意味着程序總是被加載到內存中的相同位置? 因為那似乎不合邏輯。
不,這里有兩件事情可以發揮 - 你沒有指定操作系統所以我會給出一般答案。
首先,可執行文件很少采用最終格式。 作為簡化,編譯將源轉換為目標文件,並且鏈接將對象文件組合成可執行文件。
但是必須將可執行文件加載到內存中,在那個階段,可以進行更多修改。 這些修改之一可以是修復可執行文件中的內存引用以指向已在不同位置加載的內存。
這可以通過包含本身內部地址列表的可執行文件來實現,該列表需要在運行時修復。
在許多現代操作系統中,虛擬內存和物理內存之間也存在脫節。
當您的流程開始時,您將獲得自己的(用於Windows 32位的4G,我相信)加載進程的地址空間。 此地址空間中的地址與您的實際物理內存地址幾乎沒有關系,兩者之間的轉換由內存管理單元(MMU)完成。
實際上,您的進程可能會遍布物理地址空間,因為它已被分頁並進入。但虛擬地址不會改變。
正如其他人所寫的那樣,有相對跳轉和相對調用指令基本上為eip
添加一個固定值,因此不依賴於程序在內存中的位置; 編譯器喜歡盡可能使用這些。 您可以查看代碼字節以查看編譯器使用的確切指令。 但是,我假設你在詢問跳轉/調用絕對地址。
當鏈接器生成可執行文件時,它會生成假定特定基址的絕對地址 ; Microsoft鏈接器通常使用400000h
。 當OS加載可執行文件或dll時,它通過添加實際加載可執行文件的地址與鏈接器基於它的地址之間的差異來“修復”所有絕對地址。 除.com
之外的所有可執行格式都指定了某種類型的修正表,其中列出了必須以這種方式修補的可執行文件中的所有位置。 因此,在OS將您的可執行文件加載到基本地址(例如1500000h
,您的跳轉將看起來像jmp 1511395h
。 您可以通過使用調試器查看實際代碼字節來檢查這一點。
較舊的Windows系統傾向於在鏈接器使用的基址處加載可執行文件; 這造成了安全風險,因為攻擊者會提前知道內存中的內容。 這就是為什么較新的系統使用基地址隨機化的原因。
內存位置與進程有關。 相對於程序的開頭, main
始終位於內存中的相同位置。
在x86(以及其他體系結構)上,大多數跳轉指令都是IP相關的 :指令的二進制機器代碼表示與當前指令指針的偏移量。 因此,無論代碼加載到哪個虛擬地址,跳轉指令都能正常工作。
相對跳轉采用當前機器指令的地址(稱為指令指針)並添加偏移量來計算要跳轉到的地址。
如果你看看你的代碼
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),與虛擬內存,內存映射或地址空間隨機化等高級功能無關。
大多數芯片具有相對跳躍(相對於當前位置)和虛擬尋址。
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.