view raw
BPL BPL - 6 months ago 34
Python Question

how to read ctypes structures from file

I'm trying to learn more about PE files structures and also ctypes structures. I got a couple of questions:

Given this simple declaration:

from ctypes import *

class ImageDosHeader(Structure):
_fields_ = [
('e_magic', c_uint8, 2),
('e_cblp', c_uint8, 2),
('e_cp', c_uint8, 2),
('e_crlc', c_uint8, 2),
('e_cparhdr', c_uint8, 2),
('e_minalloc', c_uint8, 2),
('e_maxalloc', c_uint8, 2),
('e_ss', c_uint8, 2),
('e_sp', c_uint8, 2),
('e_csum', c_uint8, 2),
('e_ip', c_uint8, 2),
('e_cs', c_uint8, 2),
('e_lfarlc', c_uint8, 2),
('e_ovno', c_uint8, 2),
('e_res', c_uint8, 2),
('e_oemid', c_uint8, 2),
('e_oeminfo', c_uint8, 2),
('e_res2', c_uint8, 2 * 10),
('e_lfanew', c_uint8, 4),

Question1: What's the reason to get
ValueError: number of bits invalid for bit field

If I try to read a simplified structure like this:

class ImageDosHeader(Structure):
_fields_ = [
('e_magic', c_uint8, 2),

filename = 'example1_crinkler.exe'
with open(filename, 'rb') as f:
record = ImageDosHeader()

print('-' * 80)
with open(filename, 'rb') as f:
content =

I'm getting this output:


Question2: why
is it printing 1? I'd expect it to be "MZ", or 0x4d5a, or 19802... but not 1, what am i missing here?

NS: I'm aware of pefile pypi package or similars, that package is awesome but I'd like to reinvent the wheel here using ctypes to learn also about it, just for the record :)


Both questions seem to indicate the same misunderstanding. Quoting from the ctypes documentation for Structure._fields_:

For integer type fields like c_int, a third optional item can be given. It must be a small positive integer defining the bit width of the field.

This means the third field is the number of bits, not the number of values. Thus you've instructed that e_magic is a 2 bit wide field within an uint8. The ValueError stemmed from trying to extract 20 bits out of an 8 bit value in e_res2. The 1 value came from the lowest two bits of 'M': ord('M')&0b11 == 1.

You can adjust your data types by either selecting better matching ones, like c_uint16 for the 16-bit fields, or creating other structure types, like 2*c_char. Avoid using the third entry unless you're really dealing with a bit field; ctypes already knows the size of its types.