C语言/C++内嵌汇编代码实例:文件加密

现在查看的简短程序实现如下操作:读取一个文件,对其进行加密,再将其输出到另一个文件。函数 TranslateBuffer 用一个 __asm 块定义语句,在一个字符数组内进行循环,并把每个字符与预定义值进行 XOR 运算。

内嵌语言可以使用函数形参、局部变量和代码标号。由于本例是由 Microsoft Visual C++ 编译的 Win32 控制台应用,因此其无符号整数类型为 32 位:
void TranslateBuffer(char * buf,
    unsigned count, unsigned char eChar)
{
    __asm {
        mov esi, buf
        mov ecx, count
        mov al, eChar
    L1:
        xor [esi],al
        inc esi
        loop L1
    }    // asm
}

C++ 模块

C++ 启动程序从命令行读取输入和输出文件名。在循环内调用 TranslateBuffer 从文件读取数据块,加密,再将转换后的缓冲区写入新文件:
// ENCODE.CPP    复制并加密文件。
#include <iostream>
#include <fstream>
#include "translat.h"

using namespace std;

int main( int argcount, char * args[] )
{ 
    // 从命令行读取输入和输出文件
    if( argcount < 3 ) {
        cout << "Usage: encode infile outfile" << endl;
        return -1;
    }

    const int BUFSIZE = 2000;
    char buffer[BUFSIZE];
    unsigned int count;            // 字符计算
 
    unsigned char encryptCode;
    cout << "Encryption code [0-255]? ";
    cin >> encryptCode;

    ifstream infile( args[1], ios::binary );
    ofstream outfile( args[2], ios::binary );

    cout << "Reading " << args[1] << " and creating "
        << args[2] << endl;

    while (!infile.eof() )
    {
        infile.read(buffer, BUFSIZE );
        count = infile.gcount();
        TranslateBuffer(buffer, count, encryptCode);
        outfile.write(buffer, count);
    }
    return 0;
}
用命令提示符运行该程序,并传递输入和输岀文件名是最容易的。比如,下面的命令行读取 infile.txt,生成 encoded.txt:

encode infile.txt encoded.txt

头文件

头文件 translat.h 包含了 TranslateBuffer 的一个函数原型:

void TranslateBuffer(char * buf, unsigned count, unsigned char eChar);

过程调用的开销

如果在调试器调试程序时查看 Disassembly 窗口,那么,看到函数调用和返回究竟有多少开销是很有趣的。下面的语句将三个实参送入堆栈,并调用 TranslateBuffer。在 Visual C++ 的 Disassembly 窗口,激活 Show Source Code 和 Show Symbol Names 选项:

; TranslateBuffer(buffer, count, encryptCode)
mov al,byte ptr [encryptCode]
push eax
mov ecx,dword ptr [count]
push ecx
lea edx,[buffer]
push edx
call TranslateBuffer (4159BFh)
add esp, 0Ch

下面的代码对 TranslateBuffer 进行反汇编。编译器自动插入了一些语句用于设置 EBP,以及保存标准寄存器集合,集合内的寄存器不论是否真的会被过程修改,总是被保存。
push ebp
mov ebp, esp
sub esp,40h
push ebx
push esi
push edi
;内嵌代码从这里开始。
mov esi,dword ptr [buf]
mov ecx,dword ptr [count]
mov al,byte ptr [eChar]
L1:
    xor byte ptr [esi],al
    inc esi
    loop L1 (41D762h)
;内嵌代码结束。
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
若关闭了调试器 Disassembly 窗口的 Display Symbol Names 选项,则将参数送入寄存器的三条语句如下:

mov esi,dword ptr [ebp+8]
mov ecx,dword ptr [ebp+0Ch]
mov al,byte ptr [ebp+10h]

编译器按要求生成 Debug 目标代码,这是非优化代码,适合于交互式调试。如果选择 Release 目标代码,那么编译器生成的代码就会更加有效(但易读性更差)。

忽略过程调用

本节前面给出的 TranslateBuffer 中有 6 条内嵌指令,其执行总共需要 8 条指令。

如果函数被调用几千次,那么其执行时间就比较可观了。为了消除这种开销,把内嵌代码插入调用 TranslateBuffer 的循环,得到更为有效的程序:
while (!infile.eof() )
  {
    infile.read(buffer, BUFSIZE );
    count = infile.gcount();

    __asm {
       lea esi,buffer
       mov ecx,count
       mov al, encryptCode
    L1:
       xor [esi],al
       inc  esi
       Loop L1
   } // asm
   
    outfile.write(buffer, count);
  }