代码之家  ›  专栏  ›  技术社区  ›  RTC222

Python ctypes介绍如何从传递给NASM的字符数组中读取字节

  •  0
  • RTC222  · 技术社区  · 7 年前

    更新:我在下面马克·托洛宁的回答的帮助下解决了这个问题。以下是解决方案(但有一件事让我感到困惑):

    CA_f1 = (ctypes.c_char_p * len(f1))(*(name.encode() for name in f1))
    

    在关闭优化的情况下,我总是在条目上将rcx存储到内存变量中。稍后在程序中,当我需要在rcx中使用指针时,我从内存中读取它。这适用于单个指针,但不适用于访问下面显示的指针数组Mark Tolonen;也许这是因为它是一个指针数组,而不仅仅是一个指针。如果我在进入时将rcx存储到r15中,它确实可以工作,在程序的下游,它的工作方式如下:

    ;To access the first char of the first name pair: 
    
    xor rax,rax
    mov rdx,qword[r15]
    movsx eax,BYTE[rdx]
    ret
    
    ;To access the second char of the second name pair: 
    
    mov rdx,qword[r15+8]
    movsx eax,BYTE[rdx+1]
    

    这不是问题,因为我通常在寄存器中存储尽可能多的变量;有时寄存器不够,所以我不得不在内存中存储一些寄存器。现在,在处理字符串时,如果是指针数组,我将始终保留r15来保存在rcx中传递的指针。

    了解内存位置不工作的原因吗?

    我是NASM中字符串处理的新手,正在从ctypes传递字符串。使用以下Python函数从文本文件(Windows.txt)读取字符串数据:

    with open(fname, encoding = "utf8") as f1:
            for item in f1:
                item = item.lstrip()
                item = item.rstrip()
                return_data.append(item)
        return return_data
    

    CA_f1 = (ctypes.c_char_p * len(f1))()
    

    VisualStudio确认它是指向50个名称长的字节字符串的指针,这就是问题所在,我需要的是字节,而不是列表元素。然后我使用以下ctypes语法传递它:

    CallName.argtypes = [ctypes.POINTER(ctypes.c_char_p),ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_double)]
    

    更新:在传递字符串之前,现在我将列表转换为如下字符串:

    f1_x = ' '.join(f1)
    

    现在VS显示了一个指向558字节字符串的指针,这是正确的,但我仍然无法读取一个字节。

    在我的NASM程序中,我使用以下代码将一个随机字节读入al进行测试:

    lea rdi,[rel f1_ptr]
    mov rbp,qword [rdi] ; Pointer
    xor rax,rax
    mov al,byte[rbp+1]
    

    name_array: db "Margaret Swanson"
    

    我可以这样读:

    mov rdi,name_array
    xor rax,rax
    mov al,[rdi]
    

    但不是从传递到dll的指针。

    下面是NASM中一个简单、可复制示例的完整代码。在将其传递给NASM之前,我检查了随机字节,它们是我所期望的,所以我认为这不是编码。

    [BITS 64]
    [default rel]
    
    extern malloc, calloc, realloc, free
    global Main_Entry_fn
    export Main_Entry_fn
    global FreeMem_fn
    export FreeMem_fn
    
    section .data align=16
    f1_ptr: dq 0
    f1_length: dq 0
    f2_ptr: dq 0
    f2_length: dq 0
    data_master_ptr: dq 0
    
    section .text
    
    String_Test_fn:
    ;______
    
    lea rdi,[rel f1_ptr]
    mov rbp,qword [rdi]
    xor rax,rax
    mov al,byte[rbp+10]
    ret
    
    ;__________
    ;Free the memory
    
    FreeMem_fn:
    sub rsp,40
    call free
    add rsp,40
    ret
    
    ; __________
    ; Main Entry
    
    Main_Entry_fn:
    push rdi
    push rbp
    mov [f1_ptr],rcx
    mov [f2_ptr],rdx
    
    mov [data_master_ptr],r8
    lea rdi,[data_master_ptr]
    mov rbp,[rdi]
    xor rcx,rcx
    movsd xmm0,qword[rbp+rcx]
    cvttsd2si rax,xmm0
    mov [f1_length],rax
    add rcx,8
    movsd xmm0,qword[rbp+rcx]
    cvttsd2si rax,xmm0
    mov [f2_length],rax
    add rcx,8
    
    call String_Test_fn
    
    pop rbp
    pop rdi
    ret
    

    更新2:

    作为对请求的响应,以下是要使用的ctypes包装器:

    def Read_Data():
    
        Dir= "[FULL PATH TO DATA]"
    
        fname1 = Dir + "Random Names.txt"
        fname2 = Dir + "Random Phone Numbers.txt"
    
        f1 = Trans_02_Data.StrDataRead(fname1)
        f2 = Trans_02_Data.StrDataRead(fname2)
        f2_Int = [  int(numeric_string) for numeric_string in f2]
        StringTest_asm(f1, f2_Int)
    
    def StringTest_asm(f1,f2):
    
        f1.append("0")
    
        f1_x = ' '.join(f1)
        f1_x[0].encode(encoding='UTF-8',errors='strict')
    
        Input_Length_Array = []
        Input_Length_Array.append(len(f1))
        Input_Length_Array.append(len(f2*8))
    
        length_array_out = (ctypes.c_double * len(Input_Length_Array))(*Input_Length_Array)
    
        CA_f1 = (ctypes.c_char_p * len(f1_x))() #due to SO research
        CA_f2 = (ctypes.c_double * len(f2))(*f2)
        hDLL = ctypes.WinDLL("C:/NASM_Test_Projects/StringTest/StringTest.dll")
        CallName = hDLL.Main_Entry_fn
        CallName.argtypes = [ctypes.POINTER(ctypes.c_char_p),ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_double)]
        CallName.restype = ctypes.c_int64
    
        Free_Mem = hDLL.FreeMem_fn
        Free_Mem.argtypes = [ctypes.POINTER(ctypes.c_double)]
        Free_Mem.restype = ctypes.c_int64
        start_time = timeit.default_timer()
    
        ret_ptr = CallName(CA_f1,CA_f2,length_array_out)
    
        abc = 1 #Check the value of the ret_ptr, should be non-zero   
    
    1 回复  |  直到 7 年前
        1
  •  3
  •   Mark Tolonen    7 年前

    您的名称读取代码将返回一个Unicode字符串列表。以下代码将Unicode字符串列表编码为字符串数组,并传递给采用 POINTER(c_char_p) :

    >>> import ctypes
    >>> names = ['Mark','John','Craig']
    >>> ca = (ctypes.c_char_p * len(names))(*(name.encode() for name in names))
    >>> ca
    <__main__.c_char_p_Array_3 object at 0x000001DB7CF5F6C8>
    >>> ca[0]
    b'Mark'
    >>> ca[1]
    b'John'
    >>> ca[2]
    b'Craig'
    

    如果 ca rcx x64 calling convention . 以下C代码及其反汇编显示了VS2017 Microsoft编译器如何读取它:

    DLL代码(test.c)

    #define API __declspec(dllexport)
    
    int API func(const char** instr)
    {
        return (instr[0][0] << 16) + (instr[1][0] << 8) + instr[2][0];
    }
    

    反汇编(编译优化以保持简短,添加了我的注释)

    ; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.24215.1
    
    include listing.inc
    
    INCLUDELIB LIBCMT
    INCLUDELIB OLDNAMES
    
    PUBLIC  func
    ; Function compile flags: /Ogtpy
    ; File c:\test.c
    _TEXT   SEGMENT
    instr$ = 8
    func    PROC
    
    ; 5    :     return (instr[0][0] << 16) + (instr[1][0] << 8) + instr[2][0];
    
      00000 48 8b 51 08      mov     rdx, QWORD PTR [rcx+8]  ; address of 2nd string
      00004 48 8b 01         mov     rax, QWORD PTR [rcx]    ; address of 1st string
      00007 48 8b 49 10      mov     rcx, QWORD PTR [rcx+16] ; address of 3rd string
      0000b 44 0f be 02      movsx   r8d, BYTE PTR [rdx]     ; 1st char of 2nd string, r8d=4a
      0000f 0f be 00         movsx   eax, BYTE PTR [rax]     ; 1st char of 1st string, eax=4d
      00012 0f be 11         movsx   edx, BYTE PTR [rcx]     ; 1st char of 3rd string, edx=43
      00015 c1 e0 08         shl     eax, 8                  ; eax=4d00
      00018 41 03 c0         add     eax, r8d                ; eax=4d4a
      0001b c1 e0 08         shl     eax, 8                  ; eax=4d4a00
      0001e 03 c2            add     eax, edx                ; eax=4d4a43
    
    ; 6    : }
    
      00020 c3               ret     0
    func    ENDP
    _TEXT   ENDS
    END
    

    Python代码(test.py)

    from ctypes import *
    
    dll = CDLL('test')
    dll.func.argtypes = POINTER(c_char_p),
    dll.restype = c_int
    
    names = ['Mark','John','Craig']
    ca = (c_char_p * len(names))(*(name.encode() for name in names))
    print(hex(dll.func(ca)))
    

    输出:

    0x4d4a43
    

    这是‘M’、‘J’和‘C’的正确ASCII码。

    推荐文章