Поиск в Wechat 🔍"Руководство по программированию", следуйте за этим программистом, который пишет галантерейные товары, ответьте "Ресурсы", вы можете получить обучающие маршруты и книги по внутренней разработке
предисловие
Это книга CSAPP, одна из самых известных лабораторий, бомбовая лаборатория, требует от вас разобрать спрятанные бомбы в этой программе, очень интересно, можете почитать.
Экспериментальные материалы
Это один из экспериментов в классе системного программирования в этом семестре. Он был введен из CMU. Исходный код и материалы могут бытьСайт курса КМУЧтобы получить, выберите загрузку материалов для самостоятельного изучения второго эксперимента. Для проведения этого эксперимента требуется дизассемблирование и отладка. Рекомендуется использовать gdb и objdump. Если вы не знаете gdb, вы можете взглянуть на этоПростое руководство по использованию gdb, о objdumpПросто посмотри на этоВ конце концов, я использовал только одну команду objdump -d filename для этого эксперимента.
Готов к работе
В скачанном декомпрессионном пакете три файла, полезный — исполняемый файл bomb, а также есть bomb.c, позволяющий увидеть весь процесс выполнения программы.Это основная часть основной функции.Вы можете видеть, что программа разделена на 6 фаз, каждая из которых требует от вас ввести строку строк, а затем вызвать функцию Phase_n() соответственно, чтобы определить, следует ли активировать бомбу. Сначала используйте objdump -d bomb > bomb.asm Сохраните разборку в bomb.asm, затем используйте tmux, чтобы открыть разделенный экран, слева — gdb для отладки бомбы.
Сначала найдите основную функцию следующим образом:
00000000000400da0 <main>:
400da0: 53 push %rbx
400da1: 83 ff 01 cmp $0x1,%edi
400da4: 75 10 jne 400db6 <main+0x16>
400da6: 48 8b 05 9b 29 20 00 mov 0x20299b(%rip),%rax # 603748 <stdin@@GLIBC_2.2.5>
400dad: 48 89 05 b4 29 20 00 mov %rax,0x2029b4(%rip) # 603768 <infile>
400db4: eb 63 jmp 400e19 <main+0x79>
400db6: 48 89 f3 mov %rsi,%rbx
400db9: 83 ff 02 cmp $0x2,%edi
400dbc: 75 3a jne 400df8 <main+0x58>
400dbe: 48 8b 7e 08 mov 0x8(%rsi),%rdi
400dc2: be b4 22 40 00 mov $0x4022b4,%esi
400dc7: e8 44 fe ff ff callq 400c10 <fopen@plt>
400dcc: 48 89 05 95 29 20 00 mov %rax,0x202995(%rip) # 603768 <infile>
400dd3: 48 85 c0 test %rax,%rax
400dd6: 75 41 jne 400e19 <main+0x79>
400dd8: 48 8b 4b 08 mov 0x8(%rbx),%rcx
400ddc: 48 8b 13 mov (%rbx),%rdx
400ddf: be b6 22 40 00 mov $0x4022b6,%esi
400de4: bf 01 00 00 00 mov $0x1,%edi
400de9: e8 12 fe ff ff callq 400c00 <__printf_chk@plt>
400dee: bf 08 00 00 00 mov $0x8,%edi
400df3: e8 28 fe ff ff callq 400c20 <exit@plt>
400df8: 48 8b 16 mov (%rsi),%rdx
400dfb: be d3 22 40 00 mov $0x4022d3,%esi
400e00: bf 01 00 00 00 mov $0x1,%edi
400e05: b8 00 00 00 00 mov $0x0,%eax
400e0a: e8 f1 fd ff ff callq 400c00 <__printf_chk@plt>
400e0f: bf 08 00 00 00 mov $0x8,%edi
400e14: e8 07 fe ff ff callq 400c20 <exit@plt>
400e19: e8 84 05 00 00 callq 4013a2 <initialize_bomb>
400e1e: bf 38 23 40 00 mov $0x402338,%edi
400e23: e8 e8 fc ff ff callq 400b10 <puts@plt>
400e28: bf 78 23 40 00 mov $0x402378,%edi
400e2d: e8 de fc ff ff callq 400b10 <puts@plt>
400e32: e8 67 06 00 00 callq 40149e <read_line>
400e37: 48 89 c7 mov %rax,%rdi
400e3a: e8 a1 00 00 00 callq 400ee0 <phase_1>
400e3f: e8 80 07 00 00 callq 4015c4 <phase_defused>
400e44: bf a8 23 40 00 mov $0x4023a8,%edi
400e49: e8 c2 fc ff ff callq 400b10 <puts@plt>
400e4e: e8 4b 06 00 00 callq 40149e <read_line>
400e53: 48 89 c7 mov %rax,%rdi
400e56: e8 a1 00 00 00 callq 400efc <phase_2>
400e5b: e8 64 07 00 00 callq 4015c4 <phase_defused>
400e60: bf ed 22 40 00 mov $0x4022ed,%edi
400e65: e8 a6 fc ff ff callq 400b10 <puts@plt>
400e6a: e8 2f 06 00 00 callq 40149e <read_line>
400e6f: 48 89 c7 mov %rax,%rdi
400e72: e8 cc 00 00 00 callq 400f43 <phase_3>
400e77: e8 48 07 00 00 callq 4015c4 <phase_defused>
400e7c: bf 0b 23 40 00 mov $0x40230b,%edi
400e81: e8 8a fc ff ff callq 400b10 <puts@plt>
400e86: e8 13 06 00 00 callq 40149e <read_line>
400e8b: 48 89 c7 mov %rax,%rdi
400e8e: e8 79 01 00 00 callq 40100c <phase_4>
400e93: e8 2c 07 00 00 callq 4015c4 <phase_defused>
400e98: bf d8 23 40 00 mov $0x4023d8,%edi
400e9d: e8 6e fc ff ff callq 400b10 <puts@plt>
400ea2: e8 f7 05 00 00 callq 40149e <read_line>
400ea7: 48 89 c7 mov %rax,%rdi
400eaa: e8 b3 01 00 00 callq 401062 <phase_5>
400eaf: e8 10 07 00 00 callq 4015c4 <phase_defused>
400eb4: bf 1a 23 40 00 mov $0x40231a,%edi
400eb9: e8 52 fc ff ff callq 400b10 <puts@plt>
400ebe: e8 db 05 00 00 callq 40149e <read_line>
400ec3: 48 89 c7 mov %rax,%rdi
400ec6: e8 29 02 00 00 callq 4010f4 <phase_6>
400ecb: e8 f4 06 00 00 callq 4015c4 <phase_defused>
400ed0: b8 00 00 00 00 mov $0x0,%eax
400ed5: 5b pop %rbx
Как мы видели в bomb.c, read_line сначала вызывается в основной функции, а затем возвращаемый адрес передается в функцию Phase_n.Если ввод неверный, будет выполнена функция взрыва. Так что, конечно же, следуйте траектории выполнения основной функции, чтобы разминировать одну за другой~
Phase_1
Сначала взгляните на дизассемблированный код Phase_1:
0000000000400ee0 <phase_1>:
400ee0: 48 83 ec 08 sub $0x8,%rsp
400ee4: be 00 24 40 00 mov $0x402400,%esi
400ee9: e8 4a 04 00 00 callq 401338 <strings_not_equal>
400eee: 85 c0 test %eax,%eax
400ef0: 74 05 je 400ef7 <phase_1+0x17>
400ef2: e8 43 05 00 00 callq 40143a <explode_bomb>
400ef7: 48 83 c4 08 add $0x8,%rsp
400efb: c3 retq
Ассемблерный код фазы_1 очень лаконичен, позвольте мне сначала объяснить его.
Функция read_line будет хранить адрес прочитанной строки в rdi и rsi, а функция strings_not_equal будет использовать значения в edi и esi как двухсимвольные адреса, и судить, равны ли они, и возвращать 0 в случае равенства
Посмотрите на функцию Phase_1, сначала присвойте esi 0x402400, а затем вызовите strings_not_equal.Только что проанализировано, перед каждым вызовом Phase_n будет вызываться read_line, чтобы прочитать строку и поместить ее в edi и esi. Очевидно, здесь нужно вызвать функцию сравнения строк, чтобы сравнить, равна ли введенная нами строка строке, хранящейся по адресу 0x402400, а затем вызвать тестовую команду, если eax равен 0, то есть две строки равны, перейти к концу функции, в противном случае вызовите функциюexplore_bomb, которая является функцией, которая взрывает бомбу. Ответ здесь, все, что нам нужно ввести, это строка, хранящаяся по адресу 0x402400. Затем начните отладку с помощью gdb
(gdb) b phase_1 ;打断点
(gdb) run ;执行到下一个断点
(gdb) info r ;查看寄存器值
(gdb) print (char*)(0x402400) ;查看内存中字符串
В приведенном выше окне отладки вы можете видеть, что ($edi) — это именно то приветствие, которое я ввел, а ответ «Пограничные отношения с Канадой никогда не были лучше». Затем снова откройте окно отладки и введите эту строку через фазу_1.
Вы можете записать ранее решенные ответы в файл, по одной строке для каждого ответа, а затем установить параметр командной строки set args xixi (здесь имя вашего файла ответов), когда вы начнете отладку, вы можете напрямую ввести решенные ответы.
Phase_2
Давайте сначала посмотрим на ассемблерный код, эта функция намного длиннее, и в середине много инструкций условного перехода, что не способствует пониманию функции кода, я вообще люблю отмечать это на ветке.
0000000000400efc <phase_2>:
400efc: 55 push %rbp
400efd: 53 push %rbx
400efe: 48 83 ec 28 sub $0x28,%rsp
400f02: 48 89 e6 mov %rsp,%rsi
400f05: e8 52 05 00 00 callq 40145c <read_six_numbers> ;读入六个数,第一个存在($rsp)处
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp) ;第一个数和1比较
400f0e: 74 20 je 400f30 <phase_2+0x34> ;等于1跳转
400f10: e8 25 05 00 00 callq 40143a <explode_bomb> ;否则爆炸
400f15: eb 19 jmp 400f30 <phase_2+0x34>
400f17: 8b 43 fc mov -0x4(%rbx),%eax ;取出rbx-4处的值赋给eax
400f1a: 01 c0 add %eax,%eax ; eax = eax *2
400f1c: 39 03 cmp %eax,(%rbx)
;比较eax*2和rbx处的值,注意:eax是ebx-4处的值,即将rbx和前一个数的两倍比较
400f1e: 74 05 je 400f25 <phase_2+0x29>
;如果相等就跳转,而跳转处的代码是将rbx+4
400f20: e8 15 05 00 00 callq 40143a <explode_bomb> ;否则爆炸
400f25: 48 83 c3 04 add $0x4,%rbx ; 将rbx+4
400f29: 48 39 eb cmp %rbp,%rbx
;将加4后的值和rbp比较,注意rbp是rsp+24,而rsp是第一个数,一个数四个字节。那么rbp就应该是
后那个数后面那个地址,即rbp是个循环哨兵
400f2c: 75 e9 jne 400f17 <phase_2+0x1b> ;不等就继续跳转去循环
400f2e: eb 0c jmp 400f3c <phase_2+0x40> ; 相等就结束跳转到函数结尾
400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx ;将rsp+4存到rbx
400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp ;将rsp +24 存到rbp
400f3a: eb db jmp 400f17 <phase_2+0x1b> ;跳转
400f3c: 48 83 c4 28 add $0x28,%rsp
400f40: 5b pop %rbx
400f41: 5d pop %rbp
400f42: c3 retq
Хорошо видно, что вызывается read_six_numbers.Название этой функции нам уже сказали, но осталось только посмотреть на ее код, чтобы знать, что она будет хранить первое число в адресе ($ rsp), а затем по очереди увеличивать его. Этот комментарий к коду очень ясен, основная часть — это цикл, и каждый цикл должен определить, равны ли текущее число и удвоенное предыдущее число, и взорваться, если они не равны. Кроме того, первое число должно быть 1, затем шесть введенных чисел должны быть 1 2 4 8 16 32, что можно проверить с помощью отладки gdb.
phase_3
Или сначала поставьте третью строку кода:
0000000000400f43 <phase_3>:
400f43: 48 83 ec 18 sub $0x18,%rsp
400f47: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
400f4c: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
400f51: be cf 25 40 00 mov $0x4025cf,%esi
400f56: b8 00 00 00 00 mov $0x0,%eax
400f5b: e8 90 fc ff ff callq 400bf0 <__isoc99_sscanf@plt>
400f60: 83 f8 01 cmp $0x1,%eax
400f63: 7f 05 jg 400f6a <phase_3+0x27>
400f65: e8 d0 04 00 00 callq 40143a <explode_bomb>
400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp)
400f6f: 77 3c ja 400fad <phase_3+0x6a> #将第一个数和7比较,大于跳转到炸弹
400f71: 8b 44 24 08 mov 0x8(%rsp),%eax
400f75: ff 24 c5 70 24 40 00 jmpq (,*0x402470%rax,8)
400f7c: b8 cf 00 00 00 mov $0xcf,%eax
400f81: eb 3b jmp 400fbe <phase_3+0x7b>
400f83: b8 c3 02 00 00 mov $0x2c3,%eax
400f88: eb 34 jmp 400fbe <phase_3+0x7b>
400f8a: b8 00 01 00 00 mov $0x100,%eax
400f8f: eb 2d jmp 400fbe <phase_3+0x7b>
400f91: b8 85 01 00 00 mov $0x185,%eax
400f96: eb 26 jmp 400fbe <phase_3+0x7b>
400f98: b8 ce 00 00 00 mov $0xce,%eax
400f9d: eb 1f jmp 400fbe <phase_3+0x7b>
400f9f: b8 aa 02 00 00 mov $0x2aa,%eax
400fa4: eb 18 jmp 400fbe <phase_3+0x7b>
400fa6: b8 47 01 00 00 mov $0x147,%eax
400fab: eb 11 jmp 400fbe <phase_3+0x7b>
400fad: e8 88 04 00 00 callq 40143a <explode_bomb>
400fb2: b8 00 00 00 00 mov $0x0,%eax
400fb7: eb 05 jmp 400fbe <phase_3+0x7b>
400fb9: b8 37 01 00 00 mov $0x137,%eax
400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax
400fc2: 74 05 je 400fc9 <phase_3+0x86>
400fc4: e8 71 04 00 00 callq 40143a <explode_bomb>
400fc9: 48 83 c4 18 add $0x18,%rsp
400fcd: c3 retq
Сначала я увидел sscanf, поэтому перед этой функцией должна быть строковая константа для хранения формата считываемых данных, поэтому строковая константа должна быть $0x4025cf, распечатайте ее с помощью gdb, чтобы подтвердить форматМы видим, что формат "%d %d", поэтому нам нужно ввести два целых числа. Оглядываясь назад на сборку, мы видим, что за этим кодом стоит множество операторов jmp, и они очень регулярны. Предполагается, что это таблица переходов или оператор switch. Адрес для перехода - 0x402470+%rax+8, а eax - это то, что мы Первое число, а затем каждый jmp можно рассматривать как оператор case. Мы видим, что каждый оператор case присваивает eax параметр, такой как 0xcf, 0x2c3 и т. д., а затем все case равномерно переходят к 0x400fbe, и В этом месте мы сравниваем второе число, которое мы ввели, со значением в eax.Если оно равно, пропускаем бомбу или она взорвется.Значение eax получается путем перехода к разным случаям в соответствии с первым значением. Тогда случаев столько, сколько должно быть столько ответов на задачу.Нам нужно только определить первое число и проследить вызов к одному из случаев, а затем посмотреть, какое значение константы в этом случае, что является во-вторых, мы вводим значение. Следует отметить, что первое введенное значение должно быть меньше 7, что прокомментировано в сборке, и видно, что случаев должно быть 7. Я выбрал первое число для ввода 3, а второе число нашел как 0x100 , что является десятичным числом 256.
所以此题的其中一个解为3 256
phase_4
Код разборки:
000000000040100c <phase_4>:
40100c: 48 83 ec 18 sub $0x18,%rsp
401010: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
401015: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
40101a: be cf 25 40 00 mov $0x4025cf,%esi
40101f: b8 00 00 00 00 mov $0x0,%eax
401024: e8 c7 fb ff ff callq 400bf0 <__isoc99_sscanf@plt>
401029: 83 f8 02 cmp $0x2,%eax
40102c: 75 07 jne 401035 <phase_4+0x29>
40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp)
401033: 76 05 jbe 40103a <phase_4+0x2e> #第一个数小与等于0xe跳转
401035: e8 00 04 00 00 callq 40143a <explode_bomb>
40103a: ba 0e 00 00 00 mov $0xe,%edx
40103f: be 00 00 00 00 mov $0x0,%esi
401044: 8b 7c 24 08 mov 0x8(%rsp),%edi
401048: e8 81 ff ff ff callq 400fce <func4>
40104d: 85 c0 test %eax,%eax #测试返回值是否为0,否就爆炸
40104f: 75 07 jne 401058 <phase_4+0x4c>
401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp)
401056: 74 05 je 40105d <phase_4+0x51>
401058: e8 dd 03 00 00 callq 40143a <explode_bomb>
40105d: 48 83 c4 18 add $0x18,%rsp
401061: c3 retq
Еще есть sscan, на этот раз посмотрите прямо на формат ввода, не является ли 0x4025cf строкой формата «%d %d» в предыдущем вопросе, кажется, что в этом вопросе все еще нужно ввести два целых числа, и func4 также будет называться в функции сборки Phase_4 эта функция func4 является ключевой, разборка выглядит следующим образом:
0000000000400fce <func4>:
400fce: sub $0x8,%rsp ;; 分配栈帧
400fd2: mov %edx,%eax ;; C eax
400fd4: sub %esi,%eax ;; C - B 更新 eax
400fd6: mov %eax,%ecx ;; C - B ecx
400fd8: shr $0x1f,%ecx ;; 右移 31 位, ecx 长为 32 位(也就是之前的最高位变为最低位,其余 31 位填充补 0),可以认为 ecx = 0
400fdb: add %ecx,%eax ;; C - B eax
400fdd: sar %eax ;; 这里是一个缩写 sar $1,%eax (对应的机器码为 D1F8) eax = (C-B)/2
400fdf: lea (%rax,%rsi,1),%ecx ;; (C+B)/2 ecx
400fe2: cmp %edi,%ecx ;; ecx 与 A 进行比较 (1)
400fe4: jle 400ff2 <func4+0x24> ;; ecx 小于等于 A 则跳转
400fe6: lea -0x1(%rcx),%edx ;; C = (C+B)/2 - 1
400fe9: callq 400fce <func4> ;; 递归调用
400fee: add %eax,%eax ;; 递归返回值加倍
400ff0: jmp 401007 <func4+0x39> ;; 跳转到 func 函数的出口处
400ff2: mov $0x0,%eax ;; eax = 0 (2)
400ff7: cmp %edi,%ecx ;; ecx 与 A 进行比较
400ff9: jge 401007 <func4+0x39> ;; eax 大于等于 A 则跳转
400ffb: lea 0x1(%rcx),%esi ;; B = ecx + 1
400ffe: callq 400fce <func4> ;; 递归调用
401003: lea 0x1(%rax,%rax,1),%eax ;; 递归返回值加倍并再加上 1
401007: add $0x8,%rsp ;; 释放栈帧
40100b: retq ;; 函数返回
В этой функции мы ясно видим, что func4 вызывает func4 внутри, что не является рекурсивным ассемблером. Попробуйте написать соответствующий код на языке c следующим образом:
int func4(int target, int step, int limit) {
/* edi = target; esi = step; edx = limit */
int temp = (limit - step) * 0.5;
int mid = temp + step;
if (mid > target) {
limit = mid - 1;
int ret1 = func4(target, step, limit);
return 2 * ret1;
} else {
if (mid >= target) {
return 0;
} else {
step = mid + 1;
int ret2 = func4(target, step, limit);
return (2 * ret2 + 1);
}
}
}
Наконец, ответ (7,0) выводится в соответствии с кодом языка c, но есть и другие решения этой проблемы.
phase_5
0000000000401062 <phase_5>:
401062: 53 push %rbx
401063: 48 83 ec 20 sub $0x20,%rsp
401067: 48 89 fb mov %rdi,%rbx
40106a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
401071: 00 00
401073: 48 89 44 24 18 mov %rax,0x18(%rsp)
401078: 31 c0 xor %eax,%eax
40107a: e8 9c 02 00 00 callq 40131b <string_length>
40107f: 83 f8 06 cmp $0x6,%eax #要求输入的字符串长度为6
401082: 74 4e je 4010d2 <phase_5+0x70>
401084: e8 b1 03 00 00 callq 40143a <explode_bomb>
401089: eb 47 jmp 4010d2 <phase_5+0x70>
40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx
40108f: 88 0c 24 mov %cl,(%rsp)
401092: 48 8b 14 24 mov (%rsp),%rdx
401096: 83 e2 0f and $0xf,%edx # 取edx后四位
401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx #将edx后四位作为0x4024b0字符数组的索引值
4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1) # 依次拷贝字符数组到0x10((%rsp,%rax,1))
4010a4: 48 83 c0 01 add $0x1,%rax #循环计数+1
4010a8: 48 83 f8 06 cmp $0x6,%rax #循环计数和6比较,即循环6次
4010ac: 75 dd jne 40108b <phase_5+0x29>
4010ae: c6 44 24 16 00 movb $0x0,0x16(%rsp) #字符串末尾添加"\0"
4010b3: be 5e 24 40 00 mov $0x40245e,%esi # 字符串常量
4010b8: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
4010bd: e8 76 02 00 00 callq 401338 <strings_not_equal> # 和字符串常量比较
4010c2: 85 c0 test %eax,%eax
4010c4: 74 13 je 4010d9 <phase_5+0x77>
4010c6: e8 6f 03 00 00 callq 40143a <explode_bomb>
4010cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
4010d0: eb 07 jmp 4010d9 <phase_5+0x77>
4010d2: b8 00 00 00 00 mov $0x0,%eax
4010d7: eb b2 jmp 40108b <phase_5+0x29>
4010d9: 48 8b 44 24 18 mov 0x18(%rsp),%rax
4010de: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4010e5: 00 00
4010e7: 74 05 je 4010ee <phase_5+0x8c>
4010e9: e8 42 fa ff ff callq 400b30 <__stack_chk_fail@plt>
4010ee: 48 83 c4 20 add $0x20,%rsp
4010f2: 5b pop %rbx
4010f3: c3 retq
Ниже будет сравнение со строковой константой, давайте сначала посмотрим, что это за строковая константа:Сборка "флаеров" также имеет строковую константу 0x4024b0:
"maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"
Поскольку ассемблерный код относительно длинный, я прямо объясню, что делает этот раздел: 1. Требовать ввода 6 символов, а затем зацикливать массив входных символов по очереди 2. Берет один символ в каждом раунде цикла, а затем принимает последние четыре цифры символа В качестве индекса возьмите символ со второй символьной константой и сохраните его по очереди по адресу 0x10 (%rsp) 3. Наконец, сравните строку по новому адресу 0x10 (%rsp) с "флаерами" , если то же самое, пройти, иначе взорваться, поэтому мы должны быть отодвинуты в соответствии с результатом.Например, символ f в флаерах получается по последним четырем цифрам первого символа, который мы вводим в качестве индекса в "maduiersnfotvbylТак что вы думаете, вы можете остановить бомбу с помощью Ctrl-C, не так ли?», но мы, зная, что четырехбитный двоичный индекс может содержать до 16 позиций, поэтому для выборки можно использовать только первые 16 символов этой длинной строки символов. нужные нам персонажи. Таким образом, индекс f равен 9, то есть двоичный 1001. Вам нужно запросить только символы, последние четыре цифры которых равны 1001 в таблице ascii. Я беру Y. И так далее, чтобы получить комбинацию из 6 символов: YONEFw
phase_6
Компиляция этого уровня действительно сложна для понимания, я только что прочитал некоторые части, но не смог связать их воедино, поэтому дизассемблирование здесь выкладывать не буду. Информация, которую я получил, вероятно, также заключается в том, что вам нужно ввести 6 чисел и меньше или равно 6. А также переворачивает каждое число (a = 7 - a) во время цикла. Проверяйте ответы других людей онлайн 4 3 2 1 6 5
Secret_phase
Я даже не знаю, что эта мина существует, не глядя на дизассемблированный код.А теперь давайте взглянем на эту секретную бомбу.Старое правило - смотреть на дизассемблирование.
0000000000401242 <secret_phase>:
401242: 53 push %rbx
401243: e8 56 02 00 00 callq 40149e <read_line>
401248: ba 0a 00 00 00 mov $0xa,%edx
40124d: be 00 00 00 00 mov $0x0,%esi
401252: 48 89 c7 mov %rax,%rdi
401255: e8 76 f9 ff ff callq 400bd0 <strtol@plt>
40125a: 48 89 c3 mov %rax,%rbx
40125d: 8d 40 ff lea -0x1(%rax),%eax
401260: 3d e8 03 00 00 cmp $0x3e8,%eax
401265: 76 05 jbe 40126c <secret_phase+0x2a>
401267: e8 ce 01 00 00 callq 40143a <explode_bomb>
40126c: 89 de mov %ebx,%esi
40126e: bf f0 30 60 00 mov $0x6030f0,%edi
401273: e8 8c ff ff ff callq 401204 <fun7>
401278: 83 f8 02 cmp $0x2,%eax
40127b: 74 05 je 401282 <secret_phase+0x40>
40127d: e8 b8 01 00 00 callq 40143a <explode_bomb>
401282: bf 38 24 40 00 mov $0x402438,%edi
401287: e8 84 f8 ff ff callq 400b10 <puts@plt>
40128c: e8 33 03 00 00 callq 4015c4 <phase_defused>
401291: 5b pop %rbx
401292: c3 retq
Но есть проблема.В основной функции мы не видим инструкции для вызова функции secret_phase,так где же она вызывается?Глобально поискав по ключевому слову, мы можем обнаружить, что она вызывается в функции Phase_defused, а фаза_defused есть будет выполняться при каждом проходе через фазу A, затем следующим шагом будет анализ того, при каких обстоятельствах будет инициирован вызов secret_phase
играть перед входом
00000000004015c4 <phase_defused>:
4015c4: 48 83 ec 78 sub $0x78,%rsp
4015c8: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
4015cf: 00 00
4015d1: 48 89 44 24 68 mov %rax,0x68(%rsp)
4015d6: 31 c0 xor %eax,%eax
比较输入的字符串数目是否等于6,不等于则跳转至程序结束
4015d8: 83 3d 81 21 20 00 06 cmpl $0x6,0x202181(%rip) # 603760 <num_input_strings>
4015df: 75 5e jne 40163f <phase_defused+0x7b>
4015e1: 4c 8d 44 24 10 lea 0x10(%rsp),%r8
4015e6: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
4015eb: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
4015f0: be 19 26 40 00 mov $0x402619,%esi
4015f5: bf 70 38 60 00 mov $0x603870,%edi
4015fa: e8 f1 f5 ff ff callq 400bf0 <__isoc99_sscanf@plt>
4015ff: 83 f8 03 cmp $0x3,%eax
401602: 75 31 jne 401635 <phase_defused+0x71>
401604: be 22 26 40 00 mov $0x402622,%esi
401609: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
40160e: e8 25 fd ff ff callq 401338 <strings_not_equal>
401613: 85 c0 test %eax,%eax
401615: 75 1e jne 401635 <phase_defused+0x71>
401617: bf f8 24 40 00 mov $0x4024f8,%edi
40161c: e8 ef f4 ff ff callq 400b10 <puts@plt>
401621: bf 20 25 40 00 mov $0x402520,%edi
401626: e8 e5 f4 ff ff callq 400b10 <puts@plt>
40162b: b8 00 00 00 00 mov $0x0,%eax
401630: e8 0d fc ff ff callq 401242 <secret_phase> ;调用secret_phase
401635: bf 58 25 40 00 mov $0x402558,%edi
40163a: e8 d1 f4 ff ff callq 400b10 <puts@plt>
40163f: 48 8b 44 24 68 mov 0x68(%rsp),%rax
401644: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
40164b: 00 00
40164d: 74 05 je 401654 <phase_defused+0x90>
40164f: e8 dc f4 ff ff callq 400b30 <__stack_chk_fail@plt>
401654: 48 83 c4 78 add $0x78,%rsp
401658: c3 retq
Давайте проанализируем приведенный выше код по частям.
4015d6: 31 c0 xor %eax,%eax
比较输入的字符串数目是否等于6,不等于则跳转至程序结束
4015d8: 83 3d 81 21 20 00 06 cmpl $0x6,0x202181(%rip) # 603760 <num_input_strings>
4015df: 75 5e jne 40163f <phase_defused+0x7b>
Затем, если на входе шесть строк, что означает, что вы проходите первые шесть фаз без срабатывания взрыва, вы можете ввести следующий код
4015f0: be 19 26 40 00 mov $0x402619,%esi
4015f5: bf 70 38 60 00 mov $0x603870,%edi
4015fa: e8 f1 f5 ff ff callq 400bf0 <__isoc99_sscanf@plt>
4015ff: 83 f8 03 cmp $0x3,%eax
401602: 75 31 jne 401635 <phase_defused+0x71>
Здесь esi и edi, очевидно, адреса двух строк, и дальше будет вызываться sscanf, поэтому одна должна быть введенной нами строкой, а другая — форматом в scanf("formate",&,&), подключаем Go вниз и используйте gdb, чтобы увидеть, что представляют собой эти две строкиВидно, что в esi ставится "%d %d %s", а edi - это ответ "7 0", который мы ввели в фазе_4, но это точно не подходит, %s не может совпадать. мы продолжаем видеть
4015fa: e8 f1 f5 ff ff callq 400bf0 <__isoc99_sscanf@plt>
4015ff: 83 f8 03 cmp $0x3,%eax
401602: 75 31 jne 401635 <phase_defused+0x71>
После вызова sscanf определите, равно ли возвращаемое значение eax (то есть количество правильно совпадающих подстановочных знаков) 3, если нет, перейдите в конец функции, чтобы напечатать это предложение.
401635: bf 58 25 40 00 mov $0x402558,%edi
40163a: e8 d1 f4 ff ff callq 400b10 <puts@plt>
Давайте посмотрим, что здесь находится по адресу 0x402558.Это первые шесть подсказок фазы прошли успешно, но мы не вошли в secret_phase, поэтому теперь будем считать, что мы ввели 3 совпадения, то есть будет выполнено добавление строки после четвертого решения задачи.
401604: be 22 26 40 00 mov $0x402622,%esi
401609: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
40160e: e8 25 fd ff ff callq 401338 <strings_not_equal>
401613: 85 c0 test %eax,%eax
401615: 75 1e jne 401635 <phase_defused+0x71>
401617: bf f8 24 40 00 mov $0x4024f8,%edi
40161c: e8 ef f4 ff ff callq 400b10 <puts@plt>
401621: bf 20 25 40 00 mov $0x402520,%edi
401626: e8 e5 f4 ff ff callq 400b10 <puts@plt>
40162b: b8 00 00 00 00 mov $0x0,%eax
401630: e8 0d fc ff ff callq 401242 <secret_phase> ;调用secret_phase
Здесь снова адреса двух строк передаются в esi и edi, а затем вызывается функция сравнения строк. Если нет, то она переходит в конец функции и печатает поздравления. Если они равны, строки по адресам 0x4024f8 и 0x402520 будет напечатан первым.Затем вызовите secret_phase.Похоже, что ключ к входу на секретный уровень состоит в том, чтобы сделать строки в edi и esi равными. Давайте сначала посмотрим, что представляют собой эти два места. Чтобы иметь возможность выполнить этот шаг, мы сначала добавляем строку после решения четвертого вопроса, то есть «7 0» на «7 0 xixi» (xixi добавлено случайно), поместите gdb ниже, чтобы просмотреть скриншот строки! ! ! ! ! ! ! Именно так я и думал, строка, совпавшая с %s, помещается в rdi, а заданная заранее — в rsi. Пока эти два равны, мы можем войти в секретный уровень, ну и берем ключ "DrEvil" на замену "xixi", и начинаем официально входить в секретную_фазу (так долго входил...
главный герой после прелюдии
Принято сначала ставить код дизассемблирования, хотя он и ставился раньше, но это уж слишком далеко...
0000000000401242 <secret_phase>:
401242: 53 push %rbx
401243: e8 56 02 00 00 callq 40149e <read_line>
401248: ba 0a 00 00 00 mov $0xa,%edx
40124d: be 00 00 00 00 mov $0x0,%esi
401252: 48 89 c7 mov %rax,%rdi
401255: e8 76 f9 ff ff callq 400bd0 <strtol@plt>
40125a: 48 89 c3 mov %rax,%rbx
40125d: 8d 40 ff lea -0x1(%rax),%eax
401260: 3d e8 03 00 00 cmp $0x3e8,%eax
401265: 76 05 jbe 40126c <secret_phase+0x2a>
401267: e8 ce 01 00 00 callq 40143a <explode_bomb>
40126c: 89 de mov %ebx,%esi
40126e: bf f0 30 60 00 mov $0x6030f0,%edi
401273: e8 8c ff ff ff callq 401204 <fun7>
401278: 83 f8 02 cmp $0x2,%eax
40127b: 74 05 je 401282 <secret_phase+0x40>
40127d: e8 b8 01 00 00 callq 40143a <explode_bomb>
401282: bf 38 24 40 00 mov $0x402438,%edi
401287: e8 84 f8 ff ff callq 400b10 <puts@plt>
40128c: e8 33 03 00 00 callq 4015c4 <phase_defused>
401291: 5b pop %rbx
401292: c3 retq
Еще кусок анализа
401243: e8 56 02 00 00 callq 40149e <read_line>
401248: ba 0a 00 00 00 mov $0xa,%edx
40124d: be 00 00 00 00 mov $0x0,%esi
401252: 48 89 c7 mov %rax,%rdi
401255: e8 76 f9 ff ff callq 400bd0 <strtol@plt>
40125a: 48 89 c3 mov %rax,%rbx
Здесь очевидно, что сначала читается строка, а затем вызывается функция strtol, которая используется для преобразования строк в long в языке C. Прототип функции выглядит следующим образом:
Описание: библиотечная функция C *long int strtol(const char str, char endptr, int base) преобразует строку, на которую указывает параметр str, в соответствии с заданной базой Преобразуется в длинное целое (типа long int), основание должно быть от 2 до 36 (включительно) или специальное значение 0.
Объявление: long int strtol(const char *str, char **endptr, int base)
Тогда вы, вероятно, можете догадаться, что возвращаемое значение read_line rax, хранящееся в rdi, является параметром str, а 0xa в edx должно представлять десятичное число, esi должно быть специальным значением 0, а затем анализировать возвращаемое значение strtol.
40125a: 48 89 c3 mov %rax,%rbx ;将rax保存到rbx中
40125d: 8d 40 ff lea -0x1(%rax),%eax ; eax =eax -1
401260: 3d e8 03 00 00 cmp $0x3e8,%eax ;cmp 1000, eax
401265: 76 05 jbe 40126c <secret_phase+0x2a> ;if eax < = 1000 then 跳过炸弹
401267: e8 ce 01 00 00 callq 40143a <explode_bomb> ;炸弹
40126c: 89 de mov %ebx,%esi ; 传参
40126e: bf f0 30 60 00 mov $0x6030f0,%edi ; 传参
401273: e8 8c ff ff ff callq 401204 <fun7> ; 调用fun7
401278: 83 f8 02 cmp $0x2,%eax ;比较返回值和2
40127b: 74 05 je 401282 <secret_phase+0x40> ;相等就跳转输出0x402438处的字符串并返回
40127d: e8 b8 01 00 00 callq 40143a <explode_bomb> ;不等就爆炸
401282: bf 38 24 40 00 mov $0x402438,%edi
401287: e8 84 f8 ff ff callq 400b10 <puts@plt>
Кажется, что secret_phase в целом состоит в том, чтобы ввести строку, а затем преобразовать строку в длинный тип, ошибка преобразования или преобразованное число> 1000 взорвется, а затем использовать преобразованное число для передачи в функцию fun7, если возврат значение равно 2, затем пройдите этот уровень плавно, иначе он взорвется. Итак, теперь ключ в том, что такое функция fun7, давайте зайдем и узнаем: fun7:
0000000000401204 <fun7>:
401204: 48 83 ec 08 sub $0x8,%rsp
401208: 48 85 ff test %rdi,%rdi
40120b: 74 2b je 401238 <fun7+0x34>
40120d: 8b 17 mov (%rdi),%edx
40120f: 39 f2 cmp %esi,%edx
401211: 7e 0d jle 401220 <fun7+0x1c>
401213: 48 8b 7f 08 mov 0x8(%rdi),%rdi ;rdi = (rdi+8)
401217: e8 e8 ff ff ff callq 401204 <fun7> ;递归1
40121c: 01 c0 add %eax,%eax
40121e: eb 1d jmp 40123d <fun7+0x39>
401220: b8 00 00 00 00 mov $0x0,%eax
401225: 39 f2 cmp %esi,%edx
401227: 74 14 je 40123d <fun7+0x39>
401229: 48 8b 7f 10 mov 0x10(%rdi),%rdi
40122d: e8 d2 ff ff ff callq 401204 <fun7> ;递归2
401232: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
401236: eb 05 jmp 40123d <fun7+0x39>
401238: b8 ff ff ff ff mov $0xffffffff,%eax
40123d: 48 83 c4 08 add $0x8,%rsp
401241: c3 retq
На самом деле, то, что я вижу с первого взгляда, это то, что есть два рекурсивных вызова этой функции, поэтому давайте выясним, где выход, это еще период времени.
401208: 48 85 ff test %rdi,%rdi ;edi如果为0则跳转并返回-1
40120b: 74 2b je 401238 <fun7+0x34>
Проверьте, равен ли входящий edi 0, если да, перейдите в конец и верните 0xffffffff, который равен 0
40120d: 8b 17 mov (%rdi),%edx ;取出rdi地址的值赋给edx
40120f: 39 f2 cmp %esi,%edx ;比较edx和esi的值
401211: 7e 0d jle 401220 <fun7+0x1c> ;if edx <= esi(这就是strtol转换来的数字),跳转
401213: 48 8b 7f 08 mov 0x8(%rdi),%rdi ;否则执行递归 rdi = (rdi+8)
401217: e8 e8 ff ff ff callq 401204 <fun7> ; 递归
40121c: 01 c0 add %eax,%eax ;递归返回值*2
40121e: eb 1d jmp 40123d <fun7+0x39> ;跳转至返回
этот абзац
401220: b8 00 00 00 00 mov $0x0,%eax ; 提前将eax置0,这其实是返回值
401225: 39 f2 cmp %esi,%edx ; 还是比较esi和edx
401227: 74 14 je 40123d <fun7+0x39> ; 如果相等就跳转并返回0
401229: 48 8b 7f 10 mov 0x10(%rdi),%rdi ;如果不相等就 edi = (edi+16)
40122d: e8 d2 ff ff ff callq 401204 <fun7> ;递归2
401232: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax ;递归返回值 eax = 2*eax+1
401236: eb 05 jmp 40123d <fun7+0x39> 跳转至返回
Но проблема в том, что мы ранее проанализировали, что fun7 должен вернуть 2 для прохождения, так как же мы можем вернуть 2?Детали кода были четко аннотированы.Ниже приведен рекурсивный псевдо-язык C, соответствующий версии
fun7(esi, void *rdi){
if(rdi == 0)
return -1;
if(*rdi <= esi ){
if(*rdi == esi)
return 0; step1
else
a = fun7(esi, *(rdi+16))
return 2*1+1 step2
} else {
return 2 * fun7(esi, *(rdi+8)) step3
}
}
На самом деле мы видим, что два рекурсивных изменения rdi различны, поэтому для того, чтобы вернуться к 2, порядок рекурсивных вызовов должен быть step3->step2->step1, то есть значение *rdi сначала должно быть * rdi > esi, затем *rdi
40126e: bf f0 30 60 00 mov $0x6030f0,%edi ; 传参
401273: e8 8c ff ff ff callq 401204 <fun7> ; 调用fun7
Теперь давайте проследим за предыдущим анализом, чтобы увидеть, какое число находится по адресу 0x6030f0:36! ! Итак, мы ввели число, поэтому мы можем ввести число меньше 36, чтобы увидеть второй шаг.что такое рди
8 ! ! Таким образом, входное число должно быть больше 8, чтобы перейти к третьему шагу, затем продолжайте делать это, пока не пройдет третий шаг.rdi == esi это уравнение, чтобы узнать esi, то есть число, которое мы должны ввести, затем gdb выполняет программу до третьего шага, чтобы распечатать значение, соответствующее rdi
двадцать два ! ! ! ! ! ! Все, что вам нужно сделать сейчас, это убедиться, что 22 правильно
Я освобождаю все вопросы в файл xixi и выполняю ./bomb xixiВау!Успешно прошел шесть уровней и один скрытый уровень, уже полвторого после разбора этого секретного уровня....
болтовня
Эта статья писалась давно, и каждый шаг от лабораторной работы до корректировки формата требует времени. Если вы считаете, что это полезно для вас, вы можете нажать, чтобы подписаться или лайкнуть, чтобы поддержать ~
Статья постоянно обновляется, и поиск WeChat «Руководство по программированию» — это первый раз, когда я ее получаю, а в ответе [Информация] есть подготовленные мной материалы интервью и шаблоны резюме ведущих производителей BAT.