【計算機系統結構】near return instructions 近返回 。Far return instructions 遠程返回指令

Opcode Mnemonic Description
C3 RET Near return to calling procedure.
CB RET Far return to calling procedure.
C2 iw RET imm16 Near return to calling procedure and pop imm16 bytes from stack.
CA iw RET imm16 Far return to calling procedure and pop imm16 bytes from stack.

將程序控制轉移到位於堆棧頂部的返回地址。 地址通常由CALL指令放在堆棧上,並返回到CALL指令後面的指令。

可選的源操作數指定彈出返回地址後要釋放的堆棧字節數; 默認值爲none。 此操作數可用於從堆棧中釋放傳遞給被調用過程且不再需要的參數。 當用於切換到新過程的CALL指令使用具有非零字數的調用門來訪問新過程時,必須使用它。 這裏,RET指令的源操作數必須指定與調用門的字計數字段中指定的字節數相同的字節數。

The RET instruction can be used to execute three different types of returns:

Near return
A return to a calling procedure within the current code segment (the segment currently pointed to by the CS register), sometimes referred to as an intrasegment return.
Far return
A return to a calling procedure located in a different segment than the current code segment, sometimes referred to as an intersegment return.
Inter-privilege-level far return
A far return to a different privilege level than that of the currently executing program or procedure.

當執行近返回時,處理器將返回指令指針(偏移)從堆棧頂部彈出到EIP寄存器,並在新指令指針處開始執行程序。 CS寄存器保持不變。

執行遠程返回時,處理器將返回指令指針從堆棧頂部彈出到EIP寄存器,然後將段頂選擇器從堆棧頂部彈出到CS寄存器中。然後,處理器在新指令指針的新代碼段中開始程序執行。

除了處理器檢查代碼的特權級別和訪問權限以及返回的堆棧段以確定是否允許進行控制轉移之外,特權級別遠程返回的機制類似於段間返回。如果DS,ES,FS和GS段寄存器在特權級別返回期間被清除,則它們將引用不允許在新特權級別訪問的段。由於堆棧切換也發生在特權級別返回上,因此ESP和SS寄存器從堆棧加載。

如果在特權級別調用期間將參數傳遞給被調用過程,則必須將可選的源操作數與RET指令一起使用以釋放返回的參數。

這裏,參數從被調用過程的堆棧和調用過程的堆棧(即返回的堆棧)中釋放。

switch(Instruction) {
	case NearReturn:
		if(OperandSize == 32 && !IsWithinStackLimits(TopStackBytes(12))) Exception(SS(0)); //top 12 bytes of stack not within stack limits
		//OperandSize == 16
		else if(!IsWithinStackLimits(TopStackBytes(6)) Exception(SS(0)); //IF top 6 bytes of stack not within stack limits
		TemporaryEIP = Pop();
		TemporaryEIP = TemporaryEIP & 0xFFFF;
		if(!IsWithinCodeSegmentLimits(TemporaryEIP)) Exception(GP(0));
		EIP = TemporaryEIP;
		if(HasImmediateOperand()) { //instruction has immediate operand
			if(StackAddressSize == 32) ESP = ESP + Source; //release parameters from stack
			//StackAddressSize == 16
			else SP = SP + Source; //release parameters from stack
		}
		break;
	case FarReturn:
		//Real-address mode or virtual-8086 mode
		if(PE == 0 || (PE == 1 && VM == 1)) {
			if(OperandSize == 32) {
				if(!IsWithinStackLimits(TopStackBytes(12)) Exception(SS(0)); //top 12 bytes of stack not within stack limits
				EIP = Pop();
				CS = Pop(); //32-bit pop, high-order 16 bits discarded
			}
			else { //OperandSize == 16
				if(!IsWithinStackLimits(TopStackBytes(6)) Exception(SS(0)); //top 6 bytes of stack not within stack limits
				TemporaryEIP = Pop();
				TemporaryEIP = TemporaryEIP & 0xFFFF;
				if(!IsWithinCodeSegmentLimits(TemporaryEIP)) Exception(GP(0));
				EIP = TemporaryEIP;
				CS = Pop(); //16-bit pop
			}
			if(HasImmediateOperand()) else SP = SP + Source; //instruction has immediate operand; release parameters from stack
		}
		//Protected mode, not virtual-8086 mode
		else if(PE == 1 && VM == 0) {
			if(OperandSize == 32 && !IsWithinStackLimits(OffsetStackBytes(4, 4)) Exception(SS(0)); //second doubleword on stack is not within stack limits
			//OperandSize == 16
			else if(!IsWithinStackLimits(OffsetStackBytes(2, 2))) Exception(SS(0)); //second word on stack is not within stack limits
			if(ReturnCode.SegmentSelector == 0) Exception(GP(Selector));
			if(!IsWithinDescriptorTableLimits(ReturnCode.SegmentSelector)) Exception(GP(Selector));
			ReturnCode.SegmentDescriptor = ObtainSegmentDescriptor(); //Obtain descriptor to which return code segment selector points from descriptor table
			if(!IsCodeSegment(ReturnCode.SegmentDescriptor)) Exception(GP(Selector));
			if(ReturnCode.SegmentSelector.RPL < CPL) Exception(GP(Selector));
			if(IsConforming(ReturnCode.SegmentDescriptor && ReturnCode.Segment.DPL > ReturnCode.SegmentSelector.RPL) Exception(GP(Selector));
			if(!IsPresent(ReturnCode.SegmentDescriptor)) Exception(NP(Selector));
			if(ReturnCode.SegmentSelector.RPL > CPL) {
				//Return outer privilege level
				if(OperandSize == 32 && !IsWithinStackLimits(TopStackBytes(16 + Source)) Exception(SS(0)); //top 16 + Source bytes of stack not within stack limits
				//OperandSize == 16
				else if(!IsWithinStackLimits(TopStackBytes(8 + Source)) Exception(SS(0)); //top 8 + Source bytes of stack not within stack limits
				ReturnSegmentSelector = ReadReturnSegmentSelector();
				if(StackSegmentSelector == 0) Exception(GP(0));
				if(!IsWithinDescriptorTableLimits(ReturnStack.SegmentSelector.Index)) Exception(GP(Selector));
				if(StackSegmentSelector.RPL != ReturnCode.SegmentSelector.RPL || !IsWritableDataSegment(StackSegment) || StackSegmentDescriptor.DPL != ReturnCode.SegmentSelector.RPL) Exception(GP(Selector));
				if(!IsPresent(StackSegment)) Exception(SS(StackSegmentSelector));
				if(!IsWithinLimits(ReturnCode.SegmentLimit, ReturnInstructionPointer)) Exception(GP(0));
				CPL = ReturnCode.SegmentSelector.RPL;
				if(OperandSize == 32) {
					EIP = Pop();
					CS = Pop(); //32-bit pop, high-order 16 bits discarded; segment descriptor information also loaded
					CS.RPL = CPL;
					ESP = ESP + Source; //release parameters from called procedure's stack
					TemporaryESP = Pop();
					TemporarySS = Pop(); //32-bit pop, high-order 16 bits discarded; segment descriptor information also loaded
					ESP = TemporaryESP;
					SS = TemporarySS;
				}
				//OperandSize == 16
				else {
					EIP = Pop();
					EIP = EIP & 0xFFFF;
					CS = Pop(); //16-bit pop; segment descriptor information also loaded
					CS.RPL = CPL;
					ESP = ESP + Source; //release parameters from called procedure's stack
					TemporaryESP = Pop();
					TemporarySS = Pop(); //16-bit pop; segment descriptor information also loaded
					ESP = TemporaryESP;
					SS = TemporarySS;
				}
				SegmentRegisters[] = {ES, FS, GS, DS};
				while(SegmentRegister = NextSegmentRegister(SegmentRegisters)) {
					if(IsDataPointer(SegmentRegister)  || !IsConformingCodeSegment(SegmentRegister) && CPL > SegmentDescriptor.DPL /*DPL in hidden part of segment register*/) SegmentSelector = 0; //segment register is invalid, null segment selector
					if(!IsWithinDescriptorTableLimits(SegmentSelector.Index) || (!IsData(SegmentDescriptor) && !IsReadableCodeSegment(SegmentDescriptor)) || (IsData(SegmentDescriptor) && !IsConformingCodeSegment(SegmentDescriptor) && SegmentDescriptor.DPL < CPL && SegmentDescriptor.DPL < CodeSegment.SegmentSelector.RPL)) SegmentSelectorRegister = NullSelector;
					ESP = ESP + Source; //release parameters from called procedure's stack
				}
			}
			else {
				//Return to same privilege level
				if(!IsWithinLimits(ReturnCode.SegmentLimit, ReturnInstructionPointer)) Exception(GP(0));
				if(OperandSize == 32) {
					EIP = Pop();
					CS = Pop(); //32-bit pop, high-order 16 bits are discarded
					ESP = ESP + Source; //Release parameters from stack
				}
				else { //OperandSize == 16
					EIP = Pop();
					EIP = EIP & 0xFFFF;
					ESP = ESP + Source; //Release parameters from stack
				}
			}
		}
		break;
}

參考 https://c9x.me/x86/html/file_module_x86_id_280.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章