I have added one byte in my hex editor to make a JZ -> JNZ. Problem is, program won't function anymore, which does make sense. I have an inkling that I will need to edit a part of the file that holds the filesize as well. I am not sure where this is. According to some researching, it seems that the filesize is the Second WORD of the file? If so, it is not in a form I understand. Can anyone please help me out.
|November 7, 2006, 3:11 PM|
This is typically not so doable, because you are going to be breaking relative jump/call offsets for everything including and after the offset you are adding a new byte at.
|November 7, 2006, 7:19 PM|
Why do you need to insert a byte to invert the sense of the jump? Based on your phrasing, this appears to be a Windows/x86 question. On that platform, conditional jump instructions are at least as long as their unconditional counterparts. JZ and JNZ are the same length.
|November 8, 2006, 2:04 AM|
Thank you, I didn't realize that, even though it makes perfect sense. One question though. When compiled, the command (JNZ) is turned into its hex form of 70 XX (XX meaning not always the same). I ended up settign the value to 00. How is this derived? It worked, but that doesnt satisfy me.
|November 8, 2006, 9:28 PM|
Actually, it's compiled to 75 XX. :) The XX is added to eip, so 00 tells it to add 0 to eip -- that is, jump zero bytes forward.
|November 9, 2006, 12:37 AM|
I'm sorry for this legendary bump, but does that mean that JZ/JNZ can't jump more than 255 bytes forward? That could severely limit a large function.
|June 28, 2008, 11:40 PM|
That's why there are such things as jump short, jump near, and a far jump.
|June 29, 2008, 12:12 AM|
To expand on brew's statement: Joe, yes, partially. There are two commonly used forms of jumps, typically referred to as short jumps and long jumps. In IA32 mode (as opposed to x86_64 mode), a short jump has a one byte operand which is sign extended up to 32 bits. The result of the extension is then added to eip to find the destination. A long jump has a 4 byte operand, which is simply added to eip without further modification. Overflow is ignored, since jumps backward are implemented by overflowing eip. The short jump is desirable since it requires fewer bytes to encode, and therefore you can fit more instructions in the cache. However, as you noted, short jumps can't be used with large functions. The assembler should automatically switch to a long jump when a short jump won't work. Long jumps can be used where a short jump would suffice, but generally the only reason to do that is if you're patching an existing long jump and don't need to jump as far as it once did.
|June 29, 2008, 4:56 PM|
[quote author=Kp link=topic=15996.msg178709#msg178709 date=1214758597]
a short jump has a one byte operand which is sign extended up to 32 bits.
Thanks for clarifying that! What does "sign extended" mean, though?
|June 30, 2008, 4:42 AM|
[quote author=Joe[x86] link=topic=15996.msg178713#msg178713 date=1214800970]
What does "sign extended" mean, though?
Sign extention is the addition of whatever bits that are needed to preserve the sign of a signed value when being moved to a larger operand. For example:
mov al, -1
al itself is equal to 0xFF, or -1.
ax and eax are equal to 0xFF, or 255.
The value has 'changed' because the size of the value itself has changed.
However, if one wishes to preserve the sign of a value when moving from one sized operand to another, it must be sign extended.
Executing this instruction after that first one
movsx eax, al
would 'extend' the sign, making eax now equal to 0xFFFFFFFF instead of 0xFF, however logically equal in the original value (-1).
err... maybe somebody could explain it better.
|June 30, 2008, 6:12 AM|
The value is extended from one size to a larger size. All existing bits retain their values. New bits are set equal to the value of the sign bit in the old value. Thus, if the sign bit is set in the old value, all the "new bits" (those that have no counterpart in the old value) are set. If the sign bit is clear in the old value, all the new bits are clear in the new value.
To expand again on brew's comments, consider a byte sized source value. Suppose it has value 0xc3. If it were sign extended to 32 bits, it would become 0xffffffc3. The low 8 bits from the old value are copied over. Since the highest bit is set, the other 24 bits are set, yielding all f. If we instead sign extend the value 0x73, we get 0x00000073.
Contrast this with a zero extension operation, which converts a value to a larger size and populates all new bits with zero without regard to the value of the sign bit in the old value. Sign extending moves are used for integers which are signed (signed char, signed short), where the replication of the sign bit is required to preserve their value unchanged. Zero extending moves are used for integers which are unsigned (unsigned char, unsigned short), where setting all new bits to zero is required to preserve their value unchanged.
|July 1, 2008, 3:25 AM|