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!

捕获.PNG

经过一番孜孜不倦的单步之后我们来到了这里

经过一番理智分析后,我们可以得出

如果那个jne执行了的话(Jump if not equal

会输出错误信息

而第二个jmp4015F2)相当于是在执行循环

于是。。

我们可以修改一波字符串的地址,使得错误时也输出SUCCESS

但是这是不够的,因为只有输入是9的时候才会跳出循环

于是我们可以干掉getchar和第二个jmp

全部填充成nop

捕获.PNG

经过一顿操作之后长这样

于是就变成了无论输入什么都输出SUCCESS

至于为什么不用C++呢?

自己试一下g++生成的汇编是什么样就知道了

(还是C好