2

first of all I would like to know what this python sentence does:

struct.unpack("!%sH" % (len(data) / 2), data))

And secondly, I have to make an ICMP request message using python, and the thing is that I already got some code the teacher gave me to help:

def step4(code, server, port):
    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname('icmp'))

    CHARMAP = nonprintable_to_dots()

    packet = create_packet(int(code))

    s.sendto(packet, (server, port))

    while True:
        msg = s.recv(1600)

        if not msg:
            break

        ihl = (ord(msg[0]) & 0x0F) * 4
        icmp = msg[ihl:]

        hexdump(icmp, True)

        if cksum(icmp[:]) != 0:
            print("wrong ckecksum!!")

def nonprintable_to_dots():
    return str.join('', [c if c in printable[:-5] else '.' for c in map(chr, range(256))])

def hexdump(frame, with_time=False):
    def to_chr(bytes):
        retval = bytes.translate(CHARMAP)
        return retval[:8] + ' ' + retval[8:]

    def to_hex(bytes):
        retval = str.join(' ', ["%02X" % ord(x) for x in bytes])
        return retval[:23] + ' ' + retval[23:]

    if with_time:
        print('--' + time.strftime("%H:%M:%s"))

    for i in range(0, len(frame), 16):
        line = frame[i:i + 16]
        print('%04X  %-49s |%-17s|' % (i, to_hex(line), to_chr(line)))

    print

def cksum(data):

    def sum16(data):
        "sum all the the 16-bit words in data"
        if len(data) % 2:
            data += '\0'

        return sum(struct.unpack("!%sH" % (len(data) / 2), data))

    retval = sum16(data)                       # sum
    retval = sum16(struct.pack('!L', retval))  # one's complement sum
    retval = (retval & 0xFFFF) ^ 0xFFFF        # one's complement

    return retval

The thing is that theoretically all this code is right, so I only should do the "create_packet" function, which I include here:

def create_packet(code):
    ICMP_REQUEST = 8
    checksum = 0
    identifier = 7
    sequence_number = 7

    message_header = struct.pack("!BBHHH", ICMP_REQUEST, 0, checksum, identifier, sequence_number)

    message_payload = struct.pack("!I", code)

    checksum = cksum(message_header + message_payload)

    message_header = struct.pack("!BBHHH", ICMP_REQUEST, 0, checksum, identifier, sequence_number)

    return message_header + message_payload

Whenever I execute the script, I always get this error:

Traceback (most recent call last):
    File "gymkhana.py", line 256, in <module>
        main()                                                                                                                
    File "gymkhana.py", line 19, in main
        step4(identifier, server, ginkana_port)
    File "gymkhana.py", line 181, in step4
        packet = create_packet(int(code))
    File "gymkhana.py", line 211, in create_packet
        checksum = cksum(message_header + message_payload)
    File "gymkhana.py", line 248, in cksum
        retval = sum16(data)                       # sum
    File "gymkhana.py", line 246, in sum16
        return sum(struct.unpack("!%sH" % (len(data) / 2), data))                                                           
struct.error: bad char in struct format
thmasker
  • 406
  • 1
  • 9
  • 21

1 Answers1

3

As answer to your first question, this line

struct.unpack("!%sH" % (len(data) / 2), data)    # in Python 2
struct.unpack("!%sH" % (len(data) // 2), data)   # in Python 3

says, take the n bytes in data (example: 4 bytes) and interpret them as n/2 unsigned short integers (example: 2 integers) of 2 bytes each. The ! at the beginning is about byte order and means big-endian. %s gets translated to one or more digits depending on the value of len(data) / 2, so in the example, it is the same as doing

struct.unpack("!2H", data)

Your bad char in struct format exception is because the code you present uses the / division operator. That is valid in Python 2. But in Python 3 it needs to be //. That is because / means integer division in Python 2, but floating-point division in Python 3. So in Python 3,

"!%sH" % (len(data) / 2)

comes out as

struct.unpack("!2.0H", data)

which explains your bad char error.

BoarGules
  • 16,440
  • 2
  • 27
  • 44
  • This is the data content just before the error: b'\x08\x00\x00\x00\x00\x07\x00\x07\x00\x01\x10\xc6' – thmasker Apr 05 '18 at 11:32
  • If I do this: `data = b'\x08\x00\x00\x00\x00\x07\x00\x07\x00\x01\x10\xc6'` followed by this: `struct.unpack("!%sH" % (len(data) / 2), data)` I get: `(2048, 0, 7, 7, 1, 4294)`. Is there maybe a funny character in your `unpack` format string that disappeared when you pasted the code here? – BoarGules Apr 05 '18 at 11:56
  • I just checked it and that's what I obtain. The "funny" thing is that I tried the same commands you wrote down here, and I still get the same error – thmasker Apr 05 '18 at 12:19
  • *(Now clutching at straws.)* What happens if you use `>` instead of `!` in the `struct` format string? – BoarGules Apr 05 '18 at 12:32
  • So if you take your function `sum16()` and put it in an entirely different program, and you feed it `data = b'\x08\x00\x00\x00\x00\x07\x00\x07\x00\x01\x10\xc6'` you get the error `bad char in struct format`, but if you type the same lines in at an interpreter prompt, you get a tuple of integers? – BoarGules Apr 05 '18 at 13:54
  • Nope. I always get the error `bad char in struct format` in another program as well as at an interpreter prompt – thmasker Apr 05 '18 at 16:32
  • *I have it.* Your code is valid Python 2, and so I was checking it in a Python 2 interpreter. But you are running it in Python 3. In Python 3 it needs to be `struct.unpack("!%sH" % (len(data) // 2), data)`. I have edited the answer to reflect this. – BoarGules Apr 05 '18 at 21:53
  • Oh mate, thank you so much!! That was it. I didn't think of it as a possibility because as I said on the question, theoretically the code was right and my teachers advised us to use Python 3 – thmasker Apr 06 '18 at 07:06