hakril / PythonForWindows

A codebase aimed to make interaction with Windows and native execution easier
BSD 3-Clause "New" or "Revised" License
573 stars 112 forks source link

hwo to correctly assemble hexadecimal float? #29

Closed wkingnet closed 3 years ago

wkingnet commented 3 years ago

Hi, I'm here again. I'm continuing to hack my game, now I am studying how to control the game characters to walk. This part of the work requires the construction of several floating-point stack parameters.

Now i have a new problem. It seems that PythonForWindows cannot assemble float ​​correctly.

this is my python code:

    def call_template(self, ebx, arg1=None, arg2=None, arg3=None, arg4=None,
                      arg5=None, arg6=None, arg7=None, arg8=None, arg9=None):
        code = x86.MultipleInstr()
        code += x86.Pushad()
        code += x86.Mov("EBP", "ESP")
        if arg9 is not None:
            code += x86.Push(arg9)
        if arg8 is not None:
            code += x86.Push(arg8)
        if arg7 is not None:
            code += x86.Push(arg7)
        if arg7 is not None:
            code += x86.Push(arg6)
        if arg5 is not None:
            code += x86.Push(arg5)
        if arg4 is not None:
            code += x86.Push(arg4)
        if arg3 is not None:
            code += x86.Push(arg3)
        if arg2 is not None:
            code += x86.Push(arg2)
        if arg1 is not None:
            code += x86.Push(arg1)
        code += x86.Mov("ECX", address['call_ecx'])
        code += x86.Mov("EBX", ebx)
        code += x86.Call("EBX")
        code += x86.Mov("esp", "ebp")
        code += x86.Popad()
        code += x86.Ret()
        logger.info(f"""asm run""")
        run_asm(code, pid=self.pid)

pos_cur_x = read_addr(address['player_pos_cur_x'], data_type='float')
pos_cur_y = read_addr(address['player_pos_cur_y'], data_type='float')

ebx = address['call_city_go']
arg1 = pos_dst_x
arg2 = read_addr(address['player_pos_cur_x'] + 4, data_type="float")
arg3 = pos_dst_y
arg4 = read_addr(address['player_pos_cur_y'] + 4, data_type="float")
arg5 = get_sin(pos_dst_x, pos_dst_y, pos_cur_x, pos_cur_y)
arg6 = 0
arg7 = get_cos(pos_dst_x, pos_dst_y, pos_cur_x, pos_cur_y)
arg8 = 0
arg9 = read_addr(address['status_base_turn'], data_type="float", offset_list=[0xF4])
print(ebx, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
call_template(ebx, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)

In game, the assembly stack of the game itself is like this, float 32bit: 1

But if running the code through python, the stack parameter becomes unsigned short 16bit: 2

debug in pycharm, it is still float variable: 3

So, how can I correctly assemble floating-point numbers?

By the way, could you update the version on Pypi? It will be very convenient for users to update

Thanks.

wkingnet commented 3 years ago

After several hours of trying, I wrote a small piece of code to convert float to hex, which worked very well

def float2hex(_float):
    import struct
    result = ""
    for i in struct.pack('f', _float):
        i = hex(i)[2:]
        if len(i) % 2 == 1:
            i = "0" + i
        result = result + i
    result = "68" + result  # 68 means "push" 0x????
    return result

code += x86.Raw(float2hex(pos_dst_y - pos_cur_y))
hakril commented 3 years ago

Hello,

Thank you for the issue, and well done for the solution. As none of simple_x86 and simple_x64 implement any float instruction, and as I am not that familiar with CPython float representation nor IEEE Standard for Floating-Point Arithmetic (IEEE 754) . I think that trying float interpretation here would break more thing than the potential benefit.

For your use-case what you should be able to do is something similar to your solution by using struct to reeinterpret a float as a int with :

def float_as_int(_float):
    return struct.unpack("<I", struct.pack("f", _float))[0]

>>> windows.native_exec.simple_x86.Push(float_as_int(22271.2)).get_code()
b'hf\xfe\xadF'

Even here, I am not sur this is the exact result you are looking for.