记一次有趣的单步
flag
mode_edit
最近突然对汇编来了兴趣
于是自己写了一些小东西,用gcc qwq.c -S -O0 -o qwq.s
编译一下
看了看,大致了解了一些东西
解决了一些问题,比如:
x *= 10 和 x = (x << 1) + (x << 3) 哪个更快?
测试环境:gcc 6.2.0 on Windows 10
代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int c = getchar();
c = (c << 1) + (c << 3);
getchar();
c = c * 10;
putchar(c);
}
在O0的状态下
main:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $48, %rsp
.seh_stackalloc 48
.seh_endprologue
call __main
movl $1, -4(%rbp)
call getchar
movl -4(%rbp), %eax
leal (%rax,%rax), %edx
movl -4(%rbp), %eax
sall $3, %eax
addl %edx, %eax
movl %eax, -4(%rbp)
call getchar
movl -4(%rbp), %edx
movl %edx, %eax
sall $2, %eax
addl %edx, %eax
addl %eax, %eax
movl %eax, -4(%rbp)
movl $0, %eax
addq $48, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (Rev2, Built by MSYS2 project) 6.2.0"
.def getchar; .scl 2; .type 32; .endef
在O2的状态下
main:
pushq %rbx
.seh_pushreg %rbx
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
call __main
call getchar
leal 0(,%rax,8), %edx
leal (%rdx,%rax,2), %ebx
call getchar
leal (%rbx,%rbx,4), %ecx
addl %ecx, %ecx
call putchar
xorl %eax, %eax
addq $32, %rsp
popq %rbx
ret
.seh_endproc
.ident "GCC: (Rev2, Built by MSYS2 project) 6.2.0"
.def getchar; .scl 2; .type 32; .endef
.def putchar; .scl 2; .type 32; .endef
可以看到,机智的gcc把x *= 10
变成了
x + (x << 2)
x += x
总的指令条数还是一样的
所以说还是一样快的
“破解”自己的小程序
我写了这样的东西
#include <stdio.h>
#include <string.h>
int main()
{
int c;
c = getchar();
while (1)
{
if (c - '0' == 9)
return puts("SUCCESS"), 0;
puts("WRONG");
c = getchar();
}
}
如果只拿到编译后的二进制文件,怎么做到无论输入什么都输出SUCCESS呢?
开门,放x64dbg!
经过一番孜孜不倦的单步之后我们来到了这里
经过一番理智分析后,我们可以得出
如果那个jne
执行了的话(Jump if not equal
)
会输出错误信息
而第二个jmp
(4015F2
)相当于是在执行循环
于是。。
我们可以修改一波字符串的地址,使得错误时也输出SUCCESS
但是这是不够的,因为只有输入是9的时候才会跳出循环
于是我们可以干掉getchar
和第二个jmp
全部填充成nop
经过一顿操作之后长这样
于是就变成了无论输入什么都输出SUCCESS
至于为什么不用C++呢?
自己试一下g++生成的汇编是什么样就知道了
(还是C好
navigate_before
论自动化武器对Luogu绘版的影响
再记一次有趣的推导
navigate_next