汇编 – 若水斋 https://blog.werner.wiki Try harder Sat, 10 Nov 2018 04:59:31 +0000 zh-Hans hourly 1 https://wordpress.org/?v=6.8.3 https://blog.werner.wiki/wp-content/uploads/2018/11/cropped-ql1-1-32x32.jpg 汇编 – 若水斋 https://blog.werner.wiki 32 32 OllyDbg中的常用断点 https://blog.werner.wiki/common-breakpoints-in-ollydbg/ https://blog.werner.wiki/common-breakpoints-in-ollydbg/#respond Sat, 06 Aug 2016 12:04:01 +0000 http://blog.werner.wiki/?p=133 将《0day安全:软件漏洞分析技术》第5篇 漏洞案例分析 第25章 漏洞分析技术概述 表25-2-4 OllyDbg中的常用断点 整理在此以方便查询:

常用断点类别 命令 说明
拦截窗口 bp CreateWindowExA/W 创建窗口
bp ShowWindow 显示窗口
bp UpdateWindow 更新窗口
bp GetWindowTextA/W 获取窗口文本
拦截消息框 bp MessageBoxA/W 创建消息框
bp MessageBoxExA/W 创建消息框
bp MessageBoxIndirectA/W 创建定制消息框
拦截对话框 bp DialogBoxParamA/W 创建模态对话框
bp DialogBoxIndirectParamA/W 创建模态对话框
bp CreateDialogParamA/W 创建非模态对话框
bp CreateDialogIndirectParamA/W 创建非模态对话框
bp GetDlgItemTextA/W 获取对话框文本
bp GetDlgItemInt 获取对话框整数值
拦截剪贴板 bp GetClipboardData 获取粘贴板数据
拦截注册表 bp RegOpenKeyA/W 打开子健
bp RegOpenKeyExA/W 打开子健
bp RegQueryValueA/W 查找子健
bp RegQueryValueExA/W 查找子健
bp RegSetValueA/W 设置子健
bp RegSetValueExA/W 设置子健
功能限制拦截断点 bp EnableMenuItem 禁止或允许菜单项
bp EnableWindow 禁止或允许窗口
拦截时间 bp GetLocalTime 获取本地时间
bp GetSystemTime 获取系统时间
bp GetFileTime 获取文件时间
bp GetTickCount 获得自系统成功启动以来所经历的毫秒数
bp GetCurrentTime 获取当前时间(16位)
bp SetTimer 创建定时器
bp TimerProc 定时器超时回调函数
拦截文件 bp CreateFileA/W 创建或打开文件
bp OpenFile 打开文件
bp ReadFile 读文件
bp WriteFile 写文件
bp GetModuleFileNameA/W 获取当前模块路径
bp GetFileSize 获取文件大小
bp SetFilePointer 设置文件指针
bp FindFirstFileA/W 搜索文件
bp FindFirstFileExA/W 搜索文件
拦截驱动器 bp GetDriveTypeA/W 获取磁盘驱动器类型
bp GetLogicalDrives 获取逻辑驱动器符号
bp GetLogicalDriveStringsA/W 获取当前所有逻辑驱动器的根驱动器路径
]]>
https://blog.werner.wiki/common-breakpoints-in-ollydbg/feed/ 0
没有VC如何用CodeBlocks运行shellcode https://blog.werner.wiki/run-shellcode-with-codeblocks/ https://blog.werner.wiki/run-shellcode-with-codeblocks/#respond Thu, 21 Jul 2016 11:58:57 +0000 http://blog.werner.wiki/?p=127 在《0day安全:软件漏洞分析技术(第二版)》的第3章中,作者给出了一种便捷运行shellcode的方法:

    char shellcode[]="\x66\x81\xEC\x40\x04\x33\xDB......";//欲调试的十六进制机器码
    void main()
    {
        __asm
        {
             lea    eax, shellcode
             push   eax
             ret
        }
    }

原书作者是用Visual C++6.0编译代码的,操作系统是Windows XP SP2。我在Windows XP SP3下用CodeBlocks编译如上代码时则会出错,因为Visual C++6.0中的编译器和CodeBlocks中的默认编译器gcc关于内联汇编的语法有很大不同,可参考64位ubuntu中c与intel汇编混合编程之C语言内联汇编,虽然这篇文章是介绍Linux环境下的,但gcc编译器C语言内嵌汇编的语法在Windows下和Linux下是一样的。简单尝试后得到如下可行代码:

    int main() {
        char popup_general[]=
            "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1"
            "\x4F\x68\x32\x74\x91\x0C\x8B\xF4\x8D\x7E"
            "\x0C\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33"
            "\x32\x53\x68\x75\x73\x65\x72\x54\x33\xD2"
            "\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B\x49\x1C"
            "\x57\x56\x8B\x69\x08\x8B\x79\x20\x8B\x09"
            "\x66\x39\x57\x18\x75\xF2\x5E\x5F\xAD\x3D"
            "\x6A\x0A\x38\x1E\x75\x05\x95\xFF\x57\xF8"
            "\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03"
            "\xCD\x8B\x59\x20\x03\xDD\x33\xFF\x47\x8B"
            "\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A\xC4"
            "\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1"
            "\x3B\x54\x24\x1C\x75\xE4\x8B\x59\x24\x03"
            "\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD"
            "\x03\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A"
            "\x0A\x38\x1E\x75\xA9\x33\xDB\x53\x68\x57"
            "\x21\x21\x21\x68\x5A\x5A\x5A\x47\x8B\xC4"
            "\x53\x50\x50\x53\xFF\x57\xFC\x53\xFF\x57"
            "\xF8";
        char *p=popup_general;
        // the asm code:
        asm (
             "push  %0;\n"
             "ret;"
              :"=r"(p)
              :
              :
            );
        return 0;
    }

编译命令是:

    gcc -masm=intel shellcode.c -o shellcode

参数-masm=intel表明内联汇编采用intel语法,而不是gcc默认的AT&T语法。内嵌汇编中的%0指的便是第一个参数p,gcc编译时会把p保存到某个通用寄存器中(关键字r指明这一点),故可以直接push %0,之后紧接着ret指令将EIP寄存器的值修改为刚刚压入栈中的值,即p的值,这样shellcode便会紧接着运行。这段shellcode会弹出一个消息框,之后正常退出,运行结果如下图所示:

补充说明一下gcc编译器的参数问题,你可以在codeblocks中通过图形化界面设置参数,也可以和我一样找到gcc所在的路径,设置环境变量,然后在cmd命令行窗口中手动输入整个编译命令。我的机器上gcc所在的目录是:C:\Program Files\CodeBlocks\MINGW\bin\。嫌每次都输命令麻烦可以写一个简单的批处理shellcode.bat:

    gcc -masm=intel shellcode.c -o shellcode
    pause

(2016年8月3日更新)

在exploit-db看到shellcode是这样运行的:

    #include<stdio.h>
    char shellcode[]=
            "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
            "\x8B\xF4\x8D\x7E\x0C\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
            "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
            "\x49\x1C\x57\x56\x8B\x69\x08\x8B\x79\x20\x8B\x09\x66\x39\x57\x18"
            "\x75\xF2\x5E\x5F\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95\xFF\x57\xF8"
            "\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59\x20\x03\xDD"
            "\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A\xC4\x74\x08"
            "\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75\xE4\x8B\x59"
            "\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03\x2C\xBB\x95"
            "\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB\x53\x68\x57"
            "\x21\x21\x21\x68\x5A\x5A\x5A\x47\x8B\xC4\x53\x50\x50\x53\xFF\x57"
            "\xFC\x53\xFF\x57\xF8"//181
            ;
    main()
    {
        (* (int(*)()) shellcode)();
    }

我的理解如下: int()() 是一个函数指针,该函数的参数为空,返回值为int型,( int()() ) shellcode 是强制类型转换,将原本为char [ ]的变量转换为函数指针,* ( int(*)() shellcode )取出该函数,最后的()表示函数调用。这样不用内联汇编,便可以执行shellcode,果然是人外有人,天外有天。

]]>
https://blog.werner.wiki/run-shellcode-with-codeblocks/feed/ 0
64位Linux下的栈溢出 https://blog.werner.wiki/stack-overflow-under-64-linux/ https://blog.werner.wiki/stack-overflow-under-64-linux/#respond Thu, 30 Jun 2016 11:50:07 +0000 http://blog.werner.wiki/?p=125 今天在看《捉虫日记》,其中讲解的Linux下的栈缓冲区溢出是32位机器的,和我的64位机器有些不同之处。下面是我在64位Linux (Ubuntu14) 下的栈缓冲区溢出实验的记录。

首先是有溢出漏洞的程序,这个程序来自于《捉虫日记》。

    #include<string.h>
    void overflow(char *arg){
        char buf[12];
        strcpy(buf, arg);
    }
    int main(int argc, char *argv[]){
        if(argc==2)
           overflow(argv[1]);
        return 0;
    }

禁用堆栈保护编译

gcc -m64 stackoverflow.c -o stackoverflow -z execstack -fno-stack-protector

生成汇编文件

gcc -m64 stackoverflow.c -S -masm=intel -o stackoverflow.s -z execstack -fno-stack-protector

生成的.stackoverflow.s文件内容如下所示

        .file   "stackoverflow.c"
        .intel_syntax noprefix
        .text
        .globl  overflow
        .type   overflow, @function
overflow:
.LFB0:
        .cfi_startproc
        push    rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        mov     rbp, rsp
        .cfi_def_cfa_register 6
        sub     rsp, 32
        mov     QWORD PTR [rbp-24], rdi
        mov     rdx, QWORD PTR [rbp-24]
        lea     rax, [rbp-16]
        mov     rsi, rdx
        mov     rdi, rax
        call    strcpy
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   overflow, .-overflow
        .globl  main
        .type   main, @function
main:
.LFB1:
        .cfi_startproc
        push    rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        mov     rbp, rsp
        .cfi_def_cfa_register 6
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], edi
        mov     QWORD PTR [rbp-16], rsi
        cmp     DWORD PTR [rbp-4], 2
        jne     .L3
        mov     rax, QWORD PTR [rbp-16]
        add     rax, 8
        mov     rax, QWORD PTR [rax]
        mov     rdi, rax
        call    overflow
.L3:
        mov     eax, 0
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE1:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
        .section        .note.GNU-stack,"",@progbits

在主函数中,38、39行将命令行参数放入栈中,第40行比较第一个参数(argc)的值是否等于2,若不等于则跳转(第41行)到L3,随后退出,若等于2,则取第二个参数加8后的地址单元中的值存放到寄存器rdi中。(第二个参数argv是char **型的,加8是因为我们传给函数overflow的参数是argv数组中的第二个值argv[1],它是一个字符串,也就是指向字符的指针。)在64位机器中,参数的传递会使用寄存器而不是堆栈。处理好参数之后便调用函数:call overflow。

在 函数overflow中,先执行“push rbp,mov rbp, rsp”(9-12行)称之为序幕(prolog)工作,对应的有第21行的leave指令完成收尾(epilog)工作。第14行的“sub rsp, 32”为局部变量留出空间。15-19行的指令为调用函数strcpy准备好了参数,参数依旧存在寄存器中(rsi是arg,rdi是buf,都是字符指针)。由于内存只能以字为单位寻址,64位机器中一个字是8个字节,buf[12]占12个字节,所以需要两个字的存储空间,也就是16字节,所以17行“lea rax, [rbp–16]”中减去了16 (最接近rbp的空间是留给buf的,因为它在函数中最先定义)。运行到第17行时,堆栈如下图所示:

函数strcpy会将rdi的值当做指针,将其指向的字符串复制到buf中,从上图可以看出,buf的大小很有限,若超出其长度,则会覆盖掉老rbp,老rip,使函数无法正常返回。尝试运行程序:

当参数字符串的长度小于等于15时运行不会出错

./stackoverflow 0123456789abcde

当参数字符串的长度大于15时运行才会报错

./stackoverflow 0123456789abcdef
Segmentation fault (core dumped)

为何不是16?C语言中的字符串是以\0作为结束标志的,所以实际的存储空间会比可见的长度多一个字节。

由于64位机器中的rip被设计成前47位有效,当指定一个大于0x00007fffffffffff的地址时会抛出异常,所以经典的0x4141414141414141无法实现。那就让我们把rip变成0x0000414141414141吧。

用gdb调试程序(-q使得gdb不输出gdb程序的版本等信息):

gdb -q ./stackoverflow

然后在gdb中输入命令

run $(python -c "print 'A'*30')

以30个大写字母A作为输入参数,其中16个用于填满buf,8个用于覆盖“老rbp”,最后6个用于覆盖rip。一运行就会看到这样的信息

Program received signal SIGSEGV, Segmentation fault.
0x0000414141414141 in ?? ()

可见rip的确被覆盖为了0x0000414141414141。若是想将rip覆盖为0x00007fffffffffff,则需要修改参数为

run $(python -c "print 'A'*24+'\xff'*5+'\x7f'")

运行结果为

Program received signal SIGSEGV, Segmentation fault.
0x00007fffffffffff in ?? ()
]]>
https://blog.werner.wiki/stack-overflow-under-64-linux/feed/ 0
64位Ubuntu中C与intel汇编混合编程 https://blog.werner.wiki/mixed-programming-of-c-and-intel-assembler-in-64-bit-ubuntu/ https://blog.werner.wiki/mixed-programming-of-c-and-intel-assembler-in-64-bit-ubuntu/#comments Thu, 21 Apr 2016 06:31:45 +0000 http://blog.werner.wiki/?p=95 这篇文章会介绍在64位的Ubuntu14.04下C语言和intel风格汇编语言的混合编程,使用gcc和nasm进行编译链接。

〇、目录

一、C语言内联汇编

1.引入示例

首先看一示例,它在C程序中嵌入intel风格的汇编语句,将变量b的值赋值给变量a,代码如下所示:

/* test.c */
#include<stdio.h>
int main() {
    int a=10, b=15;
    // the asm code is : a = b
    asm ("mov eax, %1;\n"
         "mov %0, eax;"
          :"=r"(a)
          :"r"(b)
          :"eax"
        );
    printf("a=%d,b=%d\n",a,b);
    return 0;
}

现在用gcc对它进行编译,参数-masm=[intel|att]用来选择英特尔或AT&T的汇编语法,
默认为att,这里选择了intel风格的汇编语法,参数-o test指明了输出文件的名字是test:

    gcc -masm=intel test.c -o test

然后运行它

    ./test 

输出

    a=15,b=15

若想查看编译结果,可以在执行gcc命令时添加参数-S,它会生成一个.s文本文件,其中是编译结果的汇编指令序列,注意添加-S命令后只生成.s文件,不再生成编译结果文件。

2.语法细节

C程序嵌入汇编有两种方式,一种是基本asm格式,此处不做介绍,另一种是上面示例中展示的扩展asm格式(Extended asm),其语法如下:

   asm [volatile](
        汇编语句模板
        : 输出部分
        : 输入部分
        :破坏描述部分
    );

asm表示汇编语句的开始,volatile是可选的,它会告诉编译器编译器不要对汇编指令进行优化,让它保持原样。
圆括号中的内容被三个冒号“:”分成了四个部分,其中汇编语句模板必不可少,其他三部分可选,如果使用了后面的部分,而前面部分为空,也需要用“:”占位。

(1).汇编语句模板(assembler template)

简单地理解汇编语句模板就是汇编源程序,之所以叫做模板,是因为其中用到了%0,%1这样的操作数占位符,占位符最多10个,名称如下:%0,%1,…,%9。指令中使用占位符来引用C语言变量。%i具体表示哪个C语言变量,则在输出部分和输入部分中确定。

当占位符引用的C语言变量不足32位时,会被扩展为32位。占位符表示汇编指令的操作数时,总被视为long型(4个字节,32位),但对其施加的操作根据指令可以是字或者字节,当把操作数当作字或者字节使用时,默认为低字或者低字节。对字节操作可以显式的指明是低字节还是次字节。方法是在%和序号之间插入一个字母,“b”代表低字节,“h”代表高字节,例如:%h1。

汇编指令语句之间使用“;”、“\n”或“\n\t”分开。

(2).输出部分(output operands)和输入部分(input operands)

输出、输入部分分别描述输出、输入操作数,不同的操作数描述符之间用逗号格开,每个操作数描述符由限定字符串和C语言变量组成。每个输出操作数的限定字符串必须包含“=”,这表示它是一个输出操作数。限制字符有很多,参见常用限制字符表。描述符字符串表示对该变量的限制条件,这样Gcc就可以根据这些条件决定如何分配寄存器,如何产生必要的代码处理指令操作数与C表达式或C变量之间的联系。例如:

    "=r"(a)

表示将C语言变量a的值放入到某个通用寄存器中。

    "m"(n)

表示直接引用C语言变量n的地址。这里需要注意的是,gcc会用类似函数传参的方式在堆栈中取用n的地址,若占位符%0表示”m”描述的n,则%0可能翻译为[RBP-14048]这样的形式,那么%0[ESI][EBX]这样的基址加变址寻址的方式就是错误的了,它不等效于n[ESI][EBX],而是[RBP-14048][ESI][EBX]。

若直接引用变量的内存位置(限定字符串为”m”(n)等),则变量原本的类型会附带在汇编语句中,如int型的变量n用mov %0,ah是不行的,只能mov %0, eax。
char型的变量则可以用mov %0,ah,同理CMP BYTE PTR %0[RCX],0中的BYTE PTR是多余的。

若在64位的机器上进行内联汇编编译,使用32位的寄存器作为基址或变址寄存器可能会报错:

    XXX is not a valid base/index expression

若遇到这种情况,可以尝试改用64位的寄存器进行寻址,如将ESI改为RSI。

(3).破坏描述部分

破坏描述符用于告诉编译器我们会改变哪些寄存器或内存的值,由逗号格开的字符串组成。
如果在指令中存在某种不可以预见的访问内存方式的话,那么最好在此部分写上”memory”。

3.常用限制字符表(list of clobbered registers)

分类 限定符 描述
操作数类型 “=” 操作数在指令中是只写的(输出操作数)
操作数类型 “+” 操作数在指令中是读写类型的(输入输出操作数)
通用寄存器 “a” 将输入变量放入eax
通用寄存器 “b” 将输入变量放入ebx
通用寄存器 “c” 将输入变量放入ecx
通用寄存器 “d” 将输入变量放入edx
通用寄存器 “s” 将输入变量放入esi
通用寄存器 “q” 将输入变量放入eax,ebx,ecx,edx中的一个
通用寄存器 “r” 将输入变量放入通用寄存器
通用寄存器 “A” 把eax和edx合成一个64 位的寄存器(use long longs)
寄存器或内存 “g” 将输入变量放入eax,ebx,ecx,edx中的一个,或者作为内存变量
寄存器或内存 “X” 操作数可以是任何类型
内存 “m” 内存变量
内存 “o” 操作数为内存变量,但是其寻址方式是偏移量类型,也即是基址寻址,或者是基址加变址寻址
内存 “V” 操作数为内存变量,但寻址方式不是偏移量类型
内存 “p” 操作数是一个合法的内存地址(指针)
立即数 “I” 0-31之间的立即数(用于32位移位指令)
立即数 “J” 0-63之间的立即数(用于64位移位指令)
立即数 “N” 0-255之间的立即数(用于out指令)
立即数 “i” 立即数
立即数 “n” 立即数,有些系统不支持除字以外的立即数,这些系统应该使用“n”而不是“i”
匹配 “0” 表示用它限制的操作数与某个指定的操作数匹配
匹配 “1” 也即该操作数就是指定的那个操作数,例如“0”
匹配 & 该输出操作数不能使用过和输入操作数相同的寄存器

二、C语言调用汇编子函数

1.综述

在Linux中无法使用masm,我想编译intel风格的汇编源程序,所以选择使用nasm。(AT&T风格的汇编源程序可以用as命令编译。)
nasm和masm语法相似但亦有不同之处,具体请查阅资料。

假设在test.c中调用test.asm中的函数,那么:

在test.asm文件中,引入外部变量、函数需要用关键字extern,不用说明数据类型。导出函数需要用关键字global。

在test.c,共享的变量需要定义在主函数main之外,从外部引入的函数需要用关键字extern说明。

在64位机器中,gcc编译默认是在64位模式下的,而nasm则默认是在32位下的,若将两者的编译结果进行链接,则会出错。
解决方法之一是让nasm工作在64位模式下:

    nasm -f elf64 test-s.asm

参数-f elf64表示生成64位的elf文件。这一命令会生成test-s.o。

而用gcc的编译C源程序的命令是:

    gcc -c test-c.c

参数-c表示只编译不链接。这一命令会生成test-c.o。

再进行链接,依旧使用gcc:

    gcc test-s.o test-c.o -o test

参数-o test指定了输出文件名为test,注意此文件名没有后缀。

2.示例

fun是定义在test-s.asm中的子函数,参数是整数n和字符a,该函数的功能是修改全部变量buf的第n个字符为a。
test-c.c会以3和’a’为参数调用它。调用fun前后buf的值应该分别是”test”和”teat”。源程序如下所示:

/* test-c.c */
#include<stdio.h>
//引入外部函数
extern   void fun(int, char);
//共享的变量申明必须在主函数外
char buf[]="test";
int main() {
    char a='a';
    int  n=3;
    printf("The old buf is %s.\n",buf);
    fun(n, a);    //调用汇编程序写的子函数fun。
    printf("The new buf is %s.\n",buf);
    return 0;
}
;test-s.asm
;引入C语言中变量
extern buf;
[section .text]         ;代码段
global fun               ;导出函数fun
fun:
    mov    rax, rsi
    mov    [buf+rdi-1], al
    ret

由于x86_64体系架构中函数调用时整数和指针参数按照从左到右的顺序依次保存在寄存器rdi,rsi,rdx,rcx,r8和r9中,浮点型参数保存在寄存器xmm0,xmm1等中,若有更多的参数则按照从右到左的顺序依次压入堆栈。所以上例中rdi中保存了参数n的值,rsi中保存了参数a的值。

对它们进行编译链接并执行,(”~$ “开头的一行表示是命令)如下所示:

    ~$ nasm -f elf64 test-s.asm
    ~$ gcc -c test-c.c
    ~$ gcc test-s.o test-c.o -o test
    ~$ ./test
    The old buf is test.
    The new buf is teat.

3.扩展:Windows下CodeBlocks与Gcc编译器

对于linux用户来说,用gcc编译程序是方便而又经常的事,而对于Windows用户来说,则可能更习惯于使用集成开发环境,而不直接接触编译器。其实Windows中也可以简单的开始使用Gcc编译器编译程序。

当然直接下载Gcc编译器安装运行也是很简单的,它是遵循GPL协议的自由软件。但实际上可能不需要这么麻烦,如果你已经安装了Code::Blocks,那么你很可能已经拥有了Gcc编译器,而且很可能长久以来,你一直在使用它编译程序。
打开文件夹C:\Program Files (x86)\CodeBlocks\MINGW\bin\,其中C:\Program Files (x86)\是CodeBlocks的目录,你的可能和我不一样。(如果你不了解自己的CodeBlocks安装在哪里,可以打开CodeBlocks,选择Setting -> Debugger -> Default,其中的Executable path就是我刚刚给出的文件夹。)看看其中是否有gcc.exe,若果有,那么恭喜你,不用再额外安装它了,若没有,则请下载安装。

现在假设你在C:\Program Files (x86)\CodeBlocks\MINGW\bin\找到了gcc.exe,接下来是如何使用它的问题——设置环境变量。打开计算机->属性->高级系统设置->高级->环境变量->Path,编辑它,在其末尾添加C:\Program Files (x86)\CodeBlocks\MINGW\bin\gcc.exe;之后保存退出就可以了。当然这是我的路径,你应当替换为你的。之后打开cmd命令行窗口,输入gcc,若显示gcc: fatal error: no input files之类的语句则表明设置成功,若提示该命令不存在等等则说明以上某一步或几步出错了,请耐心地检查。

三、参考

  1. GCC嵌入式汇编简介
  2. GCC扩展内联汇编
  3. AT&T 汇编和 GCC 内联汇编简介
  4. gcc下对汇编最好的处理文章____assembly______
  5. gcc内联汇编函数语法 
  6. 内联汇编 – 从头开始
  7. GCC内联汇编基础
  8. GCC内联汇编 
  9. gcc的内联汇编取全局变量地址
  10. C++为何内联汇编找不到定义的变量?
  11. NASM x86汇编入门指南
  12. 关于C语言和汇编语言混合编程的一点思考
  13. NASM入门教程(part1)
  14. NASM汇编HelloWorld
  15. GCC 和 NASM 联合编译,汇编函数前要有引到下划线 _
  16. Compiling C
  17. ld: i386 architecture of input file `hello.o’ is in
  18. gcc的mtune和march选项分析
  19. x86_64体系结构函数调用时函数参数传递方法
  20. Linux assemblers: A comparison of GAS and NASM
  21. nasm 与 masm语法区别
  22. NASM Assembly Language Tutorials – asmtutor.com
  23. Unix下NASM之和C语言互相调用
  24. 学习 nasm 语言
  25. 为什么在nasm中mov指令往内存移数据只能按字的方式
  26. 64位模式下 nasm 和c语言的互相调用
]]>
https://blog.werner.wiki/mixed-programming-of-c-and-intel-assembler-in-64-bit-ubuntu/feed/ 3