Posted July 4, 201510 yr Hi, is it possible somehow to get size or end address of a procedure, preferably without modifying procedure itself? e.g: Procedure Asem; begin asm mov eax, 5; add eax,8 shl eax,1 end; end;I want from my application determine size(number of bytes) of its own procedure, let's say, when I press a button. It's quite easy by using pointers to get start address of first line mov eax, 5, but how to get address of the last line(eventually RET)? For the sake of simplicity let's say the procedure is pretty simple with only one exit point and no external jumps outside of itself.
July 4, 201510 yr For sake of simplicity, use address of first byte of the next procedure. {$APPTYPE CONSOLE} uses Windows, Classes, Sysutils; Procedure Asem; begin asm mov eax, 5; add eax,8 shl eax,1 end; end; Procedure Bla; begin Writeln('whatever'); end; begin Writeln(Format('start of Asem: 0x%x', [dword(@Asem)])); Writeln(Format('start of next proc: 0x%x', [dword(@Bla)])); Writeln(Format('approximate size of Asem: 0x%x', [dword(@Bla)-dword(@Asem)])); end. Edited July 4, 201510 yr by kao
July 4, 201510 yr Well, unless you want to use some disassembly engine (BeaEngine/Capstone/etc.) that's the best I can think of.Borland's inline assembler is too limited to do anything better.
July 4, 201510 yr Author Would it help if the procedure was pure delphi/pascal instead of assembler?Only solution I came up with is to compile some markers in $I directive, but that's not a very clever solution........ scanning a file Edited July 4, 201510 yr by JustAGuy
July 4, 201510 yr Let's look at it from the another perspective - why do you need it? An why is approximate size not good enough for you?
July 4, 201510 yr so why don't you keep reading byte from memory until ret instruction(it is better to use TitanEngine...etc) program Project2; {$APPTYPE CONSOLE} uses SysUtils,Windows; function GetSize(Address:Pointer):Cardinal; var C3:Byte; tempaddress:Cardinal; i:integer; begin i:=0; tempaddress:=Cardinal(Address); while(not(C3 = $C3)) do begin i:=i+1; CopyMemory(@C3,ptr(tempaddress+i),1); end; result:=i+1; end; Procedure Asem; begin asm mov eax, 5; add eax,8 shl eax,1 end; end; begin Writeln(Format('%p',[@Asem])); Writeln(Format('%d',[GetSize(@Asem)])); Readln; end. Edited July 4, 201510 yr by n0th!ng
July 4, 201510 yr because ret instruction isnt always 0xc3.. and what if you have mov eax, 0c3c3c3c3h ?
July 4, 201510 yr Author kao; to get binary part of code I write without tedious manual extraction n0th!ng: thank you, but I did want to do it with search I was hoping to find some neat solution , but there probably is not any
July 4, 201510 yr Author because ret instruction isnt always 0xc3.. and what if you have mov eax, 0c3c3c3c3h ? also it would cause trouble if there were more than one 0xc3
July 4, 201510 yr Hi, so if I do understand you correctly then you want to find out any entire routine size lenght of your own coded source right?If you don't wanna find out each size lenght of all commands etc and you only wanna have routine entry / end then just log them both in your own table somewhere. 01020000 00401035 Routine Start 01020004 00401400 Routine End 00401035 PUSH EBP ...................... 00401400 MOV EAX,0x1 00401405 LEAVE 00401406 RETN 0x10On the other hand if your routine used more end outputs (more than only one) then you need to log them too.Or you do write some static code into each routine like this... 00401000 > E8 00000000 CALL 00401005 00401005 5F POP EDI 00401006 83EF 05 SUB EDI,0x5 ; top in edi 00401009 90 NOP 0040100A 90 NOP 0040100B 90 NOP 0040100C 90 NOP 0040100D 90 NOP 0040100E 90 NOP 0040100F 90 NOP 00401010 E8 00000000 CALL 00401015 00401015 5E POP ESI 00401016 2BF7 SUB ESI,EDI ; = lenght 00401018 83C6 07 ADD ESI,0x7 ; + add till ret 0040101B C3 RETN= 1C lenght.Maybe you do something like this.All in all I think its not so easy to get the entire routine lenght without to check all possibly dynamic commands + to follow them to find all ends & real end of routine if its using a real end.Some don't use returns so this can also be dynamic (just a example if you also wanna check unknown codes).If you only wanna check this with your own codes then use only one routine output and log them all and sub the top from the end = size of routine.No idea what you do plan to do later with that infos or for what it should be important etc. greetz
July 4, 201510 yr If you want to get the size after the procedure gets executed, you can use EIP to save the address at the very end of it (so u can easily calculate the size). begin asm mov eax, 5; add eax,8 shl eax,1 mov dword ptr [var], eip end; end;Otherwise, u've gotta search for RET(C3).
July 5, 201510 yr I think using nested routines and labels can solve your issue. procedure TForm1.Button2Click(Sender: TObject); label _end; var pStart : Pointer; pEnd : Pointer; procedure myproc; begin asm //Dummy code: pushad mov eax, 5 add eax, 6 shl eax, 1 mov eax, ebx pop dword ptr ss:[esp] sub esp, 4 popad _end: end; end; begin pStart := @myproc; lstLog.AddItem('Start = 0x' + IntToHex(DWORD(pStart), 8), Sender); asm push eax lea eax, _end //label points to 1 byte further than real end point. mov pEnd, eax pop eax end; lstLog.AddItem('End = 0x' + IntToHex(DWORD(pEnd), 8), Sender); lstLog.AddItem('myproc size = 0x' + IntToHex(DWORD(pEnd) - DWORD(pStart) - 1, 8), Sender); end; GetProcSize.rar
July 5, 201510 yr @Alzri2 mov r/m, eip or mov eip, r/mis not possible. From Intel® 64 and IA-32 Architectures Software Developer’s Manual (2015) : The processor fetches instructions from the code segment, using a logical address that consists of the segment selector in the CS register and the contents of the EIP register. The EIP register contains the offset within the code segment of the next instruction to be executed. The CS register cannot be loaded explicitly by an application program. Instead, it is loaded implicitly by instructions or internal processor operations that change program control (such as, procedure calls, interrupt handling, or task switching). The EIP register cannot be accessed directly by software; it is controlled implicitly by control-transfer instructions (such as JMP, Jcc, CALL, and RET), interrupts, and exceptions. The only way to read the EIP register is to execute a CALL instruction and then read the value of the return instruction pointer from the procedure stack. The EIP register can be loaded indirectly by modifying the value of a return instruction pointer on the procedure stack and executing a return instruction (RET or IRET).
July 5, 201510 yr @Gyrus: You're right, I forgot about the limitations of accessing EIP reg As an alternative, I tried this (don't have delphi to make an example): .data vEIP dd ? .code start: call GetEIP invoke ExitProcess, 0 GetEIP proc push [esp] pop [vEIP] ret GetEIP endp end start
July 6, 201510 yr Author for those of you interested I solved EIP problem this way @a1: lea eax,[@a1] mov _end, eax
Create an account or sign in to comment