[{"data":1,"prerenderedAt":1128},["ShallowReactive",2],{"category-data-crackme":3},[4],{"id":5,"title":6,"body":7,"description":1113,"extension":1114,"meta":1115,"navigation":59,"ogImage":1117,"path":1124,"seo":1125,"stem":1126,"__hash__":1127},"content/blogs/16. ctf-reverse-crackme-deep-dive.md","逆向一个 XOR CrackMe：从侦查到脚本还原",{"type":8,"value":9,"toc":1102},"minimark",[10,15,19,23,31,263,266,290,293,302,309,322,327,331,338,355,360,373,376,384,390,394,405,422,425,431,440,444,449,466,470,514,517,554,562,566,599,602,627,631,634,701,706,709,718,721,727,731,744,760,762,768,777,779,785,788,803,805,811,814,818,823,840,906,909,922,925,931,934,946,951,955,1050,1054,1087,1091,1098],[11,12,14],"h3",{"id":13},"一现实背景注册码校验","一、现实背景：注册码校验",[16,17,18],"p",{},"商业软件用\"输入正确序列号才放行\"的校验函数保护授权，逆向需要从二进制还原该校验逻辑。CrackMe 是这类校验的简化版，也是逆向入门最好的练习材料。",[11,20,22],{"id":21},"二题目源码与编译","二、题目源码与编译",[16,24,25,26,30],{},"出题人视角的 ",[27,28,29],"code",{},"crackme.c","：",[32,33,38],"pre",{"className":34,"code":35,"language":36,"meta":37,"style":37},"language-c shiki shiki-themes dracula","#include \u003Cstdio.h>\n#include \u003Cstring.h>\n\nint check(const char *input) {\n    char key[] = \"k3y\";\n    // key：异或密钥，编译后仍以字符串形式存在二进制里，strings 可见。\n    unsigned char enc[] = {\n        0x08,0x47,0x1f,0x10,0x41,0x1c,0x1d,0x56,\n        0x0b,0x18,0x56,0x26,0x0e,0x5d,0x1e,0x02,\n        0x5d,0x1c,0x0e,0x41,0x10,0x05,0x54,0x04\n    };\n    // enc：把正确 flag 逐字节异或后的密文，硬编码在程序里。\n    int n = strlen(input);\n    // strlen：求输入长度（不含结尾 \\0）。\n    if (n != 24) return 0;\n    // 长度必须是 24，先卡掉大部分错误输入。\n    for (int i = 0; i \u003C n; i++)\n        if ((input[i] ^ key[i % 3]) != enc[i]) return 0;\n        // 逐字节校验：输入第 i 位与 key 循环异或后必须等于密文第 i 位。\n        // key[i % 3]：i%3 在 0,1,2 之间循环，即 key 循环使用。\n    return 1;\n    // 全部通过才返回 1，main 据此打印 Correct!。\n}\n\nint main(int argc, char **argv) {\n    if (argc != 2) {\n        // argc：命令行参数个数；不带参数时 argc=1，这里要求恰好 2 个。\n        puts(\"Usage: ./crackme \u003Cflag>\");\n        return 1;\n    }\n    if (check(argv[1]))\n        // argv[1]：命令行第二个参数，即用户输入的 flag。\n        puts(\"Correct!\");\n    else\n        puts(\"Wrong!\");\n    return 0;\n}\n","c","",[27,39,40,48,54,61,67,73,79,85,91,97,103,109,115,121,127,133,139,145,151,157,163,169,175,181,186,192,198,204,210,216,222,228,234,240,246,252,258],{"__ignoreMap":37},[41,42,45],"span",{"class":43,"line":44},"line",1,[41,46,47],{},"#include \u003Cstdio.h>\n",[41,49,51],{"class":43,"line":50},2,[41,52,53],{},"#include \u003Cstring.h>\n",[41,55,57],{"class":43,"line":56},3,[41,58,60],{"emptyLinePlaceholder":59},true,"\n",[41,62,64],{"class":43,"line":63},4,[41,65,66],{},"int check(const char *input) {\n",[41,68,70],{"class":43,"line":69},5,[41,71,72],{},"    char key[] = \"k3y\";\n",[41,74,76],{"class":43,"line":75},6,[41,77,78],{},"    // key：异或密钥，编译后仍以字符串形式存在二进制里，strings 可见。\n",[41,80,82],{"class":43,"line":81},7,[41,83,84],{},"    unsigned char enc[] = {\n",[41,86,88],{"class":43,"line":87},8,[41,89,90],{},"        0x08,0x47,0x1f,0x10,0x41,0x1c,0x1d,0x56,\n",[41,92,94],{"class":43,"line":93},9,[41,95,96],{},"        0x0b,0x18,0x56,0x26,0x0e,0x5d,0x1e,0x02,\n",[41,98,100],{"class":43,"line":99},10,[41,101,102],{},"        0x5d,0x1c,0x0e,0x41,0x10,0x05,0x54,0x04\n",[41,104,106],{"class":43,"line":105},11,[41,107,108],{},"    };\n",[41,110,112],{"class":43,"line":111},12,[41,113,114],{},"    // enc：把正确 flag 逐字节异或后的密文，硬编码在程序里。\n",[41,116,118],{"class":43,"line":117},13,[41,119,120],{},"    int n = strlen(input);\n",[41,122,124],{"class":43,"line":123},14,[41,125,126],{},"    // strlen：求输入长度（不含结尾 \\0）。\n",[41,128,130],{"class":43,"line":129},15,[41,131,132],{},"    if (n != 24) return 0;\n",[41,134,136],{"class":43,"line":135},16,[41,137,138],{},"    // 长度必须是 24，先卡掉大部分错误输入。\n",[41,140,142],{"class":43,"line":141},17,[41,143,144],{},"    for (int i = 0; i \u003C n; i++)\n",[41,146,148],{"class":43,"line":147},18,[41,149,150],{},"        if ((input[i] ^ key[i % 3]) != enc[i]) return 0;\n",[41,152,154],{"class":43,"line":153},19,[41,155,156],{},"        // 逐字节校验：输入第 i 位与 key 循环异或后必须等于密文第 i 位。\n",[41,158,160],{"class":43,"line":159},20,[41,161,162],{},"        // key[i % 3]：i%3 在 0,1,2 之间循环，即 key 循环使用。\n",[41,164,166],{"class":43,"line":165},21,[41,167,168],{},"    return 1;\n",[41,170,172],{"class":43,"line":171},22,[41,173,174],{},"    // 全部通过才返回 1，main 据此打印 Correct!。\n",[41,176,178],{"class":43,"line":177},23,[41,179,180],{},"}\n",[41,182,184],{"class":43,"line":183},24,[41,185,60],{"emptyLinePlaceholder":59},[41,187,189],{"class":43,"line":188},25,[41,190,191],{},"int main(int argc, char **argv) {\n",[41,193,195],{"class":43,"line":194},26,[41,196,197],{},"    if (argc != 2) {\n",[41,199,201],{"class":43,"line":200},27,[41,202,203],{},"        // argc：命令行参数个数；不带参数时 argc=1，这里要求恰好 2 个。\n",[41,205,207],{"class":43,"line":206},28,[41,208,209],{},"        puts(\"Usage: ./crackme \u003Cflag>\");\n",[41,211,213],{"class":43,"line":212},29,[41,214,215],{},"        return 1;\n",[41,217,219],{"class":43,"line":218},30,[41,220,221],{},"    }\n",[41,223,225],{"class":43,"line":224},31,[41,226,227],{},"    if (check(argv[1]))\n",[41,229,231],{"class":43,"line":230},32,[41,232,233],{},"        // argv[1]：命令行第二个参数，即用户输入的 flag。\n",[41,235,237],{"class":43,"line":236},33,[41,238,239],{},"        puts(\"Correct!\");\n",[41,241,243],{"class":43,"line":242},34,[41,244,245],{},"    else\n",[41,247,249],{"class":43,"line":248},35,[41,250,251],{},"        puts(\"Wrong!\");\n",[41,253,255],{"class":43,"line":254},36,[41,256,257],{},"    return 0;\n",[41,259,261],{"class":43,"line":260},37,[41,262,180],{},[16,264,265],{},"编译（解题时只有二进制，没有源码）：",[32,267,271],{"className":268,"code":269,"language":270,"meta":37,"style":37},"language-bash shiki shiki-themes dracula","gcc -o crackme crackme.c\n","bash",[27,272,273],{"__ignoreMap":37},[41,274,275,279,283,287],{"class":43,"line":44},[41,276,278],{"class":277},"sAOxA","gcc",[41,280,282],{"class":281},"sIQBb"," -o",[41,284,286],{"class":285},"s-mGx"," crackme",[41,288,289],{"class":285}," crackme.c\n",[16,291,292],{},"运行确认行为：",[32,294,296],{"className":268,"code":295,"language":270,"meta":37,"style":37},"./crackme\n",[27,297,298],{"__ignoreMap":37},[41,299,300],{"class":43,"line":44},[41,301,295],{"class":277},[16,303,304,305,308],{},"输出：",[27,306,307],{},"Usage: ./crackme \u003Cflag>","。",[32,310,312],{"className":268,"code":311,"language":270,"meta":37,"style":37},"./crackme wrongflag123456789012\n",[27,313,314],{"__ignoreMap":37},[41,315,316,319],{"class":43,"line":44},[41,317,318],{"class":277},"./crackme",[41,320,321],{"class":285}," wrongflag123456789012\n",[16,323,304,324,308],{},[27,325,326],{},"Wrong!",[11,328,330],{"id":329},"三核心步骤-1侦查","三、核心步骤 1：侦查",[16,332,333,337],{},[334,335,336],"strong",{},"目的","：先确认程序类型、收集可见字符串，缩小分析范围。",[16,339,340,30,343,346,347,350,351,354],{},[334,341,342],{},"思路",[27,344,345],{},"strings"," 能看到 ",[27,348,349],{},"k3y"," 这样的短密钥（需要 ",[27,352,353],{},"-n 3","），但看不到二进制密文数组。若直接看到 flag 明文就是送分题；看不到就继续反汇编。",[356,357,359],"h4",{"id":358},"_31-file","3.1 file",[32,361,363],{"className":268,"code":362,"language":270,"meta":37,"style":37},"file crackme\n",[27,364,365],{"__ignoreMap":37},[41,366,367,370],{"class":43,"line":44},[41,368,369],{"class":277},"file",[41,371,372],{"class":285}," crackme\n",[16,374,375],{},"预期输出（Linux ELF）：",[32,377,382],{"className":378,"code":380,"language":381,"meta":37},[379],"language-text","crackme: ELF 64-bit LSB pie executable, x86-64, dynamically linked, ... not stripped\n","text",[27,383,380],{"__ignoreMap":37},[16,385,386,389],{},[27,387,388],{},"not stripped","：函数名可见。",[356,391,393],{"id":392},"_32-strings","3.2 strings",[16,395,396,397,399,400,402,403,30],{},"注意：",[27,398,345],{}," 默认只显示长度 ≥ 4 的字符串，密钥 ",[27,401,349],{}," 只有 3 个字符，必须加 ",[27,404,353],{},[32,406,408],{"className":268,"code":407,"language":270,"meta":37,"style":37},"strings -n 3 crackme\n",[27,409,410],{"__ignoreMap":37},[41,411,412,414,417,420],{"class":43,"line":44},[41,413,345],{"class":277},[41,415,416],{"class":281}," -n",[41,418,419],{"class":281}," 3",[41,421,372],{"class":285},[16,423,424],{},"预期输出：",[32,426,429],{"className":427,"code":428,"language":381,"meta":37},[379],"Usage: ./crackme \u003Cflag>\nCorrect!\nWrong!\nk3y\n",[27,430,428],{"__ignoreMap":37},[16,432,433,435,436,439],{},[27,434,349],{}," 极可能是密钥。",[27,437,438],{},"enc"," 数组是二进制数据，strings 显示不出来。",[11,441,443],{"id":442},"四核心步骤-2定位校验函数","四、核心步骤 2：定位校验函数",[16,445,446,448],{},[334,447,336],{},"：找到\"比较\"发生的代码，确认密钥如何参与运算。",[16,450,451,453,454,457,458,461,462,465],{},[334,452,342],{},"：main 里 ",[27,455,456],{},"call check"," 后紧跟 ",[27,459,460],{},"test eax, eax; je","，说明返回值决定对错。于是把目光集中到 check；Ghidra 伪代码把汇编还原成 ",[27,463,464],{},"input[i] ^ key[i%3] != enc[i]","，逆向思路立刻清晰。",[356,467,469],{"id":468},"_41-objdump-看-main","4.1 objdump 看 main",[32,471,473],{"className":268,"code":472,"language":270,"meta":37,"style":37},"objdump -d -M intel crackme | grep -A 40 '\u003Cmain>'\n",[27,474,475],{"__ignoreMap":37},[41,476,477,480,483,486,489,491,495,498,501,504,508,511],{"class":43,"line":44},[41,478,479],{"class":277},"objdump",[41,481,482],{"class":281}," -d",[41,484,485],{"class":281}," -M",[41,487,488],{"class":285}," intel",[41,490,286],{"class":285},[41,492,494],{"class":493},"s0Tla"," |",[41,496,497],{"class":277}," grep",[41,499,500],{"class":281}," -A",[41,502,503],{"class":281}," 40",[41,505,507],{"class":506},"seVfx"," '",[41,509,510],{"class":285},"\u003Cmain>",[41,512,513],{"class":506},"'\n",[16,515,516],{},"预期输出（节选）：",[32,518,522],{"className":519,"code":520,"language":521,"meta":37,"style":37},"language-asm shiki shiki-themes dracula","0000000000001189 \u003Cmain>:\n    1191:  cmp    edi, 0x2\n    1194:  jne    0x11a0\n    119e:  call   0x1149 \u003Ccheck>\n    11a3:  test   eax, eax\n    11a5:  je     0x11b5\n","asm",[27,523,524,529,534,539,544,549],{"__ignoreMap":37},[41,525,526],{"class":43,"line":44},[41,527,528],{},"0000000000001189 \u003Cmain>:\n",[41,530,531],{"class":43,"line":50},[41,532,533],{},"    1191:  cmp    edi, 0x2\n",[41,535,536],{"class":43,"line":56},[41,537,538],{},"    1194:  jne    0x11a0\n",[41,540,541],{"class":43,"line":63},[41,542,543],{},"    119e:  call   0x1149 \u003Ccheck>\n",[41,545,546],{"class":43,"line":69},[41,547,548],{},"    11a3:  test   eax, eax\n",[41,550,551],{"class":43,"line":75},[41,552,553],{},"    11a5:  je     0x11b5\n",[16,555,556,558,559,308],{},[27,557,456],{}," 说明判断逻辑在 ",[27,560,561],{},"check",[356,563,565],{"id":564},"_42-objdump-看-check","4.2 objdump 看 check",[32,567,569],{"className":268,"code":568,"language":270,"meta":37,"style":37},"objdump -d -M intel crackme | grep -A 60 '\u003Ccheck>'\n",[27,570,571],{"__ignoreMap":37},[41,572,573,575,577,579,581,583,585,587,589,592,594,597],{"class":43,"line":44},[41,574,479],{"class":277},[41,576,482],{"class":281},[41,578,485],{"class":281},[41,580,488],{"class":285},[41,582,286],{"class":285},[41,584,494],{"class":493},[41,586,497],{"class":277},[41,588,500],{"class":281},[41,590,591],{"class":281}," 60",[41,593,507],{"class":506},[41,595,596],{"class":285},"\u003Ccheck>",[41,598,513],{"class":506},[16,600,601],{},"预期会看到循环比较：",[32,603,605],{"className":519,"code":604,"language":521,"meta":37,"style":37},"movzx  eax, BYTE PTR [rax]        ; input[i]\nxor    eax, 0x...                 ; 与 key 异或\ncmp    al, BYTE PTR [rbp-...]     ; 与 enc[i] 比较\nje     0x...\n",[27,606,607,612,617,622],{"__ignoreMap":37},[41,608,609],{"class":43,"line":44},[41,610,611],{},"movzx  eax, BYTE PTR [rax]        ; input[i]\n",[41,613,614],{"class":43,"line":50},[41,615,616],{},"xor    eax, 0x...                 ; 与 key 异或\n",[41,618,619],{"class":43,"line":56},[41,620,621],{},"cmp    al, BYTE PTR [rbp-...]     ; 与 enc[i] 比较\n",[41,623,624],{"class":43,"line":63},[41,625,626],{},"je     0x...\n",[356,628,630],{"id":629},"_43-ghidra-反编译","4.3 Ghidra 反编译",[16,632,633],{},"安装并打开：",[32,635,637],{"className":268,"code":636,"language":270,"meta":37,"style":37},"apt install openjdk-21-jdk unzip\nwget https://github.com/NationalSecurityAgency/ghidra/releases/latest/download/ghidra_*.zip\nunzip ghidra_*.zip && cd ghidra_*_PUBLIC && ./ghidraRun\n",[27,638,639,653,668],{"__ignoreMap":37},[41,640,641,644,647,650],{"class":43,"line":44},[41,642,643],{"class":277},"apt",[41,645,646],{"class":285}," install",[41,648,649],{"class":285}," openjdk-21-jdk",[41,651,652],{"class":285}," unzip\n",[41,654,655,658,661,665],{"class":43,"line":50},[41,656,657],{"class":277},"wget",[41,659,660],{"class":285}," https://github.com/NationalSecurityAgency/ghidra/releases/latest/download/ghidra_",[41,662,664],{"class":663},"sqerP","*",[41,666,667],{"class":285},".zip\n",[41,669,670,673,676,678,681,685,689,691,693,696,698],{"class":43,"line":56},[41,671,672],{"class":277},"unzip",[41,674,675],{"class":285}," ghidra_",[41,677,664],{"class":663},[41,679,680],{"class":285},".zip",[41,682,684],{"class":683},"sCdxs"," && ",[41,686,688],{"class":687},"sLL85","cd",[41,690,675],{"class":285},[41,692,664],{"class":663},[41,694,695],{"class":285},"_PUBLIC",[41,697,684],{"class":683},[41,699,700],{"class":277},"./ghidraRun\n",[16,702,703,704,308],{},"操作：新建项目 → 导入 crackme → 自动分析 → 双击 ",[27,705,561],{},[16,707,708],{},"预期伪代码：",[32,710,712],{"className":34,"code":711,"language":36,"meta":37,"style":37},"if ((input[i] ^ key[i % 3]) != enc[i]) return 0;\n",[27,713,714],{"__ignoreMap":37},[41,715,716],{"class":43,"line":44},[41,717,711],{},[16,719,720],{},"得到逆运算关系：",[32,722,725],{"className":723,"code":724,"language":381,"meta":37},[379],"enc[i] = input[i] ^ key[i % 3]\ninput[i] = enc[i] ^ key[i % 3]\n",[27,726,724],{"__ignoreMap":37},[11,728,730],{"id":729},"五核心步骤-3gdb-动态调试","五、核心步骤 3：GDB 动态调试",[32,732,734],{"className":268,"code":733,"language":270,"meta":37,"style":37},"gdb ./crackme\n",[27,735,736],{"__ignoreMap":37},[41,737,738,741],{"class":43,"line":44},[41,739,740],{"class":277},"gdb",[41,742,743],{"class":285}," ./crackme\n",[32,745,748],{"className":746,"code":747,"language":740,"meta":37,"style":37},"language-gdb shiki shiki-themes dracula","(gdb) break check\n# 目的：在 check 入口暂停，观察输入参数和密文数据。\n",[27,749,750,755],{"__ignoreMap":37},[41,751,752],{"class":43,"line":44},[41,753,754],{},"(gdb) break check\n",[41,756,757],{"class":43,"line":50},[41,758,759],{},"# 目的：在 check 入口暂停，观察输入参数和密文数据。\n",[16,761,424],{},[32,763,766],{"className":764,"code":765,"language":381,"meta":37},[379],"Breakpoint 1 at 0x1149\n",[27,767,765],{"__ignoreMap":37},[32,769,771],{"className":746,"code":770,"language":740,"meta":37,"style":37},"(gdb) run AAAAAAAAAAAAAAAAAAAAAAAA\n",[27,772,773],{"__ignoreMap":37},[41,774,775],{"class":43,"line":44},[41,776,770],{},[16,778,424],{},[32,780,783],{"className":781,"code":782,"language":381,"meta":37},[379],"Breakpoint 1, 0x0000555555555149 in check ()\n",[27,784,782],{"__ignoreMap":37},[16,786,787],{},"查看 enc 数组（地址在反汇编中确定）：",[32,789,791],{"className":746,"code":790,"language":740,"meta":37,"style":37},"(gdb) x/24bx 0x5555555551e0\n# x：查看内存；/24bx：24 个字节、b 按字节、x 十六进制显示。\n",[27,792,793,798],{"__ignoreMap":37},[41,794,795],{"class":43,"line":44},[41,796,797],{},"(gdb) x/24bx 0x5555555551e0\n",[41,799,800],{"class":43,"line":50},[41,801,802],{},"# x：查看内存；/24bx：24 个字节、b 按字节、x 十六进制显示。\n",[16,804,424],{},[32,806,809],{"className":807,"code":808,"language":381,"meta":37},[379],"0x...: 0x08 0x47 0x1f 0x10 0x41 0x1c 0x1d 0x56 ...\n",[27,810,808],{"__ignoreMap":37},[16,812,813],{},"与源码中的 enc 数组一致，确认比较逻辑。",[11,815,817],{"id":816},"六核心步骤-4写脚本还原","六、核心步骤 4：写脚本还原",[16,819,820,822],{},[334,821,336],{},"：把\"加密\"反过来算：密文与 key 再异或一次即明文。",[16,824,825,827,828,831,832,835,836,839],{},[334,826,342],{},"：因为 ",[27,829,830],{},"enc[i] = input[i] ^ key[i%3]","，两边同时异或 ",[27,833,834],{},"key[i%3]"," 得 ",[27,837,838],{},"input[i] = enc[i] ^ key[i%3]","。异或的自反性是本题全部关键。",[32,841,845],{"className":842,"code":843,"language":844,"meta":37,"style":37},"language-python shiki shiki-themes dracula","enc = [0x08,0x47,0x1f,0x10,0x41,0x1c,0x1d,0x56,\n       0x0b,0x18,0x56,0x26,0x0e,0x5d,0x1e,0x02,\n       0x5d,0x1c,0x0e,0x41,0x10,0x05,0x54,0x04]\nkey = b\"k3y\"   # 从 strings -n 3 找到的密钥\n\nflag = bytes([c ^ key[i % len(key)] for i, c in enumerate(enc)])\n# enumerate(enc)：逐个取出 (下标 i, 密文字节 c)。\n# key[i % len(key)]：i 对 3 取余，让密钥循环（与 C 的 i%3 一致）。\n# c ^ key[...]：密文异或密钥 = 明文。\n# bytes([...])：把整数列表转成字节串。\nprint(flag.decode())\n# decode()：bytes 按 UTF-8 解码成字符串显示。\n","python",[27,846,847,852,857,862,867,871,876,881,886,891,896,901],{"__ignoreMap":37},[41,848,849],{"class":43,"line":44},[41,850,851],{},"enc = [0x08,0x47,0x1f,0x10,0x41,0x1c,0x1d,0x56,\n",[41,853,854],{"class":43,"line":50},[41,855,856],{},"       0x0b,0x18,0x56,0x26,0x0e,0x5d,0x1e,0x02,\n",[41,858,859],{"class":43,"line":56},[41,860,861],{},"       0x5d,0x1c,0x0e,0x41,0x10,0x05,0x54,0x04]\n",[41,863,864],{"class":43,"line":63},[41,865,866],{},"key = b\"k3y\"   # 从 strings -n 3 找到的密钥\n",[41,868,869],{"class":43,"line":69},[41,870,60],{"emptyLinePlaceholder":59},[41,872,873],{"class":43,"line":75},[41,874,875],{},"flag = bytes([c ^ key[i % len(key)] for i, c in enumerate(enc)])\n",[41,877,878],{"class":43,"line":81},[41,879,880],{},"# enumerate(enc)：逐个取出 (下标 i, 密文字节 c)。\n",[41,882,883],{"class":43,"line":87},[41,884,885],{},"# key[i % len(key)]：i 对 3 取余，让密钥循环（与 C 的 i%3 一致）。\n",[41,887,888],{"class":43,"line":93},[41,889,890],{},"# c ^ key[...]：密文异或密钥 = 明文。\n",[41,892,893],{"class":43,"line":99},[41,894,895],{},"# bytes([...])：把整数列表转成字节串。\n",[41,897,898],{"class":43,"line":105},[41,899,900],{},"print(flag.decode())\n",[41,902,903],{"class":43,"line":111},[41,904,905],{},"# decode()：bytes 按 UTF-8 解码成字符串显示。\n",[16,907,908],{},"运行：",[32,910,912],{"className":268,"code":911,"language":270,"meta":37,"style":37},"python3 solve.py\n",[27,913,914],{"__ignoreMap":37},[41,915,916,919],{"class":43,"line":44},[41,917,918],{"class":277},"python3",[41,920,921],{"class":285}," solve.py\n",[16,923,924],{},"实际输出：",[32,926,929],{"className":927,"code":928,"language":381,"meta":37},[379],"ctf{reverse_engineering}\n",[27,930,928],{"__ignoreMap":37},[16,932,933],{},"验证：",[32,935,937],{"className":268,"code":936,"language":270,"meta":37,"style":37},"./crackme ctf{reverse_engineering}\n",[27,938,939],{"__ignoreMap":37},[41,940,941,943],{"class":43,"line":44},[41,942,318],{"class":277},[41,944,945],{"class":285}," ctf{reverse_engineering}\n",[16,947,924,948,308],{},[27,949,950],{},"Correct!",[11,952,954],{"id":953},"七必学工具","七、必学工具",[956,957,958,974],"table",{},[959,960,961],"thead",{},[962,963,964,968,971],"tr",{},[965,966,967],"th",{},"工具",[965,969,970],{},"核心功能",[965,972,973],{},"命令",[975,976,977,990,1002,1018,1037],"tbody",{},[962,978,979,982,985],{},[980,981,369],"td",{},[980,983,984],{},"看类型/是否 stripped",[980,986,987],{},[27,988,989],{},"file crackme",[962,991,992,994,997],{},[980,993,345],{},[980,995,996],{},"提取字符串、指定最小长度",[980,998,999],{},[27,1000,1001],{},"strings -n 3 crackme",[962,1003,1004,1006,1009],{},[980,1005,479],{},[980,1007,1008],{},"反汇编、看数据段",[980,1010,1011,1014,1015],{},[27,1012,1013],{},"objdump -d -M intel crackme","、",[27,1016,1017],{},"objdump -s -j .rodata crackme",[962,1019,1020,1023,1026],{},[980,1021,1022],{},"GDB",[980,1024,1025],{},"断点、运行、查看内存",[980,1027,1028,1014,1031,1014,1034],{},[27,1029,1030],{},"break check",[27,1032,1033],{},"run xxx",[27,1035,1036],{},"x/24bx addr",[962,1038,1039,1042,1045],{},[980,1040,1041],{},"Ghidra",[980,1043,1044],{},"反编译伪代码",[980,1046,1047],{},[27,1048,1049],{},"ghidraRun",[11,1051,1053],{"id":1052},"八小-ctf-实战流程复述","八、小 CTF 实战：流程复述",[1055,1056,1057,1065,1076,1082],"ol",{},[1058,1059,1060,1062,1063,308],"li",{},[27,1061,1001],{}," → 找到密钥 ",[27,1064,349],{},[1058,1066,1067,1069,1070,1072,1073,308],{},[27,1068,479],{},"/Ghidra → 确认 ",[27,1071,561],{}," 里是 ",[27,1074,1075],{},"input[i] ^ key[i%3] == enc[i]",[1058,1077,1078,1079,308],{},"Python 一行异或还原 → ",[27,1080,1081],{},"ctf{reverse_engineering}",[1058,1083,1084,1085,308],{},"运行验证 → ",[27,1086,950],{},[11,1088,1090],{"id":1089},"九小结","九、小结",[16,1092,1093,1094,1097],{},"XOR CrackMe 的通用解法：",[334,1095,1096],{},"找密钥、找密文、再异或一次","。逆向题的套路基本都是\"侦查 → 定位校验 → 逆运算\"三步；后续遇到的壳、混淆只是在中间增加干扰。",[1099,1100,1101],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .sqerP, html code.shiki .sqerP{--shiki-default:#BD93F9;--shiki-default-font-style:italic}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .sLL85, html code.shiki .sLL85{--shiki-default:#8BE9FD}",{"title":37,"searchDepth":50,"depth":50,"links":1103},[1104,1105,1106,1107,1108,1109,1110,1111,1112],{"id":13,"depth":56,"text":14},{"id":21,"depth":56,"text":22},{"id":329,"depth":56,"text":330},{"id":442,"depth":56,"text":443},{"id":729,"depth":56,"text":730},{"id":816,"depth":56,"text":817},{"id":953,"depth":56,"text":954},{"id":1052,"depth":56,"text":1053},{"id":1089,"depth":56,"text":1090},"完整的 CrackMe 逆向：编译源码、file/strings 侦查、objdump/Ghidra 定位校验、GDB 动态调试、Python 逆运算还原 flag。","md",{"date":1116,"image":1117,"alt":1118,"tags":1119,"published":59},"14th Aug 2026","/blogs-img/blog7.png","XOR CrackMe 完整实操",[1120,1121,1122,1123],"ctf","reverse","crackme","example","/blogs/ctf-reverse-crackme-deep-dive",{"title":6,"description":1113},"blogs/16. ctf-reverse-crackme-deep-dive","xZZ1X760wPJNGRPAuGfcnpCTK_DSMHcI_gsMU8yxErg",1786796568698]