JamesMenetrey / MemorySharp

A C# based memory editing library targeting Windows applications, offering various functions to extract and inject data and codes into remote processes to allow interoperability.
Other
631 stars 134 forks source link

Wrong value returned when using the Fasm32Assembler without pointer #16

Closed oathgg closed 6 years ago

oathgg commented 6 years ago

When using the Fasm32Assembler you will get 2 different byte arrays when using the Assemble method.

Below I've provided a quick example: The following string is used: "jmp 4259840" byte[] asm = assembler.Assemble(asmString); // Returns 233,251,255,64,0 byte[] asm = assembler.Assemble(asmString, _addressToPatch.BaseAddress); // returns 233,231,251,64,255

The complete code i'm using is this: Fasm32Assembler assembler = new Fasm32Assembler(); string asmString = String.Join("\n", newAsm.ToArray()); // There is a bug currently in the Assembler.Assemble method. // We are forced to pass in an address otherwise it will return a wrong byte array. byte[] asm = assembler.Assemble(asmString); asm = assembler.Assemble(asmString, _addressToPatch.BaseAddress); PatchBytes(asm);

So I'm always forced to pass a pointer with the asm string otherwise the wrong values are returned. The place where the different values return from are: /// <summary> /// Assemble the specified assembly code at a base address. /// </summary> /// <param name="asm">The assembly code.</param> /// <param name="baseAddress">The address where the code is rebased.</param> /// <returns>An array of bytes containing the assembly code.</returns> public byte[] Assemble(string asm, IntPtr baseAddress) { // Rebase the code asm = String.Format("use32\norg 0x{0:X8}\n", baseAddress.ToInt64()) + asm; // Assemble and return the code return FasmNet.Assemble(asm); }

Located in Binarysharp.MemoryManagement.Assembly.Assembler.Fasm32Assembler.

zcanann commented 6 years ago

Although this is annoying, I do not think there is a way around this 'bug'. The assembler uses the assembly origin address to determine how to create the jump.

For example, if you were trying to create this: 0x20000 jmp 0x20009 ... 0x20009 inc eax

This should get assembled as a short jump in FASM because the jump is within +/- 127 bytes.

However, if you fail to pass an origin point to FASM, I think it may assume a value of 0 (or at least I'm pretty sure), producing

0x00000 jmp 0x20009 ... 0x20009 inc eax

This jump is now massive -- it used to only be a jump to +9 bytes, now its several thousand, so it will not be assembled as a short jump, even though that is what you wanted. Instead, it would either get compiled as a near jump or a far jump.

In your case, I'm pretty sure two different near jumps are being compiled. The instruction bytes produced depend on the jump distance, which is why the origin address matters.

tl;dr -- Always pass an origin point when assembling. In my opinion, the other function should be removed.

See: https://c9x.me/x86/html/file_module_x86_id_147.html https://stackoverflow.com/questions/14889643/how-encode-a-relative-short-jmp-in-x86

oathgg commented 6 years ago

I agree, after fiddling around with it some more it felt like my lack of knowledge was the cause of the issue and I jumped the gun as to say that this is a bug.

Thanks for the input, appreciate it!

zcanann commented 6 years ago

No problem. The other function does not make sense for game hacking, so it causes a lot of confusion. It took me awhile at first too to figure it out.