0

When I try passing a 16 character string from python to C and scramble it, I keep getting random error codes back.

s = ctypes.c_wchar_p("H86ETJJJJHGFTYHr")


print(libc.hash_password(s))

At the start of the code I added a statement to return the size of the string back to python, however it keeps returning a value of 8

if (sizeof(my_string) != 17) return sizeof(my_string);

If I try to return a single element of the array, it will return a number, which I am assuming is the ascii value of the character, and the code does not error out.

This works for the last element as well, which is correctly recognised as a null.

The code works within C itself perfectly. So how could I get C to accept the correct size string, or python to accept the return string?

EDIT: Forgot to mention, when I do

sizeof(*my_string)

it returns a 1

EDIT 2: Here is the function definition

unsigned char *hash_password(char *input_string)
7
  • 1
    17 is the length of the string. The size of a variable of type char* on 64Bit is 8 byte, and this is what sizeof returns. Commented Jan 29, 2021 at 15:24
  • I am aware the size of the string is 17, it is why I put the early return statement if it isn't 17. And okay, but I am not sure why doing sizeof(*my_string) returns a 1. Commented Jan 29, 2021 at 15:26
  • @PythonProgrammer You won't get it done wearing the Python glasses when coding in C. Commented Jan 29, 2021 at 15:38
  • @PythonProgrammer sizeof operator, char, wchar_t etc Commented Jan 29, 2021 at 15:50
  • 1
    mystring is a char*, *mystring refers to the single char at the pointer, char has size 1. use strlen to count all the characters starting at the pointer up to the first null byte found. Commented Jan 29, 2021 at 20:26

3 Answers 3

2

In Python 3, "H86ETJJJJHGFTYHr" is a str object made up of Unicode codepoints. Your C function declaration is unsigned char *hash_password(char *input_string). Python str is marshaled as wchar_t* when passed via ctypes, not char*. Use a bytes object for that.

Assuming sizeof is ctypes.sizeof, it works like C and returns the size of the equivalent C object. for a c_wchar_p, that's a w_char_t*, and pointers typically have a size of 4 or 8 bytes (32- or 64-bit OS). It is not the length of the string.

It's also always a good idea to declare the arguments types and return type of a function when using ctypes, so it can check for type and number of arguments correctly, instead of guessing:

import ctypes

dll = ctypes.CDLL('./your.dll')
dll.hash_password.argtypes = ctypes.c_char_p,
dll.hash_password.restype = ctypes.c_char_p

A quick-and-dirty example (note printf returns length of string printed):

>>> from ctypes import *
>>> dll = CDLL('msvcrt')
>>> dll.printf('hello\n')  # ctypes assume wchar_t* for str, so passes UTF16-encoded data
h1                         # of 68 00 65 00 ... and prints only to first null, 1 char.
>>> dll.printf.argtypes=c_char_p, # tell ctypes the correct argument type
>>> dll.printf('hello\n')           # now it detects str is incorrect.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
>>> dll.printf(b'hello\n')          # pass bytes, and `char*` is marshaled to C
hello
6
Sign up to request clarification or add additional context in comments.

2 Comments

Wow thanks, C isn't returning any errors now! but could you clarify what is happening exactly, as in, is C now receiving Unicode characters, or are they in ascii? When I return the string from C to python it works just fine, but when I perform arithmetic on the characters, I get things such as b'}1&t\xb6`\xf6\...'
@PythonProgrammer A byte string contains bytes. ASCII is just convenient for display. h is the ASCII character representing the byte value 0x68, but bytes are the underlying data. \xb6 in your example is just a display representation for the byte value 0xB6.
1

In C sizeof doesn't ever return the length of the string it returns the size in memory of the variable.

For a string declared as

char *string;

Then string is a pointer to a character, and on your system it seems like pointers are 64 bits (i.e. 8 bits).

When you do *string in C you get the content of the first element that string points to - i.e. a single character.

To get the length of a string in C, use strlen(my_string).

2 Comments

Yes, it returns the size in bytes, but I expected to see a size of 17, as each char is 1 byte right? And oh I see the problem now...Thanks, I was passing only the first char but I need **my_string to pass the whole thing right?
**mystring is only valid C if *my_string is a pointer.
1

sizeof returns the size of an object in memory. This is not the same thing as the length of a string.

In your C code, my_string is a pointer to a string. sizeof(my_string) is the size of this pointer: 8 bytes on a 64-bit machine. sizeof(*my_string) is the size of what my_string points to. Since you're getting 1, it likely means that there's another problem in your C code, which is that you're mixing up single-byte characters (char, whose size is always 1 by definition) and wide characters (wchar_t, whose size is almost always 2 or 4).

Your string is a null-terminated wide character string. To obtain its length in C, call wcslen. Note that this means that your whole string processing code must use wchar_t and wcsxxx functions. If your string is a byte string, use char, strlen and other functions that work on byte strings.

4 Comments

other functions that work on byte strings -- you mean in Python? (I'm asking if c_wchar_p is always correct.)
Honestly I have no idea, I will place my function definition in an edit, so you can see what I'm passing.
@Wolf No, in C. The ctypes module will do the right thing to convert a Python string to a char array when you use c_char_p and to a wchar array when you use c_wchar_p. At least if you've set up locales and encoding correctly (which I don't know offhand how to do).
@Gilles'SO-stopbeingevil' c_wchar_p vs char* -- that's exact the dichotomy I now see in the question ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.