`
lobin
  • 浏览: 378952 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

C 第2500章: 内嵌汇编

阅读更多

汇编语言是一种最接近机器语言的编程语言。

 

有关汇编可参考另一篇文章:https://lobin.iteye.com/blog/2442219

 

C是一种比较古老的强大的高级语言。在C语言中还可以内嵌汇编以实现更高级的功能。

 

内嵌汇编语法

在编写c程序时,在不同的平台下,选择不同的编译器,在内嵌汇编时的内嵌语法也不一样。如在VC下编写c程序时内嵌汇编语法:

_asm mov eax, edx;

完整代码:

#include<stdio.h>
void main() 
{
  _asm mov eax, edx;
}

如果是在gcc下编写c程序时内嵌汇编语法:

asm("mov %edx, %eax");

完整代码:

#include<stdio.h>
void main() 
{
  asm("mov %edx, %eax");
}

上面是一个最简单的内嵌汇编的例子,内嵌语法上是不一样的,在这个例子中,虽然简单,但却是个很实用的例子,在调用汇编时,通常会将返回结果存在ax或eax中,调用结束后我们可以从ax或eax中得到调用结果。

 

VC下内嵌汇编语法

 

GCC下内嵌汇编语法

 

在后面给出的例子中,可能是在VC下内嵌汇编,也可能是在GCC下内嵌汇编的例子。这个是很容易区分的。

 

过程

汇编中的过程定义,下面是一个在MASM下的过程定义的例子

_test	PROC NEAR
	push	ebp
	mov	ebp, esp

        ... ...

	pop	ebp
	ret	0
_test	ENDP

我们在编写C程序是,也可以编译查看对应的汇编程序代码。C程序中的函数在编译后对应汇编中的过程。如以下C程序

void test()
{

}

对应的汇编程序代码:

	TITLE	procedure_test0.c
	.386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT	SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT	ENDS
_DATA	SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA	ENDS
CONST	SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST	ENDS
_BSS	SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS	ENDS
_TLS	SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS	ENDS
FLAT	GROUP _DATA, CONST, _BSS
	ASSUME	CS: FLAT, DS: FLAT, SS: FLAT
endif
PUBLIC	_test
_TEXT	SEGMENT
_test	PROC NEAR
; File procedure_test0.c
; Line 3
	push	ebp
	mov	ebp, esp
; Line 5
	pop	ebp
	ret	0
_test	ENDP
_TEXT	ENDS
END

其中有一个汇编过程(_test)

_test	PROC NEAR
	push	ebp
	mov	ebp, esp

	pop	ebp
	ret	0
_test	ENDP

该过程对应C程序中的test函数。

上面的例子如果是在Cygwin GCC下,对应的汇编程序代码:

 

	.file	"procedure_test0.c"
	.text
.globl _test
	.def	_test;	.scl	2;	.type	32;	.endef
_test:
	pushl	%ebp
	movl	%esp, %ebp
	popl	%ebp
	ret

如果是在linux环境下,GCC编译后对应的汇编程序代码和在Cygwin下有些不同:

 

        .file   "procedure_test0.c"
        .text
.globl test
        .type   test, @function
test:
        pushl   %ebp
        movl    %esp, %ebp
        leave
        ret
        .size   test, .-test
        .section        .note.GNU-stack,"",@progbits
        .ident  "GCC: (GNU) 3.4.6 20060404 (Red Hat 3.4.6-9)"

 

 

调用

 

参数传递

 

返回值

 

返回地址

 

堆栈平衡

堆栈平衡可参考另一篇文章:https://lobin.iteye.com/blog/2441310

 

C调用汇编

 

汇编调用C

 

 

VC下C内嵌汇编

 

下面是个简单的c函数,求两个数的和:

int add(int a, int b) 
{
  return a + b;
}

void test_add() 
{
  int result = add(3, 4);
}

void main() 
{
  int result = add(3, 4);
}

我们查看下对应的汇编代码,通过以下命令生成对应的汇编指令:

 

这里生成的汇编源代码文件是MASM的汇编源代码

写道
>cl /c /Faaddtest.s addtest.c /Foaddtest.obj

 

汇编源代码(addtest.s):

	TITLE	addtest.c
	.386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT	SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT	ENDS
_DATA	SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA	ENDS
CONST	SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST	ENDS
_BSS	SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS	ENDS
_TLS	SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS	ENDS
FLAT	GROUP _DATA, CONST, _BSS
	ASSUME	CS: FLAT, DS: FLAT, SS: FLAT
endif
PUBLIC	_add
_TEXT	SEGMENT
_a$ = 8
_b$ = 12
_add	PROC NEAR
; File addtest.c
; Line 2
	push	ebp
	mov	ebp, esp
; Line 3
	mov	eax, DWORD PTR _a$[ebp]
	add	eax, DWORD PTR _b$[ebp]
; Line 4
	pop	ebp
	ret	0
_add	ENDP
_TEXT	ENDS
PUBLIC	_test_add
_TEXT	SEGMENT
_result$ = -4
_test_add PROC NEAR
; Line 7
	push	ebp
	mov	ebp, esp
	push	ecx
; Line 8
	push	4
	push	3
	call	_add
	add	esp, 8
	mov	DWORD PTR _result$[ebp], eax
; Line 9
	mov	esp, ebp
	pop	ebp
	ret	0
_test_add ENDP
_TEXT	ENDS
PUBLIC	_main
_TEXT	SEGMENT
_result$ = -4
_main	PROC NEAR
; Line 12
	push	ebp
	mov	ebp, esp
	push	ecx
; Line 13
	push	4
	push	3
	call	_add
	add	esp, 8
	mov	DWORD PTR _result$[ebp], eax
; Line 14
	mov	esp, ebp
	pop	ebp
	ret	0
_main	ENDP
_TEXT	ENDS
END

 

C嵌入汇编调用C函数

C嵌入汇编代码中可以通过call指令调用C函数。

#include<stdio.h>

void test()
{
  printf("fn test called\n");
}

void main() 
{
  __asm call test;
}

上面是通过call指令调用C函数,在汇编中我们还可以通过jmp指令来调用汇编过程,只需要在汇编过程实现和调用时维持好堆栈调用平衡。当然,也不一定非得维持好堆栈调用平衡,但最好维持平衡。所以这里也可以通过jmp指令来调用C函数。

 

在下面的例子中,通过jmp指令跳转到函数调用时,为了维持堆栈调用平衡,需要将调用返回后的指令地址压入栈,以便在函数调用返回时能够返回到调用时的下一条指令。

#include<stdio.h>

void test()
{
  printf("fn test called\n");
}

void main() 
{
  __asm {
    push offset __RET;
	jmp test;
__RET:
	; blank
  }
}

 

 

C内嵌汇编定义汇编Procedure,并在内嵌汇编中调用这个汇编Procedure。

汇编Procedure的定义,类似MASM中汇编Procedure的定义:

 

_TEXT	SEGMENT
_sum	PROC NEAR
        push	ebp
	mov	ebp, esp

        mov	eax, DWORD PTR 8[ebp]
	mov	ecx, DWORD PTR 12[ebp]

        add eax, ecx

	pop	ebp
	ret 0
_sum	ENDP
_TEXT	ENDS

 

在下面的例子中模拟定义了一个叫__fn_test1和__fn_test2的汇编Procedure,并通过call调用。其中__fn_test1无参无返回值,__fn_test2接收2个参数并返回和。

#include<stdio.h>

char __output_fn_test_fmt[] = "procedure __fn_test called.\n";

char __output_fmt[] = "a=%d, b=%d\n";

char __output_sum_fmt[] = "sum=%d\n";

int sum(int a1, int a2)
{
  printf("sum called.\n");
  return a1 + a2;
}

void test1() 
{
  // call procedure __fn_test
  __asm call	__fn_test1;
  // after procedure __fn_test, jump to next instruction: 
  //
  // __end:
  //   printf("end.\n");
  __asm jmp __end;


  // procedure __fn_test.
  // _TEXT	SEGMENT
  // __fn_test	PROC NEAR
  //	push	ebp
  //	mov	ebp, esp
  //    ...
  //    ...
  //	pop	ebp
  //	ret	0
  //_fx	ENDP
  //_TEXT	ENDS
  //
  // procedure __fn_test start.
  __asm __fn_test1: push	ebp;
  __asm mov	ebp, esp;

  __asm push offset __output_fn_test_fmt;
  __asm call printf;
  __asm pop ebx;

  // call fn(printf), with args __output_fmt, 111 and 222
  __asm push 111;
  __asm push 222;
  __asm push offset __output_fmt;
  __asm call printf;
  __asm pop ebx;
  __asm pop ebx;
  __asm pop ebx;

  // call fn(sum), with args 111 and 222
  __asm push 111;
  __asm push 222;
  __asm call sum;
  __asm pop ebx;
  __asm pop ebx;
  // call fn(printf) with args __output_sum_fmt and result of sum
  __asm push eax;
  __asm push offset __output_sum_fmt;
  __asm call printf;
  __asm pop ebx;
  __asm pop ebx;

  __asm pop	ebp;
  __asm ret	0;
  // __fn_test end.


__end:
  printf("end.\n");
}

int test2(int a, int b)
{
  int result = 0;
  // call procedure __fn_test
  __asm push a;
  __asm push b;
  __asm call	__fn_test2;
  __asm pop ebx;
  __asm pop ebx;
  __asm mov result, eax

  // after procedure __fn_test, jump to next instruction: 
  //
  // __end:
  //   printf("end.\n");
  __asm jmp __end;


  // procedure __fn_test.
  // _TEXT	SEGMENT
  // __fn_test	PROC NEAR
  //	push	ebp
  //	mov	ebp, esp
  //    ...
  //    ...
  //	pop	ebp
  //	ret	0
  //_fx	ENDP
  //_TEXT	ENDS
  //
  // procedure __fn_test start.
  __asm __fn_test2: push	ebp;
  __asm mov	ebp, esp;

  __asm push offset __output_fn_test_fmt;
  __asm call printf;
  __asm pop ebx;

  // call fn(printf), with args __output_fmt, 111 and 222
  __asm push DWORD PTR 8[ebp];
  __asm push DWORD PTR 12[ebp];
  __asm push offset __output_fmt;
  __asm call printf;
  __asm pop ebx;
  __asm pop ebx;
  __asm pop ebx;

  // call fn(sum), with args 111 and 222
  __asm push DWORD PTR 8[ebp];
  __asm push DWORD PTR 12[ebp];
  __asm call sum;
  __asm pop ebx;
  __asm pop ebx;

  __asm push eax;

  // call fn(printf) with args __output_sum_fmt and result of sum
  __asm push eax;
  __asm push offset __output_sum_fmt;
  __asm call printf;
  __asm pop ebx;
  __asm pop ebx;

  __asm pop eax;

  __asm pop	ebp;
  __asm ret	0;
  // __fn_test end.


__end:
  printf("end.\n");
  return result;
}

void main()
{
  printf("test 1\n");
  test1();

  printf("test 2\n");
  printf("result: %d\n", test2(11, 22));
}

在下面的例子中模拟定义了一个叫__fn_test1的汇编Procedure,并通过call调用。__fn_test1

无参无返回值,__fn_test1内部关键部分采用_emit。

#include<stdio.h>

char __output_fmt[] = "a=%d, b=%d\n";

void main() 
{
  __asm 
  {
	call __fn_test1 ; call offset __fn_test1

    push eax;
    push eax;
    push offset __output_fmt;
    call printf;
    pop ebx;
	pop ebx;
	pop ebx;

	jmp offset __end ; jmp __end
  }

  __asm 
  {
	__fn_test1: _emit 0x55 ; push	ebp
	
	_emit 0x8B ; v
	_emit 0xEC ; mov	ebp, esp


    // call fn(printf), with args __output_fmt, 111 and 222
    _emit 0x6A ; v
	_emit 0x6F ; push 111;

    _emit 0x68 ; |
	_emit 0xDE ; |
	_emit 0x00 ; |
	_emit 0x00 ; v
	_emit 0x00 ; push 222;
    
	push offset __output_fmt;
	//push __output_fmt;

    call printf; // call offset printf;

    _emit 0x5B ; pop ebx;
    _emit 0x5B ; pop ebx;
    _emit 0x5B ; pop ebx;

    _emit 0xB8
	_emit 0x64
    _emit 0x00
	_emit 0x00
	_emit 0x00 ; mov eax, 64h
	
	_emit 0x5D ; pop	ebp
	

	//_emit 0xC2 ; |
	//_emit 0x00 ; v
	//_emit 0x00 ; ret 0

	_emit 0xC3 ; ret
  }

__end:
  printf("end.");
}

 

在下面的例子中通过一个数组模拟定义了一个汇编Procedure,并通过call调用。该模拟的汇编Procedure无参无返回值,汇编Procedure通过机器码实现。

#include<stdio.h>

char __output_fmt[] = "a=%d, b=%d\n";

void main() 
{
  //char ___fn[] = {0x55, 0x8B, 0xEC, 0x5D, 0xC2, 0x00, 0x00}; // push	ebp; mov	ebp, esp; pop	ebp; ret 0
  //char ___fn[] = {0x55, 0x8B, 0xEC, 0x5D, 0xC3}; // push	ebp; mov	ebp, esp; pop	ebp; ret
  char ___fn[] = {0x55, 0x8B, 0xEC, 0xB8, 0x64, 0x00, 0x00, 0x00, 0x5D, 0xC3}; // push	ebp; mov	ebp, esp; mov eax, 64h; pop	ebp; ret

  
  char *__ptr_fn = ___fn;
  unsigned int __intptr_fn = (unsigned int) __ptr_fn;

  __intptr_fn = (unsigned int) ___fn;

  __asm 
  {
	push eax;
    push eax;
    push offset __output_fmt;
    call printf;
    pop ebx;
	pop ebx;
	pop ebx;

	call __intptr_fn

	push eax;
    push eax;
    push offset __output_fmt;
    call printf;
    pop ebx;
	pop ebx;
	pop ebx;

	jmp offset __end ; jmp __end
  }


__end:
  printf("end.");
}

 

 

C调用汇编

 

callasmtest.c

#include<stdio.h>

int sum(int a, int b);

int main(int argc, char *argv[])
{
  int num = sum(11, 22);
  printf("sum: %d\n", num);

  _asm push 111;
  _asm push 222;
  _asm call sum;
  _asm pop ebx;
  _asm pop ebx;
  _asm mov num, eax;
  printf("sum: %d\n", num);

  return 0;
}

sum.asm

	TITLE	sum.asm
	.386P

_TEXT	SEGMENT
_sum	PROC NEAR
        push	ebp
	mov	ebp, esp

        mov	eax, DWORD PTR 8[ebp]
	mov	ecx, DWORD PTR 12[ebp]

        add eax, ecx

	pop	ebp
	ret 0
_sum	ENDP
_TEXT	ENDS

END
>ml /c /coff sum.asm
>cl /c callasmtest.c callasmtest.obj
>cl /Fecallasmtest.exe sum.obj callasmtest.obj
>.\callasmtest.exe
sum: 33

 

GCC下内嵌汇编

 

这里有一篇对8086介绍的比较透彻的文章:https://www.cnblogs.com/zhaoyl/archive/2012/05/15/2501972.html

 

 

关于C/C++可参考另一篇文章:https://lobin.iteye.com/blog/620212

BOSH initiate a session(A XMPP extension protocol transport over HTTP)

  • 大小: 55.9 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics