首页
社区
课程
招聘
[原创]内联汇编
发表于: 2007-2-11 15:27 11135

[原创]内联汇编

2007-2-11 15:27
11135

有很多人认为汇编已经失去了用武之地,包括曾经的我。我用的是visual c++6.0。现在我要问你一个问题。
a=10,b=20。你现在要使a和b的值交换。你有什么办法。这是教科书上说得很多的方法。第一种方法:
#include <iostream.h>
void main()
{
        int a,b,c;
        a=10;
        b=20;
        c=a;
a=b;
b=c;
        cout<<“a=”<<a<<“,b=”<<b;
}
输出当然是a=20,b=10
该方法引入了一个新的变量,即多开辟了一个DWORD的空间。现在请你再看我的实现方法。第二种方法:(把加了下划线的部分改成如下代码)
_asm
        {
                push a
                push b
                pop a
                pop b
        }
我稍微解释一下:_asm告诉编译器,我要使用内联汇编代码了。换成__asm也一样(注意这里有两根英文半角下划线,前面的是一条)。上面的那段代码也等价于
_asm  push a
_asm  push b
_asm  pop a
_asm  pop b
看来还是用花括号{}来得方便。
上述,我在c++中使用了内联汇编,说实话,这种方法比第一种方法来得差。因为我的两个push就开辟了2个DWORD空间。
有没有办法不开辟额外的空间呢?请看第三种实现方法:
        _asm
        {
                mov eax,a
                mov ebx,b
                xchg eax,ebx
                mov a,eax
                mov b,ebx
        }
在听取下面的朋友的批评之后,上面这段代码不免有脱了裤子放屁之嫌,应改为
_asm
        {
                mov eax,a
                xchg eax,b
                mov a,eax
        }
这样看起来就比较好了。因为第一段代码执行的时候也要先把值移动到寄存器中。
要说内联汇编最重要的作用就是在写溢出代码和注册机中。如果你有志于学这些东西,那么下面的文章你要好好看了。可惜的是内联汇编不是宏汇编,一些伪指令内联汇编中是不能用的,比如说
.if
.elseif
.endif
虽然在很多时候写代码不方便了,但是勉强还行。
很重要的一点:一般来说,在_asm块开始的时候,你不应该假定某个寄存器中包含着值。也就是说所有的寄存器都是可用的。当然不指eip,esp,ebp用这些寄存器用得不好你会死得很难看,是cracker都知道这三个寄存器的作用吧!还要注意PUSH,POP配对。这是为了堆栈平衡。
现在基本上我们迈出了学习内联汇编的第一步,也是一大步。剩下的都只是一些细节了。
现在我要编写一个函数。
int cmpare(int a,int b)
{
         _asm
         {
                 mov eax,a
             cmp eax,b
                 jge line1;都是因为伪指令不能用,否则这里肯定是用.if伪指令更容易看懂。
                 mov eax,b
line1:
         }
}
我写这个函数的目的是为了告诉大家整数在默认情况下是采用eax来作为返回值的,还有就是在内联汇编中推荐用汇编自己的注释符号“;”,虽然说c++的注释符号也可以使用。这里我的a=10,b=20。虽然在编译时c++提示我如图1


图1

但是真正执行cout<<cmpare(10,20)输出的是20。另外浮点通过st(0)返回值。
sz[1]= 3;
        _asm
        {
                mov eax,sz[1]
                mov a,eax
        }
                cout<<a;
你知道这将会输出什么吗?你绝对意想不到,我告诉你,在我这里是63753420。是不是很迷惑。当然开始的时候我也很迷糊,咱们先把mov eax,sz[1]改成mov eax,sz[4]然后再编译,看现在已经达到了我们预想的情况a=3。至于为什么吗?原因是:[]是c++和汇编共同包含的操作符,会被编译成汇编的操作符。而一个int是4字节,所以我们的代码应该是sz[4]而不是sz[1],后面的下标实际上是起着寻址的作用。前面的数组名sz是起着基地址的作用。如果不好理解你应该用OD调试看看。为了简便我们的写法。我觉得我们可以这样操作数组:
sz[xl*lx]。
lx是我们先前就定义的各种类型常量,xl是序列,即一般的下标值。这个类型的常量我们可以用
_asm       
        {
                mov eax,type sz;sz是数组或者变量名
                mov a,eax
        }
        cout<<a;
这样就可以输出这种数据的每个数据所占的大小了,单位是byte。
内联汇编学习的路还很远,大家要努力学习。
学习汇编,最常用的东西除了寄存器就是指针了,在汇编中指针的反映就是使用[ address]。
不懂事的程序员往往很容易写出这样的代码:
_asm
        {
                lea eax,a
                mov b,[eax]
        }
这当然是错误的,这反映为对汇编代码知识不是很了解。因为mov指令是不可以这样使用的
mov m32,m32;m32表示32位储存器。所以只是应该改成下面这样。
_asm
        {
                lea eax,a
                mov ebx,[eax]
                mov b,ebx
        }
这样a的值才顺利到达了变量b这里。下面再举几个例子,只是希望对一些汇编代码不太熟悉的小鸟一些帮助。我们继续。
a=0x120;
_asm
        {
                lea ebx,a
                movzx eax, byte ptr [ebx]
                mov b,eax
        }
cout<<b;
b等于多少?知道吗?如果你说的是32,那么你很聪明,汇编学得还不错。如果你说的是48,那么你已经把整数变量和字符串混起来了,在ASCII码表中0的表示就是48。如果你说的是1,那么你应该还不懂汇编。
在内存中ebx指向的内存应该是20 01。因为所有的东西在内存中都是反向存储的。在计算机运算的时候就是01 20。在出一道练习题
在内存中20 30 04这个值是多少?答案:04 30 20。
这里我们要特别注意的是在写注册机时,你得先知道,你输入的值,它是在当整数计算,还是字符串计算。通常它取得到的都是字符串,有些软件会把字符串转换成整数。
这么点东西,我学习了一天啊!惭愧,惭愧……
最后夸奖自己一句,这篇文章真的很好,很难找到这么浅显易懂的文章啊!^_^


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (19)
雪    币: 325
活跃值: (97)
能力值: ( LV13,RANK:530 )
在线值:
发帖
回帖
粉丝
2
最初由 kflnig 发布
有很多人认为汇编已经失去了用武之地,包括曾经的我。我用的是visual c++6.0。现在我要问你一个问题。
a=10,b=20。你现在要使a和b的值交换。你有什么办法。这是教科书上说得很多的方法。第一种方法:
#include <iostream.h>
void main()
{
........


我只说“你现在要使a和b的值交换”
千万不要小视编译器优化。。具体你用Release版编译一下就知道了
另外MSVCRT也有库函数可以用:swap。
Swaps bytes.
void _swab(
   char *src,
   char *dest,
   int n
);
2007-2-11 15:51
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
3
Good,学习,
经验都是心血耗来的,
谢谢分享
2007-2-11 16:08
0
雪    币: 209
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
我觉得VC的优化是做的不错,如果是为提高效率而使用内联汇编,基本上没有必要。

调过几个Borland C++ 1999的程序,垃圾代码不是一般的多
2007-2-11 16:24
0
雪    币: 615
活跃值: (1222)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
谢谢分析经验.
2007-2-11 16:56
0
雪    币: 322
活跃值: (56)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
6

a=0x120;
_asm
{
lea ebx,a
movzx eax, byte ptr [ebx]
mov b,eax
}
cout<<b;
b等于多少?知道吗?
........


mov b,eax 楼主这个不是一样的错误么?第一个操作数必须是寄存器
2007-2-11 19:57
0
雪    币: 222
活跃值: (10)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
7
_asm
  {
    mov eax,a
    mov ebx,b
    xchg eax,ebx  //用西裤哥的话来说,这叫脱了裤子打屁
    mov a,eax
    mov b,ebx
  }

不知道这么写是否为教学意图?
2007-2-11 20:41
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
8
楼上的指出。。。。。。。
2007-2-11 21:37
0
雪    币: 146
活跃值: (33)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
9
楼主的文章确实很好,适合我们菜鸟.向楼主致敬
这样写不知道会如何编译.
a=a+b
b=a-b
a=a-b
2007-2-11 21:44
0
雪    币: 214
活跃值: (70)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
10
很好!理论+实践的文章~受教了!
2007-2-12 01:45
0
雪    币: 172
活跃值: (212)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
sz[1]= 3;
  _asm
  {
    mov eax,sz[1]
    mov a,eax
  }
    cout<<a;
这里无论我mov eax,sx
  • 都无法输出正确结果,不知道为什么?
  • 另外请教:
    >另外浮点通过st(0)返回值。
    如何在C中实现?
    2007-2-12 02:44
    0
    雪    币: 405
    活跃值: (10)
    能力值: ( LV9,RANK:1130 )
    在线值:
    发帖
    回帖
    粉丝
    12
    “最后夸奖自己一句,这篇文章真的很好,很难找到这么浅显易懂的文章啊!^_^”
    我也这么认为,
    2007-2-12 09:34
    0
    雪    币: 263
    活跃值: (10)
    能力值: ( LV9,RANK:210 )
    在线值:
    发帖
    回帖
    粉丝
    13
    最初由 binbinbin 发布
    “最后夸奖自己一句,这篇文章真的很好,很难找到这么浅显易懂的文章啊!^_^”
    我也这么认为,


    同上
    2007-2-12 10:28
    0
    雪    币: 101
    活跃值: (12)
    能力值: ( LV12,RANK:210 )
    在线值:
    发帖
    回帖
    粉丝
    14
    asm
      {
        mov eax,a
        mov ebx,b
        xchg eax,ebx  //用西裤哥的话来说,这叫脱了裤子打屁
        mov a,eax
        mov b,ebx
      }
    楼上的指出。。。。。。。

    ===========
    教学的话, 可以这么写

    asm
      {
        mov eax,a
        xchg eax,b
        mov a,eax
      }
    2007-2-12 14:29
    0
    雪    币: 101
    活跃值: (12)
    能力值: ( LV12,RANK:210 )
    在线值:
    发帖
    回帖
    粉丝
    15
    最初由 fonge 发布
    同上


    楼主写完了以后, 有没有握着自己的右手说一声耶。
    2007-2-12 14:30
    0
    雪    币: 623
    活跃值: (10)
    能力值: ( LV9,RANK:170 )
    在线值:
    发帖
    回帖
    粉丝
    16
    最初由 仙剑太郎 发布
    很好!理论+实践的文章~受教了!


    同意
    2007-2-12 17:52
    0
    雪    币: 208
    活跃值: (10)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    17
    不错的好文章啊
    2007-2-20 15:58
    0
    雪    币: 314
    活跃值: (10)
    能力值: ( LV12,RANK:570 )
    在线值:
    发帖
    回帖
    粉丝
    18
    内联汇编补遗
    kflnig狂枫
        上次写了一篇内联汇编,但是有点不周全的地方,所以写了这篇补遗。
        针对大家的批评,尤其是我那个XCHG的使用,简直是脱了裤子放屁,自己汇编功底差,一时误记,在此向大家道歉。
        mov ebx,dword ptr[eax]简单点吧,mov ebx,[eax],64bit的机器我不敢说正确,但是32bit的机器中还是后一种写法更加简单,因为指针指向的值,32bit中默认就当作dword类型了。
        结构类型,也得说一下。这个东西还是很头痛的,没有汇编写过程序的一般人是不会知道的。Yonsm 大哥的内联汇编教程中有提到。这里我再补充一下。
    #include <iostream.h>
    struct first
           {
               int a;
           };
          struct first str;
    void main()
    {      
    str.a=10;
          __asm
           {
               MOV EBX, OFFSET str
               MOV [EBX]str.a,20
           }
             cout<<str.a;
    }
         注意定义str结构类型的位置。
    Yonsm 大哥的内联汇编教程说得对,都是非常对的,可是他功力太高,忘了提醒我们一点OFFSET的特点。
        OFFSET不能用于获取局部变量的地址,只是由于OFFSET伪指令确定偏移量是在编译时预先确定好的,对局部变量在编译时是不确定的,它与堆栈寄存器的取值有关,而堆栈寄存器的取值随着执行环境的不同而不同。所以在声明struct变量的时候应该放在开头,应该是全局变量,大家注意核实,我不想误导你。对c语言这种很拗口的语法,我也并不是很清楚。当然还有一类人是不看上文也绝对不会写错的。懒人。
       因为懒人通常是
    struct first
           {
               int a;
           }str;
        这么写的。这样也可以。
        现在明白了吧!我只是提醒这个,Yonsm 大哥提到的,可以省略些代码的写法,你不妨自己去看看他的教程。
    若你看过crack7中的《Thinking in ASM 》,那么你不妨再看看我的。
    他们都在大谈swap(a,b)之后a,b的值有没有换过。我来一个稍微变态的,也作为对使用内联汇编者的提醒,小心这种情况。
    #include <iostream.h>
    void swap(int &a, int &b)
    {
            __asm
            {
            push a
            push b
            pop a
            pop b
            }
    }
    void main()
    {
            int a,b;
            cin>>a>>b;
            swap(a,b);
            cout<<a1<<b1;
    }
    答案当然是没有啦。
    2007-2-24 16:51
    0
    雪    币: 6075
    活跃值: (2236)
    能力值: (RANK:1060 )
    在线值:
    发帖
    回帖
    粉丝
    19
    好像优化newbie手册。。。

    一个原则是最好不要使用堆栈,时空都耗费很厉害
    找一个reg来交换吧。。。
    不过编译器会干的,研究这种优化上有意义吗

    这些其实就是手熟而已,经常用asm作开发的都知道。但要他们研究brainfuck可能会晕
    2007-2-28 22:09
    0
    雪    币: 314
    活跃值: (10)
    能力值: ( LV12,RANK:570 )
    在线值:
    发帖
    回帖
    粉丝
    20
    内联汇编再补遗[大结局]
    kflnig中文名:狂枫
        声明:此文或许会有多处错误,大家请踊跃发言!提出错误!本文基于WINXP,VC6.0调试通过。
            一篇内联汇编产生了这么多的补遗,^_^如果你内联汇编一般,看了这篇文章那么肯定吓一跳。因为我本来的题目是7474742。就是气死气死气死你!它早就把我气死过一回了。我的目标就是把你搞混到不敢学内联汇编!^_^
    如果你对自己的内联汇编很有信心,那么看下去。
    说点不重要的。也是复习一下。
    void main()
    {
            int a[10];
            a[0]=10;
            a[1]=20;
            __asm
            {
                    mov eax,a[0]
                    add eax,a[4]
                    mov a[0],eax
            }
            cout<<a[0];
    }
    我在第一篇《内联汇编》中已经说过。这样是输出a[0]+a[1]。
    我们再来看。法一:
    void main()
    {
            int bl1,bl2;
            bl1=10;
            bl2=20;
            __asm
            {
                    mov eax,bl1
                    xchg eax,bl2
                    mov bl1,eax
            }
            cout<<bl1<<bl2;
    }
    这个更加简单了就是bl1和bl2交换。那么法二:
    void main()
    {
            int bl1,bl2;
            bl1=10;
            bl2=20;
            __asm
            {
                    lea eax,bl1
                    mov ebx,[eax]

                    lea ecx,bl2
                    mov edx,[ecx]

                    xchg ebx,edx

                    mov [ecx],edx
                    mov [eax],ebx
            }
            cout<<bl1<<bl2;
    }
    上面的代码毫无优化可言,我只是为了对照c++源码的翻译式写法。也是bl1,bl2的值相互交换。
    从代码的高级程度无疑是法一高得多。但是,我推荐小鸟牢牢掌握法二。要问我为什么,先等等,待会儿说!
    我们知道
    void swap(int &a,int &b)
    {
    __asm{
                    push  a
                    push  b
                    pop   a
                    pop   b
                    }
    }
    这样执行
    c=10;
    d=20;
    swap(c,d);
    c依旧是10而d依旧是20。我没有什么好办法只好来一个笨点的。
    void swap(int &a,int &b)
    {
            int a1,b1;
            a1=a;
            b1=b;
            __asm
            {
                    mov eax,a1
                    xchg eax,b1
                    mov b1,eax
            }
            a=a1;
            b=b1;       
    }
    言归正传。
    void add(int c[10],int d[10])
    {
            __asm
            {
                    mov eax,c[0]
                    add eax,d[0]
                    mov c[0],eax
            }
            cout<<c[0];
    }
    void swap(int c[10],int d[10])
    {
            __asm
            {
                    mov eax,c[0]
                    xchg eax,d[0]
                    mov c[0],eax
            }
            cout<<c[0]<<d[0];
    }
    void main()
    {
            int a[10],b[10],bl1,bl2;
            a[0]=10;
            b[0]=20;
            swap(a,b);
    }
    这样是对的输出20,10。但是假如把swap(a,b);改成add(a,b);那么只会跳出一个丑陋的对话框。因为swap(a,b)没有问题,所以原因不再其它而在add函数自己内部的__asm中。这就是传数组,这个很特别的特例。我们在add函数里操纵的是数组的地址。如果你要add那么用类法二的方法吧!
    void add(int c[10],int d[10])
    {
            __asm
            {
                    mov eax,c[0]
                    mov ebx,[eax]

                    mov ecx,d[0]
                    add ebx,[ecx]

                    mov [eax],ebx
            }
            cout<<c[0];
    }
    这样就不会有丑陋的对话框了。以后不要再把两个地址加起来了。在函数中处理传入的数组尤其小心!所以上面的swap(a,b)其实是很高级的交换c[0],d[0]的地址。
    好了我的《内联汇编》教程(共三篇)结束了。写得很详细。相信大家的内联汇编技术也很强了。我也不弱^_^自己多用才是可以提高!
    这是我在寒假里给大家写的最后一篇文章了。再见!
    最后我来布置一道终极考试题目a=10;这用内联汇编怎么写。
    这是一题怎么写都不会错的。比如
    __asm
    {
    mov                eax,10
    mov          a,eax
    }
    或者
    _asm
    {
                    mov                eax,10
                    mov         [a],eax
    }
    都对!不要问我为什么,我也不知道,这是VC的编译器这么说的。
    所以我们可以知道,假如是这样:
    void add(int c,int d)
    {
            __asm
            {
                    mov eax,c
                    add eax,d
                    mov c,eax
            }
            cout<<c;
    }
    这个add函数可以安全执行。我相信此刻你已被内联汇编搞混了头脑!到底该怎么用,还是自己去试验然后总结吧!
    2007-3-14 20:26
    0
    游客
    登录 | 注册 方可回帖
    返回
    //